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.assertThrows;
13 import java.util.Collection;
14 import java.util.List;
15 import org.junit.jupiter.api.BeforeEach;
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.schema.LeafNode;
21 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
22 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
23 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
24 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
25 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidateNode;
26 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
28 import org.opendaylight.yangtools.yang.data.tree.api.ModificationType;
29 import org.opendaylight.yangtools.yang.data.tree.api.TreeType;
30 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
31 import org.opendaylight.yangtools.yang.data.tree.spi.DataTreeCandidates;
32 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
34 class DataTreeCandidatesTest extends AbstractTestModelTest {
35 private DataTree dataTree;
38 void setUp() throws Exception {
39 dataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL, SCHEMA_CONTEXT);
41 final var testContainer = Builders.containerBuilder()
42 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
43 .withChild(Builders.containerBuilder()
44 .withNodeIdentifier(new NodeIdentifier(SchemaContext.NAME))
48 final var modification = (InMemoryDataTreeModification) dataTree.takeSnapshot()
50 final var cursor = modification.openCursor();
51 cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
54 dataTree.validate(modification);
55 final var candidate = dataTree.prepare(modification);
56 dataTree.commit(candidate);
60 void testRootedCandidate() throws DataValidationFailedException {
61 final var innerDataTree = new InMemoryDataTreeFactory().create(
62 new DataTreeConfiguration.Builder(TreeType.OPERATIONAL)
63 .setMandatoryNodesValidation(true)
64 .setRootPath(TestModel.INNER_CONTAINER_PATH)
65 .setUniqueIndexes(true).build(), SCHEMA_CONTEXT);
67 final var leaf = ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "testing-value");
69 final var modification = innerDataTree.takeSnapshot().newModification();
70 modification.write(TestModel.VALUE_PATH, leaf);
73 dataTree.validate(modification);
74 final var candidate = dataTree.prepare(modification);
75 dataTree.commit(candidate);
77 final var newModification = dataTree.takeSnapshot().newModification();
78 final var newCandidate = DataTreeCandidates.newDataTreeCandidate(TestModel.INNER_CONTAINER_PATH,
79 candidate.getRootNode());
81 // lets see if getting the identifier of the root node throws an exception
82 assertThrows(IllegalStateException.class, () -> newCandidate.getRootNode().name());
84 // lets see if we can apply this rooted candidate to a new dataTree
85 DataTreeCandidates.applyToModification(newModification,
88 final var readLeaf = (LeafNode<?>) newModification.readNode(TestModel.INNER_VALUE_PATH).orElseThrow();
89 assertEquals(readLeaf, leaf);
93 void testEmptyMergeOnContainer() throws DataValidationFailedException {
94 final var modification = dataTree.takeSnapshot().newModification();
95 modification.merge(TestModel.NON_PRESENCE_PATH, ImmutableNodes.containerNode(TestModel.NON_PRESENCE_QNAME));
97 dataTree.validate(modification);
99 // The entire transaction needs to fizzle to a no-op
100 final var candidate = dataTree.prepare(modification);
101 final var node = candidate.getRootNode();
102 assertEquals(ModificationType.UNMODIFIED, node.modificationType());
105 assertUnmodified(1, node.childNodes());
109 void testEmptyWriteOnContainer() throws DataValidationFailedException {
110 final var modification = dataTree.takeSnapshot().newModification();
111 modification.write(TestModel.NON_PRESENCE_PATH, ImmutableNodes.containerNode(TestModel.NON_PRESENCE_QNAME));
112 modification.ready();
113 dataTree.validate(modification);
115 // The entire transaction needs to fizzle to a no-op
116 final var candidate = dataTree.prepare(modification);
117 final var node = candidate.getRootNode();
118 assertEquals(ModificationType.UNMODIFIED, node.modificationType());
121 assertUnmodified(1, node.childNodes());
125 void testEmptyMergesOnDeleted() throws DataValidationFailedException {
126 final var modification = dataTree.takeSnapshot().newModification();
127 modification.delete(TestModel.NON_PRESENCE_PATH);
128 modification.merge(TestModel.DEEP_CHOICE_PATH, ImmutableNodes.choiceNode(TestModel.DEEP_CHOICE_QNAME));
129 modification.ready();
130 dataTree.validate(modification);
132 final var candidate = dataTree.prepare(modification);
133 assertEquals(YangInstanceIdentifier.of(), candidate.getRootPath());
134 final var node = candidate.getRootNode();
135 assertEquals(ModificationType.UNMODIFIED, node.modificationType());
138 assertUnmodified(1, node.childNodes());
142 void testEmptyMergesOnExisting() throws DataValidationFailedException {
143 // Make sure 'non-presence' is present
144 var modification = dataTree.takeSnapshot().newModification();
145 modification.write(TestModel.NAME_PATH, ImmutableNodes.leafNode(TestModel.NAME_QNAME, "foo"));
146 modification.ready();
147 dataTree.validate(modification);
148 dataTree.commit(dataTree.prepare(modification));
150 // Issue an empty merge on it and a child choice
151 modification = dataTree.takeSnapshot().newModification();
152 modification.merge(TestModel.NON_PRESENCE_PATH, ImmutableNodes.containerNode(TestModel.NON_PRESENCE_QNAME));
153 modification.merge(TestModel.DEEP_CHOICE_PATH, ImmutableNodes.choiceNode(TestModel.DEEP_CHOICE_QNAME));
154 modification.ready();
155 dataTree.validate(modification);
157 // The entire transaction needs to fizzle to a no-op
158 final var candidate = dataTree.prepare(modification);
159 assertEquals(YangInstanceIdentifier.of(), candidate.getRootPath());
160 final var node = candidate.getRootNode();
161 assertEquals(ModificationType.UNMODIFIED, node.modificationType());
163 // 'non-presence' and 'test'
164 assertUnmodified(2, node.childNodes());
168 void testAggregateWithoutChanges() throws DataValidationFailedException {
169 final var modification1 = dataTree.takeSnapshot().newModification();
171 TestModel.INNER_CONTAINER_PATH.node(QName.create(TestModel.INNER_CONTAINER_QNAME,"value")),
172 ImmutableNodes.leafNode(QName.create(TestModel.INNER_CONTAINER_QNAME,"value"),"value1"));
173 modification1.ready();
174 dataTree.validate(modification1);
175 DataTreeCandidate candidate1 = dataTree.prepare(modification1);
176 dataTree.commit(candidate1);
178 final var modification2 = dataTree.takeSnapshot().newModification();
179 modification2.delete(TestModel.INNER_CONTAINER_PATH);
180 modification2.ready();
181 dataTree.validate(modification2);
182 final var candidate2 = dataTree.prepare(modification2);
183 dataTree.commit(candidate2);
185 final var aggregateCandidate = DataTreeCandidates.aggregate(List.of(candidate1, candidate2));
187 assertEquals(ModificationType.UNMODIFIED, aggregateCandidate.getRootNode().modificationType());
191 void testAggregate() throws DataValidationFailedException {
192 final var modification = dataTree.takeSnapshot().newModification();
194 TestModel.INNER_CONTAINER_PATH.node(QName.create(TestModel.INNER_CONTAINER_QNAME,"value")),
195 ImmutableNodes.leafNode(QName.create(TestModel.INNER_CONTAINER_QNAME,"value"),"value1"));
196 modification.ready();
197 dataTree.validate(modification);
198 final var candidate = dataTree.prepare(modification);
199 dataTree.commit(candidate);
201 final var modification1 = dataTree.takeSnapshot().newModification();
202 modification1.delete(TestModel.INNER_CONTAINER_PATH);
203 modification1.ready();
204 dataTree.validate(modification1);
205 DataTreeCandidate candidate1 = dataTree.prepare(modification1);
206 dataTree.commit(candidate1);
208 final var modification2 = dataTree.takeSnapshot().newModification();
210 TestModel.INNER_CONTAINER_PATH.node(QName.create(TestModel.INNER_CONTAINER_QNAME,"value")),
211 ImmutableNodes.leafNode(QName.create(TestModel.INNER_CONTAINER_QNAME,"value"),"value2"));
212 modification2.ready();
213 dataTree.validate(modification2);
214 final var candidate2 = dataTree.prepare(modification2);
215 dataTree.commit(candidate2);
217 final var aggregateCandidate = DataTreeCandidates.aggregate(List.of(candidate1, candidate2));
219 assertEquals(ModificationType.SUBTREE_MODIFIED,aggregateCandidate.getRootNode().modificationType());
222 private static void assertUnmodified(final int expSize, final Collection<DataTreeCandidateNode> nodes) {
223 assertEquals(expSize, nodes.size());
224 nodes.forEach(node -> assertEquals(ModificationType.UNMODIFIED, node.modificationType()));