summaryrefslogtreecommitdiffstats
path: root/infra/translate-impl
diff options
context:
space:
mode:
authorJan Srnicek <jsrnicek@cisco.com>2017-11-03 13:33:53 +0100
committerMarek Gradzki <mgradzki@cisco.com>2017-11-03 12:58:01 +0000
commit9779f4b3ffe24bb2338630c66169da92c880ffbb (patch)
treefac37c53b70b7bbfcea00f180554b3a0dfddd59c /infra/translate-impl
parent054eb07de938df56000a8fc5cb41cb77f84bf2b5 (diff)
HONEYCOMB-359 - Wildcarded writers
Adds option to specify subtree writer that can handle whole subtree of nodes without having whole subtree specified. Its checking if node is children at runtime, rather than having pre-computed tree Change-Id: Ic46f2bd6de84f0dd14865825399f5a90a1f80859 Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
Diffstat (limited to 'infra/translate-impl')
-rw-r--r--infra/translate-impl/pom.xml12
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/registry/CompositeReaderRegistryBuilder.java22
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java67
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryBuilder.java14
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java31
-rw-r--r--infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java72
-rw-r--r--infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/WildcardedSubtreeWriterTest.java115
7 files changed, 279 insertions, 54 deletions
diff --git a/infra/translate-impl/pom.xml b/infra/translate-impl/pom.xml
index c11893a..aa2a48b 100644
--- a/infra/translate-impl/pom.xml
+++ b/infra/translate-impl/pom.xml
@@ -53,6 +53,12 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>io.fd.honeycomb.it</groupId>
+ <artifactId>honeycomb-test-model</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>junit</groupId>
@@ -69,6 +75,12 @@
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.25</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/registry/CompositeReaderRegistryBuilder.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/registry/CompositeReaderRegistryBuilder.java
index f8290f1..4f7e1e1 100644
--- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/registry/CompositeReaderRegistryBuilder.java
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/read/registry/CompositeReaderRegistryBuilder.java
@@ -16,8 +16,6 @@
package io.fd.honeycomb.translate.impl.read.registry;
-import static com.google.common.base.Preconditions.checkArgument;
-
import com.google.common.collect.ImmutableMap;
import io.fd.honeycomb.translate.impl.read.GenericListReader;
import io.fd.honeycomb.translate.impl.read.GenericReader;
@@ -29,12 +27,6 @@ import io.fd.honeycomb.translate.read.registry.ReaderRegistry;
import io.fd.honeycomb.translate.read.registry.ReaderRegistryBuilder;
import io.fd.honeycomb.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder;
import io.fd.honeycomb.translate.util.YangDAG;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.NotThreadSafe;
import org.opendaylight.yangtools.concepts.Builder;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Identifiable;
@@ -43,6 +35,15 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
@NotThreadSafe
public final class CompositeReaderRegistryBuilder
extends AbstractSubtreeManagerRegistryBuilderBuilder<Reader<? extends DataObject, ? extends Builder<?>>, ReaderRegistry>
@@ -63,6 +64,11 @@ public final class CompositeReaderRegistryBuilder
}
@Override
+ protected Reader<? extends DataObject, ? extends Builder<?>> getWildcardedSubtreeHandler(@Nonnull Reader<? extends DataObject, ? extends Builder<?>> handler) {
+ throw new UnsupportedOperationException("Wildcarded readers are not supported");
+ }
+
+ @Override
public <D extends DataObject> void addStructuralReader(@Nonnull InstanceIdentifier<D> id,
@Nonnull Class<? extends Builder<D>> builderType) {
// prevent case to submit structural reader for list, which would cause fail because target setter consumes
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java
index 146ddb9..c3bc1ee 100644
--- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistry.java
@@ -21,7 +21,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
@@ -32,12 +31,15 @@ import io.fd.honeycomb.translate.write.WriteContext;
import io.fd.honeycomb.translate.write.Writer;
import io.fd.honeycomb.translate.write.registry.UpdateFailedException;
import io.fd.honeycomb.translate.write.registry.WriterRegistry;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
@@ -54,39 +56,23 @@ final class FlatWriterRegistry implements WriterRegistry {
private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistry.class);
- // All types handled by writers directly or as children
- private final ImmutableSet<InstanceIdentifier<?>> handledTypes;
-
private final Set<InstanceIdentifier<?>> writersOrderReversed;
private final Set<InstanceIdentifier<?>> writersOrder;
- private final Map<InstanceIdentifier<?>, Writer<?>> writers;
+ private final Map<InstanceIdentifier<?>, Writer<?>> writersById;
+ private final Set<? extends Writer<?>> writers;
/**
* Create flat registry instance.
*
- * @param writers immutable, ordered map of writers to use to process updates. Order of the writers has to be one in
+ * @param writersById immutable, ordered map of writers to use to process updates. Order of the writers has to be one in
* which create and update operations should be handled. Deletes will be handled in reversed order.
* All deletes are handled before handling all the updates.
*/
- FlatWriterRegistry(@Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
- this.writers = writers;
- this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writers.keySet())));
- this.writersOrder = writers.keySet();
- this.handledTypes = getAllHandledTypes(writers);
- }
-
- private static ImmutableSet<InstanceIdentifier<?>> getAllHandledTypes(
- @Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
- final ImmutableSet.Builder<InstanceIdentifier<?>> handledTypesBuilder = ImmutableSet.builder();
- for (Map.Entry<InstanceIdentifier<?>, Writer<?>> writerEntry : writers.entrySet()) {
- final InstanceIdentifier<?> writerType = writerEntry.getKey();
- final Writer<?> writer = writerEntry.getValue();
- handledTypesBuilder.add(writerType);
- if (writer instanceof SubtreeWriter) {
- handledTypesBuilder.addAll(((SubtreeWriter<?>) writer).getHandledChildTypes());
- }
- }
- return handledTypesBuilder.build();
+ FlatWriterRegistry(@Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writersById) {
+ this.writersById = writersById;
+ this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writersById.keySet())));
+ this.writersOrder = writersById.keySet();
+ this.writers = writersById.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toSet());
}
@Override
@@ -145,9 +131,9 @@ final class FlatWriterRegistry implements WriterRegistry {
if (writer == null) {
// This node must be handled by a subtree writer, find it and call it or else fail
- checkArgument(handledTypes.contains(singleType), "Unable to process update. Missing writers for: %s",
- singleType);
writer = getSubtreeWriterResponsible(singleType);
+ checkArgument(writer != null, "Unable to process update. Missing writers for: %s",
+ singleType);
singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer);
}
@@ -168,9 +154,9 @@ final class FlatWriterRegistry implements WriterRegistry {
@Nullable
private Writer<?> getSubtreeWriterResponsible(final InstanceIdentifier<?> singleType) {
- return writers.values().stream()
+ return writersById.values().stream()
.filter(w -> w instanceof SubtreeWriter)
- .filter(w -> ((SubtreeWriter<?>) w).getHandledChildTypes().contains(singleType))
+ .filter(w -> w.canProcess(singleType))
.findFirst()
.orElse(null);
}
@@ -249,10 +235,23 @@ final class FlatWriterRegistry implements WriterRegistry {
private void checkAllTypesCanBeHandled(
@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) {
- if (!handledTypes.containsAll(updates.keySet())) {
- final Sets.SetView<InstanceIdentifier<?>> missingWriters = Sets.difference(updates.keySet(), handledTypes);
- LOG.warn("Unable to process update. Missing writers for: {}", missingWriters);
- throw new IllegalArgumentException("Unable to process update. Missing writers for: " + missingWriters);
+
+ List<InstanceIdentifier<?>> noWriterNodes = new ArrayList<>();
+ for (InstanceIdentifier<?> id : updates.keySet()) {
+ // either there is direct writer for the iid
+ if (writersById.containsKey(id)) {
+ continue;
+ } else {
+ // or subtree one
+ if (writers.stream().anyMatch(o -> o.canProcess(id))) {
+ continue;
+ }
+ }
+ noWriterNodes.add(id);
+ }
+
+ if (!noWriterNodes.isEmpty()) {
+ throw new IllegalArgumentException("Unable to process update. Missing writers for: " + noWriterNodes);
}
}
@@ -269,7 +268,7 @@ final class FlatWriterRegistry implements WriterRegistry {
@Nullable
private Writer<?> getWriter(@Nonnull final InstanceIdentifier<?> singleType) {
- return writers.get(singleType);
+ return writersById.get(singleType);
}
}
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryBuilder.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryBuilder.java
index 0c6d0a1..4360606 100644
--- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryBuilder.java
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryBuilder.java
@@ -24,15 +24,16 @@ import io.fd.honeycomb.translate.write.Writer;
import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
import io.fd.honeycomb.translate.write.registry.WriterRegistry;
import io.fd.honeycomb.translate.write.registry.WriterRegistryBuilder;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.NotThreadSafe;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import java.util.Set;
+import java.util.stream.Collectors;
+
/**
* Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
*/
@@ -53,6 +54,11 @@ public final class FlatWriterRegistryBuilder
return SubtreeWriter.createForWriter(handledChildren, writer);
}
+ @Override
+ protected Writer<? extends DataObject> getWildcardedSubtreeHandler(@Nonnull Writer<? extends DataObject> handler) {
+ return SubtreeWriter.createWildcardedForWriter(handler);
+ }
+
/**
* Create FlatWriterRegistry with writers ordered according to submitted relationships.
*/
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java
index b2a571b..a1a5f3f 100644
--- a/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/translate/impl/write/registry/SubtreeWriter.java
@@ -37,15 +37,16 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
private final Writer<D> delegate;
private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
+ private boolean isWildcarded = false;
- private SubtreeWriter(final Writer<D> delegate, Set<InstanceIdentifier<?>> handledTypes) {
+ private SubtreeWriter(final Writer<D> delegate, final Set<InstanceIdentifier<?>> handledTypes) {
this.delegate = delegate;
for (InstanceIdentifier<?> handledType : handledTypes) {
// Iid has to start with writer's handled root type
checkArgument(delegate.getManagedDataObjectType().getTargetType().equals(
handledType.getPathArguments().iterator().next().getType()),
"Handled node from subtree has to be identified by an instance identifier starting from: %s."
- + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType);
+ + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType);
checkArgument(Iterables.size(handledType.getPathArguments()) > 1,
"Handled node from subtree identifier too short: %s", handledType);
handledChildTypes.add(InstanceIdentifier.create(Iterables.concat(
@@ -53,6 +54,11 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
}
}
+ private SubtreeWriter(final Writer<D> delegate) {
+ this.delegate = delegate;
+ this.isWildcarded = true;
+ }
+
/**
* Return set of types also handled by this writer. All of the types are children of the type managed by this
* writer excluding the type of this writer.
@@ -75,6 +81,20 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
}
@Override
+ public boolean canProcess(@Nonnull InstanceIdentifier<?> instanceIdentifier) {
+ if (isWildcarded) {
+ final Class<D> parent = delegate.getManagedDataObjectType().getTargetType();
+ for (InstanceIdentifier.PathArgument pathArgument : instanceIdentifier.getPathArguments()) {
+ if (pathArgument.getType().equals(parent)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return handledChildTypes.contains(instanceIdentifier);
+ }
+
+ @Override
@Nonnull
public InstanceIdentifier<D> getManagedDataObjectType() {
return delegate.getManagedDataObjectType();
@@ -87,4 +107,11 @@ final class SubtreeWriter<D extends DataObject> implements Writer<D> {
@Nonnull final Writer<? extends DataObject> writer) {
return new SubtreeWriter<>(writer, handledChildren);
}
+
+ /**
+ * Wrap a writer as a subtree writer.
+ */
+ static Writer<?> createWildcardedForWriter(@Nonnull final Writer<? extends DataObject> writer) {
+ return new SubtreeWriter<>(writer);
+ }
}
diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java
index 1514369..01852d1 100644
--- a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java
+++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/FlatWriterRegistryTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -39,15 +40,19 @@ import io.fd.honeycomb.translate.util.DataObjects.DataObject1;
import io.fd.honeycomb.translate.util.DataObjects.DataObject2;
import io.fd.honeycomb.translate.write.DataObjectUpdate;
import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
import io.fd.honeycomb.translate.write.Writer;
import io.fd.honeycomb.translate.write.registry.UpdateFailedException;
import io.fd.honeycomb.translate.write.registry.WriterRegistry;
import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -72,6 +77,18 @@ public class FlatWriterRegistryTest {
when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
when(writer3.getManagedDataObjectType()).thenReturn(DataObjects.DataObject3.IID);
+ when(writer4.getManagedDataObjectType()).thenReturn(DataObjects.DataObject1ChildK.IID);
+ // TODO - HONEYCOMB-412 - thenCallRealMethod doest work with default methods
+ // https://stackoverflow.com/questions/27663252/can-you-make-mockito-1-10-17-work-with-default-methods-in-interfaces
+ when(writer1.canProcess(any())).thenAnswer(answerWithImpl());
+ when(writer2.canProcess(any())).thenAnswer(answerWithImpl());
+ when(writer3.canProcess(any())).thenAnswer(answerWithImpl());
+ when(writer4.canProcess(any())).thenAnswer(answerWithImpl());
+ }
+
+ private static Answer<Object> answerWithImpl() {
+ return invocationOnMock -> new CheckedMockWriter(Writer.class.cast(invocationOnMock.getMock())).canProcess(
+ InstanceIdentifier.class.cast(invocationOnMock.getArguments()[0]));
}
@Test
@@ -112,8 +129,12 @@ public class FlatWriterRegistryTest {
inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
- verifyNoMoreInteractions(writer1);
- verifyNoMoreInteractions(writer2);
+ // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
+ // we are really interested just in invocations of processModification(),so adding specific verify to check that
+ verify(writer1,times(1)).processModification(any(),any(),any(),any());
+ verify(writer2,times(1)).processModification(any(),any(),any(),any());
+ //verifyNoMoreInteractions(writer1);
+ //verifyNoMoreInteractions(writer2);
}
@Test
@@ -136,8 +157,12 @@ public class FlatWriterRegistryTest {
inOrder.verify(writer2).processModification(iid2, dataObject2, null, ctx);
inOrder.verify(writer1).processModification(iid, dataObject, null, ctx);
- verifyNoMoreInteractions(writer1);
- verifyNoMoreInteractions(writer2);
+ // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
+ // we are really interested just in invocations of processModification(),so adding specific verify to check that
+ verify(writer1,times(1)).processModification(any(),any(),any(),any());
+ verify(writer2,times(1)).processModification(any(),any(),any(),any());
+ //verifyNoMoreInteractions(writer1);
+ //verifyNoMoreInteractions(writer2);
}
@Test
@@ -170,8 +195,12 @@ public class FlatWriterRegistryTest {
inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
- verifyNoMoreInteractions(writer1);
- verifyNoMoreInteractions(writer2);
+ // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
+ // we are really interested just in invocations of processModification(),so adding specific verify to check that
+ verify(writer1,times(2)).processModification(any(),any(),any(),any());
+ verify(writer2,times(2)).processModification(any(),any(),any(),any());
+ //verifyNoMoreInteractions(writer1);
+ //verifyNoMoreInteractions(writer2);
}
@Test(expected = IllegalArgumentException.class)
@@ -298,4 +327,35 @@ public class FlatWriterRegistryTest {
final InstanceIdentifier<D> iid) {
return DataObjectUpdate.create(iid, mock(type), mock(type));
}
+
+ //TODO - HONEYCOMB-412 - remove after
+ /**
+ * Used to utilize default implementation of canProcess()
+ * */
+ static class CheckedMockWriter implements Writer{
+
+ private final Writer mockedWriter;
+
+ CheckedMockWriter(final Writer mockedWriter) {
+ this.mockedWriter = mockedWriter;
+ }
+
+ @Override
+ public void processModification(@Nonnull final InstanceIdentifier id, @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx)
+ throws WriteFailedException {
+ mockedWriter.processModification(id,dataBefore,dataAfter,ctx);
+ }
+
+ @Override
+ public boolean supportsDirectUpdate() {
+ return mockedWriter.supportsDirectUpdate();
+ }
+
+ @Nonnull
+ @Override
+ public InstanceIdentifier getManagedDataObjectType() {
+ return mockedWriter.getManagedDataObjectType();
+ }
+ }
} \ No newline at end of file
diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/WildcardedSubtreeWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/WildcardedSubtreeWriterTest.java
new file mode 100644
index 0000000..7006eea
--- /dev/null
+++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/translate/impl/write/registry/WildcardedSubtreeWriterTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.translate.impl.write.registry;
+
+import io.fd.honeycomb.translate.write.Writer;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+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.FromAugmentAugment;
+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.SimpleNestedAugment;
+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.from.augment.FromAugmentEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class WildcardedSubtreeWriterTest {
+
+ private static final InstanceIdentifier<ContainerWithList> C_WITH_LIST = InstanceIdentifier.create(ContainerWithList.class);
+ private static final InstanceIdentifier<ContainerWithChoice> C_WITH_CHOICE = InstanceIdentifier.create(ContainerWithChoice.class);
+ private static final InstanceIdentifier<AugTarget> C_AUG = InstanceIdentifier.create(AugTarget.class);
+
+ private static final InstanceIdentifier<ListInContainer> L_IN_CONTAINER = C_WITH_LIST.child(ListInContainer.class);
+ private static final InstanceIdentifier<ContainerInList> C_IN_LIST = L_IN_CONTAINER.child(ContainerInList.class);
+
+ private static final InstanceIdentifier<NestedList> N_LIST = C_IN_LIST.child(NestedList.class);
+
+ private Writer subtreeContainerWithList;
+ private Writer subtreeContainerWithChoice;
+ private Writer subtreeAugTarget;
+
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ Writer<ContainerWithList> writerContainerWithList = mock(Writer.class);
+ Writer<ContainerWithChoice> writerContainerWithChoice = mock(Writer.class);
+ Writer<AugTarget> writerAugTarget = mock(Writer.class);
+ when(writerContainerWithList.getManagedDataObjectType()).thenReturn(C_WITH_LIST);
+ when(writerContainerWithChoice.getManagedDataObjectType()).thenReturn(C_WITH_CHOICE);
+ when(writerAugTarget.getManagedDataObjectType()).thenReturn(C_AUG);
+ subtreeContainerWithList = SubtreeWriter.createWildcardedForWriter(writerContainerWithList);
+ subtreeContainerWithChoice = SubtreeWriter.createWildcardedForWriter(writerContainerWithChoice);
+ subtreeAugTarget = SubtreeWriter.createWildcardedForWriter(writerAugTarget);
+ }
+
+ @Test
+ public void testParent() {
+ assertTrue(subtreeContainerWithList.canProcess(C_WITH_LIST));
+ assertFalse(subtreeContainerWithList.canProcess(C_WITH_CHOICE));
+
+ assertTrue(subtreeContainerWithChoice.canProcess(C_WITH_CHOICE));
+ assertFalse(subtreeContainerWithChoice.canProcess(C_WITH_LIST));
+
+ assertTrue(subtreeAugTarget.canProcess(C_AUG));
+ assertFalse(subtreeAugTarget.canProcess(C_WITH_LIST));
+ }
+
+ @Test
+ public void testDirectChild() {
+ assertTrue(subtreeContainerWithList.canProcess(L_IN_CONTAINER));
+ assertFalse(subtreeContainerWithList.canProcess(C_WITH_CHOICE.child(C3.class)));
+
+ assertTrue(subtreeContainerWithChoice.canProcess(C_WITH_CHOICE.child(C3.class)));
+ assertFalse(subtreeContainerWithChoice.canProcess(L_IN_CONTAINER));
+ }
+
+ @Test
+ public void testIndirectChild() {
+ assertTrue(subtreeContainerWithList.canProcess(C_IN_LIST));
+ assertTrue(subtreeContainerWithList.canProcess(N_LIST));
+ }
+
+ @Test
+ public void testAugDirectChild() {
+ assertTrue(subtreeAugTarget.canProcess(C_AUG.augmentation(FromAugmentAugment.class).child(FromAugment.class)));
+ assertFalse(subtreeContainerWithList.canProcess(C_AUG.augmentation(FromAugmentAugment.class).child(FromAugment.class)));
+ }
+
+ @Test
+ public void testAugIndirectChild() {
+ assertTrue(subtreeAugTarget.canProcess(C_AUG.augmentation(FromAugmentAugment.class)
+ .child(FromAugment.class)
+ .augmentation(SimpleNestedAugment.class)));
+ assertFalse(subtreeContainerWithList.canProcess(C_AUG.augmentation(FromAugmentAugment.class)
+ .child(FromAugment.class)
+ .augmentation(FromAugmentListAugment.class)
+ .child(FromAugmentEntry.class)));
+ }
+}