5dedfdcf47ad731d595719fe219d72436cc440a1
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / streams / listeners / ListenerAdapterTest.java
1 /*
2  * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.nb.rfc8040.streams.listeners;
9
10 import static org.junit.Assert.assertTrue;
11 import static org.junit.Assert.fail;
12
13 import com.google.common.util.concurrent.Uninterruptibles;
14 import java.io.IOException;
15 import java.net.URISyntaxException;
16 import java.nio.file.Files;
17 import java.nio.file.Paths;
18 import java.util.concurrent.CountDownLatch;
19 import java.util.concurrent.TimeUnit;
20 import org.json.JSONException;
21 import org.json.JSONObject;
22 import org.junit.AfterClass;
23 import org.junit.Before;
24 import org.junit.BeforeClass;
25 import org.junit.Test;
26 import org.opendaylight.mdsal.binding.api.DataBroker;
27 import org.opendaylight.mdsal.binding.api.WriteTransaction;
28 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractConcurrentDataBrokerTest;
29 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
30 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
33 import org.opendaylight.restconf.api.query.ChangedLeafNodesOnlyParam;
34 import org.opendaylight.restconf.api.query.LeafNodesOnlyParam;
35 import org.opendaylight.restconf.api.query.SkipNotificationDataParam;
36 import org.opendaylight.restconf.api.query.StartTimeParam;
37 import org.opendaylight.restconf.nb.rfc8040.NotificationQueryParams;
38 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindContext;
39 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
40 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.PatchCont1Builder;
41 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice1.PatchCase1Builder;
42 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.PatchCase11Builder;
43 import org.opendaylight.yang.gen.v1.augment.instance.identifier.patch.module.rev220218.patch.cont.patch.choice2.patch.case11.patch.sub.choice11.PatchSubCase11Builder;
44 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;
45 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchCont;
46 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.PatchContBuilder;
47 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1;
48 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Builder;
49 import org.opendaylight.yang.gen.v1.instance.identifier.patch.module.rev151121.patch.cont.MyList1Key;
50 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
52 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
53 import org.opendaylight.yangtools.yang.common.QName;
54 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
55 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
56 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
57 import org.skyscreamer.jsonassert.JSONAssert;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60 import org.xmlunit.assertj.XmlAssert;
61
62 public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
63     private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapterTest.class);
64
65     private static final String JSON_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.json";
66     private static final String JSON_NOTIF_LEAVES_UPDATE =  "/listener-adapter-test/notif-leaves-update.json";
67     private static final String JSON_NOTIF_LEAVES_DELETE = "/listener-adapter-test/notif-leaves-delete.json";
68     private static final String JSON_NOTIF_CHANGED_LEAVES_CREATE =
69             "/listener-adapter-test/notif-changed-leaves-create.json";
70     private static final String JSON_NOTIF_CHANGED_LEAVES_UPDATE =
71             "/listener-adapter-test/notif-changed-leaves-update.json";
72     private static final String JSON_NOTIF_CHANGED_LEAVES_DELETE =
73             "/listener-adapter-test/notif-changed-leaves-delete.json";
74
75     private static final String XML_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.xml";
76     private static final String XML_NOTIF_LEAVES_UPDATE =  "/listener-adapter-test/notif-leaves-update.xml";
77     private static final String XML_NOTIF_LEAVES_DELETE =  "/listener-adapter-test/notif-leaves-delete.xml";
78     private static final String XML_NOTIF_CHANGED_LEAVES_CREATE =
79             "/listener-adapter-test/notif-changed-leaves-create.xml";
80     private static final String XML_NOTIF_CHANGED_LEAVES_UPDATE =
81             "/listener-adapter-test/notif-changed-leaves-update.xml";
82     private static final String XML_NOTIF_CHANGED_LEAVES_DELETE =
83             "/listener-adapter-test/notif-changed-leaves-delete.xml";
84
85     private static final String JSON_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.json";
86     private static final String JSON_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.json";
87     private static final String JSON_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.json";
88     private static final String JSON_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.json";
89     private static final String JSON_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.json";
90     private static final String JSON_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.json";
91     private static final String JSON_NOTIF_WITHOUT_DATA_CONT_CREATE =
92             "/listener-adapter-test/notif-without-data-cont-create.json";
93     private static final String JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE =
94             "/listener-adapter-test/notif-without-data-cont-update.json";
95     private static final String JSON_NOTIF_WITHOUT_DATA_CONT_DELETE =
96             "/listener-adapter-test/notif-without-data-cont-delete.json";
97     private static final String JSON_NOTIF_WITHOUT_DATA_LIST_CREATE =
98             "/listener-adapter-test/notif-without-data-list-create.json";
99     private static final String JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE =
100             "/listener-adapter-test/notif-without-data-list-update.json";
101     private static final String JSON_NOTIF_WITHOUT_DATA_LIST_DELETE =
102             "/listener-adapter-test/notif-without-data-list-delete.json";
103
104     private static final String XML_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.xml";
105     private static final String XML_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.xml";
106     private static final String XML_NOTIF_CONT_DELETE = "/listener-adapter-test/notif-cont-delete.xml";
107     private static final String XML_NOTIF_LIST_CREATE = "/listener-adapter-test/notif-list-create.xml";
108     private static final String XML_NOTIF_LIST_UPDATE = "/listener-adapter-test/notif-list-update.xml";
109     private static final String XML_NOTIF_LIST_DELETE = "/listener-adapter-test/notif-list-delete.xml";
110     private static final String XML_NOTIF_WITHOUT_DATA_CONT_CREATE =
111             "/listener-adapter-test/notif-without-data-cont-create.xml";
112     private static final String XML_NOTIF_WITHOUT_DATA_CONT_UPDATE =
113             "/listener-adapter-test/notif-without-data-cont-update.xml";
114     private static final String XML_NOTIF_WITHOUT_DATA_CONT_DELETE =
115             "/listener-adapter-test/notif-without-data-cont-delete.xml";
116     private static final String XML_NOTIF_WITHOUT_DATA_LIST_CREATE =
117             "/listener-adapter-test/notif-without-data-list-create.xml";
118     private static final String XML_NOTIF_WITHOUT_DATA_LIST_UPDATE =
119             "/listener-adapter-test/notif-without-data-list-update.xml";
120     private static final String XML_NOTIF_WITHOUT_DATA_LIST_DELETE =
121             "/listener-adapter-test/notif-without-data-list-delete.xml";
122
123     private static final YangInstanceIdentifier PATCH_CONT_YIID = YangInstanceIdentifier.of(PatchCont.QNAME);
124
125     private static final YangInstanceIdentifier MY_LIST1_YIID = YangInstanceIdentifier.builder()
126             .node(PatchCont.QNAME)
127             .node(MyList1.QNAME)
128             .nodeWithKey(MyList1.QNAME, QName.create(PatchCont.QNAME.getModule(), "name"), "Althea")
129             .build();
130
131     private static EffectiveModelContext SCHEMA_CONTEXT;
132
133     private DataBroker dataBroker;
134     private DOMDataBroker domDataBroker;
135     private DatabindProvider databindProvider;
136
137     @BeforeClass
138     public static void beforeClass() {
139         SCHEMA_CONTEXT = YangParserTestUtils.parseYangResourceDirectory("/instanceidentifier/yang");
140     }
141
142     @AfterClass
143     public static void afterClass() {
144         SCHEMA_CONTEXT = null;
145     }
146
147     @Before
148     public void setUp() throws Exception {
149         dataBroker = getDataBroker();
150         domDataBroker = getDomBroker();
151         databindProvider = () -> DatabindContext.ofModel(SCHEMA_CONTEXT);
152     }
153
154     class ListenerAdapterTester extends ListenerAdapter {
155
156         private volatile String lastNotification;
157         private CountDownLatch notificationLatch = new CountDownLatch(1);
158
159         ListenerAdapterTester(final YangInstanceIdentifier path, final String streamName,
160                               final NotificationOutputType outputType,
161                               final boolean leafNodesOnly, final boolean skipNotificationData) {
162             super(path, streamName, outputType);
163             setQueryParams(NotificationQueryParams.of(StartTimeParam.forUriValue("1970-01-01T00:00:00Z"), null, null,
164                 LeafNodesOnlyParam.of(leafNodesOnly), SkipNotificationDataParam.of(skipNotificationData), null));
165         }
166
167         ListenerAdapterTester(final YangInstanceIdentifier path, final String streamName,
168                               final NotificationOutputType outputType,
169                               final boolean changedLeafNodesOnly) {
170             super(path, streamName, outputType);
171             setQueryParams(NotificationQueryParams.of(StartTimeParam.forUriValue("1970-01-01T00:00:00Z"), null, null,
172                     null, null, ChangedLeafNodesOnlyParam.of(changedLeafNodesOnly)));
173         }
174
175         @Override
176         protected void post(final String data) {
177             lastNotification = data;
178             notificationLatch.countDown();
179         }
180
181         public void assertGot(final String json) throws JSONException {
182             // FIXME: use awaitility
183             if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
184                 fail("Timed out waiting for notification for: " + json);
185             }
186
187             LOG.info("lastNotification: {}", lastNotification);
188             final String withFakeDate = withFakeDate(lastNotification);
189             LOG.info("Comparing: \n{}\n{}", json, withFakeDate);
190
191             JSONAssert.assertEquals(json, withFakeDate, false);
192             lastNotification = null;
193             notificationLatch = new CountDownLatch(1);
194         }
195
196         public void assertXmlSimilar(final String xml) {
197             awaitUntilNotification(xml);
198
199             LOG.info("lastNotification: {}", lastNotification);
200             final String withFakeDate = withFakeXmlDate(lastNotification);
201             LOG.info("Comparing: \n{}\n{}", xml, withFakeDate);
202
203             XmlAssert.assertThat(xml).and(withFakeDate).ignoreWhitespace().ignoreChildNodesOrder().areSimilar();
204             lastNotification = null;
205             notificationLatch = new CountDownLatch(1);
206         }
207
208         public String awaitUntilNotification(final String xml) {
209             // FIXME: use awaitility
210             if (!Uninterruptibles.awaitUninterruptibly(notificationLatch, 500, TimeUnit.SECONDS)) {
211                 fail("Timed out waiting for notification for: " + xml);
212             }
213             return lastNotification;
214         }
215
216         public void resetLatch() {
217             notificationLatch = new CountDownLatch(1);
218         }
219     }
220
221     @Test
222     public void testJsonNotifsLeaves() throws Exception {
223         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
224             true, false);
225         adapter.setCloseVars(domDataBroker, databindProvider);
226
227         final DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
228                 .getInstance(DOMDataTreeChangeService.class);
229         final DOMDataTreeIdentifier root =
230                 new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
231         changeService.registerDataTreeChangeListener(root, adapter);
232
233         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
234         final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
235         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
236             .addAugmentation(new PatchCont1Builder()
237                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
238                 .setLeaf1("AugmentLeaf")
239                 .build())
240             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
241             .build());
242         writeTransaction.commit();
243         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE));
244
245         writeTransaction = dataBroker.newWriteOnlyTransaction();
246         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
247             .addAugmentation(new PatchCont1Builder()
248                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
249                 .setLeaf1("AugmentLeaf")
250                 .build())
251             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
252             .build());
253         writeTransaction.commit();
254         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE));
255
256         writeTransaction = dataBroker.newWriteOnlyTransaction();
257         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
258         writeTransaction.commit();
259         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DELETE));
260     }
261
262     @Test
263     public void testJsonNotifsChangedLeaves() throws Exception {
264         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
265                 true);
266         adapter.setCloseVars(domDataBroker, databindProvider);
267
268         final DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
269                 .getInstance(DOMDataTreeChangeService.class);
270         final DOMDataTreeIdentifier root =
271                 new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
272         changeService.registerDataTreeChangeListener(root, adapter);
273
274         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
275         final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
276         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
277             .addAugmentation(new PatchCont1Builder()
278                 .setPatchChoice2(new PatchCase11Builder()
279                     .setPatchSubChoice11(new PatchSubCase11Builder()
280                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
281                         .build())
282                     .build())
283                 .setLeaf1("AugmentLeaf")
284                 .build())
285             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
286             .build());
287         writeTransaction.commit();
288         adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_CREATE));
289
290         writeTransaction = dataBroker.newWriteOnlyTransaction();
291         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
292             .addAugmentation(new PatchCont1Builder()
293                 .setPatchChoice2(new PatchCase11Builder()
294                     .setPatchSubChoice11(new PatchSubCase11Builder()
295                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
296                         .build())
297                     .build())
298                 .setLeaf1("AugmentLeaf")
299                 .build())
300             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
301             .build());
302         writeTransaction.commit();
303         adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_UPDATE));
304
305         writeTransaction = dataBroker.newWriteOnlyTransaction();
306         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
307         writeTransaction.commit();
308         adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_DELETE));
309     }
310
311     @Test
312     public void testXmlLeavesOnly() throws Exception {
313         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
314             true, false);
315         adapter.setCloseVars(domDataBroker, databindProvider);
316
317         DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
318                 .getInstance(DOMDataTreeChangeService.class);
319         DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
320         changeService.registerDataTreeChangeListener(root, adapter);
321         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
322         final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
323         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
324             .addAugmentation(new PatchCont1Builder()
325                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
326                 .setLeaf1("AugmentLeaf")
327                 .build())
328             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
329             .build());
330         writeTransaction.commit();
331         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_CREATE));
332
333         writeTransaction = dataBroker.newWriteOnlyTransaction();
334         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
335             .addAugmentation(new PatchCont1Builder()
336                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
337                 .setLeaf1("AugmentLeaf")
338                 .build())
339             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
340             .build());
341         writeTransaction.commit();
342         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_UPDATE));
343
344         writeTransaction = dataBroker.newWriteOnlyTransaction();
345         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
346         writeTransaction.commit();
347
348         // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
349         final String notification = adapter.awaitUntilNotification("");
350         assertTrue(notification.contains("instance-identifier-patch-module:my-leaf12"));
351         assertTrue(notification.contains("instance-identifier-patch-module:my-leaf11"));
352         assertTrue(notification.contains("instance-identifier-patch-module:name"));
353         assertTrue(notification.contains("augment-instance-identifier-patch-module:case-leaf1"));
354         assertTrue(notification.contains("augment-instance-identifier-patch-module:leaf1"));
355     }
356
357     @Test
358     public void testXmlChangedLeavesOnly() throws Exception {
359         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
360                 true);
361         adapter.setCloseVars(domDataBroker, databindProvider);
362
363         DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
364                 .getInstance(DOMDataTreeChangeService.class);
365         DOMDataTreeIdentifier root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
366         changeService.registerDataTreeChangeListener(root, adapter);
367         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
368         final InstanceIdentifier<PatchCont> iid = InstanceIdentifier.create(PatchCont.class);
369         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
370             .addAugmentation(new PatchCont1Builder()
371                 .setPatchChoice2(new PatchCase11Builder()
372                     .setPatchSubChoice11(new PatchSubCase11Builder()
373                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
374                         .build())
375                     .build())
376                 .setLeaf1("AugmentLeaf")
377                 .build())
378             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
379             .build());
380         writeTransaction.commit();
381         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_CREATE));
382
383         writeTransaction = dataBroker.newWriteOnlyTransaction();
384         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
385             .addAugmentation(new PatchCont1Builder()
386                 .setPatchChoice2(new PatchCase11Builder()
387                     .setPatchSubChoice11(new PatchSubCase11Builder()
388                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
389                         .build())
390                     .build())
391                 .setLeaf1("AugmentLeaf")
392                 .build())
393             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
394             .build());
395         writeTransaction.commit();
396         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_UPDATE));
397
398         writeTransaction = dataBroker.newWriteOnlyTransaction();
399         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
400         writeTransaction.commit();
401         // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
402         final String notification = adapter.awaitUntilNotification("");
403
404         assertTrue(notification.contains("instance-identifier-patch-module:my-leaf11"));
405         assertTrue(notification.contains("instance-identifier-patch-module:my-leaf12"));
406         assertTrue(notification.contains("instance-identifier-patch-module:name"));
407         assertTrue(notification.contains("augment-instance-identifier-patch-module:case-leaf11"));
408         assertTrue(notification.contains("augment-instance-identifier-patch-module:patch-choice2"));
409         assertTrue(notification.contains("augment-instance-identifier-patch-module:patch-sub-choice11"));
410         assertTrue(notification.contains("augment-instance-identifier-patch-module:patch-sub-sub-choice11"));
411         assertTrue(notification.contains("augment-instance-identifier-patch-module:leaf1"));
412     }
413
414     @Test
415     public void testJsonContNotifications() throws Exception {
416         jsonNotifications(PATCH_CONT_YIID, false, JSON_NOTIF_CONT_CREATE,
417                 JSON_NOTIF_CONT_UPDATE, JSON_NOTIF_CONT_DELETE);
418     }
419
420     @Test
421     public void testJsonListNotifications() throws Exception {
422         jsonNotifications(MY_LIST1_YIID, false, JSON_NOTIF_LIST_CREATE,
423                 JSON_NOTIF_LIST_UPDATE, JSON_NOTIF_LIST_DELETE);
424     }
425
426     @Test
427     public void testJsonContNotificationsWithoutData() throws Exception {
428         jsonNotifications(PATCH_CONT_YIID, true, JSON_NOTIF_WITHOUT_DATA_CONT_CREATE,
429                 JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE, JSON_NOTIF_WITHOUT_DATA_CONT_DELETE);
430     }
431
432     @Test
433     public void testJsonListNotificationsWithoutData() throws Exception {
434         jsonNotifications(MY_LIST1_YIID, true, JSON_NOTIF_WITHOUT_DATA_LIST_CREATE,
435                 JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE, JSON_NOTIF_WITHOUT_DATA_LIST_DELETE);
436     }
437
438     @Test
439     public void testXmlContNotifications() throws Exception {
440         xmlNotifications(PATCH_CONT_YIID, false, XML_NOTIF_CONT_CREATE,
441                 XML_NOTIF_CONT_UPDATE, XML_NOTIF_CONT_DELETE);
442     }
443
444     @Test
445     public void testXmlListNotifications() throws Exception {
446         xmlNotifications(MY_LIST1_YIID, false, XML_NOTIF_LIST_CREATE,
447                 XML_NOTIF_LIST_UPDATE, XML_NOTIF_LIST_DELETE);
448     }
449
450     @Test
451     public void testXmlContNotificationsWithoutData() throws Exception {
452         xmlNotifications(PATCH_CONT_YIID, true, XML_NOTIF_WITHOUT_DATA_CONT_CREATE,
453                 XML_NOTIF_WITHOUT_DATA_CONT_UPDATE, XML_NOTIF_WITHOUT_DATA_CONT_DELETE);
454     }
455
456     @Test
457     public void testXmlListNotificationsWithoutData() throws Exception {
458         xmlNotifications(MY_LIST1_YIID, true, XML_NOTIF_WITHOUT_DATA_LIST_CREATE,
459                 XML_NOTIF_WITHOUT_DATA_LIST_UPDATE, XML_NOTIF_WITHOUT_DATA_LIST_DELETE);
460     }
461
462     static String withFakeDate(final String in) throws JSONException {
463         final JSONObject doc = new JSONObject(in);
464         final JSONObject notification =
465                 doc.getJSONObject("urn-ietf-params-xml-ns-netconf-notification-1.0:notification");
466         if (notification == null) {
467             return in;
468         }
469         notification.put("event-time", "someDate");
470         return doc.toString();
471     }
472
473     static String withFakeXmlDate(final String in) {
474         return in.replaceAll("<eventTime>.*</eventTime>", "<eventTime>someDate</eventTime>");
475     }
476
477     private String getNotifJson(final String path) throws IOException, URISyntaxException, JSONException {
478         return withFakeDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
479     }
480
481     private String getResultXml(final String path) throws IOException, URISyntaxException, JSONException {
482         return withFakeXmlDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
483     }
484
485     private void jsonNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
486             final String jsonNotifCreate, final String jsonNotifUpdate, final String jsonNotifDelete) throws Exception {
487         final var adapter = new ListenerAdapterTester(pathYiid, "Casey",
488                 NotificationOutputType.JSON, false, skipData);
489         adapter.setCloseVars(domDataBroker, databindProvider);
490
491         final var changeService = domDataBroker.getExtensions()
492                 .getInstance(DOMDataTreeChangeService.class);
493         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, pathYiid);
494         changeService.registerDataTreeChangeListener(root, adapter);
495
496         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
497         MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
498         final var iid = InstanceIdentifier.create(PatchCont.class)
499                 .child(MyList1.class, new MyList1Key("Althea"));
500         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
501         writeTransaction.commit();
502         adapter.assertGot(getNotifJson(jsonNotifCreate));
503
504         writeTransaction = dataBroker.newWriteOnlyTransaction();
505         builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
506         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
507         writeTransaction.commit();
508         adapter.assertGot(getNotifJson(jsonNotifUpdate));
509
510         writeTransaction = dataBroker.newWriteOnlyTransaction();
511         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
512         writeTransaction.commit();
513         adapter.assertGot(getNotifJson(jsonNotifDelete));
514     }
515
516     private void xmlNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
517             final String xmlNotifCreate, final String xmlNotifUpdate, final String xmlNotifDelete) throws Exception {
518         final var adapter = new ListenerAdapterTester(pathYiid, "Casey", NotificationOutputType.XML,
519                 false, skipData);
520         adapter.setCloseVars(domDataBroker, databindProvider);
521
522         final var changeService = domDataBroker.getExtensions()
523                 .getInstance(DOMDataTreeChangeService.class);
524         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, pathYiid);
525         changeService.registerDataTreeChangeListener(root, adapter);
526
527         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
528         MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
529         final var iid = InstanceIdentifier.create(PatchCont.class)
530                 .child(MyList1.class, new MyList1Key("Althea"));
531         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
532         writeTransaction.commit();
533         adapter.assertXmlSimilar(getResultXml(xmlNotifCreate));
534
535         writeTransaction = dataBroker.newWriteOnlyTransaction();
536         builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
537         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
538         writeTransaction.commit();
539         adapter.assertXmlSimilar(getResultXml(xmlNotifUpdate));
540
541         writeTransaction = dataBroker.newWriteOnlyTransaction();
542         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
543         writeTransaction.commit();
544         adapter.assertXmlSimilar(getResultXml(xmlNotifDelete));
545     }
546 }