Bug 5830: Mandatory leaf enforcement is not correct with presence container
[yangtools.git] / yang / yang-data-impl / src / test / java / org / opendaylight / yangtools / yang / data / impl / schema / tree / Bug5830Test.java
1 /*
2  * Copyright (c) 2016 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.data.impl.schema.tree;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertTrue;
13 import static org.junit.Assert.fail;
14
15 import org.junit.Test;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
18 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
22 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
25 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
26 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
27 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
28 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
29 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
31 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
32
33 public class Bug5830Test {
34     private static final String NS = "foo";
35     private static final String REV = "2016-05-17";
36     private static final QName TASK_CONTAINER = QName.create(NS, REV, "task-container");
37     private static final QName TASK = QName.create(NS, REV, "task");
38     private static final QName TASK_ID = QName.create(NS, REV, "task-id");
39     private static final QName TASK_DATA = QName.create(NS, REV, "task-data");
40     private static final QName OTHER_DATA = QName.create(NS, REV, "other-data");
41     private static final QName MANDATORY_DATA = QName.create(NS, REV, "mandatory-data");
42     private static final QName TASK_MANDATORY_LEAF = QName.create(NS, REV, "task-mandatory-leaf");
43     private static final QName NON_PRESENCE_CONTAINER = QName.create(NS, REV, "non-presence-container");
44     private static final QName NON_PRESENCE_CONTAINER_2 = QName.create(NS, REV, "non-presence-container-2");
45     private static final QName PRESENCE_CONTAINER_2 = QName.create(NS, REV, "presence-container-2");
46     private static final QName MANDATORY_LEAF_2 = QName.create(NS, REV, "mandatory-leaf-2");
47
48     private static InMemoryDataTree initDataTree(final SchemaContext schemaContext)
49             throws DataValidationFailedException {
50         final InMemoryDataTree inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(
51                 TreeType.CONFIGURATION);
52         inMemoryDataTree.setSchemaContext(schemaContext);
53
54         final MapNode taskNode = Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(TASK)).build();
55         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
56         modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK), taskNode);
57         modificationTree.ready();
58
59         inMemoryDataTree.validate(modificationTree);
60         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
61         inMemoryDataTree.commit(prepare);
62         return inMemoryDataTree;
63     }
64
65     @Test
66     public void testMandatoryNodes() throws ReactorException, DataValidationFailedException {
67         testPresenceContainer();
68         testNonPresenceContainer();
69         testMultipleContainers();
70     }
71
72     private static void testPresenceContainer() throws ReactorException, DataValidationFailedException {
73         final SchemaContext schemaContext = TestModel.createTestContext("/bug5830/foo-presence.yang");
74         assertNotNull("Schema context must not be null.", schemaContext);
75
76         testContainerIsNotPresent(schemaContext);
77         try {
78             testContainerIsPresent(schemaContext);
79             fail("Should fail due to missing mandatory node under present presence container.");
80         } catch (final IllegalArgumentException e) {
81             assertEquals(
82                     "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant /(foo?revision=2016-05-17)"
83                             + "mandatory-data", e.getMessage());
84         }
85         testMandatoryDataLeafIsPresent(schemaContext);
86     }
87
88     private static void testNonPresenceContainer() throws ReactorException, DataValidationFailedException {
89         final SchemaContext schemaContext = TestModel.createTestContext("/bug5830/foo-non-presence.yang");
90         assertNotNull("Schema context must not be null.", schemaContext);
91
92         try {
93             testContainerIsNotPresent(schemaContext);
94             fail("Should fail due to missing mandatory node.");
95         } catch (final IllegalArgumentException e) {
96             assertEquals(
97                     "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
98                             + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
99         }
100
101         try {
102             testContainerIsPresent(schemaContext);
103             fail("Should fail due to missing mandatory node.");
104         } catch (final IllegalArgumentException e) {
105             assertEquals(
106                     "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
107                             + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
108         }
109         testMandatoryDataLeafIsPresent(schemaContext);
110     }
111
112     private static void testMultipleContainers() throws ReactorException, DataValidationFailedException {
113         final SchemaContext schemaContext = TestModel.createTestContext("/bug5830/foo-multiple.yang");
114         assertNotNull("Schema context must not be null.", schemaContext);
115
116         testContainerIsNotPresent(schemaContext);
117
118         try {
119             testContainerIsPresent(schemaContext);
120             fail("Should fail due to missing mandatory node under present presence container.");
121         } catch (final IllegalArgumentException e) {
122             assertTrue(e.getMessage().startsWith(
123                     "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant"));
124         }
125
126         try {
127             testMandatoryDataLeafIsPresent(schemaContext);
128             fail("Should fail due to missing mandatory node under present presence container.");
129         } catch (final IllegalArgumentException e) {
130             assertEquals("Node (foo?revision=2016-05-17)task-data "
131                     + "is missing mandatory descendant /(foo?revision=2016-05-17)non-presence-container/"
132                     + "non-presence-container-2/mandatory-leaf-2", e.getMessage());
133         }
134
135         testMandatoryLeaf2IsPresent(schemaContext, false);
136
137         try {
138             testMandatoryLeaf2IsPresent(schemaContext, true);
139             fail("Should fail due to missing mandatory node under present presence container.");
140         } catch (final IllegalArgumentException e) {
141             assertEquals("Node (foo?revision=2016-05-17)presence-container-2 is missing mandatory "
142                     + "descendant /(foo?revision=2016-05-17)mandatory-leaf-3", e.getMessage());
143         }
144     }
145
146     private static void testContainerIsNotPresent(final SchemaContext schemaContext)
147             throws DataValidationFailedException {
148         final InMemoryDataTree inMemoryDataTree = initDataTree(schemaContext);
149         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
150                 .withNodeIdentifier(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123"))
151                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
152                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data")).build();
153
154         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
155         modificationTree.write(
156                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
157                         .node(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123")), taskEntryNode);
158         modificationTree.ready();
159
160         inMemoryDataTree.validate(modificationTree);
161         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
162         inMemoryDataTree.commit(prepare);
163     }
164
165     private static void testContainerIsPresent(final SchemaContext schemaContext) throws DataValidationFailedException {
166         final InMemoryDataTree inMemoryDataTree = initDataTree(schemaContext);
167
168         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
169                 .withNodeIdentifier(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123"))
170                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
171                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
172                 .withChild(createTaskDataContainer(false)).build();
173
174         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
175         modificationTree.write(
176                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
177                         .node(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123")), taskEntryNode);
178         modificationTree.ready();
179
180         inMemoryDataTree.validate(modificationTree);
181         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
182         inMemoryDataTree.commit(prepare);
183     }
184
185     private static void testMandatoryDataLeafIsPresent(final SchemaContext schemaContext)
186             throws DataValidationFailedException {
187         final InMemoryDataTree inMemoryDataTree = initDataTree(schemaContext);
188
189         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
190                 .withNodeIdentifier(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123"))
191                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
192                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
193                 .withChild(createTaskDataContainer(true)).build();
194
195         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
196         modificationTree.write(
197                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
198                         .node(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123")), taskEntryNode);
199         modificationTree.ready();
200
201         inMemoryDataTree.validate(modificationTree);
202         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
203         inMemoryDataTree.commit(prepare);
204     }
205
206     private static void testMandatoryLeaf2IsPresent(final SchemaContext schemaContext, final boolean withPresenceContianer)
207             throws DataValidationFailedException {
208         final InMemoryDataTree inMemoryDataTree = initDataTree(schemaContext);
209
210         final MapEntryNode taskEntryNode = Builders.mapEntryBuilder()
211                 .withNodeIdentifier(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123"))
212                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
213                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
214                 .withChild(createTaskDataMultipleContainer(withPresenceContianer)).build();
215
216         final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
217         modificationTree.write(
218                 YangInstanceIdentifier.of(TASK_CONTAINER).node(TASK)
219                         .node(new NodeIdentifierWithPredicates(TASK, TASK_ID, "123")), taskEntryNode);
220         modificationTree.ready();
221
222         inMemoryDataTree.validate(modificationTree);
223         final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
224         inMemoryDataTree.commit(prepare);
225     }
226
227     private static DataContainerChild<?, ?> createTaskDataContainer(final boolean withMandatoryNode) {
228         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> taskDataBuilder = Builders.containerBuilder()
229                 .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
230                 .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"));
231         if (withMandatoryNode) {
232             taskDataBuilder.withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"));
233         }
234         return taskDataBuilder.build();
235     }
236
237     private static DataContainerChild<?, ?> createTaskDataMultipleContainer(final boolean withPresenceContianer) {
238         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> nonPresenceContainerBuilder = Builders
239                 .containerBuilder()
240                 .withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER))
241                 .withChild(
242                         Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER_2))
243                                 .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF_2, "mandatory leaf data 2")).build());
244
245         if (withPresenceContianer) {
246             nonPresenceContainerBuilder.withChild(Builders.containerBuilder()
247                     .withNodeIdentifier(new NodeIdentifier(PRESENCE_CONTAINER_2)).build());
248         }
249
250         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> taskDataBuilder = Builders.containerBuilder()
251                 .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
252                 .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"));
253         taskDataBuilder.withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"));
254         taskDataBuilder.withChild(nonPresenceContainerBuilder.build());
255
256         return taskDataBuilder.build();
257     }
258 }