2 * Copyright (c) 2018 Pantheon Technologies, s.ro.. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.dom.adapter;
10 import static org.junit.Assert.assertEquals;
11 import static org.mockito.ArgumentMatchers.anyList;
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;
18 import java.util.List;
19 import java.util.concurrent.ExecutionException;
20 import org.junit.Test;
21 import org.mockito.ArgumentCaptor;
22 import org.opendaylight.mdsal.binding.api.DataObjectModification;
23 import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType;
24 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
25 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
26 import org.opendaylight.mdsal.binding.api.DataTreeModification;
27 import org.opendaylight.mdsal.binding.api.WriteTransaction;
28 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
30 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.AddressableCont;
31 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.AddressableContBuilder;
32 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.Container;
33 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.ContainerBuilder;
34 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.UnaddressableCont;
35 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.UnaddressableContBuilder;
36 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.WithChoice;
37 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.WithChoiceBuilder;
38 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.addressable.cont.AddressableChild;
39 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.addressable.cont.AddressableChildBuilder;
40 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Keyed;
41 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedBuilder;
42 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedKey;
43 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Unkeyed;
44 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.UnkeyedBuilder;
45 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.with.choice.Foo;
46 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.with.choice.foo.addressable._case.Addressable;
47 import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.with.choice.foo.addressable._case.AddressableBuilder;
48 import org.opendaylight.yangtools.yang.binding.ChildOf;
49 import org.opendaylight.yangtools.yang.binding.DataRoot;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.binding.NodeStep;
52 import org.opendaylight.yangtools.yang.common.QName;
53 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
54 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
55 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
56 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
58 public class Mdsal298Test extends AbstractDataBrokerTest {
59 private static final InstanceIdentifier<Container> CONTAINER = InstanceIdentifier.create(Container.class);
60 private static final DataTreeIdentifier<Container> CONTAINER_TID = DataTreeIdentifier.of(CONFIGURATION,
62 private static final NodeIdentifier CONTAINER_NID = new NodeIdentifier(Container.QNAME);
63 private static final QName FOO_QNAME = QName.create(Container.QNAME, "foo");
64 private static final QName BAZ_QNAME = QName.create(UnaddressableCont.QNAME, "baz");
66 private static final InstanceIdentifier<WithChoice> CHOICE_CONTAINER = InstanceIdentifier.create(WithChoice.class);
67 private static final DataTreeIdentifier<WithChoice> CHOICE_CONTAINER_TID = DataTreeIdentifier.of(CONFIGURATION,
69 private static final NodeIdentifier CHOICE_CONTAINER_NID = new NodeIdentifier(WithChoice.QNAME);
70 private static final NodeIdentifier CHOICE_NID = new NodeIdentifier(Foo.QNAME);
71 private static final InstanceIdentifier<Addressable> ADDRESSABLE_CASE = CHOICE_CONTAINER
72 .child((Class)Addressable.class);
74 private static final InstanceIdentifier<AddressableCont> ADDRESSABLE_CONTAINER =
75 InstanceIdentifier.create(AddressableCont.class);
76 private static final DataTreeIdentifier<AddressableCont> ADDRESSABLE_CONTAINER_TID = DataTreeIdentifier.of(
77 CONFIGURATION, ADDRESSABLE_CONTAINER);
78 private static final NodeIdentifier ADDRESSABLE_CONTAINER_NID = new NodeIdentifier(AddressableCont.QNAME);
80 private static final InstanceIdentifier<UnaddressableCont> UNADDRESSABLE_CONTAINER =
81 InstanceIdentifier.create(UnaddressableCont.class);
82 private static final DataTreeIdentifier<UnaddressableCont> UNADDRESSABLE_CONTAINER_TID = DataTreeIdentifier.of(
83 CONFIGURATION, UNADDRESSABLE_CONTAINER);
84 private static final NodeIdentifier UNADDRESSABLE_CONTAINER_NID = new NodeIdentifier(UnaddressableCont.QNAME);
87 public void testKeyedDataTreeModification() throws InterruptedException, ExecutionException {
88 final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
89 new ContainerBuilder().build());
91 final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
92 domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CONTAINER_NID).node(Keyed.QNAME),
93 ImmutableNodes.newUserMapBuilder()
94 .withNodeIdentifier(new NodeIdentifier(Keyed.QNAME))
95 .addChild(ImmutableNodes.newMapEntryBuilder()
96 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Keyed.QNAME, FOO_QNAME, "foo"))
97 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "foo"))
99 .addChild(ImmutableNodes.newMapEntryBuilder()
100 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Keyed.QNAME, FOO_QNAME, "bar"))
101 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
104 domTx.commit().get();
106 final var captor = ArgumentCaptor.forClass(List.class);
107 verify(listener).onDataTreeChanged(captor.capture());
108 List<DataTreeModification<Container>> capture = captor.getValue();
109 assertEquals(1, capture.size());
111 final DataTreeModification<Container> change = capture.get(0);
112 assertEquals(CONTAINER_TID, change.getRootPath());
113 final DataObjectModification<Container> changedContainer = change.getRootNode();
114 assertEquals(new NodeStep<>(Container.class), changedContainer.step());
115 assertEquals(ModificationType.SUBTREE_MODIFIED, changedContainer.modificationType());
117 final Container containerAfter = changedContainer.dataAfter();
118 assertEquals(new ContainerBuilder()
120 new KeyedBuilder().setFoo("foo").withKey(new KeyedKey("foo")).build(),
121 new KeyedBuilder().setFoo("bar").withKey(new KeyedKey("bar")).build()))
122 .build(), containerAfter);
124 final var changedChildren = changedContainer.modifiedChildren();
125 assertEquals(2, changedChildren.size());
127 final var it = changedChildren.iterator();
128 final DataObjectModification<?> changedChild1 = it.next();
129 assertEquals(ModificationType.WRITE, changedChild1.modificationType());
130 assertEquals(List.of(), changedChild1.modifiedChildren());
131 final Keyed child1After = (Keyed) changedChild1.dataAfter();
132 assertEquals("foo", child1After.getFoo());
134 final DataObjectModification<?> changedChild2 = it.next();
135 assertEquals(ModificationType.WRITE, changedChild2.modificationType());
136 assertEquals(List.of(), changedChild2.modifiedChildren());
137 final Keyed child2After = (Keyed) changedChild2.dataAfter();
138 assertEquals("bar", child2After.getFoo());
142 public void testUnkeyedDataTreeModification() throws InterruptedException, ExecutionException {
143 final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
144 new ContainerBuilder().build());
146 final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
147 domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CONTAINER_NID).node(Unkeyed.QNAME),
148 ImmutableNodes.newUnkeyedListBuilder()
149 .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
150 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
151 .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
152 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "foo"))
154 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
155 .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
156 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
159 domTx.commit().get();
161 final var captor = ArgumentCaptor.forClass(List.class);
162 verify(listener).onDataTreeChanged(captor.capture());
163 List<DataTreeModification<Container>> capture = captor.getValue();
164 assertEquals(1, capture.size());
166 final DataTreeModification<Container> change = capture.get(0);
167 assertEquals(CONTAINER_TID, change.getRootPath());
168 final DataObjectModification<Container> changedContainer = change.getRootNode();
169 assertEquals(new NodeStep<>(Container.class), changedContainer.step());
170 assertEquals(ModificationType.WRITE, changedContainer.modificationType());
172 final Container containerAfter = changedContainer.dataAfter();
173 assertEquals(new ContainerBuilder()
175 new UnkeyedBuilder().setFoo("foo").build(),
176 new UnkeyedBuilder().setFoo("bar").build()))
177 .build(), containerAfter);
179 final var changedChildren = changedContainer.modifiedChildren();
180 assertEquals(0, changedChildren.size());
184 public void testChoiceDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
185 final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
187 doNothing().when(listener).onDataTreeChanged(anyList());
189 final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
190 writeTx.put(CONFIGURATION, ADDRESSABLE_CASE, new AddressableBuilder().build());
191 writeTx.commit().get();
193 final var captor = ArgumentCaptor.forClass(List.class);
194 verify(listener).onDataTreeChanged(captor.capture());
195 List<DataTreeModification<WithChoice>> capture = captor.getValue();
196 assertEquals(1, capture.size());
198 final DataTreeModification<WithChoice> choiceChange = capture.iterator().next();
199 assertEquals(CHOICE_CONTAINER_TID, choiceChange.getRootPath());
200 final DataObjectModification<WithChoice> changedContainer = choiceChange.getRootNode();
201 assertEquals(ModificationType.SUBTREE_MODIFIED, changedContainer.modificationType());
202 assertEquals(new NodeStep<>(WithChoice.class), changedContainer.step());
204 final var choiceChildren = changedContainer.modifiedChildren();
205 assertEquals(1, choiceChildren.size());
207 final var changedCase = (DataObjectModification<Addressable>) choiceChildren.iterator().next();
208 assertEquals(ModificationType.WRITE, changedCase.modificationType());
209 assertEquals(new NodeStep<>(Addressable.class), changedCase.step());
210 assertEquals(new AddressableBuilder().build(), changedCase.dataAfter());
214 public void testDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
215 final DataTreeChangeListener<AddressableCont> listener = assertWrittenContainer(AddressableCont.QNAME,
216 AddressableCont.class, new AddressableContBuilder().build());
218 doNothing().when(listener).onDataTreeChanged(anyList());
220 final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
221 writeTx.put(CONFIGURATION, ADDRESSABLE_CONTAINER.child(AddressableChild.class),
222 new AddressableChildBuilder().build());
223 writeTx.commit().get();
225 final var captor = ArgumentCaptor.forClass(List.class);
226 verify(listener).onDataTreeChanged(captor.capture());
227 final List<DataTreeModification<AddressableCont>> capture = captor.getValue();
228 assertEquals(1, capture.size());
230 final DataTreeModification<AddressableCont> contChange = capture.iterator().next();
231 assertEquals(ADDRESSABLE_CONTAINER_TID, contChange.getRootPath());
232 final DataObjectModification<AddressableCont> changedContainer = contChange.getRootNode();
233 assertEquals(ModificationType.SUBTREE_MODIFIED, changedContainer.modificationType());
234 assertEquals(new NodeStep<>(AddressableCont.class), changedContainer.step());
236 final var contChildren = changedContainer.modifiedChildren();
237 assertEquals(1, contChildren.size());
239 final var changedChild = (DataObjectModification<Addressable>) contChildren.iterator().next();
240 assertEquals(ModificationType.WRITE, changedChild.modificationType());
241 assertEquals(new NodeStep<>(AddressableChild.class), changedChild.step());
242 assertEquals(new AddressableChildBuilder().build(), changedChild.dataAfter());
246 public void testDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
247 final DataTreeChangeListener<UnaddressableCont> listener = assertWrittenContainer(UnaddressableCont.QNAME,
248 UnaddressableCont.class, new UnaddressableContBuilder().build());
250 doNothing().when(listener).onDataTreeChanged(anyList());
252 final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
253 domTx.put(CONFIGURATION, YangInstanceIdentifier.of(UNADDRESSABLE_CONTAINER_NID)
254 .node(QName.create(UnaddressableCont.QNAME, "baz")),
255 ImmutableNodes.leafNode(BAZ_QNAME, "baz"));
256 domTx.commit().get();
258 final var captor = ArgumentCaptor.forClass(List.class);
259 verify(listener).onDataTreeChanged(captor.capture());
260 List<DataTreeModification<UnaddressableCont>> capture = captor.getValue();
261 assertEquals(1, capture.size());
263 final DataTreeModification<UnaddressableCont> contChange = capture.iterator().next();
264 assertEquals(UNADDRESSABLE_CONTAINER_TID, contChange.getRootPath());
265 final DataObjectModification<UnaddressableCont> changedContainer = contChange.getRootNode();
266 assertEquals(ModificationType.WRITE, changedContainer.modificationType());
267 assertEquals(new NodeStep<>(UnaddressableCont.class), changedContainer.step());
269 final var contChildren = changedContainer.modifiedChildren();
270 assertEquals(0, contChildren.size());
274 public void testChoiceDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
275 final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
277 doNothing().when(listener).onDataTreeChanged(anyList());
279 final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
280 domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CHOICE_CONTAINER_NID).node(Foo.QNAME),
281 ImmutableNodes.newChoiceBuilder()
282 .withNodeIdentifier(new NodeIdentifier(Foo.QNAME))
283 .withChild(ImmutableNodes.newSystemLeafSetBuilder()
284 .withNodeIdentifier(new NodeIdentifier(QName.create(Foo.QNAME, "unaddressable")))
285 .withChildValue("foo")
288 domTx.commit().get();
290 final var captor = ArgumentCaptor.forClass(List.class);
291 verify(listener).onDataTreeChanged(captor.capture());
292 List<DataTreeModification<WithChoice>> capture = captor.getValue();
293 assertEquals(1, capture.size());
295 final DataTreeModification<WithChoice> choiceChange = capture.get(0);
296 assertEquals(CHOICE_CONTAINER_TID, choiceChange.getRootPath());
297 final DataObjectModification<WithChoice> changedContainer = choiceChange.getRootNode();
300 assertEquals(ModificationType.WRITE, changedContainer.modificationType());
301 assertEquals(new NodeStep<>(WithChoice.class), changedContainer.step());
303 final var choiceChildren = changedContainer.modifiedChildren();
304 assertEquals(0, choiceChildren.size());
307 private <T extends ChildOf<? extends DataRoot>> DataTreeChangeListener<T> assertWrittenContainer(final QName qname,
308 final Class<T> bindingClass, final T expected)
309 throws InterruptedException, ExecutionException {
310 final DataTreeChangeListener<T> listener = mock(DataTreeChangeListener.class);
311 doNothing().when(listener).onDataTreeChanged(anyList());
313 final DataTreeIdentifier<T> dti = DataTreeIdentifier.of(CONFIGURATION, InstanceIdentifier.create(bindingClass));
314 getDataBroker().registerDataTreeChangeListener(dti, listener);
316 final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
317 domTx.put(CONFIGURATION, YangInstanceIdentifier.of(new NodeIdentifier(qname)),
318 ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(qname)).build());
319 domTx.commit().get();
321 final var captor = ArgumentCaptor.forClass(List.class);
322 verify(listener).onDataTreeChanged(captor.capture());
323 List<DataTreeModification<T>> capture = captor.getValue();
324 assertEquals(1, capture.size());
326 final DataTreeModification<T> change = capture.iterator().next();
327 assertEquals(dti, change.getRootPath());
328 final DataObjectModification<T> changedContainer = change.getRootNode();
329 assertEquals(ModificationType.WRITE, changedContainer.modificationType());
330 assertEquals(new NodeStep<>(bindingClass), changedContainer.step());
332 final T containerAfter = changedContainer.dataAfter();
333 assertEquals(expected, containerAfter);
335 // No further modifications should occur
336 assertEquals(List.of(), changedContainer.modifiedChildren());
339 doNothing().when(listener).onDataTreeChanged(anyList());
343 private DataTreeChangeListener<WithChoice> assertWrittenWithChoice() throws InterruptedException,
345 final DataTreeChangeListener<WithChoice> listener = mock(DataTreeChangeListener.class);
346 doNothing().when(listener).onDataTreeChanged(anyList());
347 getDataBroker().registerDataTreeChangeListener(CHOICE_CONTAINER_TID, listener);
349 final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
350 domTx.put(CONFIGURATION, YangInstanceIdentifier.of(CHOICE_CONTAINER_NID), ImmutableNodes.newContainerBuilder()
351 .withNodeIdentifier(CHOICE_CONTAINER_NID)
352 .withChild(ImmutableNodes.newChoiceBuilder().withNodeIdentifier(CHOICE_NID).build())
354 domTx.commit().get();
356 final var captor = ArgumentCaptor.forClass(List.class);
357 verify(listener).onDataTreeChanged(captor.capture());
358 List<DataTreeModification<WithChoice>> capture = captor.getValue();
359 assertEquals(1, capture.size());
361 final DataTreeModification<WithChoice> change = capture.iterator().next();
362 assertEquals(CHOICE_CONTAINER_TID, change.getRootPath());
363 final DataObjectModification<WithChoice> changedContainer = change.getRootNode();
364 assertEquals(ModificationType.WRITE, changedContainer.modificationType());
365 assertEquals(new NodeStep<>(WithChoice.class), changedContainer.step());
367 final WithChoice containerAfter = changedContainer.dataAfter();
368 assertEquals(new WithChoiceBuilder().build(), containerAfter);
370 // No further modifications should occur
371 assertEquals(List.of(), changedContainer.modifiedChildren());
374 doNothing().when(listener).onDataTreeChanged(anyList());