Rename opendaylight.mdsal.binding.runtime.spi
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / 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.tree.impl;
9
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertNotNull;
12 import static org.junit.jupiter.api.Assertions.assertThrows;
13 import static org.junit.jupiter.api.Assertions.assertTrue;
14 import static org.junit.jupiter.api.Assertions.fail;
15
16 import org.junit.jupiter.api.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.DataContainerChild;
22 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
23 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
24 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
25 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
26 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
27 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
28 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
29
30 class Bug5830Test {
31     private static final String NS = "foo";
32     private static final String REV = "2016-05-17";
33     private static final QName TASK_CONTAINER = QName.create(NS, REV, "task-container");
34     private static final QName TASK = QName.create(NS, REV, "task");
35     private static final QName TASK_ID = QName.create(NS, REV, "task-id");
36     private static final QName TASK_DATA = QName.create(NS, REV, "task-data");
37     private static final QName OTHER_DATA = QName.create(NS, REV, "other-data");
38     private static final QName MANDATORY_DATA = QName.create(NS, REV, "mandatory-data");
39     private static final QName TASK_MANDATORY_LEAF = QName.create(NS, REV, "task-mandatory-leaf");
40     private static final QName NON_PRESENCE_CONTAINER = QName.create(NS, REV, "non-presence-container");
41     private static final QName NON_PRESENCE_CONTAINER_2 = QName.create(NS, REV, "non-presence-container-2");
42     private static final QName PRESENCE_CONTAINER_2 = QName.create(NS, REV, "presence-container-2");
43     private static final QName MANDATORY_LEAF_2 = QName.create(NS, REV, "mandatory-leaf-2");
44
45     private static DataTree initDataTree(final EffectiveModelContext schemaContext)
46             throws DataValidationFailedException {
47         final var inMemoryDataTree = new InMemoryDataTreeFactory().create(
48                 DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
49
50         final var taskNode = ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(new NodeIdentifier(TASK)).build();
51         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
52         modificationTree.write(YangInstanceIdentifier.of(TASK_CONTAINER, TASK), taskNode);
53         modificationTree.ready();
54
55         inMemoryDataTree.validate(modificationTree);
56         final var prepare = inMemoryDataTree.prepare(modificationTree);
57         inMemoryDataTree.commit(prepare);
58         return inMemoryDataTree;
59     }
60
61     @Test
62     void testMandatoryNodes() throws DataValidationFailedException {
63         testPresenceContainer();
64         testNonPresenceContainer();
65         testMultipleContainers();
66     }
67
68     private static void testPresenceContainer() throws DataValidationFailedException {
69         final var schemaContext = YangParserTestUtils.parseYang("""
70             module foo-presence {
71               yang-version 1;
72               namespace "foo";
73               prefix foo;
74
75               revision 2016-05-17 {
76                 description "test";
77               }
78
79               container task-container {
80                 list task {
81                   key "task-id";
82                   leaf task-id {
83                     type string;
84                   }
85                   leaf task-mandatory-leaf {
86                     type string;
87                     mandatory true;
88                   }
89
90                   container task-data {
91                     presence "Task data";
92                     leaf mandatory-data {
93                       type string;
94                       mandatory true;
95                     }
96                     leaf other-data {
97                       type string;
98                     }
99                   }
100                 }
101               }
102             }""");
103         assertNotNull(schemaContext, "Schema context must not be null.");
104
105         testContainerIsNotPresent(schemaContext);
106         final var ex = assertThrows(IllegalArgumentException.class, () -> testContainerIsPresent(schemaContext));
107         assertEquals(
108             "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant /(foo?revision=2016-05-17)"
109                 + "mandatory-data", ex.getMessage());
110         testMandatoryDataLeafIsPresent(schemaContext);
111     }
112
113     private static void testNonPresenceContainer() throws DataValidationFailedException {
114         final var schemaContext = YangParserTestUtils.parseYang("""
115             module foo-non-presence {
116               yang-version 1;
117               namespace "foo";
118               prefix foo;
119
120               revision 2016-05-17 {
121                 description "test";
122               }
123
124               container task-container {
125                 list task {
126                   key "task-id";
127
128                   leaf task-id {
129                     type string;
130                   }
131                   leaf task-mandatory-leaf {
132                     type string;
133                     mandatory true;
134                   }
135
136                   container task-data {
137                     leaf mandatory-data {
138                       type string;
139                       mandatory true;
140                     }
141                     leaf other-data {
142                       type string;
143                     }
144                   }
145                 }
146               }
147             }""");
148         assertNotNull(schemaContext, "Schema context must not be null.");
149
150         try {
151             testContainerIsNotPresent(schemaContext);
152             fail("Should fail due to missing mandatory node.");
153         } catch (IllegalArgumentException e) {
154             assertEquals(
155                     "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
156                             + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
157         }
158
159         try {
160             testContainerIsPresent(schemaContext);
161             fail("Should fail due to missing mandatory node.");
162         } catch (IllegalArgumentException e) {
163             assertEquals(
164                     "Node (foo?revision=2016-05-17)task[{(foo?revision=2016-05-17)task-id=123}] is missing mandatory "
165                             + "descendant /(foo?revision=2016-05-17)task-data/mandatory-data", e.getMessage());
166         }
167         testMandatoryDataLeafIsPresent(schemaContext);
168     }
169
170     private static void testMultipleContainers() throws DataValidationFailedException {
171         final var schemaContext = YangParserTestUtils.parseYang("""
172             module foo-multiple {
173               yang-version 1;
174               namespace "foo";
175               prefix foo;
176
177               revision 2016-05-17 {
178                 description "test";
179               }
180
181               container task-container {
182                 list task {
183                   key "task-id";
184
185                   leaf task-id {
186                     type string;
187                   }
188                   leaf task-mandatory-leaf {
189                     type string;
190                     mandatory true;
191                   }
192
193                   container task-data {
194                     presence "Task data";
195
196                     leaf mandatory-data {
197                       type string;
198                       mandatory true;
199                     }
200                     leaf other-data {
201                       type string;
202                     }
203
204                     container non-presence-container {
205
206                       container presence-container {
207                         presence "presence container";
208
209                         leaf mandatory-leaf {
210                           mandatory true;
211                           type string;
212                         }
213                       }
214
215                       container non-presence-container-2 {
216                         leaf mandatory-leaf-2 {
217                           mandatory true;
218                           type string;
219                         }
220                       }
221
222                       container presence-container-2 {
223                         presence "presence container";
224
225                         leaf mandatory-leaf-3 {
226                           mandatory true;
227                           type string;
228                         }
229                       }
230                     }
231                   }
232                 }
233               }
234             }""");
235         assertNotNull(schemaContext, "Schema context must not be null.");
236
237         testContainerIsNotPresent(schemaContext);
238
239         try {
240             testContainerIsPresent(schemaContext);
241             fail("Should fail due to missing mandatory node under present presence container.");
242         } catch (IllegalArgumentException e) {
243             assertTrue(e.getMessage().startsWith(
244                     "Node (foo?revision=2016-05-17)task-data is missing mandatory descendant"));
245         }
246
247         try {
248             testMandatoryDataLeafIsPresent(schemaContext);
249             fail("Should fail due to missing mandatory node under present presence container.");
250         } catch (IllegalArgumentException e) {
251             assertEquals("""
252                 Node (foo?revision=2016-05-17)task-data is missing mandatory descendant \
253                 /(foo?revision=2016-05-17)non-presence-container/\
254                 non-presence-container-2/mandatory-leaf-2""", e.getMessage());
255         }
256
257         testMandatoryLeaf2IsPresent(schemaContext, false);
258
259         try {
260             testMandatoryLeaf2IsPresent(schemaContext, true);
261             fail("Should fail due to missing mandatory node under present presence container.");
262         } catch (IllegalArgumentException e) {
263             assertEquals(
264                     "Node (foo?revision=2016-05-17)presence-container-2 is missing mandatory "
265                             + "descendant /(foo?revision=2016-05-17)mandatory-leaf-3", e.getMessage());
266         }
267     }
268
269     private static void testContainerIsNotPresent(final EffectiveModelContext schemaContext)
270             throws DataValidationFailedException {
271         final var inMemoryDataTree = initDataTree(schemaContext);
272         final var taskEntryNode = ImmutableNodes.newMapEntryBuilder()
273                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
274                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
275                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
276                 .build();
277
278         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
279         modificationTree.write(
280             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
281             taskEntryNode);
282         modificationTree.ready();
283
284         inMemoryDataTree.validate(modificationTree);
285         final var prepare = inMemoryDataTree.prepare(modificationTree);
286         inMemoryDataTree.commit(prepare);
287     }
288
289     private static void testContainerIsPresent(final EffectiveModelContext schemaContext)
290             throws DataValidationFailedException {
291         final var inMemoryDataTree = initDataTree(schemaContext);
292
293         final var taskEntryNode = ImmutableNodes.newMapEntryBuilder()
294                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
295                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
296                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
297                 .withChild(createTaskDataContainer(false)).build();
298
299         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
300         modificationTree.write(
301             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
302             taskEntryNode);
303         modificationTree.ready();
304
305         inMemoryDataTree.validate(modificationTree);
306         final var prepare = inMemoryDataTree.prepare(modificationTree);
307         inMemoryDataTree.commit(prepare);
308     }
309
310     private static void testMandatoryDataLeafIsPresent(final EffectiveModelContext schemaContext)
311             throws DataValidationFailedException {
312         final var inMemoryDataTree = initDataTree(schemaContext);
313
314         final var taskEntryNode = ImmutableNodes.newMapEntryBuilder()
315                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
316                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
317                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
318                 .withChild(createTaskDataContainer(true)).build();
319
320         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
321         modificationTree.write(
322             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
323             taskEntryNode);
324         modificationTree.ready();
325
326         inMemoryDataTree.validate(modificationTree);
327         final var prepare = inMemoryDataTree.prepare(modificationTree);
328         inMemoryDataTree.commit(prepare);
329     }
330
331     private static void testMandatoryLeaf2IsPresent(final EffectiveModelContext schemaContext,
332             final boolean withPresenceContianer) throws DataValidationFailedException {
333         final var inMemoryDataTree = initDataTree(schemaContext);
334
335         final var taskEntryNode = ImmutableNodes.newMapEntryBuilder()
336                 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123"))
337                 .withChild(ImmutableNodes.leafNode(TASK_ID, "123"))
338                 .withChild(ImmutableNodes.leafNode(TASK_MANDATORY_LEAF, "mandatory data"))
339                 .withChild(createTaskDataMultipleContainer(withPresenceContianer))
340                 .build();
341
342         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
343         modificationTree.write(
344             YangInstanceIdentifier.of(TASK_CONTAINER, TASK).node(NodeIdentifierWithPredicates.of(TASK, TASK_ID, "123")),
345             taskEntryNode);
346         modificationTree.ready();
347
348         inMemoryDataTree.validate(modificationTree);
349         final var prepare = inMemoryDataTree.prepare(modificationTree);
350         inMemoryDataTree.commit(prepare);
351     }
352
353     private static DataContainerChild createTaskDataContainer(final boolean withMandatoryNode) {
354         final var taskDataBuilder = ImmutableNodes.newContainerBuilder()
355                 .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
356                 .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"));
357         if (withMandatoryNode) {
358             taskDataBuilder.withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"));
359         }
360         return taskDataBuilder.build();
361     }
362
363     private static DataContainerChild createTaskDataMultipleContainer(final boolean withPresenceContianer) {
364         final var nonPresenceContainerBuilder = ImmutableNodes.newContainerBuilder()
365             .withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER))
366             .withChild(ImmutableNodes.newContainerBuilder()
367                 .withNodeIdentifier(new NodeIdentifier(NON_PRESENCE_CONTAINER_2))
368                 .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF_2, "mandatory leaf data 2")).build());
369
370         if (withPresenceContianer) {
371             nonPresenceContainerBuilder.withChild(ImmutableNodes.newContainerBuilder()
372                     .withNodeIdentifier(new NodeIdentifier(PRESENCE_CONTAINER_2)).build());
373         }
374
375         return ImmutableNodes.newContainerBuilder()
376             .withNodeIdentifier(new NodeIdentifier(TASK_DATA))
377             .withChild(ImmutableNodes.leafNode(OTHER_DATA, "foo"))
378             .withChild(ImmutableNodes.leafNode(MANDATORY_DATA, "mandatory-data-value"))
379             .withChild(nonPresenceContainerBuilder.build())
380             .build();
381     }
382 }