Bug 1441: Bug fixes, clean-up and test migration
[yangtools.git] / yang / yang-data-codec-xml / src / test / java / org / opendaylight / yangtools / yang / data / codec / xml / XmlStreamUtilsTest.java
1 /*
2  * Copyright (c) 2016 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.yangtools.yang.data.codec.xml;
10
11 import static org.hamcrest.CoreMatchers.containsString;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertThat;
15 import static org.junit.Assert.assertTrue;
16
17 import com.google.common.base.Optional;
18 import com.google.common.collect.Maps;
19 import java.io.ByteArrayOutputStream;
20 import java.io.FileNotFoundException;
21 import java.net.URI;
22 import java.net.URISyntaxException;
23 import java.util.AbstractMap;
24 import java.util.Date;
25 import java.util.Map;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 import javax.xml.stream.XMLOutputFactory;
29 import javax.xml.stream.XMLStreamWriter;
30 import org.custommonkey.xmlunit.Diff;
31 import org.custommonkey.xmlunit.XMLUnit;
32 import org.junit.BeforeClass;
33 import org.junit.Ignore;
34 import org.junit.Test;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.common.QNameModule;
37 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.Module;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
43 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
46 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
47 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
48 import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
49 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
50 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
51 import org.w3c.dom.Document;
52
53 public class XmlStreamUtilsTest {
54
55     public static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
56
57     private static SchemaContext schemaContext;
58     private static Module leafRefModule;
59
60     @BeforeClass
61     public static void initialize() throws URISyntaxException, FileNotFoundException, ReactorException {
62         final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
63         reactor.addSource(new YangStatementSourceImpl("/leafref-test.yang", false));
64
65         schemaContext = reactor.buildEffective();
66         assertNotNull(schemaContext);
67         assertEquals(1, schemaContext.getModules().size());
68         leafRefModule = schemaContext.getModules().iterator().next();
69         assertNotNull(leafRefModule);
70     }
71
72     @Test
73     public void testWriteAttribute() throws Exception {
74         final ByteArrayOutputStream out = new ByteArrayOutputStream();
75         final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
76         writer.writeStartElement("element");
77
78         QName name = getAttrQName("namespace", "2012-12-12", "attr", Optional.of("prefix"));
79         final Map.Entry<QName, String> attributeEntry = new AbstractMap.SimpleEntry<>(name, "value");
80
81         name = getAttrQName("namespace2", "2012-12-12", "attr", Optional.absent());
82         final Map.Entry<QName, String> attributeEntryNoPrefix = new AbstractMap.SimpleEntry<>(name, "value");
83
84         final RandomPrefix randomPrefix = new RandomPrefix();
85         XmlStreamUtils.writeAttribute(writer, attributeEntry, randomPrefix);
86         XmlStreamUtils.writeAttribute(writer, attributeEntryNoPrefix, randomPrefix);
87
88         writer.writeEndElement();
89         writer.close();
90         out.close();
91
92         final String xmlAsString = new String(out.toByteArray());
93
94         final Map<String, String> mappedPrefixes = mapPrefixed(randomPrefix.getPrefixes());
95         assertEquals(2, mappedPrefixes.size());
96         final String randomPrefixValue = mappedPrefixes.get("namespace2");
97
98         final String expectedXmlAsString = "<element xmlns:a=\"namespace\" a:attr=\"value\" xmlns:" + randomPrefixValue
99                 + "=\"namespace2\" " + randomPrefixValue + ":attr=\"value\"></element>";
100
101         XMLUnit.setIgnoreAttributeOrder(true);
102         final Document control = XMLUnit.buildControlDocument(expectedXmlAsString);
103         final Document test = XMLUnit.buildTestDocument(xmlAsString);
104         final Diff diff = XMLUnit.compareXML(control, test);
105
106         final boolean identical = diff.identical();
107         assertTrue("Xml differs: " + diff.toString(), identical);
108     }
109
110     @Test
111     public void testWriteIdentityRef() throws Exception {
112         final ByteArrayOutputStream out = new ByteArrayOutputStream();
113         final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
114
115         writer.writeStartElement("element");
116         final QNameModule parent = QNameModule.create(URI.create("parent:uri"), new Date());
117         XmlStreamUtils.write(writer, null, QName.create(parent, "identity"), Optional.of(parent));
118         writer.writeEndElement();
119
120         writer.writeStartElement("elementDifferent");
121         XmlStreamUtils.write(writer, null, QName.create("different:namespace", "identity"), Optional.of(parent));
122         writer.writeEndElement();
123
124         writer.close();
125         out.close();
126
127         final String xmlAsString = new String(out.toByteArray()).replaceAll("\\s*", "");
128         assertThat(xmlAsString, containsString("element>identity"));
129
130         final Pattern prefixedIdentityPattern = Pattern.compile(".*\"different:namespace\">(.*):identity.*");
131         final Matcher matcher = prefixedIdentityPattern.matcher(xmlAsString);
132         assertTrue("Xml: " + xmlAsString + " should match: " + prefixedIdentityPattern, matcher.matches());
133     }
134
135     /**
136      * One leafref reference to other leafref via relative references
137      */
138     @Test
139     public void testLeafRefRelativeChaining() {
140         getTargetNodeForLeafRef("leafname3", StringTypeDefinition.class);
141     }
142
143     @Test
144     public void testLeafRefRelative() {
145         getTargetNodeForLeafRef("pointToStringLeaf", StringTypeDefinition.class);
146     }
147
148     @Test
149     public void testLeafRefAbsoluteWithSameTarget() {
150         getTargetNodeForLeafRef("absname", InstanceIdentifierTypeDefinition.class);
151     }
152
153     /**
154      * Tests relative path with double point inside path (e. g. "../../lf:interface/../lf:cont2/lf:stringleaf")
155      */
156     // ignored because this isn't implemented
157     @Ignore
158     @Test
159     public void testLeafRefWithDoublePointInPath() {
160         getTargetNodeForLeafRef("lf-with-double-point-inside", StringTypeDefinition.class);
161     }
162
163     @Test
164     public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
165         final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname",
166             InstanceIdentifierTypeDefinition.class);
167         final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname",
168             InstanceIdentifierTypeDefinition.class);
169         assertEquals(targetNodeForAbsname, targetNodeForRelname);
170     }
171
172     private TypeDefinition<?> getTargetNodeForLeafRef(final String nodeName, final Class<?> clas) {
173         final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName);
174         assertNotNull(schemaNode);
175         final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode);
176         assertNotNull(leafrefTypedef);
177         final TypeDefinition<?> targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext,
178                 schemaNode);
179         assertTrue("Wrong class found.", clas.isInstance(targetBaseType));
180         return targetBaseType;
181     }
182
183     private static Map<String, String> mapPrefixed(final Iterable<Map.Entry<URI, String>> prefixes) {
184         final Map<String, String> mappedPrefixes = Maps.newHashMap();
185         for (final Map.Entry<URI, String> prefix : prefixes) {
186             mappedPrefixes.put(prefix.getKey().toString(), prefix.getValue());
187         }
188         return mappedPrefixes;
189     }
190
191     private static QName getAttrQName(final String namespace, final String revision, final String localName,
192             final Optional<String> prefix) {
193         if (prefix.isPresent()) {
194             final QName moduleQName = QName.create(namespace, revision, "module");
195             final QNameModule module = QNameModule.create(moduleQName.getNamespace(), moduleQName.getRevision());
196             return QName.create(module, localName);
197         } else {
198             return QName.create(namespace, revision, localName);
199         }
200     }
201
202     private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) {
203         for (final DataSchemaNode childNode : module.getChildNodes()) {
204             if (childNode instanceof DataNodeContainer) {
205                 LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer) childNode,
206                         nodeName);
207                 if (leafrefFromRecursion != null) {
208                     return leafrefFromRecursion;
209                 }
210             } else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) {
211                 final TypeDefinition<?> leafSchemaNodeType = ((LeafSchemaNode) childNode).getType();
212                 if (leafSchemaNodeType instanceof LeafrefTypeDefinition) {
213                     return (LeafSchemaNode) childNode;
214                 }
215             }
216         }
217         return null;
218     }
219
220     private static LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
221         final TypeDefinition<?> type = schemaNode.getType();
222         if (type instanceof LeafrefTypeDefinition) {
223             return (LeafrefTypeDefinition) type;
224         }
225         return null;
226     }
227 }