HONEYCOMB-106 - Support for generic cache management
[honeycomb.git] / infra / data-impl / src / test / java / io / fd / honeycomb / v3po / data / impl / ModificationDiffTest.java
1 package io.fd.honeycomb.v3po.data.impl;
2
3 import static org.hamcrest.CoreMatchers.hasItems;
4 import static org.hamcrest.CoreMatchers.is;
5 import static org.junit.Assert.assertThat;
6
7 import java.util.Map;
8 import org.junit.Test;
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;
31
32 public class ModificationDiffTest {
33
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");
41
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");
47
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));
50
51
52     @Test
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);
60
61         final ModificationDiff modificationDiff = getModificationDiff(prepare);
62
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);
66     }
67
68     @Test
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"))
77                         .build())
78                 .build();
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);
82
83         final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
84
85         assertThat(updates.size(), is(1));
86         assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class),
87                 WITH_CHOICE_CONTAINER_ID, null, containerWithChoice);
88     }
89
90     private DataTreeModification getModification(final TipProducingDataTree dataTree) {
91         final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
92         return dataTreeSnapshot.newModification();
93     }
94
95     @Test
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);
102
103         final ModificationDiff modificationDiff = getModificationDiff(prepare);
104
105         assertThat(modificationDiff.getUpdates().size(), is(0));
106     }
107
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);
114     }
115
116     @Test
117     public void testUpdateWrite() throws Exception {
118         final TipProducingDataTree dataTree = getDataTree();
119         final ContainerNode topContainer = getTopContainer("string1");
120         addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
121
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);
126
127         final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
128
129         assertThat(updates.size(), is(1));
130         assertThat(updates.values().size(), is(1));
131         assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
132     }
133
134     private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) {
135         return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode());
136     }
137
138     @Test
139     public void testUpdateMerge() throws Exception {
140         final TipProducingDataTree dataTree = getDataTree();
141         final ContainerNode topContainer = getTopContainer("string1");
142         addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
143
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);
148
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);
153     }
154
155     @Test
156     public void testUpdateDelete() throws Exception {
157         final TipProducingDataTree dataTree = getDataTree();
158         final ContainerNode topContainer = getTopContainer("string1");
159         addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
160
161         final DataTreeModification dataTreeModification = getModification(dataTree);
162         dataTreeModification.delete(TOP_CONTAINER_ID);
163         final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
164
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);
169     }
170
171     @Test
172     public void testWriteAndUpdateInnerList() throws Exception {
173         final TipProducingDataTree dataTree = getDataTree();
174
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));
181
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);
188
189         Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
190
191         assertThat(updates.size(), is(1));
192         assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class),
193                 listEntryId, null, mapNode.getValue().iterator().next());
194
195         // Commit so that update can be tested next
196         dataTree.commit(prepare);
197
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();
202
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);
209
210         updates = getModificationDiff(prepare).getUpdates();
211         assertThat(updates.size(), is(1 /*Actual list entry*/));
212     }
213 //
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));
221     }
222
223     @Test
224     public void testWriteTopContainerAndInnerList() throws Exception {
225         final TipProducingDataTree dataTree = getDataTree();
226
227         DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
228         DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
229
230         final ContainerNode topContainer = getTopContainer("string1");
231         dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
232
233         final YangInstanceIdentifier listId =
234                 YangInstanceIdentifier.create(
235                         new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
236                         new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
237
238         final MapNode mapNode = getNestedList("name1", "text");
239         final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
240
241         dataTreeModification.write(listId, mapNode);
242
243         final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
244
245         final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
246
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(
254                 TOP_CONTAINER_ID,
255                 listEntryId));
256     }
257
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()))
263                     .findFirst().get();
264     }
265
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()))
271                     .findFirst().get();
272     }
273
274     @Test
275     public void testWriteDeepList() throws Exception {
276         final TipProducingDataTree dataTree = getDataTree();
277
278         DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
279         DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
280
281         YangInstanceIdentifier listId =
282                 YangInstanceIdentifier.create(
283                         new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
284                         new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
285
286         MapNode mapNode = getNestedList("name1", "text");
287         dataTreeModification.write(listId, mapNode);
288
289         dataTreeModification.ready();
290         dataTree.validate(dataTreeModification);
291         DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
292         dataTree.commit(prepare);
293
294         dataTreeSnapshot = dataTree.takeSnapshot();
295         dataTreeModification = dataTreeSnapshot.newModification();
296
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),
302                 nestedListNodeId);
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"));
307
308         final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next();
309         // Merge parent list, just to see no modifications on it
310         dataTreeModification.merge(
311                 listId,
312                 Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId)
313                         .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build());
314         dataTreeModification.merge(
315                 deepListId,
316                 Builders.mapBuilder()
317                         .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
318                         .build());
319         dataTreeModification.merge(
320                 deepListEntryId,
321                 deepListEntry);
322
323         dataTreeModification.ready();
324         dataTree.validate(dataTreeModification);
325         prepare = dataTree.prepare(dataTreeModification);
326         dataTree.commit(prepare);
327
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);
331     }
332
333     @Test
334     public void testDeleteInnerListItem() throws Exception {
335         final TipProducingDataTree dataTree = getDataTree();
336
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));
343
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);
349
350         // Commit so that update can be tested next
351         dataTree.commit(prepare);
352
353         YangInstanceIdentifier listItemId = listId.node(
354                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"));
355
356         dataTreeSnapshot = dataTree.takeSnapshot();
357         dataTreeModification = dataTreeSnapshot.newModification();
358         dataTreeModification.delete(listItemId);
359         dataTreeModification.ready();
360         dataTree.validate(dataTreeModification);
361         prepare = dataTree.prepare(dataTreeModification);
362
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);
366     }
367
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);
378     }
379
380     static TipProducingDataTree getDataTree() throws ReactorException {
381         final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
382         dataTree.setSchemaContext(getSchemaCtx());
383         return dataTree;
384     }
385
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))
390                 .build();
391     }
392
393     static MapNode getNestedList(final String listItemName, final String text) {
394         return Builders.mapBuilder()
395                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME))
396                 .withChild(
397                         Builders.mapEntryBuilder()
398                                 .withNodeIdentifier(
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))
403                                 .build()
404                 )
405                 .build();
406     }
407
408     private MapNode getDeepList(final String listItemName) {
409         return Builders.mapBuilder()
410                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
411                 .withChild(
412                         Builders.mapEntryBuilder()
413                                 .withNodeIdentifier(
414                                         new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME,
415                                                 NAME_LEAF_QNAME, listItemName))
416                                 .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName))
417                                 .build()
418                 )
419                 .build();
420     }
421
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();
426     }
427 }