--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.net.URI;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OrderedListTest {
+ private Logger LOG = LoggerFactory.getLogger(OrderedListTest.class);
+
+ private TipProducingDataTree inMemoryDataTree;
+ private SchemaContext context;
+
+ private QNameModule testModule;
+ private QName parentContainer;
+ private QName childContainer;
+ private QName parentOrderedList;
+ private QName childOrderedList;
+ private QName parentKeyLeaf;
+ private QName parentOrdinaryLeaf;
+ private QName childKeyLeaf;
+ private QName childOrdinaryLeaf;
+
+ @Before
+ public void setup() throws Exception {
+ final File resourceFile = new File(Bug4295Test.class.getResource("/ordered-list-modification-test.yang")
+ .toURI());
+ context = RetestUtils.parseYangSources(resourceFile);
+ testModule = QNameModule.create(new URI("ordered-list-modification-test"),
+ SimpleDateFormatUtil.getRevisionFormat().parse("1970-01-01"));
+ parentContainer = QName.create(testModule, "parent-container");
+ childContainer = QName.create(testModule, "child-container");
+ parentOrderedList = QName.create(testModule, "parent-ordered-list");
+ childOrderedList = QName.create(testModule, "child-ordered-list");
+ parentKeyLeaf = QName.create(testModule, "parent-key-leaf");
+ childKeyLeaf = QName.create(testModule, "child-key-leaf");
+ parentOrdinaryLeaf = QName.create(testModule, "parent-ordinary-leaf");
+ childOrdinaryLeaf = QName.create(testModule, "child-ordinary-leaf");
+ inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.OPERATIONAL);
+ inMemoryDataTree.setSchemaContext(context);
+ }
+
+ @Test
+ public void testsequentialModifications() throws DataValidationFailedException {
+ modification1();
+ modification2();
+ delete1();
+ delete2();
+ modification3();
+ modification4();
+ }
+
+ public void modification1() throws DataValidationFailedException {
+ OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentOrderedList))
+ .withChild(createParentOrderedListEntry("pkval1", "plfval1"))
+ .withChild(createParentOrderedListEntry("pkval2", "plfval2"))
+ .withChild(createParentOrderedListEntry("pkval3", "plfval3")).build();
+
+ ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build())
+ .build();
+
+ YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainer);
+
+ DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+ treeModification.write(path1, parentContainerNode);
+
+ OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+ new NodeIdentifier(childOrderedList))
+ .withChild(createChildOrderedListEntry("chkval1", "chlfval1"))
+ .withChild(createChildOrderedListEntry("chkval2", "chlfval2")).build();
+
+ YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+ .node(parentOrderedList).node(createParentOrderedListEntryPath("pkval2")).node(childOrderedList);
+
+ treeModification.write(path2, childOrderedListNode);
+ treeModification.ready();
+ inMemoryDataTree.validate(treeModification);
+ inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+ DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+ Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
+ assertTrue(readNode.isPresent());
+
+ readNode = snapshotAfterCommits.readNode(path2);
+ assertTrue(readNode.isPresent());
+ }
+
+ public void modification2() throws DataValidationFailedException {
+ OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentOrderedList))
+ .withChild(createParentOrderedListEntry("pkval3", "plfval3updated"))
+ .withChild(createParentOrderedListEntry("pkval4", "plfval4"))
+ .withChild(createParentOrderedListEntry("pkval5", "plfval5")).build();
+
+ ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build())
+ .build();
+
+ DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+
+ YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainer);
+ treeModification.merge(path1, parentContainerNode);
+
+ OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+ new NodeIdentifier(childOrderedList))
+ .withChild(createChildOrderedListEntry("chkval1", "chlfval1updated"))
+ .withChild(createChildOrderedListEntry("chkval2", "chlfval2updated"))
+ .withChild(createChildOrderedListEntry("chkval3", "chlfval3")).build();
+
+ YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainer).node(childContainer).node
+ (parentOrderedList).node(createParentOrderedListEntryPath("pkval2")).node(childOrderedList);
+ treeModification.merge(path2, childOrderedListNode);
+
+ treeModification.ready();
+ inMemoryDataTree.validate(treeModification);
+ inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+ DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+ Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
+ assertTrue(readNode.isPresent());
+
+ readNode = snapshotAfterCommits.readNode(path2);
+ assertTrue(readNode.isPresent());
+ }
+
+ public void modification3() throws DataValidationFailedException {
+ OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentOrderedList))
+ .withChild(createParentOrderedListEntry("pkval1", "plfval1")).build();
+
+ ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build())
+ .build();
+
+ YangInstanceIdentifier path1 = YangInstanceIdentifier.of(parentContainer);
+
+ DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+ treeModification.write(path1, parentContainerNode);
+
+ OrderedMapNode childOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(
+ new NodeIdentifier(childOrderedList))
+ .withChild(createChildOrderedListEntry("chkval1", "chlfval1new")).build();
+
+ YangInstanceIdentifier path2 = YangInstanceIdentifier.of(parentContainer).node(childContainer).node
+ (parentOrderedList)
+ .node(createParentOrderedListEntryPath("pkval4")).node(childOrderedList);
+
+ treeModification.merge(path2, childOrderedListNode);
+
+ try {
+ treeModification.ready();
+ fail("Exception should have been thrown.");
+ inMemoryDataTree.validate(treeModification);
+ inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+ } catch (final IllegalArgumentException ex) {
+ LOG.debug("IllegalArgumentException was thrown as expected: {}", ex);
+ assertTrue(ex.getMessage().contains("Metadata not available for modification NodeModification"));
+ }
+
+ DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+ Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path1);
+ assertTrue(readNode.isPresent());
+
+ readNode = snapshotAfterCommits.readNode(path2);
+ assertFalse(readNode.isPresent());
+ }
+
+ public void modification4() throws DataValidationFailedException {
+ DataTreeModification treeModification1 = inMemoryDataTree.takeSnapshot().newModification();
+ DataTreeModification treeModification2 = inMemoryDataTree.takeSnapshot().newModification();
+
+ OrderedMapNode parentOrderedListNode = Builders.orderedMapBuilder().withNodeIdentifier(new
+ NodeIdentifier(parentOrderedList)).withChild(createParentOrderedListEntry("pkval1",
+ "plfval1")).build();
+
+ OrderedMapNode parentOrderedListNode2 = Builders.orderedMapBuilder().withNodeIdentifier(new
+ NodeIdentifier(parentOrderedList)).withChild(createParentOrderedListEntry("pkval2",
+ "plfval2")).build();
+
+ ContainerNode parentContainerNode = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode).build()).build();
+
+ ContainerNode parentContainerNode2 = Builders.containerBuilder().withNodeIdentifier(
+ new NodeIdentifier(parentContainer)).withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new NodeIdentifier(childContainer)).withChild(parentOrderedListNode2).build())
+ .build();
+
+ YangInstanceIdentifier path = YangInstanceIdentifier.of(parentContainer);
+
+ treeModification1.write(path, parentContainerNode);
+ treeModification2.write(path, parentContainerNode2);
+ treeModification1.ready();
+ treeModification2.ready();
+
+ inMemoryDataTree.validate(treeModification1);
+ inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification1));
+
+ try {
+ inMemoryDataTree.validate(treeModification2);
+ fail("Exception should have been thrown.");
+ inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification2));
+ } catch (ConflictingModificationAppliedException ex) {
+ LOG.debug("ConflictingModificationAppliedException was thrown as expected: {}", ex);
+ assertTrue(ex.getMessage().contains("Node was replaced by other transaction"));
+ }
+
+ DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+ Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path);
+ assertTrue(readNode.isPresent());
+ }
+
+ public void delete1() throws DataValidationFailedException {
+ YangInstanceIdentifier path = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+ .node(parentOrderedList).node(createParentOrderedListEntryPath("pkval2")).node(childOrderedList)
+ .node(createChildOrderedListEntryPath("chkval1"));
+
+ DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+ treeModification.delete(path);
+ treeModification.ready();
+ inMemoryDataTree.validate(treeModification);
+ inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+ DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+ Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path);
+ assertFalse(readNode.isPresent());
+ }
+
+ public void delete2() throws DataValidationFailedException {
+ YangInstanceIdentifier path = YangInstanceIdentifier.of(parentContainer).node(childContainer)
+ .node(parentOrderedList).node(createParentOrderedListEntryPath("pkval2"));
+
+ DataTreeModification treeModification = inMemoryDataTree.takeSnapshot().newModification();
+ treeModification.delete(path);
+ treeModification.ready();
+ inMemoryDataTree.validate(treeModification);
+ inMemoryDataTree.commit(inMemoryDataTree.prepare(treeModification));
+
+ DataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+ Optional<NormalizedNode<?, ?>> readNode = snapshotAfterCommits.readNode(path);
+ assertFalse(readNode.isPresent());
+ }
+
+ private MapEntryNode createParentOrderedListEntry(String keyValue, String leafValue) {
+ return Builders.mapEntryBuilder().withNodeIdentifier(new NodeIdentifierWithPredicates(parentOrderedList,
+ parentKeyLeaf, keyValue))
+ .withChild(Builders.leafBuilder().withNodeIdentifier(NodeIdentifier.create(parentOrdinaryLeaf)).withValue
+ (leafValue).build()).build();
+ }
+
+ private MapEntryNode createChildOrderedListEntry(String keyValue, String leafValue) {
+ return Builders.mapEntryBuilder().withNodeIdentifier(new NodeIdentifierWithPredicates(childOrderedList,
+ childKeyLeaf, keyValue))
+ .withChild(Builders.leafBuilder().withNodeIdentifier(NodeIdentifier.create(childOrdinaryLeaf)).withValue
+ (leafValue).build()).build();
+ }
+
+ private NodeIdentifierWithPredicates createParentOrderedListEntryPath(String keyValue) {
+ ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+ ImmutableMap<QName, Object> keys = builder.put(parentKeyLeaf, keyValue).build();
+ return new NodeIdentifierWithPredicates(parentOrderedList, keys);
+ }
+
+ private NodeIdentifierWithPredicates createChildOrderedListEntryPath(String keyValue) {
+ ImmutableMap.Builder<QName, Object> builder = ImmutableMap.builder();
+ ImmutableMap<QName, Object> keys = builder.put(childKeyLeaf, keyValue).build();
+ return new NodeIdentifierWithPredicates(childOrderedList, keys);
+ }
+}