Rename opendaylight.mdsal.binding.runtime.spi
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / YT776Test.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.assertThrows;
12
13 import com.google.common.collect.ImmutableMap;
14 import org.junit.jupiter.api.AfterAll;
15 import org.junit.jupiter.api.BeforeAll;
16 import org.junit.jupiter.api.BeforeEach;
17 import org.junit.jupiter.api.Test;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
22 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
24 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
25 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
26 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
29 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
30 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
31 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
32
33 class YT776Test {
34     private static final QName MODULE = QName.create("yt776", "yt776");
35     private static final NodeIdentifier BOX = new NodeIdentifier(QName.create(MODULE, "box"));
36     private static final QName OBJECT = QName.create(MODULE, "object");
37     private static final QName OBJECT_ID = QName.create(MODULE, "object-id");
38     private static final NodeIdentifier OBJECT_LIST = new NodeIdentifier(OBJECT);
39     private static final NodeIdentifierWithPredicates OBJECT_ITEM = NodeIdentifierWithPredicates.of(OBJECT,
40         ImmutableMap.of(OBJECT_ID, "1"));
41     private static final LeafNode<?> OBJECT_ID_LEAF = ImmutableNodes.leafNode(OBJECT_ID, "1");
42     private static final NodeIdentifier ATTRIBUTES = new NodeIdentifier(QName.create(MODULE, "attributes"));
43
44     private static final QName NESTED = QName.create(MODULE, "nested");
45     private static final QName NESTED_ATTRIBUTE = QName.create(MODULE, "nested-attribute");
46     private static final NodeIdentifier NESTED_LIST = new NodeIdentifier(NESTED);
47     private static final NodeIdentifierWithPredicates NESTED_ITEM = NodeIdentifierWithPredicates.of(NESTED,
48         ImmutableMap.of(NESTED_ATTRIBUTE, "foo"));
49
50     private static final NodeIdentifier ANY_OF = new NodeIdentifier(QName.create(MODULE, "any-of"));
51     private static final QName SOME_LEAF = QName.create(MODULE, "some-leaf");
52     private static final NodeIdentifier SOME_LEAF_ID = new NodeIdentifier(SOME_LEAF);
53     private static final QName SOME_LIST = QName.create(MODULE, "some-list");
54     private static final NodeIdentifier SOME_LIST_ID = new NodeIdentifier(SOME_LIST);
55     private static final NodeIdentifierWithPredicates SOME_LIST_ITEM = NodeIdentifierWithPredicates.of(SOME_LIST,
56                 ImmutableMap.of(SOME_LEAF, "foo"));
57     private static EffectiveModelContext SCHEMA_CONTEXT;
58
59     private DataTree dataTree;
60
61     @BeforeAll
62     static void beforeClass() {
63         SCHEMA_CONTEXT = YangParserTestUtils.parseYang("""
64             module yt776 {
65               namespace yt776;
66               prefix yt776;
67
68               container box {
69                 list object {
70                   key object-id;
71
72                   leaf object-id {
73                     type string;
74                   }
75
76                   leaf-list attributes {
77                     type string;
78                     min-elements 1;
79                     max-elements 2;
80                   }
81
82                   list nested {
83                     key nested-attribute;
84                     max-elements 1;
85                     leaf nested-attribute {
86                       type string;
87                     }
88                   }
89                 }
90
91                 choice any-of {
92                   leaf some-leaf {
93                     type string;
94                   }
95                   list some-list {
96                     key some-leaf;
97                     min-elements 1;
98
99                     leaf some-leaf {
100                       type string;
101                     }
102                   }
103                 }
104               }
105             }""");
106     }
107
108     @AfterAll
109     static void afterClass() {
110         SCHEMA_CONTEXT = null;
111     }
112
113     @BeforeEach
114     void init() {
115         dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
116     }
117
118     @Test
119     void testNoAttributes() {
120         final var mod = dataTree.takeSnapshot().newModification();
121         mod.write(YangInstanceIdentifier.of(BOX), ImmutableNodes.newContainerBuilder()
122             .withNodeIdentifier(BOX)
123             .withChild(ImmutableNodes.newSystemMapBuilder()
124                 .withNodeIdentifier(OBJECT_LIST)
125                 .addChild(ImmutableNodes.newMapEntryBuilder()
126                     .withNodeIdentifier(OBJECT_ITEM)
127                     .withChild(OBJECT_ID_LEAF)
128                     .build())
129                 .build())
130             .build());
131
132         final var ex = assertThrows(IllegalArgumentException.class, mod::ready);
133         // FIXME: This is actually mandatory leaf enforcer kicking in: attributes have to be present. This is
134         //        most probably not what we want.
135         assertEquals("Node (yt776)object[{(yt776)object-id=1}] is missing mandatory descendant /(yt776)attributes",
136                 ex.getMessage());
137     }
138
139     @Test
140     void testEmptyAttributes() throws DataValidationFailedException {
141         final var mod = write(ImmutableNodes.newContainerBuilder()
142             .withNodeIdentifier(BOX)
143             .withChild(ImmutableNodes.newSystemMapBuilder()
144                 .withNodeIdentifier(OBJECT_LIST)
145                 .addChild(ImmutableNodes.newMapEntryBuilder()
146                     .withNodeIdentifier(OBJECT_ITEM)
147                     .withChild(OBJECT_ID_LEAF)
148                     .withChild(ImmutableNodes.newSystemLeafSetBuilder().withNodeIdentifier(ATTRIBUTES).build())
149                     .build())
150                 .build())
151             .build());
152
153         final var ex = assertThrows(MinMaxElementsValidationFailedException.class, mod::ready);
154         assertEquals("(yt776)attributes does not have enough elements (0), needs at least 1", ex.getMessage());
155         ListConstraintsValidation.assertTooFewElements(ex);
156     }
157
158     @Test
159     void testOneAttribute() throws DataValidationFailedException {
160         writeAndCommit(ImmutableNodes.newContainerBuilder()
161             .withNodeIdentifier(BOX)
162             .withChild(ImmutableNodes.newSystemMapBuilder()
163                 .withNodeIdentifier(OBJECT_LIST)
164                 .addChild(ImmutableNodes.newMapEntryBuilder()
165                     .withNodeIdentifier(OBJECT_ITEM)
166                     .withChild(OBJECT_ID_LEAF)
167                     .withChild(ImmutableNodes.newSystemLeafSetBuilder()
168                         .withNodeIdentifier(ATTRIBUTES)
169                         .withChildValue("object1")
170                         .build())
171                     .build())
172                 .build())
173             .build());
174     }
175
176     @Test
177     void testTwoAttributes() throws DataValidationFailedException {
178         writeAndCommit(ImmutableNodes.newContainerBuilder()
179             .withNodeIdentifier(BOX)
180             .withChild(ImmutableNodes.newSystemMapBuilder()
181                 .withNodeIdentifier(OBJECT_LIST)
182                 .addChild(ImmutableNodes.newMapEntryBuilder()
183                     .withNodeIdentifier(OBJECT_ITEM)
184                     .withChild(OBJECT_ID_LEAF)
185                     .withChild(ImmutableNodes.newSystemLeafSetBuilder()
186                         .withNodeIdentifier(ATTRIBUTES)
187                         .withChildValue("object1")
188                         .withChildValue("object2")
189                         .build())
190                     .build())
191                 .build())
192             .build());
193     }
194
195     @Test
196     void testThreeAttributes() throws DataValidationFailedException {
197         final var mod = write(ImmutableNodes.newContainerBuilder()
198             .withNodeIdentifier(BOX)
199             .withChild(ImmutableNodes.newSystemMapBuilder()
200                 .withNodeIdentifier(OBJECT_LIST)
201                 .addChild(ImmutableNodes.newMapEntryBuilder()
202                     .withNodeIdentifier(OBJECT_ITEM)
203                     .withChild(OBJECT_ID_LEAF)
204                     .withChild(ImmutableNodes.newSystemLeafSetBuilder()
205                         .withNodeIdentifier(ATTRIBUTES)
206                         .withChildValue("object1")
207                         .withChildValue("object2")
208                         .withChildValue("object3")
209                         .build())
210                     .build())
211                 .build())
212             .build());
213
214         final var ex = assertThrows(MinMaxElementsValidationFailedException.class, mod::ready);
215         assertEquals("(yt776)attributes has too many elements (3), can have at most 2", ex.getMessage());
216         ListConstraintsValidation.assertTooManyElements(ex);
217     }
218
219     @Test
220     void testEmptyAndMergeOne() throws DataValidationFailedException {
221         final var mod = dataTree.takeSnapshot().newModification();
222         mod.write(YangInstanceIdentifier.of(BOX), ImmutableNodes.newContainerBuilder()
223             .withNodeIdentifier(BOX)
224             .withChild(ImmutableNodes.newSystemMapBuilder()
225                 .withNodeIdentifier(OBJECT_LIST)
226                 .addChild(ImmutableNodes.newMapEntryBuilder()
227                     .withNodeIdentifier(OBJECT_ITEM)
228                     .withChild(OBJECT_ID_LEAF)
229                     .build())
230                 .build())
231             .build());
232         mod.merge(YangInstanceIdentifier.of(BOX), ImmutableNodes.newContainerBuilder()
233             .withNodeIdentifier(BOX)
234             .withChild(ImmutableNodes.newSystemMapBuilder()
235                 .withNodeIdentifier(OBJECT_LIST)
236                 .addChild(ImmutableNodes.newMapEntryBuilder()
237                     .withNodeIdentifier(OBJECT_ITEM)
238                     .withChild(OBJECT_ID_LEAF)
239                     .withChild(ImmutableNodes.newSystemLeafSetBuilder()
240                         .withNodeIdentifier(ATTRIBUTES)
241                         .withChildValue("object1")
242                         .build())
243                     .build())
244                 .build())
245             .build());
246
247         commit(mod);
248     }
249
250     @Test
251     void testEmptyAndMergeOneWithListTouched() throws DataValidationFailedException {
252         final var mod = dataTree.takeSnapshot().newModification();
253         mod.write(YangInstanceIdentifier.of(BOX), ImmutableNodes.newContainerBuilder()
254             .withNodeIdentifier(BOX)
255             .withChild(ImmutableNodes.newSystemMapBuilder()
256                 .withNodeIdentifier(OBJECT_LIST)
257                 .addChild(ImmutableNodes.newMapEntryBuilder()
258                     .withNodeIdentifier(OBJECT_ITEM)
259                     .withChild(OBJECT_ID_LEAF)
260                     .build())
261                 .build())
262             .build());
263         mod.merge(YangInstanceIdentifier.of(BOX), ImmutableNodes.newContainerBuilder()
264             .withNodeIdentifier(BOX)
265             .withChild(ImmutableNodes.newSystemMapBuilder()
266                 .withNodeIdentifier(OBJECT_LIST)
267                 .addChild(ImmutableNodes.newMapEntryBuilder()
268                     .withNodeIdentifier(OBJECT_ITEM)
269                     .withChild(OBJECT_ID_LEAF)
270                     .withChild(ImmutableNodes.newSystemLeafSetBuilder()
271                         .withNodeIdentifier(ATTRIBUTES)
272                         .withChildValue("object1")
273                         .build())
274                     .build())
275                 .build())
276             .build());
277
278         mod.delete(YangInstanceIdentifier.of(BOX, OBJECT_LIST, OBJECT_ITEM, NESTED_LIST, NESTED_ITEM));
279
280         commit(mod);
281     }
282
283     @Test
284     void testDisappearInChoice() throws DataValidationFailedException {
285         var mod = dataTree.takeSnapshot().newModification();
286         // Initialize choice with list
287         mod.write(YangInstanceIdentifier.of(BOX), ImmutableNodes.newContainerBuilder()
288             .withNodeIdentifier(BOX)
289             .withChild(ImmutableNodes.newChoiceBuilder()
290                 .withNodeIdentifier(ANY_OF)
291                 .withChild(ImmutableNodes.newSystemMapBuilder()
292                     .withNodeIdentifier(SOME_LIST_ID)
293                     .withChild(ImmutableNodes.newMapEntryBuilder()
294                         .withNodeIdentifier(SOME_LIST_ITEM)
295                         .withChild(ImmutableNodes.leafNode(SOME_LEAF_ID, "foo"))
296                         .build())
297                     .build())
298                 .build())
299             .build());
300         commit(mod);
301
302         // Now delete the single item, causing the list to fizzle, while creating the alterinative case
303         mod = dataTree.takeSnapshot().newModification();
304         mod.delete(YangInstanceIdentifier.of(BOX, ANY_OF, SOME_LIST_ID, SOME_LIST_ITEM));
305         mod.write(YangInstanceIdentifier.of(BOX, ANY_OF, SOME_LEAF_ID), ImmutableNodes.leafNode(SOME_LEAF_ID, "foo"));
306
307         commit(mod);
308     }
309
310     private DataTreeModification write(final ContainerNode data) throws DataValidationFailedException {
311         final var mod = dataTree.takeSnapshot().newModification();
312         mod.write(YangInstanceIdentifier.of(BOX), data);
313         return mod;
314     }
315
316     private void writeAndCommit(final ContainerNode data) throws DataValidationFailedException {
317         final var mod = dataTree.takeSnapshot().newModification();
318         mod.write(YangInstanceIdentifier.of(BOX), data);
319         commit(mod);
320     }
321
322     private void commit(final DataTreeModification mod) throws DataValidationFailedException {
323         mod.ready();
324         dataTree.validate(mod);
325         dataTree.commit(dataTree.prepare(mod));
326     }
327 }