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