Bug 5485: Improve DataTreeModification pruning on recovery
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / test / java / org / opendaylight / controller / cluster / datastore / node / utils / transformer / NormalizedNodePrunerTest.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.Mockito.mock;
15 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry;
16 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
17 import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
18 import com.google.common.collect.Sets;
19 import java.io.IOException;
20 import java.util.concurrent.atomic.AtomicInteger;
21 import javax.xml.transform.dom.DOMSource;
22 import org.junit.Assert;
23 import org.junit.Before;
24 import org.junit.Test;
25 import org.mockito.MockitoAnnotations;
26 import org.opendaylight.controller.cluster.datastore.node.utils.NormalizedNodeNavigator;
27 import org.opendaylight.controller.cluster.datastore.node.utils.NormalizedNodeVisitor;
28 import org.opendaylight.controller.cluster.datastore.util.TestModel;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
33 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
39 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
40 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44
45 public class NormalizedNodePrunerTest {
46     private static final SchemaContext NO_TEST_SCHEMA = TestModel.createTestContextWithoutTestSchema();
47     private static final SchemaContext NO_AUG_SCHEMA = TestModel.createTestContextWithoutAugmentationSchema();
48     private static final SchemaContext FULL_SCHEMA = TestModel.createTestContext();
49
50     @Before
51     public void setUp(){
52         MockitoAnnotations.initMocks(this);
53     }
54
55     private NormalizedNodePruner prunerFullSchema(YangInstanceIdentifier path) {
56         return new NormalizedNodePruner(path, FULL_SCHEMA);
57     }
58
59     private NormalizedNodePruner prunerNoAugSchema(YangInstanceIdentifier path) {
60         return new NormalizedNodePruner(path, NO_AUG_SCHEMA);
61     }
62
63     private NormalizedNodePruner prunerNoTestSchema(YangInstanceIdentifier path) {
64         return new NormalizedNodePruner(path, NO_TEST_SCHEMA);
65     }
66
67     @Test
68     public void testNodesNotPrunedWhenSchemaPresent() throws IOException {
69         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH);
70
71         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
72
73         NormalizedNode<?, ?> expected = createTestContainer();
74
75         normalizedNodeWriter.write(expected);
76
77         NormalizedNode<?, ?> actual = pruner.normalizedNode();
78
79         assertEquals(expected, actual);
80
81     }
82
83     @Test(expected = IllegalStateException.class)
84     public void testReusePruner() throws IOException {
85         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH);
86
87         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
88
89         NormalizedNode<?, ?> expected = createTestContainer();
90
91         normalizedNodeWriter.write(expected);
92
93         NormalizedNode<?, ?> actual = pruner.normalizedNode();
94
95         assertEquals(expected, actual);
96
97         NormalizedNodeWriter.forStreamWriter(pruner).write(expected);
98
99     }
100
101
102     @Test
103     public void testNodesPrunedWhenAugmentationSchemaMissing() throws IOException {
104         NormalizedNodePruner pruner = prunerNoAugSchema(TestModel.TEST_PATH);
105
106         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
107
108         NormalizedNode<?, ?> expected = createTestContainer();
109
110         normalizedNodeWriter.write(expected);
111
112         NormalizedNode<?, ?> actual = pruner.normalizedNode();
113
114         Assert.assertNotEquals(expected, actual);
115
116         // Asserting true here instead of checking actual value because I don't want this assertion to be fragile
117         assertTrue(countNodes(expected, "store:aug") > 0);
118
119         // All nodes from the augmentation module are gone from the resulting node
120         assertEquals(0, countNodes(actual, "store:aug"));
121     }
122
123     @Test
124     public void testNodesPrunedWhenTestSchemaMissing() throws IOException {
125         NormalizedNodePruner pruner = prunerNoTestSchema(TestModel.TEST_PATH);
126
127         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
128
129         NormalizedNode<?, ?> expected = createTestContainer();
130
131         normalizedNodeWriter.write(expected);
132
133         NormalizedNode<?, ?> actual = pruner.normalizedNode();
134
135         // Since top level schema is missing null is returned
136         assertNull(actual);
137
138         // Asserting true here instead of checking actual value because I don't want this assertion to be fragile
139         assertTrue(countNodes(expected, "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test") > 0);
140
141     }
142
143     private static int countNodes(NormalizedNode<?,?> normalizedNode, final String namespaceFilter){
144         if(normalizedNode == null){
145             return 0;
146         }
147         final AtomicInteger count = new AtomicInteger();
148         new NormalizedNodeNavigator(new NormalizedNodeVisitor() {
149
150             @Override
151             public void visitNode(int level, String parentPath, NormalizedNode<?, ?> normalizedNode) {
152                 if(!(normalizedNode.getIdentifier() instanceof AugmentationIdentifier)) {
153                     if (normalizedNode.getIdentifier().getNodeType().getNamespace().toString().contains(namespaceFilter)) {
154                         count.incrementAndGet();
155                     }
156                 }
157             }
158         }).navigate(YangInstanceIdentifier.EMPTY.toString(), normalizedNode);
159
160         return count.get();
161     }
162
163     @Test
164     public void testLeafNodeNotPrunedWhenHasNoParent() throws IOException {
165         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.DESC_QNAME));
166         NormalizedNode<?, ?> input = Builders.leafBuilder().withNodeIdentifier(
167                 new NodeIdentifier(TestModel.DESC_QNAME)).withValue("test").build();
168         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
169
170         NormalizedNode<?, ?> actual = pruner.normalizedNode();
171         assertEquals("normalizedNode", input, actual);
172     }
173
174     @Test
175     public void testLeafNodePrunedWhenHasAugmentationParentAndSchemaMissing() throws IOException {
176         AugmentationIdentifier augId = new AugmentationIdentifier(Sets.newHashSet(TestModel.AUG_CONT_QNAME));
177         NormalizedNodePruner pruner = prunerFullSchema(YangInstanceIdentifier.builder().
178                 node(TestModel.TEST_QNAME).node(TestModel.AUGMENTED_LIST_QNAME).
179                         node(TestModel.AUGMENTED_LIST_QNAME).node(augId).build());
180         LeafNode<Object> child = Builders.leafBuilder().withNodeIdentifier(
181                 new NodeIdentifier(TestModel.INVALID_QNAME)).withValue("test").build();
182         NormalizedNode<?, ?> input = Builders.augmentationBuilder().withNodeIdentifier(augId).withChild(child).build();
183         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
184
185         NormalizedNode<?, ?> actual = pruner.normalizedNode();
186         assertEquals("normalizedNode", Builders.augmentationBuilder().withNodeIdentifier(augId).build(), actual);
187     }
188
189     @Test
190     public void testLeafNodePrunedWhenHasNoParentAndSchemaMissing() throws IOException {
191         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.INVALID_QNAME));
192         NormalizedNode<?, ?> input = Builders.leafBuilder().withNodeIdentifier(
193                 new NodeIdentifier(TestModel.INVALID_QNAME)).withValue("test").build();
194         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
195
196         NormalizedNode<?, ?> actual = pruner.normalizedNode();
197         assertNull(actual);
198     }
199
200
201     @Test
202     public void testLeafSetEntryNodeNotPrunedWhenHasNoParent() throws IOException {
203         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.SHOE_QNAME));
204         NormalizedNode<?, ?> input = Builders.leafSetEntryBuilder().withValue("puma").withNodeIdentifier(
205                 new NodeWithValue<>(TestModel.SHOE_QNAME, "puma")).build();
206         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
207
208         NormalizedNode<?, ?> actual = pruner.normalizedNode();
209         assertEquals("normalizedNode", input, actual);
210     }
211
212     @Test
213     public void testLeafSetEntryNodeNotPrunedWhenHasParent() throws IOException {
214         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.SHOE_QNAME));
215         LeafSetEntryNode<Object> child = Builders.leafSetEntryBuilder().withValue("puma").withNodeIdentifier(
216                 new NodeWithValue<>(TestModel.SHOE_QNAME, "puma")).build();
217         NormalizedNode<?, ?> input = Builders.leafSetBuilder().withNodeIdentifier(
218                 new NodeIdentifier(TestModel.SHOE_QNAME)).withChild(child).build();
219         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
220
221         NormalizedNode<?, ?> actual = pruner.normalizedNode();
222         assertEquals("normalizedNode", input, actual);
223     }
224
225     @Test
226     public void testLeafSetEntryNodePrunedWhenHasNoParentAndSchemaMissing() throws IOException {
227         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.INVALID_QNAME));
228         NormalizedNode<?, ?> input = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
229                 new NodeWithValue<>(TestModel.INVALID_QNAME, "test")).build();
230         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
231
232         NormalizedNode<?, ?> actual = pruner.normalizedNode();
233         assertNull(actual);
234     }
235
236     @Test
237     public void testLeafSetEntryNodePrunedWhenHasParentAndSchemaMissing() throws IOException {
238         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.INVALID_QNAME));
239         LeafSetEntryNode<Object> child = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
240                 new NodeWithValue<>(TestModel.INVALID_QNAME, "test")).build();
241         NormalizedNode<?, ?> input = Builders.leafSetBuilder().withNodeIdentifier(
242                 new NodeIdentifier(TestModel.INVALID_QNAME)).withChild(child).build();
243         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
244
245         NormalizedNode<?, ?> actual = pruner.normalizedNode();
246         assertNull(actual);
247     }
248
249     @Test
250     public void testAnyXMLNodeNotPrunedWhenHasNoParent() throws IOException {
251         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH.node(TestModel.ANY_XML_QNAME));
252         NormalizedNode<?, ?> input = Builders.anyXmlBuilder().withNodeIdentifier(
253                 new NodeIdentifier(TestModel.ANY_XML_QNAME)).withValue(mock(DOMSource.class)).build();
254         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
255
256         NormalizedNode<?, ?> actual = pruner.normalizedNode();
257         assertEquals("normalizedNode", input, actual);
258     }
259
260
261     @Test
262     public void testAnyXMLNodeNotPrunedWhenHasParent() throws IOException {
263         NormalizedNodePruner pruner = prunerFullSchema(TestModel.TEST_PATH);
264         AnyXmlNode child = Builders.anyXmlBuilder().withNodeIdentifier(
265                 new NodeIdentifier(TestModel.ANY_XML_QNAME)).withValue(mock(DOMSource.class)).build();
266         NormalizedNode<?, ?> input = Builders.containerBuilder().withNodeIdentifier(
267                 new NodeIdentifier(TestModel.TEST_QNAME)).withChild(child).build();
268         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
269
270         NormalizedNode<?, ?> actual = pruner.normalizedNode();
271         assertEquals("normalizedNode", input, actual);
272     }
273
274     @Test
275     public void testAnyXmlNodePrunedWhenHasNoParentAndSchemaMissing() throws IOException {
276         NormalizedNodePruner pruner = prunerNoTestSchema(TestModel.TEST_PATH.node(TestModel.ANY_XML_QNAME));
277         NormalizedNode<?, ?> input = Builders.anyXmlBuilder().withNodeIdentifier(
278                 new NodeIdentifier(TestModel.ANY_XML_QNAME)).withValue(mock(DOMSource.class)).build();
279         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
280
281         NormalizedNode<?, ?> actual = pruner.normalizedNode();
282         assertNull(actual);
283     }
284
285     @Test
286     public void testInnerContainerNodeWithFullPathPathNotPruned() throws IOException {
287         YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
288                 node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
289                     node(TestModel.INNER_LIST_QNAME).nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").
290                         node(TestModel.INNER_CONTAINER_QNAME).build();
291         NormalizedNodePruner pruner = prunerFullSchema(path);
292
293         NormalizedNode<?, ?> input = ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME);
294         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
295
296         NormalizedNode<?, ?> actual = pruner.normalizedNode();
297         assertEquals("normalizedNode", input, actual);
298     }
299
300     @Test
301     public void testInnerContainerNodeWithFullPathPrunedWhenSchemaMissing() throws IOException {
302         YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
303                 node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
304                     node(TestModel.INNER_LIST_QNAME).nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").
305                         node(TestModel.INVALID_QNAME).build();
306         NormalizedNodePruner pruner = prunerFullSchema(path);
307
308         NormalizedNode<?, ?> input = ImmutableNodes.containerNode(TestModel.INVALID_QNAME);
309         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
310
311         NormalizedNode<?, ?> actual = pruner.normalizedNode();
312         assertNull(actual);
313     }
314
315     @Test
316     public void testInnerContainerNodeWithParentPathPrunedWhenSchemaMissing() throws IOException {
317         YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
318                 node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build();
319         NormalizedNodePruner pruner = prunerFullSchema(path);
320
321         MapNode innerList = mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(mapEntryBuilder(
322                 TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").withChild(
323                         ImmutableNodes.containerNode(TestModel.INVALID_QNAME)).build()).build();
324         NormalizedNode<?, ?> input = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
325                 withChild(innerList).build();
326         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
327
328         NormalizedNode<?, ?> expected = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
329                 withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(mapEntryBuilder(
330                 TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").build()).build()).build();
331         NormalizedNode<?, ?> actual = pruner.normalizedNode();
332         assertEquals("normalizedNode", expected, actual);
333     }
334
335     @Test
336     public void testInnerListNodeWithFullPathNotPruned() throws IOException {
337         YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
338                 node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
339                     node(TestModel.INNER_LIST_QNAME).build();
340         NormalizedNodePruner pruner = prunerFullSchema(path);
341
342         MapNode input = mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(mapEntryBuilder(
343                 TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "one").withChild(
344                         ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME)).build()).build();
345         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
346
347         NormalizedNode<?, ?> actual = pruner.normalizedNode();
348         assertEquals("normalizedNode", input, actual);
349     }
350
351     @Test
352     public void testInnerListNodeWithFullPathPrunedWhenSchemaMissing() throws IOException {
353         YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
354                 node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
355                     node(TestModel.INVALID_QNAME).build();
356         NormalizedNodePruner pruner = prunerFullSchema(path);
357
358         MapNode input = mapNodeBuilder(TestModel.INVALID_QNAME).withChild(mapEntryBuilder(
359                 TestModel.INVALID_QNAME, TestModel.NAME_QNAME, "one").withChild(
360                         ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME)).build()).build();
361         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
362
363         NormalizedNode<?, ?> actual = pruner.normalizedNode();
364         assertNull(actual);
365     }
366
367     @Test
368     public void testInnerListNodeWithParentPathPrunedWhenSchemaMissing() throws IOException {
369         YangInstanceIdentifier path = YangInstanceIdentifier.builder().node(TestModel.TEST_QNAME).
370                 node(TestModel.OUTER_LIST_QNAME).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build();
371         NormalizedNodePruner pruner = prunerFullSchema(path);
372
373         MapNode innerList = mapNodeBuilder(TestModel.INVALID_QNAME).withChild(mapEntryBuilder(
374                 TestModel.INVALID_QNAME, TestModel.NAME_QNAME, "one").withChild(
375                         ImmutableNodes.containerNode(TestModel.INNER_CONTAINER_QNAME)).build()).build();
376         NormalizedNode<?, ?> input = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).
377                 withChild(innerList).build();
378         NormalizedNodeWriter.forStreamWriter(pruner).write(input);
379
380         NormalizedNode<?, ?> expected = mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
381         NormalizedNode<?, ?> actual = pruner.normalizedNode();
382         assertEquals("normalizedNode", expected, actual);
383     }
384
385     private static NormalizedNode<?, ?> createTestContainer() {
386         byte[] bytes1 = {1,2,3};
387         LeafSetEntryNode<Object> entry1 = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
388                 new NodeWithValue<>(TestModel.BINARY_LEAF_LIST_QNAME, bytes1)).
389                 withValue(bytes1).build();
390
391         byte[] bytes2 = {};
392         LeafSetEntryNode<Object> entry2 = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
393                 new NodeWithValue<>(TestModel.BINARY_LEAF_LIST_QNAME, bytes2)).
394                 withValue(bytes2).build();
395
396         LeafSetEntryNode<Object> entry3 = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
397                 new NodeWithValue<>(TestModel.BINARY_LEAF_LIST_QNAME, null)).withValue(null).build();
398
399
400         return TestModel.createBaseTestContainerBuilder().
401                 withChild(ImmutableLeafSetNodeBuilder.create().withNodeIdentifier(
402                         new NodeIdentifier(TestModel.BINARY_LEAF_LIST_QNAME)).
403                         withChild(entry1).withChild(entry2).withChild(entry3).build()).
404                 withChild(ImmutableNodes.leafNode(TestModel.SOME_BINARY_DATA_QNAME, new byte[]{1, 2, 3, 4})).
405                 build();
406     }
407 }