2 * Copyright (c) 2016 Cisco Systems, Inc. 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.fail;
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.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.MapEntryNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
24 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
25 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
26 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
27 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
29 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
30 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
31 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
32 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
33 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
34 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
36 class Bug5968MergeTest {
37 private static final String NS = "bug5968";
38 private static final String REV = "2016-07-28";
39 private static final QName ROOT = QName.create(NS, REV, "root");
40 private static final QName MY_LIST = QName.create(NS, REV, "my-list");
41 private static final QName LIST_ID = QName.create(NS, REV, "list-id");
42 private static final QName MANDATORY_LEAF = QName.create(NS, REV, "mandatory-leaf");
43 private static final QName COMMON_LEAF = QName.create(NS, REV, "common-leaf");
44 private static EffectiveModelContext SCHEMA_CONTEXT;
47 static void beforeClass() {
48 SCHEMA_CONTEXT = YangParserTestUtils.parseYang("""
77 static void afterClass() {
78 SCHEMA_CONTEXT = null;
81 private static DataTree initDataTree(final EffectiveModelContext schemaContext, final boolean withMapNode)
82 throws DataValidationFailedException {
83 final var inMemoryDataTree = new InMemoryDataTreeFactory().create(
84 DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
86 final var root = Builders.containerBuilder()
87 .withNodeIdentifier(new NodeIdentifier(ROOT));
88 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
89 modificationTree.merge(YangInstanceIdentifier.of(ROOT),
90 withMapNode ? root.withChild(
91 Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST)).build()).build()
93 modificationTree.ready();
95 inMemoryDataTree.validate(modificationTree);
96 final var prepare = inMemoryDataTree.prepare(modificationTree);
97 inMemoryDataTree.commit(prepare);
99 return inMemoryDataTree;
102 private static DataTree emptyDataTree(final EffectiveModelContext schemaContext) {
103 return new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
107 void mergeInvalidContainerTest() throws DataValidationFailedException {
108 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
110 final var myList = createMap(true);
111 final var root = Builders.containerBuilder()
112 .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
114 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
115 modificationTree.merge(YangInstanceIdentifier.of(ROOT), root.build());
118 modificationTree.ready();
119 inMemoryDataTree.validate(modificationTree);
120 final var prepare = inMemoryDataTree.prepare(modificationTree);
121 inMemoryDataTree.commit(prepare);
122 fail("Should fail due to missing mandatory leaf.");
123 } catch (final IllegalArgumentException e) {
124 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
125 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
130 void mergeInvalidMapTest() throws DataValidationFailedException {
131 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
132 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
133 mergeMap(modificationTree, true);
136 modificationTree.ready();
137 inMemoryDataTree.validate(modificationTree);
138 final var prepare = inMemoryDataTree.prepare(modificationTree);
139 inMemoryDataTree.commit(prepare);
140 fail("Should fail due to missing mandatory leaf.");
141 } catch (final IllegalArgumentException e) {
142 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
143 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
148 void mergeInvalidMapEntryTest() throws DataValidationFailedException {
149 final var inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
150 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
152 mergeMapEntry(modificationTree, "1", null, "common-value");
155 modificationTree.ready();
156 inMemoryDataTree.validate(modificationTree);
157 final var prepare = inMemoryDataTree.prepare(modificationTree);
158 inMemoryDataTree.commit(prepare);
159 fail("Should fail due to missing mandatory leaf.");
160 } catch (final IllegalArgumentException e) {
161 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
162 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
166 private static void mergeMap(final DataTreeModification modificationTree,
167 final boolean mandatoryDataMissing) throws DataValidationFailedException {
168 modificationTree.merge(YangInstanceIdentifier.of(ROOT, MY_LIST), createMap(mandatoryDataMissing));
171 private static SystemMapNode createMap(final boolean mandatoryDataMissing) throws DataValidationFailedException {
172 return Builders.mapBuilder()
173 .withNodeIdentifier(new NodeIdentifier(MY_LIST))
174 .withChild(mandatoryDataMissing ? createMapEntry("1", "common-value")
175 : createMapEntry("1", "mandatory-value", "common-value"))
179 private static void mergeMapEntry(final DataTreeModification modificationTree, final Object listIdValue,
180 final Object mandatoryLeafValue, final Object commonLeafValue) throws DataValidationFailedException {
181 final var taskEntryNode = mandatoryLeafValue == null ? createMapEntry(listIdValue, commonLeafValue)
182 : createMapEntry(listIdValue, mandatoryLeafValue, commonLeafValue);
184 modificationTree.merge(
185 YangInstanceIdentifier.of(ROOT, MY_LIST)
186 .node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, listIdValue)),
190 private static MapEntryNode createMapEntry(final Object listIdValue, final Object mandatoryLeafValue,
191 final Object commonLeafValue) {
192 return Builders.mapEntryBuilder()
193 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, listIdValue))
194 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
195 .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF, mandatoryLeafValue))
196 .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
199 private static MapEntryNode createMapEntry(final Object listIdValue, final Object commonLeafValue) {
200 return Builders.mapEntryBuilder()
201 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
202 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
203 .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
206 private static MapEntryNode createMapEntryM(final Object listIdValue, final Object mandatoryLeafValue) {
207 return Builders.mapEntryBuilder()
208 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
209 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
210 .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF, mandatoryLeafValue)).build();
214 void mergeValidContainerTest() throws DataValidationFailedException {
215 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
217 final var myList = createMap(false);
218 final var root = Builders.containerBuilder()
219 .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
221 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
222 modificationTree.merge(YangInstanceIdentifier.of(ROOT), root.build());
223 modificationTree.ready();
224 inMemoryDataTree.validate(modificationTree);
225 final var prepare = inMemoryDataTree.prepare(modificationTree);
226 inMemoryDataTree.commit(prepare);
230 void mergeValidMapTest() throws DataValidationFailedException {
231 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
232 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
233 mergeMap(modificationTree, false);
235 modificationTree.ready();
236 inMemoryDataTree.validate(modificationTree);
237 final var prepare = inMemoryDataTree.prepare(modificationTree);
238 inMemoryDataTree.commit(prepare);
242 void mergeValidMapEntryTest() throws DataValidationFailedException {
243 final var inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
244 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
246 mergeMapEntry(modificationTree, "1", "mandatory-value", "common-value");
248 modificationTree.ready();
249 inMemoryDataTree.validate(modificationTree);
250 final var prepare = inMemoryDataTree.prepare(modificationTree);
251 inMemoryDataTree.commit(prepare);
255 void validMultiStepsMergeTest() throws DataValidationFailedException {
256 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
257 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
259 modificationTree.merge(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
260 modificationTree.merge(YangInstanceIdentifier.of(ROOT, MY_LIST), createMapBuilder().build());
261 modificationTree.merge(
262 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
263 createEmptyMapEntryBuilder("1").build());
264 modificationTree.merge(
265 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
266 createMapEntry("1", "mandatory-value", "common-value"));
268 modificationTree.ready();
269 inMemoryDataTree.validate(modificationTree);
270 final var prepare = inMemoryDataTree.prepare(modificationTree);
271 inMemoryDataTree.commit(prepare);
275 void invalidMultiStepsMergeTest() throws DataValidationFailedException {
276 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
277 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
279 modificationTree.merge(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
280 modificationTree.merge(YangInstanceIdentifier.of(ROOT, MY_LIST), createMapBuilder().build());
281 modificationTree.merge(
282 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
283 createEmptyMapEntryBuilder("1").build());
284 modificationTree.merge(
285 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
286 createMapEntry("1", "common-value"));
289 modificationTree.ready();
290 inMemoryDataTree.validate(modificationTree);
291 final var prepare = inMemoryDataTree.prepare(modificationTree);
292 inMemoryDataTree.commit(prepare);
293 fail("Should fail due to missing mandatory leaf.");
294 } catch (final IllegalArgumentException e) {
295 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
296 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
300 private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createEmptyMapEntryBuilder(
301 final Object listIdValue) {
302 return Builders.mapEntryBuilder()
303 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
304 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue));
307 private static CollectionNodeBuilder<MapEntryNode, SystemMapNode> createMapBuilder() {
308 return Builders.mapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST));
311 private static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createContainerBuilder() {
312 return Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(ROOT));
316 void validMultiStepsWriteAndMergeTest() throws DataValidationFailedException {
317 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
318 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
320 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
321 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
322 modificationTree.merge(
323 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
324 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
325 createEmptyMapEntryBuilder("1").build());
326 modificationTree.merge(
327 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
328 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
329 createMapEntry("1", "mandatory-value", "common-value"));
331 modificationTree.ready();
332 inMemoryDataTree.validate(modificationTree);
333 final var prepare = inMemoryDataTree.prepare(modificationTree);
334 inMemoryDataTree.commit(prepare);
338 void invalidMultiStepsWriteAndMergeTest() throws DataValidationFailedException {
339 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
340 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
342 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
343 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
344 modificationTree.merge(
345 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
346 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
347 createEmptyMapEntryBuilder("1").build());
348 modificationTree.merge(
349 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
350 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
351 createMapEntry("1", "common-value"));
354 modificationTree.ready();
355 inMemoryDataTree.validate(modificationTree);
356 final var prepare = inMemoryDataTree.prepare(modificationTree);
357 inMemoryDataTree.commit(prepare);
358 fail("Should fail due to missing mandatory leaf.");
359 } catch (final IllegalArgumentException e) {
360 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
361 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
366 void validMapEntryMultiCommitMergeTest() throws DataValidationFailedException {
367 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
368 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
370 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
371 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
372 modificationTree.merge(
373 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
374 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
375 createEmptyMapEntryBuilder("1").build());
376 modificationTree.merge(
377 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
378 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
379 createMapEntryM("1", "mandatory-value"));
381 modificationTree.ready();
382 inMemoryDataTree.validate(modificationTree);
383 final var prepare = inMemoryDataTree.prepare(modificationTree);
384 inMemoryDataTree.commit(prepare);
386 final var modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
387 modificationTree2.merge(
388 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
389 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
390 createMapEntry("1", "common-value"));
391 modificationTree2.ready();
392 inMemoryDataTree.validate(modificationTree2);
393 final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
394 inMemoryDataTree.commit(prepare2);
398 void invalidMapEntryMultiCommitMergeTest() throws DataValidationFailedException {
399 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
400 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
402 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
403 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
404 modificationTree.merge(
405 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
406 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
407 createEmptyMapEntryBuilder("1").build());
408 modificationTree.merge(
409 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
410 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
411 createMapEntryM("1", "mandatory-value"));
413 modificationTree.ready();
414 inMemoryDataTree.validate(modificationTree);
415 final var prepare = inMemoryDataTree.prepare(modificationTree);
416 inMemoryDataTree.commit(prepare);
418 final var modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
419 modificationTree2.write(
420 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
421 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
422 createMapEntry("1", "common-value"));
423 modificationTree2.merge(
424 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
425 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
426 createMapEntryM("1", "mandatory-value"));
427 modificationTree2.merge(
428 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
429 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "2"))),
430 createMapEntry("2", "common-value"));
432 modificationTree2.ready();
433 inMemoryDataTree.validate(modificationTree2);
434 final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
435 inMemoryDataTree.commit(prepare2);
436 fail("Should fail due to missing mandatory leaf.");
437 } catch (final IllegalArgumentException e) {
438 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=2}] is "
439 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
444 * This test consists of two transactions (i.e. data tree modifications) on
445 * empty data tree. The first one writes mandatory data and second one
446 * writes common data without any mandatory data.
449 void validMapEntryMultiCommitMergeTest2() throws DataValidationFailedException {
450 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
451 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
452 final var modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
454 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
455 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
456 modificationTree.merge(
457 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
458 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
459 createEmptyMapEntryBuilder("1").build());
460 modificationTree.merge(
461 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
462 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
463 createMapEntryM("1", "mandatory-value"));
465 modificationTree.ready();
466 inMemoryDataTree.validate(modificationTree);
467 final var prepare = inMemoryDataTree.prepare(modificationTree);
468 inMemoryDataTree.commit(prepare);
470 modificationTree2.merge(
471 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
472 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
473 createMapEntry("1", "common-value"));
474 modificationTree2.ready();
475 inMemoryDataTree.validate(modificationTree2);
476 final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
477 inMemoryDataTree.commit(prepare2);