From d23f4be2c62a8fbab984fc7dea8ec2e317b8a662 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 14 Jun 2016 10:48:26 +0200 Subject: HONEYCOMB-92: Process modifications recursively + Fix update subtree, child writer lookup + Change initializers operation to merge Change-Id: I6ece7eb3d17d5a0b4a413189ddd383567d7e2270 Signed-off-by: Maros Marsalek --- .../v3po/data/impl/DataTreeUtilsTest.java | 68 ---- .../data/impl/ModifiableDataTreeDelegatorTest.java | 100 ++++-- .../v3po/data/impl/ModificationDiffTest.java | 376 +++++++++++++++++++++ 3 files changed, 446 insertions(+), 98 deletions(-) delete mode 100644 v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtilsTest.java create mode 100644 v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java (limited to 'v3po/data-impl/src/test/java/io') diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtilsTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtilsTest.java deleted file mode 100644 index 40792a135..000000000 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtilsTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.v3po.data.impl; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import org.junit.Test; -import org.mockito.Matchers; -import org.mockito.Mockito; -import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; -import org.opendaylight.yangtools.yang.binding.DataObject; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; - -public class DataTreeUtilsTest { - - @Test - public void testChildrenFromNormalized() throws Exception { - final ContainerNode parent = Mockito.mock(ContainerNode.class); - final BindingNormalizedNodeSerializer serializer = Mockito.mock(BindingNormalizedNodeSerializer.class); - - final Collection list = new ArrayList<>(); - Mockito.doReturn(list).when(parent).getValue(); - - // init child1 (will not be serialized) - final DataContainerChild child1 = Mockito.mock(DataContainerChild.class); - Mockito.when(child1.getIdentifier()).thenReturn(Mockito.mock(YangInstanceIdentifier.PathArgument.class)); - Mockito.when(serializer.fromNormalizedNode(Matchers.any(YangInstanceIdentifier.class), Matchers.eq(child1))).thenReturn(null); - list.add(child1); - - // init child 2 (will be serialized) - final DataContainerChild child2 = Mockito.mock(DataContainerChild.class); - Mockito.when(child2.getIdentifier()).thenReturn(Mockito.mock(YangInstanceIdentifier.PathArgument.class)); - - final Map.Entry entry = Mockito.mock(Map.Entry.class); - final InstanceIdentifier id = Mockito.mock(InstanceIdentifier.class); - Mockito.doReturn(id).when(entry).getKey(); - final DataObject data = Mockito.mock(DataObject.class); - Mockito.doReturn(data).when(entry).getValue(); - Mockito.when(serializer.fromNormalizedNode(Matchers.any(YangInstanceIdentifier.class), Matchers.eq(child2))).thenReturn(entry); - - list.add(child2); - - // run tested method - final Map, DataObject> map = DataTreeUtils.childrenFromNormalized(parent, serializer); - assertEquals(1, map.size()); - assertEquals(data, map.get(id)); - } -} \ No newline at end of file diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java index fed32da8a..086636de6 100644 --- a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java @@ -37,9 +37,8 @@ import io.fd.honeycomb.v3po.data.DataModification; import io.fd.honeycomb.v3po.translate.TranslationException; import io.fd.honeycomb.v3po.translate.write.WriteContext; import io.fd.honeycomb.v3po.translate.write.WriterRegistry; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.HashMap; import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -58,6 +57,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; public class ModifiableDataTreeDelegatorTest { @@ -109,7 +109,7 @@ public class ModifiableDataTreeDelegatorTest { when(dataTree.takeSnapshot()).thenReturn(snapshot); when(snapshot.newModification()).thenReturn(modification); - final DataModification dataTreeSnapshot = configDataTree.newModification(); + configDataTree.newModification(); // Snapshot captured twice, so that original data could be provided to translation layer without any possible // modification verify(dataTree, times(2)).takeSnapshot(); @@ -135,12 +135,19 @@ public class ModifiableDataTreeDelegatorTest { // Prepare modification: final DataTreeCandidateNode rootNode = mockRootNode(); + doReturn(ModificationType.SUBTREE_MODIFIED).when(rootNode).getModificationType(); + doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(rootNode).getIdentifier(); + final DataTreeCandidateNode childNode = mock(DataTreeCandidateNode.class); + doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(childNode).getIdentifier(); + doReturn(Collections.singleton(childNode)).when(rootNode).getChildNodes(); + doReturn(ModificationType.WRITE).when(childNode).getModificationType(); + // data before: final ContainerNode nodeBefore = mockContainerNode(dataBefore); - when(rootNode.getDataBefore()).thenReturn(Optional.>fromNullable(nodeBefore)); + when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore)); // data after: final ContainerNode nodeAfter = mockContainerNode(dataAfter); - when(rootNode.getDataAfter()).thenReturn(Optional.>fromNullable(nodeAfter)); + when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter)); // Run the test doReturn(rootNode).when(prepare).getRootNode(); @@ -160,9 +167,7 @@ public class ModifiableDataTreeDelegatorTest { } private Map, DataObject> mapOf(final DataObject dataBefore, final Class type) { - return eq( - Collections., DataObject>singletonMap(InstanceIdentifier.create(type), - dataBefore)); + return eq(Collections.singletonMap(InstanceIdentifier.create(type),dataBefore)); } private DataObject mockDataObject(final String name, final Class classToMock) { @@ -194,12 +199,19 @@ public class ModifiableDataTreeDelegatorTest { // Prepare modification: final DataTreeCandidateNode rootNode = mockRootNode(); + doReturn(ModificationType.SUBTREE_MODIFIED).when(rootNode).getModificationType(); + doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(rootNode).getIdentifier(); + final DataTreeCandidateNode childNode = mock(DataTreeCandidateNode.class); + doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(childNode).getIdentifier(); + doReturn(Collections.singleton(childNode)).when(rootNode).getChildNodes(); + doReturn(ModificationType.WRITE).when(childNode).getModificationType(); + // data before: final ContainerNode nodeBefore = mockContainerNode(dataBefore); - when(rootNode.getDataBefore()).thenReturn(Optional.>fromNullable(nodeBefore)); + when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore)); // data after: final ContainerNode nodeAfter = mockContainerNode(dataAfter); - when(rootNode.getDataAfter()).thenReturn(Optional.>fromNullable(nodeAfter)); + when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter)); // Run the test try { @@ -245,12 +257,19 @@ public class ModifiableDataTreeDelegatorTest { // Prepare modification: final DataTreeCandidateNode rootNode = mockRootNode(); + doReturn(ModificationType.SUBTREE_MODIFIED).when(rootNode).getModificationType(); + doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(rootNode).getIdentifier(); + final DataTreeCandidateNode childNode = mock(DataTreeCandidateNode.class); + doReturn(mock(YangInstanceIdentifier.PathArgument.class)).when(childNode).getIdentifier(); + doReturn(Collections.singleton(childNode)).when(rootNode).getChildNodes(); + doReturn(ModificationType.WRITE).when(childNode).getModificationType(); + // data before: final ContainerNode nodeBefore = mockContainerNode(dataBefore); - when(rootNode.getDataBefore()).thenReturn(Optional.>fromNullable(nodeBefore)); + when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore)); // data after: final ContainerNode nodeAfter = mockContainerNode(dataAfter); - when(rootNode.getDataAfter()).thenReturn(Optional.>fromNullable(nodeAfter)); + when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter)); // Run the test try { @@ -267,6 +286,37 @@ public class ModifiableDataTreeDelegatorTest { fail("RevertFailedException was expected"); } + @Test + public void testChildrenFromNormalized() throws Exception { + final BindingNormalizedNodeSerializer serializer = mock(BindingNormalizedNodeSerializer.class); + + final Map> map = new HashMap<>(); + + // init child1 (will not be serialized) + final DataContainerChild child1 = mock(DataContainerChild.class); + when(child1.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class)); + when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(child1))).thenReturn(null); + map.put(mock(YangInstanceIdentifier.class), child1); + + // init child 2 (will be serialized) + final DataContainerChild child2 = mock(DataContainerChild.class); + when(child2.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class)); + + final Map.Entry entry = mock(Map.Entry.class); + final InstanceIdentifier id = mock(InstanceIdentifier.class); + doReturn(id).when(entry).getKey(); + final DataObject data = mock(DataObject.class); + doReturn(data).when(entry).getValue(); + when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(child2))).thenReturn(entry); + map.put(mock(YangInstanceIdentifier.class), child2); + + // run tested method + final Map, DataObject> baMap = + ModifiableDataTreeDelegator.toBindingAware(map, serializer); + assertEquals(1, baMap.size()); + assertEquals(data, baMap.get(id)); + } + private DataTreeCandidateNode mockRootNode() { final DataTreeCandidate candidate = mock(DataTreeCandidate.class); when(dataTree.prepare(modification)).thenReturn(candidate); @@ -277,32 +327,22 @@ public class ModifiableDataTreeDelegatorTest { return rootNode; } - private ContainerNode mockContainerNode(DataObject... modifications) { - final int numberOfChildren = modifications.length; - + private ContainerNode mockContainerNode(DataObject modification) { final YangInstanceIdentifier.NodeIdentifier identifier = YangInstanceIdentifier.NodeIdentifier.create(QName.create("/")); final ContainerNode node = mock(ContainerNode.class); when(node.getIdentifier()).thenReturn(identifier); - final List list = new ArrayList<>(numberOfChildren); - doReturn(list).when(node).getValue(); + final Map.Entry entry = mock(Map.Entry.class); + final Class implementedInterface = + (Class) modification.getImplementedInterface(); + final InstanceIdentifier id = InstanceIdentifier.create(implementedInterface); - for (DataObject modification : modifications) { - final DataContainerChild child = mock(DataContainerChild.class); - when(child.getIdentifier()).thenReturn(mock(YangInstanceIdentifier.PathArgument.class)); - list.add(child); + doReturn(id).when(entry).getKey(); + doReturn(modification).when(entry).getValue(); + doReturn(entry).when(serializer).fromNormalizedNode(any(YangInstanceIdentifier.class), eq(node)); - final Map.Entry entry = mock(Map.Entry.class); - final Class implementedInterface = - (Class) modification.getImplementedInterface(); - final InstanceIdentifier id = InstanceIdentifier.create(implementedInterface); - - doReturn(id).when(entry).getKey(); - doReturn(modification).when(entry).getValue(); - doReturn(entry).when(serializer).fromNormalizedNode(any(YangInstanceIdentifier.class), eq(child)); - } return node; } } diff --git a/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java new file mode 100644 index 000000000..f5c270618 --- /dev/null +++ b/v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java @@ -0,0 +1,376 @@ +package io.fd.honeycomb.v3po.data.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Map; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +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.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip; +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.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; + +public class ModificationDiffTest { + + private static final QName TOP_CONTAINER_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container"); + private static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string"); + private static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name"); + private static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text"); + private static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list"); + private static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list"); + + private YangInstanceIdentifier topContainerId = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);; + + @Test + public void testInitialWrite() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final NormalizedNode topContainer = getTopContainer("string1"); + final YangInstanceIdentifier topContainerId = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); + dataTreeModification.write(topContainerId, topContainer); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertTrue(modificationDiff.getModificationsBefore().isEmpty()); + assertAfter(topContainer, topContainerId, modificationDiff); + } + + @Test + public void testUpdateWrite() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final NormalizedNode topContainerBefore = addTopContainer(dataTree); + + final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final NormalizedNode topContainerAfter = getTopContainer("string2"); + dataTreeModification.write(topContainerId, topContainerAfter); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertBefore(topContainerBefore, topContainerId, modificationDiff); + assertAfter(topContainerAfter, topContainerId, modificationDiff); + } + + @Test + public void testUpdateMerge() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final NormalizedNode topContainerBefore = addTopContainer(dataTree); + + final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final NormalizedNode topContainerAfter = getTopContainer("string2"); + dataTreeModification.merge(topContainerId, topContainerAfter); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertBefore(topContainerBefore, topContainerId, modificationDiff); + assertAfter(topContainerAfter, topContainerId, modificationDiff); + } + + @Test + public void testUpdateDelete() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final NormalizedNode topContainerBefore = addTopContainer(dataTree); + + final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + final DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.delete(topContainerId); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertBefore(topContainerBefore, topContainerId, modificationDiff); + assertTrue(modificationDiff.getModificationsAfter().isEmpty()); + } + + @Test + public void testWriteAndUpdateInnerList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + addTopContainer(dataTree); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + ModifiableDataTreeDelegator.ModificationDiff modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertTrue(modificationDiff.getModificationsBefore().isEmpty()); + assertAfter(mapNode, listId, modificationDiff); + + // Commit so that update can be tested next + dataTree.commit(prepare); + + YangInstanceIdentifier listItemId = listId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + MapEntryNode mapEntryNode = + getNestedList("name1", "text-update").getValue().iterator().next(); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.write(listItemId, mapEntryNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + + modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff); + assertAfter(mapEntryNode, listItemId, modificationDiff); + } + + @Test + public void testWriteTopContainerAndInnerList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + + final ContainerNode topContainer = getTopContainer("string1"); + dataTreeModification.write(topContainerId, topContainer); + + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + final DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertTrue(modificationDiff.getModificationsBefore().isEmpty()); + + // TODO HONEYCOMB-94 2 after modifications should appear, for top-container and nested-list entry + assertAfter(Builders.containerBuilder(topContainer) + .withChild(mapNode) + .build(), + topContainerId, modificationDiff); + } + + @Test + public void testWriteDeepList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + addTopContainer(dataTree); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + + YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + + final YangInstanceIdentifier.NodeIdentifierWithPredicates nestedListNodeId = + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"); + listId = YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME), + nestedListNodeId); + final YangInstanceIdentifier deepListId = listId.node(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)); + final YangInstanceIdentifier deepListEntryId = deepListId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME,"name1")); + + final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next(); + // Merge parent list, just to see no modifications on it + dataTreeModification.merge( + listId, + Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId).withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build()); + dataTreeModification.merge( + deepListId, + Builders.mapBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)).build()); + dataTreeModification.merge( + deepListEntryId, + deepListEntry); + + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertTrue(modificationDiff.getModificationsBefore().isEmpty()); + assertAfter(getDeepList("name1"), deepListId, modificationDiff); + } + + @Test + public void testDeleteInnerListItem() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + addTopContainer(dataTree); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + // Commit so that update can be tested next + dataTree.commit(prepare); + + YangInstanceIdentifier listItemId = listId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.delete(listItemId); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = + ModifiableDataTreeDelegator.ModificationDiff + .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + + assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff); + assertTrue(modificationDiff.getModificationsAfter().isEmpty()); + } + + private NormalizedNode addTopContainer(final TipProducingDataTree dataTree) throws DataValidationFailedException { + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final NormalizedNode topContainerBefore = getTopContainer("string1"); + dataTreeModification.write(topContainerId, topContainerBefore); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + return topContainerBefore; + } + + private void assertAfter(final NormalizedNode topContainer, final YangInstanceIdentifier topContainerId, + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) { + assertModification(topContainer, topContainerId, modificationDiff.getModificationsAfter()); + } + + private void assertModification(final NormalizedNode topContainer, + final YangInstanceIdentifier topContainerId, + final Map> modificationMap) { + assertEquals(1, modificationMap.keySet().size()); + assertEquals(topContainerId, modificationMap.keySet().iterator().next()); + assertEquals(topContainer, modificationMap.values().iterator().next()); + } + + private void assertBefore(final NormalizedNode topContainerBefore, + final YangInstanceIdentifier topContainerId, + final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) { + assertModification(topContainerBefore, topContainerId, modificationDiff.getModificationsBefore()); + } + + private TipProducingDataTree getDataTree() throws ReactorException { + final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); + dataTree.setSchemaContext(getSchemaCtx()); + return dataTree; + } + + private ContainerNode getTopContainer(final String stringValue) { + return Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME)) + .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) + .build(); + } + + private MapNode getNestedList(final String listItemName, final String text) { + return Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)) + .withChild( + Builders.mapEntryBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(TEXT_LEAF_QNAME, text)) + .build() + ) + .build(); + } + + private MapNode getDeepList(final String listItemName) { + return Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) + .withChild( + Builders.mapEntryBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) + .build() + ) + .build(); + } + + private SchemaContext getSchemaCtx() throws ReactorException { + final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + buildAction.addSource(new YangStatementSourceImpl(getClass().getResourceAsStream("/test-diff.yang"))); + return buildAction.buildEffective(); + } +} \ No newline at end of file -- cgit 1.2.3-korg