Clean up adapter constructors
[netconf.git] / restconf / restconf-nb / src / test / java / org / opendaylight / restconf / nb / rfc8040 / streams / 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;
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.ServerSentEvents();
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(streamName, outputType, listenersBroker, LogicalDatastoreType.CONFIGURATION, path);
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 var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
244         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
245         changeService.registerDataTreeChangeListener(root, adapter);
246
247         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
248         final var iid = InstanceIdentifier.create(PatchCont.class);
249         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
250             .addAugmentation(new PatchCont1Builder()
251                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
252                 .setLeaf1("AugmentLeaf")
253                 .build())
254             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
255             .build());
256         writeTransaction.commit();
257         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_CREATE));
258
259         writeTransaction = dataBroker.newWriteOnlyTransaction();
260         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
261             .addAugmentation(new PatchCont1Builder()
262                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
263                 .setLeaf1("AugmentLeaf")
264                 .build())
265             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
266             .build());
267         writeTransaction.commit();
268         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_UPDATE));
269
270         writeTransaction = dataBroker.newWriteOnlyTransaction();
271         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
272         writeTransaction.commit();
273         adapter.assertGot(getNotifJson(JSON_NOTIF_LEAVES_DELETE));
274     }
275
276     @Test
277     public void testJsonNotifsChangedLeaves() throws Exception {
278         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
279                 false, false, true, false, listenersBroker);
280         adapter.setCloseVars(domDataBroker, databindProvider);
281
282         final var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
283         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
284         changeService.registerDataTreeChangeListener(root, adapter);
285
286         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
287         final var iid = InstanceIdentifier.create(PatchCont.class);
288         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
289             .addAugmentation(new PatchCont1Builder()
290                 .setPatchChoice2(new PatchCase11Builder()
291                     .setPatchSubChoice11(new PatchSubCase11Builder()
292                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
293                         .build())
294                     .build())
295                 .setLeaf1("AugmentLeaf")
296                 .build())
297             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
298             .build());
299         writeTransaction.commit();
300         adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_CREATE));
301
302         writeTransaction = dataBroker.newWriteOnlyTransaction();
303         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
304             .addAugmentation(new PatchCont1Builder()
305                 .setPatchChoice2(new PatchCase11Builder()
306                     .setPatchSubChoice11(new PatchSubCase11Builder()
307                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
308                         .build())
309                     .build())
310                 .setLeaf1("AugmentLeaf")
311                 .build())
312             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
313             .build());
314         writeTransaction.commit();
315         adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_UPDATE));
316
317         writeTransaction = dataBroker.newWriteOnlyTransaction();
318         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
319         writeTransaction.commit();
320         adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_DELETE));
321     }
322
323     @Test
324     public void testJsonChildNodesOnly() throws Exception {
325         final var adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
326             NotificationOutputType.JSON, false, false, false, true, listenersBroker);
327         adapter.setCloseVars(domDataBroker, databindProvider);
328
329         final var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
330         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
331         changeService.registerDataTreeChangeListener(root, adapter);
332
333         final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
334         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
335         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
336             new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
337         writeTransaction.commit();
338         adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_CREATE));
339
340         writeTransaction = dataBroker.newWriteOnlyTransaction();
341         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
342             new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
343         writeTransaction.commit();
344         adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1));
345
346         writeTransaction = dataBroker.newWriteOnlyTransaction();
347         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
348             new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
349         writeTransaction.commit();
350         adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2));
351
352         writeTransaction = dataBroker.newWriteOnlyTransaction();
353         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
354         writeTransaction.commit();
355         adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_DELETE));
356     }
357
358     @Test
359     public void testXmlLeavesOnly() throws Exception {
360         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
361             true, false, false, false, listenersBroker);
362         adapter.setCloseVars(domDataBroker, databindProvider);
363
364         final var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
365         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
366         changeService.registerDataTreeChangeListener(root, adapter);
367         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
368         final var iid = InstanceIdentifier.create(PatchCont.class);
369         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
370             .addAugmentation(new PatchCont1Builder()
371                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceLeaf").build())
372                 .setLeaf1("AugmentLeaf")
373                 .build())
374             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
375             .build());
376         writeTransaction.commit();
377         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_CREATE));
378
379         writeTransaction = dataBroker.newWriteOnlyTransaction();
380         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
381             .addAugmentation(new PatchCont1Builder()
382                 .setPatchChoice1(new PatchCase1Builder().setCaseLeaf1("ChoiceUpdate").build())
383                 .setLeaf1("AugmentLeaf")
384                 .build())
385             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
386             .build());
387         writeTransaction.commit();
388         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_LEAVES_UPDATE));
389
390         writeTransaction = dataBroker.newWriteOnlyTransaction();
391         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
392         writeTransaction.commit();
393
394         // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
395         final String notification = adapter.awaitUntilNotification("");
396         assertThat(notification, allOf(
397             containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
398                 + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
399             containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
400                 + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
401             containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
402                 + "/a:my-list1[a:name='Althea']/a:name</path>"),
403             containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
404                 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
405             containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
406                 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf1</path>")));
407     }
408
409     @Test
410     public void testXmlChangedLeavesOnly() throws Exception {
411         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
412                 false, false, true, false, listenersBroker);
413         adapter.setCloseVars(domDataBroker, databindProvider);
414
415         final var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
416         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
417         changeService.registerDataTreeChangeListener(root, adapter);
418         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
419         final var iid = InstanceIdentifier.create(PatchCont.class);
420         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
421             .addAugmentation(new PatchCont1Builder()
422                 .setPatchChoice2(new PatchCase11Builder()
423                     .setPatchSubChoice11(new PatchSubCase11Builder()
424                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceLeaf").build())
425                         .build())
426                     .build())
427                 .setLeaf1("AugmentLeaf")
428                 .build())
429             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build()))
430             .build());
431         writeTransaction.commit();
432         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_CREATE));
433
434         writeTransaction = dataBroker.newWriteOnlyTransaction();
435         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, new PatchContBuilder()
436             .addAugmentation(new PatchCont1Builder()
437                 .setPatchChoice2(new PatchCase11Builder()
438                     .setPatchSubChoice11(new PatchSubCase11Builder()
439                         .setPatchSubSubChoice11(new PatchSubSubCase11Builder().setCaseLeaf11("ChoiceUpdate").build())
440                         .build())
441                     .build())
442                 .setLeaf1("AugmentLeaf")
443                 .build())
444             .setMyList1(BindingMap.of(new MyList1Builder().setMyLeaf12("Bertha").setName("Althea").build()))
445             .build());
446         writeTransaction.commit();
447         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHANGED_LEAVES_UPDATE));
448
449         writeTransaction = dataBroker.newWriteOnlyTransaction();
450         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
451         writeTransaction.commit();
452
453         // xmlunit cannot compare deeper children it seems out of the box so just check the iid encoding
454         final String notification = adapter.awaitUntilNotification("");
455         assertThat(notification, allOf(
456             containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
457                 + "/a:my-list1[a:name='Althea']/a:my-leaf11</path>"),
458             containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
459                 + "/a:my-list1[a:name='Althea']/a:my-leaf12</path>"),
460             containsString("<path xmlns:a=\"instance:identifier:patch:module\">/a:patch-cont"
461                 + "/a:my-list1[a:name='Althea']/a:name</path>"),
462             containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
463                 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:leaf1</path>"),
464             containsString("<path xmlns:a=\"instance:identifier:patch:module\" "
465                 + "xmlns:b=\"augment:instance:identifier:patch:module\">/a:patch-cont/b:case-leaf11</path>")));
466     }
467
468     @Test
469     public void testXmlChildNodesOnly() throws Exception {
470         final var adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
471             NotificationOutputType.XML, false, false, false, true, listenersBroker);
472         adapter.setCloseVars(domDataBroker, databindProvider);
473
474         final var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
475         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
476         changeService.registerDataTreeChangeListener(root, adapter);
477
478         final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
479         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
480         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
481             new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
482         writeTransaction.commit();
483         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_CREATE));
484
485         writeTransaction = dataBroker.newWriteOnlyTransaction();
486         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
487             new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
488         writeTransaction.commit();
489         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE1));
490
491         writeTransaction = dataBroker.newWriteOnlyTransaction();
492         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
493             new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
494         writeTransaction.commit();
495         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE2));
496
497         writeTransaction = dataBroker.newWriteOnlyTransaction();
498         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
499         writeTransaction.commit();
500         adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_DELETE));
501     }
502
503     @Test
504     public void testJsonContNotifications() throws Exception {
505         jsonNotifications(PATCH_CONT_YIID, false, JSON_NOTIF_CONT_CREATE,
506                 JSON_NOTIF_CONT_UPDATE, JSON_NOTIF_CONT_DELETE);
507     }
508
509     @Test
510     public void testJsonListNotifications() throws Exception {
511         jsonNotifications(MY_LIST1_YIID, false, JSON_NOTIF_LIST_CREATE,
512                 JSON_NOTIF_LIST_UPDATE, JSON_NOTIF_LIST_DELETE);
513     }
514
515     @Test
516     public void testJsonContNotificationsWithoutData() throws Exception {
517         jsonNotifications(PATCH_CONT_YIID, true, JSON_NOTIF_WITHOUT_DATA_CONT_CREATE,
518                 JSON_NOTIF_WITHOUT_DATA_CONT_UPDATE, JSON_NOTIF_WITHOUT_DATA_CONT_DELETE);
519     }
520
521     @Test
522     public void testJsonListNotificationsWithoutData() throws Exception {
523         jsonNotifications(MY_LIST1_YIID, true, JSON_NOTIF_WITHOUT_DATA_LIST_CREATE,
524                 JSON_NOTIF_WITHOUT_DATA_LIST_UPDATE, JSON_NOTIF_WITHOUT_DATA_LIST_DELETE);
525     }
526
527     @Test
528     public void testXmlContNotifications() throws Exception {
529         xmlNotifications(PATCH_CONT_YIID, false, XML_NOTIF_CONT_CREATE,
530                 XML_NOTIF_CONT_UPDATE, XML_NOTIF_CONT_DELETE);
531     }
532
533     @Test
534     public void testXmlListNotifications() throws Exception {
535         xmlNotifications(MY_LIST1_YIID, false, XML_NOTIF_LIST_CREATE,
536                 XML_NOTIF_LIST_UPDATE, XML_NOTIF_LIST_DELETE);
537     }
538
539     @Test
540     public void testXmlContNotificationsWithoutData() throws Exception {
541         xmlNotifications(PATCH_CONT_YIID, true, XML_NOTIF_WITHOUT_DATA_CONT_CREATE,
542                 XML_NOTIF_WITHOUT_DATA_CONT_UPDATE, XML_NOTIF_WITHOUT_DATA_CONT_DELETE);
543     }
544
545     @Test
546     public void testXmlListNotificationsWithoutData() throws Exception {
547         xmlNotifications(MY_LIST1_YIID, true, XML_NOTIF_WITHOUT_DATA_LIST_CREATE,
548                 XML_NOTIF_WITHOUT_DATA_LIST_UPDATE, XML_NOTIF_WITHOUT_DATA_LIST_DELETE);
549     }
550
551     static String withFakeDate(final String in) throws JSONException {
552         final JSONObject doc = new JSONObject(in);
553         final JSONObject notification = doc.getJSONObject("ietf-restconf:notification");
554         if (notification == null) {
555             return in;
556         }
557         notification.put("event-time", "someDate");
558         return doc.toString();
559     }
560
561     static String withFakeXmlDate(final String in) {
562         return in.replaceAll("<eventTime>.*</eventTime>", "<eventTime>someDate</eventTime>");
563     }
564
565     private String getNotifJson(final String path) throws IOException, URISyntaxException, JSONException {
566         return withFakeDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
567     }
568
569     private String getResultXml(final String path) throws IOException, URISyntaxException, JSONException {
570         return withFakeXmlDate(Files.readString(Paths.get(getClass().getResource(path).toURI())));
571     }
572
573     private void jsonNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
574             final String jsonNotifCreate, final String jsonNotifUpdate, final String jsonNotifDelete) throws Exception {
575         final var adapter = new ListenerAdapterTester(pathYiid, "Casey",
576                 NotificationOutputType.JSON, false, skipData, false, false, listenersBroker);
577         adapter.setCloseVars(domDataBroker, databindProvider);
578
579         final var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
580         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, pathYiid);
581         changeService.registerDataTreeChangeListener(root, adapter);
582
583         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
584         MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
585         final var iid = InstanceIdentifier.create(PatchCont.class)
586                 .child(MyList1.class, new MyList1Key("Althea"));
587         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
588         writeTransaction.commit();
589         adapter.assertGot(getNotifJson(jsonNotifCreate));
590
591         writeTransaction = dataBroker.newWriteOnlyTransaction();
592         builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
593         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
594         writeTransaction.commit();
595         adapter.assertGot(getNotifJson(jsonNotifUpdate));
596
597         writeTransaction = dataBroker.newWriteOnlyTransaction();
598         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
599         writeTransaction.commit();
600         adapter.assertGot(getNotifJson(jsonNotifDelete));
601     }
602
603     private void xmlNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
604             final String xmlNotifCreate, final String xmlNotifUpdate, final String xmlNotifDelete) throws Exception {
605         final var adapter = new ListenerAdapterTester(pathYiid, "Casey", NotificationOutputType.XML,
606                 false, skipData, false, false, listenersBroker);
607         adapter.setCloseVars(domDataBroker, databindProvider);
608
609         final var changeService = domDataBroker.getExtensions().getInstance(DOMDataTreeChangeService.class);
610         final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, pathYiid);
611         changeService.registerDataTreeChangeListener(root, adapter);
612
613         WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
614         MyList1Builder builder = new MyList1Builder().setMyLeaf11("Jed").setName("Althea");
615         final var iid = InstanceIdentifier.create(PatchCont.class)
616                 .child(MyList1.class, new MyList1Key("Althea"));
617         writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
618         writeTransaction.commit();
619         adapter.assertXmlSimilar(getResultXml(xmlNotifCreate));
620
621         writeTransaction = dataBroker.newWriteOnlyTransaction();
622         builder = new MyList1Builder().withKey(new MyList1Key("Althea")).setMyLeaf12("Bertha");
623         writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid, builder.build());
624         writeTransaction.commit();
625         adapter.assertXmlSimilar(getResultXml(xmlNotifUpdate));
626
627         writeTransaction = dataBroker.newWriteOnlyTransaction();
628         writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
629         writeTransaction.commit();
630         adapter.assertXmlSimilar(getResultXml(xmlNotifDelete));
631     }
632 }