8180a055bd3b41b44c01cf0fb27084325bb1e1be
[yangtools.git] / codec / yang-data-codec-xml / src / test / java / org / opendaylight / yangtools / yang / data / codec / xml / XmlToNormalizedNodesTest.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 package org.opendaylight.yangtools.yang.data.codec.xml;
9
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertThrows;
15
16 import com.google.common.collect.ImmutableSet;
17 import java.io.IOException;
18 import java.net.URISyntaxException;
19 import java.util.HashMap;
20 import java.util.Map;
21 import javax.xml.parsers.ParserConfigurationException;
22 import javax.xml.stream.XMLStreamException;
23 import org.junit.AfterClass;
24 import org.junit.BeforeClass;
25 import org.junit.Test;
26 import org.opendaylight.yangtools.util.xml.UntrustedXML;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.common.QNameModule;
29 import org.opendaylight.yangtools.yang.common.XMLNamespace;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
34 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
41 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
42 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
43 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
44 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
45 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
46 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
47 import org.xml.sax.SAXException;
48
49 public class XmlToNormalizedNodesTest {
50
51     private static final QNameModule FOO_MODULE = QNameModule.create(XMLNamespace.of("foo-namespace"));
52     private static final QName PARENT_CONTAINER = QName.create(FOO_MODULE, "parent-container");
53
54     private static final QNameModule BAZ_MODULE = QNameModule.create(XMLNamespace.of("baz-namespace"));
55     private static final QName OUTER_CONTAINER = QName.create(BAZ_MODULE, "outer-container");
56
57     private static final QName MY_CONTAINER_1 = QName.create(BAZ_MODULE, "my-container-1");
58     private static final QName MY_KEYED_LIST = QName.create(BAZ_MODULE, "my-keyed-list");
59     private static final QName MY_KEY_LEAF = QName.create(BAZ_MODULE, "my-key-leaf");
60     private static final QName MY_LEAF_IN_LIST_1 = QName.create(BAZ_MODULE, "my-leaf-in-list-1");
61     private static final QName MY_LEAF_IN_LIST_2 = QName.create(BAZ_MODULE, "my-leaf-in-list-2");
62     private static final QName MY_LEAF_1 = QName.create(BAZ_MODULE, "my-leaf-1");
63     private static final QName MY_LEAFLIST = QName.create(BAZ_MODULE, "my-leaf-list");
64
65     private static final QName MY_CONTAINER_2 = QName.create(BAZ_MODULE, "my-container-2");
66     private static final QName INNER_CONTAINER = QName.create(BAZ_MODULE, "inner-container");
67     private static final QName MY_LEAF_2 = QName.create(BAZ_MODULE, "my-leaf-2");
68     private static final QName MY_LEAF_3 = QName.create(BAZ_MODULE, "my-leaf-3");
69     private static final QName MY_CHOICE = QName.create(BAZ_MODULE, "my-choice");
70     private static final QName MY_LEAF_IN_CASE_2 = QName.create(BAZ_MODULE, "my-leaf-in-case-2");
71
72     private static final QName MY_CONTAINER_3 = QName.create(BAZ_MODULE, "my-container-3");
73     private static final QName MY_DOUBLY_KEYED_LIST = QName.create(BAZ_MODULE, "my-doubly-keyed-list");
74     private static final QName MY_FIRST_KEY_LEAF = QName.create(BAZ_MODULE, "my-first-key-leaf");
75     private static final QName MY_SECOND_KEY_LEAF = QName.create(BAZ_MODULE, "my-second-key-leaf");
76     private static final QName MY_LEAF_IN_LIST_3 = QName.create(BAZ_MODULE, "my-leaf-in-list-3");
77
78     private static EffectiveModelContext schemaContext;
79     private static Inference outerContainerSchema;
80     private static Inference parentContainerSchema;
81
82     @BeforeClass
83     public static void setup() {
84         schemaContext = YangParserTestUtils.parseYangResourceDirectory("/");
85         parentContainerSchema = Inference.ofDataTreePath(schemaContext, PARENT_CONTAINER);
86         outerContainerSchema = Inference.ofDataTreePath(schemaContext, OUTER_CONTAINER);
87     }
88
89     @AfterClass
90     public static void cleanup() {
91         schemaContext = null;
92         parentContainerSchema = null;
93         outerContainerSchema = null;
94     }
95
96     @Test
97     public void testComplexXmlParsing() throws IOException, SAXException, URISyntaxException, XMLStreamException,
98             ParserConfigurationException {
99         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/baz.xml");
100         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
101         final var result = new NormalizationResultHolder();
102         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
103
104         try (var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema)) {
105             xmlParser.parse(reader);
106         }
107
108         final NormalizedNode transformedInput = result.getResult().data();
109         assertNotNull(transformedInput);
110
111         final NormalizedNode expectedNormalizedNode = buildOuterContainerNode();
112         assertNotNull(expectedNormalizedNode);
113
114         assertEquals(expectedNormalizedNode, transformedInput);
115     }
116
117     @Test
118     public void testSimpleXmlParsing() throws IOException, URISyntaxException, XMLStreamException,
119             ParserConfigurationException, SAXException {
120         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/foo.xml");
121         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
122         final var result = new NormalizationResultHolder();
123         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
124         final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
125         xmlParser.parse(reader);
126
127         final var transformedInput = result.getResult().data();
128         assertNotNull(transformedInput);
129     }
130
131     @Test
132     public void shouldFailOnDuplicateLeaf() throws XMLStreamException, IOException,
133             ParserConfigurationException, SAXException, URISyntaxException {
134         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo.xml");
135         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
136         final var result = new NormalizationResultHolder();
137         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
138         final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
139         final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
140         assertThat(ex.getMessage(), containsString("""
141             Duplicate element "decimal64-leaf" in namespace "foo-namespace" with parent \
142             "EmptyContainerEffectiveStatement{argument=(foo-namespace)leaf-container}" in XML input"""));
143     }
144
145     @Test
146     public void shouldFailOnDuplicateAnyXml() throws XMLStreamException, IOException,
147             ParserConfigurationException, SAXException, URISyntaxException {
148         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-2.xml");
149         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
150         final var result = new NormalizationResultHolder();
151         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
152         final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
153         final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
154         assertThat(ex.getMessage(), containsString("""
155             Duplicate element "my-anyxml" in namespace "foo-namespace" with parent \
156             "EmptyContainerEffectiveStatement{argument=(foo-namespace)anyxml-container}" in XML input"""));
157     }
158
159     @Test
160     public void shouldFailOnDuplicateContainer() throws XMLStreamException, IOException,
161             ParserConfigurationException, SAXException, URISyntaxException {
162         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-foo-3.xml");
163         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
164         final var result = new NormalizationResultHolder();
165         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
166         final var xmlParser = XmlParserStream.create(streamWriter, parentContainerSchema);
167         final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
168         assertThat(ex.getMessage(), containsString("""
169             Duplicate element "leaf-container" in namespace "foo-namespace" with parent \
170             "EmptyContainerEffectiveStatement{argument=(foo-namespace)parent-container}" in XML input"""));
171     }
172
173     @Test
174     public void shouldFailOnUnterminatedLeafElement() throws XMLStreamException, IOException,
175             ParserConfigurationException, SAXException, URISyntaxException {
176         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz.xml");
177         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
178         final var result = new NormalizationResultHolder();
179         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
180         final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
181         final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
182         assertThat(ex.getMessage(), containsString(" START_ELEMENT "));
183     }
184
185     @Test
186     public void shouldFailOnUnterminatedLeafElement2() throws XMLStreamException, IOException,
187             ParserConfigurationException, SAXException, URISyntaxException {
188         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-2.xml");
189         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
190         final var result = new NormalizationResultHolder();
191         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
192         final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
193         final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
194         assertThat(ex.getMessage(), containsString("</my-leaf-1>"));
195     }
196
197     @Test
198     public void shouldFailOnUnterminatedContainerElement() throws XMLStreamException, IOException,
199             ParserConfigurationException, SAXException, URISyntaxException {
200         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-4.xml");
201         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
202         final var result = new NormalizationResultHolder();
203         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
204         final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
205         final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
206         assertThat(ex.getMessage(), containsString("</my-container-1>"));
207     }
208
209     @Test
210     public void shouldFailOnUnknownChildNode() throws XMLStreamException, IOException,
211             ParserConfigurationException, SAXException, URISyntaxException {
212         final var resourceAsStream = XmlToNormalizedNodesTest.class.getResourceAsStream("/invalid-baz-3.xml");
213         final var reader = UntrustedXML.createXMLStreamReader(resourceAsStream);
214         final var result = new NormalizationResultHolder();
215         final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
216         final var xmlParser = XmlParserStream.create(streamWriter, outerContainerSchema);
217         final var ex = assertThrows(XMLStreamException.class, () -> xmlParser.parse(reader));
218         assertThat(ex.getMessage(), containsString("""
219             Schema for node with name my-container-1 and namespace baz-namespace does not exist in parent \
220             EmptyContainerEffectiveStatement{argument=(baz-namespace)my-container-1}"""));
221     }
222
223     private static NormalizedNode buildOuterContainerNode() {
224         // my-container-1
225         MapNode myKeyedListNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_KEYED_LIST))
226                 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
227                         NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue1"))
228                         .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
229                                 .withValue("listleafvalue1").build())
230                         .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
231                                 .withValue("listleafvalue2").build()).build())
232                 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
233                         NodeIdentifierWithPredicates.of(MY_KEYED_LIST, MY_KEY_LEAF, "listkeyvalue2"))
234                         .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_1))
235                                 .withValue("listleafvalue12").build())
236                         .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_LIST_2))
237                                 .withValue("listleafvalue22").build()).build()).build();
238
239         LeafNode<?> myLeaf1Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_1))
240                 .withValue("value1").build();
241
242         SystemLeafSetNode<?> myLeafListNode = Builders.leafSetBuilder()
243                 .withNodeIdentifier(new NodeIdentifier(MY_LEAFLIST))
244                 .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
245                         new NodeWithValue<>(MY_LEAFLIST, "lflvalue1")).withValue("lflvalue1").build())
246                 .withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(
247                         new NodeWithValue<>(MY_LEAFLIST, "lflvalue2")).withValue("lflvalue2").build()).build();
248
249         ContainerNode myContainer1Node = Builders.containerBuilder().withNodeIdentifier(
250                 new NodeIdentifier(MY_CONTAINER_1))
251                 .withChild(myKeyedListNode)
252                 .withChild(myLeaf1Node)
253                 .withChild(myLeafListNode).build();
254
255         // my-container-2
256         ContainerNode innerContainerNode = Builders.containerBuilder().withNodeIdentifier(
257                 new NodeIdentifier(INNER_CONTAINER))
258                 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_2))
259                         .withValue("value2").build()).build();
260
261         LeafNode<?> myLeaf3Node = Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_3))
262                 .withValue("value3").build();
263
264         ChoiceNode myChoiceNode = Builders.choiceBuilder().withNodeIdentifier(new NodeIdentifier(MY_CHOICE))
265                 .withChild(Builders.leafBuilder().withNodeIdentifier(new NodeIdentifier(MY_LEAF_IN_CASE_2))
266                         .withValue("case2value").build()).build();
267
268         ContainerNode myContainer2Node = Builders.containerBuilder().withNodeIdentifier(
269                 new NodeIdentifier(MY_CONTAINER_2))
270                 .withChild(innerContainerNode)
271                 .withChild(myLeaf3Node)
272                 .withChild(myChoiceNode).build();
273
274         // my-container-3
275         Map<QName, Object> keys = new HashMap<>();
276         keys.put(MY_FIRST_KEY_LEAF, "listkeyvalue1");
277         keys.put(MY_SECOND_KEY_LEAF, "listkeyvalue2");
278
279         MapNode myDoublyKeyedListNode = Builders.mapBuilder()
280                 .withNodeIdentifier(new NodeIdentifier(MY_DOUBLY_KEYED_LIST))
281                 .withChild(Builders.mapEntryBuilder().withNodeIdentifier(
282                         NodeIdentifierWithPredicates.of(MY_DOUBLY_KEYED_LIST, keys))
283                         .withChild(Builders.leafBuilder().withNodeIdentifier(
284                                 new NodeIdentifier(MY_LEAF_IN_LIST_3)).withValue("listleafvalue1").build()).build())
285                 .build();
286
287         AugmentationNode myDoublyKeyedListAugNode = Builders.augmentationBuilder().withNodeIdentifier(
288                 new AugmentationIdentifier(ImmutableSet.of(MY_DOUBLY_KEYED_LIST)))
289                 .withChild(myDoublyKeyedListNode).build();
290
291         ContainerNode myContainer3Node = Builders.containerBuilder().withNodeIdentifier(
292                 new NodeIdentifier(MY_CONTAINER_3))
293                 .withChild(myDoublyKeyedListAugNode).build();
294
295         AugmentationNode myContainer3AugNode = Builders.augmentationBuilder().withNodeIdentifier(
296                 new AugmentationIdentifier(ImmutableSet.of(MY_CONTAINER_3)))
297                 .withChild(myContainer3Node).build();
298
299         ContainerNode outerContainerNode = Builders.containerBuilder().withNodeIdentifier(
300                 new NodeIdentifier(OUTER_CONTAINER))
301                 .withChild(myContainer1Node)
302                 .withChild(myContainer2Node)
303                 .withChild(myContainer3AugNode).build();
304
305         return outerContainerNode;
306     }
307 }