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