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.spi.node.ImmutableNodes;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
29 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
30 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
31 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
33 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
35 class Bug5968MergeTest {
36 private static final String NS = "bug5968";
37 private static final String REV = "2016-07-28";
38 private static final QName ROOT = QName.create(NS, REV, "root");
39 private static final QName MY_LIST = QName.create(NS, REV, "my-list");
40 private static final QName LIST_ID = QName.create(NS, REV, "list-id");
41 private static final QName MANDATORY_LEAF = QName.create(NS, REV, "mandatory-leaf");
42 private static final QName COMMON_LEAF = QName.create(NS, REV, "common-leaf");
43 private static EffectiveModelContext SCHEMA_CONTEXT;
46 static void beforeClass() {
47 SCHEMA_CONTEXT = YangParserTestUtils.parseYang("""
76 static void afterClass() {
77 SCHEMA_CONTEXT = null;
80 private static DataTree initDataTree(final EffectiveModelContext schemaContext, final boolean withMapNode)
81 throws DataValidationFailedException {
82 final var inMemoryDataTree = new InMemoryDataTreeFactory().create(
83 DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
85 final var root = ImmutableNodes.newContainerBuilder()
86 .withNodeIdentifier(new NodeIdentifier(ROOT));
87 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
88 modificationTree.merge(YangInstanceIdentifier.of(ROOT),
89 withMapNode ? root.withChild(
90 ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST)).build()).build()
92 modificationTree.ready();
94 inMemoryDataTree.validate(modificationTree);
95 final var prepare = inMemoryDataTree.prepare(modificationTree);
96 inMemoryDataTree.commit(prepare);
98 return inMemoryDataTree;
101 private static DataTree emptyDataTree(final EffectiveModelContext schemaContext) {
102 return new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_CONFIGURATION, schemaContext);
106 void mergeInvalidContainerTest() throws DataValidationFailedException {
107 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
109 final var myList = createMap(true);
110 final var root = ImmutableNodes.newContainerBuilder()
111 .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
113 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
114 modificationTree.merge(YangInstanceIdentifier.of(ROOT), root.build());
117 modificationTree.ready();
118 inMemoryDataTree.validate(modificationTree);
119 final var prepare = inMemoryDataTree.prepare(modificationTree);
120 inMemoryDataTree.commit(prepare);
121 fail("Should fail due to missing mandatory leaf.");
122 } catch (final IllegalArgumentException e) {
123 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
124 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
129 void mergeInvalidMapTest() throws DataValidationFailedException {
130 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
131 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
132 mergeMap(modificationTree, true);
135 modificationTree.ready();
136 inMemoryDataTree.validate(modificationTree);
137 final var prepare = inMemoryDataTree.prepare(modificationTree);
138 inMemoryDataTree.commit(prepare);
139 fail("Should fail due to missing mandatory leaf.");
140 } catch (final IllegalArgumentException e) {
141 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
142 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
147 void mergeInvalidMapEntryTest() throws DataValidationFailedException {
148 final var inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
149 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
151 mergeMapEntry(modificationTree, "1", null, "common-value");
154 modificationTree.ready();
155 inMemoryDataTree.validate(modificationTree);
156 final var prepare = inMemoryDataTree.prepare(modificationTree);
157 inMemoryDataTree.commit(prepare);
158 fail("Should fail due to missing mandatory leaf.");
159 } catch (final IllegalArgumentException e) {
160 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
161 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
165 private static void mergeMap(final DataTreeModification modificationTree,
166 final boolean mandatoryDataMissing) throws DataValidationFailedException {
167 modificationTree.merge(YangInstanceIdentifier.of(ROOT, MY_LIST), createMap(mandatoryDataMissing));
170 private static SystemMapNode createMap(final boolean mandatoryDataMissing) throws DataValidationFailedException {
171 return ImmutableNodes.newSystemMapBuilder()
172 .withNodeIdentifier(new NodeIdentifier(MY_LIST))
173 .withChild(mandatoryDataMissing ? createMapEntry("1", "common-value")
174 : createMapEntry("1", "mandatory-value", "common-value"))
178 private static void mergeMapEntry(final DataTreeModification modificationTree, final Object listIdValue,
179 final Object mandatoryLeafValue, final Object commonLeafValue) throws DataValidationFailedException {
180 final var taskEntryNode = mandatoryLeafValue == null ? createMapEntry(listIdValue, commonLeafValue)
181 : createMapEntry(listIdValue, mandatoryLeafValue, commonLeafValue);
183 modificationTree.merge(
184 YangInstanceIdentifier.of(ROOT, MY_LIST)
185 .node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, listIdValue)),
189 private static MapEntryNode createMapEntry(final Object listIdValue, final Object mandatoryLeafValue,
190 final Object commonLeafValue) {
191 return ImmutableNodes.newMapEntryBuilder()
192 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, listIdValue))
193 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
194 .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF, mandatoryLeafValue))
195 .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
198 private static MapEntryNode createMapEntry(final Object listIdValue, final Object commonLeafValue) {
199 return ImmutableNodes.newMapEntryBuilder()
200 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
201 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
202 .withChild(ImmutableNodes.leafNode(COMMON_LEAF, commonLeafValue)).build();
205 private static MapEntryNode createMapEntryM(final Object listIdValue, final Object mandatoryLeafValue) {
206 return ImmutableNodes.newMapEntryBuilder()
207 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
208 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue))
209 .withChild(ImmutableNodes.leafNode(MANDATORY_LEAF, mandatoryLeafValue)).build();
213 void mergeValidContainerTest() throws DataValidationFailedException {
214 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
216 final var myList = createMap(false);
217 final var root = ImmutableNodes.newContainerBuilder()
218 .withNodeIdentifier(new NodeIdentifier(ROOT)).withChild(myList);
220 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
221 modificationTree.merge(YangInstanceIdentifier.of(ROOT), root.build());
222 modificationTree.ready();
223 inMemoryDataTree.validate(modificationTree);
224 final var prepare = inMemoryDataTree.prepare(modificationTree);
225 inMemoryDataTree.commit(prepare);
229 void mergeValidMapTest() throws DataValidationFailedException {
230 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
231 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
232 mergeMap(modificationTree, false);
234 modificationTree.ready();
235 inMemoryDataTree.validate(modificationTree);
236 final var prepare = inMemoryDataTree.prepare(modificationTree);
237 inMemoryDataTree.commit(prepare);
241 void mergeValidMapEntryTest() throws DataValidationFailedException {
242 final var inMemoryDataTree = initDataTree(SCHEMA_CONTEXT, true);
243 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
245 mergeMapEntry(modificationTree, "1", "mandatory-value", "common-value");
247 modificationTree.ready();
248 inMemoryDataTree.validate(modificationTree);
249 final var prepare = inMemoryDataTree.prepare(modificationTree);
250 inMemoryDataTree.commit(prepare);
254 void validMultiStepsMergeTest() throws DataValidationFailedException {
255 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
256 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
258 modificationTree.merge(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
259 modificationTree.merge(YangInstanceIdentifier.of(ROOT, MY_LIST), createMapBuilder().build());
260 modificationTree.merge(
261 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
262 createEmptyMapEntryBuilder("1").build());
263 modificationTree.merge(
264 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
265 createMapEntry("1", "mandatory-value", "common-value"));
267 modificationTree.ready();
268 inMemoryDataTree.validate(modificationTree);
269 final var prepare = inMemoryDataTree.prepare(modificationTree);
270 inMemoryDataTree.commit(prepare);
274 void invalidMultiStepsMergeTest() throws DataValidationFailedException {
275 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
276 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
278 modificationTree.merge(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
279 modificationTree.merge(YangInstanceIdentifier.of(ROOT, MY_LIST), createMapBuilder().build());
280 modificationTree.merge(
281 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
282 createEmptyMapEntryBuilder("1").build());
283 modificationTree.merge(
284 YangInstanceIdentifier.of(ROOT, MY_LIST).node(NodeIdentifierWithPredicates.of(MY_LIST, LIST_ID, "1")),
285 createMapEntry("1", "common-value"));
288 modificationTree.ready();
289 inMemoryDataTree.validate(modificationTree);
290 final var prepare = inMemoryDataTree.prepare(modificationTree);
291 inMemoryDataTree.commit(prepare);
292 fail("Should fail due to missing mandatory leaf.");
293 } catch (final IllegalArgumentException e) {
294 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
295 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
299 private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> createEmptyMapEntryBuilder(
300 final Object listIdValue) {
301 return ImmutableNodes.newMapEntryBuilder()
302 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, listIdValue)))
303 .withChild(ImmutableNodes.leafNode(LIST_ID, listIdValue));
306 private static CollectionNodeBuilder<MapEntryNode, SystemMapNode> createMapBuilder() {
307 return ImmutableNodes.newSystemMapBuilder().withNodeIdentifier(new NodeIdentifier(MY_LIST));
310 private static DataContainerNodeBuilder<NodeIdentifier, ContainerNode> createContainerBuilder() {
311 return ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(ROOT));
315 void validMultiStepsWriteAndMergeTest() throws DataValidationFailedException {
316 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
317 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
319 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
320 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
321 modificationTree.merge(
322 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
323 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
324 createEmptyMapEntryBuilder("1").build());
325 modificationTree.merge(
326 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
327 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
328 createMapEntry("1", "mandatory-value", "common-value"));
330 modificationTree.ready();
331 inMemoryDataTree.validate(modificationTree);
332 final var prepare = inMemoryDataTree.prepare(modificationTree);
333 inMemoryDataTree.commit(prepare);
337 void invalidMultiStepsWriteAndMergeTest() throws DataValidationFailedException {
338 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
339 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
341 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
342 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
343 modificationTree.merge(
344 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
345 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
346 createEmptyMapEntryBuilder("1").build());
347 modificationTree.merge(
348 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
349 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
350 createMapEntry("1", "common-value"));
353 modificationTree.ready();
354 inMemoryDataTree.validate(modificationTree);
355 final var prepare = inMemoryDataTree.prepare(modificationTree);
356 inMemoryDataTree.commit(prepare);
357 fail("Should fail due to missing mandatory leaf.");
358 } catch (final IllegalArgumentException e) {
359 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=1}] is "
360 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
365 void validMapEntryMultiCommitMergeTest() throws DataValidationFailedException {
366 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
367 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
369 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
370 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
371 modificationTree.merge(
372 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
373 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
374 createEmptyMapEntryBuilder("1").build());
375 modificationTree.merge(
376 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
377 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
378 createMapEntryM("1", "mandatory-value"));
380 modificationTree.ready();
381 inMemoryDataTree.validate(modificationTree);
382 final var prepare = inMemoryDataTree.prepare(modificationTree);
383 inMemoryDataTree.commit(prepare);
385 final var modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
386 modificationTree2.merge(
387 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
388 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
389 createMapEntry("1", "common-value"));
390 modificationTree2.ready();
391 inMemoryDataTree.validate(modificationTree2);
392 final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
393 inMemoryDataTree.commit(prepare2);
397 void invalidMapEntryMultiCommitMergeTest() throws DataValidationFailedException {
398 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
399 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
401 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
402 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
403 modificationTree.merge(
404 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
405 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
406 createEmptyMapEntryBuilder("1").build());
407 modificationTree.merge(
408 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
409 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
410 createMapEntryM("1", "mandatory-value"));
412 modificationTree.ready();
413 inMemoryDataTree.validate(modificationTree);
414 final var prepare = inMemoryDataTree.prepare(modificationTree);
415 inMemoryDataTree.commit(prepare);
417 final var modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
418 modificationTree2.write(
419 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
420 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
421 createMapEntry("1", "common-value"));
422 modificationTree2.merge(
423 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
424 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
425 createMapEntryM("1", "mandatory-value"));
426 modificationTree2.merge(
427 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
428 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "2"))),
429 createMapEntry("2", "common-value"));
431 modificationTree2.ready();
432 inMemoryDataTree.validate(modificationTree2);
433 final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
434 inMemoryDataTree.commit(prepare2);
435 fail("Should fail due to missing mandatory leaf.");
436 } catch (final IllegalArgumentException e) {
437 assertEquals("Node (bug5968?revision=2016-07-28)my-list[{(bug5968?revision=2016-07-28)list-id=2}] is "
438 + "missing mandatory descendant /(bug5968?revision=2016-07-28)mandatory-leaf", e.getMessage());
443 * This test consists of two transactions (i.e. data tree modifications) on
444 * empty data tree. The first one writes mandatory data and second one
445 * writes common data without any mandatory data.
448 void validMapEntryMultiCommitMergeTest2() throws DataValidationFailedException {
449 final var inMemoryDataTree = emptyDataTree(SCHEMA_CONTEXT);
450 final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
451 final var modificationTree2 = inMemoryDataTree.takeSnapshot().newModification();
453 modificationTree.write(YangInstanceIdentifier.of(ROOT), createContainerBuilder().build());
454 modificationTree.merge(YangInstanceIdentifier.of(ROOT).node(MY_LIST), createMapBuilder().build());
455 modificationTree.merge(
456 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
457 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
458 createEmptyMapEntryBuilder("1").build());
459 modificationTree.merge(
460 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
461 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
462 createMapEntryM("1", "mandatory-value"));
464 modificationTree.ready();
465 inMemoryDataTree.validate(modificationTree);
466 final var prepare = inMemoryDataTree.prepare(modificationTree);
467 inMemoryDataTree.commit(prepare);
469 modificationTree2.merge(
470 YangInstanceIdentifier.of(ROOT).node(MY_LIST)
471 .node(NodeIdentifierWithPredicates.of(MY_LIST, ImmutableMap.of(LIST_ID, "1"))),
472 createMapEntry("1", "common-value"));
473 modificationTree2.ready();
474 inMemoryDataTree.validate(modificationTree2);
475 final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
476 inMemoryDataTree.commit(prepare2);