diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-06-14 10:48:26 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-06-15 08:59:04 +0200 |
commit | cd27d3d1e8d0a1d086c60ea841da62142483a3fb (patch) | |
tree | ef692178760061bc75dcd3606885147d63965545 /v3po/data-impl/src | |
parent | 8dbdffd7d25174dd8032fc80141f27a94225d984 (diff) |
HONEYCOMB-92: Process modifications recursively
+ Fix update subtree, child writer lookup
+ Change initializers operation to merge
Change-Id: I6ece7eb3d17d5a0b4a413189ddd383567d7e2270
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/data-impl/src')
6 files changed, 668 insertions, 190 deletions
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java deleted file mode 100644 index 5833459ea..000000000 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataTreeUtils.java +++ /dev/null @@ -1,77 +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 com.google.common.base.Preconditions; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.binding.data.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.DataContainerChild; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Utility class for various operations on DataTree. - */ -final class DataTreeUtils { - - private static final Logger LOG = LoggerFactory.getLogger(DataTreeUtils.class); - - private DataTreeUtils() { - throw new UnsupportedOperationException("Can't instantiate util class"); - } - - /** - * Translates children of supplied YANG ContainerNode into Binding data. - * - * @param parent ContainerNode representing data - * @param serializer service for serialization between Java Binding Data representation and NormalizedNode - * representation. - * @return NormalizedNode representation of parent's node children - */ - static Map<InstanceIdentifier<?>, DataObject> childrenFromNormalized(@Nonnull final DataContainerNode parent, - @Nonnull final BindingNormalizedNodeSerializer serializer) { - - Preconditions.checkNotNull(parent, "parent node should not be null"); - Preconditions.checkNotNull(serializer, "serializer should not be null"); - - final Map<InstanceIdentifier<?>, DataObject> map = new HashMap<>(); - - final Collection<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> children = - parent.getValue(); - - for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> child : children) { - final YangInstanceIdentifier.PathArgument pathArgument = child.getIdentifier(); - final YangInstanceIdentifier identifier = YangInstanceIdentifier.create(pathArgument); - LOG.debug("DataTreeUtils.childrenFromNormalized() child={}, pathArgument={}, identifier={}", child, - pathArgument, identifier); - - final Map.Entry<InstanceIdentifier<?>, DataObject> entry = serializer.fromNormalizedNode(identifier, child); - if (entry != null) { - map.put(entry.getKey(), entry.getValue()); - } - } - - return map; - } -} diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java index 75ffb702a..c8d258b43 100644 --- a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java +++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java @@ -16,11 +16,13 @@ package io.fd.honeycomb.v3po.data.impl; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.util.concurrent.Futures.immediateCheckedFuture; -import static io.fd.honeycomb.v3po.data.impl.DataTreeUtils.childrenFromNormalized; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; +import com.google.common.collect.Sets; import com.google.common.util.concurrent.CheckedFuture; import io.fd.honeycomb.v3po.data.DataModification; import io.fd.honeycomb.v3po.data.ReadableDataManager; @@ -30,6 +32,8 @@ import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext; import io.fd.honeycomb.v3po.translate.write.WriteContext; import io.fd.honeycomb.v3po.translate.write.WriterRegistry; import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; @@ -38,11 +42,12 @@ import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSe 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.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,10 +60,10 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeDelegator.class); private static final ReadableDataManager EMPTY_OPERATIONAL = p -> immediateCheckedFuture(Optional.absent()); - // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ? - private final BindingNormalizedNodeSerializer serializer; private final WriterRegistry writerRegistry; private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; + // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ? + private final BindingNormalizedNodeSerializer serializer; /** * Creates configuration data tree instance. @@ -107,14 +112,16 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager throws TranslationException { final DataTreeCandidateNode rootNode = candidate.getRootNode(); final YangInstanceIdentifier rootPath = candidate.getRootPath(); - final Optional<NormalizedNode<?, ?>> normalizedDataBefore = rootNode.getDataBefore(); - final Optional<NormalizedNode<?, ?>> normalizedDataAfter = rootNode.getDataAfter(); - LOG.debug("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", - rootPath, rootNode, normalizedDataBefore, normalizedDataAfter); + LOG.trace("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}", + rootPath, rootNode, rootNode.getDataBefore(), rootNode.getDataAfter()); - final Map<InstanceIdentifier<?>, DataObject> nodesBefore = toBindingAware(normalizedDataBefore); + final ModificationDiff modificationDiff = + ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, rootNode); + LOG.debug("ConfigDataTree.modify() diff: {}", modificationDiff); + + final Map<InstanceIdentifier<?>, DataObject> nodesBefore = toBindingAware(modificationDiff.getModificationsBefore()); LOG.debug("ConfigDataTree.modify() extracted nodesBefore={}", nodesBefore.keySet()); - final Map<InstanceIdentifier<?>, DataObject> nodesAfter = toBindingAware(normalizedDataAfter); + final Map<InstanceIdentifier<?>, DataObject> nodesAfter = toBindingAware(modificationDiff.getModificationsAfter()); LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet()); try (final WriteContext ctx = getTransactionWriteContext()) { @@ -160,12 +167,175 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext); } - private Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Optional<NormalizedNode<?, ?>> parentOptional) { - if (parentOptional.isPresent()) { - final DataContainerNode parent = (DataContainerNode) parentOptional.get(); - return childrenFromNormalized(parent, serializer); + private Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> biNodes) { + return ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer); + } + } + + @VisibleForTesting + static Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> biNodes, + final BindingNormalizedNodeSerializer serializer) { + final HashMap<InstanceIdentifier<?>, DataObject> transformed = new HashMap<>(biNodes.size()); + for (Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> biEntry : biNodes.entrySet()) { + final Map.Entry<InstanceIdentifier<?>, DataObject> baEntry = serializer.fromNormalizedNode(biEntry.getKey(), biEntry.getValue()); + if(baEntry != null) { + transformed.put(baEntry.getKey(), baEntry.getValue()); + } + } + return transformed; + } + + @VisibleForTesting + static final class ModificationDiff { + + private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap(), Collections.emptyMap()); + private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE); + + private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsBefore; + private final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsAfter; + + private ModificationDiff(@Nonnull final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsBefore, + @Nonnull final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationsAfter) { + this.modificationsBefore = modificationsBefore; + this.modificationsAfter = modificationsAfter; + } + + Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getModificationsBefore() { + return modificationsBefore; + } + + Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getModificationsAfter() { + return modificationsAfter; + } + + private ModificationDiff merge(final ModificationDiff other) { + if (this == EMPTY_DIFF) { + return other; } - return Collections.emptyMap(); + + if (other == EMPTY_DIFF) { + return this; + } + + return new ModificationDiff(join(modificationsBefore, other.modificationsBefore), + join(modificationsAfter, other.modificationsAfter)); + } + + private static Map<YangInstanceIdentifier, NormalizedNode<?, ?>> join( + final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> mapOne, + final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> mapTwo) { + // Check unique modifications + // TODO Probably not necessary to check + final Sets.SetView<YangInstanceIdentifier> duplicates = Sets.intersection(mapOne.keySet(), mapTwo.keySet()); + checkArgument(duplicates.size() == 0, "Duplicates detected: %s. In maps: %s and %s", duplicates, mapOne, mapTwo); + final HashMap<YangInstanceIdentifier, NormalizedNode<?, ?>> joined = new HashMap<>(); + joined.putAll(mapOne); + joined.putAll(mapTwo); + return joined; + } + + private static ModificationDiff createFromBefore(YangInstanceIdentifier idBefore, DataTreeCandidateNode candidate) { + return new ModificationDiff( + Collections.singletonMap(idBefore, candidate.getDataBefore().get()), + Collections.emptyMap()); + } + + private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) { + return new ModificationDiff( + Collections.singletonMap(id, candidate.getDataBefore().get()), + Collections.singletonMap(id, candidate.getDataAfter().get())); + } + + private static ModificationDiff createFromAfter(YangInstanceIdentifier idAfter, DataTreeCandidateNode candidate) { + return new ModificationDiff( + Collections.emptyMap(), + Collections.singletonMap(idAfter, candidate.getDataAfter().get())); + } + + /** + * Produce a diff from a candidate node recursively + */ + @Nonnull + static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid, + @Nonnull final DataTreeCandidateNode currentCandidate) { + switch (currentCandidate.getModificationType()) { + case APPEARED: + case DISAPPEARED: + case UNMODIFIED: { + // (dis)appeared nodes are not important, no real data to process + return ModificationDiff.EMPTY_DIFF; + } + case WRITE: { + return currentCandidate.getDataBefore().isPresent() ? + ModificationDiff.create(yangIid, currentCandidate) : + ModificationDiff.createFromAfter(yangIid, currentCandidate); + // TODO HONEYCOMB-94 process children recursively to get modifications for child nodes + } + case DELETE: + return ModificationDiff.createFromBefore(yangIid, currentCandidate); + case SUBTREE_MODIFIED: { + // Modifications here are presented also for leaves. However that kind of granularity is not required + // So if there's a modified leaf, mark current complex node also as modification + java.util.Optional<Boolean> leavesModified = currentCandidate.getChildNodes().stream() + .filter(ModificationDiff::isLeaf) + // For some reason, we get modifications on unmodified list keys TODO debug and report ODL bug + // and that messes up our modifications collection here, so we need to skip + .filter(ModificationDiff::isModification) + .map(child -> LEAF_MODIFICATIONS.contains(child.getModificationType())) + .reduce((aBoolean, aBoolean2) -> aBoolean || aBoolean2); + + // + if(leavesModified.isPresent() && leavesModified.get()) { + return ModificationDiff.create(yangIid, currentCandidate); + // TODO HONEYCOMB-94 process children recursively to get modifications for child nodes even if current + // was modified + } else { + // SUBTREE MODIFIED, no modification on current, but process children recursively + return currentCandidate.getChildNodes().stream() + // not interested in modifications to leaves + .filter(child -> !isLeaf(child)) + .map(candidate -> recursivelyFromCandidate(yangIid.node(candidate.getIdentifier()), candidate)) + .reduce(ModificationDiff::merge) + .orElse(EMPTY_DIFF); + } + } + default: + throw new IllegalStateException("Unknown modification type: " + + currentCandidate.getModificationType() + ". Unsupported"); + } + } + + /** + * Check whether candidate.before and candidate.after is different. If not + * return false. + */ + private static boolean isModification(final DataTreeCandidateNode a) { + if(a.getDataBefore().isPresent()) { + if(a.getDataAfter().isPresent()) { + return !a.getDataAfter().get().equals(a.getDataBefore().get()); + } else { + return true; + } + } + + return true; + } + + /** + * Check whether candidate node is for a leaf type node + */ + private static boolean isLeaf(final DataTreeCandidateNode a) { + return a.getDataAfter().isPresent() + ? (a.getDataAfter().get() instanceof LeafNode<?>) + : (a.getDataBefore().get() instanceof LeafNode<?>); + } + + @Override + public String toString() { + return "ModificationDiff{" + + "modificationsBefore=" + modificationsBefore + + ", modificationsAfter=" + modificationsAfter + + '}'; } } } 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<DataContainerChild> 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<InstanceIdentifier<?>, 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.<NormalizedNode<?, ?>>fromNullable(nodeBefore)); + when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore)); // data after: final ContainerNode nodeAfter = mockContainerNode(dataAfter); - when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>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<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) { - return eq( - Collections.<InstanceIdentifier<?>, DataObject>singletonMap(InstanceIdentifier.create(type), - dataBefore)); + return eq(Collections.singletonMap(InstanceIdentifier.create(type),dataBefore)); } private DataObject mockDataObject(final String name, final Class<? extends DataObject> 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.<NormalizedNode<?, ?>>fromNullable(nodeBefore)); + when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore)); // data after: final ContainerNode nodeAfter = mockContainerNode(dataAfter); - when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>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.<NormalizedNode<?, ?>>fromNullable(nodeBefore)); + when(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore)); // data after: final ContainerNode nodeAfter = mockContainerNode(dataAfter); - when(rootNode.getDataAfter()).thenReturn(Optional.<NormalizedNode<?, ?>>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<YangInstanceIdentifier, NormalizedNode<?, ?>> 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<InstanceIdentifier<?>, 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<DataContainerChild> list = new ArrayList<>(numberOfChildren); - doReturn(list).when(node).getValue(); + final Map.Entry entry = mock(Map.Entry.class); + final Class<? extends DataObject> implementedInterface = + (Class<? extends DataObject>) 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<? extends DataObject> implementedInterface = - (Class<? extends DataObject>) 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<YangInstanceIdentifier, NormalizedNode<?, ?>> 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 diff --git a/v3po/data-impl/src/test/resources/test-diff.yang b/v3po/data-impl/src/test/resources/test-diff.yang new file mode 100644 index 000000000..7e8721f00 --- /dev/null +++ b/v3po/data-impl/src/test/resources/test-diff.yang @@ -0,0 +1,37 @@ +module test-diff { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:test:diff"; + prefix "td"; + + revision "2015-01-05" { + description "Initial revision"; + } + + container top-container { + leaf string { + type string; + } + + list nested-list { + key "name"; + + leaf name { + type string; + } + + leaf text { + type string; + } + + list deep-list { + key "name"; + + leaf name { + type string; + } + + } + } + } + +} |