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