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