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