summaryrefslogtreecommitdiffstats
path: root/v3po/data-impl
diff options
context:
space:
mode:
authorMaros Marsalek <mmarsale@cisco.com>2016-06-29 09:14:51 +0200
committerMaros Marsalek <mmarsale@cisco.com>2016-07-13 11:24:26 +0200
commit9f6b16d6e8ade6dfa40e9bbf4196d55adf8f2fec (patch)
tree9cd3d971e42b9351fbba36c788631e7a68a1027d /v3po/data-impl
parent348d54eb9a762f1bde68ef8becb5d9e5a1ca7006 (diff)
HONEYCOMB-94 Reimplement writer registry with better ordering options
Now the registry is flat and allows for full control of writer execution order Change-Id: I864e1d676588ffe59b596145e0829e81b1a1ed2f Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'v3po/data-impl')
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java228
-rw-r--r--v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java278
-rw-r--r--v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java4
-rw-r--r--v3po/data-impl/src/main/yang/data-impl.yang4
-rw-r--r--v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java367
-rw-r--r--v3po/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java215
-rw-r--r--v3po/data-impl/src/test/resources/test-diff.yang17
7 files changed, 631 insertions, 482 deletions
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 91de1885e..77aa12aba 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,38 +16,36 @@
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 com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
-import com.google.common.collect.Sets;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import io.fd.honeycomb.v3po.data.DataModification;
import io.fd.honeycomb.v3po.data.ReadableDataManager;
import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
import io.fd.honeycomb.v3po.translate.util.write.TransactionMappingContext;
import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
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 javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
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.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;
@@ -110,6 +108,7 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
@Override
protected void processCandidate(final DataTreeCandidate candidate)
throws TranslationException {
+
final DataTreeCandidateNode rootNode = candidate.getRootNode();
final YangInstanceIdentifier rootPath = candidate.getRootPath();
LOG.trace("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}",
@@ -119,13 +118,12 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
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(modificationDiff.getModificationsAfter());
- LOG.debug("ConfigDataTree.modify() extracted nodesAfter={}", nodesAfter.keySet());
+ // Distinguish between updates (create + update) and deletes
+ final WriterRegistry.DataObjectUpdates baUpdates = toBindingAware(modificationDiff.getUpdates());
+ LOG.debug("ConfigDataTree.modify() extracted updates={}", baUpdates);
try (final WriteContext ctx = getTransactionWriteContext()) {
- writerRegistry.update(nodesBefore, nodesAfter, ctx);
+ writerRegistry.update(baUpdates, ctx);
final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult =
((TransactionMappingContext) ctx.getMappingContext()).submit();
@@ -152,7 +150,7 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
LOG.error(msg, e);
throw new TranslationException(msg, e);
} catch (TranslationException e) {
- LOG.error("Error while processing data change (before={}, after={})", nodesBefore, nodesAfter, e);
+ LOG.error("Error while processing data change (updates={})", baUpdates, e);
throw e;
}
}
@@ -167,177 +165,71 @@ public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager
return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext);
}
- private Map<InstanceIdentifier<?>, DataObject> toBindingAware(final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> biNodes) {
+ private WriterRegistry.DataObjectUpdates toBindingAware(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> 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());
+ static WriterRegistry.DataObjectUpdates toBindingAware(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes,
+ final BindingNormalizedNodeSerializer serializer) {
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> dataObjectUpdates = HashMultimap.create();
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> dataObjectDeletes =
+ HashMultimap.create();
+
+ for (Map.Entry<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biEntry : biNodes.entrySet()) {
+ final InstanceIdentifier<?> unkeyedIid =
+ RWUtils.makeIidWildcarded(serializer.fromYangInstanceIdentifier(biEntry.getKey()));
+
+ ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate = biEntry.getValue();
+ final DataObjectUpdate dataObjectUpdate = toDataObjectUpdate(normalizedNodeUpdate, serializer);
+ if (dataObjectUpdate != null) {
+ if (dataObjectUpdate instanceof DataObjectUpdate.DataObjectDelete) {
+ dataObjectDeletes.put(unkeyedIid, ((DataObjectUpdate.DataObjectDelete) dataObjectUpdate));
+ } else {
+ dataObjectUpdates.put(unkeyedIid, dataObjectUpdate);
+ }
}
}
- return transformed;
+ return new WriterRegistry.DataObjectUpdates(dataObjectUpdates, dataObjectDeletes);
}
- @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;
+ @Nullable
+ private static DataObjectUpdate toDataObjectUpdate(
+ final ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate,
+ final BindingNormalizedNodeSerializer serializer) {
- 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;
- }
+ InstanceIdentifier<?> baId = serializer.fromYangInstanceIdentifier(normalizedNodeUpdate.getId());
+ checkNotNull(baId, "Unable to transform instance identifier: %s into BA", normalizedNodeUpdate.getId());
- Map<YangInstanceIdentifier, NormalizedNode<?, ?>> getModificationsAfter() {
- return modificationsAfter;
- }
-
- private ModificationDiff merge(final ModificationDiff other) {
- if (this == EMPTY_DIFF) {
- return other;
- }
+ DataObject dataObjectBefore = getDataObject(serializer,
+ normalizedNodeUpdate.getDataBefore(), normalizedNodeUpdate.getId());
+ DataObject dataObjectAfter =
+ getDataObject(serializer, normalizedNodeUpdate.getDataAfter(), normalizedNodeUpdate.getId());
- 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((boolOne, boolTwo) -> boolOne || boolTwo);
-
- 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");
- }
- }
+ return dataObjectBefore == null && dataObjectAfter == null
+ ? null
+ : DataObjectUpdate.create(baId, dataObjectBefore, dataObjectAfter);
+ }
- /**
- * Check whether candidate.before and candidate.after is different. If not
- * return false.
- */
- private static boolean isModification(final DataTreeCandidateNode candidateNode) {
- if (candidateNode.getDataBefore().isPresent()) {
- if (candidateNode.getDataAfter().isPresent()) {
- return !candidateNode.getDataAfter().get().equals(candidateNode.getDataBefore().get());
- } else {
- return true;
- }
+ @Nullable
+ private static DataObject getDataObject(@Nonnull final BindingNormalizedNodeSerializer serializer,
+ @Nullable final NormalizedNode<?, ?> data,
+ @Nonnull final YangInstanceIdentifier id) {
+ DataObject dataObject = null;
+ if (data != null) {
+ final Map.Entry<InstanceIdentifier<?>, DataObject> dataObjectEntry =
+ serializer.fromNormalizedNode(id, data);
+ if (dataObjectEntry != null) {
+ dataObject = dataObjectEntry.getValue();
}
-
- // considering not a modification if data after is also null
- return candidateNode.getDataAfter().isPresent();
- }
-
- /**
- * Check whether candidate node is for a leaf type node.
- */
- private static boolean isLeaf(final DataTreeCandidateNode candidateNode) {
- // orNull intentional, some candidate nodes have both data after and data before null
- return candidateNode.getDataAfter().orNull() instanceof LeafNode<?>
- || candidateNode.getDataBefore().orNull() instanceof LeafNode<?>;
- }
-
- @Override
- public String toString() {
- return "ModificationDiff{"
- + "modificationsBefore=" + modificationsBefore
- + ", modificationsAfter=" + modificationsAfter
- + '}';
}
+ return dataObject;
}
+
}
diff --git a/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java
new file mode 100644
index 000000000..abc0062de
--- /dev/null
+++ b/v3po/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java
@@ -0,0 +1,278 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+
+/**
+ * Recursively collects and provides all unique and non-null modifications (modified normalized nodes).
+ */
+final class ModificationDiff {
+
+ private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap());
+ private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE);
+
+ private final Map<YangInstanceIdentifier, NormalizedNodeUpdate> updates;
+
+ private ModificationDiff(@Nonnull Map<YangInstanceIdentifier, NormalizedNodeUpdate> updates) {
+ this.updates = updates;
+ }
+
+ /**
+ * Get processed modifications.
+ *
+ * @return mapped modifications, where key is keyed {@link YangInstanceIdentifier}.
+ */
+ Map<YangInstanceIdentifier, NormalizedNodeUpdate> getUpdates() {
+ return updates;
+ }
+
+ private ModificationDiff merge(final ModificationDiff other) {
+ if (this == EMPTY_DIFF) {
+ return other;
+ }
+
+ if (other == EMPTY_DIFF) {
+ return this;
+ }
+
+ return new ModificationDiff(join(updates, other.updates));
+ }
+
+ private static Map<YangInstanceIdentifier, NormalizedNodeUpdate> join(Map<YangInstanceIdentifier, NormalizedNodeUpdate> first,
+ Map<YangInstanceIdentifier, NormalizedNodeUpdate> second) {
+ final Map<YangInstanceIdentifier, NormalizedNodeUpdate> merged = new HashMap<>();
+ merged.putAll(first);
+ merged.putAll(second);
+ return merged;
+ }
+
+ private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) {
+ return new ModificationDiff(ImmutableMap.of(id, NormalizedNodeUpdate.create(id, candidate)));
+ }
+
+ /**
+ * Produce an aggregated diff from a candidate node recursively. MixinNodes are ignored as modifications and so
+ * are complex nodes which direct leaves were not modified.
+ */
+ @Nonnull
+ static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid,
+ @Nonnull final DataTreeCandidateNode currentCandidate) {
+ // recursively process child nodes for exact modifications
+ return recursivelyChildrenFromCandidate(yangIid, currentCandidate)
+ // also add modification on current level, if elligible
+ .merge(isModification(currentCandidate)
+ ? ModificationDiff.create(yangIid, currentCandidate)
+ : EMPTY_DIFF);
+ }
+
+ /**
+ * Check whether current node was modified. {@link MixinNode}s are ignored
+ * and only nodes which direct leaves(or choices) are modified are considered a modification.
+ */
+ private static Boolean isModification(@Nonnull final DataTreeCandidateNode currentCandidate) {
+ // Mixin nodes are not considered modifications
+ if (isMixin(currentCandidate) && !isAugment(currentCandidate)) {
+ return false;
+ } else {
+ return isCurrentModified(currentCandidate);
+ }
+ }
+
+ private static Boolean isCurrentModified(final @Nonnull DataTreeCandidateNode currentCandidate) {
+ // Check if there are any modified leaves and if so, consider current node as modified
+ final Boolean directLeavesModified = 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::isBeforeAndAfterDifferent)
+ .filter(child -> LEAF_MODIFICATIONS.contains(child.getModificationType()))
+ .findFirst()
+ .isPresent();
+
+ return directLeavesModified
+ // Also check choices (choices do not exist in BA world and if anything within a choice was modified,
+ // consider its parent as being modified)
+ || currentCandidate.getChildNodes().stream()
+ .filter(ModificationDiff::isChoice)
+ // Recursively check each choice if there was any change to it
+ .filter(ModificationDiff::isCurrentModified)
+ .findFirst()
+ .isPresent();
+ }
+
+ /**
+ * Process all non-leaf child nodes recursively, creating aggregated {@link ModificationDiff}.
+ */
+ private static ModificationDiff recursivelyChildrenFromCandidate(final @Nonnull YangInstanceIdentifier yangIid,
+ final @Nonnull DataTreeCandidateNode currentCandidate) {
+ // recursively process child nodes for specific modifications
+ 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);
+ }
+
+ /**
+ * Check whether candidate.before and candidate.after is different. If not return false.
+ */
+ private static boolean isBeforeAndAfterDifferent(@Nonnull final DataTreeCandidateNode candidateNode) {
+ if (candidateNode.getDataBefore().isPresent()) {
+ return !candidateNode.getDataBefore().get().equals(candidateNode.getDataAfter().orNull());
+ }
+
+ // considering not a modification if data after is also null
+ return candidateNode.getDataAfter().isPresent();
+ }
+
+ /**
+ * Check whether candidate node is for a leaf type node.
+ */
+ private static boolean isLeaf(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof LeafNode<?>
+ || candidateNode.getDataBefore().orNull() instanceof LeafNode<?>;
+ }
+
+ /**
+ * Check whether candidate node is for a Mixin type node.
+ */
+ private static boolean isMixin(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof MixinNode
+ || candidateNode.getDataBefore().orNull() instanceof MixinNode;
+ }
+
+ /**
+ * Check whether candidate node is for an Augmentation type node.
+ */
+ private static boolean isAugment(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof AugmentationNode
+ || candidateNode.getDataBefore().orNull() instanceof AugmentationNode;
+ }
+
+ /**
+ * Check whether candidate node is for a Choice type node.
+ */
+ private static boolean isChoice(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof ChoiceNode
+ || candidateNode.getDataBefore().orNull() instanceof ChoiceNode;
+ }
+
+ @Override
+ public String toString() {
+ return "ModificationDiff{updates=" + updates + '}';
+ }
+
+ /**
+ * Update to a normalized node identifiable by its {@link YangInstanceIdentifier}.
+ */
+ static final class NormalizedNodeUpdate {
+
+ @Nonnull
+ private final YangInstanceIdentifier id;
+ @Nullable
+ private final NormalizedNode<?, ?> dataBefore;
+ @Nullable
+ private final NormalizedNode<?, ?> dataAfter;
+
+ private NormalizedNodeUpdate(@Nonnull final YangInstanceIdentifier id,
+ @Nullable final NormalizedNode<?, ?> dataBefore,
+ @Nullable final NormalizedNode<?, ?> dataAfter) {
+ this.id = checkNotNull(id);
+ this.dataAfter = dataAfter;
+ this.dataBefore = dataBefore;
+ }
+
+ @Nullable
+ public NormalizedNode<?, ?> getDataBefore() {
+ return dataBefore;
+ }
+
+ @Nullable
+ public NormalizedNode<?, ?> getDataAfter() {
+ return dataAfter;
+ }
+
+ @Nonnull
+ public YangInstanceIdentifier getId() {
+ return id;
+ }
+
+ static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id,
+ @Nonnull final DataTreeCandidateNode candidate) {
+ return create(id, candidate.getDataBefore().orNull(), candidate.getDataAfter().orNull());
+ }
+
+ static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id,
+ @Nullable final NormalizedNode<?, ?> dataBefore,
+ @Nullable final NormalizedNode<?, ?> dataAfter) {
+ checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null");
+ return new NormalizedNodeUpdate(id, dataBefore, dataAfter);
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ final NormalizedNodeUpdate that = (NormalizedNodeUpdate) other;
+
+ return id.equals(that.id);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "NormalizedNodeUpdate{" + "id=" + id
+ + ", dataBefore=" + dataBefore
+ + ", dataAfter=" + dataAfter
+ + '}';
+ }
+ }
+
+}
diff --git a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
index 3e871e09a..eabcdcbc8 100644
--- a/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
+++ b/v3po/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
@@ -38,8 +38,8 @@ public class ConfigDataTreeModule extends
public java.lang.AutoCloseable createInstance() {
LOG.debug("ConfigDataTreeModule.createInstance()");
return new CloseableConfigDataTree(
- new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(), getWriterRegistryDependency(),
- getContextBindingBrokerDependency()));
+ new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(),
+ getWriterRegistryBuilderDependency().build(), getContextBindingBrokerDependency()));
}
private static final class CloseableConfigDataTree implements ModifiableDataManager, AutoCloseable {
diff --git a/v3po/data-impl/src/main/yang/data-impl.yang b/v3po/data-impl/src/main/yang/data-impl.yang
index 0ea76cf0e..922846371 100644
--- a/v3po/data-impl/src/main/yang/data-impl.yang
+++ b/v3po/data-impl/src/main/yang/data-impl.yang
@@ -108,11 +108,11 @@ module data-impl {
}
}
- container writer-registry {
+ container writer-registry-builder {
uses config:service-ref {
refine type {
mandatory true;
- config:required-identity tapi:honeycomb-writer-registry;
+ config:required-identity tapi:honeycomb-writer-registry-builder;
}
}
}
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 086636de6..c2653661a 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
@@ -16,27 +16,34 @@
package io.fd.honeycomb.v3po.data.impl;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import io.fd.honeycomb.v3po.data.DataModification;
import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
import io.fd.honeycomb.v3po.translate.write.WriteContext;
import io.fd.honeycomb.v3po.translate.write.WriterRegistry;
+import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -46,18 +53,14 @@ import org.mockito.Mock;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
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.DataContainerChild;
+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.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 {
@@ -65,112 +68,66 @@ public class ModifiableDataTreeDelegatorTest {
private WriterRegistry writer;
@Mock
private BindingNormalizedNodeSerializer serializer;
- @Mock
private DataTree dataTree;
@Mock
private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification;
@Mock
private DataBroker contextBroker;
+ @Mock
+ private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx;
private ModifiableDataTreeManager configDataTree;
+ static final InstanceIdentifier<?> DEFAULT_ID = InstanceIdentifier.create(DataObject.class);
+ static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class);
+
@Before
- public void setUp() {
+ public void setUp() throws Exception {
initMocks(this);
- configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker);
- }
-
- @Test
- public void testRead() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
+ dataTree = ModificationDiffTest.getDataTree();
+ when(contextBroker.newReadWriteTransaction()).thenReturn(tx);
+ when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
- final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
- final Optional node = mock(Optional.class);
- doReturn(node).when(modification).readNode(path);
+ when(serializer.fromYangInstanceIdentifier(any(YangInstanceIdentifier.class))).thenReturn(((InstanceIdentifier) DEFAULT_ID));
+ final Map.Entry<InstanceIdentifier<?>, DataObject> parsed = new AbstractMap.SimpleEntry<>(DEFAULT_ID, DEFAULT_DATA_OBJECT);
+ when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class))).thenReturn(parsed);
- final DataModification dataTreeSnapshot = configDataTree.newModification();
- final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
- dataTreeSnapshot.read(path);
-
- verify(dataTree, times(2)).takeSnapshot();
- verify(modification).readNode(path);
-
- assertTrue(future.isDone());
- assertEquals(node, future.get());
+ configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker);
}
@Test
- public void testNewModification() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
-
- configDataTree.newModification();
- // Snapshot captured twice, so that original data could be provided to translation layer without any possible
- // modification
- verify(dataTree, times(2)).takeSnapshot();
- verify(snapshot, times(2)).newModification();
+ public void testRead() throws Exception {
+ final ContainerNode topContainer = ModificationDiffTest.getTopContainer("topContainer");
+ ModificationDiffTest.addNodeToTree(dataTree, topContainer, ModificationDiffTest.TOP_CONTAINER_ID);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
+ configDataTree.read(ModificationDiffTest.TOP_CONTAINER_ID);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read2 =
+ configDataTree.newModification().read(ModificationDiffTest.TOP_CONTAINER_ID);
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = read.get();
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional2 = read2.get();
+
+ assertEquals(normalizedNodeOptional, normalizedNodeOptional2);
+ assertTrue(normalizedNodeOptional.isPresent());
+ assertEquals(topContainer, normalizedNodeOptional.get());
+ assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional);
}
@Test
public void testCommitSuccessful() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
- final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
- doReturn(prepare).when(dataTree).prepare(modification);
-
- final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock(
- org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class);
- doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction();
- doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit();
-
- final DataObject dataBefore = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
- // 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(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore));
- // data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter);
- when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter));
-
- // Run the test
- doReturn(rootNode).when(prepare).getRootNode();
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
+
final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
dataModification.commit();
- // Verify all changes were processed:
- verify(writer).update(
- mapOf(dataBefore, Ethernet.class),
- mapOf(dataAfter, Ethernet.class),
- any(WriteContext.class));
-
- // Verify modification was validated
- verify(dataTree).validate(modification);
- // Verify context transaction was finished
- verify(ctxTransaction).submit();
- }
-
- private Map<InstanceIdentifier<?>, DataObject> mapOf(final DataObject dataBefore, final Class<Ethernet> type) {
- return eq(Collections.singletonMap(InstanceIdentifier.create(type),dataBefore));
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> map = HashMultimap.create();
+ map.put(DEFAULT_ID, DataObjectUpdate.create(DEFAULT_ID, DEFAULT_DATA_OBJECT, DEFAULT_DATA_OBJECT));
+ verify(writer).update(eq(new WriterRegistry.DataObjectUpdates(map, ImmutableMultimap.of())), any(WriteContext.class));
+ assertEquals(nestedList, dataTree.takeSnapshot().readNode(ModificationDiffTest.NESTED_LIST_ID).get());
}
- private DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
+ private static DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
final DataObject dataBefore = mock(classToMock, name);
doReturn(classToMock).when(dataBefore).getImplementedInterface();
return dataBefore;
@@ -178,171 +135,135 @@ public class ModifiableDataTreeDelegatorTest {
@Test
public void testCommitUndoSuccessful() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
- final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
- doReturn(prepare).when(dataTree).prepare(modification);
-
- // Prepare data changes:
- final DataObject dataBefore = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
- final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
- io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
// Fail on update:
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
final TranslationException failedOnUpdateException = new TranslationException("update failed");
- doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
- failedOnUpdateException)).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
-
- // 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(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore));
- // data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter);
- when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter));
-
- // Run the test
+ doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
+ .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+
try {
- doReturn(rootNode).when(prepare).getRootNode();
+ // Run the test
final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
dataModification.commit();
- } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException e) {
- verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+ fail("WriterRegistry.BulkUpdateException was expected");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+ assertThat(e.getFailedIds(), hasItem(DEFAULT_ID));
verify(reverter).revert();
assertEquals(failedOnUpdateException, e.getCause());
- return;
}
-
- fail("WriterRegistry.BulkUpdateException was expected");
}
@Test
public void testCommitUndoFailed() throws Exception {
- final org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot
- snapshot = mock(org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot.class);
- when(dataTree.takeSnapshot()).thenReturn(snapshot);
- when(snapshot.newModification()).thenReturn(modification);
- final DataTreeCandidate prepare = mock(DataTreeCandidate.class);
- doReturn(prepare).when(dataTree).prepare(modification);
-
- // Prepare data changes:
- final DataObject dataBefore = mockDataObject("before", Ethernet.class);
- final DataObject dataAfter = mockDataObject("after", Ethernet.class);
-
- final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter reverter = mock(
- io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.class);
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
// Fail on update:
- doThrow(new io.fd.honeycomb.v3po.translate.write.WriterRegistry.BulkUpdateException(InstanceIdentifier.create(Ethernet.class), reverter,
- new TranslationException("update failed"))).when(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
+ final TranslationException failedOnUpdateException = new TranslationException("update failed");
+ doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
+ .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
// Fail on revert:
- final TranslationException failedOnRevertException = new TranslationException("update failed");
- final io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException revertFailedException =
- new io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException(Collections.<InstanceIdentifier<?>>emptyList(),
- failedOnRevertException);
- doThrow(revertFailedException).when(reverter).revert();
-
- // 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(childNode.getDataBefore()).thenReturn(Optional.fromNullable(nodeBefore));
- // data after:
- final ContainerNode nodeAfter = mockContainerNode(dataAfter);
- when(childNode.getDataAfter()).thenReturn(Optional.fromNullable(nodeAfter));
-
- // Run the test
+ final TranslationException failedOnRevertException = new TranslationException("revert failed");
+ doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException))
+ .when(reverter).revert();
+
try {
- doReturn(rootNode).when(prepare).getRootNode();
+ // Run the test
final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
dataModification.commit();
- } catch (io.fd.honeycomb.v3po.translate.write.WriterRegistry.Reverter.RevertFailedException e) {
- verify(writer).update(anyMap(), anyMap(), any(WriteContext.class));
+ fail("WriterRegistry.Reverter.RevertFailedException was expected");
+ } catch (WriterRegistry.Reverter.RevertFailedException e) {
+ verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
verify(reverter).revert();
assertEquals(failedOnRevertException, e.getCause());
- return;
}
-
- fail("RevertFailedException was expected");
}
+ private abstract static class DataObject1 implements DataObject {}
+ private abstract static class DataObject2 implements DataObject {}
+ private abstract static class DataObject3 implements DataObject {}
+
@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));
+ public void testToBindingAware() throws Exception {
+ when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes = new HashMap<>();
+ // delete
+ final QName nn1 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid1 = mockYid(nn1);
+ final InstanceIdentifier iid1 = mockIid(yid1, DataObject1.class);
+ final NormalizedNode nn1B = mockNormalizedNode(nn1);
+ final DataObject1 do1B = mockDataObject(yid1, iid1, nn1B, DataObject1.class);
+ biNodes.put(yid1, ModificationDiff.NormalizedNodeUpdate.create(yid1, nn1B, null));
+
+ // create
+ final QName nn2 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid2 = mockYid(nn2);
+ final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);;
+ final NormalizedNode nn2A = mockNormalizedNode(nn2);
+ final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class);
+ biNodes.put(yid2, ModificationDiff.NormalizedNodeUpdate.create(yid2, null, nn2A));
+
+ // update
+ final QName nn3 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid3 = mockYid(nn3);
+ final InstanceIdentifier iid3 = mockIid(yid3, DataObject3.class);
+ final NormalizedNode nn3B = mockNormalizedNode(nn3);
+ final DataObject3 do3B = mockDataObject(yid3, iid3, nn3B, DataObject3.class);
+ final NormalizedNode nn3A = mockNormalizedNode(nn3);
+ final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class);;
+ biNodes.put(yid3, ModificationDiff.NormalizedNodeUpdate.create(yid3, nn3B, nn3A));
+
+ final WriterRegistry.DataObjectUpdates dataObjectUpdates =
+ ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer);
+
+ assertThat(dataObjectUpdates.getDeletes().size(), is(1));
+ assertThat(dataObjectUpdates.getDeletes().keySet(), hasItem(((InstanceIdentifier<?>) iid1)));
+ assertThat(dataObjectUpdates.getDeletes().values(), hasItem(
+ ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid1, do1B, null))));
+
+ assertThat(dataObjectUpdates.getUpdates().size(), is(2));
+ assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier<?>) iid2, (InstanceIdentifier<?>) iid3));
+ assertThat(dataObjectUpdates.getUpdates().values(), hasItems(
+ DataObjectUpdate.create(iid2, null, do2A),
+ DataObjectUpdate.create(iid3, do3B, do3A)));
+
+ assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3));
}
- private DataTreeCandidateNode mockRootNode() {
- final DataTreeCandidate candidate = mock(DataTreeCandidate.class);
- when(dataTree.prepare(modification)).thenReturn(candidate);
-
- final DataTreeCandidateNode rootNode = mock(DataTreeCandidateNode.class);
- when(candidate.getRootNode()).thenReturn(rootNode);
-
- return rootNode;
+ private <D extends DataObject> D mockDataObject(final YangInstanceIdentifier yid1,
+ final InstanceIdentifier iid1,
+ final NormalizedNode nn1B,
+ final Class<D> type) {
+ final D do1B = mock(type);
+ when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B));
+ return do1B;
}
- 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 Map.Entry entry = mock(Map.Entry.class);
- final Class<? extends DataObject> implementedInterface =
- (Class<? extends DataObject>) modification.getImplementedInterface();
- final InstanceIdentifier<?> id = InstanceIdentifier.create(implementedInterface);
+ private NormalizedNode mockNormalizedNode(final QName nn1) {
+ final NormalizedNode nn1B = mock(NormalizedNode.class);
+ when(nn1B.getNodeType()).thenReturn(nn1);
+ return nn1B;
+ }
- doReturn(id).when(entry).getKey();
- doReturn(modification).when(entry).getValue();
- doReturn(entry).when(serializer).fromNormalizedNode(any(YangInstanceIdentifier.class), eq(node));
+ private InstanceIdentifier mockIid(final YangInstanceIdentifier yid1,
+ final Class<? extends DataObject> type) {
+ final InstanceIdentifier iid1 = InstanceIdentifier.create(type);
+ when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1);
+ return iid1;
+ }
- return node;
+ private YangInstanceIdentifier mockYid(final QName nn1) {
+ final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class);
+ when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1));
+ return yid1;
}
}
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
index 3475973d6..bc7582e93 100644
--- 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
@@ -1,7 +1,8 @@
package io.fd.honeycomb.v3po.data.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
import java.util.Map;
import org.junit.Test;
@@ -11,6 +12,8 @@ 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.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
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;
@@ -28,15 +31,23 @@ import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceIm
public class ModificationDiffTest {
- private static final QName TOP_CONTAINER_QNAME =
+ 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");
+ static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string");
+ static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name");
+ static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text");
+ static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list");
+ static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list");
+
+ static final QName WITH_CHOICE_CONTAINER_QNAME =
+ QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "with-choice");
+ static final QName CHOICE_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "choice");
+ static final QName IN_CASE1_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case1");
+ static final QName IN_CASE2_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case2");
+
+ static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
+ static final YangInstanceIdentifier NESTED_LIST_ID = TOP_CONTAINER_ID.node(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
- private static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
@Test
public void testInitialWrite() throws Exception {
@@ -47,10 +58,33 @@ public class ModificationDiffTest {
dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final ModificationDiff modificationDiff = getModificationDiff(prepare);
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertAfter(topContainer, TOP_CONTAINER_ID, modificationDiff);
+ assertThat(modificationDiff.getUpdates().size(), is(1));
+ assertThat(modificationDiff.getUpdates().values().size(), is(1));
+ assertUpdate(modificationDiff.getUpdates().values().iterator().next(), TOP_CONTAINER_ID, null, topContainer);
+ }
+
+ @Test
+ public void testInitialWriteForContainerWithChoice() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ final ContainerNode containerWithChoice = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(WITH_CHOICE_CONTAINER_QNAME))
+ .withChild(Builders.choiceBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME))
+ .withChild(ImmutableNodes.leafNode(IN_CASE1_LEAF_QNAME, "withinCase1"))
+ .build())
+ .build();
+ final YangInstanceIdentifier WITH_CHOICE_CONTAINER_ID = YangInstanceIdentifier.of(WITH_CHOICE_CONTAINER_QNAME);
+ dataTreeModification.write(WITH_CHOICE_CONTAINER_ID, containerWithChoice);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class),
+ WITH_CHOICE_CONTAINER_ID, null, containerWithChoice);
}
private DataTreeModification getModification(final TipProducingDataTree dataTree) {
@@ -66,10 +100,9 @@ public class ModificationDiffTest {
dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final ModificationDiff modificationDiff = getModificationDiff(prepare);
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertTrue(modificationDiff.getModificationsAfter().isEmpty());
+ assertThat(modificationDiff.getUpdates().size(), is(0));
}
private DataTreeCandidateTip prepareModification(final TipProducingDataTree dataTree,
@@ -83,60 +116,61 @@ public class ModificationDiffTest {
@Test
public void testUpdateWrite() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- final NormalizedNode<?, ?> topContainerBefore = addTopContainer(dataTree);
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
final DataTreeModification dataTreeModification = getModification(dataTree);
final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
dataTreeModification.write(TOP_CONTAINER_ID, topContainerAfter);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
- assertBefore(topContainerBefore, TOP_CONTAINER_ID, modificationDiff);
- assertAfter(topContainerAfter, TOP_CONTAINER_ID, modificationDiff);
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
}
- private ModifiableDataTreeDelegator.ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) {
- return ModifiableDataTreeDelegator.ModificationDiff
- .recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode());
+ private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) {
+ return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode());
}
@Test
public void testUpdateMerge() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- final NormalizedNode<?, ?> topContainerBefore = addTopContainer(dataTree);
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
final DataTreeModification dataTreeModification = getModification(dataTree);
final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
dataTreeModification.merge(TOP_CONTAINER_ID, topContainerAfter);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff =
- getModificationDiff(prepare);
-
- assertBefore(topContainerBefore, TOP_CONTAINER_ID, modificationDiff);
- assertAfter(topContainerAfter, TOP_CONTAINER_ID, modificationDiff);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
}
@Test
public void testUpdateDelete() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- final NormalizedNode<?, ?> topContainerBefore = addTopContainer(dataTree);
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
final DataTreeModification dataTreeModification = getModification(dataTree);
dataTreeModification.delete(TOP_CONTAINER_ID);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
-
- assertBefore(topContainerBefore, TOP_CONTAINER_ID, modificationDiff);
- assertTrue(modificationDiff.getModificationsAfter().isEmpty());
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, null);
}
@Test
public void testWriteAndUpdateInnerList() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- addTopContainer(dataTree);
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
@@ -146,15 +180,17 @@ public class ModificationDiffTest {
new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
final MapNode mapNode = getNestedList("name1", "text");
+ final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
dataTreeModification.write(listId, mapNode);
dataTreeModification.ready();
dataTree.validate(dataTreeModification);
DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
- ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertAfter(mapNode, listId, modificationDiff);
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class),
+ listEntryId, null, mapNode.getValue().iterator().next());
// Commit so that update can be tested next
dataTree.commit(prepare);
@@ -171,9 +207,17 @@ public class ModificationDiffTest {
dataTree.validate(dataTreeModification);
prepare = dataTree.prepare(dataTreeModification);
- modificationDiff = getModificationDiff(prepare);
- assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff);
- assertAfter(mapEntryNode, listItemId, modificationDiff);
+ updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1 /*Actual list entry*/));
+ }
+//
+ private void assertUpdate(final ModificationDiff.NormalizedNodeUpdate update,
+ final YangInstanceIdentifier idExpected,
+ final NormalizedNode<?, ?> beforeExpected,
+ final NormalizedNode<?, ?> afterExpected) {
+ assertThat(update.getId(), is(idExpected));
+ assertThat(update.getDataBefore(), is(beforeExpected));
+ assertThat(update.getDataAfter(), is(afterExpected));
}
@Test
@@ -192,25 +236,44 @@ public class ModificationDiffTest {
new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
final MapNode mapNode = getNestedList("name1", "text");
+ final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
+
dataTreeModification.write(listId, mapNode);
final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(2));
+ assertThat(updates.values().size(), is(2));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), TOP_CONTAINER_ID, null,
+ Builders.containerBuilder(topContainer).withChild(mapNode).build());
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), listEntryId, null, mapNode.getValue().iterator().next());
+ // Assert that keys of the updates map are not wildcarded YID
+ assertThat(updates.keySet(), hasItems(
+ TOP_CONTAINER_ID,
+ listEntryId));
+ }
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
+ private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForAfterType(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
+ final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
+ return updates.values().stream()
+ .filter(update -> containerNodeClass.isAssignableFrom(update.getDataAfter().getClass()))
+ .findFirst().get();
+ }
- // TODO HONEYCOMB-94 2 after modifications should appear, for top-container and nested-list entry
- assertAfter(Builders.containerBuilder(topContainer)
- .withChild(mapNode)
- .build(),
- TOP_CONTAINER_ID, modificationDiff);
+ private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForBeforeType(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
+ final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
+ return updates.values().stream()
+ .filter(update -> containerNodeClass.isAssignableFrom(update.getDataBefore().getClass()))
+ .findFirst().get();
}
@Test
public void testWriteDeepList() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- addTopContainer(dataTree);
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
@@ -250,7 +313,8 @@ public class ModificationDiffTest {
.withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build());
dataTreeModification.merge(
deepListId,
- Builders.mapBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
+ Builders.mapBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
.build());
dataTreeModification.merge(
deepListEntryId,
@@ -261,16 +325,14 @@ public class ModificationDiffTest {
prepare = dataTree.prepare(dataTreeModification);
dataTree.commit(prepare);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
-
- assertTrue(modificationDiff.getModificationsBefore().isEmpty());
- assertAfter(getDeepList("name1"), deepListId, modificationDiff);
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), deepListEntryId, null, deepListEntry);
}
@Test
public void testDeleteInnerListItem() throws Exception {
final TipProducingDataTree dataTree = getDataTree();
- addTopContainer(dataTree);
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
@@ -298,58 +360,37 @@ public class ModificationDiffTest {
dataTree.validate(dataTreeModification);
prepare = dataTree.prepare(dataTreeModification);
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff = getModificationDiff(prepare);
-
- assertBefore(mapNode.getValue().iterator().next(), listItemId, modificationDiff);
- assertTrue(modificationDiff.getModificationsAfter().isEmpty());
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForBeforeType(updates, MapEntryNode.class), listItemId, mapNode.getValue().iterator().next(), null);
}
- private NormalizedNode<?, ?> addTopContainer(final TipProducingDataTree dataTree)
+ static void addNodeToTree(final DataTree dataTree, final NormalizedNode<?, ?> node,
+ final YangInstanceIdentifier id)
throws DataValidationFailedException {
DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
- final NormalizedNode<?, ?> topContainerBefore = getTopContainer("string1");
- dataTreeModification.write(TOP_CONTAINER_ID, topContainerBefore);
+ dataTreeModification.write(id, node);
dataTreeModification.ready();
dataTree.validate(dataTreeModification);
- DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
+ DataTreeCandidate prepare = dataTree.prepare(dataTreeModification);
dataTree.commit(prepare);
- return topContainerBefore;
- }
-
- private void assertAfter(final NormalizedNode<?, ?> topContainer, final YangInstanceIdentifier TOP_CONTAINER_ID,
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) {
- assertModification(topContainer, TOP_CONTAINER_ID, modificationDiff.getModificationsAfter());
- }
-
- private void assertModification(final NormalizedNode<?, ?> topContainer,
- final YangInstanceIdentifier TOP_CONTAINER_ID,
- final Map<YangInstanceIdentifier, NormalizedNode<?, ?>> modificationMap) {
- assertEquals(1, modificationMap.keySet().size());
- assertEquals(TOP_CONTAINER_ID, modificationMap.keySet().iterator().next());
- assertEquals(topContainer, modificationMap.values().iterator().next());
- }
-
- private void assertBefore(final NormalizedNode<?, ?> topContainerBefore,
- final YangInstanceIdentifier TOP_CONTAINER_ID,
- final ModifiableDataTreeDelegator.ModificationDiff modificationDiff) {
- assertModification(topContainerBefore, TOP_CONTAINER_ID, modificationDiff.getModificationsBefore());
}
- private TipProducingDataTree getDataTree() throws ReactorException {
+ static TipProducingDataTree getDataTree() throws ReactorException {
final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
dataTree.setSchemaContext(getSchemaCtx());
return dataTree;
}
- private ContainerNode getTopContainer(final String stringValue) {
+ static 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) {
+ static MapNode getNestedList(final String listItemName, final String text) {
return Builders.mapBuilder()
.withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME))
.withChild(
@@ -378,9 +419,9 @@ public class ModificationDiffTest {
.build();
}
- private SchemaContext getSchemaCtx() throws ReactorException {
+ private static SchemaContext getSchemaCtx() throws ReactorException {
final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild();
- buildAction.addSource(new YangStatementSourceImpl(getClass().getResourceAsStream("/test-diff.yang")));
+ buildAction.addSource(new YangStatementSourceImpl(ModificationDiffTest.class.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
index 7e8721f00..5cccc8718 100644
--- a/v3po/data-impl/src/test/resources/test-diff.yang
+++ b/v3po/data-impl/src/test/resources/test-diff.yang
@@ -34,4 +34,21 @@ module test-diff {
}
}
+ container with-choice {
+
+ choice choice {
+ case case1 {
+ leaf in-case1 {
+ type string;
+ }
+ }
+
+ case case2 {
+ leaf in-case2 {
+ type string;
+ }
+ }
+ }
+ }
+
}