2 * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.stmt;
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;
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;
36 class DeviationResolutionTest extends AbstractYangTest {
38 void testDeviateNotSupported() {
39 final var schemaContext = assertEffectiveModelDir("/deviation-resolution-test/deviation-not-supported");
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")));
45 assertEquals(1, myContA.getChildNodes().size());
46 assertNotNull(myContA.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-a3")));
48 assertNull(importedModule.dataChildByName(QName.create(importedModule.getQNameModule(), "my-cont-b")));
50 final var myContC = assertInstanceOf(ContainerSchemaNode.class,
51 importedModule.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-cont-c")));
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")));
59 void testDeviateAdd() {
60 final var schemaContext = assertEffectiveModel(
61 "/deviation-resolution-test/deviation-add/foo.yang",
62 "/deviation-resolution-test/deviation-add/bar.yang");
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")));
68 assertEquals(Optional.of(Boolean.FALSE), myLeafList.effectiveConfig());
69 assertEquals(3, myLeafList.getDefaults().size());
71 final var constraint = myLeafList.getElementCountConstraint().orElseThrow();
72 assertEquals((Object) 10, constraint.getMaxElements());
73 assertEquals((Object) 5, constraint.getMinElements());
74 assertNotNull(myLeafList.getType().getUnits());
76 final var myList = assertInstanceOf(ListSchemaNode.class,
77 barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-list")));
78 assertEquals(2, myList.getUniqueConstraints().size());
80 final var myChoice = assertInstanceOf(ChoiceSchemaNode.class,
81 barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-choice")));
82 assertEquals("c2", myChoice.getDefaultCase().orElseThrow().getQName().getLocalName());
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());
90 final var myNotification = barModule.getNotifications().iterator().next();
91 assertEquals(2, myNotification.getMustConstraints().size());
93 final var myAnyxml = assertInstanceOf(AnyxmlSchemaNode.class,
94 barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-anyxml")));
95 assertTrue(myAnyxml.isMandatory());
97 final var myAnyData = assertInstanceOf(AnydataSchemaNode.class,
98 barModule.findDataChildByName(QName.create(barModule.getQNameModule(), "my-anydata")).orElseThrow());
99 assertTrue(myAnyData.isMandatory());
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");
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")));
112 assertInstanceOf(Uint32TypeDefinition.class, myLeaf.getType());
113 assertEquals(Optional.of("bytes"), myLeaf.getType().getUnits());
114 assertEquals(Optional.of("10"), myLeaf.getType().getDefaultValue());
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());
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());
129 final var myCont = assertInstanceOf(ContainerSchemaNode.class,
130 barModule.getDataChildByName(QName.create(barModule.getQNameModule(), "my-cont")));
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());
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());
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");
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")));
160 assertEquals(Optional.empty(), myLeaf.getType().getDefaultValue());
161 assertEquals(Optional.empty(), myLeaf.getType().getUnits());
162 assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
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());
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());
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());
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());
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");
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");
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");
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");
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");
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");
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");
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");
259 @SuppressWarnings("checkstyle:regexpSinglelineJava")
260 void shouldLogInvalidDeviateReplaceAttempt() throws Exception {
261 final var stdout = System.out;
262 final var output = new ByteArrayOutputStream();
264 System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
266 TestUtils.parseYangSource(
267 "/deviation-resolution-test/deviation-replace/foo-invalid-2.yang",
268 "/deviation-resolution-test/deviation-replace/bar-invalid-2.yang");
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."""));
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;
284 System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
286 TestUtils.parseYangSource(
287 "/deviation-resolution-test/deviation-delete/foo-invalid.yang",
288 "/deviation-resolution-test/deviation-delete/bar-invalid.yang");
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."));
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");
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");
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");