1 package io.fd.honeycomb.v3po.data.impl;
3 import static org.hamcrest.CoreMatchers.hasItems;
4 import static org.hamcrest.CoreMatchers.is;
5 import static org.junit.Assert.assertThat;
9 import org.opendaylight.yangtools.yang.common.QName;
10 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
11 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
12 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
13 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
14 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
15 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
16 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
17 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
18 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
19 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
21 import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
22 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
23 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
24 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
25 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
26 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
28 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
29 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
30 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
32 public class ModificationDiffTest {
34 static final QName TOP_CONTAINER_QNAME =
35 QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container");
36 static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string");
37 static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name");
38 static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text");
39 static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list");
40 static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list");
42 static final QName WITH_CHOICE_CONTAINER_QNAME =
43 QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "with-choice");
44 static final QName CHOICE_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "choice");
45 static final QName IN_CASE1_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case1");
46 static final QName IN_CASE2_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case2");
48 static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
49 static final YangInstanceIdentifier NESTED_LIST_ID = TOP_CONTAINER_ID.node(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
53 public void testInitialWrite() throws Exception {
54 final TipProducingDataTree dataTree = getDataTree();
55 final DataTreeModification dataTreeModification = getModification(dataTree);
56 final NormalizedNode<?, ?> topContainer = getTopContainer("string1");
57 final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
58 dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
59 final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
61 final ModificationDiff modificationDiff = getModificationDiff(prepare);
63 assertThat(modificationDiff.getUpdates().size(), is(1));
64 assertThat(modificationDiff.getUpdates().values().size(), is(1));
65 assertUpdate(modificationDiff.getUpdates().values().iterator().next(), TOP_CONTAINER_ID, null, topContainer);
69 public void testInitialWriteForContainerWithChoice() throws Exception {
70 final TipProducingDataTree dataTree = getDataTree();
71 final DataTreeModification dataTreeModification = getModification(dataTree);
72 final ContainerNode containerWithChoice = Builders.containerBuilder()
73 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(WITH_CHOICE_CONTAINER_QNAME))
74 .withChild(Builders.choiceBuilder()
75 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME))
76 .withChild(ImmutableNodes.leafNode(IN_CASE1_LEAF_QNAME, "withinCase1"))
79 final YangInstanceIdentifier WITH_CHOICE_CONTAINER_ID = YangInstanceIdentifier.of(WITH_CHOICE_CONTAINER_QNAME);
80 dataTreeModification.write(WITH_CHOICE_CONTAINER_ID, containerWithChoice);
81 final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
83 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
85 assertThat(updates.size(), is(1));
86 assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class),
87 WITH_CHOICE_CONTAINER_ID, null, containerWithChoice);
90 private DataTreeModification getModification(final TipProducingDataTree dataTree) {
91 final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
92 return dataTreeSnapshot.newModification();
96 public void testWriteNonPresenceEmptyContainer() throws Exception {
97 final TipProducingDataTree dataTree = getDataTree();
98 final DataTreeModification dataTreeModification = getModification(dataTree);
99 final NormalizedNode<?, ?> topContainer = ImmutableNodes.containerNode(TOP_CONTAINER_QNAME);
100 dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
101 final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
103 final ModificationDiff modificationDiff = getModificationDiff(prepare);
105 assertThat(modificationDiff.getUpdates().size(), is(0));
108 private DataTreeCandidateTip prepareModification(final TipProducingDataTree dataTree,
109 final DataTreeModification dataTreeModification)
110 throws DataValidationFailedException {
111 dataTreeModification.ready();
112 dataTree.validate(dataTreeModification);
113 return dataTree.prepare(dataTreeModification);
117 public void testUpdateWrite() throws Exception {
118 final TipProducingDataTree dataTree = getDataTree();
119 final ContainerNode topContainer = getTopContainer("string1");
120 addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
122 final DataTreeModification dataTreeModification = getModification(dataTree);
123 final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
124 dataTreeModification.write(TOP_CONTAINER_ID, topContainerAfter);
125 final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
127 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
129 assertThat(updates.size(), is(1));
130 assertThat(updates.values().size(), is(1));
131 assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
134 private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) {
135 return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode());
139 public void testUpdateMerge() throws Exception {
140 final TipProducingDataTree dataTree = getDataTree();
141 final ContainerNode topContainer = getTopContainer("string1");
142 addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
144 final DataTreeModification dataTreeModification = getModification(dataTree);
145 final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
146 dataTreeModification.merge(TOP_CONTAINER_ID, topContainerAfter);
147 final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
149 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
150 assertThat(updates.size(), is(1));
151 assertThat(updates.values().size(), is(1));
152 assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
156 public void testUpdateDelete() throws Exception {
157 final TipProducingDataTree dataTree = getDataTree();
158 final ContainerNode topContainer = getTopContainer("string1");
159 addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
161 final DataTreeModification dataTreeModification = getModification(dataTree);
162 dataTreeModification.delete(TOP_CONTAINER_ID);
163 final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
165 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
166 assertThat(updates.size(), is(1));
167 assertThat(updates.values().size(), is(1));
168 assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, null);
172 public void testWriteAndUpdateInnerList() throws Exception {
173 final TipProducingDataTree dataTree = getDataTree();
175 DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
176 DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
177 final YangInstanceIdentifier listId =
178 YangInstanceIdentifier.create(
179 new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
180 new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
182 final MapNode mapNode = getNestedList("name1", "text");
183 final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
184 dataTreeModification.write(listId, mapNode);
185 dataTreeModification.ready();
186 dataTree.validate(dataTreeModification);
187 DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
189 Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
191 assertThat(updates.size(), is(1));
192 assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class),
193 listEntryId, null, mapNode.getValue().iterator().next());
195 // Commit so that update can be tested next
196 dataTree.commit(prepare);
198 YangInstanceIdentifier listItemId = listId.node(
199 new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"));
200 MapEntryNode mapEntryNode =
201 getNestedList("name1", "text-update").getValue().iterator().next();
203 dataTreeSnapshot = dataTree.takeSnapshot();
204 dataTreeModification = dataTreeSnapshot.newModification();
205 dataTreeModification.write(listItemId, mapEntryNode);
206 dataTreeModification.ready();
207 dataTree.validate(dataTreeModification);
208 prepare = dataTree.prepare(dataTreeModification);
210 updates = getModificationDiff(prepare).getUpdates();
211 assertThat(updates.size(), is(1 /*Actual list entry*/));
214 private void assertUpdate(final ModificationDiff.NormalizedNodeUpdate update,
215 final YangInstanceIdentifier idExpected,
216 final NormalizedNode<?, ?> beforeExpected,
217 final NormalizedNode<?, ?> afterExpected) {
218 assertThat(update.getId(), is(idExpected));
219 assertThat(update.getDataBefore(), is(beforeExpected));
220 assertThat(update.getDataAfter(), is(afterExpected));
224 public void testWriteTopContainerAndInnerList() throws Exception {
225 final TipProducingDataTree dataTree = getDataTree();
227 DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
228 DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
230 final ContainerNode topContainer = getTopContainer("string1");
231 dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
233 final YangInstanceIdentifier listId =
234 YangInstanceIdentifier.create(
235 new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
236 new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
238 final MapNode mapNode = getNestedList("name1", "text");
239 final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
241 dataTreeModification.write(listId, mapNode);
243 final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
245 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
247 assertThat(updates.size(), is(2));
248 assertThat(updates.values().size(), is(2));
249 assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), TOP_CONTAINER_ID, null,
250 Builders.containerBuilder(topContainer).withChild(mapNode).build());
251 assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), listEntryId, null, mapNode.getValue().iterator().next());
252 // Assert that keys of the updates map are not wildcarded YID
253 assertThat(updates.keySet(), hasItems(
258 private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForAfterType(
259 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
260 final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
261 return updates.values().stream()
262 .filter(update -> containerNodeClass.isAssignableFrom(update.getDataAfter().getClass()))
266 private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForBeforeType(
267 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
268 final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
269 return updates.values().stream()
270 .filter(update -> containerNodeClass.isAssignableFrom(update.getDataBefore().getClass()))
275 public void testWriteDeepList() throws Exception {
276 final TipProducingDataTree dataTree = getDataTree();
278 DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
279 DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
281 YangInstanceIdentifier listId =
282 YangInstanceIdentifier.create(
283 new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
284 new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
286 MapNode mapNode = getNestedList("name1", "text");
287 dataTreeModification.write(listId, mapNode);
289 dataTreeModification.ready();
290 dataTree.validate(dataTreeModification);
291 DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
292 dataTree.commit(prepare);
294 dataTreeSnapshot = dataTree.takeSnapshot();
295 dataTreeModification = dataTreeSnapshot.newModification();
297 final YangInstanceIdentifier.NodeIdentifierWithPredicates nestedListNodeId =
298 new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1");
299 listId = YangInstanceIdentifier.create(
300 new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
301 new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME),
303 final YangInstanceIdentifier deepListId =
304 listId.node(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME));
305 final YangInstanceIdentifier deepListEntryId = deepListId.node(
306 new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME, "name1"));
308 final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next();
309 // Merge parent list, just to see no modifications on it
310 dataTreeModification.merge(
312 Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId)
313 .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build());
314 dataTreeModification.merge(
316 Builders.mapBuilder()
317 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
319 dataTreeModification.merge(
323 dataTreeModification.ready();
324 dataTree.validate(dataTreeModification);
325 prepare = dataTree.prepare(dataTreeModification);
326 dataTree.commit(prepare);
328 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
329 assertThat(updates.size(), is(1));
330 assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), deepListEntryId, null, deepListEntry);
334 public void testDeleteInnerListItem() throws Exception {
335 final TipProducingDataTree dataTree = getDataTree();
337 DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
338 DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
339 final YangInstanceIdentifier listId =
340 YangInstanceIdentifier.create(
341 new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
342 new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
344 final MapNode mapNode = getNestedList("name1", "text");
345 dataTreeModification.write(listId, mapNode);
346 dataTreeModification.ready();
347 dataTree.validate(dataTreeModification);
348 DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
350 // Commit so that update can be tested next
351 dataTree.commit(prepare);
353 YangInstanceIdentifier listItemId = listId.node(
354 new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"));
356 dataTreeSnapshot = dataTree.takeSnapshot();
357 dataTreeModification = dataTreeSnapshot.newModification();
358 dataTreeModification.delete(listItemId);
359 dataTreeModification.ready();
360 dataTree.validate(dataTreeModification);
361 prepare = dataTree.prepare(dataTreeModification);
363 final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
364 assertThat(updates.size(), is(1));
365 assertUpdate(getNormalizedNodeUpdateForBeforeType(updates, MapEntryNode.class), listItemId, mapNode.getValue().iterator().next(), null);
368 static void addNodeToTree(final DataTree dataTree, final NormalizedNode<?, ?> node,
369 final YangInstanceIdentifier id)
370 throws DataValidationFailedException {
371 DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
372 DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
373 dataTreeModification.write(id, node);
374 dataTreeModification.ready();
375 dataTree.validate(dataTreeModification);
376 DataTreeCandidate prepare = dataTree.prepare(dataTreeModification);
377 dataTree.commit(prepare);
380 static TipProducingDataTree getDataTree() throws ReactorException {
381 final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
382 dataTree.setSchemaContext(getSchemaCtx());
386 static ContainerNode getTopContainer(final String stringValue) {
387 return Builders.containerBuilder()
388 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME))
389 .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue))
393 static MapNode getNestedList(final String listItemName, final String text) {
394 return Builders.mapBuilder()
395 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME))
397 Builders.mapEntryBuilder()
399 new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME,
400 NAME_LEAF_QNAME, listItemName))
401 .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName))
402 .withChild(ImmutableNodes.leafNode(TEXT_LEAF_QNAME, text))
408 private MapNode getDeepList(final String listItemName) {
409 return Builders.mapBuilder()
410 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
412 Builders.mapEntryBuilder()
414 new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME,
415 NAME_LEAF_QNAME, listItemName))
416 .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName))
422 private static SchemaContext getSchemaCtx() throws ReactorException {
423 final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild();
424 buildAction.addSource(new YangStatementSourceImpl(ModificationDiffTest.class.getResourceAsStream("/test-diff.yang")));
425 return buildAction.buildEffective();