X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-inmemory-datastore%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fmd%2Fsal%2Fdom%2Fstore%2Fimpl%2FDatastoreTestTask.java;h=a62c0ba88f1fd5226e947160030ee651ac2c418d;hp=26987a6fba6426169ee7df98340c8ac0f0f4be47;hb=refs%2Fchanges%2F00%2F71800%2F2;hpb=fe4049d34de103016d11f3a9050853c6380646d3 diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java index 26987a6fba..a62c0ba88f 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java @@ -8,95 +8,120 @@ package org.opendaylight.controller.md.sal.dom.store.impl; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.SettableFuture; +import com.google.common.util.concurrent.Uninterruptibles; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener; import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreTreeChangePublisher; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; - -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.SettableFuture; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; public class DatastoreTestTask { private final DOMStore store; - private AsyncDataChangeListener> changeListener; private WriteTransactionCustomizer setup; private WriteTransactionCustomizer write; private ReadTransactionVerifier read; private WriteTransactionCustomizer cleanup; private YangInstanceIdentifier changePath; - private DataChangeScope changeScope; - private boolean postSetup = false; - private final ChangeEventListener internalListener; + private DOMStoreTreeChangePublisher storeTreeChangePublisher; + private ChangeEventListener internalListener; + private final TestDCLExecutorService dclExecutorService; - public DatastoreTestTask(final DOMStore datastore) { + public DatastoreTestTask(final DOMStore datastore, final TestDCLExecutorService dclExecutorService) { this.store = datastore; - internalListener = new ChangeEventListener(); + this.dclExecutorService = dclExecutorService; } - public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope, - final AsyncDataChangeListener> changeListener) { - this.changeListener = changeListener; + @SafeVarargs + public final DatastoreTestTask changeListener(final YangInstanceIdentifier path, + Function... matchers) { + assertTrue(store instanceof DOMStoreTreeChangePublisher); + this.storeTreeChangePublisher = (DOMStoreTreeChangePublisher)store; this.changePath = path; - this.changeScope = scope; + this.internalListener = new ChangeEventListener(matchers); return this; } - public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope) { - this.changePath = path; - this.changeScope = scope; - return this; + public static Function added(YangInstanceIdentifier path) { + return candidate -> candidate.getRootNode().getModificationType() == ModificationType.WRITE + && path.equals(candidate.getRootPath()) && !candidate.getRootNode().getDataBefore().isPresent() + && candidate.getRootNode().getDataAfter().isPresent(); + } + + public static Function replaced(YangInstanceIdentifier path) { + return candidate -> candidate.getRootNode().getModificationType() == ModificationType.WRITE + && path.equals(candidate.getRootPath()) && candidate.getRootNode().getDataBefore().isPresent() + && candidate.getRootNode().getDataAfter().isPresent(); } - public DatastoreTestTask setup(final WriteTransactionCustomizer setup) { - this.setup = setup; + public static Function deleted(YangInstanceIdentifier path) { + return candidate -> candidate.getRootNode().getModificationType() == ModificationType.DELETE + && path.equals(candidate.getRootPath()) && candidate.getRootNode().getDataBefore().isPresent() + && !candidate.getRootNode().getDataAfter().isPresent(); + } + + public static Function subtreeModified(YangInstanceIdentifier path) { + return candidate -> candidate.getRootNode().getModificationType() == ModificationType.SUBTREE_MODIFIED + && path.equals(candidate.getRootPath()) && candidate.getRootNode().getDataBefore().isPresent() + && candidate.getRootNode().getDataAfter().isPresent(); + } + + public DatastoreTestTask setup(final WriteTransactionCustomizer customizer) { + this.setup = customizer; return this; } - public DatastoreTestTask test(final WriteTransactionCustomizer write) { - this.write = write; + public DatastoreTestTask test(final WriteTransactionCustomizer customizer) { + this.write = customizer; return this; } - public DatastoreTestTask read(final ReadTransactionVerifier read) { - this.read = read; + public DatastoreTestTask read(final ReadTransactionVerifier customizer) { + this.read = customizer; return this; } - public DatastoreTestTask cleanup(final WriteTransactionCustomizer cleanup) { - this.cleanup = cleanup; + public DatastoreTestTask cleanup(final WriteTransactionCustomizer customizer) { + this.cleanup = customizer; return this; } - public void run() throws InterruptedException, ExecutionException { + public void run() throws Exception { if (setup != null) { execute(setup); } ListenerRegistration registration = null; if (changePath != null) { - registration = store.registerChangeListener(changePath, internalListener, changeScope); + registration = storeTreeChangePublisher.registerTreeChangeListener(changePath, internalListener); } Preconditions.checkState(write != null, "Write Transaction must be set."); - postSetup = true; + + dclExecutorService.afterTestSetup(); + execute(write); if (registration != null) { registration.close(); } - if (changeListener != null) { - changeListener.onDataChanged(internalListener.receivedChange.get()); - } + if (read != null) { read.verify(store.newReadOnlyTransaction()); } @@ -105,8 +130,12 @@ public class DatastoreTestTask { } } - public Future>> getChangeEvent() { - return internalListener.receivedChange; + public void verifyChangeEvents() { + internalListener.verifyChangeEvents(); + } + + public void verifyNoChangeEvent() { + internalListener.verifyNoChangeEvent(); } private void execute(final WriteTransactionCustomizer writeCustomizer) throws InterruptedException, @@ -120,55 +149,93 @@ public class DatastoreTestTask { } public interface WriteTransactionCustomizer { - public void customize(DOMStoreReadWriteTransaction tx); + void customize(DOMStoreReadWriteTransaction tx); } public interface ReadTransactionVerifier { - public void verify(DOMStoreReadTransaction tx); + void verify(DOMStoreReadTransaction tx); } - private final class ChangeEventListener implements - AsyncDataChangeListener> { + private final class ChangeEventListener implements DOMDataTreeChangeListener { + + final SettableFuture> future = SettableFuture.create(); + final Collection accumulatedChanges = new ArrayList<>(); + final Function[] matchers; + final int expChangeCount; + + ChangeEventListener(Function[] matchers) { + this.expChangeCount = matchers.length; + this.matchers = matchers; + } + + Collection changes() { + try { + Collection changes = internalListener.future.get(10, TimeUnit.SECONDS); + Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS); + return changes; + } catch (TimeoutException e) { + throw new AssertionError(String.format( + "Data tree change notifications not received. Expected: %s. Actual: %s - %s", + expChangeCount, accumulatedChanges.size(), accumulatedChanges), e); + } catch (InterruptedException | ExecutionException e) { + throw new AssertionError("Data tree change notifications failed", e); + } + } - protected final SettableFuture>> receivedChange = SettableFuture - .create(); + void verifyChangeEvents() { + Collection changes = new ArrayList<>(changes()); + Iterator iter = changes.iterator(); + while (iter.hasNext()) { + DataTreeCandidate dataTreeModification = iter.next(); + for (Function matcher: matchers) { + if (matcher.apply(dataTreeModification)) { + iter.remove(); + break; + } + } + } + + if (!changes.isEmpty()) { + DataTreeCandidate mod = changes.iterator().next(); + fail(String.format("Received unexpected notification: type: %s, path: %s, before: %s, after: %s", + mod.getRootNode().getModificationType(), mod.getRootPath(), + mod.getRootNode().getDataBefore(), mod.getRootNode().getDataAfter())); + } + } + + void verifyNoChangeEvent() { + try { + Object unexpected = internalListener.future.get(500, TimeUnit.MILLISECONDS); + fail("Got unexpected Data tree change notifications: " + unexpected); + } catch (TimeoutException e) { + // Expected + } catch (InterruptedException | ExecutionException e) { + throw new AssertionError("Data tree change notifications failed", e); + } + } @Override - public void onDataChanged(final AsyncDataChangeEvent> change) { - if (postSetup) { - receivedChange.set(change); + public void onDataTreeChanged(Collection changes) { + synchronized (accumulatedChanges) { + accumulatedChanges.addAll(changes); + if (expChangeCount == accumulatedChanges.size()) { + future.set(new ArrayList<>(accumulatedChanges)); + } } } } public static final WriteTransactionCustomizer simpleWrite(final YangInstanceIdentifier path, final NormalizedNode data) { - return new WriteTransactionCustomizer() { - - @Override - public void customize(final DOMStoreReadWriteTransaction tx) { - tx.write(path, data); - } - }; + return tx -> tx.write(path, data); } public static final WriteTransactionCustomizer simpleMerge(final YangInstanceIdentifier path, final NormalizedNode data) { - return new WriteTransactionCustomizer() { - - @Override - public void customize(final DOMStoreReadWriteTransaction tx) { - tx.merge(path, data); - } - }; + return tx -> tx.merge(path, data); } public static final WriteTransactionCustomizer simpleDelete(final YangInstanceIdentifier path) { - return new WriteTransactionCustomizer() { - @Override - public void customize(final DOMStoreReadWriteTransaction tx) { - tx.delete(path); - } - }; + return tx -> tx.delete(path); } }