From: Ed Warnicke Date: Tue, 8 Jul 2014 18:45:45 +0000 (+0000) Subject: Merge "Bug 1224, Bug 1221: Added Test case scenarios for Data Change Events." X-Git-Tag: release/helium~510 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=04da6ab70e5cefc3df74fa2f49d20085e5e76cc0;hp=d80c9663968b16cac94ae50f49f573358a4da6c5 Merge "Bug 1224, Bug 1221: Added Test case scenarios for Data Change Events." --- diff --git a/opendaylight/md-sal/sal-inmemory-datastore/pom.xml b/opendaylight/md-sal/sal-inmemory-datastore/pom.xml index dadef821eb..906847426c 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/pom.xml +++ b/opendaylight/md-sal/sal-inmemory-datastore/pom.xml @@ -105,9 +105,19 @@ org.slf4j slf4j-simple - ${slf4j.version} test + + org.opendaylight.yangtools + binding-generator-impl + test + + + org.opendaylight.controller + sal-test-model + test + ${project.version} + diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java index 71a3534c81..b679de54d6 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java @@ -7,12 +7,17 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Multimap; +import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.Callable; + import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory; @@ -32,16 +37,12 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.Callable; - -import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; /** * Resolve Data Change Events based on modifications and listeners @@ -438,17 +439,19 @@ final class ResolveDataChangeEventsTask implements Callable childListeners = getListenerChildrenWildcarded(listeners, childId); + switch (childMod.getModificationType()) { case WRITE: case MERGE: case DELETE: one.merge(resolveAnyChangeEvent(childPath, childListeners, childMod)); + oneModified = true; break; case SUBTREE_MODIFIED: subtree.merge(resolveSubtreeChangeEvent(childPath, childListeners, childMod)); @@ -458,11 +461,20 @@ final class ResolveDataChangeEventsTask implements Callable top() { + return Builders.containerBuilder().withNodeIdentifier( + new NodeIdentifier(Top.QNAME)); + } + + + + protected static void assertEmpty(final Collection set) { + Assert.assertTrue(set.isEmpty()); + } + + protected static void assertEmpty(final Map set) { + Assert.assertTrue(set.isEmpty()); + } + + protected static void assertContains(final Collection set, final K... values) { + for (K key : values) { + Assert.assertTrue(set.contains(key)); + } + + } + + protected static void assertNotContains(final Collection set, final K... values) { + for (K key : values) { + Assert.assertFalse(set.contains(key)); + } + } + + protected static void assertContains(final Map map, final K... values) { + for (K key : values) { + Assert.assertTrue(map.containsKey(key)); + } + } + + protected static void assertNotContains(final Map map, final K... values) { + for (K key : values) { + Assert.assertFalse(map.containsKey(key)); + } + } + + protected static CollectionNodeBuilder topLevelMap() { + return ImmutableNodes.mapNodeBuilder(TopLevelList.QNAME); + } + + protected static CollectionNodeBuilder nestedMap() { + return Builders.orderedMapBuilder().withNodeIdentifier(new NodeIdentifier(NestedList.QNAME)); + } + + public static DataContainerNodeBuilder topLevelList( + final String key) { + return ImmutableNodes.mapEntryBuilder(TopLevelList.QNAME, NAME_QNAME, + key); + } + + public static DataContainerNodeBuilder nestedList( + final String key) { + return ImmutableNodes + .mapEntryBuilder(NestedList.QNAME, NAME_QNAME, key); + } + + public static final WriteTransactionCustomizer writeOneTopMultipleNested( + final String topName, final String... nestedName) { + CollectionNodeBuilder nestedMapBuilder = nestedMap(); + for (String nestedItem : nestedName) { + nestedMapBuilder.addChild(nestedList(nestedItem).build()); + } + + final ContainerNode data = top().addChild( + topLevelMap().addChild( + topLevelList(topName) + .addChild(nestedMapBuilder.build()).build()) + .build()).build(); + + return DatastoreTestTask.simpleWrite(TOP_LEVEL, data); + } + + public static final WriteTransactionCustomizer deleteNested(final String topName, + final String nestedName) { + return DatastoreTestTask.simpleDelete(path(topName, nestedName)); + } +} 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 new file mode 100644 index 0000000000..8ac93b1804 --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DatastoreTestTask.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.impl; + +import static org.junit.Assert.assertTrue; + +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 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.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.SettableFuture; + +public class DatastoreTestTask { + + private final DOMStore store; + private AsyncDataChangeListener> changeListener; + + private WriteTransactionCustomizer setup; + private WriteTransactionCustomizer write; + private ReadTransactionVerifier read; + private WriteTransactionCustomizer cleanup; + private InstanceIdentifier changePath; + private DataChangeScope changeScope; + private boolean postSetup = false; + private final ChangeEventListener internalListener; + + public DatastoreTestTask(final DOMStore datastore) { + this.store = datastore; + internalListener = new ChangeEventListener(); + } + + public DatastoreTestTask changeListener(final InstanceIdentifier path, final DataChangeScope scope, + final AsyncDataChangeListener> changeListener) { + this.changeListener = changeListener; + this.changePath = path; + this.changeScope = scope; + return this; + } + + public DatastoreTestTask changeListener(final InstanceIdentifier path, final DataChangeScope scope) { + this.changePath = path; + this.changeScope = scope; + return this; + } + + public DatastoreTestTask setup(final WriteTransactionCustomizer setup) { + this.setup = setup; + return this; + } + + public DatastoreTestTask test(final WriteTransactionCustomizer write) { + this.write = write; + return this; + } + + public DatastoreTestTask read(final ReadTransactionVerifier read) { + this.read = read; + return this; + } + + public DatastoreTestTask cleanup(final WriteTransactionCustomizer cleanup) { + this.cleanup = cleanup; + return this; + } + + public void run() throws InterruptedException, ExecutionException { + if (setup != null) { + execute(setup); + } + ListenerRegistration registration = null; + if (changePath != null) { + registration = store.registerChangeListener(changePath, internalListener, changeScope); + } + + Preconditions.checkState(write != null, "Write Transaction must be set."); + postSetup = true; + execute(write); + if (registration != null) { + registration.close(); + } + if (changeListener != null) { + changeListener.onDataChanged(internalListener.receivedChange.get()); + } + if (read != null) { + read.verify(store.newReadOnlyTransaction()); + } + if (cleanup != null) { + execute(cleanup); + } + } + + public Future>> getChangeEvent() { + return internalListener.receivedChange; + } + + private void execute(final WriteTransactionCustomizer writeCustomizer) throws InterruptedException, + ExecutionException { + DOMStoreReadWriteTransaction tx = store.newReadWriteTransaction(); + writeCustomizer.customize(tx); + DOMStoreThreePhaseCommitCohort cohort = tx.ready(); + assertTrue(cohort.canCommit().get()); + cohort.preCommit().get(); + cohort.commit().get(); + } + + public interface WriteTransactionCustomizer { + public void customize(DOMStoreReadWriteTransaction tx); + } + + public interface ReadTransactionVerifier { + public void verify(DOMStoreReadTransaction tx); + } + + private final class ChangeEventListener implements + AsyncDataChangeListener> { + + protected final SettableFuture>> receivedChange = SettableFuture + .create(); + + @Override + public void onDataChanged(final AsyncDataChangeEvent> change) { + if (postSetup) { + receivedChange.set(change); + } + } + } + + public static final WriteTransactionCustomizer simpleWrite(final InstanceIdentifier path, + final NormalizedNode data) { + return new WriteTransactionCustomizer() { + + @Override + public void customize(final DOMStoreReadWriteTransaction tx) { + tx.write(path, data); + } + }; + } + + public static final WriteTransactionCustomizer simpleMerge(final InstanceIdentifier path, + final NormalizedNode data) { + return new WriteTransactionCustomizer() { + + @Override + public void customize(final DOMStoreReadWriteTransaction tx) { + tx.merge(path, data); + } + }; + } + + public static final WriteTransactionCustomizer simpleDelete(final InstanceIdentifier path) { + return new WriteTransactionCustomizer() { + @Override + public void customize(final DOMStoreReadWriteTransaction tx) { + tx.delete(path); + } + }; + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java new file mode 100644 index 0000000000..54d2043dc7 --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.impl; + +import java.util.concurrent.ExecutionException; + +import org.junit.Test; +import org.opendaylight.controller.md.sal.dom.store.impl.DatastoreTestTask.WriteTransactionCustomizer; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; + +public abstract class DefaultDataChangeListenerTestSuite extends AbstractDataChangeListenerTest { + + protected static final String FOO_SIBLING = "foo-sibling"; + + abstract protected void customizeTask(DatastoreTestTask task); + + @Test + public final void putTopLevelOneNested() throws InterruptedException, ExecutionException { + + DatastoreTestTask task = newTestTask().test(writeOneTopMultipleNested(FOO, BAR)); + customizeTask(task); + task.run(); + putTopLevelOneNested(task); + } + + @Test + public final void existingTopWriteSibling() throws InterruptedException, ExecutionException { + DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO)).test( + new WriteTransactionCustomizer() { + @Override + public void customize(final DOMStoreReadWriteTransaction tx) { + tx.write(path(FOO_SIBLING), topLevelList(FOO_SIBLING).build()); + } + }); + customizeTask(task); + task.run(); + existingTopWriteSibling(task); + } + + protected abstract void existingTopWriteSibling(DatastoreTestTask task) throws InterruptedException, ExecutionException; + + + @Test + public final void existingTopWriteTwoNested() throws InterruptedException, ExecutionException { + DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO)).test( + new WriteTransactionCustomizer() { + @Override + public void customize(final DOMStoreReadWriteTransaction tx) { + tx.write(path(FOO,BAR), nestedList(BAR).build()); + tx.write(path(FOO,BAZ), nestedList(BAZ).build()); + } + }); + customizeTask(task); + task.run(); + existingTopWriteTwoNested(task); + } + + protected abstract void existingTopWriteTwoNested(DatastoreTestTask task) throws InterruptedException, ExecutionException; + + + @Test + public final void existingOneNestedWriteAdditionalNested() throws InterruptedException, ExecutionException { + DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR)).test( + new WriteTransactionCustomizer() { + @Override + public void customize(final DOMStoreReadWriteTransaction tx) { + tx.write(path(FOO,BAZ), nestedList(BAZ).build()); + } + }); + customizeTask(task); + task.run(); + existingOneNestedWriteAdditionalNested(task); + } + + protected abstract void existingOneNestedWriteAdditionalNested(DatastoreTestTask task) throws InterruptedException, ExecutionException; + + protected abstract void putTopLevelOneNested(DatastoreTestTask task) throws InterruptedException, + ExecutionException; + + @Test + public final void replaceTopLevelNestedChanged() throws InterruptedException, ExecutionException { + DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR)).test( + writeOneTopMultipleNested(FOO, BAZ)); + customizeTask(task); + task.run(); + replaceTopLevelNestedChanged(task); + } + + protected abstract void replaceTopLevelNestedChanged(DatastoreTestTask task) throws InterruptedException, + ExecutionException; + + @Test + public final void putTopLevelWithTwoNested() throws InterruptedException, ExecutionException { + + DatastoreTestTask task = newTestTask().test(writeOneTopMultipleNested(FOO, BAR, BAZ)); + customizeTask(task); + task.run(); + putTopLevelWithTwoNested(task); + } + + protected abstract void putTopLevelWithTwoNested(DatastoreTestTask task) throws InterruptedException, + ExecutionException; + + @Test + public final void twoNestedExistsOneIsDeleted() throws InterruptedException, ExecutionException { + + DatastoreTestTask task = newTestTask().setup(writeOneTopMultipleNested(FOO, BAR, BAZ)).test( + deleteNested(FOO, BAZ)); + customizeTask(task); + task.run(); + twoNestedExistsOneIsDeleted(task); + } + + protected abstract void twoNestedExistsOneIsDeleted(DatastoreTestTask task) throws InterruptedException, + ExecutionException; + + @Test + public final void nestedListExistsRootDeleted() throws InterruptedException, ExecutionException { + + DatastoreTestTask task = newTestTask().cleanup(null).setup(writeOneTopMultipleNested(FOO, BAR, BAZ)) + .test(DatastoreTestTask.simpleDelete(TOP_LEVEL)); + customizeTask(task); + task.run(); + nestedListExistsRootDeleted(task); + } + + protected abstract void nestedListExistsRootDeleted(DatastoreTestTask task) throws InterruptedException, + ExecutionException; +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java index 369a7da138..96369dea5f 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java @@ -1,8 +1,18 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.md.sal.dom.store.impl; -import com.google.common.base.Optional; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ExecutionException; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -14,11 +24,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import java.util.concurrent.ExecutionException; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; public class InMemoryDataStoreTest { @@ -237,7 +245,7 @@ public class InMemoryDataStoreTest { /** * Reads /test from readTx Read should return container. */ - private static Optional> assertTestContainerExists(DOMStoreReadTransaction readTx) + private static Optional> assertTestContainerExists(final DOMStoreReadTransaction readTx) throws InterruptedException, ExecutionException { ListenableFuture>> writeTxContainer = readTx.read(TestModel.TEST_PATH); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/RootScopeSubtreeTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/RootScopeSubtreeTest.java new file mode 100644 index 0000000000..ef2a4743e2 --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/RootScopeSubtreeTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.impl; + +import java.util.concurrent.ExecutionException; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class RootScopeSubtreeTest extends DefaultDataChangeListenerTestSuite { + + @Override + protected void customizeTask(final DatastoreTestTask task) { + task.changeListener(TOP_LEVEL, DataChangeScope.SUBTREE); + } + + @Override + public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), TOP_LEVEL, path(FOO), path(FOO, BAR)); + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + } + + @Override + public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO, BAZ)); + assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO)); + assertContains(change.getRemovedPaths(), path(FOO, BAR)); + } + + @Override + protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), TOP_LEVEL, path(FOO), path(FOO, BAR), path(FOO, BAZ)); + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + } + + @Override + protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertEmpty(change.getCreatedData()); + assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO)); + assertContains(change.getRemovedPaths(), path(FOO, BAZ)); + } + + @Override + protected void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertEmpty(change.getCreatedData()); + assertEmpty(change.getUpdatedData()); + assertContains(change.getRemovedPaths(), TOP_LEVEL, path(FOO), path(FOO, BAR), path(FOO, BAZ)); + } + + @Override + protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO,BAZ)); + assertNotContains(change.getCreatedData(), path(FOO,BAR)); + assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO)); + assertEmpty(change.getRemovedPaths()); + } + + @Override + protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO,BAR),path(FOO,BAZ)); + assertContains(change.getUpdatedData(), TOP_LEVEL, path(FOO)); + assertNotContains(change.getUpdatedData(), path(FOO,BAR)); + assertEmpty(change.getRemovedPaths()); + } + + @Override + protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO_SIBLING)); + assertContains(change.getUpdatedData(), TOP_LEVEL); + assertNotContains(change.getUpdatedData(), path(FOO)); + assertEmpty(change.getRemovedPaths()); + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java new file mode 100644 index 0000000000..4c1a860f2a --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.impl; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite { + + private static final InstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node( + TopLevelList.QNAME); + + @Override + protected void customizeTask(final DatastoreTestTask task) { + task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.BASE); + } + + @Override + public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertNotNull(change); + + assertNotContains(change.getCreatedData(), TOP_LEVEL); + assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR)); + + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + + } + + @Override + public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + assertNotNull(change); + + assertContains(change.getCreatedData(), path(FOO, BAZ)); + assertContains(change.getUpdatedData(), path(FOO)); + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertContains(change.getRemovedPaths(), path(FOO, BAR)); + + } + + @Override + protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + assertNotNull(change); + assertFalse(change.getCreatedData().isEmpty()); + + assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ)); + assertNotContains(change.getCreatedData(), TOP_LEVEL); + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + + } + + @Override + protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + Future future = task.getChangeEvent(); + /* + * Base listener should be notified only and only if actual node changed its state, + * since deletion of child, did not result in change of node we are listening + * for, we should not be getting data change event + * and this means settable future containing receivedDataChangeEvent is not done. + * + */ + assertFalse(future.isDone()); + } + + @Override + public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertEmpty(change.getCreatedData()); + assertEmpty(change.getUpdatedData()); + + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR)); + } + + @Override + protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) { + Future future = task.getChangeEvent(); + /* + * One listener should be notified only and only if actual node changed its state, + * since deletion of nested child (in this case /nested-list/nested-list[foo], + * did not result in change of node we are listening + * for, we should not be getting data change event + * and this means settable future containing receivedDataChangeEvent is not done. + * + */ + assertFalse(future.isDone()); + } + + @Override + protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + Future future = task.getChangeEvent(); + /* + * One listener should be notified only and only if actual node changed its state, + * since deletion of nested child (in this case /nested-list/nested-list[foo], + * did not result in change of node we are listening + * for, we should not be getting data change event + * and this means settable future containing receivedDataChangeEvent is not done. + * + */ + assertFalse(future.isDone()); + } + + @Override + protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO_SIBLING)); + assertNotContains(change.getUpdatedData(), path(FOO), TOP_LEVEL); + assertEmpty(change.getRemovedPaths()); + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java new file mode 100644 index 0000000000..1f2cb224ab --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.impl; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { + + private static final InstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node( + TopLevelList.QNAME); + + @Override + protected void customizeTask(final DatastoreTestTask task) { + task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.ONE); + } + + @Override + public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertNotNull(change); + + assertNotContains(change.getCreatedData(), TOP_LEVEL); + assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR)); + + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + + } + + @Override + public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + assertNotNull(change); + + assertContains(change.getCreatedData(), path(FOO, BAZ)); + assertContains(change.getUpdatedData(), path(FOO)); + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertContains(change.getRemovedPaths(), path(FOO, BAR)); + + } + + @Override + protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + assertNotNull(change); + assertFalse(change.getCreatedData().isEmpty()); + + assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ)); + assertNotContains(change.getCreatedData(), TOP_LEVEL); + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + + } + + @Override + protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + Future future = task.getChangeEvent(); + /* + * One listener should be notified only and only if actual node changed its state, + * since deletion of nested child (in this case /nested-list/nested-list[foo], + * did not result in change of node we are listening + * for, we should not be getting data change event + * and this means settable future containing receivedDataChangeEvent is not done. + * + */ + assertFalse(future.isDone()); + } + + @Override + public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertEmpty(change.getCreatedData()); + assertEmpty(change.getUpdatedData()); + + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR)); + } + + @Override + protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) { + Future future = task.getChangeEvent(); + /* + * One listener should be notified only and only if actual node changed its state, + * since deletion of nested child (in this case /nested-list/nested-list[foo], + * did not result in change of node we are listening + * for, we should not be getting data change event + * and this means settable future containing receivedDataChangeEvent is not done. + * + */ + assertFalse(future.isDone()); + } + + @Override + protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + Future future = task.getChangeEvent(); + /* + * One listener should be notified only and only if actual node changed its state, + * since deletion of nested child (in this case /nested-list/nested-list[foo], + * did not result in change of node we are listening + * for, we should not be getting data change event + * and this means settable future containing receivedDataChangeEvent is not done. + * + */ + assertFalse(future.isDone()); + } + + @Override + protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO_SIBLING)); + assertNotContains(change.getUpdatedData(),path(FOO), TOP_LEVEL); + assertEmpty(change.getRemovedPaths()); + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeSubtreeTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeSubtreeTest.java new file mode 100644 index 0000000000..010204888b --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeSubtreeTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.dom.store.impl; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ExecutionException; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class WildcardedScopeSubtreeTest extends DefaultDataChangeListenerTestSuite { + + private static final InstanceIdentifier TOP_LEVEL_LIST_ALL = TOP_LEVEL.node(TopLevelList.QNAME).node( + TopLevelList.QNAME); + + @Override + protected void customizeTask(final DatastoreTestTask task) { + task.changeListener(TOP_LEVEL_LIST_ALL, DataChangeScope.SUBTREE); + } + + @Override + public void putTopLevelOneNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertNotContains(change.getCreatedData(), TOP_LEVEL); + assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR)); + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + + } + + @Override + public void replaceTopLevelNestedChanged(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + assertNotNull(change); + + assertContains(change.getCreatedData(), path(FOO, BAZ)); + assertContains(change.getUpdatedData(), path(FOO)); + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertContains(change.getRemovedPaths(), path(FOO, BAR)); + + } + + @Override + protected void putTopLevelWithTwoNested(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + assertNotNull(change); + assertFalse(change.getCreatedData().isEmpty()); + + assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ)); + assertNotContains(change.getCreatedData(), TOP_LEVEL); + assertEmpty(change.getUpdatedData()); + assertEmpty(change.getRemovedPaths()); + + } + + @Override + protected void twoNestedExistsOneIsDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + assertNotNull(change); + assertTrue(change.getCreatedData().isEmpty()); + assertContains(change.getUpdatedData(), path(FOO)); + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertContains(change.getRemovedPaths(),path(FOO, BAZ)); + } + + @Override + public void nestedListExistsRootDeleted(final DatastoreTestTask task) throws InterruptedException, + ExecutionException { + + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertEmpty(change.getCreatedData()); + assertEmpty(change.getUpdatedData()); + + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR)); + } + + @Override + protected void existingOneNestedWriteAdditionalNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO,BAZ)); + assertNotContains(change.getCreatedData(), path(FOO,BAR)); + assertContains(change.getUpdatedData(), path(FOO)); + assertNotContains(change.getUpdatedData(), TOP_LEVEL); + assertEmpty(change.getRemovedPaths()); + } + + @Override + protected void existingTopWriteTwoNested(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO,BAR),path(FOO,BAZ)); + assertContains(change.getUpdatedData(), path(FOO)); + assertNotContains(change.getUpdatedData(), TOP_LEVEL, path(FOO,BAR)); + assertEmpty(change.getRemovedPaths()); + } + + @Override + protected void existingTopWriteSibling(final DatastoreTestTask task) throws InterruptedException, ExecutionException { + AsyncDataChangeEvent> change = task.getChangeEvent().get(); + + assertContains(change.getCreatedData(), path(FOO_SIBLING)); + assertNotContains(change.getUpdatedData(), path(FOO), TOP_LEVEL); + assertEmpty(change.getRemovedPaths()); + } +} diff --git a/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-list-test.yang b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-list-test.yang new file mode 100644 index 0000000000..48666e1689 --- /dev/null +++ b/opendaylight/md-sal/sal-test-model/src/main/yang/opendaylight-mdsal-list-test.yang @@ -0,0 +1,51 @@ +module opendaylight-mdsal-list-test { + + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:list"; + prefix list-test; + + + description + "This module contains a collection of YANG definitions used for + some test cases."; + + revision 2014-07-01 { + description + "Test model for testing data broker with nested lists."; + } + + grouping two-level-list { + list top-level-list { + description + "Top Level List"; + key "name"; + leaf name { + type string; + } + list nested-list { + key "name"; + leaf name { + type string; + } + leaf type { + type string; + mandatory true; + description + "Mandatory type of list."; + } + ordered-by user; + description + "A list of service functions that compose the service chain"; + } + } + } + + container top { + uses two-level-list; + } + + rpc put-top { + input { + uses two-level-list; + } + } +} \ No newline at end of file