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