Bug 5109: Handle stand alone leaf nodes in CDS streaming
[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.assertNotNull;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.mockito.Matchers.any;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.mock;
18 import static org.mockito.Mockito.never;
19 import static org.mockito.Mockito.verify;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.common.collect.ImmutableSet;
22 import java.io.IOException;
23 import java.util.concurrent.atomic.AtomicInteger;
24 import javax.xml.transform.dom.DOMSource;
25 import org.junit.Assert;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.mockito.Mock;
29 import org.mockito.MockitoAnnotations;
30 import org.opendaylight.controller.cluster.datastore.node.utils.NormalizedNodeNavigator;
31 import org.opendaylight.controller.cluster.datastore.node.utils.NormalizedNodeVisitor;
32 import org.opendaylight.controller.cluster.datastore.util.TestModel;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
40 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
41 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
44 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
46 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
48 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
49
50 public class NormalizedNodePrunerTest {
51
52     private NormalizedNodePruner prunerFullSchema;
53
54     private NormalizedNodePruner prunerNoAugSchema;
55
56     @Mock
57     private NormalizedNodeBuilderWrapper normalizedNodeBuilderWrapper;
58
59     @Mock
60     private NormalizedNodeContainerBuilder normalizedNodeContainerBuilder;
61
62     @Mock
63     private NormalizedNode<?,?> normalizedNode;
64
65     @Before
66     public void setUp(){
67         MockitoAnnotations.initMocks(this);
68         prunerFullSchema = new NormalizedNodePruner(TestModel.createTestContext());
69         prunerNoAugSchema = new NormalizedNodePruner(TestModel.createTestContextWithoutAugmentationSchema());
70         doReturn(normalizedNodeContainerBuilder).when(normalizedNodeBuilderWrapper).builder();
71         doReturn(TestModel.BOOLEAN_LEAF_QNAME).when(normalizedNodeBuilderWrapper).nodeType();
72         doReturn(normalizedNode).when(normalizedNodeContainerBuilder).build();
73         doReturn(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME)).when(normalizedNodeBuilderWrapper).identifier();
74     }
75
76     @Test
77     public void testNodesNotPrunedWhenSchemaPresent() throws IOException {
78         NormalizedNodePruner pruner = prunerFullSchema;
79
80         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
81
82         NormalizedNode<?, ?> expected = createTestContainer();
83
84         normalizedNodeWriter.write(expected);
85
86         NormalizedNode<?, ?> actual = pruner.normalizedNode();
87
88         assertEquals(expected, actual);
89
90     }
91
92     @Test(expected = IllegalStateException.class)
93     public void testReusePruner() throws IOException {
94         NormalizedNodePruner pruner = prunerFullSchema;
95
96         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
97
98         NormalizedNode<?, ?> expected = createTestContainer();
99
100         normalizedNodeWriter.write(expected);
101
102         NormalizedNode<?, ?> actual = pruner.normalizedNode();
103
104         assertEquals(expected, actual);
105
106         NormalizedNodeWriter.forStreamWriter(pruner).write(expected);
107
108     }
109
110
111     @Test
112     public void testNodesPrunedWhenAugmentationSchemaNotPresent() throws IOException {
113         NormalizedNodePruner pruner = prunerNoAugSchema;
114
115         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
116
117         NormalizedNode<?, ?> expected = createTestContainer();
118
119         normalizedNodeWriter.write(expected);
120
121         NormalizedNode<?, ?> actual = pruner.normalizedNode();
122
123         Assert.assertNotEquals(expected, actual);
124
125         // Asserting true here instead of checking actual value because I don't want this assertion to be fragile
126         assertTrue(countNodes(expected, "store:aug") > 0);
127
128         // All nodes from the augmentation module are gone from the resulting node
129         assertEquals(0, countNodes(actual, "store:aug"));
130     }
131
132     @Test
133     public void testNodesPrunedWhenTestSchemaNotPresent() throws IOException {
134         NormalizedNodePruner pruner = new NormalizedNodePruner(TestModel.createTestContextWithoutTestSchema());
135
136         NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(pruner);
137
138         NormalizedNode<?, ?> expected = createTestContainer();
139
140         normalizedNodeWriter.write(expected);
141
142         NormalizedNode<?, ?> actual = pruner.normalizedNode();
143
144         // Since top level schema is missing null is returned
145         assertNull(actual);
146
147         // Asserting true here instead of checking actual value because I don't want this assertion to be fragile
148         assertTrue(countNodes(expected, "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test") > 0);
149
150     }
151
152
153     private static int countNodes(NormalizedNode<?,?> normalizedNode, final String namespaceFilter){
154         if(normalizedNode == null){
155             return 0;
156         }
157         final AtomicInteger count = new AtomicInteger();
158         new NormalizedNodeNavigator(new NormalizedNodeVisitor() {
159
160             @Override
161             public void visitNode(int level, String parentPath, NormalizedNode<?, ?> normalizedNode) {
162                 if(!(normalizedNode.getIdentifier() instanceof YangInstanceIdentifier.AugmentationIdentifier)) {
163                     if (normalizedNode.getIdentifier().getNodeType().getNamespace().toString().contains(namespaceFilter)) {
164                         count.incrementAndGet();
165                     }
166                 }
167             }
168         }).navigate(YangInstanceIdentifier.builder().build().toString(), normalizedNode);
169
170         return count.get();
171     }
172
173     @Test
174     public void testLeafNodeHasNoParent() throws IOException {
175         NormalizedNode<?, ?> input = Builders.leafBuilder().withNodeIdentifier(
176                 new YangInstanceIdentifier.NodeIdentifier(TestModel.DESC_QNAME)).withValue("test").build();
177         NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
178
179         NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
180         assertEquals("normalizedNode", input, actual);
181     }
182
183     @Test
184     public void testLeafNodeHasParent() throws IOException {
185         LeafNode<Object> child = Builders.leafBuilder().withNodeIdentifier(
186                 new YangInstanceIdentifier.NodeIdentifier(TestModel.DESC_QNAME)).withValue("test").build();
187         NormalizedNode<?, ?> input = Builders.containerBuilder().withNodeIdentifier(
188                 new YangInstanceIdentifier.NodeIdentifier(TestModel.AUG_CONT_QNAME)).withChild(child).build();
189         NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
190
191         NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
192         assertEquals("normalizedNode", input, actual);
193     }
194
195     @Test
196     public void testLeafNodeSchemaMissing() throws IOException {
197         prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
198         prunerNoAugSchema.leafNode(new YangInstanceIdentifier.NodeIdentifier(TestModel.AUG_CONT_QNAME), mock(Object.class));
199         verify(normalizedNodeContainerBuilder, never()).addChild(any(NormalizedNode.class));
200     }
201
202     @Test
203     public void testLeafSetEntryNodeHasNoParent() throws IOException {
204         NormalizedNode<?, ?> input = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
205                 new YangInstanceIdentifier.NodeWithValue<>(TestModel.FAMILY_QNAME, "test")).build();
206         NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
207
208         NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
209         assertEquals("normalizedNode", input, actual);
210     }
211
212     @Test
213     public void testLeafSetEntryNodeHasParent() throws IOException {
214         LeafSetEntryNode<Object> child = Builders.leafSetEntryBuilder().withValue("test").withNodeIdentifier(
215                 new YangInstanceIdentifier.NodeWithValue<>(TestModel.FAMILY_QNAME, "test")).build();
216         NormalizedNode<?, ?> input = Builders.leafSetBuilder().withNodeIdentifier(
217                 new YangInstanceIdentifier.NodeIdentifier(TestModel.FAMILY_QNAME)).withChild(child).build();
218         NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
219
220         NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
221         assertEquals("normalizedNode", input, actual);
222     }
223
224     @Test
225     public void testLeafSetEntryNodeSchemaMissing() throws IOException {
226         doReturn(new YangInstanceIdentifier.NodeIdentifier(TestModel.AUG_CONT_QNAME)).when(normalizedNodeBuilderWrapper).identifier();
227
228         prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
229         prunerNoAugSchema.leafSetEntryNode(TestModel.AUG_CONT_QNAME, "");
230
231         verify(normalizedNodeContainerBuilder, never()).addChild(any(NormalizedNode.class));
232     }
233
234     @Test
235     public void testAnyXMLNodeHasNoParent() throws IOException {
236         NormalizedNode<?, ?> input = Builders.anyXmlBuilder().withNodeIdentifier(
237                 new YangInstanceIdentifier.NodeIdentifier(TestModel.CHILD_NAME_QNAME)).
238                     withValue(mock(DOMSource.class)).build();
239         NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
240
241         NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
242         assertEquals("normalizedNode", input, actual);
243     }
244
245     @Test
246     public void testAnyXMLNodeHasParent() throws IOException {
247         AnyXmlNode child = Builders.anyXmlBuilder().withNodeIdentifier(
248                 new YangInstanceIdentifier.NodeIdentifier(TestModel.CHILD_NAME_QNAME)).
249                     withValue(mock(DOMSource.class)).build();
250         NormalizedNode<?, ?> input = Builders.containerBuilder().withNodeIdentifier(
251                 new YangInstanceIdentifier.NodeIdentifier(TestModel.AUG_CONT_QNAME)).withChild(child).build();
252         NormalizedNodeWriter.forStreamWriter(prunerFullSchema).write(input);
253
254         NormalizedNode<?, ?> actual = prunerFullSchema.normalizedNode();
255         assertEquals("normalizedNode", input, actual);
256     }
257
258     @Test
259     public void testAnyXmlNodeSchemaMissing() throws IOException {
260         prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
261         prunerNoAugSchema.anyxmlNode(new YangInstanceIdentifier.NodeIdentifier(TestModel.AUG_CONT_QNAME), mock(DOMSource.class));
262
263         verify(normalizedNodeContainerBuilder, never()).addChild(any(NormalizedNode.class));
264     }
265
266
267     @Test
268     public void testLeafSetPushesBuilderToStack() throws IOException {
269         prunerFullSchema.startLeafSet(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
270
271         assertEquals(1, prunerFullSchema.stack().size());
272         assertNotNull(prunerFullSchema.stack().peek());
273         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof ListNodeBuilder);
274     }
275
276     @Test
277     public void testStartContainerNodePushesBuilderToStack() throws IOException {
278         prunerFullSchema.startContainerNode(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
279
280         assertEquals(1, prunerFullSchema.stack().size());
281         assertNotNull(prunerFullSchema.stack().peek());
282         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeAttrBuilder);
283     }
284
285     @Test
286     public void testStartUnkeyedListPushesBuilderToStack() throws IOException {
287         prunerFullSchema.startUnkeyedList(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
288
289         assertEquals(1, prunerFullSchema.stack().size());
290         assertNotNull(prunerFullSchema.stack().peek());
291         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof CollectionNodeBuilder);
292     }
293
294     @Test
295     public void testStartUnkeyedListItemPushesBuilderToStack() throws IOException {
296         prunerFullSchema.startUnkeyedListItem(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
297
298         assertEquals(1, prunerFullSchema.stack().size());
299         assertNotNull(prunerFullSchema.stack().peek());
300         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeAttrBuilder);
301     }
302
303     @Test
304     public void testStartMapNodePushesBuilderToStack() throws IOException {
305         prunerFullSchema.startMapNode(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
306
307         assertEquals(1, prunerFullSchema.stack().size());
308         assertNotNull(prunerFullSchema.stack().peek());
309         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof CollectionNodeBuilder);
310     }
311
312     @Test
313     public void testStartMapEntryNodePushesBuilderToStack() throws IOException {
314         prunerFullSchema.startMapEntryNode(
315                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(TestModel.BOOLEAN_LEAF_QNAME,
316                         ImmutableMap.<QName, Object>of(TestModel.BOOLEAN_LEAF_QNAME, "value")), 10);
317
318         assertEquals(1, prunerFullSchema.stack().size());
319         assertNotNull(prunerFullSchema.stack().peek());
320         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeAttrBuilder);
321     }
322
323     @Test
324     public void testStartOrderedMapNodePushesBuilderToStack() throws IOException {
325         prunerFullSchema.startOrderedMapNode(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
326
327         assertEquals(1, prunerFullSchema.stack().size());
328         assertNotNull(prunerFullSchema.stack().peek());
329         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof CollectionNodeBuilder);
330     }
331
332     @Test
333     public void testStartChoiceNodePushesBuilderToStack() throws IOException {
334         prunerFullSchema.startChoiceNode(new YangInstanceIdentifier.NodeIdentifier(TestModel.BOOLEAN_LEAF_QNAME), 10);
335
336         assertEquals(1, prunerFullSchema.stack().size());
337         assertNotNull(prunerFullSchema.stack().peek());
338         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeBuilder);
339     }
340
341     @Test
342     public void testStartAugmentationPushesBuilderToStack() throws IOException {
343         prunerFullSchema.startAugmentationNode(new YangInstanceIdentifier.AugmentationIdentifier(ImmutableSet.of(TestModel.AUG_CONT_QNAME)));
344
345         assertEquals(1, prunerFullSchema.stack().size());
346         assertNotNull(prunerFullSchema.stack().peek());
347         assertTrue(prunerFullSchema.stack().peek().builder().toString(), prunerFullSchema.stack().peek().builder() instanceof DataContainerNodeBuilder);
348     }
349
350     @Test(expected = IllegalStateException.class)
351     public void testEndNodeWhenNoBuildersOnStack() throws IOException {
352         prunerFullSchema.endNode();
353     }
354
355     @Test
356     public void testEndNodeWhenOneBuildersOnStack() throws IOException {
357         prunerFullSchema.stack().push(normalizedNodeBuilderWrapper);
358         prunerFullSchema.endNode();
359         assertEquals(normalizedNode, prunerFullSchema.normalizedNode());
360     }
361
362     @Test
363     public void testEndNodeSchemaMissing() throws IOException {
364         doReturn(new YangInstanceIdentifier.NodeIdentifier(TestModel.AUG_CONT_QNAME)).when(normalizedNodeBuilderWrapper).identifier();
365
366         prunerNoAugSchema.stack().push(normalizedNodeBuilderWrapper);
367         prunerNoAugSchema.endNode();
368
369         assertEquals(null, prunerNoAugSchema.normalizedNode());
370     }
371
372     @Test
373     public void testEndNodeWhenMoreThanOneBuilderOnStack() throws IOException {
374         // A little lazy in adding the "parent" builder
375         prunerFullSchema.stack().push(normalizedNodeBuilderWrapper);
376         prunerFullSchema.stack().push(normalizedNodeBuilderWrapper);
377         prunerFullSchema.endNode();
378         assertEquals(null, prunerFullSchema.normalizedNode());
379
380         verify(normalizedNodeContainerBuilder).addChild(any(NormalizedNode.class));
381     }
382
383     private static NormalizedNode<?, ?> createTestContainer() {
384         byte[] bytes1 = {1,2,3};
385         LeafSetEntryNode<Object> entry1 = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
386                 new YangInstanceIdentifier.NodeWithValue(TestModel.BINARY_LEAF_LIST_QNAME, bytes1)).
387                 withValue(bytes1).build();
388
389         byte[] bytes2 = {};
390         LeafSetEntryNode<Object> entry2 = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
391                 new YangInstanceIdentifier.NodeWithValue(TestModel.BINARY_LEAF_LIST_QNAME, bytes2)).
392                 withValue(bytes2).build();
393
394         LeafSetEntryNode<Object> entry3 = ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier(
395                 new YangInstanceIdentifier.NodeWithValue(TestModel.BINARY_LEAF_LIST_QNAME, null)).
396                 withValue(null).build();
397
398
399         return TestModel.createBaseTestContainerBuilder().
400                 withChild(ImmutableLeafSetNodeBuilder.create().withNodeIdentifier(
401                         new YangInstanceIdentifier.NodeIdentifier(TestModel.BINARY_LEAF_LIST_QNAME)).
402                         withChild(entry1).withChild(entry2).withChild(entry3).build()).
403                 withChild(ImmutableNodes.leafNode(TestModel.SOME_BINARY_DATA_QNAME, new byte[]{1, 2, 3, 4})).
404                 withChild(Builders.orderedMapBuilder().
405                         withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.ORDERED_LIST_QNAME)).
406                         withChild(ImmutableNodes.mapEntry(TestModel.ORDERED_LIST_ENTRY_QNAME,
407                                 TestModel.ID_QNAME, 11)).build()).
408                 build();
409     }
410 }