258eacee00aceeca46e4d5b68d556273b0192afa
[mdsal.git] / binding / mdsal-binding-dom-adapter / src / test / java / org / opendaylight / mdsal / binding / dom / adapter / Mdsal298Test.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.ro.. 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.mdsal.binding.dom.adapter;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.mockito.ArgumentMatchers.anyCollection;
12 import static org.mockito.Mockito.doNothing;
13 import static org.mockito.Mockito.mock;
14 import static org.mockito.Mockito.reset;
15 import static org.mockito.Mockito.verify;
16 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
17
18 import java.util.Collection;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.concurrent.ExecutionException;
22 import org.junit.Test;
23 import org.mockito.ArgumentCaptor;
24 import org.opendaylight.mdsal.binding.api.DataObjectModification;
25 import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
26 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
27 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
28 import org.opendaylight.mdsal.binding.api.DataTreeModification;
29 import org.opendaylight.mdsal.binding.api.WriteTransaction;
30 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
32 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.AddressableCont;
33 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.AddressableContBuilder;
34 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.Container;
35 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.ContainerBuilder;
36 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.UnaddressableCont;
37 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.UnaddressableContBuilder;
38 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.WithChoice;
39 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.WithChoiceBuilder;
40 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.addressable.cont.AddressableChild;
41 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.addressable.cont.AddressableChildBuilder;
42 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Keyed;
43 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedBuilder;
44 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedKey;
45 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Unkeyed;
46 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.UnkeyedBuilder;
47 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.with.choice.Foo;
48 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.with.choice.foo.addressable._case.Addressable;
49 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.with.choice.foo.addressable._case.AddressableBuilder;
50 import org.opendaylight.yangtools.yang.binding.ChildOf;
51 import org.opendaylight.yangtools.yang.binding.DataRoot;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.binding.NodeStep;
54 import org.opendaylight.yangtools.yang.common.QName;
55 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
56 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
57 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
58 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
59
60 public class Mdsal298Test extends AbstractDataBrokerTest {
61     private static final InstanceIdentifier<Container> CONTAINER = InstanceIdentifier.create(Container.class);
62     private static final DataTreeIdentifier<Container> CONTAINER_TID = DataTreeIdentifier.of(CONFIGURATION,
63         CONTAINER);
64     private static final NodeIdentifier CONTAINER_NID = new NodeIdentifier(Container.QNAME);
65     private static final QName FOO_QNAME = QName.create(Container.QNAME, "foo");
66     private static final QName BAZ_QNAME = QName.create(UnaddressableCont.QNAME, "baz");
67
68     private static final InstanceIdentifier<WithChoice> CHOICE_CONTAINER = InstanceIdentifier.create(WithChoice.class);
69     private static final DataTreeIdentifier<WithChoice> CHOICE_CONTAINER_TID = DataTreeIdentifier.of(CONFIGURATION,
70         CHOICE_CONTAINER);
71     private static final NodeIdentifier CHOICE_CONTAINER_NID = new NodeIdentifier(WithChoice.QNAME);
72     private static final NodeIdentifier CHOICE_NID = new NodeIdentifier(Foo.QNAME);
73     private static final InstanceIdentifier<Addressable> ADDRESSABLE_CASE = CHOICE_CONTAINER
74             .child((Class)Addressable.class);
75
76     private static final InstanceIdentifier<AddressableCont> ADDRESSABLE_CONTAINER =
77             InstanceIdentifier.create(AddressableCont.class);
78     private static final DataTreeIdentifier<AddressableCont> ADDRESSABLE_CONTAINER_TID = DataTreeIdentifier.of(
79         CONFIGURATION, ADDRESSABLE_CONTAINER);
80     private static final NodeIdentifier ADDRESSABLE_CONTAINER_NID = new NodeIdentifier(AddressableCont.QNAME);
81
82     private static final InstanceIdentifier<UnaddressableCont> UNADDRESSABLE_CONTAINER =
83             InstanceIdentifier.create(UnaddressableCont.class);
84     private static final DataTreeIdentifier<UnaddressableCont> UNADDRESSABLE_CONTAINER_TID = DataTreeIdentifier.of(
85         CONFIGURATION, UNADDRESSABLE_CONTAINER);
86     private static final NodeIdentifier UNADDRESSABLE_CONTAINER_NID = new NodeIdentifier(UnaddressableCont.QNAME);
87
88     @Test
89     public void testKeyedDataTreeModification() throws InterruptedException, ExecutionException {
90         final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
91             new ContainerBuilder().build());
92
93         final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
94         domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CONTAINER_NID).node(Keyed.QNAME),
95             ImmutableNodes.newUserMapBuilder()
96                 .withNodeIdentifier(new NodeIdentifier(Keyed.QNAME))
97                 .addChild(ImmutableNodes.newMapEntryBuilder()
98                     .withNodeIdentifier(NodeIdentifierWithPredicates.of(Keyed.QNAME, FOO_QNAME, "foo"))
99                     .addChild(ImmutableNodes.leafNode(FOO_QNAME, "foo"))
100                     .build())
101                 .addChild(ImmutableNodes.newMapEntryBuilder()
102                     .withNodeIdentifier(NodeIdentifierWithPredicates.of(Keyed.QNAME, FOO_QNAME, "bar"))
103                     .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
104                     .build())
105                 .build());
106         domTx.commit().get();
107
108         final var captor = ArgumentCaptor.forClass(Collection.class);
109         verify(listener).onDataTreeChanged(captor.capture());
110         Collection<DataTreeModification<Container>> capture = captor.getValue();
111         assertEquals(1, capture.size());
112
113         final DataTreeModification<Container> change = capture.iterator().next();
114         assertEquals(CONTAINER_TID, change.getRootPath());
115         final DataObjectModification<Container> changedContainer = change.getRootNode();
116         assertEquals(new NodeStep<>(Container.class), changedContainer.getIdentifier());
117         assertEquals(ModificationType.SUBTREE_MODIFIED, changedContainer.getModificationType());
118
119         final Container containerAfter = changedContainer.getDataAfter();
120         assertEquals(new ContainerBuilder()
121             .setKeyed(List.of(
122                 new KeyedBuilder().setFoo("foo").withKey(new KeyedKey("foo")).build(),
123                 new KeyedBuilder().setFoo("bar").withKey(new KeyedKey("bar")).build()))
124             .build(), containerAfter);
125
126         final Collection<? extends DataObjectModification<?>> changedChildren = changedContainer.getModifiedChildren();
127         assertEquals(2, changedChildren.size());
128
129         final Iterator<? extends DataObjectModification<?>> it = changedChildren.iterator();
130         final DataObjectModification<?> changedChild1 = it.next();
131         assertEquals(ModificationType.WRITE, changedChild1.getModificationType());
132         assertEquals(List.of(), changedChild1.getModifiedChildren());
133         final Keyed child1After = (Keyed) changedChild1.getDataAfter();
134         assertEquals("foo", child1After.getFoo());
135
136         final DataObjectModification<?> changedChild2 = it.next();
137         assertEquals(ModificationType.WRITE, changedChild2.getModificationType());
138         assertEquals(List.of(), changedChild2.getModifiedChildren());
139         final Keyed child2After = (Keyed) changedChild2.getDataAfter();
140         assertEquals("bar", child2After.getFoo());
141     }
142
143     @Test
144     public void testUnkeyedDataTreeModification() throws InterruptedException, ExecutionException {
145         final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
146             new ContainerBuilder().build());
147
148         final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
149         domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CONTAINER_NID).node(Unkeyed.QNAME),
150             ImmutableNodes.newUnkeyedListBuilder()
151                 .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
152                 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
153                     .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
154                     .addChild(ImmutableNodes.leafNode(FOO_QNAME, "foo"))
155                     .build())
156                 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
157                     .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
158                     .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
159                     .build())
160                 .build());
161         domTx.commit().get();
162
163         final var captor = ArgumentCaptor.forClass(Collection.class);
164         verify(listener).onDataTreeChanged(captor.capture());
165         Collection<DataTreeModification<Container>> capture = captor.getValue();
166         assertEquals(1, capture.size());
167
168         final DataTreeModification<Container> change = capture.iterator().next();
169         assertEquals(CONTAINER_TID, change.getRootPath());
170         final DataObjectModification<Container> changedContainer = change.getRootNode();
171         assertEquals(new NodeStep<>(Container.class), changedContainer.getIdentifier());
172         assertEquals(ModificationType.WRITE, changedContainer.getModificationType());
173
174         final Container containerAfter = changedContainer.getDataAfter();
175         assertEquals(new ContainerBuilder()
176                 .setUnkeyed(List.of(
177                     new UnkeyedBuilder().setFoo("foo").build(),
178                     new UnkeyedBuilder().setFoo("bar").build()))
179                 .build(), containerAfter);
180
181         final Collection<? extends DataObjectModification<?>> changedChildren = changedContainer.getModifiedChildren();
182         assertEquals(0, changedChildren.size());
183     }
184
185     @Test
186     public void testChoiceDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
187         final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
188
189         doNothing().when(listener).onDataTreeChanged(anyCollection());
190
191         final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
192         writeTx.put(CONFIGURATION, ADDRESSABLE_CASE, new AddressableBuilder().build());
193         writeTx.commit().get();
194
195         final var captor = ArgumentCaptor.forClass(Collection.class);
196         verify(listener).onDataTreeChanged(captor.capture());
197         Collection<DataTreeModification<WithChoice>> capture = captor.getValue();
198         assertEquals(1, capture.size());
199
200         final DataTreeModification<WithChoice> choiceChange = capture.iterator().next();
201         assertEquals(CHOICE_CONTAINER_TID, choiceChange.getRootPath());
202         final DataObjectModification<WithChoice> changedContainer = choiceChange.getRootNode();
203         assertEquals(ModificationType.SUBTREE_MODIFIED, changedContainer.getModificationType());
204         assertEquals(new NodeStep<>(WithChoice.class), changedContainer.getIdentifier());
205
206         final Collection<? extends DataObjectModification<?>> choiceChildren = changedContainer.getModifiedChildren();
207         assertEquals(1, choiceChildren.size());
208
209         final DataObjectModification<Addressable> changedCase = (DataObjectModification<Addressable>) choiceChildren
210                 .iterator().next();
211         assertEquals(ModificationType.WRITE, changedCase.getModificationType());
212         assertEquals(new NodeStep<>(Addressable.class), changedCase.getIdentifier());
213         assertEquals(new AddressableBuilder().build(), changedCase.getDataAfter());
214     }
215
216     @Test
217     public void testDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
218         final DataTreeChangeListener<AddressableCont> listener = assertWrittenContainer(AddressableCont.QNAME,
219             AddressableCont.class, new AddressableContBuilder().build());
220
221         doNothing().when(listener).onDataTreeChanged(anyCollection());
222
223         final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
224         writeTx.put(CONFIGURATION, ADDRESSABLE_CONTAINER.child(AddressableChild.class),
225             new AddressableChildBuilder().build());
226         writeTx.commit().get();
227
228         final var captor = ArgumentCaptor.forClass(Collection.class);
229         verify(listener).onDataTreeChanged(captor.capture());
230         Collection<DataTreeModification<AddressableCont>> capture = captor.getValue();
231         assertEquals(1, capture.size());
232
233         final DataTreeModification<AddressableCont> contChange = capture.iterator().next();
234         assertEquals(ADDRESSABLE_CONTAINER_TID, contChange.getRootPath());
235         final DataObjectModification<AddressableCont> changedContainer = contChange.getRootNode();
236         assertEquals(ModificationType.SUBTREE_MODIFIED, changedContainer.getModificationType());
237         assertEquals(new NodeStep<>(AddressableCont.class), changedContainer.getIdentifier());
238
239         final Collection<? extends DataObjectModification<?>> contChildren = changedContainer.getModifiedChildren();
240         assertEquals(1, contChildren.size());
241
242         final DataObjectModification<Addressable> changedChild = (DataObjectModification<Addressable>) contChildren
243                 .iterator().next();
244         assertEquals(ModificationType.WRITE, changedChild.getModificationType());
245         assertEquals(new NodeStep<>(AddressableChild.class), changedChild.getIdentifier());
246         assertEquals(new AddressableChildBuilder().build(), changedChild.getDataAfter());
247     }
248
249     @Test
250     public void testDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
251         final DataTreeChangeListener<UnaddressableCont> listener = assertWrittenContainer(UnaddressableCont.QNAME,
252             UnaddressableCont.class, new UnaddressableContBuilder().build());
253
254         doNothing().when(listener).onDataTreeChanged(anyCollection());
255
256         final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
257         domTx.put(CONFIGURATION, YangInstanceIdentifier.of(UNADDRESSABLE_CONTAINER_NID)
258             .node(QName.create(UnaddressableCont.QNAME, "baz")),
259             ImmutableNodes.leafNode(BAZ_QNAME, "baz"));
260         domTx.commit().get();
261
262         final var captor = ArgumentCaptor.forClass(Collection.class);
263         verify(listener).onDataTreeChanged(captor.capture());
264         Collection<DataTreeModification<UnaddressableCont>> capture = captor.getValue();
265         assertEquals(1, capture.size());
266
267         final DataTreeModification<UnaddressableCont> contChange = capture.iterator().next();
268         assertEquals(UNADDRESSABLE_CONTAINER_TID, contChange.getRootPath());
269         final DataObjectModification<UnaddressableCont> changedContainer = contChange.getRootNode();
270         assertEquals(ModificationType.WRITE, changedContainer.getModificationType());
271         assertEquals(new NodeStep<>(UnaddressableCont.class), changedContainer.getIdentifier());
272
273         final Collection<? extends DataObjectModification<?>> contChildren = changedContainer.getModifiedChildren();
274         assertEquals(0, contChildren.size());
275     }
276
277     @Test
278     public void testChoiceDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
279         final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
280
281         doNothing().when(listener).onDataTreeChanged(anyCollection());
282
283         final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
284         domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CHOICE_CONTAINER_NID).node(Foo.QNAME),
285             ImmutableNodes.newChoiceBuilder()
286                 .withNodeIdentifier(new NodeIdentifier(Foo.QNAME))
287                 .withChild(ImmutableNodes.newSystemLeafSetBuilder()
288                     .withNodeIdentifier(new NodeIdentifier(QName.create(Foo.QNAME, "unaddressable")))
289                     .withChildValue("foo")
290                     .build())
291                 .build());
292         domTx.commit().get();
293
294         final var captor = ArgumentCaptor.forClass(Collection.class);
295         verify(listener).onDataTreeChanged(captor.capture());
296         Collection<DataTreeModification<WithChoice>> capture = captor.getValue();
297         assertEquals(1, capture.size());
298
299         final DataTreeModification<WithChoice> choiceChange = capture.iterator().next();
300         assertEquals(CHOICE_CONTAINER_TID, choiceChange.getRootPath());
301         final DataObjectModification<WithChoice> changedContainer = choiceChange.getRootNode();
302
303         // Should be write
304         assertEquals(ModificationType.WRITE, changedContainer.getModificationType());
305         assertEquals(new NodeStep<>(WithChoice.class), changedContainer.getIdentifier());
306
307         final Collection<? extends DataObjectModification<?>> choiceChildren = changedContainer.getModifiedChildren();
308         assertEquals(0, choiceChildren.size());
309     }
310
311     private <T extends ChildOf<? extends DataRoot>> DataTreeChangeListener<T> assertWrittenContainer(final QName qname,
312             final Class<T> bindingClass, final T expected)
313             throws InterruptedException, ExecutionException {
314         final DataTreeChangeListener<T> listener = mock(DataTreeChangeListener.class);
315         doNothing().when(listener).onDataTreeChanged(anyCollection());
316
317         final DataTreeIdentifier<T> dti = DataTreeIdentifier.of(CONFIGURATION, InstanceIdentifier.create(bindingClass));
318         getDataBroker().registerDataTreeChangeListener(dti, listener);
319
320         final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
321         domTx.put(CONFIGURATION, YangInstanceIdentifier.of(new NodeIdentifier(qname)),
322             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(qname)).build());
323         domTx.commit().get();
324
325         final var captor = ArgumentCaptor.forClass(Collection.class);
326         verify(listener).onDataTreeChanged(captor.capture());
327         Collection<DataTreeModification<T>> capture = captor.getValue();
328         assertEquals(1, capture.size());
329
330         final DataTreeModification<T> change = capture.iterator().next();
331         assertEquals(dti, change.getRootPath());
332         final DataObjectModification<T> changedContainer = change.getRootNode();
333         assertEquals(ModificationType.WRITE, changedContainer.getModificationType());
334         assertEquals(new NodeStep<>(bindingClass), changedContainer.getIdentifier());
335
336         final T containerAfter = changedContainer.getDataAfter();
337         assertEquals(expected, containerAfter);
338
339         // No further modifications should occur
340         assertEquals(List.of(), changedContainer.getModifiedChildren());
341
342         reset(listener);
343         doNothing().when(listener).onDataTreeChanged(anyCollection());
344         return listener;
345     }
346
347     private DataTreeChangeListener<WithChoice> assertWrittenWithChoice() throws InterruptedException,
348             ExecutionException {
349         final DataTreeChangeListener<WithChoice> listener = mock(DataTreeChangeListener.class);
350         doNothing().when(listener).onDataTreeChanged(anyCollection());
351         getDataBroker().registerDataTreeChangeListener(CHOICE_CONTAINER_TID, listener);
352
353         final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
354         domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CHOICE_CONTAINER_NID), ImmutableNodes.newContainerBuilder()
355             .withNodeIdentifier(CHOICE_CONTAINER_NID)
356             .withChild(ImmutableNodes.newChoiceBuilder().withNodeIdentifier(CHOICE_NID).build())
357             .build());
358         domTx.commit().get();
359
360         final var captor = ArgumentCaptor.forClass(Collection.class);
361         verify(listener).onDataTreeChanged(captor.capture());
362         Collection<DataTreeModification<WithChoice>> capture = captor.getValue();
363         assertEquals(1, capture.size());
364
365         final DataTreeModification<WithChoice> change = capture.iterator().next();
366         assertEquals(CHOICE_CONTAINER_TID, change.getRootPath());
367         final DataObjectModification<WithChoice> changedContainer = change.getRootNode();
368         assertEquals(ModificationType.WRITE, changedContainer.getModificationType());
369         assertEquals(new NodeStep<>(WithChoice.class), changedContainer.getIdentifier());
370
371         final WithChoice containerAfter = changedContainer.getDataAfter();
372         assertEquals(new WithChoiceBuilder().build(), containerAfter);
373
374         // No further modifications should occur
375         assertEquals(List.of(), changedContainer.getModifiedChildren());
376
377         reset(listener);
378         doNothing().when(listener).onDataTreeChanged(anyCollection());
379
380         return listener;
381     }
382 }