2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.tree.impl;
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertThrows;
12 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
13 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
14 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.leafBuilder;
15 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.leafSetBuilder;
16 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.mapBuilder;
17 import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.mapEntryBuilder;
18 import static org.opendaylight.yangtools.yang.data.tree.impl.ListConstraintsValidation.assertTooFewElements;
19 import static org.opendaylight.yangtools.yang.data.tree.impl.ListConstraintsValidation.assertTooManyElements;
21 import com.google.common.collect.ImmutableMap;
22 import org.junit.jupiter.api.AfterAll;
23 import org.junit.jupiter.api.BeforeAll;
24 import org.junit.jupiter.api.BeforeEach;
25 import org.junit.jupiter.api.Test;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
30 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
32 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
33 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
34 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
35 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
36 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
37 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
38 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
41 private static final QName MODULE = QName.create("yt776", "yt776");
42 private static final NodeIdentifier BOX = new NodeIdentifier(QName.create(MODULE, "box"));
43 private static final QName OBJECT = QName.create(MODULE, "object");
44 private static final QName OBJECT_ID = QName.create(MODULE, "object-id");
45 private static final NodeIdentifier OBJECT_LIST = new NodeIdentifier(OBJECT);
46 private static final NodeIdentifierWithPredicates OBJECT_ITEM = NodeIdentifierWithPredicates.of(OBJECT,
47 ImmutableMap.of(OBJECT_ID, "1"));
48 private static final LeafNode<?> OBJECT_ID_LEAF = leafBuilder().withNodeIdentifier(new NodeIdentifier(OBJECT_ID))
49 .withValue("1").build();
50 private static final NodeIdentifier ATTRIBUTES = new NodeIdentifier(QName.create(MODULE, "attributes"));
52 private static final QName NESTED = QName.create(MODULE, "nested");
53 private static final QName NESTED_ATTRIBUTE = QName.create(MODULE, "nested-attribute");
54 private static final NodeIdentifier NESTED_LIST = new NodeIdentifier(NESTED);
55 private static final NodeIdentifierWithPredicates NESTED_ITEM = NodeIdentifierWithPredicates.of(NESTED,
56 ImmutableMap.of(NESTED_ATTRIBUTE, "foo"));
58 private static final NodeIdentifier ANY_OF = new NodeIdentifier(QName.create(MODULE, "any-of"));
59 private static final QName SOME_LEAF = QName.create(MODULE, "some-leaf");
60 private static final NodeIdentifier SOME_LEAF_ID = new NodeIdentifier(SOME_LEAF);
61 private static final QName SOME_LIST = QName.create(MODULE, "some-list");
62 private static final NodeIdentifier SOME_LIST_ID = new NodeIdentifier(SOME_LIST);
63 private static final NodeIdentifierWithPredicates SOME_LIST_ITEM = NodeIdentifierWithPredicates.of(SOME_LIST,
64 ImmutableMap.of(SOME_LEAF, "foo"));
65 private static EffectiveModelContext SCHEMA_CONTEXT;
67 private DataTree dataTree;
70 static void beforeClass() {
71 SCHEMA_CONTEXT = YangParserTestUtils.parseYang("""
84 leaf-list attributes {
93 leaf nested-attribute {
117 static void afterClass() {
118 SCHEMA_CONTEXT = null;
123 dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, SCHEMA_CONTEXT);
127 void testNoAttributes() {
128 final var mod = dataTree.takeSnapshot().newModification();
129 mod.write(YangInstanceIdentifier.of(BOX), containerBuilder().withNodeIdentifier(BOX)
130 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
131 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
132 .withChild(OBJECT_ID_LEAF)
137 final IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, mod::ready);
138 // FIXME: This is actually mandatory leaf enforcer kicking in: attributes have to be present. This is
139 // most probably not what we want.
140 assertEquals("Node (yt776)object[{(yt776)object-id=1}] is missing mandatory descendant /(yt776)attributes",
145 void testEmptyAttributes() throws DataValidationFailedException {
146 final var mod = write(containerBuilder().withNodeIdentifier(BOX)
147 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
148 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
149 .withChild(OBJECT_ID_LEAF)
150 .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES).build())
155 final var ex = assertThrows(MinMaxElementsValidationFailedException.class, mod::ready);
156 assertEquals("(yt776)attributes does not have enough elements (0), needs at least 1", ex.getMessage());
157 assertTooFewElements(ex);
161 void testOneAttribute() throws DataValidationFailedException {
162 writeAndCommit(containerBuilder().withNodeIdentifier(BOX)
163 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
164 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
165 .withChild(OBJECT_ID_LEAF)
166 .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
167 .withChildValue("object1")
175 void testTwoAttributes() throws DataValidationFailedException {
176 writeAndCommit(containerBuilder().withNodeIdentifier(BOX)
177 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
178 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
179 .withChild(OBJECT_ID_LEAF)
180 .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
181 .withChildValue("object1")
182 .withChildValue("object2")
190 void testThreeAttributes() throws DataValidationFailedException {
191 final var mod = write(containerBuilder().withNodeIdentifier(BOX)
192 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
193 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
194 .withChild(OBJECT_ID_LEAF)
195 .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
196 .withChildValue("object1")
197 .withChildValue("object2")
198 .withChildValue("object3")
204 final var ex = assertThrows(MinMaxElementsValidationFailedException.class, mod::ready);
205 assertEquals("(yt776)attributes has too many elements (3), can have at most 2", ex.getMessage());
206 assertTooManyElements(ex);
210 void testEmptyAndMergeOne() throws DataValidationFailedException {
211 final var mod = dataTree.takeSnapshot().newModification();
212 mod.write(YangInstanceIdentifier.of(BOX), containerBuilder().withNodeIdentifier(BOX)
213 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
214 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
215 .withChild(OBJECT_ID_LEAF)
219 mod.merge(YangInstanceIdentifier.of(BOX), containerBuilder().withNodeIdentifier(BOX)
220 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
221 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
222 .withChild(OBJECT_ID_LEAF)
223 .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
224 .withChildValue("object1")
234 void testEmptyAndMergeOneWithListTouched() throws DataValidationFailedException {
235 final var mod = dataTree.takeSnapshot().newModification();
236 mod.write(YangInstanceIdentifier.of(BOX), containerBuilder().withNodeIdentifier(BOX)
237 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
238 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
239 .withChild(OBJECT_ID_LEAF)
243 mod.merge(YangInstanceIdentifier.of(BOX), containerBuilder().withNodeIdentifier(BOX)
244 .withChild(mapBuilder().withNodeIdentifier(OBJECT_LIST)
245 .addChild(mapEntryBuilder().withNodeIdentifier(OBJECT_ITEM)
246 .withChild(OBJECT_ID_LEAF)
247 .withChild(leafSetBuilder().withNodeIdentifier(ATTRIBUTES)
248 .withChildValue("object1")
254 mod.delete(YangInstanceIdentifier.of(BOX, OBJECT_LIST, OBJECT_ITEM, NESTED_LIST, NESTED_ITEM));
260 void testDisappearInChoice() throws DataValidationFailedException {
261 var mod = dataTree.takeSnapshot().newModification();
262 // Initialize choice with list
263 mod.write(YangInstanceIdentifier.of(BOX), containerBuilder()
264 .withNodeIdentifier(BOX)
265 .withChild(choiceBuilder().withNodeIdentifier(ANY_OF)
266 .withChild(mapBuilder().withNodeIdentifier(SOME_LIST_ID)
267 .withChild(mapEntryBuilder()
268 .withNodeIdentifier(SOME_LIST_ITEM)
269 .withChild(leafBuilder().withNodeIdentifier(SOME_LEAF_ID).withValue("foo").build())
276 // Now delete the single item, causing the list to fizzle, while creating the alterinative case
277 mod = dataTree.takeSnapshot().newModification();
278 mod.delete(YangInstanceIdentifier.of(BOX, ANY_OF, SOME_LIST_ID, SOME_LIST_ITEM));
279 mod.write(YangInstanceIdentifier.of(BOX, ANY_OF, SOME_LEAF_ID),
280 leafBuilder().withNodeIdentifier(SOME_LEAF_ID).withValue("foo").build());
285 private DataTreeModification write(final ContainerNode data) throws DataValidationFailedException {
286 final var mod = dataTree.takeSnapshot().newModification();
287 mod.write(YangInstanceIdentifier.of(BOX), data);
291 private void writeAndCommit(final ContainerNode data) throws DataValidationFailedException {
292 final var mod = dataTree.takeSnapshot().newModification();
293 mod.write(YangInstanceIdentifier.of(BOX), data);
297 private void commit(final DataTreeModification mod) throws DataValidationFailedException {
299 dataTree.validate(mod);
300 dataTree.commit(dataTree.prepare(mod));