Backport controller's binding-broker tests 71/90071/6
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 29 May 2020 11:11:04 +0000 (13:11 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 29 May 2020 13:00:52 +0000 (15:00 +0200)
Pick up the class as it existed in controller as of
822e39fedebaf35ea9826067ad223dbfa98042ee.

JIRA: MDSAL-557
Change-Id: Ieaaeb594e33f8c5a4ac515d93c70db960cedb81c
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
12 files changed:
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1125RegressionTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1333DataChangeListenerTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug2562DeserializedUnkeyedListTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug3090MultiKeyList.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug4494Test.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug4513Test.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ListInsertionDataChangeListenerTest.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/test/AbstractDataTreeChangeListenerTest.java [new file with mode: 0644]
binding/mdsal-binding-test-model/src/main/yang/opendaylight-listener-test.yang [new file with mode: 0644]
binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-bug-2562.yang [new file with mode: 0644]
binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-bug-3090.yang [new file with mode: 0644]

diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1125RegressionTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1125RegressionTest.java
new file mode 100644 (file)
index 0000000..490299f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015 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.mdsal.binding.dom.adapter;
+
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.topLevelList;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataTreeChangeListenerTest;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+/**
+ * Regression test suite for Bug 1125 - Can't detect switch disconnection
+ * https://bugs.opendaylight.org/show_bug.cgi?id=1125.
+ */
+public class Bug1125RegressionTest extends AbstractDataTreeChangeListenerTest {
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+    private static final InstanceIdentifier<TopLevelList> TOP_FOO_PATH = TOP_PATH
+            .child(TopLevelList.class, TOP_FOO_KEY);
+
+    private static final InstanceIdentifier<TreeComplexUsesAugment> FOO_AUGMENT_PATH = TOP_FOO_PATH
+            .augmentation(TreeComplexUsesAugment.class);
+
+    private static final InstanceIdentifier<TreeComplexUsesAugment> WILDCARDED_AUGMENT_PATH = TOP_PATH
+            .child(TopLevelList.class).augmentation(
+                    TreeComplexUsesAugment.class);
+
+    @Override
+    protected Set<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(Top.class),
+                BindingReflections.getModuleInfo(TreeComplexUsesAugment.class));
+    }
+
+    @Test
+    public void deleteAndListenAugment() {
+        deleteAndListenAugment(TOP_PATH);
+
+        deleteAndListenAugment(TOP_FOO_PATH);
+
+        deleteAndListenAugment(FOO_AUGMENT_PATH);
+    }
+
+    private void deleteAndListenAugment(final InstanceIdentifier<?> path) {
+        TreeComplexUsesAugment augment = writeInitialState();
+        TestListener<TreeComplexUsesAugment> listener = createListener(LogicalDatastoreType.OPERATIONAL,
+                WILDCARDED_AUGMENT_PATH, added(FOO_AUGMENT_PATH, augment), deleted(FOO_AUGMENT_PATH, augment));
+        WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
+        tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+        assertCommit(tx.commit());
+        listener.verify();
+    }
+
+    private TreeComplexUsesAugment writeInitialState() {
+        WriteTransaction initialTx = getDataBroker().newWriteOnlyTransaction();
+        initialTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH, new TopBuilder().build());
+        TreeComplexUsesAugment fooAugment = new TreeComplexUsesAugmentBuilder()
+                .setContainerWithUses(new ContainerWithUsesBuilder().setLeafFromGrouping("foo").build())
+                .build();
+        initialTx.put(LogicalDatastoreType.OPERATIONAL, path(TOP_FOO_KEY), topLevelList(TOP_FOO_KEY, fooAugment));
+        assertCommit(initialTx.commit());
+        return fooAugment;
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1333DataChangeListenerTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1333DataChangeListenerTest.java
new file mode 100644 (file)
index 0000000..10db76f
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.mdsal.binding.dom.adapter;
+
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.USES_ONE_KEY;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.USES_TWO_KEY;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.topLevelList;
+import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataTreeChangeListenerTest;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ListViaUses;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+/**
+ * This testsuite tries to replicate bug 1333 and tests regresion of it  using test-model with similar construction as
+ * one reported.
+ *
+ * <p>
+ * See  https://bugs.opendaylight.org/show_bug.cgi?id=1333 for Bug Description
+ */
+public class Bug1333DataChangeListenerTest extends AbstractDataTreeChangeListenerTest {
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> AUGMENT_WILDCARD =
+            TOP_PATH.child(TopLevelList.class).augmentation(TreeComplexUsesAugment.class);
+
+    @Override
+    protected Set<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(Top.class),
+                BindingReflections.getModuleInfo(TreeComplexUsesAugment.class));
+    }
+
+    private static Top topWithListItem() {
+        return top(topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY)));
+    }
+
+    public Top writeTopWithListItem(final LogicalDatastoreType store) {
+        ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+        Top topItem = topWithListItem();
+        tx.put(store, TOP_PATH, topItem);
+        assertCommit(tx.commit());
+        return topItem;
+    }
+
+    public void deleteItem(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+        ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+        tx.delete(store, path);
+        assertCommit(tx.commit());
+    }
+
+    @Test
+    public void writeTopWithListItemAugmentedListenTopSubtree() {
+        TestListener<Top> listener = createListener(CONFIGURATION, TOP_PATH, added(TOP_PATH, topWithListItem()));
+
+        writeTopWithListItem(CONFIGURATION);
+
+        listener.verify();
+    }
+
+    @Test
+    public void writeTopWithListItemAugmentedListenAugmentSubtreeWildcarded() {
+        TestListener<TreeComplexUsesAugment> listener = createListener(CONFIGURATION, AUGMENT_WILDCARD,
+                added(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY)));
+
+        writeTopWithListItem(CONFIGURATION);
+
+        listener.verify();
+    }
+
+    @Test
+    public void deleteAugmentChildListenTopSubtree() {
+        Top top = writeTopWithListItem(CONFIGURATION);
+
+        TestListener<Top> listener = createListener(CONFIGURATION, TOP_PATH, added(TOP_PATH, top),
+                subtreeModified(TOP_PATH, top, top(topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_TWO_KEY)))));
+
+        InstanceIdentifier<ListViaUses> deletePath = path(TOP_FOO_KEY, USES_ONE_KEY);
+        deleteItem(CONFIGURATION, deletePath);
+
+        listener.verify();
+    }
+
+    @Test
+    public void deleteAugmentChildListenAugmentSubtreeWildcarded() {
+        writeTopWithListItem(CONFIGURATION);
+
+        TestListener<TreeComplexUsesAugment> listener = createListener(CONFIGURATION, AUGMENT_WILDCARD,
+                added(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY)),
+                subtreeModified(path(TOP_FOO_KEY, TreeComplexUsesAugment.class),
+                    complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY), complexUsesAugment(USES_TWO_KEY)));
+
+        InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY, USES_ONE_KEY);
+        deleteItem(CONFIGURATION, deletePath);
+
+        listener.verify();
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug1418AugmentationTest.java
new file mode 100644 (file)
index 0000000..9977962
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2014, 2015 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.mdsal.binding.dom.adapter;
+
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.leafOnlyUsesAugment;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.topLevelList;
+import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataTreeChangeListenerTest;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.TreeLeafOnlyUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+public class Bug1418AugmentationTest extends AbstractDataTreeChangeListenerTest {
+    private static final InstanceIdentifier<Top> TOP = InstanceIdentifier.create(Top.class);
+    private static final InstanceIdentifier<TopLevelList> TOP_FOO = TOP.child(TopLevelList.class, TOP_FOO_KEY);
+    private static final InstanceIdentifier<TreeLeafOnlyUsesAugment> SIMPLE_AUGMENT =
+            TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeLeafOnlyUsesAugment.class);
+    private static final InstanceIdentifier<TreeComplexUsesAugment> COMPLEX_AUGMENT =
+            TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeComplexUsesAugment.class);
+    private static final ListViaUsesKey LIST_VIA_USES_KEY =
+            new ListViaUsesKey("list key");
+    private static final ListViaUsesKey LIST_VIA_USES_KEY_MOD =
+            new ListViaUsesKey("list key modified");
+
+    @Override
+    protected Set<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(Top.class),
+                BindingReflections.getModuleInfo(TreeComplexUsesAugment.class),
+                BindingReflections.getModuleInfo(TreeLeafOnlyUsesAugment.class));
+    }
+
+    @Test
+    public void leafOnlyAugmentationCreatedTest() {
+        TreeLeafOnlyUsesAugment leafOnlyUsesAugment = leafOnlyUsesAugment("test leaf");
+        final TestListener<TreeLeafOnlyUsesAugment> listener = createListener(CONFIGURATION, SIMPLE_AUGMENT,
+                added(path(TOP_FOO_KEY, TreeLeafOnlyUsesAugment.class), leafOnlyUsesAugment));
+
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(CONFIGURATION, TOP, top());
+        writeTx.put(CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        writeTx.put(CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment);
+        assertCommit(writeTx.commit());
+
+        listener.verify();
+    }
+
+    @Test
+    public void leafOnlyAugmentationUpdatedTest() {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(CONFIGURATION, TOP, top());
+        writeTx.put(CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        TreeLeafOnlyUsesAugment leafOnlyUsesAugmentBefore = leafOnlyUsesAugment("test leaf");
+        writeTx.put(CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugmentBefore);
+        assertCommit(writeTx.commit());
+
+        TreeLeafOnlyUsesAugment leafOnlyUsesAugmentAfter = leafOnlyUsesAugment("test leaf changed");
+        final TestListener<TreeLeafOnlyUsesAugment> listener = createListener(CONFIGURATION, SIMPLE_AUGMENT,
+                added(path(TOP_FOO_KEY, TreeLeafOnlyUsesAugment.class), leafOnlyUsesAugmentBefore),
+                replaced(path(TOP_FOO_KEY, TreeLeafOnlyUsesAugment.class), leafOnlyUsesAugmentBefore,
+                    leafOnlyUsesAugmentAfter));
+
+        writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugmentAfter);
+        assertCommit(writeTx.commit());
+
+        listener.verify();
+    }
+
+    @Test
+    public void leafOnlyAugmentationDeletedTest() {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(CONFIGURATION, TOP, top());
+        writeTx.put(CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        TreeLeafOnlyUsesAugment leafOnlyUsesAugment = leafOnlyUsesAugment("test leaf");
+        writeTx.put(CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment);
+        assertCommit(writeTx.commit());
+
+        final TestListener<TreeLeafOnlyUsesAugment> listener = createListener(CONFIGURATION, SIMPLE_AUGMENT,
+                added(path(TOP_FOO_KEY, TreeLeafOnlyUsesAugment.class), leafOnlyUsesAugment),
+                deleted(path(TOP_FOO_KEY, TreeLeafOnlyUsesAugment.class), leafOnlyUsesAugment));
+
+        writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.delete(CONFIGURATION, SIMPLE_AUGMENT);
+        assertCommit(writeTx.commit());
+
+        listener.verify();
+    }
+
+    @Test
+    public void complexAugmentationCreatedTest() {
+        TreeComplexUsesAugment complexUsesAugment = complexUsesAugment(LIST_VIA_USES_KEY);
+        final TestListener<TreeComplexUsesAugment>  listener = createListener(CONFIGURATION, COMPLEX_AUGMENT,
+                added(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugment));
+
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(CONFIGURATION, TOP, top());
+        writeTx.put(CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        writeTx.put(CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment);
+        assertCommit(writeTx.commit());
+
+        listener.verify();
+    }
+
+    @Test
+    public void complexAugmentationUpdatedTest() {
+        WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(CONFIGURATION, TOP, top());
+        writeTx.put(CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY)));
+        TreeComplexUsesAugment complexUsesAugmentBefore = complexUsesAugment(LIST_VIA_USES_KEY);
+        writeTx.put(CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugmentBefore);
+        assertCommit(writeTx.commit());
+
+        TreeComplexUsesAugment complexUsesAugmentAfter = complexUsesAugment(LIST_VIA_USES_KEY_MOD);
+
+        final TestListener<TreeComplexUsesAugment> listener = createListener(CONFIGURATION, COMPLEX_AUGMENT,
+                added(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore),
+                replaced(path(TOP_FOO_KEY, TreeComplexUsesAugment.class), complexUsesAugmentBefore,
+                        complexUsesAugmentAfter));
+
+        writeTx = getDataBroker().newWriteOnlyTransaction();
+        writeTx.put(CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugmentAfter);
+        assertCommit(writeTx.commit());
+
+        listener.verify();
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug2562DeserializedUnkeyedListTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug2562DeserializedUnkeyedListTest.java
new file mode 100644 (file)
index 0000000..348e8dd
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 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.mdsal.binding.dom.adapter;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataTreeChangeListenerTest;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.opendaylight.test.bug._2562.namespace.rev160101.Root;
+import org.opendaylight.yang.gen.v1.opendaylight.test.bug._2562.namespace.rev160101.RootBuilder;
+import org.opendaylight.yang.gen.v1.opendaylight.test.bug._2562.namespace.rev160101.root.Fooroot;
+import org.opendaylight.yang.gen.v1.opendaylight.test.bug._2562.namespace.rev160101.root.FoorootBuilder;
+import org.opendaylight.yang.gen.v1.opendaylight.test.bug._2562.namespace.rev160101.root.fooroot.Barroot;
+import org.opendaylight.yang.gen.v1.opendaylight.test.bug._2562.namespace.rev160101.root.fooroot.BarrootBuilder;
+import org.opendaylight.yang.gen.v1.opendaylight.test.bug._2562.namespace.rev160101.root.fooroot.BarrootKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+public class Bug2562DeserializedUnkeyedListTest extends AbstractDataTreeChangeListenerTest {
+    private static final InstanceIdentifier<Root> ROOT_PATH = InstanceIdentifier.create(Root.class);
+
+    @Override
+    protected Set<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(Root.class));
+    }
+
+    @Test
+    public void writeListToList2562Root() {
+        final Barroot barRoot = new BarrootBuilder().setType(2).setValue(2).withKey(new BarrootKey(2)).build();
+        final Fooroot fooRoot = new FoorootBuilder().setBarroot(Arrays.asList(barRoot)).build();
+        final Root root = new RootBuilder().setFooroot(Arrays.asList(fooRoot)).build();
+
+        final TestListener<Root> listenerRoot = createListener(LogicalDatastoreType.CONFIGURATION, ROOT_PATH,
+                added(ROOT_PATH, root));
+
+        final ReadWriteTransaction readWriteTransaction = getDataBroker().newReadWriteTransaction();
+        readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, ROOT_PATH, root);
+        assertCommit(readWriteTransaction.commit());
+
+        listenerRoot.verify();
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug3090MultiKeyList.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug3090MultiKeyList.java
new file mode 100644 (file)
index 0000000..c990068
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 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.mdsal.binding.dom.adapter;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataTreeChangeListenerTest;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.opendaylight.test.bug._3090.rev160101.Root;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.opendaylight.test.bug._3090.rev160101.RootBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.opendaylight.test.bug._3090.rev160101.root.ListInRoot;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.opendaylight.test.bug._3090.rev160101.root.ListInRootBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+public class Bug3090MultiKeyList extends AbstractDataTreeChangeListenerTest {
+    private static final InstanceIdentifier<Root> ROOT_PATH = InstanceIdentifier.create(Root.class);
+
+    @Override
+    protected Set<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(Root.class));
+    }
+
+    @Test
+    public void listWithMultiKeyTest() {
+        final List<ListInRoot> listInRoots = new ArrayList<>();
+        for (int i = 0; i < 10; i++) {
+            listInRoots.add(new ListInRootBuilder()
+                .setLeafA("leaf a" + i)
+                .setLeafC("leaf c" + i)
+                .setLeafB("leaf b" + i)
+                .build()
+            );
+        }
+
+        final Root root = new RootBuilder().setListInRoot(listInRoots).build();
+
+        final TestListener<Root> listener = createListener(LogicalDatastoreType.CONFIGURATION, ROOT_PATH,
+                match(ModificationType.WRITE, ROOT_PATH, Objects::isNull,
+                        (Function<Root, Boolean>) dataAfter -> checkData(root, dataAfter)));
+
+        final ReadWriteTransaction readWriteTransaction = getDataBroker().newReadWriteTransaction();
+        readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, ROOT_PATH, root);
+        assertCommit(readWriteTransaction.commit());
+
+        listener.verify();
+    }
+
+    private static boolean checkData(final Root expected, final Root actual) {
+        if (actual == null) {
+            return false;
+        }
+
+        Set<ListInRoot> expListInRoot = new HashSet<>(expected.getListInRoot().values());
+        Set<ListInRoot> actualListInRoot = actual.getListInRoot().values().stream()
+                .map(list -> new ListInRootBuilder(list).build()).collect(Collectors.toSet());
+        return expListInRoot.equals(actualListInRoot);
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug4494Test.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug4494Test.java
new file mode 100644 (file)
index 0000000..b1f1375
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 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.mdsal.binding.dom.adapter;
+
+import java.util.ArrayList;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.TopBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class Bug4494Test extends AbstractDataBrokerTest {
+    @Test
+    public void testDelete() throws Exception {
+        DataBroker dataBroker = getDataBroker();
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        ArrayList<TopLevelList> list = new ArrayList<>();
+        list.add(new TopLevelListBuilder().setName("name").build());
+        TopBuilder builder = new TopBuilder().setTopLevelList(list);
+        writeTransaction.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Top.class), builder.build());
+        assertCommit(writeTransaction.commit());
+
+        InstanceIdentifier<TopLevelList> id = InstanceIdentifier.builder(Top.class)
+            .child(TopLevelList.class, new TopLevelListKey("name")).build();
+
+        ReadWriteTransaction writeTransaction1 = dataBroker.newReadWriteTransaction();
+
+        writeTransaction1.delete(LogicalDatastoreType.OPERATIONAL, id);
+        assertCommit(writeTransaction1.commit());
+        ReadWriteTransaction writeTransaction2 = dataBroker.newReadWriteTransaction();
+
+        writeTransaction2.delete(LogicalDatastoreType.OPERATIONAL, id);
+        assertCommit(writeTransaction2.commit());
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug4513Test.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/Bug4513Test.java
new file mode 100644 (file)
index 0000000..a378c92
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc., Inocybe Technologies 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.mdsal.binding.dom.adapter;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+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.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.listener.rev150825.ListenerTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.listener.rev150825.ListenerTestBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.listener.rev150825.listener.test.ListItem;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.listener.rev150825.listener.test.ListItemBuilder;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.Uint32;
+
+/**
+ * Regression test suite for https://bugs.opendaylight.org/show_bug.cgi?id=4513 - Change event is empty when
+ * Homogeneous composite key is used homogeneous composite key is used.
+ */
+public class Bug4513Test extends AbstractDataBrokerTest {
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    @Test
+    public void testDataTreeChangeListener() {
+        DataBroker dataBroker = getDataBroker();
+
+        DataTreeChangeListener<ListItem> listener = mock(DataTreeChangeListener.class);
+        InstanceIdentifier<ListItem> wildCard = InstanceIdentifier.builder(ListenerTest.class)
+                .child(ListItem.class).build();
+        ListenerRegistration<DataTreeChangeListener<ListItem>> reg = dataBroker.registerDataTreeChangeListener(
+                DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, wildCard), listener);
+
+        final ListItem item = writeListItem();
+
+        ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class);
+
+        verify(listener, timeout(100)).onDataTreeChanged(captor.capture());
+
+        Collection<DataTreeModification<ListItem>> mods = captor.getValue();
+        assertEquals("ListItem", item, mods.iterator().next().getRootNode().getDataAfter());
+    }
+
+    private ListItem writeListItem() {
+        WriteTransaction writeTransaction = getDataBroker().newWriteOnlyTransaction();
+        final ListItem item = new ListItemBuilder().setSip("name").setOp(Uint32.valueOf(43)).build();
+        ListenerTestBuilder builder = new ListenerTestBuilder().setListItem(Arrays.asList(item));
+        writeTransaction.put(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(
+                ListenerTest.class).build(), builder.build());
+        assertCommit(writeTransaction.commit());
+        return item;
+    }
+}
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ListInsertionDataChangeListenerTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/ListInsertionDataChangeListenerTest.java
new file mode 100644 (file)
index 0000000..8a251b3
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * 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.mdsal.binding.dom.adapter;
+
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.mdsal.binding.test.model.util.ListsBindingUtils.topLevelList;
+import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.mdsal.binding.api.DataTreeModification;
+import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataTreeChangeListenerTest;
+import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+/**
+ * This testsuite tests explanation for data change scope and data modifications which were described in
+ * https://lists.opendaylight.org/pipermail/controller-dev/2014-July/005541.html.
+ */
+public class ListInsertionDataChangeListenerTest extends AbstractDataTreeChangeListenerTest {
+    private static final InstanceIdentifier<Top> TOP = InstanceIdentifier.create(Top.class);
+    private static final InstanceIdentifier<TopLevelList> WILDCARDED = TOP.child(TopLevelList.class);
+    private static final InstanceIdentifier<TopLevelList> TOP_FOO = TOP.child(TopLevelList.class, TOP_FOO_KEY);
+    private static final InstanceIdentifier<TopLevelList> TOP_BAR = TOP.child(TopLevelList.class, TOP_BAR_KEY);
+
+    @Override
+    protected Set<YangModuleInfo> getModuleInfos() throws Exception {
+        return ImmutableSet.of(BindingReflections.getModuleInfo(Top.class));
+    }
+
+    @Before
+    public void setupWithDataBroker() {
+        WriteTransaction initialTx = getDataBroker().newWriteOnlyTransaction();
+        initialTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_FOO_KEY)));
+        assertCommit(initialTx.commit());
+    }
+
+    @Test
+    public void replaceTopNodeSubtreeListeners() {
+        final TopLevelList topBar = topLevelList(TOP_BAR_KEY);
+        final Top top = top(topBar);
+        final TopLevelList topFoo = topLevelList(TOP_FOO_KEY);
+
+        // Listener for TOP element
+        final TestListener<Top> topListener = createListener(CONFIGURATION, TOP,
+                added(TOP, top(topLevelList(TOP_FOO_KEY))), replaced(TOP, top(topFoo), top));
+
+        // Listener for all list items. This one should see Foo item deleted and Bar item added.
+        final TestListener<TopLevelList> allListener = createListener(CONFIGURATION, WILDCARDED,
+                added(TOP_FOO, topFoo), added(TOP_BAR, topBar), deleted(TOP_FOO, topFoo));
+
+        // Listener for all Foo item. This one should see only Foo item deleted.
+        final TestListener<TopLevelList> fooListener = createListener(CONFIGURATION, TOP_FOO,
+                added(TOP_FOO, topFoo), deleted(TOP_FOO, topFoo));
+
+        // Listener for bar list items.
+        final TestListener<TopLevelList> barListener = createListener(CONFIGURATION, TOP_BAR,
+                added(TOP_BAR, topBar));
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.put(CONFIGURATION, TOP, top);
+        assertCommit(writeTx.commit());
+
+        topListener.verify();
+        allListener.verify();
+        fooListener.verify();
+        barListener.verify();
+    }
+
+    @Test
+    public void mergeTopNodeSubtreeListeners() {
+        final TopLevelList topBar = topLevelList(TOP_BAR_KEY);
+        final TopLevelList topFoo = topLevelList(TOP_FOO_KEY);
+
+        final TestListener<Top> topListener = createListener(CONFIGURATION, TOP,
+                added(TOP, top(topLevelList(TOP_FOO_KEY))), topSubtreeModified(topFoo, topBar));
+        final TestListener<TopLevelList> allListener = createListener(CONFIGURATION, WILDCARDED,
+                added(TOP_FOO, topFoo), added(TOP_BAR, topBar));
+        final TestListener<TopLevelList> fooListener = createListener(CONFIGURATION, TOP_FOO,
+                added(TOP_FOO, topFoo));
+        final TestListener<TopLevelList> barListener = createListener(CONFIGURATION, TOP_BAR,
+                added(TOP_BAR, topBar));
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.merge(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY)));
+        assertCommit(writeTx.commit());
+
+        topListener.verify();
+        allListener.verify();
+        fooListener.verify();
+        barListener.verify();
+    }
+
+    @Test
+    public void putTopBarNodeSubtreeListeners() {
+        final TopLevelList topBar = topLevelList(TOP_BAR_KEY);
+        final TopLevelList topFoo = topLevelList(TOP_FOO_KEY);
+
+        final TestListener<Top> topListener = createListener(CONFIGURATION, TOP,
+                added(TOP, top(topLevelList(TOP_FOO_KEY))), topSubtreeModified(topFoo, topBar));
+        final TestListener<TopLevelList> allListener = createListener(CONFIGURATION, WILDCARDED,
+                added(TOP_FOO, topFoo), added(TOP_BAR, topBar));
+        final TestListener<TopLevelList> fooListener = createListener(CONFIGURATION, TOP_FOO,
+                added(TOP_FOO, topFoo));
+        final TestListener<TopLevelList> barListener = createListener(CONFIGURATION, TOP_BAR,
+                added(TOP_BAR, topBar));
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.put(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
+        assertCommit(writeTx.commit());
+
+        topListener.verify();
+        allListener.verify();
+        fooListener.verify();
+        barListener.verify();
+    }
+
+    @Test
+    public void mergeTopBarNodeSubtreeListeners() {
+        final TopLevelList topBar = topLevelList(TOP_BAR_KEY);
+        final TopLevelList topFoo = topLevelList(TOP_FOO_KEY);
+
+        final TestListener<Top> topListener = createListener(CONFIGURATION, TOP,
+                added(TOP, top(topLevelList(TOP_FOO_KEY))), topSubtreeModified(topFoo, topBar));
+        final TestListener<TopLevelList> allListener = createListener(CONFIGURATION, WILDCARDED,
+                added(TOP_FOO, topFoo), added(TOP_BAR, topBar));
+        final TestListener<TopLevelList> fooListener = createListener(CONFIGURATION, TOP_FOO,
+                added(TOP_FOO, topFoo));
+        final TestListener<TopLevelList> barListener = createListener(CONFIGURATION, TOP_BAR,
+                added(TOP_BAR, topBar));
+
+        ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
+        writeTx.merge(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
+        assertCommit(writeTx.commit());
+
+        topListener.verify();
+        allListener.verify();
+        fooListener.verify();
+        barListener.verify();
+    }
+
+    private static Function<DataTreeModification<Top>, Boolean> topSubtreeModified(final TopLevelList topFoo,
+            final TopLevelList topBar) {
+        return match(ModificationType.SUBTREE_MODIFIED, TOP,
+            (Function<Top, Boolean>) dataBefore -> Objects.equals(top(topFoo), dataBefore),
+            dataAfter -> {
+                Set<TopLevelList> expList = new HashSet<>(top(topBar, topFoo).getTopLevelList().values());
+                Set<TopLevelList> actualList = dataAfter.getTopLevelList().values().stream()
+                        .map(list -> new TopLevelListBuilder(list).build()).collect(Collectors.toSet());
+                return expList.equals(actualList);
+            });
+    }
+}
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
new file mode 100644 (file)
index 0000000..0b0e9a0
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018 Inocybe Technologies 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.mdsal.binding.dom.adapter.test;
+
+import static org.junit.Assert.fail;
+
+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.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+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.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Abstract base that provides a DTCL for verification.
+ *
+ * @author Thomas Pantelis
+ */
+public class AbstractDataTreeChangeListenerTest extends AbstractConcurrentDataBrokerTest {
+    protected static final class TestListener<T extends DataObject> implements DataTreeChangeListener<T> {
+        private final SettableFuture<Collection<DataTreeModification<T>>> future = SettableFuture.create();
+        private final List<DataTreeModification<T>> accumulatedChanges = new ArrayList<>();
+        private final Function<DataTreeModification<T>, Boolean>[] matchers;
+        private final int expChangeCount;
+
+        private TestListener(final Function<DataTreeModification<T>, Boolean>[] matchers) {
+            this.expChangeCount = matchers.length;
+            this.matchers = matchers;
+        }
+
+        @Override
+        public void onDataTreeChanged(final Collection<DataTreeModification<T>> changes) {
+            synchronized (accumulatedChanges) {
+                accumulatedChanges.addAll(changes);
+                if (expChangeCount == accumulatedChanges.size()) {
+                    future.set(new ArrayList<>(accumulatedChanges));
+                }
+            }
+        }
+
+        public Collection<DataTreeModification<T>> changes() {
+            try {
+                final Collection<DataTreeModification<T>> 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() {
+            Collection<DataTreeModification<T>> changes = new ArrayList<>(changes());
+            Iterator<DataTreeModification<T>> iter = changes.iterator();
+            while (iter.hasNext()) {
+                DataTreeModification<T> dataTreeModification = iter.next();
+                for (Function<DataTreeModification<T>, Boolean> matcher: matchers) {
+                    if (matcher.apply(dataTreeModification)) {
+                        iter.remove();
+                        break;
+                    }
+                }
+            }
+
+            if (!changes.isEmpty()) {
+                DataTreeModification<T> mod = changes.iterator().next();
+                fail(String.format("Received unexpected notification: type: %s, path: %s, before: %s, after: %s",
+                        mod.getRootNode().getModificationType(), mod.getRootPath().getRootIdentifier(),
+                        mod.getRootNode().getDataBefore(), mod.getRootNode().getDataAfter()));
+            }
+        }
+
+        public boolean hasChanges() {
+            synchronized (accumulatedChanges) {
+                return !accumulatedChanges.isEmpty();
+            }
+        }
+    }
+
+    protected AbstractDataTreeChangeListenerTest() {
+        super(true);
+    }
+
+    @SafeVarargs
+    protected final <T extends DataObject> TestListener<T> createListener(final LogicalDatastoreType store,
+            final InstanceIdentifier<T> path, final Function<DataTreeModification<T>, Boolean>... matchers) {
+        TestListener<T> listener = new TestListener<>(matchers);
+        getDataBroker().registerDataTreeChangeListener(DataTreeIdentifier.create(store, path), listener);
+        return listener;
+    }
+
+    public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> match(
+            final ModificationType type, final InstanceIdentifier<T> path, final Function<T, Boolean> checkDataBefore,
+            final Function<T, Boolean> checkDataAfter) {
+        return modification -> type == modification.getRootNode().getModificationType()
+                && path.equals(modification.getRootPath().getRootIdentifier())
+                && checkDataBefore.apply(modification.getRootNode().getDataBefore())
+                && checkDataAfter.apply(modification.getRootNode().getDataAfter());
+    }
+
+    public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> match(final ModificationType type,
+            final InstanceIdentifier<T> path, final T expDataBefore, final T expDataAfter) {
+        return match(type, path, dataBefore -> Objects.equals(expDataBefore, dataBefore),
+            (Function<T, Boolean>) dataAfter -> Objects.equals(expDataAfter, dataAfter));
+    }
+
+    public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> added(
+            final InstanceIdentifier<T> path, final T data) {
+        return match(ModificationType.WRITE, path, null, data);
+    }
+
+    public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> replaced(
+            final InstanceIdentifier<T> path, final T dataBefore, final T dataAfter) {
+        return match(ModificationType.WRITE, path, dataBefore, dataAfter);
+    }
+
+    public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> deleted(
+            final InstanceIdentifier<T> path, final T dataBefore) {
+        return match(ModificationType.DELETE, path, dataBefore, null);
+    }
+
+    public static <T extends DataObject> Function<DataTreeModification<T>, Boolean> subtreeModified(
+            final InstanceIdentifier<T> path, final T dataBefore, final T dataAfter) {
+        return match(ModificationType.SUBTREE_MODIFIED, path, dataBefore, dataAfter);
+    }
+}
diff --git a/binding/mdsal-binding-test-model/src/main/yang/opendaylight-listener-test.yang b/binding/mdsal-binding-test-model/src/main/yang/opendaylight-listener-test.yang
new file mode 100644 (file)
index 0000000..3cad60e
--- /dev/null
@@ -0,0 +1,22 @@
+module listener-container {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:listener";
+    prefix "test";
+
+    revision "2015-08-25" {
+        description
+            "Initial revision.";
+    }
+
+    container listener-test {
+        list list-item {
+            key "sip op";
+            leaf sip {
+                type string;
+            }
+            leaf op {
+                type uint32;
+            }
+        }
+    }
+}
diff --git a/binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-bug-2562.yang b/binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-bug-2562.yang
new file mode 100644 (file)
index 0000000..75102ed
--- /dev/null
@@ -0,0 +1,19 @@
+module opendaylight-test-bug-2562 {
+    yang-version 1;
+    namespace "opendaylight-test-bug-2562:namespace";
+    prefix "ty";
+    revision "2016-01-01" {
+        description
+            "bug 2562 Binding Data Codec: Incorrectly deserialized unkeyed list from NormalizedNode";
+    }
+
+    container root {
+        list fooroot {
+            list barroot {
+                key "type";
+                leaf type { type int32; }
+                leaf value { type int32; }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-bug-3090.yang b/binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-bug-3090.yang
new file mode 100644 (file)
index 0000000..9818bd8
--- /dev/null
@@ -0,0 +1,28 @@
+module opendaylight-test-bug-3090 {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:opendaylight-test-bug-3090";
+    prefix "rpc";
+
+    description
+        "Test model for Bug 3090 - An AsyncDataChangeEvent instance's getCreatedData() method can't get data if list has multi-key";
+
+    revision "2016-01-01" {
+        description
+            "Bug 3090";
+    }
+
+    container root {
+        list list-in-root {
+            key "leaf-c leaf-a leaf-b";
+            leaf leaf-a {
+                type string;
+            }
+            leaf leaf-b {
+                type string;
+            }
+            leaf leaf-c {
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file