Deprecate simple DataTreeFactory.create()
[yangtools.git] / parser / yang-parser-rfc7950 / src / test / java / org / opendaylight / yangtools / yang / stmt / DeviationResolutionTest.java
1 /*
2  * Copyright (c) 2017 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.stmt;
9
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.hamcrest.CoreMatchers.startsWith;
12 import static org.hamcrest.MatcherAssert.assertThat;
13 import static org.junit.jupiter.api.Assertions.assertEquals;
14 import static org.junit.jupiter.api.Assertions.assertFalse;
15 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
16 import static org.junit.jupiter.api.Assertions.assertNotNull;
17 import static org.junit.jupiter.api.Assertions.assertNull;
18 import static org.junit.jupiter.api.Assertions.assertTrue;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.PrintStream;
22 import java.nio.charset.StandardCharsets;
23 import java.util.Optional;
24 import org.junit.jupiter.api.Test;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.Revision;
27 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
35
36 class DeviationResolutionTest extends AbstractYangTest {
37     @Test
38     void testDeviateNotSupported() {
39         final var schemaContext = assertEffectiveModelDir("/deviation-resolution-test/deviation-not-supported");
40
41         final var importedModule = schemaContext.findModule("imported", Revision.of("2017-01-20")).orElseThrow();
42         final var myContA = assertInstanceOf(ContainerSchemaNode.class,
43             importedModule.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-cont-a")));
44
45         assertEquals(1, myContA.getChildNodes().size());
46         assertNotNull(myContA.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-a3")));
47
48         assertNull(importedModule.dataChildByName(QName.create(importedModule.getQNameModule(), "my-cont-b")));
49
50         final var myContC = assertInstanceOf(ContainerSchemaNode.class,
51             importedModule.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-cont-c")));
52
53         assertEquals(2, myContC.getChildNodes().size());
54         assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c1")));
55         assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c2")));
56     }
57
58     @Test
59     void testDeviateAdd() {
60         final var schemaContext = assertEffectiveModel(
61             "/deviation-resolution-test/deviation-add/foo.yang",
62             "/deviation-resolution-test/deviation-add/bar.yang");
63
64         final var barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).orElseThrow();
65         final var myLeafList = assertInstanceOf(LeafListSchemaNode.class,
66             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-leaf-list")));
67
68         assertEquals(Optional.of(Boolean.FALSE), myLeafList.effectiveConfig());
69         assertEquals(3, myLeafList.getDefaults().size());
70
71         final var constraint = myLeafList.getElementCountConstraint().orElseThrow();
72         assertEquals((Object) 10, constraint.getMaxElements());
73         assertEquals((Object) 5, constraint.getMinElements());
74         assertNotNull(myLeafList.getType().getUnits());
75
76         final var myList = assertInstanceOf(ListSchemaNode.class,
77             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-list")));
78         assertEquals(2, myList.getUniqueConstraints().size());
79
80         final var myChoice = assertInstanceOf(ChoiceSchemaNode.class,
81             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-choice")));
82         assertEquals("c2", myChoice.getDefaultCase().orElseThrow().getQName().getLocalName());
83
84         final var myRpc = barModule.getRpcs().iterator().next();
85         final var input = myRpc.getInput();
86         assertEquals(2, input.getMustConstraints().size());
87         final var output = myRpc.getOutput();
88         assertEquals(2, output.getMustConstraints().size());
89
90         final var myNotification = barModule.getNotifications().iterator().next();
91         assertEquals(2, myNotification.getMustConstraints().size());
92
93         final var myAnyxml = assertInstanceOf(AnyxmlSchemaNode.class,
94             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-anyxml")));
95         assertTrue(myAnyxml.isMandatory());
96
97         final var myAnyData = assertInstanceOf(AnydataSchemaNode.class,
98             barModule.findDataChildByName(QName.create(barModule.getQNameModule(), "my-anydata")).orElseThrow());
99         assertTrue(myAnyData.isMandatory());
100     }
101
102     @Test
103     void testDeviateReplace() throws Exception {
104         final var schemaContext = assertEffectiveModel(
105             "/deviation-resolution-test/deviation-replace/foo.yang",
106             "/deviation-resolution-test/deviation-replace/bar.yang");
107
108         final var barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).orElseThrow();
109         final var myLeaf = assertInstanceOf(LeafSchemaNode.class,
110             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-leaf")));
111
112         assertInstanceOf(Uint32TypeDefinition.class, myLeaf.getType());
113         assertEquals(Optional.of("bytes"), myLeaf.getType().getUnits());
114         assertEquals(Optional.of("10"), myLeaf.getType().getDefaultValue());
115
116         final var myLeafList = assertInstanceOf(LeafListSchemaNode.class,
117             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-leaf-list-test")));
118         final var constraint = myLeafList.getElementCountConstraint().orElseThrow();
119         assertEquals((Object) 6, constraint.getMaxElements());
120         assertEquals((Object) 3, constraint.getMinElements());
121         assertEquals(Optional.of(Boolean.TRUE), myLeafList.effectiveConfig());
122
123         final var myChoice = assertInstanceOf(ChoiceSchemaNode.class,
124             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-choice")));
125         assertFalse(myChoice.isMandatory());
126         // FIXME: we need a supported extension to properly test this
127         assertEquals(0, myChoice.getUnknownSchemaNodes().size());
128
129         final var myCont = assertInstanceOf(ContainerSchemaNode.class,
130             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-cont")));
131
132         final var myAugLeaf = assertInstanceOf(LeafSchemaNode.class,
133             myCont.getDataChildByName(QName.create(barModule.getQNameModule(), "my-aug-leaf")));
134         assertNotNull(myAugLeaf);
135         assertInstanceOf(Uint32TypeDefinition.class, myAugLeaf.getType());
136         assertEquals(Optional.of("seconds"), myAugLeaf.getType().getUnits());
137         assertEquals(Optional.of("new-def-val"), myAugLeaf.getType().getDefaultValue());
138         // FIXME: we need a supported extension to properly test this
139         assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
140
141         final var myUsedLeaf = assertInstanceOf(LeafSchemaNode.class,
142             myCont.getDataChildByName(QName.create(barModule.getQNameModule(), "my-used-leaf")));
143         assertInstanceOf(Uint32TypeDefinition.class, myUsedLeaf.getType());
144         assertEquals(Optional.of("weeks"), myUsedLeaf.getType().getUnits());
145         assertEquals(Optional.of("new-def-val"), myUsedLeaf.getType().getDefaultValue());
146         // FIXME: we need a supported extension to properly test this
147         assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
148     }
149
150     @Test
151     void testDeviateDelete() throws Exception {
152         final var schemaContext = assertEffectiveModel(
153             "/deviation-resolution-test/deviation-delete/foo.yang",
154             "/deviation-resolution-test/deviation-delete/bar.yang");
155
156         final var barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).orElseThrow();
157         final var myLeaf = assertInstanceOf(LeafSchemaNode.class,
158             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-leaf")));
159
160         assertEquals(Optional.empty(), myLeaf.getType().getDefaultValue());
161         assertEquals(Optional.empty(), myLeaf.getType().getUnits());
162         assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
163
164         final var myLeafList = assertInstanceOf(LeafListSchemaNode.class,
165             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-leaf-list")));
166         assertEquals(0, myLeafList.getDefaults().size());
167         assertEquals(0, myLeafList.getMustConstraints().size());
168
169         final var myList = assertInstanceOf(ListSchemaNode.class,
170             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-list")));
171         assertEquals(0, myList.getUniqueConstraints().size());
172         assertEquals(0, myList.getUnknownSchemaNodes().size());
173
174         final var myCont = assertInstanceOf(ContainerSchemaNode.class,
175             barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-cont")));
176         final var myAugLeaf = assertInstanceOf(LeafSchemaNode.class,
177             myCont.getDataChildByName(QName.create(barModule.getQNameModule(), "my-aug-leaf")));
178         assertEquals(Optional.empty(), myAugLeaf.getType().getDefaultValue());
179         assertEquals(Optional.empty(), myAugLeaf.getType().getUnits());
180         assertEquals(0, myAugLeaf.getMustConstraints().size());
181         assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
182
183         final var myUsedLeaf = assertInstanceOf(LeafSchemaNode.class,
184             myCont.getDataChildByName(QName.create(barModule.getQNameModule(), "my-used-leaf")));
185         assertNotNull(myUsedLeaf);
186         assertEquals(Optional.empty(), myUsedLeaf.getType().getDefaultValue());
187         assertEquals(Optional.empty(), myUsedLeaf.getType().getUnits());
188         assertEquals(0, myUsedLeaf.getMustConstraints().size());
189         assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
190     }
191
192     @Test
193     void shouldFailOnInvalidYang10Model() {
194         assertInvalidSubstatementException(startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."),
195             "/deviation-resolution-test/deviation-add/foo10-invalid.yang",
196             "/deviation-resolution-test/deviation-add/bar10-invalid.yang");
197     }
198
199     @Test
200     void shouldFailOnInvalidYang10Model2() {
201         assertInvalidSubstatementException(startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."),
202             "/deviation-resolution-test/deviation-delete/foo10-invalid.yang",
203             "/deviation-resolution-test/deviation-delete/bar10-invalid.yang");
204     }
205
206     @Test
207     void shouldFailOnInvalidDeviationTarget() {
208         assertInferenceException(startsWith("(bar?revision=2017-01-20)my-cont is not a valid deviation "
209             + "target for substatement (urn:ietf:params:xml:ns:yang:yin:1)max-elements."),
210             "/deviation-resolution-test/foo-invalid-deviation-target.yang",
211             "/deviation-resolution-test/bar.yang");
212     }
213
214     @Test
215     void shouldFailOnInvalidDeviationPath() {
216         assertInferenceException(startsWith(
217             "Deviation target 'Absolute{qnames=[(bar?revision=2017-01-20)invalid, path]}' not found"),
218             "/deviation-resolution-test/foo-invalid-deviation-path.yang",
219             "/deviation-resolution-test/bar.yang");
220     }
221
222     @Test
223     void shouldFailOnInvalidDeviateAdd() {
224         assertInferenceException(startsWith("""
225             Deviation cannot add substatement (urn:ietf:params:xml:ns:yang:yin:1)config to target node \
226             (bar?revision=2017-01-20)my-leaf because it is already defined in target and can appear only once."""),
227             "/deviation-resolution-test/deviation-add/foo-invalid.yang",
228             "/deviation-resolution-test/deviation-add/bar-invalid.yang");
229     }
230
231     @Test
232     void shouldFailOnInvalidDeviateAdd2() {
233         assertInferenceException(startsWith("""
234             Deviation cannot add substatement (urn:ietf:params:xml:ns:yang:yin:1)default to target node \
235             (bar?revision=2017-01-20)my-leaf because it is already defined in target and can appear only once."""),
236             "/deviation-resolution-test/deviation-add/foo-invalid-2.yang",
237             "/deviation-resolution-test/deviation-add/bar-invalid-2.yang");
238     }
239
240     @Test
241     void shouldFailOnInvalidDeviateAdd3() {
242         assertInferenceException(startsWith("""
243             Deviation cannot add substatement (urn:ietf:params:xml:ns:yang:yin:1)default to target node \
244             (bar?revision=2017-02-01)my-used-leaf because it is already defined in target and can appear only once."""),
245             "/deviation-resolution-test/deviation-add/foo-invalid-4.yang",
246             "/deviation-resolution-test/deviation-add/bar-invalid-4.yang");
247     }
248
249     @Test
250     void shouldFailOnInvalidDeviateReplace() {
251         assertInferenceException(startsWith("""
252             Deviation cannot replace substatement (urn:ietf:params:xml:ns:yang:yin:1)units in target node \
253             (bar?revision=2017-01-20)my-leaf because it does not exist in target node."""),
254             "/deviation-resolution-test/deviation-replace/foo-invalid.yang",
255             "/deviation-resolution-test/deviation-replace/bar-invalid.yang");
256     }
257
258     @Test
259     @SuppressWarnings("checkstyle:regexpSinglelineJava")
260     void shouldLogInvalidDeviateReplaceAttempt() throws Exception {
261         final var stdout = System.out;
262         final var output = new ByteArrayOutputStream();
263
264         System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
265
266         TestUtils.parseYangSource(
267             "/deviation-resolution-test/deviation-replace/foo-invalid-2.yang",
268             "/deviation-resolution-test/deviation-replace/bar-invalid-2.yang");
269
270         final var testLog = output.toString();
271         System.setOut(stdout);
272         assertThat(testLog, containsString("""
273             Deviation cannot replace substatement (urn:ietf:params:xml:ns:yang:yin:1)default in target leaf-list \
274             (bar?revision=2017-01-20)my-leaf-list because a leaf-list can have multiple default statements."""));
275     }
276
277     @Test
278     @SuppressWarnings("checkstyle:regexpSinglelineJava")
279     void shouldLogInvalidDeviateDeleteAttempt() throws Exception {
280         final PrintStream stdout = System.out;
281         final ByteArrayOutputStream output = new ByteArrayOutputStream();
282         final String testLog;
283
284         System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
285
286         TestUtils.parseYangSource(
287             "/deviation-resolution-test/deviation-delete/foo-invalid.yang",
288             "/deviation-resolution-test/deviation-delete/bar-invalid.yang");
289
290         testLog = output.toString();
291         System.setOut(stdout);
292         assertThat(testLog, containsString(
293             "Deviation cannot delete substatement (urn:ietf:params:xml:ns:yang:yin:1)units with argument 'seconds' in "
294                 + "target node (bar?revision=2017-01-20)my-leaf because it does not exist in the target node."));
295     }
296
297     @Test
298     void shouldFailOnInvalidDeviateAddSubstatement() {
299         assertInvalidSubstatementException(startsWith("TYPE is not valid for DEVIATE."),
300             "/deviation-resolution-test/deviation-add/foo-invalid-3.yang",
301             "/deviation-resolution-test/deviation-add/bar-invalid-3.yang");
302     }
303
304     @Test
305     void shouldFailOnInvalidDeviateReplaceSubstatement() {
306         assertInvalidSubstatementException(startsWith("MUST is not valid for DEVIATE."),
307             "/deviation-resolution-test/deviation-replace/foo-invalid-3.yang",
308             "/deviation-resolution-test/deviation-replace/bar-invalid-3.yang");
309     }
310
311     @Test
312     void shouldFailOnInvalidDeviateDeleteSubstatement() {
313         assertInvalidSubstatementException(startsWith("CONFIG is not valid for DEVIATE."),
314             "/deviation-resolution-test/deviation-delete/foo-invalid-2.yang",
315             "/deviation-resolution-test/deviation-delete/bar-invalid-2.yang");
316     }
317 }