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