Further cleanup of tests
[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 extends AbstractYangTest {
48     @Test
49     public void testDeviateNotSupported() throws Exception {
50         final var schemaContext = assertEffectiveModelDir("/deviation-resolution-test/deviation-not-supported");
51         assertNotNull(schemaContext);
52
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);
57
58         assertEquals(1, myContA.getChildNodes().size());
59         assertNotNull(myContA.getDataChildByName(QName.create(importedModule.getQNameModule(), "my-leaf-a3")));
60
61         final ContainerSchemaNode myContB = (ContainerSchemaNode) importedModule.dataChildByName(
62                 QName.create(importedModule.getQNameModule(), "my-cont-b"));
63         assertNull(myContB);
64
65         final ContainerSchemaNode myContC = (ContainerSchemaNode) importedModule.getDataChildByName(
66                 QName.create(importedModule.getQNameModule(), "my-cont-c"));
67         assertNotNull(myContC);
68
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")));
72     }
73
74     @Test
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);
80
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);
85
86         assertEquals(Optional.of(Boolean.FALSE), myLeafList.effectiveConfig());
87         assertEquals(3, myLeafList.getDefaults().size());
88
89         final ElementCountConstraint constraint = myLeafList.getElementCountConstraint().get();
90         assertEquals((Object) 10, constraint.getMaxElements());
91         assertEquals((Object) 5, constraint.getMinElements());
92         assertNotNull(myLeafList.getType().getUnits());
93
94         final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
95                 QName.create(barModule.getQNameModule(), "my-list"));
96         assertNotNull(myList);
97         assertEquals(2, myList.getUniqueConstraints().size());
98
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());
103
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());
109
110         final NotificationDefinition myNotification = barModule.getNotifications().iterator().next();
111         assertEquals(2, myNotification.getMustConstraints().size());
112
113         final AnyxmlSchemaNode myAnyxml = (AnyxmlSchemaNode) barModule.getDataChildByName(
114                 QName.create(barModule.getQNameModule(), "my-anyxml"));
115         assertNotNull(myAnyxml);
116         assertTrue(myAnyxml.isMandatory());
117
118         final AnydataSchemaNode myAnyData = (AnydataSchemaNode) barModule.findDataChildByName(
119                 QName.create(barModule.getQNameModule(), "my-anydata")).orElse(null);
120         assertNotNull(myAnyData);
121         assertTrue(myAnyData.isMandatory());
122     }
123
124     @Test
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);
130
131         final Module barModule = schemaContext.findModule("bar", Revision.of("2017-01-20")).get();
132         assertNotNull(barModule);
133
134         final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
135                 QName.create(barModule.getQNameModule(), "my-leaf"));
136         assertNotNull(myLeaf);
137
138         assertThat(myLeaf.getType(), instanceOf(Uint32TypeDefinition.class));
139         assertEquals(Optional.of("bytes"), myLeaf.getType().getUnits());
140         assertEquals(Optional.of("10"), myLeaf.getType().getDefaultValue());
141
142         final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
143                 QName.create(barModule.getQNameModule(), "my-leaf-list-test"));
144         assertNotNull(myLeafList);
145
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());
150
151         final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
152                 QName.create(barModule.getQNameModule(), "my-choice"));
153         assertNotNull(myChoice);
154
155         assertFalse(myChoice.isMandatory());
156         // FIXME: we need a supported extension to properly test this
157         assertEquals(0, myChoice.getUnknownSchemaNodes().size());
158
159         final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
160                 QName.create(barModule.getQNameModule(), "my-cont"));
161         assertNotNull(myCont);
162
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());
171
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());
180     }
181
182     @Test
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);
188
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);
193
194         assertEquals(Optional.empty(), myLeaf.getType().getDefaultValue());
195         assertEquals(Optional.empty(), myLeaf.getType().getUnits());
196         assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
197
198         final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
199                 QName.create(barModule.getQNameModule(), "my-leaf-list"));
200         assertNotNull(myLeafList);
201
202         assertEquals(0, myLeafList.getDefaults().size());
203         assertEquals(0, myLeafList.getMustConstraints().size());
204
205         final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
206                 QName.create(barModule.getQNameModule(), "my-list"));
207         assertNotNull(myList);
208
209         assertEquals(0, myList.getUniqueConstraints().size());
210         assertEquals(0, myList.getUnknownSchemaNodes().size());
211
212         final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
213                 QName.create(barModule.getQNameModule(), "my-cont"));
214         assertNotNull(myCont);
215
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());
223
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());
231     }
232
233     @Test
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."));
241     }
242
243     @Test
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."));
251     }
252
253     @Test
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."));
262     }
263
264     @Test
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"));
273     }
274
275     @Test
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."));
285     }
286
287     @Test
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."));
297     }
298
299     @Test
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."));
309     }
310
311     @Test
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."));
321     }
322
323     @Test
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;
329
330         System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
331
332         TestUtils.parseYangSource(
333             "/deviation-resolution-test/deviation-replace/foo-invalid-2.yang",
334             "/deviation-resolution-test/deviation-replace/bar-invalid-2.yang");
335
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."));
342     }
343
344     @Test
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;
350
351         System.setOut(new PrintStream(output, true, StandardCharsets.UTF_8));
352
353         TestUtils.parseYangSource(
354             "/deviation-resolution-test/deviation-delete/foo-invalid.yang",
355             "/deviation-resolution-test/deviation-delete/bar-invalid.yang");
356
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."));
362     }
363
364     @Test
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."));
372     }
373
374     @Test
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."));
382     }
383
384     @Test
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."));
392     }
393 }