summaryrefslogtreecommitdiffstats
path: root/infra
diff options
context:
space:
mode:
authorMarek Gradzki <mgradzki@cisco.com>2017-01-17 13:30:50 +0100
committerMarek Gradzki <mgradzki@cisco.com>2017-01-23 08:44:59 +0000
commit60e463b17b05458c1f9a7fd72f9e99d71124eedf (patch)
treeb4fc9fb42aa2414a8a02ea6cebd17332729acdb3 /infra
parenta3c54fffa45fadb59daa32e7dcecb1f66c847c43 (diff)
HONEYCOMB-302: add support for nested augmentations
Change-Id: I60f1b3f79ddb578d6fca157fe5736de40b30623e Signed-off-by: Marek Gradzki <mgradzki@cisco.com> (cherry picked from commit 78886acd688284585c2e219e18d7289f49cc8a45)
Diffstat (limited to 'infra')
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java114
-rw-r--r--infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java9
-rw-r--r--infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/NestedAugmentationWriteTest.java280
-rw-r--r--infra/it/test-model/src/main/yang/hc-aug-test.yang63
4 files changed, 440 insertions, 26 deletions
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java
index 86666ba70..9903d99fc 100644
--- a/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/data/impl/ModificationDiff.java
@@ -24,6 +24,7 @@ import static org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationT
import static org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType.WRITE;
import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
@@ -296,19 +297,23 @@ final class ModificationDiff {
private final DataTreeCandidateNode dataCandidate;
// Using Object as type for schema node since it's the only type that's a parent to all schema node types from
// yangtools. The hierarchy does not use e.g. SchemaNode class for all types
+ private final Object parentNode;
private final Object schemaNode;
Modification(final YangInstanceIdentifier id,
final DataTreeCandidateNode dataCandidate,
+ final Object parentNode,
final Object schemaNode) {
this.id = id;
this.dataCandidate = dataCandidate;
+ this.parentNode = parentNode;
this.schemaNode = schemaNode;
}
- Stream<Modification> streamChildren() {
- return dataCandidate.getChildNodes().stream()
- .map(child -> new Modification(id.node(child.getIdentifier()), child, schemaChild(schemaNode, child.getIdentifier())));
+ Modification(final YangInstanceIdentifier id,
+ final DataTreeCandidateNode dataCandidate,
+ final Object schemaNode) {
+ this(id, dataCandidate, schemaNode, schemaNode);
}
List<Modification> getChildNodes() {
@@ -355,35 +360,98 @@ final class ModificationDiff {
return dataCandidate.getDataAfter().isPresent();
}
+ private AugmentationSchema findAugmentation(Object currentNode,
+ final YangInstanceIdentifier.AugmentationIdentifier identifier) {
+ if (currentNode != null) {
+ // check if identifier points to some augmentation of currentNode
+ if (currentNode instanceof AugmentationTarget) {
+ Optional<AugmentationSchema> augmentationSchema =
+ ((AugmentationTarget) currentNode).getAvailableAugmentations().stream()
+ .filter(aug -> identifier.equals(new YangInstanceIdentifier.AugmentationIdentifier(
+ aug.getChildNodes().stream()
+ .map(SchemaNode::getQName)
+ .collect(Collectors.toSet()))))
+ .findFirst();
+ if (augmentationSchema.isPresent()) {
+ return augmentationSchema.get();
+ }
+ }
+
+ // continue search:
+ Collection<DataSchemaNode> childNodes = Collections.emptyList();
+ if (currentNode instanceof DataNodeContainer) {
+ childNodes = ((DataNodeContainer) currentNode).getChildNodes();
+ } else if (currentNode instanceof ChoiceSchemaNode) {
+ childNodes = ((ChoiceSchemaNode) currentNode).getCases().stream()
+ .flatMap(cas -> cas.getChildNodes().stream()).collect(Collectors.toList());
+ }
+ return childNodes.stream().map(n -> findAugmentation(n, identifier)).filter(n -> n != null).findFirst()
+ .orElse(null);
+ } else {
+ return null;
+ }
+ }
+
+ Stream<Modification> streamChildren() {
+ return dataCandidate.getChildNodes().stream()
+ .map(child -> {
+ final YangInstanceIdentifier childId = id.node(child.getIdentifier());
+ final Object schemaChild = schemaChild(schemaNode, child.getIdentifier());
+ // An augment cannot change other augment, so we do not update parent node if we are streaming
+ // children of AugmentationSchema (otherwise we would fail to find schema for nested augmentations):
+ final Object newParent = (schemaNode instanceof AugmentationSchema)
+ ? parentNode
+ : schemaNode;
+ return new Modification(childId, child, newParent, schemaChild);
+ });
+ }
+
/**
* Find next schema node in hierarchy.
*/
- private Object schemaChild(final Object schema, final YangInstanceIdentifier.PathArgument identifier) {
+ private Object schemaChild(final Object schemaNode, final YangInstanceIdentifier.PathArgument identifier) {
Object found = null;
if (identifier instanceof YangInstanceIdentifier.AugmentationIdentifier) {
- if (schema instanceof AugmentationTarget) {
+ if (schemaNode instanceof AugmentationTarget) {
// Find matching augmentation
- found = ((AugmentationTarget) schema).getAvailableAugmentations().stream()
- .filter(aug -> identifier.equals(new YangInstanceIdentifier.AugmentationIdentifier(
- aug.getChildNodes().stream()
- .map(SchemaNode::getQName)
- .collect(Collectors.toSet()))))
- .findFirst()
- .orElse(null);
+ found = ((AugmentationTarget) schemaNode).getAvailableAugmentations().stream()
+ .filter(aug -> identifier.equals(new YangInstanceIdentifier.AugmentationIdentifier(
+ aug.getChildNodes().stream()
+ .map(SchemaNode::getQName)
+ .collect(Collectors.toSet()))))
+ .findFirst()
+ .orElse(null);
+
+ if (found == null) {
+ // An augment cannot change other augment, but all augments only change their targets (data nodes).
+ //
+ // As a consequence, if nested augmentations are present,
+ // AugmentationSchema might reference child schema node instances that do not include changes
+ // from nested augments.
+ //
+ // But schemaNode, as mentioned earlier, contains all the changes introduced by augments.
+ //
+ // On the other hand, in case of augments which introduce leaves,
+ // we need to address AugmentationSchema node directly so we can't simply do
+ // found = schemaNode;
+ //
+ found =
+ findAugmentation(parentNode, (YangInstanceIdentifier.AugmentationIdentifier) identifier);
+ }
}
- } else if (schema instanceof DataNodeContainer) {
+ } else if (schemaNode instanceof DataNodeContainer) {
// Special handling for list aggregator nodes. If we are at list aggregator node e.g. MapNode and
// we are searching for schema for a list entry e.g. MapEntryNode just return the same schema
- if (schema instanceof ListSchemaNode &&
- ((SchemaNode) schema).getQName().equals(identifier.getNodeType())) {
- found = schema;
+ if (schemaNode instanceof ListSchemaNode &&
+ ((SchemaNode) schemaNode).getQName().equals(identifier.getNodeType())) {
+ found = schemaNode;
} else {
- found = ((DataNodeContainer) schema).getDataChildByName(identifier.getNodeType());
+ found = ((DataNodeContainer) schemaNode).getDataChildByName(identifier.getNodeType());
}
- } else if (schema instanceof ChoiceSchemaNode) {
+ } else if (schemaNode instanceof ChoiceSchemaNode) {
// For choices, iterate through all the cases
- final Optional<DataSchemaNode> maybeChild = ((ChoiceSchemaNode) schema).getCases().stream()
+ final Optional<DataSchemaNode> maybeChild = ((ChoiceSchemaNode) schemaNode).getCases().stream()
.flatMap(cas -> cas.getChildNodes().stream())
.filter(child -> child.getQName().equals(identifier.getNodeType()))
.findFirst();
@@ -391,12 +459,12 @@ final class ModificationDiff {
found = maybeChild.get();
}
// Special handling for leaf-list nodes. Basically the same as is for list mixin nodes
- } else if (schema instanceof LeafListSchemaNode &&
- ((SchemaNode) schema).getQName().equals(identifier.getNodeType())) {
- found = schema;
+ } else if (schemaNode instanceof LeafListSchemaNode &&
+ ((SchemaNode) schemaNode).getQName().equals(identifier.getNodeType())) {
+ found = schemaNode;
}
- return checkNotNull(found, "Unable to find child node in: %s identifiable by: %s", schema, identifier);
+ return checkNotNull(found, "Unable to find child node in: %s identifiable by: %s", schemaNode, identifier);
}
@Override
diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java
index 3c996f6a6..e2a74ab69 100644
--- a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java
+++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/AbstractInfraTest.java
@@ -21,14 +21,13 @@ import static org.mockito.Mockito.when;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.Futures;
-import java.util.Collections;
+import java.util.Arrays;
import java.util.Map;
import javassist.ClassPool;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.$YangModuleInfoImpl;
import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
@@ -70,7 +69,11 @@ abstract class AbstractInfraTest {
static ModuleInfoBackedContext getSchemaContext() {
final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
- moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance()));
+ moduleInfoBackedContext.addModuleInfos(Arrays.asList(
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.$YangModuleInfoImpl.getInstance(),
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.$YangModuleInfoImpl.getInstance()
+
+ ));
return moduleInfoBackedContext;
}
diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/NestedAugmentationWriteTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/NestedAugmentationWriteTest.java
new file mode 100644
index 000000000..ef7430dd3
--- /dev/null
+++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/data/impl/NestedAugmentationWriteTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2017 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.data.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import io.fd.honeycomb.data.DataModification;
+import io.fd.honeycomb.translate.impl.write.registry.FlatWriterRegistryBuilder;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.Writer;
+import io.fd.honeycomb.translate.write.registry.WriterRegistry;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.AugTarget;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.AugTargetBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugment2Augment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugment2AugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugmentAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugmentAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugmentListAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.FromAugmentListAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.SimpleAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.SimpleNestedAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.SimpleNestedAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.FromAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.FromAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.from.augment.FromAugment2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.from.augment.FromAugment2Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.from.augment.FromAugmentEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.from.augment.FromAugmentEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.aug.test.rev161222.aug.target.from.augment.FromAugmentEntryKey;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+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.tree.InMemoryDataTreeFactory;
+
+/**
+ * Testing write for model with nested augmentation.
+ * See <a href="https://jira.fd.io/browse/HONEYCOMB-302">HONEYCOMB-302</a>} for more details.
+ */
+public class NestedAugmentationWriteTest extends AbstractInfraTest {
+
+ private static final InstanceIdentifier<AugTarget> AUG_TARGET_ID = InstanceIdentifier.create(AugTarget.class);
+ private static final InstanceIdentifier<FromAugmentAugment> FROM_AUGMENT_AUGMENT_ID =
+ AUG_TARGET_ID.augmentation(FromAugmentAugment.class);
+ private static final InstanceIdentifier<FromAugment> FROM_AUGMENT_ID =
+ FROM_AUGMENT_AUGMENT_ID.child(FromAugment.class);
+ private static final InstanceIdentifier<SimpleAugment> SIMPLE_AUGMENT_ID =
+ AUG_TARGET_ID.augmentation(SimpleAugment.class);
+ private static final InstanceIdentifier<FromAugment2Augment> FROM_AUGMENT2_AUGMENT_ID =
+ FROM_AUGMENT_ID.augmentation(FromAugment2Augment.class);
+ private static final InstanceIdentifier<FromAugment2> FROM_AUGMENT2_ID =
+ FROM_AUGMENT2_AUGMENT_ID.child(FromAugment2.class);
+ private static final InstanceIdentifier<SimpleNestedAugment> SIMPLE_NESTED_AUGMENT_ID =
+ FROM_AUGMENT_ID.augmentation(SimpleNestedAugment.class);
+ private static final InstanceIdentifier<FromAugmentListAugment> FROM_AUGMENT_LIST_AUGMENT_ID =
+ FROM_AUGMENT_ID.augmentation(FromAugmentListAugment.class);
+ private static final InstanceIdentifier<FromAugmentEntry> FROM_AUGMENT_ENTRY_ID =
+ FROM_AUGMENT_LIST_AUGMENT_ID.child(FromAugmentEntry.class);
+
+ private TipProducingDataTree dataTree;
+ private WriterRegistry writerRegistry;
+
+ private final Writer<AugTarget> augTargetWriter = mockWriter(AUG_TARGET_ID);
+ private final Writer<FromAugment> fromAugmentWriter = mockWriter(FROM_AUGMENT_ID);
+ private final Writer<FromAugment2> fromAugment2Writer = mockWriter(FROM_AUGMENT2_ID);
+ private final Writer<FromAugmentEntry> fromAugmentListWriter = mockWriter(FROM_AUGMENT_ENTRY_ID);
+
+ private final Writer<SimpleAugment> simpleAugmentWriter = mockWriter(SIMPLE_AUGMENT_ID);
+ private final Writer<SimpleNestedAugment> simpleNestedAugmentWriter = mockWriter(SIMPLE_NESTED_AUGMENT_ID);
+
+ private static <D extends DataObject> Writer<D> mockWriter(final InstanceIdentifier<D> id) {
+ final Writer<D> mock = (Writer<D>) mock(Writer.class);
+ when(mock.getManagedDataObjectType()).thenReturn(id);
+ return mock;
+ }
+
+ @Override
+ void postSetup() {
+ initDataTree();
+ initWriterRegistry();
+ }
+
+ private void initDataTree() {
+ dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+ dataTree.setSchemaContext(schemaContext);
+ }
+
+ private void initWriterRegistry() {
+ writerRegistry = new FlatWriterRegistryBuilder()
+ .add(augTargetWriter)
+ .add(fromAugmentWriter)
+ .add(simpleAugmentWriter)
+ .add(fromAugment2Writer)
+ .add(simpleNestedAugmentWriter)
+ .add(fromAugmentListWriter)
+ .build();
+ }
+
+ @Test
+ public void testSimpleAugmentationWrite() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
+
+ final DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ final AugTarget data = new AugTargetBuilder()
+ .setSomeLeaf("aug-target-leaf-val")
+ .addAugmentation(SimpleAugment.class, simpleAugment())
+ .build();
+
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(AUG_TARGET_ID, data);
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+
+ dataModification.commit();
+
+ verify(simpleAugmentWriter).update(eq(SIMPLE_AUGMENT_ID), eq(null), eq(simpleAugment()), any(WriteContext.class));
+ }
+
+ @Test
+ public void testSimpleNestedAugmentationWrite() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
+
+ final DataModification dataModification = modifiableDataTreeDelegator.newModification();
+
+ final SimpleNestedAugment augData = simpleNestedAugment();
+ final AugTarget data = augTarget(fromAugmentSimple(augData));
+
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(AUG_TARGET_ID, data);
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+
+ dataModification.commit();
+
+ verify(augTargetWriter).update(eq(AUG_TARGET_ID), eq(null), eq(data), any(WriteContext.class));
+ verify(fromAugmentWriter).update(eq(FROM_AUGMENT_ID), eq(null), eq(fromAugmentSimple(augData)), any(WriteContext.class));
+ verify(simpleNestedAugmentWriter).update(eq(SIMPLE_NESTED_AUGMENT_ID), eq(null), eq(augData), any(WriteContext.class));
+ }
+
+ private SimpleAugment simpleAugment() {
+ return new SimpleAugmentBuilder().setSimpleAugmentLeaf("val").build();
+ }
+
+ @Test
+ public void testNestedAugmentationWrite() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
+
+ final DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ final AugTarget data = augTarget(fromAugment(FromAugment2Augment.class, fromAugment2Augment(fromAugment2())));
+
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(AUG_TARGET_ID, data);
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+
+ dataModification.commit();
+
+ verify(augTargetWriter).update(eq(AUG_TARGET_ID), eq(null), eq(data), any(WriteContext.class));
+ verify(fromAugmentWriter).update(eq(FROM_AUGMENT_ID), eq(null), eq(fromAugment()), any(WriteContext.class));
+ verify(fromAugment2Writer).update(eq(FROM_AUGMENT2_ID), eq(null), eq(fromAugment2()), any(WriteContext.class));
+ }
+
+ @Test
+ public void testNestedAugmentationListWrite() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
+
+ final DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ final List<FromAugmentEntry> entries = Arrays.asList(
+ new FromAugmentEntryBuilder().setSomeLeaf("1").build(),
+ new FromAugmentEntryBuilder().setSomeLeaf("2").build()
+ );
+ final FromAugment fromAugment = fromAugment(FromAugmentListAugment.class, fromAugmentList(entries));
+ final AugTarget data = augTarget(fromAugment);
+
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(AUG_TARGET_ID, data);
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+ dataModification.commit();
+
+ final ArgumentCaptor<DataObject> doCaptor = ArgumentCaptor.forClass(DataObject.class);
+ verify(augTargetWriter).update(eq(AUG_TARGET_ID), eq(null), doCaptor.capture(), any(WriteContext.class));
+ assertEquals(data.getSomeLeaf(), ((AugTarget)doCaptor.getValue()).getSomeLeaf());
+
+ verify(fromAugmentWriter).update(eq(FROM_AUGMENT_ID), eq(null), doCaptor.capture(), any(WriteContext.class));
+ assertEquals(fromAugment.getSomeLeaf(), ((FromAugment)doCaptor.getValue()).getSomeLeaf());
+
+
+ final KeyedInstanceIdentifier<FromAugmentEntry, FromAugmentEntryKey> keyedNestedList1 =
+ FROM_AUGMENT_LIST_AUGMENT_ID.child(FromAugmentEntry.class, new FromAugmentEntryKey("1"));
+ final KeyedInstanceIdentifier<FromAugmentEntry, FromAugmentEntryKey> keyedNestedList2 =
+ FROM_AUGMENT_LIST_AUGMENT_ID.child(FromAugmentEntry.class, new FromAugmentEntryKey("2"));
+
+ verify(fromAugmentListWriter)
+ .update(eq(keyedNestedList1), eq(null), eq(entries.get(0)), any(WriteContext.class));
+ verify(fromAugmentListWriter)
+ .update(eq(keyedNestedList2), eq(null), eq(entries.get(1)), any(WriteContext.class));
+ }
+
+ private AugTarget augTarget(FromAugment fromAugment) {
+ return new AugTargetBuilder()
+ .setSomeLeaf("aug-target-leaf-val")
+ .addAugmentation(FromAugmentAugment.class,
+ new FromAugmentAugmentBuilder().setFromAugment(fromAugment).build())
+ .build();
+ }
+
+ private FromAugment fromAugment() {
+ return new FromAugmentBuilder()
+ .setSomeLeaf("from-augment-leaf-val")
+ .addAugmentation(FromAugment2Augment.class, new FromAugment2AugmentBuilder()
+ .setFromAugment2(fromAugment2()).build())
+ .build();
+ }
+
+ private FromAugment fromAugmentSimple(SimpleNestedAugment simpleNestedAugment) {
+ return new FromAugmentBuilder()
+ .setSomeLeaf("from-augment-leaf-val")
+ .addAugmentation(SimpleNestedAugment.class, simpleNestedAugment)
+ .build();
+ }
+
+ private SimpleNestedAugment simpleNestedAugment() {
+ return new SimpleNestedAugmentBuilder()
+ .setSimpleNestedAugmentLeaf("simple-nested-augment-leaf-val").build();
+ }
+
+ private FromAugment fromAugment(final Class<? extends Augmentation<FromAugment>> augmentationClass,
+ final Augmentation<FromAugment> augmentation) {
+ return new FromAugmentBuilder()
+ .setSomeLeaf("from-augment-leaf-val")
+ .addAugmentation(augmentationClass, augmentation)
+ .build();
+ }
+
+ private FromAugment2Augment fromAugment2Augment(FromAugment2 fromAugment2) {
+ return new FromAugment2AugmentBuilder().setFromAugment2(fromAugment2).build();
+ }
+
+ private FromAugment2 fromAugment2() {
+ return new FromAugment2Builder()
+ .setNewLeaf("new-leaf-val")
+ .build();
+ }
+
+ private FromAugmentListAugment fromAugmentList(final List<FromAugmentEntry> entries) {
+ return new FromAugmentListAugmentBuilder()
+ .setFromAugmentEntry(entries)
+ .build();
+ }
+}
diff --git a/infra/it/test-model/src/main/yang/hc-aug-test.yang b/infra/it/test-model/src/main/yang/hc-aug-test.yang
new file mode 100644
index 000000000..8a83ab56f
--- /dev/null
+++ b/infra/it/test-model/src/main/yang/hc-aug-test.yang
@@ -0,0 +1,63 @@
+module hc-aug-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:hc:aug:test";
+ prefix "hcat";
+
+ revision "2017-01--9" {
+ description "Test model for aumentations of augmentations (https://jira.fd.io/browse/HONEYCOMB-302)";
+ }
+
+ import yang-ext {
+ prefix "ext";
+ }
+
+ container aug-target {
+ leaf some-leaf {
+ type string;
+ }
+ }
+
+ augment "/aug-target" {
+ ext:augment-identifier "from-augment-augment";
+ container from-augment {
+ leaf some-leaf {
+ type string;
+ }
+ }
+ }
+
+ augment "/aug-target" {
+ ext:augment-identifier "simple-augment";
+ leaf simple-augment-leaf {
+ type string;
+ }
+ }
+
+ augment "/aug-target/from-augment" {
+ ext:augment-identifier "simple-nested-augment";
+ leaf simple-nested-augment-leaf {
+ type string;
+ }
+ }
+
+ augment "/aug-target/from-augment" {
+ ext:augment-identifier "from-augment2-augment";
+ container from-augment-2 {
+ leaf new-leaf {
+ type string;
+ }
+ }
+ }
+
+ augment "/aug-target/from-augment" {
+ ext:augment-identifier "from-augment-list-augment";
+ list from-augment-entry {
+ key some-leaf;
+
+ leaf some-leaf {
+ type string;
+ }
+ }
+ }
+
+}