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.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;
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;
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,
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");
68 private static final InstanceIdentifier<WithChoice> CHOICE_CONTAINER = InstanceIdentifier.create(WithChoice.class);
69 private static final DataTreeIdentifier<WithChoice> CHOICE_CONTAINER_TID = DataTreeIdentifier.of(CONFIGURATION,
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);
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);
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);
89 public void testKeyedDataTreeModification() throws InterruptedException, ExecutionException {
90 final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
91 new ContainerBuilder().build());
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"))
101 .addChild(ImmutableNodes.newMapEntryBuilder()
102 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Keyed.QNAME, FOO_QNAME, "bar"))
103 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
106 domTx.commit().get();
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());
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());
119 final Container containerAfter = changedContainer.getDataAfter();
120 assertEquals(new ContainerBuilder()
122 new KeyedBuilder().setFoo("foo").withKey(new KeyedKey("foo")).build(),
123 new KeyedBuilder().setFoo("bar").withKey(new KeyedKey("bar")).build()))
124 .build(), containerAfter);
126 final Collection<? extends DataObjectModification<?>> changedChildren = changedContainer.getModifiedChildren();
127 assertEquals(2, changedChildren.size());
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());
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());
144 public void testUnkeyedDataTreeModification() throws InterruptedException, ExecutionException {
145 final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
146 new ContainerBuilder().build());
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"))
156 .withChild(ImmutableNodes.newUnkeyedListEntryBuilder()
157 .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
158 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
161 domTx.commit().get();
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());
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());
174 final Container containerAfter = changedContainer.getDataAfter();
175 assertEquals(new ContainerBuilder()
177 new UnkeyedBuilder().setFoo("foo").build(),
178 new UnkeyedBuilder().setFoo("bar").build()))
179 .build(), containerAfter);
181 final Collection<? extends DataObjectModification<?>> changedChildren = changedContainer.getModifiedChildren();
182 assertEquals(0, changedChildren.size());
186 public void testChoiceDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
187 final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
189 doNothing().when(listener).onDataTreeChanged(anyCollection());
191 final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
192 writeTx.put(CONFIGURATION, ADDRESSABLE_CASE, new AddressableBuilder().build());
193 writeTx.commit().get();
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());
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());
206 final Collection<? extends DataObjectModification<?>> choiceChildren = changedContainer.getModifiedChildren();
207 assertEquals(1, choiceChildren.size());
209 final DataObjectModification<Addressable> changedCase = (DataObjectModification<Addressable>) choiceChildren
211 assertEquals(ModificationType.WRITE, changedCase.getModificationType());
212 assertEquals(new NodeStep<>(Addressable.class), changedCase.getIdentifier());
213 assertEquals(new AddressableBuilder().build(), changedCase.getDataAfter());
217 public void testDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
218 final DataTreeChangeListener<AddressableCont> listener = assertWrittenContainer(AddressableCont.QNAME,
219 AddressableCont.class, new AddressableContBuilder().build());
221 doNothing().when(listener).onDataTreeChanged(anyCollection());
223 final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
224 writeTx.put(CONFIGURATION, ADDRESSABLE_CONTAINER.child(AddressableChild.class),
225 new AddressableChildBuilder().build());
226 writeTx.commit().get();
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());
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());
239 final Collection<? extends DataObjectModification<?>> contChildren = changedContainer.getModifiedChildren();
240 assertEquals(1, contChildren.size());
242 final DataObjectModification<Addressable> changedChild = (DataObjectModification<Addressable>) contChildren
244 assertEquals(ModificationType.WRITE, changedChild.getModificationType());
245 assertEquals(new NodeStep<>(AddressableChild.class), changedChild.getIdentifier());
246 assertEquals(new AddressableChildBuilder().build(), changedChild.getDataAfter());
250 public void testDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
251 final DataTreeChangeListener<UnaddressableCont> listener = assertWrittenContainer(UnaddressableCont.QNAME,
252 UnaddressableCont.class, new UnaddressableContBuilder().build());
254 doNothing().when(listener).onDataTreeChanged(anyCollection());
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();
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());
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());
273 final Collection<? extends DataObjectModification<?>> contChildren = changedContainer.getModifiedChildren();
274 assertEquals(0, contChildren.size());
278 public void testChoiceDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
279 final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
281 doNothing().when(listener).onDataTreeChanged(anyCollection());
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")
292 domTx.commit().get();
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());
299 final DataTreeModification<WithChoice> choiceChange = capture.iterator().next();
300 assertEquals(CHOICE_CONTAINER_TID, choiceChange.getRootPath());
301 final DataObjectModification<WithChoice> changedContainer = choiceChange.getRootNode();
304 assertEquals(ModificationType.WRITE, changedContainer.getModificationType());
305 assertEquals(new NodeStep<>(WithChoice.class), changedContainer.getIdentifier());
307 final Collection<? extends DataObjectModification<?>> choiceChildren = changedContainer.getModifiedChildren();
308 assertEquals(0, choiceChildren.size());
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());
317 final DataTreeIdentifier<T> dti = DataTreeIdentifier.of(CONFIGURATION, InstanceIdentifier.create(bindingClass));
318 getDataBroker().registerDataTreeChangeListener(dti, listener);
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();
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());
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());
336 final T containerAfter = changedContainer.getDataAfter();
337 assertEquals(expected, containerAfter);
339 // No further modifications should occur
340 assertEquals(List.of(), changedContainer.getModifiedChildren());
343 doNothing().when(listener).onDataTreeChanged(anyCollection());
347 private DataTreeChangeListener<WithChoice> assertWrittenWithChoice() throws InterruptedException,
349 final DataTreeChangeListener<WithChoice> listener = mock(DataTreeChangeListener.class);
350 doNothing().when(listener).onDataTreeChanged(anyCollection());
351 getDataBroker().registerDataTreeChangeListener(CHOICE_CONTAINER_TID, listener);
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())
358 domTx.commit().get();
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());
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());
371 final WithChoice containerAfter = changedContainer.getDataAfter();
372 assertEquals(new WithChoiceBuilder().build(), containerAfter);
374 // No further modifications should occur
375 assertEquals(List.of(), changedContainer.getModifiedChildren());
378 doNothing().when(listener).onDataTreeChanged(anyCollection());