Turn ListenersBroker into a component
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / streams / listeners / ListenerAdapterTest.java
index 7f7e0ef8bc709e4e723c47b0f09550b7298eaf1b..f706b32240c015013b33790066635dc4127448b6 100644 (file)
@@ -7,17 +7,16 @@
  */
 package org.opendaylight.restconf.nb.rfc8040.streams.listeners;
 
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.fail;
 
 import com.google.common.util.concurrent.Uninterruptibles;
 import java.io.IOException;
 import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Paths;
-import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import org.json.JSONException;
@@ -33,14 +32,19 @@ import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
-import org.opendaylight.restconf.nb.rfc8040.LeafNodesOnlyParam;
+import org.opendaylight.restconf.api.query.ChangedLeafNodesOnlyParam;
+import org.opendaylight.restconf.api.query.ChildNodesOnlyParam;
+import org.opendaylight.restconf.api.query.LeafNodesOnlyParam;
+import org.opendaylight.restconf.api.query.SkipNotificationDataParam;
+import org.opendaylight.restconf.api.query.StartTimeParam;
 import org.opendaylight.restconf.nb.rfc8040.NotificationQueryParams;
-import org.opendaylight.restconf.nb.rfc8040.SkipNotificationDataParam;
-import org.opendaylight.restconf.nb.rfc8040.StartTimeParam;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.PatchCont1Builder;
 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice1.PatchCase1Builder;
+import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.PatchCase11Builder;
+import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.patch.case11.patch.sub.choice11.PatchSubCase11Builder;
+import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.patch.case11.patch.sub.choice11.patch.sub.case11.patch.sub.sub.choice11.PatchSubSubCase11Builder;
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont;
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchContBuilder;
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1;
@@ -48,8 +52,9 @@ import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.p
 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Key;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.util.BindingMap;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 import org.skyscreamer.jsonassert.JSONAssert;
@@ -62,37 +67,89 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
 
     private static final String JSON_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.json";
     private static final String JSON_NOTIF_LEAVES_UPDATE =  "/listener-adapter-test/notif-leaves-update.json";
-    private static final String JSON_NOTIF_LEAVES_DEL =  "/listener-adapter-test/notif-leaves-del.json";
-    private static final String JSON_NOTIF_CREATE = "/listener-adapter-test/notif-create.json";
-    private static final String JSON_NOTIF_UPDATE = "/listener-adapter-test/notif-update.json";
-    private static final String JSON_NOTIF_DEL = "/listener-adapter-test/notif-del.json";
-    private static final String JSON_NOTIF_WITHOUT_DATA_CREATE =
-            "/listener-adapter-test/notif-without-data-create.json";
-    private static final String JSON_NOTIF_WITHOUT_DATA_UPDATE =
-            "/listener-adapter-test/notif-without-data-update.json";
-    private static final String JSON_NOTIF_WITHOUT_DATA_DELETE =
-            "/listener-adapter-test/notif-without-data-del.json";
-
-    private static final String XML_NOTIF_CREATE = "/listener-adapter-test/notif-create.xml";
-    private static final String XML_NOTIF_UPDATE =  "/listener-adapter-test/notif-update.xml";
-    private static final String XML_NOTIF_DEL =  "/listener-adapter-test/notif-delete.xml";
+    private static final String JSON_NOTIF_LEAVES_DELETE = "/listener-adapter-test/notif-leaves-delete.json";
+    private static final String JSON_NOTIF_CHANGED_LEAVES_CREATE =
+            "/listener-adapter-test/notif-changed-leaves-create.json";
+    private static final String JSON_NOTIF_CHANGED_LEAVES_UPDATE =
+            "/listener-adapter-test/notif-changed-leaves-update.json";
+    private static final String JSON_NOTIF_CHANGED_LEAVES_DELETE =
+            "/listener-adapter-test/notif-changed-leaves-delete.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_CREATE =
+            "/listener-adapter-test/notif-child-nodes-only-create.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
+        "/listener-adapter-test/notif-child-nodes-only-update1.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
+        "/listener-adapter-test/notif-child-nodes-only-update2.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_DELETE =
+            "/listener-adapter-test/notif-child-nodes-only-delete.json";
 
     private static final String XML_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.xml";
     private static final String XML_NOTIF_LEAVES_UPDATE =  "/listener-adapter-test/notif-leaves-update.xml";
-    private static final String XML_NOTIF_LEAVES_DEL =  "/listener-adapter-test/notif-leaves-delete.xml";
-
-    private static final String XML_NOTIF_WITHOUT_DATA_CREATE =
-            "/listener-adapter-test/notif-without-data-create.xml";
-    private static final String XML_NOTIF_WITHOUT_DATA_UPDATE =
-            "/listener-adapter-test/notif-without-data-update.xml";
-    private static final String XML_NOTIF_WITHOUT_DATA_DELETE =
-            "/listener-adapter-test/notif-without-data-delete.xml";
-
-    private static final YangInstanceIdentifier PATCH_CONT_YIID =
-            YangInstanceIdentifier.create(new NodeIdentifier(PatchCont.QNAME));
+    private static final String XML_NOTIF_LEAVES_DELETE =  "/listener-adapter-test/notif-leaves-delete.xml";
+    private static final String XML_NOTIF_CHANGED_LEAVES_CREATE =
+            "/listener-adapter-test/notif-changed-leaves-create.xml";
+    private static final String XML_NOTIF_CHANGED_LEAVES_UPDATE =
+            "/listener-adapter-test/notif-changed-leaves-update.xml";
+    private static final String XML_NOTIF_CHANGED_LEAVES_DELETE =
+            "/listener-adapter-test/notif-changed-leaves-delete.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_CREATE =
+        "/listener-adapter-test/notif-child-nodes-only-create.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
+        "/listener-adapter-test/notif-child-nodes-only-update1.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
+        "/listener-adapter-test/notif-child-nodes-only-update2.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_DELETE =
+        "/listener-adapter-test/notif-child-nodes-only-delete.xml";
+
+    private static final String JSON_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.json";
+    private static final String JSON_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.json";
+    private static final String JSON_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.json";
+    private static final String JSON_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.json";
+    private static final String JSON_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.json";
+    private static final String JSON_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.json";
+    private static final String JSON_NOTIF_WITHOUT_DATA_CONT_CREATE =
+            "/listener-adapter-test/notif-without-data-cont-create.json";
+    private static final String JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE =
+            "/listener-adapter-test/notif-without-data-cont-update.json";
+    private static final String JSON_NOTIF_WITHOUT_DATA_CONT_DELETE =
+            "/listener-adapter-test/notif-without-data-cont-delete.json";
+    private static final String JSON_NOTIF_WITHOUT_DATA_LIST_CREATE =
+            "/listener-adapter-test/notif-without-data-list-create.json";
+    private static final String JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE =
+            "/listener-adapter-test/notif-without-data-list-update.json";
+    private static final String JSON_NOTIF_WITHOUT_DATA_LIST_DELETE =
+            "/listener-adapter-test/notif-without-data-list-delete.json";
+
+    private static final String XML_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.xml";
+    private static final String XML_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.xml";
+    private static final String XML_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.xml";
+    private static final String XML_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.xml";
+    private static final String XML_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.xml";
+    private static final String XML_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.xml";
+    private static final String XML_NOTIF_WITHOUT_DATA_CONT_CREATE =
+            "/listener-adapter-test/notif-without-data-cont-create.xml";
+    private static final String XML_NOTIF_WITHOUT_DATA_CONT_UPDATE =
+            "/listener-adapter-test/notif-without-data-cont-update.xml";
+    private static final String XML_NOTIF_WITHOUT_DATA_CONT_DELETE =
+            "/listener-adapter-test/notif-without-data-cont-delete.xml";
+    private static final String XML_NOTIF_WITHOUT_DATA_LIST_CREATE =
+            "/listener-adapter-test/notif-without-data-list-create.xml";
+    private static final String XML_NOTIF_WITHOUT_DATA_LIST_UPDATE =
+            "/listener-adapter-test/notif-without-data-list-update.xml";
+    private static final String XML_NOTIF_WITHOUT_DATA_LIST_DELETE =
+            "/listener-adapter-test/notif-without-data-list-delete.xml";
+
+    private static final YangInstanceIdentifier PATCH_CONT_YIID = YangInstanceIdentifier.of(PatchCont.QNAME);
+
+    private static final YangInstanceIdentifier MY_LIST1_YIID = YangInstanceIdentifier.builder()
+            .node(PatchCont.QNAME)
+            .node(MyList1.QNAME)
+            .nodeWithKey(MyList1.QNAME, QName.create(PatchCont.QNAME.getModule(), "name"), "Althea")
+            .build();
 
     private static EffectiveModelContext SCHEMA_CONTEXT;
 
+    private final ListenersBroker listenersBroker = new ListenersBroker();
     private DataBroker dataBroker;
     private DOMDataBroker domDataBroker;
     private DatabindProvider databindProvider;
@@ -120,11 +177,15 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         private CountDownLatch notificationLatch = new CountDownLatch(1);
 
         ListenerAdapterTester(final YangInstanceIdentifier path, final String streamName,
-                              final NotificationOutputType outputType,
-                              final boolean leafNodesOnly, final boolean skipNotificationData) {
-            super(path, streamName, outputType);
+                final NotificationOutputType outputType, final boolean leafNodesOnly,
+                final boolean skipNotificationData, final boolean changedLeafNodesOnly, final boolean childNodesOnly,
+                final ListenersBroker listenersBroker) {
+            super(path, streamName, outputType, listenersBroker);
             setQueryParams(NotificationQueryParams.of(StartTimeParam.forUriValue("1970-01-01T00:00:00Z"), null, null,
-                LeafNodesOnlyParam.of(leafNodesOnly), SkipNotificationDataParam.of(skipNotificationData)));
+                leafNodesOnly ? LeafNodesOnlyParam.of(true) : null,
+                skipNotificationData ? SkipNotificationDataParam.of(true) : null,
+                changedLeafNodesOnly ? ChangedLeafNodesOnlyParam.of(true) : null,
+                childNodesOnly ? ChildNodesOnlyParam.of(true) : null));
         }
 
         @Override
@@ -149,7 +210,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         }
 
         public void assertXmlSimilar(final String xml) {
-            awaitUntillNotification(xml);
+            awaitUntilNotification(xml);
 
             LOG.info("lastNotification: {}", lastNotification);
             final String withFakeDate = withFakeXmlDate(lastNotification);
@@ -160,7 +221,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
             notificationLatch = new CountDownLatch(1);
         }
 
-        public String awaitUntillNotification(final String xml) {
+        public String awaitUntilNotification(final String xml) {
             // FIXME: use awaitility
             if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
                 fail("Timed out waiting for notification for: " + xml);
@@ -173,37 +234,10 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         }
     }
 
-    static String withFakeDate(final String in) throws JSONException {
-        final JSONObject doc = new JSONObject(in);
-        final JSONObject notification =
-                doc.getJSONObject("urn-ietf-params-xml-ns-netconf-notification-1.0:notification");
-        if (notification == null) {
-            return in;
-        }
-        notification.put("event-time", "someDate");
-        return doc.toString();
-    }
-
-    static String withFakeXmlDate(final String in) {
-        return in.replaceAll("<eventTime>.*</eventTime>", "<eventTime>someDate</eventTime>");
-    }
-
-    private String getNotifJson(final String path) throws IOException, URISyntaxException, JSONException {
-        final URL url = getClass().getResource(path);
-        final byte[] bytes = Files.readAllBytes(Paths.get(url.toURI()));
-        return withFakeDate(new String(bytes, StandardCharsets.UTF_8));
-    }
-
-    private String getResultXml(final String path) throws IOException, URISyntaxException, JSONException {
-        final URL url = getClass().getResource(path);
-        final byte[] bytes = Files.readAllBytes(Paths.get(url.toURI()));
-        return withFakeXmlDate(new String(bytes, StandardCharsets.UTF_8));
-    }
-
     @Test
     public void testJsonNotifsLeaves() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
-            true, false);
+            true, false, false, false, listenersBroker);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         final DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
@@ -214,44 +248,37 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
 
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
         final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
-        PatchContBuilder builder =
-                new PatchContBuilder()
-                        .addAugmentation(
-                                new PatchCont1Builder()
-                            .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
-                                        .setLeaf1("AugmentLeaf").build())
-                        .setMyList1(
-                                Map.of(new MyList1Key("Althea"),
-                                        new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build())
-                        );
-        writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
+            .build());
         writeTransaction.commit();
         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
-        builder = new PatchContBuilder()
-                .addAugmentation(
-                        new PatchCont1Builder()
-                                .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
-                                .setLeaf1("AugmentLeaf").build())
-                .setMyList1(
-                        Map.of(new MyList1Key("Althea"),
-                                new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build())
-                );
-        writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
+            .build());
         writeTransaction.commit();
         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
         writeTransaction.commit();
-        adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DEL));
+        adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DELETE));
     }
 
     @Test
-    public void testJsonNotifs() throws Exception {
+    public void testJsonNotifsChangedLeaves() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
-            false, false);
+                false, false, true, false, listenersBroker);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         final DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
@@ -261,59 +288,82 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         changeService.registerDataTreeChangeListener(root, adapter);
 
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-        MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
-        final InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class)
-                .child(MyList1.class, new MyList1Key("Althea"));
-        writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice2(new PatchCase11Builder()
+                    .setPatchSubChoice11(new PatchSubCase11Builder()
+                        .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
+                        .build())
+                    .build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
+            .build());
         writeTransaction.commit();
-        adapter.assertGot(getNotifJson(JSON_NOTIF_CREATE));
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_CREATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
-        builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
-        writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice2(new PatchCase11Builder()
+                    .setPatchSubChoice11(new PatchSubCase11Builder()
+                        .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
+                        .build())
+                    .build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
+            .build());
         writeTransaction.commit();
-        adapter.assertGot(getNotifJson(JSON_NOTIF_UPDATE));
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_UPDATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
         writeTransaction.commit();
-        adapter.assertGot(getNotifJson(JSON_NOTIF_DEL));
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_DELETE));
     }
 
     @Test
-    public void testJsonNotifsWithoutData() throws Exception {
-        ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
-            false, true);
+    public void testJsonChildNodesOnly() throws Exception {
+        final var adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
+            NotificationOutputType.JSON, false, false, false, true, listenersBroker);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
-        DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
-                .getInstance(DOMDataTreeChangeService.class);
-        DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+        final var changeService = domDataBroker.getExtensions()
+            .getInstance(DOMDataTreeChangeService.class);
+        final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
         changeService.registerDataTreeChangeListener(root, adapter);
+
+        final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-        MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
-        InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class)
-                .child(MyList1.class, new MyList1Key("Althea"));
-        writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
         writeTransaction.commit();
-        adapter.assertGot(getNotifJson(JSON_NOTIF_WITHOUT_DATA_CREATE));
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_CREATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
-        builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
-        writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
         writeTransaction.commit();
-        adapter.assertGot(getNotifJson(JSON_NOTIF_WITHOUT_DATA_UPDATE));
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
         writeTransaction.commit();
-        adapter.assertGot(getNotifJson(JSON_NOTIF_WITHOUT_DATA_DELETE));
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_DELETE));
     }
 
     @Test
-    public void testXmlNotifications() throws Exception {
+    public void testXmlLeavesOnly() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
-            false, false);
+            true, false, false, false, listenersBroker);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
@@ -321,104 +371,272 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
         changeService.registerDataTreeChangeListener(root, adapter);
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-        MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
-        InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class)
-                .child(MyList1.class, new MyList1Key("Althea"));
-        writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
+            .build());
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CREATE));
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_CREATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
-        builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
-        writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
+            .build());
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_UPDATE));
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_UPDATE));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_DEL));
+
+        // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
+        final String notification = adapter.awaitUntilNotification("");
+        assertThat(notification, allOf(
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
+                + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
+                + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
+                + "/a:my-list1[a:name='Althea']/a:name</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
+                + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
+                + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf1</path>")));
     }
 
     @Test
-    public void testXmlSkipData() throws Exception {
+    public void testXmlChangedLeavesOnly() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
-            false, true);
+                false, false, true, false, listenersBroker);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
                 .getInstance(DOMDataTreeChangeService.class);
         DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
         changeService.registerDataTreeChangeListener(root, adapter);
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice2(new PatchCase11Builder()
+                    .setPatchSubChoice11(new PatchSubCase11Builder()
+                        .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
+                        .build())
+                    .build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
+            .build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_CREATE));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
+            .addAugmentation(new PatchCont1Builder()
+                .setPatchChoice2(new PatchCase11Builder()
+                    .setPatchSubChoice11(new PatchSubCase11Builder()
+                        .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
+                        .build())
+                    .build())
+                .setLeaf1("AugmentLeaf")
+                .build())
+            .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
+            .build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_UPDATE));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
+        writeTransaction.commit();
+
+        // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
+        final String notification = adapter.awaitUntilNotification("");
+        assertThat(notification, allOf(
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
+                + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
+                + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
+                + "/a:my-list1[a:name='Althea']/a:name</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
+                + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
+            containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
+                + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf11</path>")));
+    }
+
+    @Test
+    public void testXmlChildNodesOnly() throws Exception {
+        final var adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
+            NotificationOutputType.XML, false, false, false, true, listenersBroker);
+        adapter.setCloseVars(domDataBroker, databindProvider);
+
+        final var changeService = domDataBroker.getExtensions()
+            .getInstance(DOMDataTreeChangeService.class);
+        final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+        changeService.registerDataTreeChangeListener(root, adapter);
+
+        final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_CREATE));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE1));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE2));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_DELETE));
+    }
+
+    @Test
+    public void testJsonContNotifications() throws Exception {
+        jsonNotifications(PATCH_CONT_YIID, false, JSON_NOTIF_CONT_CREATE,
+                JSON_NOTIF_CONT_UPDATE, JSON_NOTIF_CONT_DELETE);
+    }
+
+    @Test
+    public void testJsonListNotifications() throws Exception {
+        jsonNotifications(MY_LIST1_YIID, false, JSON_NOTIF_LIST_CREATE,
+                JSON_NOTIF_LIST_UPDATE, JSON_NOTIF_LIST_DELETE);
+    }
+
+    @Test
+    public void testJsonContNotificationsWithoutData() throws Exception {
+        jsonNotifications(PATCH_CONT_YIID, true, JSON_NOTIF_WITHOUT_DATA_CONT_CREATE,
+                JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE, JSON_NOTIF_WITHOUT_DATA_CONT_DELETE);
+    }
+
+    @Test
+    public void testJsonListNotificationsWithoutData() throws Exception {
+        jsonNotifications(MY_LIST1_YIID, true, JSON_NOTIF_WITHOUT_DATA_LIST_CREATE,
+                JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE, JSON_NOTIF_WITHOUT_DATA_LIST_DELETE);
+    }
+
+    @Test
+    public void testXmlContNotifications() throws Exception {
+        xmlNotifications(PATCH_CONT_YIID, false, XML_NOTIF_CONT_CREATE,
+                XML_NOTIF_CONT_UPDATE, XML_NOTIF_CONT_DELETE);
+    }
+
+    @Test
+    public void testXmlListNotifications() throws Exception {
+        xmlNotifications(MY_LIST1_YIID, false, XML_NOTIF_LIST_CREATE,
+                XML_NOTIF_LIST_UPDATE, XML_NOTIF_LIST_DELETE);
+    }
+
+    @Test
+    public void testXmlContNotificationsWithoutData() throws Exception {
+        xmlNotifications(PATCH_CONT_YIID, true, XML_NOTIF_WITHOUT_DATA_CONT_CREATE,
+                XML_NOTIF_WITHOUT_DATA_CONT_UPDATE, XML_NOTIF_WITHOUT_DATA_CONT_DELETE);
+    }
+
+    @Test
+    public void testXmlListNotificationsWithoutData() throws Exception {
+        xmlNotifications(MY_LIST1_YIID, true, XML_NOTIF_WITHOUT_DATA_LIST_CREATE,
+                XML_NOTIF_WITHOUT_DATA_LIST_UPDATE, XML_NOTIF_WITHOUT_DATA_LIST_DELETE);
+    }
+
+    static String withFakeDate(final String in) throws JSONException {
+        final JSONObject doc = new JSONObject(in);
+        final JSONObject notification = doc.getJSONObject("ietf-restconf:notification");
+        if (notification == null) {
+            return in;
+        }
+        notification.put("event-time", "someDate");
+        return doc.toString();
+    }
+
+    static String withFakeXmlDate(final String in) {
+        return in.replaceAll("<eventTime>.*</eventTime>", "<eventTime>someDate</eventTime>");
+    }
+
+    private String getNotifJson(final String path) throws IOException, URISyntaxException, JSONException {
+        return withFakeDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
+    }
+
+    private String getResultXml(final String path) throws IOException, URISyntaxException, JSONException {
+        return withFakeXmlDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
+    }
+
+    private void jsonNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
+            final String jsonNotifCreate, final String jsonNotifUpdate, final String jsonNotifDelete) throws Exception {
+        final var adapter = new ListenerAdapterTester(pathYiid, "Casey",
+                NotificationOutputType.JSON, false, skipData, false, false, listenersBroker);
+        adapter.setCloseVars(domDataBroker, databindProvider);
+
+        final var changeService = domDataBroker.getExtensions()
+                .getInstance(DOMDataTreeChangeService.class);
+        final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, pathYiid);
+        changeService.registerDataTreeChangeListener(root, adapter);
+
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
         MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
-        InstanceIdentifier<MyList1> iid = InstanceIdentifier.create(PatchCont.class)
+        final var iid = InstanceIdentifier.create(PatchCont.class)
                 .child(MyList1.class, new MyList1Key("Althea"));
-        writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_WITHOUT_DATA_CREATE));
+        adapter.assertGot(getNotifJson(jsonNotifCreate));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
         builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
-        writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_WITHOUT_DATA_UPDATE));
+        adapter.assertGot(getNotifJson(jsonNotifUpdate));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_WITHOUT_DATA_DELETE));
+        adapter.assertGot(getNotifJson(jsonNotifDelete));
     }
 
-    @Test
-    public void testXmlLeavesOnly() throws Exception {
-        ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
-            true, false);
+    private void xmlNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
+            final String xmlNotifCreate, final String xmlNotifUpdate, final String xmlNotifDelete) throws Exception {
+        final var adapter = new ListenerAdapterTester(pathYiid, "Casey", NotificationOutputType.XML,
+                false, skipData, false, false, listenersBroker);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
-        DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
+        final var changeService = domDataBroker.getExtensions()
                 .getInstance(DOMDataTreeChangeService.class);
-        DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+        final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, pathYiid);
         changeService.registerDataTreeChangeListener(root, adapter);
+
         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
-        final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
-        PatchContBuilder builder = new PatchContBuilder()
-                .addAugmentation(
-                        new PatchCont1Builder()
-                                .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
-                                .setLeaf1("AugmentLeaf").build())
-                                .setMyList1(
-                                        Map.of(new MyList1Key("Althea"),
-                                                new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build())
-                        );
-        writeTransaction.mergeParentStructurePut(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
+        final var iid = InstanceIdentifier.create(PatchCont.class)
+                .child(MyList1.class, new MyList1Key("Althea"));
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_CREATE));
+        adapter.assertXmlSimilar(getResultXml(xmlNotifCreate));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
-        builder = new PatchContBuilder()
-                .addAugmentation(
-                        new PatchCont1Builder()
-                                .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
-                                .setLeaf1("AugmentLeaf").build())
-                                .setMyList1(
-                                    Map.of(new MyList1Key("Althea"),
-                                                new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build())
-                );
-        writeTransaction.mergeParentStructureMerge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
+        builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
         writeTransaction.commit();
-        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_UPDATE));
+        adapter.assertXmlSimilar(getResultXml(xmlNotifUpdate));
 
         writeTransaction = dataBroker.newWriteOnlyTransaction();
         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
         writeTransaction.commit();
-
-        // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
-        final String notification = adapter.awaitUntillNotification("");
-        assertTrue(notification.contains("instance-identifier-patch-module:my-leaf12"));
-        assertTrue(notification.contains("instance-identifier-patch-module:my-leaf11"));
-        assertTrue(notification.contains("instance-identifier-patch-module:name"));
-        assertTrue(notification.contains("augment-instance-identifier-patch-module:case-leaf1"));
-        assertTrue(notification.contains("augment-instance-identifier-patch-module:leaf1"));
+        adapter.assertXmlSimilar(getResultXml(xmlNotifDelete));
     }
 }