fb28704952ac2d6b93a613bf9e4f73dcce011034
[controller.git] / opendaylight / md-sal / sal-protocolbuffer-encoding / src / test / java / org / opendaylight / controller / cluster / datastore / util / NormalizedNodeXmlConverterTest.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the terms of the Eclipse
5  * Public License v1.0 which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.cluster.datastore.util;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.Lists;
12 import com.google.common.collect.Maps;
13 import com.google.common.collect.Sets;
14 import org.custommonkey.xmlunit.Diff;
15 import org.custommonkey.xmlunit.XMLUnit;
16 import org.junit.Test;
17 import org.opendaylight.controller.protobuff.messages.common.SimpleNormalizedNodeMessage;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
25 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
26 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
27 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
28 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
29 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
30 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
31 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
32 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
33 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
34 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
35 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.Module;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
40 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45 import org.xml.sax.SAXException;
46
47 import javax.xml.parsers.DocumentBuilder;
48 import javax.xml.parsers.DocumentBuilderFactory;
49 import javax.xml.parsers.ParserConfigurationException;
50 import javax.xml.transform.OutputKeys;
51 import javax.xml.transform.Transformer;
52 import javax.xml.transform.TransformerException;
53 import javax.xml.transform.TransformerFactory;
54 import javax.xml.transform.TransformerFactoryConfigurationError;
55 import javax.xml.transform.dom.DOMSource;
56 import javax.xml.transform.stream.StreamResult;
57 import java.io.ByteArrayInputStream;
58 import java.io.IOException;
59 import java.io.InputStream;
60 import java.io.StringWriter;
61 import java.net.URI;
62 import java.text.ParseException;
63 import java.text.SimpleDateFormat;
64 import java.util.ArrayList;
65 import java.util.Collection;
66 import java.util.Collections;
67 import java.util.Date;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Set;
71
72
73 /**
74  * Two of the testcases in the yangtools/yang-data-impl are leveraged (with  modification) to
75  * create the serialization of NormalizedNode using the ProtocolBuffer
76  *
77  * @syedbahm
78  *
79  */
80
81
82 public class NormalizedNodeXmlConverterTest {
83     private static final Logger logger = LoggerFactory
84         .getLogger(NormalizedNodeXmlConverterTest.class);
85     public static final String NAMESPACE =
86         "urn:opendaylight:params:xml:ns:yang:controller:test";
87     private static Date revision;
88     private ContainerNode expectedNode;
89     private ContainerSchemaNode containerNode;
90     private String xmlPath;
91
92     static {
93         try {
94             revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-03-13");
95         } catch (ParseException e) {
96             throw new RuntimeException(e);
97         }
98     }
99
100     public static DataSchemaNode getSchemaNode(final SchemaContext context,
101         final String moduleName, final String childNodeName) {
102         for (Module module : context.getModules()) {
103             if (module.getName().equals(moduleName)) {
104                 DataSchemaNode found =
105                     findChildNode(module.getChildNodes(), childNodeName);
106                 Preconditions.checkState(found != null, "Unable to find %s",
107                     childNodeName);
108                 return found;
109             }
110         }
111         throw new IllegalStateException("Unable to find child node "
112             + childNodeName);
113     }
114
115     static DataSchemaNode findChildNode(final Collection<DataSchemaNode> children, final String name) {
116         List<DataNodeContainer> containers = Lists.newArrayList();
117
118         for (DataSchemaNode dataSchemaNode : children) {
119             if (dataSchemaNode.getQName().getLocalName().equals(name)) {
120                 return dataSchemaNode;
121             }
122             if (dataSchemaNode instanceof DataNodeContainer) {
123                 containers.add((DataNodeContainer) dataSchemaNode);
124             } else if (dataSchemaNode instanceof ChoiceNode) {
125                 containers.addAll(((ChoiceNode) dataSchemaNode).getCases());
126             }
127         }
128
129         for (DataNodeContainer container : containers) {
130             DataSchemaNode retVal = findChildNode(container.getChildNodes(), name);
131             if (retVal != null) {
132                 return retVal;
133             }
134         }
135
136         return null;
137     }
138
139     public static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(
140         final String localName) {
141         return new YangInstanceIdentifier.NodeIdentifier(QName.create(
142             URI.create(NAMESPACE), revision, localName));
143     }
144
145     public static YangInstanceIdentifier.AugmentationIdentifier getAugmentIdentifier(
146         final String... childNames) {
147         Set<QName> qn = Sets.newHashSet();
148
149         for (String childName : childNames) {
150             qn.add(getNodeIdentifier(childName).getNodeType());
151         }
152
153         return new YangInstanceIdentifier.AugmentationIdentifier(qn);
154     }
155
156
157     public static ContainerNode augmentChoiceExpectedNode() {
158
159         DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b =
160             Builders.containerBuilder();
161         b.withNodeIdentifier(getNodeIdentifier("container"));
162
163         b.withChild(Builders
164             .choiceBuilder()
165             .withNodeIdentifier(getNodeIdentifier("ch2"))
166             .withChild(
167                 Builders.leafBuilder()
168                     .withNodeIdentifier(getNodeIdentifier("c2Leaf")).withValue("2")
169                     .build())
170             .withChild(
171                 Builders
172                     .choiceBuilder()
173                     .withNodeIdentifier(getNodeIdentifier("c2DeepChoice"))
174                     .withChild(
175                         Builders
176                             .leafBuilder()
177                             .withNodeIdentifier(
178                                 getNodeIdentifier("c2DeepChoiceCase1Leaf2"))
179                             .withValue("2").build()).build()).build());
180
181         b.withChild(Builders
182             .choiceBuilder()
183             .withNodeIdentifier(getNodeIdentifier("ch3"))
184             .withChild(
185                 Builders.leafBuilder()
186                     .withNodeIdentifier(getNodeIdentifier("c3Leaf")).withValue("3")
187                     .build()).build());
188
189         b.withChild(Builders
190             .augmentationBuilder()
191             .withNodeIdentifier(getAugmentIdentifier("augLeaf"))
192             .withChild(
193                 Builders.leafBuilder()
194                     .withNodeIdentifier(getNodeIdentifier("augLeaf"))
195                     .withValue("augment").build()).build());
196
197         b.withChild(Builders
198             .augmentationBuilder()
199             .withNodeIdentifier(getAugmentIdentifier("ch"))
200             .withChild(
201                 Builders
202                     .choiceBuilder()
203                     .withNodeIdentifier(getNodeIdentifier("ch"))
204                     .withChild(
205                         Builders.leafBuilder()
206                             .withNodeIdentifier(getNodeIdentifier("c1Leaf"))
207                             .withValue("1").build())
208                     .withChild(
209                         Builders
210                             .augmentationBuilder()
211                             .withNodeIdentifier(
212                                 getAugmentIdentifier("c1Leaf_AnotherAugment",
213                                     "deepChoice"))
214                             .withChild(
215                                 Builders
216                                     .leafBuilder()
217                                     .withNodeIdentifier(
218                                         getNodeIdentifier("c1Leaf_AnotherAugment"))
219                                     .withValue("1").build())
220                             .withChild(
221                                 Builders
222                                     .choiceBuilder()
223                                     .withNodeIdentifier(
224                                         getNodeIdentifier("deepChoice"))
225                                     .withChild(
226                                         Builders
227                                             .leafBuilder()
228                                             .withNodeIdentifier(
229                                                 getNodeIdentifier("deepLeafc1"))
230                                             .withValue("1").build()).build())
231                             .build()).build()).build());
232
233         return b.build();
234     }
235
236
237
238     public void init(final String yangPath, final String xmlPath, final ContainerNode expectedNode)
239         throws Exception {
240         SchemaContext schema = parseTestSchema(yangPath);
241         this.xmlPath = xmlPath;
242         this.containerNode =
243             (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
244         this.expectedNode = expectedNode;
245     }
246
247     SchemaContext parseTestSchema(final String yangPath) throws Exception {
248
249         YangParserImpl yangParserImpl = new YangParserImpl();
250         InputStream stream =
251             NormalizedNodeXmlConverterTest.class.getResourceAsStream(yangPath);
252         ArrayList<InputStream> al = new ArrayList<InputStream>();
253         al.add(stream);
254         Set<Module> modules = yangParserImpl.parseYangModelsFromStreams(al);
255         return yangParserImpl.resolveSchemaContext(modules);
256
257     }
258
259
260     @Test
261     public void testConversionWithAugmentChoice() throws Exception {
262         init("/augment_choice.yang", "/augment_choice.xml",
263             augmentChoiceExpectedNode());
264         Document doc = loadDocument(xmlPath);
265
266         ContainerNode built =
267             DomToNormalizedNodeParserFactory
268                 .getInstance(DomUtils.defaultValueCodecProvider())
269                 .getContainerNodeParser()
270                 .parse(Collections.singletonList(doc.getDocumentElement()),
271                     containerNode);
272
273         if (expectedNode != null) {
274             junit.framework.Assert.assertEquals(expectedNode, built);
275         }
276
277         logger.info("{}", built);
278
279         Iterable<Element> els =
280             DomFromNormalizedNodeSerializerFactory
281                 .getInstance(XmlDocumentUtils.getDocument(),
282                     DomUtils.defaultValueCodecProvider())
283                 .getContainerNodeSerializer().serialize(containerNode, built);
284
285         Element el = els.iterator().next();
286
287         XMLUnit.setIgnoreWhitespace(true);
288         XMLUnit.setIgnoreComments(true);
289
290         System.out.println(toString(doc.getDocumentElement()));
291         System.out.println(toString(el));
292
293         new Diff(
294             XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
295             XMLUnit.buildTestDocument(toString(el))).similar();
296     }
297
298     private static ContainerNode listLeafListWithAttributes() {
299         DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b =
300             Builders.containerBuilder();
301         b.withNodeIdentifier(getNodeIdentifier("container"));
302
303         CollectionNodeBuilder<MapEntryNode, MapNode> listBuilder =
304             Builders.mapBuilder().withNodeIdentifier(getNodeIdentifier("list"));
305
306         Map<QName, Object> predicates = Maps.newHashMap();
307         predicates.put(getNodeIdentifier("uint32InList").getNodeType(), 3L);
308
309         DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> list1Builder =
310             Builders.mapEntryBuilder().withNodeIdentifier(
311                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
312                     getNodeIdentifier("list").getNodeType(), predicates));
313         NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> uint32InListBuilder =
314             Builders.leafBuilder().withNodeIdentifier(
315                 getNodeIdentifier("uint32InList"));
316
317         list1Builder.withChild(uint32InListBuilder.withValue(3L).build());
318
319         listBuilder.withChild(list1Builder.build());
320         b.withChild(listBuilder.build());
321
322         NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> booleanBuilder =
323             Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("boolean"));
324         booleanBuilder.withValue(false);
325         b.withChild(booleanBuilder.build());
326
327         ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafListBuilder =
328             Builders.leafSetBuilder().withNodeIdentifier(
329                 getNodeIdentifier("leafList"));
330
331         NormalizedNodeBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>> leafList1Builder =
332             Builders.leafSetEntryBuilder().withNodeIdentifier(
333                 new YangInstanceIdentifier.NodeWithValue(getNodeIdentifier("leafList")
334                     .getNodeType(), "a"));
335
336         leafList1Builder.withValue("a");
337
338         leafListBuilder.withChild(leafList1Builder.build());
339         b.withChild(leafListBuilder.build());
340
341         return b.build();
342     }
343
344
345     @Test
346     public void testConversionWithAttributes() throws Exception {
347         init("/test.yang", "/simple_xml_with_attributes.xml",
348             listLeafListWithAttributes());
349         Document doc = loadDocument(xmlPath);
350
351         ContainerNode built =
352             DomToNormalizedNodeParserFactory
353                 .getInstance(DomUtils.defaultValueCodecProvider())
354                 .getContainerNodeParser()
355                 .parse(Collections.singletonList(doc.getDocumentElement()),
356                     containerNode);
357
358         if (expectedNode != null) {
359             junit.framework.Assert.assertEquals(expectedNode, built);
360         }
361
362         logger.info("{}", built);
363
364         Iterable<Element> els =
365             DomFromNormalizedNodeSerializerFactory
366                 .getInstance(XmlDocumentUtils.getDocument(),
367                     DomUtils.defaultValueCodecProvider())
368                 .getContainerNodeSerializer().serialize(containerNode, built);
369
370         Element el = els.iterator().next();
371
372         XMLUnit.setIgnoreWhitespace(true);
373         XMLUnit.setIgnoreComments(true);
374
375         System.out.println(toString(doc.getDocumentElement()));
376         System.out.println(toString(el));
377
378         new Diff(
379             XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
380             XMLUnit.buildTestDocument(toString(el))).similar();
381     }
382
383
384     private Document loadDocument(final String xmlPath) throws Exception {
385         InputStream resourceAsStream =
386             NormalizedNodeXmlConverterTest.class.getResourceAsStream(xmlPath);
387
388         Document currentConfigElement = readXmlToDocument(resourceAsStream);
389         Preconditions.checkNotNull(currentConfigElement);
390         return currentConfigElement;
391     }
392
393     private static final DocumentBuilderFactory BUILDERFACTORY;
394
395     static {
396         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
397         factory.setNamespaceAware(true);
398         factory.setCoalescing(true);
399         factory.setIgnoringElementContentWhitespace(true);
400         factory.setIgnoringComments(true);
401         BUILDERFACTORY = factory;
402     }
403
404     private Document readXmlToDocument(final InputStream xmlContent)
405         throws IOException, SAXException {
406         DocumentBuilder dBuilder;
407         try {
408             dBuilder = BUILDERFACTORY.newDocumentBuilder();
409         } catch (ParserConfigurationException e) {
410             throw new RuntimeException("Failed to parse XML document", e);
411         }
412         Document doc = dBuilder.parse(xmlContent);
413
414         doc.getDocumentElement().normalize();
415         return doc;
416     }
417
418     public static String toString(final Element xml) {
419         try {
420             Transformer transformer =
421                 TransformerFactory.newInstance().newTransformer();
422             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
423
424             StreamResult result = new StreamResult(new StringWriter());
425             DOMSource source = new DOMSource(xml);
426             transformer.transform(source, result);
427
428             return result.getWriter().toString();
429         } catch (IllegalArgumentException | TransformerFactoryConfigurationError
430             | TransformerException e) {
431             throw new RuntimeException("Unable to serialize xml element " + xml, e);
432         }
433     }
434
435     @Test
436     public void testConversionToNormalizedXml() throws Exception {
437         SimpleNormalizedNodeMessage.NormalizedNodeXml nnXml =
438             EncoderDecoderUtil.encode(parseTestSchema("/augment_choice.yang"),
439                 augmentChoiceExpectedNode());
440         Document expectedDoc = loadDocument("/augment_choice.xml");
441         Document convertedDoc =
442             EncoderDecoderUtil.factory.newDocumentBuilder().parse(
443                 new ByteArrayInputStream(nnXml.getXmlString().getBytes("utf-8")));
444         System.out.println(toString(convertedDoc.getDocumentElement()));
445         XMLUnit.setIgnoreWhitespace(true);
446         XMLUnit.setIgnoreComments(true);
447         new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
448             .getDocumentElement())),
449             XMLUnit.buildTestDocument(toString(convertedDoc
450                 .getDocumentElement()))).similar();
451         System.out.println(toString(expectedDoc.getDocumentElement()));
452
453     }
454
455
456     @Test
457     public void testConversionFromXmlToNormalizedNode() throws Exception {
458         SimpleNormalizedNodeMessage.NormalizedNodeXml nnXml =
459             EncoderDecoderUtil.encode(parseTestSchema("/test.yang"),
460                 listLeafListWithAttributes());
461         Document expectedDoc = loadDocument("/simple_xml_with_attributes.xml");
462         Document convertedDoc =
463             EncoderDecoderUtil.factory.newDocumentBuilder().parse(
464                 new ByteArrayInputStream(nnXml.getXmlString().getBytes("utf-8")));
465         System.out.println(toString(convertedDoc.getDocumentElement()));
466         XMLUnit.setIgnoreWhitespace(true);
467         XMLUnit.setIgnoreComments(true);
468         new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
469             .getDocumentElement())),
470             XMLUnit.buildTestDocument(toString(convertedDoc
471                 .getDocumentElement()))).similar();
472         System.out.println(toString(expectedDoc.getDocumentElement()));
473
474         // now we will try to convert xml back to normalize node.
475         ContainerNode cn =
476             (ContainerNode) EncoderDecoderUtil.decode(
477                 parseTestSchema("/test.yang"), nnXml);
478         junit.framework.Assert.assertEquals(listLeafListWithAttributes(), cn);
479
480     }
481
482 }