648f644f36a0158fbe20cba11bb558ed403befac
[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.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;
20
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;
46
47 public class DeviationResolutionTest {
48
49     @Test
50     public void testDeviateNotSupported() throws Exception {
51         final EffectiveModelContext schemaContext = StmtTestUtils.parseYangSources(
52                 "/deviation-resolution-test/deviation-not-supported");
53         assertNotNull(schemaContext);
54
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);
59
60         assertEquals(1, myContA.getChildNodes().size());
61         assertNotNull(myContA.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-a3")));
62
63         final ContainerSchemaNode myContB = (ContainerSchemaNode) importedModule.dataChildByName(
64                 QName.create(importedModule.getQNameModule(), "my-cont-b"));
65         assertNull(myContB);
66
67         final ContainerSchemaNode myContC = (ContainerSchemaNode) importedModule.getDataChildByName(
68                 QName.create(importedModule.getQNameModule(), "my-cont-c"));
69         assertNotNull(myContC);
70
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")));
74     }
75
76     @Test
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);
82
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);
87
88         assertEquals(Optional.of(Boolean.FALSE), myLeafList.effectiveConfig());
89         assertEquals(3, myLeafList.getDefaults().size());
90
91         final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
92         assertEquals((Object) 10, constraint.getMaxElements());
93         assertEquals((Object) 5, constraint.getMinElements());
94         assertNotNull(myLeafList.getType().getUnits());
95
96         final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
97                 QName.create(barModule.getQNameModule(), "my-list"));
98         assertNotNull(myList);
99         assertEquals(2, myList.getUniqueConstraints().size());
100
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());
105
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());
111
112         final NotificationDefinition myNotification = barModule.getNotifications().iterator().next();
113         assertEquals(2, myNotification.getMustConstraints().size());
114
115         final AnyxmlSchemaNode myAnyxml = (AnyxmlSchemaNode) barModule.getDataChildByName(
116                 QName.create(barModule.getQNameModule(), "my-anyxml"));
117         assertNotNull(myAnyxml);
118         assertTrue(myAnyxml.isMandatory());
119
120         final AnydataSchemaNode myAnyData = (AnydataSchemaNode) barModule.findDataChildByName(
121                 QName.create(barModule.getQNameModule(), "my-anydata")).orElse(null);
122         assertNotNull(myAnyData);
123         assertTrue(myAnyData.isMandatory());
124     }
125
126     @Test
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);
132
133         final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
134         assertNotNull(barModule);
135
136         final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
137                 QName.create(barModule.getQNameModule(), "my-leaf"));
138         assertNotNull(myLeaf);
139
140         assertThat(myLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
141         assertEquals(Optional.of("bytes"), myLeaf.getType().getUnits());
142         assertEquals(Optional.of("10"), myLeaf.getType().getDefaultValue());
143
144         final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
145                 QName.create(barModule.getQNameModule(), "my-leaf-list-test"));
146         assertNotNull(myLeafList);
147
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());
152
153         final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
154                 QName.create(barModule.getQNameModule(), "my-choice"));
155         assertNotNull(myChoice);
156
157         assertFalse(myChoice.isMandatory());
158         // FIXME: we need a supported extension to properly test this
159         assertEquals(0, myChoice.getUnknownSchemaNodes().size());
160
161         final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
162                 QName.create(barModule.getQNameModule(), "my-cont"));
163         assertNotNull(myCont);
164
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());
173
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());
182     }
183
184     @Test
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);
190
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);
195
196         assertEquals(Optional.empty(), myLeaf.getType().getDefaultValue());
197         assertEquals(Optional.empty(), myLeaf.getType().getUnits());
198         assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
199
200         final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
201                 QName.create(barModule.getQNameModule(), "my-leaf-list"));
202         assertNotNull(myLeafList);
203
204         assertEquals(0, myLeafList.getDefaults().size());
205         assertEquals(0, myLeafList.getMustConstraints().size());
206
207         final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
208                 QName.create(barModule.getQNameModule(), "my-list"));
209         assertNotNull(myList);
210
211         assertEquals(0, myList.getUniqueConstraints().size());
212         assertEquals(0, myList.getUnknownSchemaNodes().size());
213
214         final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
215                 QName.create(barModule.getQNameModule(), "my-cont"));
216         assertNotNull(myCont);
217
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());
225
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());
233     }
234
235     @Test
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."));
243     }
244
245     @Test
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."));
253     }
254
255     @Test
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."));
264     }
265
266     @Test
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();
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"));
275     }
276
277     @Test
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."));
287     }
288
289     @Test
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."));
299     }
300
301     @Test
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."));
311     }
312
313     @Test
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."));
323     }
324
325     @Test
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;
331
332         System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
333
334         TestUtils.parseYangSource(
335             "/deviation-resolution-test/deviation-replace/foo-invalid-2.yang",
336             "/deviation-resolution-test/deviation-replace/bar-invalid-2.yang");
337
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."));
344     }
345
346     @Test
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;
352
353         System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
354
355         TestUtils.parseYangSource(
356             "/deviation-resolution-test/deviation-delete/foo-invalid.yang",
357             "/deviation-resolution-test/deviation-delete/bar-invalid.yang");
358
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."));
364     }
365
366     @Test
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."));
374     }
375
376     @Test
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."));
384     }
385
386     @Test
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."));
394     }
395 }