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