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.instanceOf;
12 import static org.hamcrest.CoreMatchers.startsWith;
13 import static org.hamcrest.MatcherAssert.assertThat;
14 import static org.junit.Assert.assertEquals;
15 import static org.junit.Assert.assertFalse;
16 import static org.junit.Assert.assertNotNull;
17 import static org.junit.Assert.assertNull;
18 import static org.junit.Assert.assertThrows;
19 import static org.junit.Assert.assertTrue;
21 import java.io.ByteArrayOutputStream;
22 import java.io.PrintStream;
23 import java.nio.charset.StandardCharsets;
24 import java.util.Optional;
25 import org.junit.Test;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.Revision;
28 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
33 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
34 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.Module;
39 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
40 import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
42 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
47 public class DeviationResolutionTest extends AbstractYangTest {
49 public void testDeviateNotSupported() throws Exception {
50 final var schemaContext = assertEffectiveModelDir("/deviation-resolution-test/deviation-not-supported");
51 assertNotNull(schemaContext);
53 final Module importedModule = schemaContext.findModule("imported", Revision.of("2017-01-20")).get();
54 final ContainerSchemaNode myContA = (ContainerSchemaNode) importedModule.getDataChildByName(
55 QName.create(importedModule.getQNameModule(), "my-cont-a"));
56 assertNotNull(myContA);
58 assertEquals(1, myContA.getChildNodes().size());
59 assertNotNull(myContA.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-a3")));
61 final ContainerSchemaNode myContB = (ContainerSchemaNode) importedModule.dataChildByName(
62 QName.create(importedModule.getQNameModule(), "my-cont-b"));
65 final ContainerSchemaNode myContC = (ContainerSchemaNode) importedModule.getDataChildByName(
66 QName.create(importedModule.getQNameModule(), "my-cont-c"));
67 assertNotNull(myContC);
69 assertEquals(2, myContC.getChildNodes().size());
70 assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c1")));
71 assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c2")));
75 public void testDeviateAdd() throws Exception {
76 final EffectiveModelContext schemaContext = TestUtils.parseYangSource(
77 "/deviation-resolution-test/deviation-add/foo.yang",
78 "/deviation-resolution-test/deviation-add/bar.yang");
79 assertNotNull(schemaContext);
81 final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
82 final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
83 QName.create(barModule.getQNameModule(), "my-leaf-list"));
84 assertNotNull(myLeafList);
86 assertEquals(Optional.of(Boolean.FALSE), myLeafList.effectiveConfig());
87 assertEquals(3, myLeafList.getDefaults().size());
89 final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
90 assertEquals((Object) 10, constraint.getMaxElements());
91 assertEquals((Object) 5, constraint.getMinElements());
92 assertNotNull(myLeafList.getType().getUnits());
94 final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
95 QName.create(barModule.getQNameModule(), "my-list"));
96 assertNotNull(myList);
97 assertEquals(2, myList.getUniqueConstraints().size());
99 final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
100 QName.create(barModule.getQNameModule(), "my-choice"));
101 assertNotNull(myChoice);
102 assertEquals("c2", myChoice.getDefaultCase().get().getQName().getLocalName());
104 final RpcDefinition myRpc = barModule.getRpcs().iterator().next();
105 final InputSchemaNode input = myRpc.getInput();
106 assertEquals(2, input.getMustConstraints().size());
107 final OutputSchemaNode output = myRpc.getOutput();
108 assertEquals(2, output.getMustConstraints().size());
110 final NotificationDefinition myNotification = barModule.getNotifications().iterator().next();
111 assertEquals(2, myNotification.getMustConstraints().size());
113 final AnyxmlSchemaNode myAnyxml = (AnyxmlSchemaNode) barModule.getDataChildByName(
114 QName.create(barModule.getQNameModule(), "my-anyxml"));
115 assertNotNull(myAnyxml);
116 assertTrue(myAnyxml.isMandatory());
118 final AnydataSchemaNode myAnyData = (AnydataSchemaNode) barModule.findDataChildByName(
119 QName.create(barModule.getQNameModule(), "my-anydata")).orElse(null);
120 assertNotNull(myAnyData);
121 assertTrue(myAnyData.isMandatory());
125 public void testDeviateReplace() throws Exception {
126 final EffectiveModelContext schemaContext = TestUtils.parseYangSource(
127 "/deviation-resolution-test/deviation-replace/foo.yang",
128 "/deviation-resolution-test/deviation-replace/bar.yang");
129 assertNotNull(schemaContext);
131 final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
132 assertNotNull(barModule);
134 final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
135 QName.create(barModule.getQNameModule(), "my-leaf"));
136 assertNotNull(myLeaf);
138 assertThat(myLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
139 assertEquals(Optional.of("bytes"), myLeaf.getType().getUnits());
140 assertEquals(Optional.of("10"), myLeaf.getType().getDefaultValue());
142 final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
143 QName.create(barModule.getQNameModule(), "my-leaf-list-test"));
144 assertNotNull(myLeafList);
146 final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
147 assertEquals((Object) 6, constraint.getMaxElements());
148 assertEquals((Object) 3, constraint.getMinElements());
149 assertEquals(Optional.of(Boolean.TRUE), myLeafList.effectiveConfig());
151 final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
152 QName.create(barModule.getQNameModule(), "my-choice"));
153 assertNotNull(myChoice);
155 assertFalse(myChoice.isMandatory());
156 // FIXME: we need a supported extension to properly test this
157 assertEquals(0, myChoice.getUnknownSchemaNodes().size());
159 final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
160 QName.create(barModule.getQNameModule(), "my-cont"));
161 assertNotNull(myCont);
163 final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
164 QName.create(barModule.getQNameModule(), "my-aug-leaf"));
165 assertNotNull(myAugLeaf);
166 assertThat(myAugLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
167 assertEquals(Optional.of("seconds"), myAugLeaf.getType().getUnits());
168 assertEquals(Optional.of("new-def-val"), myAugLeaf.getType().getDefaultValue());
169 // FIXME: we need a supported extension to properly test this
170 assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
172 final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
173 QName.create(barModule.getQNameModule(), "my-used-leaf"));
174 assertNotNull(myUsedLeaf);
175 assertThat(myUsedLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
176 assertEquals(Optional.of("weeks"), myUsedLeaf.getType().getUnits());
177 assertEquals(Optional.of("new-def-val"), myUsedLeaf.getType().getDefaultValue());
178 // FIXME: we need a supported extension to properly test this
179 assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
183 public void testDeviateDelete() throws Exception {
184 final EffectiveModelContext schemaContext = TestUtils.parseYangSource(
185 "/deviation-resolution-test/deviation-delete/foo.yang",
186 "/deviation-resolution-test/deviation-delete/bar.yang");
187 assertNotNull(schemaContext);
189 final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
190 final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
191 QName.create(barModule.getQNameModule(), "my-leaf"));
192 assertNotNull(myLeaf);
194 assertEquals(Optional.empty(), myLeaf.getType().getDefaultValue());
195 assertEquals(Optional.empty(), myLeaf.getType().getUnits());
196 assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
198 final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
199 QName.create(barModule.getQNameModule(), "my-leaf-list"));
200 assertNotNull(myLeafList);
202 assertEquals(0, myLeafList.getDefaults().size());
203 assertEquals(0, myLeafList.getMustConstraints().size());
205 final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
206 QName.create(barModule.getQNameModule(), "my-list"));
207 assertNotNull(myList);
209 assertEquals(0, myList.getUniqueConstraints().size());
210 assertEquals(0, myList.getUnknownSchemaNodes().size());
212 final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
213 QName.create(barModule.getQNameModule(), "my-cont"));
214 assertNotNull(myCont);
216 final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
217 QName.create(barModule.getQNameModule(), "my-aug-leaf"));
218 assertNotNull(myAugLeaf);
219 assertEquals(Optional.empty(), myAugLeaf.getType().getDefaultValue());
220 assertEquals(Optional.empty(), myAugLeaf.getType().getUnits());
221 assertEquals(0, myAugLeaf.getMustConstraints().size());
222 assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
224 final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
225 QName.create(barModule.getQNameModule(), "my-used-leaf"));
226 assertNotNull(myUsedLeaf);
227 assertEquals(Optional.empty(), myUsedLeaf.getType().getDefaultValue());
228 assertEquals(Optional.empty(), myUsedLeaf.getType().getUnits());
229 assertEquals(0, myUsedLeaf.getMustConstraints().size());
230 assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
234 public void shouldFailOnInvalidYang10Model() {
235 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
236 "/deviation-resolution-test/deviation-add/foo10-invalid.yang",
237 "/deviation-resolution-test/deviation-add/bar10-invalid.yang"));
238 final Throwable cause = ex.getCause();
239 assertThat(cause, instanceOf(InvalidSubstatementException.class));
240 assertThat(cause.getMessage(), startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
244 public void shouldFailOnInvalidYang10Model2() {
245 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
246 "/deviation-resolution-test/deviation-delete/foo10-invalid.yang",
247 "/deviation-resolution-test/deviation-delete/bar10-invalid.yang"));
248 final Throwable cause = ex.getCause();
249 assertThat(cause, instanceOf(InvalidSubstatementException.class));
250 assertThat(cause.getMessage(), startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
254 public void shouldFailOnInvalidDeviationTarget() {
255 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
256 "/deviation-resolution-test/foo-invalid-deviation-target.yang",
257 "/deviation-resolution-test/bar.yang"));
258 final Throwable cause = ex.getCause();
259 assertThat(cause, instanceOf(InferenceException.class));
260 assertThat(cause.getMessage(), startsWith("(bar?revision=2017-01-20)my-cont is not a valid deviation "
261 + "target for substatement (urn:ietf:params:xml:ns:yang:yin:1)max-elements."));
265 public void shouldFailOnInvalidDeviationPath() {
266 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
267 "/deviation-resolution-test/foo-invalid-deviation-path.yang",
268 "/deviation-resolution-test/bar.yang"));
269 final Throwable cause = ex.getCause();
270 assertThat(cause, instanceOf(InferenceException.class));
271 assertThat(cause.getMessage(), startsWith(
272 "Deviation target 'Absolute{qnames=[(bar?revision=2017-01-20)invalid, path]}' not found"));
276 public void shouldFailOnInvalidDeviateAdd() {
277 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
278 "/deviation-resolution-test/deviation-add/foo-invalid.yang",
279 "/deviation-resolution-test/deviation-add/bar-invalid.yang"));
280 final Throwable cause = ex.getCause();
281 assertThat(cause, instanceOf(InferenceException.class));
282 assertThat(cause.getMessage(), startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
283 + ":yin:1)config to target node (bar?revision=2017-01-20)my-leaf because it is already defined in"
284 + " target and can appear only once."));
288 public void shouldFailOnInvalidDeviateAdd2() {
289 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
290 "/deviation-resolution-test/deviation-add/foo-invalid-2.yang",
291 "/deviation-resolution-test/deviation-add/bar-invalid-2.yang"));
292 final Throwable cause = ex.getCause();
293 assertThat(cause, instanceOf(InferenceException.class));
294 assertThat(cause.getMessage(), startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
295 + ":yin:1)default to target node (bar?revision=2017-01-20)my-leaf because it is already defined in"
296 + " target and can appear only once."));
300 public void shouldFailOnInvalidDeviateAdd3() {
301 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
302 "/deviation-resolution-test/deviation-add/foo-invalid-4.yang",
303 "/deviation-resolution-test/deviation-add/bar-invalid-4.yang"));
304 final Throwable cause = ex.getCause();
305 assertThat(cause, instanceOf(InferenceException.class));
306 assertThat(cause.getMessage(), startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
307 + ":yin:1)default to target node (bar?revision=2017-02-01)my-used-leaf because it is already "
308 + "defined in target and can appear only once."));
312 public void shouldFailOnInvalidDeviateReplace() {
313 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
314 "/deviation-resolution-test/deviation-replace/foo-invalid.yang",
315 "/deviation-resolution-test/deviation-replace/bar-invalid.yang"));
316 final Throwable cause = ex.getCause();
317 assertThat(cause, instanceOf(InferenceException.class));
318 assertThat(cause.getMessage(), startsWith("Deviation cannot replace substatement "
319 + "(urn:ietf:params:xml:ns:yang:yin:1)units in target node (bar?revision=2017-01-20)my-leaf "
320 + "because it does not exist in target node."));
324 @SuppressWarnings("checkstyle:regexpSinglelineJava")
325 public void shouldLogInvalidDeviateReplaceAttempt() throws Exception {
326 final PrintStream stdout = System.out;
327 final ByteArrayOutputStream output = new ByteArrayOutputStream();
328 final String testLog;
330 System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
332 TestUtils.parseYangSource(
333 "/deviation-resolution-test/deviation-replace/foo-invalid-2.yang",
334 "/deviation-resolution-test/deviation-replace/bar-invalid-2.yang");
336 testLog = output.toString();
337 System.setOut(stdout);
338 assertThat(testLog, containsString(
339 "Deviation cannot replace substatement (urn:ietf:params:xml:ns:yang:yin:1)default in target leaf-list "
340 + "(bar?revision=2017-01-20)my-leaf-list because a leaf-list can have multiple "
341 + "default statements."));
345 @SuppressWarnings("checkstyle:regexpSinglelineJava")
346 public void shouldLogInvalidDeviateDeleteAttempt() throws Exception {
347 final PrintStream stdout = System.out;
348 final ByteArrayOutputStream output = new ByteArrayOutputStream();
349 final String testLog;
351 System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
353 TestUtils.parseYangSource(
354 "/deviation-resolution-test/deviation-delete/foo-invalid.yang",
355 "/deviation-resolution-test/deviation-delete/bar-invalid.yang");
357 testLog = output.toString();
358 System.setOut(stdout);
359 assertThat(testLog, containsString(
360 "Deviation cannot delete substatement (urn:ietf:params:xml:ns:yang:yin:1)units with argument 'seconds' in "
361 + "target node (bar?revision=2017-01-20)my-leaf because it does not exist in the target node."));
365 public void shouldFailOnInvalidDeviateAddSubstatement() {
366 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
367 "/deviation-resolution-test/deviation-add/foo-invalid-3.yang",
368 "/deviation-resolution-test/deviation-add/bar-invalid-3.yang"));
369 final Throwable cause = ex.getCause();
370 assertThat(cause, instanceOf(InvalidSubstatementException.class));
371 assertThat(cause.getMessage(), startsWith("TYPE is not valid for DEVIATE."));
375 public void shouldFailOnInvalidDeviateReplaceSubstatement() {
376 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
377 "/deviation-resolution-test/deviation-replace/foo-invalid-3.yang",
378 "/deviation-resolution-test/deviation-replace/bar-invalid-3.yang"));
379 final Throwable cause = ex.getCause();
380 assertThat(cause, instanceOf(InvalidSubstatementException.class));
381 assertThat(cause.getMessage(), startsWith("MUST is not valid for DEVIATE."));
385 public void shouldFailOnInvalidDeviateDeleteSubstatement() throws Exception {
386 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
387 "/deviation-resolution-test/deviation-delete/foo-invalid-2.yang",
388 "/deviation-resolution-test/deviation-delete/bar-invalid-2.yang"));
389 final Throwable cause = ex.getCause();
390 assertThat(cause, instanceOf(InvalidSubstatementException.class));
391 assertThat(cause.getMessage(), startsWith("CONFIG is not valid for DEVIATE."));