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 {
50 public void testDeviateNotSupported() throws Exception {
51 final EffectiveModelContext schemaContext = StmtTestUtils.parseYangSources(
52 "/deviation-resolution-test/deviation-not-supported");
53 assertNotNull(schemaContext);
55 final Module importedModule = schemaContext.findModule("imported", Revision.of("2017-01-20")).get();
56 final ContainerSchemaNode myContA = (ContainerSchemaNode) importedModule.getDataChildByName(
57 QName.create(importedModule.getQNameModule(), "my-cont-a"));
58 assertNotNull(myContA);
60 assertEquals(1, myContA.getChildNodes().size());
61 assertNotNull(myContA.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-a3")));
63 final ContainerSchemaNode myContB = (ContainerSchemaNode) importedModule.dataChildByName(
64 QName.create(importedModule.getQNameModule(), "my-cont-b"));
67 final ContainerSchemaNode myContC = (ContainerSchemaNode) importedModule.getDataChildByName(
68 QName.create(importedModule.getQNameModule(), "my-cont-c"));
69 assertNotNull(myContC);
71 assertEquals(2, myContC.getChildNodes().size());
72 assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c1")));
73 assertNotNull(myContC.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-c2")));
77 public void testDeviateAdd() throws Exception {
78 final EffectiveModelContext schemaContext = TestUtils.parseYangSource(
79 "/deviation-resolution-test/deviation-add/foo.yang",
80 "/deviation-resolution-test/deviation-add/bar.yang");
81 assertNotNull(schemaContext);
83 final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
84 final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
85 QName.create(barModule.getQNameModule(), "my-leaf-list"));
86 assertNotNull(myLeafList);
88 assertEquals(Optional.of(Boolean.FALSE), myLeafList.effectiveConfig());
89 assertEquals(3, myLeafList.getDefaults().size());
91 final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
92 assertEquals((Object) 10, constraint.getMaxElements());
93 assertEquals((Object) 5, constraint.getMinElements());
94 assertNotNull(myLeafList.getType().getUnits());
96 final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
97 QName.create(barModule.getQNameModule(), "my-list"));
98 assertNotNull(myList);
99 assertEquals(2, myList.getUniqueConstraints().size());
101 final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
102 QName.create(barModule.getQNameModule(), "my-choice"));
103 assertNotNull(myChoice);
104 assertEquals("c2", myChoice.getDefaultCase().get().getQName().getLocalName());
106 final RpcDefinition myRpc = barModule.getRpcs().iterator().next();
107 final InputSchemaNode input = myRpc.getInput();
108 assertEquals(2, input.getMustConstraints().size());
109 final OutputSchemaNode output = myRpc.getOutput();
110 assertEquals(2, output.getMustConstraints().size());
112 final NotificationDefinition myNotification = barModule.getNotifications().iterator().next();
113 assertEquals(2, myNotification.getMustConstraints().size());
115 final AnyxmlSchemaNode myAnyxml = (AnyxmlSchemaNode) barModule.getDataChildByName(
116 QName.create(barModule.getQNameModule(), "my-anyxml"));
117 assertNotNull(myAnyxml);
118 assertTrue(myAnyxml.isMandatory());
120 final AnydataSchemaNode myAnyData = (AnydataSchemaNode) barModule.findDataChildByName(
121 QName.create(barModule.getQNameModule(), "my-anydata")).orElse(null);
122 assertNotNull(myAnyData);
123 assertTrue(myAnyData.isMandatory());
127 public void testDeviateReplace() throws Exception {
128 final EffectiveModelContext schemaContext = TestUtils.parseYangSource(
129 "/deviation-resolution-test/deviation-replace/foo.yang",
130 "/deviation-resolution-test/deviation-replace/bar.yang");
131 assertNotNull(schemaContext);
133 final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
134 assertNotNull(barModule);
136 final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
137 QName.create(barModule.getQNameModule(), "my-leaf"));
138 assertNotNull(myLeaf);
140 assertThat(myLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
141 assertEquals(Optional.of("bytes"), myLeaf.getType().getUnits());
142 assertEquals(Optional.of("10"), myLeaf.getType().getDefaultValue());
144 final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
145 QName.create(barModule.getQNameModule(), "my-leaf-list-test"));
146 assertNotNull(myLeafList);
148 final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
149 assertEquals((Object) 6, constraint.getMaxElements());
150 assertEquals((Object) 3, constraint.getMinElements());
151 assertEquals(Optional.of(Boolean.TRUE), myLeafList.effectiveConfig());
153 final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
154 QName.create(barModule.getQNameModule(), "my-choice"));
155 assertNotNull(myChoice);
157 assertFalse(myChoice.isMandatory());
158 // FIXME: we need a supported extension to properly test this
159 assertEquals(0, myChoice.getUnknownSchemaNodes().size());
161 final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
162 QName.create(barModule.getQNameModule(), "my-cont"));
163 assertNotNull(myCont);
165 final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
166 QName.create(barModule.getQNameModule(), "my-aug-leaf"));
167 assertNotNull(myAugLeaf);
168 assertThat(myAugLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
169 assertEquals(Optional.of("seconds"), myAugLeaf.getType().getUnits());
170 assertEquals(Optional.of("new-def-val"), myAugLeaf.getType().getDefaultValue());
171 // FIXME: we need a supported extension to properly test this
172 assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
174 final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
175 QName.create(barModule.getQNameModule(), "my-used-leaf"));
176 assertNotNull(myUsedLeaf);
177 assertThat(myUsedLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
178 assertEquals(Optional.of("weeks"), myUsedLeaf.getType().getUnits());
179 assertEquals(Optional.of("new-def-val"), myUsedLeaf.getType().getDefaultValue());
180 // FIXME: we need a supported extension to properly test this
181 assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
185 public void testDeviateDelete() throws Exception {
186 final EffectiveModelContext schemaContext = TestUtils.parseYangSource(
187 "/deviation-resolution-test/deviation-delete/foo.yang",
188 "/deviation-resolution-test/deviation-delete/bar.yang");
189 assertNotNull(schemaContext);
191 final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
192 final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
193 QName.create(barModule.getQNameModule(), "my-leaf"));
194 assertNotNull(myLeaf);
196 assertEquals(Optional.empty(), myLeaf.getType().getDefaultValue());
197 assertEquals(Optional.empty(), myLeaf.getType().getUnits());
198 assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
200 final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
201 QName.create(barModule.getQNameModule(), "my-leaf-list"));
202 assertNotNull(myLeafList);
204 assertEquals(0, myLeafList.getDefaults().size());
205 assertEquals(0, myLeafList.getMustConstraints().size());
207 final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
208 QName.create(barModule.getQNameModule(), "my-list"));
209 assertNotNull(myList);
211 assertEquals(0, myList.getUniqueConstraints().size());
212 assertEquals(0, myList.getUnknownSchemaNodes().size());
214 final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
215 QName.create(barModule.getQNameModule(), "my-cont"));
216 assertNotNull(myCont);
218 final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
219 QName.create(barModule.getQNameModule(), "my-aug-leaf"));
220 assertNotNull(myAugLeaf);
221 assertEquals(Optional.empty(), myAugLeaf.getType().getDefaultValue());
222 assertEquals(Optional.empty(), myAugLeaf.getType().getUnits());
223 assertEquals(0, myAugLeaf.getMustConstraints().size());
224 assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
226 final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
227 QName.create(barModule.getQNameModule(), "my-used-leaf"));
228 assertNotNull(myUsedLeaf);
229 assertEquals(Optional.empty(), myUsedLeaf.getType().getDefaultValue());
230 assertEquals(Optional.empty(), myUsedLeaf.getType().getUnits());
231 assertEquals(0, myUsedLeaf.getMustConstraints().size());
232 assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
236 public void shouldFailOnInvalidYang10Model() {
237 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
238 "/deviation-resolution-test/deviation-add/foo10-invalid.yang",
239 "/deviation-resolution-test/deviation-add/bar10-invalid.yang"));
240 final Throwable cause = ex.getCause();
241 assertThat(cause, instanceOf(InvalidSubstatementException.class));
242 assertThat(cause.getMessage(), startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
246 public void shouldFailOnInvalidYang10Model2() {
247 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
248 "/deviation-resolution-test/deviation-delete/foo10-invalid.yang",
249 "/deviation-resolution-test/deviation-delete/bar10-invalid.yang"));
250 final Throwable cause = ex.getCause();
251 assertThat(cause, instanceOf(InvalidSubstatementException.class));
252 assertThat(cause.getMessage(), startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
256 public void shouldFailOnInvalidDeviationTarget() {
257 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
258 "/deviation-resolution-test/foo-invalid-deviation-target.yang",
259 "/deviation-resolution-test/bar.yang"));
260 final Throwable cause = ex.getCause();
261 assertThat(cause, instanceOf(InferenceException.class));
262 assertThat(cause.getMessage(), startsWith("(bar?revision=2017-01-20)my-cont is not a valid deviation "
263 + "target for substatement (urn:ietf:params:xml:ns:yang:yin:1)max-elements."));
267 public void shouldFailOnInvalidDeviationPath() {
268 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
269 "/deviation-resolution-test/foo-invalid-deviation-path.yang",
270 "/deviation-resolution-test/bar.yang"));
271 final Throwable cause = ex.getCause().getCause();
272 assertThat(cause, instanceOf(InferenceException.class));
273 assertThat(cause.getMessage(), startsWith(
274 "Deviation target 'Absolute{qnames=[(bar?revision=2017-01-20)invalid, path]}' not found"));
278 public void shouldFailOnInvalidDeviateAdd() {
279 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
280 "/deviation-resolution-test/deviation-add/foo-invalid.yang",
281 "/deviation-resolution-test/deviation-add/bar-invalid.yang"));
282 final Throwable cause = ex.getCause();
283 assertThat(cause, instanceOf(InferenceException.class));
284 assertThat(cause.getMessage(), startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
285 + ":yin:1)config to target node (bar?revision=2017-01-20)my-leaf because it is already defined in"
286 + " target and can appear only once."));
290 public void shouldFailOnInvalidDeviateAdd2() {
291 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
292 "/deviation-resolution-test/deviation-add/foo-invalid-2.yang",
293 "/deviation-resolution-test/deviation-add/bar-invalid-2.yang"));
294 final Throwable cause = ex.getCause();
295 assertThat(cause, instanceOf(InferenceException.class));
296 assertThat(cause.getMessage(), startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
297 + ":yin:1)default to target node (bar?revision=2017-01-20)my-leaf because it is already defined in"
298 + " target and can appear only once."));
302 public void shouldFailOnInvalidDeviateAdd3() {
303 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
304 "/deviation-resolution-test/deviation-add/foo-invalid-4.yang",
305 "/deviation-resolution-test/deviation-add/bar-invalid-4.yang"));
306 final Throwable cause = ex.getCause();
307 assertThat(cause, instanceOf(InferenceException.class));
308 assertThat(cause.getMessage(), startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang"
309 + ":yin:1)default to target node (bar?revision=2017-02-01)my-used-leaf because it is already "
310 + "defined in target and can appear only once."));
314 public void shouldFailOnInvalidDeviateReplace() {
315 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
316 "/deviation-resolution-test/deviation-replace/foo-invalid.yang",
317 "/deviation-resolution-test/deviation-replace/bar-invalid.yang"));
318 final Throwable cause = ex.getCause();
319 assertThat(cause, instanceOf(InferenceException.class));
320 assertThat(cause.getMessage(), startsWith("Deviation cannot replace substatement "
321 + "(urn:ietf:params:xml:ns:yang:yin:1)units in target node (bar?revision=2017-01-20)my-leaf "
322 + "because it does not exist in target node."));
326 @SuppressWarnings("checkstyle:regexpSinglelineJava")
327 public void shouldLogInvalidDeviateReplaceAttempt() throws Exception {
328 final PrintStream stdout = System.out;
329 final ByteArrayOutputStream output = new ByteArrayOutputStream();
330 final String testLog;
332 System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
334 TestUtils.parseYangSource(
335 "/deviation-resolution-test/deviation-replace/foo-invalid-2.yang",
336 "/deviation-resolution-test/deviation-replace/bar-invalid-2.yang");
338 testLog = output.toString();
339 System.setOut(stdout);
340 assertThat(testLog, containsString(
341 "Deviation cannot replace substatement (urn:ietf:params:xml:ns:yang:yin:1)default in target leaf-list "
342 + "(bar?revision=2017-01-20)my-leaf-list because a leaf-list can have multiple "
343 + "default statements."));
347 @SuppressWarnings("checkstyle:regexpSinglelineJava")
348 public void shouldLogInvalidDeviateDeleteAttempt() throws Exception {
349 final PrintStream stdout = System.out;
350 final ByteArrayOutputStream output = new ByteArrayOutputStream();
351 final String testLog;
353 System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
355 TestUtils.parseYangSource(
356 "/deviation-resolution-test/deviation-delete/foo-invalid.yang",
357 "/deviation-resolution-test/deviation-delete/bar-invalid.yang");
359 testLog = output.toString();
360 System.setOut(stdout);
361 assertThat(testLog, containsString(
362 "Deviation cannot delete substatement (urn:ietf:params:xml:ns:yang:yin:1)units with argument 'seconds' in "
363 + "target node (bar?revision=2017-01-20)my-leaf because it does not exist in the target node."));
367 public void shouldFailOnInvalidDeviateAddSubstatement() {
368 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
369 "/deviation-resolution-test/deviation-add/foo-invalid-3.yang",
370 "/deviation-resolution-test/deviation-add/bar-invalid-3.yang"));
371 final Throwable cause = ex.getCause();
372 assertThat(cause, instanceOf(InvalidSubstatementException.class));
373 assertThat(cause.getMessage(), startsWith("TYPE is not valid for DEVIATE."));
377 public void shouldFailOnInvalidDeviateReplaceSubstatement() {
378 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
379 "/deviation-resolution-test/deviation-replace/foo-invalid-3.yang",
380 "/deviation-resolution-test/deviation-replace/bar-invalid-3.yang"));
381 final Throwable cause = ex.getCause();
382 assertThat(cause, instanceOf(InvalidSubstatementException.class));
383 assertThat(cause.getMessage(), startsWith("MUST is not valid for DEVIATE."));
387 public void shouldFailOnInvalidDeviateDeleteSubstatement() throws Exception {
388 final ReactorException ex = assertThrows(ReactorException.class, () -> TestUtils.parseYangSource(
389 "/deviation-resolution-test/deviation-delete/foo-invalid-2.yang",
390 "/deviation-resolution-test/deviation-delete/bar-invalid-2.yang"));
391 final Throwable cause = ex.getCause();
392 assertThat(cause, instanceOf(InvalidSubstatementException.class));
393 assertThat(cause.getMessage(), startsWith("CONFIG is not valid for DEVIATE."));