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.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;
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,
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");
69 private static final InstanceIdentifier<WithChoice> CHOICE_CONTAINER = InstanceIdentifier.create(WithChoice.class);
70 private static final DataTreeIdentifier<WithChoice> CHOICE_CONTAINER_TID = DataTreeIdentifier.create(CONFIGURATION,
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);
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);
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);
90 public void testKeyedDataTreeModification() throws InterruptedException, ExecutionException {
91 final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
92 new ContainerBuilder().build());
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"))
102 .addChild(Builders.mapEntryBuilder()
103 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Keyed.QNAME, FOO_QNAME, "bar"))
104 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
107 domTx.commit().get();
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());
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());
120 final Container containerAfter = changedContainer.getDataAfter();
121 assertEquals(new ContainerBuilder()
123 new KeyedBuilder().setFoo("foo").withKey(new KeyedKey("foo")).build(),
124 new KeyedBuilder().setFoo("bar").withKey(new KeyedKey("bar")).build()))
125 .build(), containerAfter);
127 final Collection<? extends DataObjectModification<?>> changedChildren = changedContainer.getModifiedChildren();
128 assertEquals(2, changedChildren.size());
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());
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());
145 public void testUnkeyedDataTreeModification() throws InterruptedException, ExecutionException {
146 final DataTreeChangeListener<Container> listener = assertWrittenContainer(Container.QNAME, Container.class,
147 new ContainerBuilder().build());
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"))
157 .withChild(Builders.unkeyedListEntryBuilder()
158 .withNodeIdentifier(new NodeIdentifier(Unkeyed.QNAME))
159 .addChild(ImmutableNodes.leafNode(FOO_QNAME, "bar"))
162 domTx.commit().get();
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());
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());
175 final Container containerAfter = changedContainer.getDataAfter();
176 assertEquals(new ContainerBuilder()
178 new UnkeyedBuilder().setFoo("foo").build(),
179 new UnkeyedBuilder().setFoo("bar").build()))
180 .build(), containerAfter);
182 final Collection<? extends DataObjectModification<?>> changedChildren = changedContainer.getModifiedChildren();
183 assertEquals(0, changedChildren.size());
187 public void testChoiceDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
188 final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
190 doNothing().when(listener).onDataTreeChanged(anyCollection());
192 final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
193 writeTx.put(CONFIGURATION, ADDRESSABLE_CASE, new AddressableBuilder().build());
194 writeTx.commit().get();
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());
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());
207 final Collection<? extends DataObjectModification<?>> choiceChildren = changedContainer.getModifiedChildren();
208 assertEquals(1, choiceChildren.size());
210 final DataObjectModification<Addressable> changedCase = (DataObjectModification<Addressable>) choiceChildren
212 assertEquals(ModificationType.WRITE, changedCase.getModificationType());
213 assertEquals(Item.of(Addressable.class), changedCase.getIdentifier());
214 assertEquals(new AddressableBuilder().build(), changedCase.getDataAfter());
218 public void testDataTreeModificationAddressable() throws InterruptedException, ExecutionException {
219 final DataTreeChangeListener<AddressableCont> listener = assertWrittenContainer(AddressableCont.QNAME,
220 AddressableCont.class, new AddressableContBuilder().build());
222 doNothing().when(listener).onDataTreeChanged(anyCollection());
224 final WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
225 writeTx.put(CONFIGURATION, ADDRESSABLE_CONTAINER.child(AddressableChild.class),
226 new AddressableChildBuilder().build());
227 writeTx.commit().get();
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());
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());
240 final Collection<? extends DataObjectModification<?>> contChildren = changedContainer.getModifiedChildren();
241 assertEquals(1, contChildren.size());
243 final DataObjectModification<Addressable> changedChild = (DataObjectModification<Addressable>) contChildren
245 assertEquals(ModificationType.WRITE, changedChild.getModificationType());
246 assertEquals(Item.of(AddressableChild.class), changedChild.getIdentifier());
247 assertEquals(new AddressableChildBuilder().build(), changedChild.getDataAfter());
251 public void testDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
252 final DataTreeChangeListener<UnaddressableCont> listener = assertWrittenContainer(UnaddressableCont.QNAME,
253 UnaddressableCont.class, new UnaddressableContBuilder().build());
255 doNothing().when(listener).onDataTreeChanged(anyCollection());
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();
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());
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());
274 final Collection<? extends DataObjectModification<?>> contChildren = changedContainer.getModifiedChildren();
275 assertEquals(0, contChildren.size());
279 public void testChoiceDataTreeModificationUnaddressable() throws InterruptedException, ExecutionException {
280 final DataTreeChangeListener<WithChoice> listener = assertWrittenWithChoice();
282 doNothing().when(listener).onDataTreeChanged(anyCollection());
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")
293 domTx.commit().get();
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());
300 final DataTreeModification<WithChoice> choiceChange = capture.iterator().next();
301 assertEquals(CHOICE_CONTAINER_TID, choiceChange.getRootPath());
302 final DataObjectModification<WithChoice> changedContainer = choiceChange.getRootNode();
305 assertEquals(ModificationType.WRITE, changedContainer.getModificationType());
306 assertEquals(Item.of(WithChoice.class), changedContainer.getIdentifier());
308 final Collection<? extends DataObjectModification<?>> choiceChildren = changedContainer.getModifiedChildren();
309 assertEquals(0, choiceChildren.size());
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(anyCollection());
318 final DataTreeIdentifier<T> dti = DataTreeIdentifier.create(CONFIGURATION,
319 InstanceIdentifier.create(bindingClass));
320 getDataBroker().registerDataTreeChangeListener(dti, listener);
322 final DOMDataTreeWriteTransaction domTx = getDomBroker().newWriteOnlyTransaction();
323 domTx.put(CONFIGURATION, YangInstanceIdentifier.create(new NodeIdentifier(qname)),
324 ImmutableNodes.containerNode(qname));
325 domTx.commit().get();
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());
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());
338 final T containerAfter = changedContainer.getDataAfter();
339 assertEquals(expected, containerAfter);
341 // No further modifications should occur
342 assertEquals(List.of(), changedContainer.getModifiedChildren());
345 doNothing().when(listener).onDataTreeChanged(anyCollection());
349 private DataTreeChangeListener<WithChoice> assertWrittenWithChoice() throws InterruptedException,
351 final DataTreeChangeListener<WithChoice> listener = mock(DataTreeChangeListener.class);
352 doNothing().when(listener).onDataTreeChanged(anyCollection());
353 getDataBroker().registerDataTreeChangeListener(CHOICE_CONTAINER_TID, listener);
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())
361 domTx.commit().get();
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());
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());
374 final WithChoice containerAfter = changedContainer.getDataAfter();
375 assertEquals(new WithChoiceBuilder().build(), containerAfter);
377 // No further modifications should occur
378 assertEquals(List.of(), changedContainer.getModifiedChildren());
381 doNothing().when(listener).onDataTreeChanged(anyCollection());