Bug 8307: Add the option for activating deviation statements
[yangtools.git] / yang / yang-parser-impl / 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
9 package org.opendaylight.yangtools.yang.stmt;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertNull;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17 import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.File;
21 import java.io.PrintStream;
22 import java.util.Date;
23 import org.junit.Test;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
26 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.Module;
33 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
34 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
36 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
40
41 public class DeviationResolutionTest {
42
43     @Test
44     public void testDeviateNotSupported() throws Exception {
45         final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
46                 "/deviation-resolution-test/deviation-not-supported");
47         assertNotNull(schemaContext);
48
49         final Date revision = SimpleDateFormatUtil.getRevisionFormat().parse("2017-01-20");
50
51         final Module importedModule = schemaContext.findModuleByName("imported", revision);
52         assertNotNull(importedModule);
53
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.getDataChildByName(
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 SchemaContext schemaContext = StmtTestUtils.parseYangSources(
77                 sourceForResource("/deviation-resolution-test/deviation-add/foo.yang"),
78                 sourceForResource("/deviation-resolution-test/deviation-add/bar.yang"));
79         assertNotNull(schemaContext);
80
81         final Date revision = SimpleDateFormatUtil.getRevisionFormat().parse("2017-01-20");
82
83         final Module barModule = schemaContext.findModuleByName("bar", revision);
84         assertNotNull(barModule);
85
86         final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
87                 QName.create(barModule.getQNameModule(), "my-leaf-list"));
88         assertNotNull(myLeafList);
89
90         assertFalse(myLeafList.isConfiguration());
91         assertEquals(3, myLeafList.getDefaults().size());
92         assertEquals(10, myLeafList.getConstraints().getMaxElements().intValue());
93         assertEquals(5, myLeafList.getConstraints().getMinElements().intValue());
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());
105
106         final RpcDefinition myRpc = barModule.getRpcs().iterator().next();
107         final ContainerSchemaNode input = myRpc.getInput();
108         assertEquals(2, input.getConstraints().getMustConstraints().size());
109         final ContainerSchemaNode output = myRpc.getOutput();
110         assertEquals(2, output.getConstraints().getMustConstraints().size());
111
112         final NotificationDefinition myNotification = barModule.getNotifications().iterator().next();
113         assertEquals(2, myNotification.getConstraints().getMustConstraints().size());
114
115         final AnyXmlSchemaNode myAnyxml = (AnyXmlSchemaNode) barModule.getDataChildByName(
116                 QName.create(barModule.getQNameModule(), "my-anyxml"));
117         assertNotNull(myAnyxml);
118         assertTrue(myAnyxml.getConstraints().isMandatory());
119         assertEquals(2, myAnyxml.getUnknownSchemaNodes().size());
120     }
121
122     @Test
123     public void testDeviateReplace() throws Exception {
124         final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
125                 sourceForResource("/deviation-resolution-test/deviation-replace/foo.yang"),
126                 sourceForResource("/deviation-resolution-test/deviation-replace/bar.yang"));
127         assertNotNull(schemaContext);
128
129         final Date revision = SimpleDateFormatUtil.getRevisionFormat().parse("2017-01-20");
130
131         final Module barModule = schemaContext.findModuleByName("bar", revision);
132         assertNotNull(barModule);
133
134         final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
135                 QName.create(barModule.getQNameModule(), "my-leaf"));
136         assertNotNull(myLeaf);
137
138         assertTrue(myLeaf.getType() instanceof UnsignedIntegerTypeDefinition);
139         assertNotNull(myLeaf.getUnits());
140         assertEquals("bytes", myLeaf.getUnits());
141         assertNotNull(myLeaf.getDefault());
142         assertEquals("10", myLeaf.getDefault());
143
144         final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
145                 QName.create(barModule.getQNameModule(), "my-leaf-list-test"));
146         assertNotNull(myLeafList);
147
148         assertEquals(6, myLeafList.getConstraints().getMaxElements().intValue());
149         assertEquals(3, myLeafList.getConstraints().getMinElements().intValue());
150         assertTrue(myLeafList.isConfiguration());
151
152         final ChoiceSchemaNode myChoice = (ChoiceSchemaNode) barModule.getDataChildByName(
153                 QName.create(barModule.getQNameModule(), "my-choice"));
154         assertNotNull(myChoice);
155
156         assertFalse(myChoice.getConstraints().isMandatory());
157         assertEquals(1, myChoice.getUnknownSchemaNodes().size());
158         assertEquals("new arg", myChoice.getUnknownSchemaNodes().iterator().next().getNodeParameter());
159
160         final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
161                 QName.create(barModule.getQNameModule(), "my-cont"));
162         assertNotNull(myCont);
163
164         final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
165                 QName.create(barModule.getQNameModule(), "my-aug-leaf"));
166         assertNotNull(myAugLeaf);
167         assertTrue(myAugLeaf.getType() instanceof UnsignedIntegerTypeDefinition);
168         assertNotNull(myAugLeaf.getUnits());
169         assertEquals("seconds", myAugLeaf.getUnits());
170         assertNotNull(myAugLeaf.getDefault());
171         assertEquals("new-def-val", myAugLeaf.getDefault());
172         assertEquals(1, myAugLeaf.getUnknownSchemaNodes().size());
173         assertEquals("new arg", myAugLeaf.getUnknownSchemaNodes().iterator().next().getNodeParameter());
174
175         final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
176                 QName.create(barModule.getQNameModule(), "my-used-leaf"));
177         assertNotNull(myUsedLeaf);
178         assertTrue(myUsedLeaf.getType() instanceof UnsignedIntegerTypeDefinition);
179         assertNotNull(myUsedLeaf.getUnits());
180         assertEquals("weeks", myUsedLeaf.getUnits());
181         assertNotNull(myUsedLeaf.getDefault());
182         assertEquals("new-def-val", myUsedLeaf.getDefault());
183         assertEquals(1, myUsedLeaf.getUnknownSchemaNodes().size());
184         assertEquals("new arg", myUsedLeaf.getUnknownSchemaNodes().iterator().next().getNodeParameter());
185     }
186
187     @Test
188     public void testDeviateDelete() throws Exception {
189         final SchemaContext schemaContext = StmtTestUtils.parseYangSources(
190                 sourceForResource("/deviation-resolution-test/deviation-delete/foo.yang"),
191                 sourceForResource("/deviation-resolution-test/deviation-delete/bar.yang"));
192         assertNotNull(schemaContext);
193
194         final Date revision = SimpleDateFormatUtil.getRevisionFormat().parse("2017-01-20");
195
196         final Module barModule = schemaContext.findModuleByName("bar", revision);
197         assertNotNull(barModule);
198
199         final LeafSchemaNode myLeaf = (LeafSchemaNode) barModule.getDataChildByName(
200                 QName.create(barModule.getQNameModule(), "my-leaf"));
201         assertNotNull(myLeaf);
202
203         assertNull(myLeaf.getDefault());
204         assertNull(myLeaf.getUnits());
205         assertEquals(0, myLeaf.getUnknownSchemaNodes().size());
206
207         final LeafListSchemaNode myLeafList = (LeafListSchemaNode) barModule.getDataChildByName(
208                 QName.create(barModule.getQNameModule(), "my-leaf-list"));
209         assertNotNull(myLeafList);
210
211         assertEquals(0, myLeafList.getDefaults().size());
212         assertEquals(0, myLeafList.getConstraints().getMustConstraints().size());
213
214         final ListSchemaNode myList = (ListSchemaNode) barModule.getDataChildByName(
215                 QName.create(barModule.getQNameModule(), "my-list"));
216         assertNotNull(myList);
217
218         assertEquals(0, myList.getUniqueConstraints().size());
219         assertEquals(0, myList.getUnknownSchemaNodes().size());
220
221         final ContainerSchemaNode myCont = (ContainerSchemaNode) barModule.getDataChildByName(
222                 QName.create(barModule.getQNameModule(), "my-cont"));
223         assertNotNull(myCont);
224
225         final LeafSchemaNode myAugLeaf = (LeafSchemaNode) myCont.getDataChildByName(
226                 QName.create(barModule.getQNameModule(), "my-aug-leaf"));
227         assertNotNull(myAugLeaf);
228         assertNull(myAugLeaf.getDefault());
229         assertNull(myAugLeaf.getUnits());
230         assertEquals(0, myAugLeaf.getConstraints().getMustConstraints().size());
231         assertEquals(0, myAugLeaf.getUnknownSchemaNodes().size());
232
233         final LeafSchemaNode myUsedLeaf = (LeafSchemaNode) myCont.getDataChildByName(
234                 QName.create(barModule.getQNameModule(), "my-used-leaf"));
235         assertNotNull(myUsedLeaf);
236         assertNull(myUsedLeaf.getDefault());
237         assertNull(myUsedLeaf.getUnits());
238         assertEquals(0, myUsedLeaf.getConstraints().getMustConstraints().size());
239         assertEquals(0, myUsedLeaf.getUnknownSchemaNodes().size());
240     }
241
242     @Test
243     public void shouldFailOnInvalidYang10Model() throws Exception {
244         try {
245             StmtTestUtils.parseYangSources(
246                     sourceForResource("/deviation-resolution-test/deviation-add/foo10-invalid.yang"),
247                     sourceForResource("/deviation-resolution-test/deviation-add/bar10-invalid.yang"));
248             fail("An exception should have been thrown.");
249         } catch (final ReactorException ex) {
250             final Throwable cause = ex.getCause();
251             assertTrue(cause instanceof InvalidSubstatementException);
252             assertTrue(cause.getMessage().startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
253         }
254     }
255
256     @Test
257     public void shouldFailOnInvalidYang10Model2() throws Exception {
258         try {
259             StmtTestUtils.parseYangSources(
260                     sourceForResource("/deviation-resolution-test/deviation-delete/foo10-invalid.yang"),
261                     sourceForResource("/deviation-resolution-test/deviation-delete/bar10-invalid.yang"));
262             fail("An exception should have been thrown.");
263         } catch (final ReactorException ex) {
264             final Throwable cause = ex.getCause();
265             assertTrue(cause instanceof InvalidSubstatementException);
266             assertTrue(cause.getMessage().startsWith("Maximal count of DEFAULT for DEVIATE is 1, detected 2."));
267         }
268     }
269
270     @Test
271     public void shouldFailOnInvalidDeviationTarget() throws Exception {
272         try {
273             StmtTestUtils.parseYangSources(sourceForResource(
274                     "/deviation-resolution-test/foo-invalid-deviation-target.yang"),
275                     sourceForResource("/deviation-resolution-test/bar.yang"));
276             fail("An exception should have been thrown.");
277         } catch (final ReactorException ex) {
278             final Throwable cause = ex.getCause();
279             assertTrue(cause instanceof InferenceException);
280             assertTrue(cause.getMessage().startsWith("(bar?revision=2017-01-20)my-cont is not a valid deviation " +
281                     "target for substatement (urn:ietf:params:xml:ns:yang:yin:1)max-elements."));
282         }
283     }
284
285     @Test
286     public void shouldFailOnInvalidDeviationPath() throws Exception {
287         try {
288             StmtTestUtils.parseYangSources(
289                     sourceForResource("/deviation-resolution-test/foo-invalid-deviation-path.yang"),
290                     sourceForResource("/deviation-resolution-test/bar.yang"));
291             fail("An exception should have been thrown.");
292         } catch (final ReactorException ex) {
293             final Throwable cause = ex.getCause().getCause();
294             assertTrue(cause instanceof InferenceException);
295             assertTrue(cause.getMessage().startsWith("Deviation target 'Absolute{path=[(bar?revision=2017-01-20)" +
296                     "invalid, (bar?revision=2017-01-20)path]}' not found"));
297         }
298     }
299
300     @Test
301     public void shouldFailOnInvalidDeviateAdd() throws Exception {
302         try {
303             StmtTestUtils.parseYangSources(
304                     sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid.yang"),
305                     sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid.yang"));
306             fail("An exception should have been thrown.");
307         } catch (final ReactorException ex) {
308             final Throwable cause = ex.getCause();
309             assertTrue(cause instanceof InferenceException);
310             assertTrue(cause.getMessage().startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang" +
311                     ":yin:1)config to target node (bar?revision=2017-01-20)my-leaf because it is already defined in" +
312                     " target and can appear only once."));
313         }
314     }
315
316     @Test
317     public void shouldFailOnInvalidDeviateAdd2() throws Exception {
318         try {
319             StmtTestUtils.parseYangSources(
320                     sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid-2.yang"),
321                     sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid-2.yang"));
322             fail("An exception should have been thrown.");
323         } catch (final ReactorException ex) {
324             final Throwable cause = ex.getCause();
325             assertTrue(cause instanceof InferenceException);
326             assertTrue(cause.getMessage().startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang" +
327                     ":yin:1)default to target node (bar?revision=2017-01-20)my-leaf because it is already defined in" +
328                     " target and can appear only once."));
329         }
330     }
331
332     @Test
333     public void shouldFailOnInvalidDeviateAdd3() throws Exception {
334         try {
335             StmtTestUtils.parseYangSources(
336                     sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid-4.yang"),
337                     sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid-4.yang"));
338             fail("An exception should have been thrown.");
339         } catch (final ReactorException ex) {
340             final Throwable cause = ex.getCause();
341             assertTrue(cause instanceof InferenceException);
342             assertTrue(cause.getMessage().startsWith("Deviation cannot add substatement (urn:ietf:params:xml:ns:yang" +
343                     ":yin:1)default to target node (bar?revision=2017-02-01)my-used-leaf because it is already " +
344                     "defined in target and can appear only once."));
345         }
346     }
347
348     @Test
349     public void shouldFailOnInvalidDeviateReplace() throws Exception {
350         try {
351             StmtTestUtils.parseYangSources(
352                     sourceForResource("/deviation-resolution-test/deviation-replace/foo-invalid.yang"),
353                     sourceForResource("/deviation-resolution-test/deviation-replace/bar-invalid.yang"));
354             fail("An exception should have been thrown.");
355         } catch (final ReactorException ex) {
356             final Throwable cause = ex.getCause();
357             assertTrue(cause instanceof InferenceException);
358             assertTrue(cause.getMessage().startsWith("Deviation cannot replace substatement " +
359                     "(urn:ietf:params:xml:ns:yang:yin:1)units in target node (bar?revision=2017-01-20)my-leaf " +
360                     "because it does not exist in target node."));
361         }
362     }
363
364     @Test
365     public void shouldLogInvalidDeviateReplaceAttempt() throws Exception {
366         final PrintStream stdout = System.out;
367         final ByteArrayOutputStream output = new ByteArrayOutputStream();
368         final String testLog;
369
370         System.setOut(new PrintStream(output, true, "UTF-8"));
371
372         StmtTestUtils.parseYangSources(
373                 sourceForResource("/deviation-resolution-test/deviation-replace/foo-invalid-2.yang"),
374                 sourceForResource("/deviation-resolution-test/deviation-replace/bar-invalid-2.yang"));
375
376         testLog = output.toString();
377         assertTrue(testLog.contains("Deviation cannot replace substatement (urn:ietf:params:xml:ns:yang:yin:1)default" +
378                 " in target leaf-list (bar?revision=2017-01-20)my-leaf-list because a leaf-list can have multiple " +
379                 "default statements."));
380         System.setOut(stdout);
381     }
382
383     @Test
384     public void shouldLogInvalidDeviateDeleteAttempt() throws Exception {
385         final PrintStream stdout = System.out;
386         final ByteArrayOutputStream output = new ByteArrayOutputStream();
387         final String testLog;
388
389         System.setOut(new PrintStream(output, true, "UTF-8"));
390
391         StmtTestUtils.parseYangSources(
392                 sourceForResource("/deviation-resolution-test/deviation-delete/foo-invalid.yang"),
393                 sourceForResource("/deviation-resolution-test/deviation-delete/bar-invalid.yang"));
394
395         testLog = output.toString();
396         assertTrue(testLog.contains("Deviation cannot delete substatement (urn:ietf:params:xml:ns:yang:yin:1)units " +
397                 "with argument 'seconds' in target node (bar?revision=2017-01-20)my-leaf because it does not exist " +
398                 "in the target node."));
399         System.setOut(stdout);
400     }
401
402     @Test
403     public void shouldFailOnInvalidDeviateAddSubstatement() throws Exception {
404         try {
405             StmtTestUtils.parseYangSources(
406                     sourceForResource("/deviation-resolution-test/deviation-add/foo-invalid-3.yang"),
407                     sourceForResource("/deviation-resolution-test/deviation-add/bar-invalid-3.yang"));
408             fail("An exception should have been thrown.");
409         } catch (final ReactorException ex) {
410             final Throwable cause = ex.getCause();
411             assertTrue(cause instanceof InvalidSubstatementException);
412             assertTrue(cause.getMessage().startsWith("TYPE is not valid for DEVIATE."));
413         }
414     }
415
416     @Test
417     public void shouldFailOnInvalidDeviateReplaceSubstatement() throws Exception {
418         try {
419             StmtTestUtils.parseYangSources(
420                     sourceForResource("/deviation-resolution-test/deviation-replace/foo-invalid-3.yang"),
421                     sourceForResource("/deviation-resolution-test/deviation-replace/bar-invalid-3.yang"));
422             fail("An exception should have been thrown.");
423         } catch (final ReactorException ex) {
424             final Throwable cause = ex.getCause();
425             assertTrue(cause instanceof InvalidSubstatementException);
426             assertTrue(cause.getMessage().startsWith("MUST is not valid for DEVIATE."));
427         }
428     }
429
430     @Test
431     public void shouldFailOnInvalidDeviateDeleteSubstatement() throws Exception {
432         try {
433             StmtTestUtils.parseYangSources(
434                     sourceForResource("/deviation-resolution-test/deviation-delete/foo-invalid-2.yang"),
435                     sourceForResource("/deviation-resolution-test/deviation-delete/bar-invalid-2.yang"));
436             fail("An exception should have been thrown.");
437         } catch (final ReactorException ex) {
438             final Throwable cause = ex.getCause();
439             assertTrue(cause instanceof InvalidSubstatementException);
440             assertTrue(cause.getMessage().startsWith("CONFIG is not valid for DEVIATE."));
441         }
442     }
443 }