X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-dom-adapter%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fadapter%2Ftest%2FAbstractDataTreeChangeListenerTest.java;h=249b765838c12d73ebaf2e06796536d717022c69;hb=68ec91ba3edcf95953a00d41dca525b1e83dbba5;hp=b11d595450cb78aeb1475d088bd27022565aadd7;hpb=a0152cab742a702b57c4f537c58721f17820a8ec;p=mdsal.git diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/AbstractDataTreeChangeListenerTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/AbstractDataTreeChangeListenerTest.java index b11d595450..249b765838 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/AbstractDataTreeChangeListenerTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/AbstractDataTreeChangeListenerTest.java @@ -7,25 +7,25 @@ */ package org.opendaylight.mdsal.binding.dom.adapter.test; +import static java.util.Objects.requireNonNull; import static org.junit.Assert.fail; -import com.google.common.util.concurrent.SettableFuture; +import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.Uninterruptibles; import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Objects; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType; import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; import org.opendaylight.mdsal.binding.api.DataTreeModification; import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -47,41 +47,20 @@ public class AbstractDataTreeChangeListenerTest extends AbstractConcurrentDataBr boolean apply(T data); } - protected static final class TestListener implements DataTreeChangeListener { - private final SettableFuture>> future = SettableFuture.create(); - private final List> accumulatedChanges = new ArrayList<>(); - private final Deque> matchers; - private final int expChangeCount; + protected static final class ModificationCollector implements AutoCloseable { + private final TestListener listener; + private final Registration reg; - private TestListener(final List> matchers) { - this.matchers = new ArrayDeque<>(matchers); - expChangeCount = this.matchers.size(); + private ModificationCollector(final TestListener listener, final Registration reg) { + this.listener = requireNonNull(listener); + this.reg = requireNonNull(reg); } - @Override - public void onDataTreeChanged(final Collection> changes) { - synchronized (accumulatedChanges) { - accumulatedChanges.addAll(changes); - if (expChangeCount == accumulatedChanges.size()) { - future.set(List.copyOf(accumulatedChanges)); - } - } - } + @SafeVarargs + public final void assertModifications(final Matcher... inOrder) { + final var matchers = new ArrayDeque<>(Arrays.asList(inOrder)); + final var changes = new ArrayDeque<>(listener.awaitChanges(matchers.size())); - public List> changes() { - try { - final var changes = future.get(5, TimeUnit.SECONDS); - Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); - return changes; - } catch (InterruptedException | TimeoutException | ExecutionException e) { - throw new AssertionError(String.format( - "Data tree change notifications not received. Expected: %s. Actual: %s - %s", - expChangeCount, accumulatedChanges.size(), accumulatedChanges), e); - } - } - - public void verify() { - final var changes = new ArrayDeque<>(changes()); while (!changes.isEmpty()) { final var mod = changes.pop(); final var matcher = matchers.peek(); @@ -101,9 +80,60 @@ public class AbstractDataTreeChangeListenerTest extends AbstractConcurrentDataBr } } - public boolean hasChanges() { - synchronized (accumulatedChanges) { - return !accumulatedChanges.isEmpty(); + @Override + public void close() { + reg.close(); + } + } + + private static final class TestListener implements DataTreeChangeListener { + private final Deque> accumulatedChanges = new ArrayDeque<>(); + + private boolean synced; + + @Override + public synchronized void onDataTreeChanged(final Collection> changes) { + accumulatedChanges.addAll(changes); + synced = true; + } + + @Override + public synchronized void onInitialData() { + synced = true; + } + + private void awaitSync() { + final var sw = Stopwatch.createStarted(); + + do { + synchronized (this) { + if (synced) { + return; + } + } + + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + } while (sw.elapsed(TimeUnit.SECONDS) < 5); + + throw new AssertionError("Failed to achieve initial sync"); + } + + private List> awaitChanges(final int expectedCount) { + final var sw = Stopwatch.createStarted(); + + do { + synchronized (this) { + if (accumulatedChanges.size() >= expectedCount) { + return List.copyOf(accumulatedChanges); + } + } + + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + } while (sw.elapsed(TimeUnit.SECONDS) < 5); + + synchronized (this) { + throw new AssertionError("Expected %s changes, received only %s".formatted(expectedCount, + accumulatedChanges.size())); } } } @@ -112,42 +142,45 @@ public class AbstractDataTreeChangeListenerTest extends AbstractConcurrentDataBr super(true); } - @SafeVarargs - protected final TestListener createListener(final LogicalDatastoreType store, - final InstanceIdentifier path, final Matcher... matchers) { - final var listener = new TestListener<>(Arrays.asList(matchers)); - getDataBroker().registerDataTreeChangeListener(DataTreeIdentifier.create(store, path), listener); - return listener; + protected final @NonNull ModificationCollector createCollector( + final LogicalDatastoreType store, final InstanceIdentifier path) { + final var listener = new TestListener(); + final var reg = getDataBroker().registerDataTreeChangeListener(DataTreeIdentifier.create(store, path), + listener); + listener.awaitSync(); + return new ModificationCollector<>(listener, reg); } - public static Matcher match(final ModificationType type, final InstanceIdentifier path, - final DataMatcher checkDataBefore, final DataMatcher checkDataAfter) { + public static @NonNull Matcher match(final ModificationType type, + final InstanceIdentifier path, final DataMatcher checkDataBefore, + final DataMatcher checkDataAfter) { return modification -> type == modification.getRootNode().getModificationType() && path.equals(modification.getRootPath().getRootIdentifier()) && checkDataBefore.apply(modification.getRootNode().getDataBefore()) && checkDataAfter.apply(modification.getRootNode().getDataAfter()); } - public static Matcher match(final ModificationType type, final InstanceIdentifier path, - final T expDataBefore, final T expDataAfter) { + public static @NonNull Matcher match(final ModificationType type, + final InstanceIdentifier path, final T expDataBefore, final T expDataAfter) { return match(type, path, dataBefore -> Objects.equals(expDataBefore, dataBefore), (DataMatcher) dataAfter -> Objects.equals(expDataAfter, dataAfter)); } - public static Matcher added(final InstanceIdentifier path, final T data) { + public static @NonNull Matcher added(final InstanceIdentifier path, final T data) { return match(ModificationType.WRITE, path, null, data); } - public static Matcher replaced(final InstanceIdentifier path, final T dataBefore, - final T dataAfter) { + public static @NonNull Matcher replaced(final InstanceIdentifier path, + final T dataBefore, final T dataAfter) { return match(ModificationType.WRITE, path, dataBefore, dataAfter); } - public static Matcher deleted(final InstanceIdentifier path, final T dataBefore) { + public static @NonNull Matcher deleted(final InstanceIdentifier path, + final T dataBefore) { return match(ModificationType.DELETE, path, dataBefore, null); } - public static Matcher subtreeModified(final InstanceIdentifier path, + public static @NonNull Matcher subtreeModified(final InstanceIdentifier path, final T dataBefore, final T dataAfter) { return match(ModificationType.SUBTREE_MODIFIED, path, dataBefore, dataAfter); }