From 0578156b721fa01c8c645b8f9625ecebdb6449e4 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Wed, 27 Jul 2016 11:05:51 +0200 Subject: HONEYCOMB-130: Separate v3po plugin from HC infra Creating folders: - common/ - infra/ - v3po/ - vpp-common/ Change-Id: I2c39e1b17e39e7c0f0628f44aa5fe08563fa06e4 Signed-off-by: Maros Marsalek --- infra/it/it-test/pom.xml | 66 +++ .../v3po/data/impl/AbstractInfraTest.java | 106 +++++ .../v3po/data/impl/HoneycombReadInfraTest.java | 460 ++++++++++++++++++ .../data/impl/HoneycombSubtreeReadInfraTest.java | 150 ++++++ .../v3po/data/impl/HoneycombWriteInfraTest.java | 521 +++++++++++++++++++++ .../java/io/fd/honeycomb/v3po/data/impl/Ids.java | 62 +++ infra/it/pom.xml | 51 ++ infra/it/test-model/pom.xml | 36 ++ infra/it/test-model/src/main/yang/hc-test.yang | 123 +++++ 9 files changed, 1575 insertions(+) create mode 100644 infra/it/it-test/pom.xml create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java create mode 100644 infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java create mode 100644 infra/it/pom.xml create mode 100644 infra/it/test-model/pom.xml create mode 100644 infra/it/test-model/src/main/yang/hc-test.yang (limited to 'infra/it') diff --git a/infra/it/it-test/pom.xml b/infra/it/it-test/pom.xml new file mode 100644 index 000000000..c7a310390 --- /dev/null +++ b/infra/it/it-test/pom.xml @@ -0,0 +1,66 @@ + + + + + impl-parent + io.fd.honeycomb.common + 1.0.0-SNAPSHOT + ../../../common/impl-parent + + + 4.0.0 + io.fd.honeycomb + honeycomb-it-test + 1.0.0-SNAPSHOT + + + + ${project.groupId} + data-impl + ${project.version} + + + ${project.groupId} + translate-impl + ${project.version} + + + org.skinny-framework + skinny-logback + 1.0.8 + test + + + junit + junit + test + + + org.mockito + mockito-all + test + + + ${project.groupId} + honeycomb-test-model + ${project.version} + + + + + \ No newline at end of file diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java new file mode 100644 index 000000000..47644cc70 --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.data.impl; + +import static org.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.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; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Base IT test infrastructure. + */ +abstract class AbstractInfraTest { + + protected BindingNormalizedNodeSerializer serializer; + protected SchemaContext schemaContext; + + @Mock + protected org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker; + @Mock + private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTx; + + static BindingToNormalizedNodeCodec getSerializer(final ModuleInfoBackedContext moduleInfoBackedContext, + final SchemaContext schemaContext) { + final DataObjectSerializerGenerator serializerGenerator = new StreamWriterGenerator(JavassistUtils.forClassPool( + ClassPool.getDefault())); + final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(serializerGenerator); + final BindingRuntimeContext ctx = + BindingRuntimeContext.create(moduleInfoBackedContext, schemaContext); + codecRegistry.onBindingRuntimeContextUpdated(ctx); + return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry); + } + + static ModuleInfoBackedContext getSchemaContext() { + final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); + moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance())); + return moduleInfoBackedContext; + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(contextBroker.newReadWriteTransaction()).thenReturn(ctxTx); + when(ctxTx.submit()).thenReturn(Futures.immediateCheckedFuture(null)); + + initSerializer(); + postSetup(); + } + + abstract void postSetup(); + + private void initSerializer() { + final ModuleInfoBackedContext moduleInfoBackedContext = getSchemaContext(); + schemaContext = moduleInfoBackedContext.tryToCreateSchemaContext().get(); + serializer = getSerializer(moduleInfoBackedContext, schemaContext); + } + + protected Multimap, ? extends DataObject> toBinding( + final NormalizedNode read) { + Multimap, DataObject> baNodes = HashMultimap.create(); + + for (DataContainerChild o : ((DataContainerNode) read).getValue()) { + final YangInstanceIdentifier yid = YangInstanceIdentifier.of(o.getNodeType()); + final Map.Entry, DataObject> baEntry = serializer.fromNormalizedNode(yid, o); + baNodes.put(baEntry.getKey(), baEntry.getValue()); + } + return baNodes; + } +} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java new file mode 100644 index 000000000..b1f3a196a --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.data.impl; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.translate.impl.read.GenericListReader; +import io.fd.honeycomb.v3po.translate.read.ListReader; +import io.fd.honeycomb.v3po.translate.read.ReadContext; +import io.fd.honeycomb.v3po.translate.read.ReadFailedException; +import io.fd.honeycomb.v3po.translate.read.Reader; +import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.v3po.translate.util.RWUtils; +import io.fd.honeycomb.v3po.translate.util.read.ReflexiveListReaderCustomizer; +import io.fd.honeycomb.v3po.translate.util.read.ReflexiveReader; +import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.exceptions.misusing.MockitoConfigurationException; +import org.mockito.invocation.InvocationOnMock; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder; +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.ContainerWithListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; +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.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +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.ContainerInListBuilder; +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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.Identifiable; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class HoneycombReadInfraTest extends AbstractInfraTest { + + @Mock + private ReadContext ctx; + private ReaderRegistry registry; + + // 1 + private Reader simpleContainerReader = + mockReader(Ids.SIMPLE_CONTAINER_ID, this::readSimpleContainer, SimpleContainerBuilder.class); + // 1.1 + private Reader simpleAugmentReader = + mockReader(Ids.SIMPLE_AUGMENT_ID, this::readSimpleAugment, SimpleAugmentBuilder.class); + // 1.2 + // Noop reader(no real attributes) + private Reader complexAugmentReader = + mockReader(Ids.COMPLEX_AUGMENT_ID, HoneycombReadInfraTest::noopRead, ComplexAugmentBuilder.class); + // 1.2.1 + private Reader complexAugmentContainerReader = + mockReader(Ids.COMPLEX_AUGMENT_CONTAINER_ID, this::readComplexAugmentContainer, ComplexAugmentContainerBuilder.class); + // 2 + // Noop reader(no real attributes) + private Reader containerWithListReader = + mockReader(Ids.CONTAINER_WITH_LIST_ID, HoneycombReadInfraTest::noopRead, ContainerWithListBuilder.class); + // 2.1 + private ListReader listInContainerReader = + mockListReader(Ids.LIST_IN_CONTAINER_ID, this::readListInContainer, this::getListInContainerIds, + ListInContainerBuilder.class); + // 2.1.1 + private Reader containerInListReader = + mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class); + // 2.1.1.1 + private ListReader nestedListReader = + mockListReader(Ids.NESTED_LIST_ID, this::readNestedList, this::getNestedListIds, + NestedListBuilder.class); + + @Override + void postSetup() { + initReaderRegistry(); + } + + private void initReaderRegistry() { + registry = new CompositeReaderRegistryBuilder() + .add(containerWithListReader) // 2 + .addBefore(simpleContainerReader, Ids.CONTAINER_WITH_LIST_ID) // 1 + .add(simpleAugmentReader) // 1.1 + .addAfter(complexAugmentReader, Ids.SIMPLE_AUGMENT_ID) // 1.2 + .add(complexAugmentContainerReader) // 1.2.1 + .add(listInContainerReader) // 2.1 + .add(containerInListReader) // 2.1.1 + .addBefore(nestedListReader, Ids.SIMPLE_AUGMENT_ID) // 2.1.1.1 - Before relationship should be ignored + .build(); + } + + private Reader[] getOrderedReaders() { + return new Reader[]{simpleContainerReader, // 1 + simpleAugmentReader, // 1.1 + complexAugmentReader, // 1.2 + complexAugmentContainerReader, // 1.2.1 + containerWithListReader, // 2 + listInContainerReader, // 2.1 + containerInListReader, // 2.1.1 + nestedListReader}; // 2.1.1.1 + } + + @Test + public void testReadAll() throws Exception { + final ReadableDataTreeDelegator readableDataTreeDelegator = + new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker); + final CheckedFuture>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> + read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY); + + final Multimap, ? extends DataObject> readAll = + toBinding(read.checkedGet().get()); + assertThat(readAll.size(), is(2)); + assertTrue(readAll.containsKey(Ids.CONTAINER_WITH_LIST_ID)); + assertEquals(readContainerWithList(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get()); + assertTrue(readAll.containsKey(Ids.SIMPLE_CONTAINER_ID)); + assertEquals(readSimpleContainer(), readAll.get(Ids.SIMPLE_CONTAINER_ID).stream().findFirst().get()); + + final Reader[] ordered = getOrderedReaders(); + final InOrder inOrder = inOrder(ordered); + + final List listInContainerKeys = getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx); + + verifyRootReader(inOrder, simpleContainerReader, Ids.SIMPLE_CONTAINER_ID, SimpleContainerBuilder.class); + verifyLeafChildReader(inOrder, simpleAugmentReader, Ids.SIMPLE_AUGMENT_ID); + verifyCompositeChildReader(inOrder, complexAugmentReader, ComplexAugmentBuilder.class, Ids.COMPLEX_AUGMENT_ID); + verifyLeafChildReader(inOrder, complexAugmentContainerReader, Ids.COMPLEX_AUGMENT_CONTAINER_ID); + verifyRootReader(inOrder, containerWithListReader, Ids.CONTAINER_WITH_LIST_ID, ContainerWithListBuilder.class); + verifyCompositeListReader(inOrder, listInContainerReader, Ids.LIST_IN_CONTAINER_ID, + listInContainerKeys, ListInContainerBuilder.class); + + for (ListInContainerKey k : listInContainerKeys) { + final InstanceIdentifier id = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, k).child(ContainerInList.class); + verifyCompositeChildReader(inOrder, containerInListReader, ContainerInListBuilder.class, id); + final InstanceIdentifier nestedId = id.child(NestedList.class); + verifyLeafListReader(inOrder, nestedListReader, nestedId); + } + + for (Reader reader : ordered) { + verifyNoMoreReadInteractions(reader); + } + } + + private > void verifyNoMoreReadInteractions(final Reader reader) { + verify(reader, atLeastOnce()).getManagedDataObjectType(); + verifyNoMoreInteractions(reader); + } + + private > void verifyCompositeChildReader(final InOrder inOrder, + final Reader reader, + final Class builderCls, + final InstanceIdentifier id) + throws ReadFailedException { + verifyRootReader(inOrder, reader, id, builderCls); + verify(reader, atLeastOnce()).merge(any(Builder.class), any(id.getTargetType())); + } + + private , K extends Identifier, B extends Builder> void verifyCompositeListReader( + final InOrder inOrder, + final ListReader reader, + final InstanceIdentifier id, + final List keys, + final Class builderCls) + throws ReadFailedException { + + // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method + inOrder.verify(reader).getAllIds(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class)); + keys.stream() + .map(k -> RWUtils.replaceLastInId(id, RWUtils.getCurrentIdItem(id, k))) + .forEach(keyedId -> { + try { + verify(reader).getBuilder(keyedId); + verify(reader).readCurrentAttributes(eq(keyedId), any(builderCls), any(ReadContext.class)); + } catch (ReadFailedException e) { throw new RuntimeException(e); } + }); + verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType())); + } + + private , K extends Identifier, B extends Builder> void verifyLeafListReader( + final InOrder inOrder, + final ListReader reader, + final InstanceIdentifier id) + throws ReadFailedException { + + // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method + inOrder.verify(reader).readList(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class)); + verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType())); + } + + private > void verifyRootReader(final InOrder inOrder, + final Reader reader, + final InstanceIdentifier id, + final Class builderCls) + throws ReadFailedException { + inOrder.verify(reader).readCurrentAttributes(eq(id), any(builderCls), any(ReadContext.class)); + verify(reader).getBuilder(id); + } + + + private > void verifyLeafChildReader(final InOrder inOrder, + final Reader reader, + final InstanceIdentifier... id) + throws ReadFailedException { + for (InstanceIdentifier singleId : id) { + inOrder.verify(reader).read(eq(singleId), any(ReadContext.class)); + verify(reader, atLeastOnce()).merge(any(Builder.class), any(singleId.getTargetType())); + } + } + + static > Reader mockReader(InstanceIdentifier id, + CurrentAttributesReader currentAttributesReader, + Class builderClass) { + final ReflexiveReader reflex = new ReflexiveReader(id, builderClass) { + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + currentAttributesReader.readCurrentAttributes(id, builder, ctx); + } + }; + + // Simple spy on top of this reflexive reader cannot be used, spy also checks protected methods for invocation + // but those cannot be verified by mockito + final Reader mock = mock(Reader.class); + try { + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock) + .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType())); + doReturn(id).when(mock).getManagedDataObjectType(); + doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return mock; + } + + static , K extends Identifier, B extends Builder> ListReader mockListReader( + InstanceIdentifier id, + CurrentAttributesReader currentAttributesReader, + ListKeysReader listKeysReader, + Class builderClass) { + + ListReader reflex = new GenericListReader<>(id, + new ReflexiveListReaderCustomizer(id.getTargetType(), builderClass) { + + @Nonnull + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, @Nonnull final ReadContext context) + throws ReadFailedException { + return listKeysReader.getAllIds(id, context); + } + + @Override + public void readCurrentAttributes(final InstanceIdentifier id, final B builder, + final ReadContext context) + throws ReadFailedException { + currentAttributesReader.readCurrentAttributes(id, builder, context); + } + }); + + final ListReader mock = mock(ListReader.class /*, withSettings().verboseLogging()*/); + try { + // not using eq(id) instead using any(InstanceIdentifier.class) due to InstanceIdentifier.equals weird behavior + // with wildcarded instance identifiers for lists + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock) + .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType())); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), anyListOf(id.getTargetType())); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).getAllIds(any(InstanceIdentifier.class), any(ReadContext.class)); + doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).readList(any(InstanceIdentifier.class), any(ReadContext.class)); + doReturn(id).when(mock).getManagedDataObjectType(); + doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return mock; + } + + private static > Object reflexiveAnswer(final Reader instance, + final InvocationOnMock i) { + try { + return i.getMethod().invoke(instance, i.getArguments()); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new MockitoConfigurationException("Unable to invoke stubbed method invocation: " + i + " on " + instance); + } + } + + @FunctionalInterface + interface CurrentAttributesReader> { + void readCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final B builder, + @Nonnull final ReadContext ctx); + } + + @FunctionalInterface + interface ListKeysReader, K extends Identifier> { + List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext ctx); + } + + + private void readSimpleContainer(final InstanceIdentifier id, + final SimpleContainerBuilder b, + final ReadContext readContext) { + b.setSimpleContainerName("simple"); + } + + private SimpleContainer readSimpleContainer() { + final SimpleContainerBuilder b = new SimpleContainerBuilder(); + readSimpleContainer(Ids.SIMPLE_CONTAINER_ID, b, ctx); + b.addAugmentation(SimpleAugment.class, readSimpleAugment()); + b.addAugmentation(ComplexAugment.class, + new ComplexAugmentBuilder().setComplexAugmentContainer(readComplexAugmentContainer()).build()); + return b.build(); + } + + private void readSimpleAugment(final InstanceIdentifier id, + final SimpleAugmentBuilder b, + final ReadContext readContext) { + b.setSimpleAugmentLeaf("simple augment"); + } + + private SimpleAugment readSimpleAugment() { + final SimpleAugmentBuilder b = new SimpleAugmentBuilder(); + readSimpleAugment(Ids.SIMPLE_AUGMENT_ID, b, ctx); + return b.build(); + } + + private void readComplexAugmentContainer(final InstanceIdentifier id, + final ComplexAugmentContainerBuilder b, + final ReadContext readContext) { + b.setSomeLeaf("nested container in augment"); + } + + private ComplexAugmentContainer readComplexAugmentContainer() { + final ComplexAugmentContainerBuilder b = new ComplexAugmentContainerBuilder(); + readComplexAugmentContainer(Ids.COMPLEX_AUGMENT_CONTAINER_ID, b, ctx); + return b.build(); + } + + private ContainerWithList readContainerWithList() { + final ContainerWithListBuilder b = new ContainerWithListBuilder(); + b.setListInContainer(readListInContainer()); + return b.build(); + } + + private List getListInContainerIds(final InstanceIdentifier id, + final ReadContext readContext) { + return Lists.newArrayList(1L, 2L) + .stream() + .map(ListInContainerKey::new) + .collect(Collectors.toList()); + } + + private List getNestedListIds(final InstanceIdentifier id, + final ReadContext readContext) { + return Lists.newArrayList("one", "two") + .stream() + .map(NestedListKey::new) + .collect(Collectors.toList()); + } + + private void readListInContainer(final InstanceIdentifier id, + final ListInContainerBuilder b, + final ReadContext readContext) { + final ListInContainerKey key = id.firstKeyOf(ListInContainer.class); + b.setId(key.getId()); + final ContainerInListBuilder cilBuilder = new ContainerInListBuilder(); + readContainerInList(id.child(ContainerInList.class), cilBuilder, readContext); + b.setContainerInList(cilBuilder.build()); + } + + private void readNestedList(final InstanceIdentifier id, + final NestedListBuilder b, + final ReadContext readContext) { + final NestedListKey key = id.firstKeyOf(NestedList.class); + b.setNestedId(key.getNestedId()); + b.setNestedName(key.getNestedId() + "name"); + } + + private void readContainerInList(final InstanceIdentifier id, + final ContainerInListBuilder b, + final ReadContext readContext) { + b.setName(id.firstKeyOf(ListInContainer.class).getId().toString()); + b.setNestedList(getNestedListIds(Ids.NESTED_LIST_ID, ctx).stream() + .map(key -> id.child(NestedList.class, key)) + .map(nestedId -> { + final NestedListBuilder nestedB = new NestedListBuilder(); + readNestedList(nestedId, nestedB, readContext); + return nestedB.build(); + }) + .collect(Collectors.toList())); + } + + private List readListInContainer() { + return getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx).stream() + .map(key -> Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, key)) + .map(id -> { + final ListInContainerBuilder b = new ListInContainerBuilder(); + readListInContainer(id, b, ctx); + return b.build(); + }).collect(Collectors.toList()); + } + + static > void noopRead(final InstanceIdentifier id, final B b, + final ReadContext readContext) { + // NOOP + } + +} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java new file mode 100644 index 000000000..8faeb84b6 --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.data.impl; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.translate.impl.read.GenericListReader; +import io.fd.honeycomb.v3po.translate.read.ListReader; +import io.fd.honeycomb.v3po.translate.read.ReadContext; +import io.fd.honeycomb.v3po.translate.read.ReadFailedException; +import io.fd.honeycomb.v3po.translate.read.Reader; +import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; +import io.fd.honeycomb.v3po.translate.util.read.ReflexiveListReaderCustomizer; +import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.junit.Test; +import org.mockito.Mock; +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.ContainerWithListBuilder; +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.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +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.ContainerInListBuilder; +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.NormalizedNode; + +public class HoneycombSubtreeReadInfraTest extends AbstractInfraTest { + + @Mock + private ReadContext ctx; + private ReaderRegistry registry; + + private Reader containerWithListReader = + HoneycombReadInfraTest.mockReader(Ids.CONTAINER_WITH_LIST_ID, this::readSubtree, ContainerWithListBuilder.class); + + private ListReader listInContainerReader = + new GenericListReader<>(Ids.LIST_IN_CONTAINER_ID, + new ReflexiveListReaderCustomizer(Ids.LIST_IN_CONTAINER_ID.getTargetType(), ListInContainerBuilder.class) { + + @Nonnull + @Override + public List getAllIds(@Nonnull final InstanceIdentifier id, + @Nonnull final ReadContext context) + throws ReadFailedException { + // FIXME this is the only way of extending subtree reader via its list child + // Reflexive list reader has to be used in place of the list(managed by subtree reader perent) + // to enable further children readers. However, it will not work out of the box, because + // reflexive list reader has no way to tell what are the IDs to correctly invoke its children. + // Only way is to override the getAllIds method in reflexive reader and return the same list + // as parent used (this can be done using cache or repeated dump call) + return Lists.newArrayList(new ListInContainerKey(1L), new ListInContainerKey(2L)); + } + + @Override + public void readCurrentAttributes(final InstanceIdentifier id, + final ListInContainerBuilder builder, + final ReadContext context) throws ReadFailedException { + super.readCurrentAttributes(id, builder, context); + builder.setKey(id.firstKeyOf(ListInContainer.class)); + } + }); + + private Reader containerInListReader = + HoneycombReadInfraTest.mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class); + + // TODO Test subtree readers especially composite structure where readers are under subtree reader + + @Override + void postSetup() { + initReaderRegistry(); + } + + private void initReaderRegistry() { + registry = new CompositeReaderRegistryBuilder() + // Subtree reader handling its child list + .subtreeAdd(Sets.newHashSet(Ids.LIST_IN_CONTAINER_ID), containerWithListReader) + // Reflexive + .add(listInContainerReader) + .add(containerInListReader) + .build(); + } + + @Test + public void testReadAll() throws Exception { + final ReadableDataTreeDelegator readableDataTreeDelegator = + new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker); + final CheckedFuture>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> + read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY); + + final Multimap, ? extends DataObject> readAll = + toBinding(read.checkedGet().get()); + assertThat(readAll.size(), is(1)); + assertEquals(readEntireSubtree(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get()); + } + + private void readSubtree(final InstanceIdentifier id, + final ContainerWithListBuilder b, + final ReadContext readContext) { + b.setListInContainer(Lists.newArrayList(1L, 2L).stream() + .map(l -> new ListInContainerBuilder().setId(l).build()) + .collect(Collectors.toList())); + } + + private ContainerWithList readEntireSubtree() { + final ContainerWithListBuilder b = new ContainerWithListBuilder(); + b.setListInContainer(Lists.newArrayList(1L, 2L).stream() + .map(l -> { + final ContainerInListBuilder containerInListBuilder = new ContainerInListBuilder(); + readContainerInList( + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey(l)).child(ContainerInList.class), + containerInListBuilder, + ctx); + return new ListInContainerBuilder().setId(l).setContainerInList(containerInListBuilder.build()).build(); + }) + .collect(Collectors.toList())); + return b.build(); + } + + private void readContainerInList(final InstanceIdentifier id, + final ContainerInListBuilder b, + final ReadContext readContext) { + b.setName(id.firstKeyOf(ListInContainer.class).getId().toString()); + } +} diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java new file mode 100644 index 000000000..b3a06ec46 --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.data.impl; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.translate.util.write.registry.FlatWriterRegistryBuilder; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.write.Writer; +import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.mockito.InOrder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder; +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.ContainerWithChoiceBuilder; +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.ContainerWithListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.Choice; +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.choice.choice.c3.C3Builder; +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.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +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.ContainerInListBuilder; +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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGroupingBuilder; +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 honeycomb writes from data tree up to mocked writers. + */ +public class HoneycombWriteInfraTest extends AbstractInfraTest { + + private TipProducingDataTree dataTree; + private WriterRegistry writerRegistry; + + Writer simpleContainerWriter = mockWriter(Ids.SIMPLE_CONTAINER_ID); + Writer complexAugmentWriter = mockWriter(Ids.COMPLEX_AUGMENT_ID); + Writer complexAugmentContainerWriter = mockWriter(Ids.COMPLEX_AUGMENT_CONTAINER_ID); + Writer simpleAugmentWriter = mockWriter(Ids.SIMPLE_AUGMENT_ID); + + Writer containerWithListWriter = mockWriter(Ids.CONTAINER_WITH_LIST_ID); + Writer listInContainerWriter = mockWriter(Ids.LIST_IN_CONTAINER_ID); + Writer containerInListWriter = mockWriter(Ids.CONTAINER_IN_LIST_ID); + Writer nestedListWriter = mockWriter(Ids.NESTED_LIST_ID); + + Writer containerWithChoiceWriter = mockWriter(Ids.CONTAINER_WITH_CHOICE_ID); + Writer containerFromGroupingWriter = mockWriter(Ids.CONTAINER_FROM_GROUPING_ID); + Writer c3Writer = mockWriter(Ids.C3_ID); + + + private static Writer mockWriter(final InstanceIdentifier id) { + final Writer mock = (Writer) 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(complexAugmentWriter) // unordered + .add(nestedListWriter) // 6 + .addAfter(listInContainerWriter, Ids.NESTED_LIST_ID) // 7 + .addAfter(containerInListWriter, Ids.LIST_IN_CONTAINER_ID) // 8 + .addAfter(containerWithListWriter, Ids.CONTAINER_IN_LIST_ID) // 9 + .addBefore(containerFromGroupingWriter, Ids.NESTED_LIST_ID) // 5 + .addBefore(containerWithChoiceWriter, Ids.CONTAINER_FROM_GROUPING_ID) // 4 + .addBefore(simpleContainerWriter, Ids.CONTAINER_WITH_CHOICE_ID) // 3 + .addBefore(c3Writer, Ids.SIMPLE_CONTAINER_ID) // 2 + .addBefore(simpleAugmentWriter, Ids.SIMPLE_CONTAINER_ID) // 2 + .addBefore(complexAugmentContainerWriter, Sets.newHashSet(Ids.C3_ID, Ids.SIMPLE_AUGMENT_ID)) // 1 + .build(); + } + + @Test + public void testWriteEmptyNonPresenceContainer() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final DataModification dataModification = modifiableDataTreeDelegator.newModification(); + final SimpleContainer data = new SimpleContainerBuilder() + .build(); + + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, data); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + + dataModification.commit(); + + verify(simpleContainerWriter).getManagedDataObjectType(); + verifyNoMoreInteractions(simpleContainerWriter); + } + + @Test + public void testWriteEverything() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final DataModification dataModification = modifiableDataTreeDelegator.newModification(); + // Now write everything we can + writeSimpleContainer(dataModification); + writeContainerWithChoice(dataModification); + writeContainerWithList(dataModification); + dataModification.commit(); + + final Writer[] orderedWriters = getOrderedWriters(); + final InOrder inOrder = inOrder(orderedWriters); + verifyOrderedWrites(orderedWriters, inOrder); + } + + private void verifyOrderedWrites(final Writer[] orderedWriters, final InOrder inOrder) + throws io.fd.honeycomb.v3po.translate.write.WriteFailedException { + // TODO Modifications are not produced for nodes that do not contain any actual leaves (except when choice is a child) do we need those triggers ? + // Unordered + // verify(complexAugmentWriter).update(eq(COMPLEX_AUGMENT_ID), eq(null), eq(getComplexAugment()), any(WriteContext.class)); + // 1 + inOrder.verify(complexAugmentContainerWriter) + .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(null), eq(getComplexAugmentContainer()), any(WriteContext.class)); + // 2 + inOrder.verify(c3Writer) + .update(eq(Ids.C3_ID), eq(null), eq(getC3()), any(WriteContext.class)); + // 2 + verify(simpleAugmentWriter) + .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(null), eq(getSimpleAugment()), any(WriteContext.class)); + // 3 + inOrder.verify(simpleContainerWriter) + .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(null), eq(getSimpleContainer()), any(WriteContext.class)); + // 4 + inOrder.verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(getContainerWithChoiceWithComplexCase()), any(WriteContext.class)); + // 5 + inOrder.verify(containerFromGroupingWriter) + .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(null), eq(getContainerFromGrouping()), any(WriteContext.class)); + + final KeyedInstanceIdentifier keyedListInContainer1 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1)); + final KeyedInstanceIdentifier keyedNestedList1 = + keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1")); + final KeyedInstanceIdentifier keyedListInContainer2 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2)); + final KeyedInstanceIdentifier keyedNestedList2 = + keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2")); + + // 6 - two items + inOrder.verify(nestedListWriter) + .update(eq(keyedNestedList1), eq(null), eq(getSingleNestedList("1")), any(WriteContext.class)); + verify(nestedListWriter) + .update(eq(keyedNestedList2), eq(null), eq(getSingleNestedList("2")), any(WriteContext.class)); + + // 7 - two items + inOrder.verify(listInContainerWriter) + .update(eq(keyedListInContainer1), eq(null), eq(getSingleListInContainer((long)1)), any(WriteContext.class)); + verify(listInContainerWriter) + .update(eq(keyedListInContainer2), eq(null), eq(getSingleListInContainer((long)2)), any(WriteContext.class)); + + // 8 + inOrder.verify(containerInListWriter) + .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(null), eq(getContainerInList("1")), any(WriteContext.class)); + verify(containerInListWriter) + .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(null), eq(getContainerInList("2")), any(WriteContext.class)); + + // 9 - Ignored because the container has no leaves, only complex child nodes + // inOrder.verify(containerWithListWriter) + // .update(eq(CONTAINER_WITH_LIST_ID), eq(null), eq(getContainerWithList()), any(WriteContext.class)); + + for (Writer orderedWriter : orderedWriters) { + verify(orderedWriter).getManagedDataObjectType(); + verifyNoMoreInteractions(orderedWriter); + } + } + + private Writer[] getOrderedWriters() { + return new Writer[]{complexAugmentWriter, // Unordered + complexAugmentContainerWriter, // 1 + c3Writer, // 2 + simpleAugmentWriter, // 2 + simpleContainerWriter, // 3 + containerWithChoiceWriter, // 4 + containerFromGroupingWriter, // 5 + nestedListWriter, // 6 + listInContainerWriter, // 7 + containerInListWriter, // 8 + containerWithListWriter}; + } + + @Test + public void testDeletes() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + DataModification dataModification = modifiableDataTreeDelegator.newModification(); + // Now write everything we can + writeSimpleContainer(dataModification); + writeContainerWithChoice(dataModification); + writeContainerWithList(dataModification); + dataModification.commit(); + // Verify writes to be able to verifyNoMore interactions at the end + verifyOrderedWrites(getOrderedWriters(), inOrder(getOrderedWriters())); + + dataModification = modifiableDataTreeDelegator.newModification(); + deleteSimpleContainer(dataModification); + deleteContainerWithChoice(dataModification); + deleteContainerWithList(dataModification); + dataModification.commit(); + + final Writer[] orderedWriters = getOrderedWriters(); + Collections.reverse(Arrays.asList(orderedWriters)); + final InOrder inOrder = inOrder(orderedWriters); + + final KeyedInstanceIdentifier keyedListInContainer1 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1)); + final KeyedInstanceIdentifier keyedNestedList1 = + keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1")); + final KeyedInstanceIdentifier keyedListInContainer2 = + Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2)); + final KeyedInstanceIdentifier keyedNestedList2 = + keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2")); + + // Deletes are handled in reverse order + // 1 + inOrder.verify(containerInListWriter) + .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(getContainerInList("1")), eq(null), any(WriteContext.class)); + verify(containerInListWriter) + .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(getContainerInList("2")), eq(null), any(WriteContext.class)); + + // 2 + inOrder.verify(listInContainerWriter) + .update(eq(keyedListInContainer1), eq(getSingleListInContainer((long)1)), eq(null), any(WriteContext.class)); + verify(listInContainerWriter) + .update(eq(keyedListInContainer2), eq(getSingleListInContainer((long)2)), eq(null), any(WriteContext.class)); + + // 3 + inOrder.verify(nestedListWriter) + .update(eq(keyedNestedList1), eq(getSingleNestedList("1")), eq(null), any(WriteContext.class)); + verify(nestedListWriter) + .update(eq(keyedNestedList2), eq(getSingleNestedList("2")), eq(null), any(WriteContext.class)); + // 4 + inOrder.verify(containerFromGroupingWriter) + .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(getContainerFromGrouping()), eq(null), any(WriteContext.class)); + // 5 + inOrder.verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(getContainerWithChoiceWithComplexCase()), eq(null), any(WriteContext.class)); + // 6 + inOrder.verify(simpleContainerWriter) + .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(getSimpleContainer()), eq(null), any(WriteContext.class)); + // 7 + verify(simpleAugmentWriter) + .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(getSimpleAugment()), eq(null), any(WriteContext.class)); + // 8 + inOrder.verify(c3Writer) + .update(eq(Ids.C3_ID), eq(getC3()), eq(null), any(WriteContext.class)); + // 9 + inOrder.verify(complexAugmentContainerWriter) + .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(getComplexAugmentContainer()), eq(null), any(WriteContext.class)); + + for (Writer orderedWriter : orderedWriters) { + verify(orderedWriter).getManagedDataObjectType(); + verifyNoMoreInteractions(orderedWriter); + } + } + + private void writeContainerWithList(final DataModification dataModification) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.CONTAINER_WITH_LIST_ID, getContainerWithList()); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private void deleteContainerWithList(final DataModification dataModification) { + dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_LIST_ID)); + } + + private ContainerWithList getContainerWithList() { + return new ContainerWithListBuilder() + .setListInContainer(getListInContainer((long)1, (long)2)) + .build(); + } + + private List getListInContainer(final Long... keys) { + final ArrayList objects = Lists.newArrayList(); + for (Long key : keys) { + objects.add(getSingleListInContainer(key)); + } + return objects; + } + + private ListInContainer getSingleListInContainer(final Long key) { + return new ListInContainerBuilder() + .setId(key) + .setContainerInList(getContainerInList(Long.toString(key))) + .build(); + } + + private ContainerInList getContainerInList(String... nestedKeys) { + return new ContainerInListBuilder() + .setName("inlist") + .setNestedList(getNestedList(nestedKeys)) + .build(); + } + + private List getNestedList(String... keys) { + final ArrayList nestedList = new ArrayList<>(); + for (String key : keys) { + nestedList.add(getSingleNestedList(key)); + } + return nestedList; + } + + private NestedList getSingleNestedList(final String key) { + return new NestedListBuilder() + .setNestedId(key) + .setNestedName(key) + .build(); + } + + private void writeContainerWithChoice(final DataModification dataModification) { + writeContainerWithChoice(dataModification, getContainerWithChoiceWithComplexCase()); + } + + + private void deleteContainerWithChoice(final DataModification dataModification) { + dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_CHOICE_ID)); + } + + private void writeContainerWithChoice(final DataModification dataModification, + final ContainerWithChoice containerWithChoice) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.CONTAINER_WITH_CHOICE_ID, containerWithChoice); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private ContainerWithChoice getContainerWithChoiceWithComplexCase() { + return new ContainerWithChoiceBuilder() + .setLeafFromGrouping("fromG") + .setName("name") + .setContainerFromGrouping(getContainerFromGrouping()) + .setChoice(getComplexCase()) + .build(); + } + + private Choice getComplexCase() { + return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.C3Builder() + .setC3(getC3()) + .build(); + } + + private C3 getC3() { + return new C3Builder() + .setName("c3") + .build(); + } + + private void writeContainerFromGrouping(final DataModification dataModification) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.CONTAINER_FROM_GROUPING_ID, getContainerFromGrouping()); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private ContainerFromGrouping getContainerFromGrouping() { + return new ContainerFromGroupingBuilder() + .setLeafInContainerFromGrouping(111) + .build(); + } + + + private void writeSimpleContainer(final DataModification dataModification) { + final Map.Entry> normalizedNode = + serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, getSimpleContainer()); + dataModification.write(normalizedNode.getKey(), normalizedNode.getValue()); + } + + private void deleteSimpleContainer(final DataModification dataModification) { + final YangInstanceIdentifier yangId = + serializer.toYangInstanceIdentifier(Ids.SIMPLE_CONTAINER_ID); + dataModification.delete(yangId); + } + + private SimpleContainer getSimpleContainer() { + return new SimpleContainerBuilder() + .setSimpleContainerName("n") + .addAugmentation(SimpleAugment.class, getSimpleAugment()) + .addAugmentation(ComplexAugment.class, getComplexAugment()) + .build(); + } + + private ComplexAugment getComplexAugment() { + return new ComplexAugmentBuilder() + .setComplexAugmentContainer(getComplexAugmentContainer()) + .build(); + } + + private ComplexAugmentContainer getComplexAugmentContainer() { + return new ComplexAugmentContainerBuilder().setSomeLeaf("s").build(); + } + + private SimpleAugment getSimpleAugment() { + return new SimpleAugmentBuilder().setSimpleAugmentLeaf("a").build(); + } + + @Test + public void testWriteAndDeleteInTx() throws Exception { + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final DataModification dataModification = modifiableDataTreeDelegator.newModification(); + // Now write everything we can + writeSimpleContainer(dataModification); + deleteSimpleContainer(dataModification); + dataModification.commit(); + + verify(simpleContainerWriter).getManagedDataObjectType(); + // No modification + verifyNoMoreInteractions(simpleContainerWriter); + } + + @Test + public void testSubtreeWriter() throws Exception { + writerRegistry = new FlatWriterRegistryBuilder() + // Handles also container from grouping + .subtreeAdd(Sets.newHashSet(Ids.CONTAINER_FROM_GROUPING_ID), containerWithChoiceWriter) + .build(); + + final ModifiableDataTreeDelegator modifiableDataTreeDelegator = + new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker); + + final ContainerWithChoice containerWithChoice = + new ContainerWithChoiceBuilder().setContainerFromGrouping(getContainerFromGrouping()).build(); + + // Test write subtree node + DataModification dataModification = modifiableDataTreeDelegator.newModification(); + writeContainerFromGrouping(dataModification); + dataModification.commit(); + + verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType(); + verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(containerWithChoice), any(WriteContext.class)); + verifyNoMoreInteractions(containerWithChoiceWriter); + + // Test delete sub-node + dataModification = modifiableDataTreeDelegator.newModification(); + final ContainerWithChoice containerWithChoiceEmpty = new ContainerWithChoiceBuilder().build(); + deleteContainerFromGrouping(dataModification); + dataModification.commit(); + + verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType(); + verify(containerWithChoiceWriter) + .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(containerWithChoice), eq(containerWithChoiceEmpty), any(WriteContext.class)); + verifyNoMoreInteractions(containerWithChoiceWriter); + + // Test write with subtree node that's not handled by subtree writer + dataModification = modifiableDataTreeDelegator.newModification(); + writeContainerWithChoice(dataModification); + try { + dataModification.commit(); + fail("Missing writer for C3 should occur"); + } catch (IllegalArgumentException e) { + return; + } + } + + private void deleteContainerFromGrouping(final DataModification dataModification) { + dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_FROM_GROUPING_ID)); + } +} \ No newline at end of file diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java new file mode 100644 index 000000000..200f50c32 --- /dev/null +++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java @@ -0,0 +1,62 @@ +/* + * 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 org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment; +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.SimpleAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Instance identifiers referencing all complex nodes within IT test-model. + */ +interface Ids { + + // Simple container + // ORDER = 3 + InstanceIdentifier SIMPLE_CONTAINER_ID = InstanceIdentifier.create(SimpleContainer.class); + // 2 + InstanceIdentifier SIMPLE_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(SimpleAugment.class); + // UNORDERED + InstanceIdentifier COMPLEX_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(ComplexAugment.class); + // 1 + InstanceIdentifier COMPLEX_AUGMENT_CONTAINER_ID = COMPLEX_AUGMENT_ID.child(ComplexAugmentContainer.class); + // Container with list + // 9 + InstanceIdentifier CONTAINER_WITH_LIST_ID = InstanceIdentifier.create(ContainerWithList.class); + // 7 + InstanceIdentifier LIST_IN_CONTAINER_ID = CONTAINER_WITH_LIST_ID.child(ListInContainer.class); + // 8 + InstanceIdentifier CONTAINER_IN_LIST_ID = LIST_IN_CONTAINER_ID.child(ContainerInList.class); + // 6 + InstanceIdentifier NESTED_LIST_ID = CONTAINER_IN_LIST_ID.child(NestedList.class); + // Container with choice + // 4 + InstanceIdentifier CONTAINER_WITH_CHOICE_ID = InstanceIdentifier.create(ContainerWithChoice.class); + // 2 + InstanceIdentifier C3_ID = CONTAINER_WITH_CHOICE_ID.child(C3.class); + // 5 + InstanceIdentifier CONTAINER_FROM_GROUPING_ID = CONTAINER_WITH_CHOICE_ID.child(ContainerFromGrouping.class); +} diff --git a/infra/it/pom.xml b/infra/it/pom.xml new file mode 100644 index 000000000..e565f22df --- /dev/null +++ b/infra/it/pom.xml @@ -0,0 +1,51 @@ + + + + + io.fd.honeycomb.common + honeycomb-it-aggregator + 1.0.0-SNAPSHOT + honeycomb + pom + 4.0.0 + + 3.1.1 + + + test-model + it-test + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + + diff --git a/infra/it/test-model/pom.xml b/infra/it/test-model/pom.xml new file mode 100644 index 000000000..2f087edf9 --- /dev/null +++ b/infra/it/test-model/pom.xml @@ -0,0 +1,36 @@ + + + + + io.fd.honeycomb.common + api-parent + 1.0.0-SNAPSHOT + ../../../common/api-parent + + + 4.0.0 + io.fd.honeycomb + honeycomb-test-model + 1.0.0-SNAPSHOT + bundle + + + + org.opendaylight.mdsal.model + yang-ext + + + diff --git a/infra/it/test-model/src/main/yang/hc-test.yang b/infra/it/test-model/src/main/yang/hc-test.yang new file mode 100644 index 000000000..e13b5001f --- /dev/null +++ b/infra/it/test-model/src/main/yang/hc-test.yang @@ -0,0 +1,123 @@ +module hc-test { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:hc:test"; + prefix "hct"; + + revision "2015-01-05" { + description "Testing HC model with bindings"; + } + + import yang-ext { + prefix "ext"; + } + + // WRITE ORDER 3 + // READ ORDER 1 + container simple-container { + leaf simple-container-name { + type string; + } + } + + grouping some-attributes { + leaf leaf-from-grouping { + type string; + } + + // WRITE ORDER 5 + container container-from-grouping { + leaf leaf-in-container-from-grouping { + type int32; + } + } + } + + // WRITE ORDER 9 (no real attributes though) + // READ ORDER 2 + container container-with-list { + // WRITE ORDER 7 + // READ ORDER 2.1 + list list-in-container { + key "id"; + ordered-by "user"; + + leaf id { + type uint32; + } + + // WRITE ORDER 8 + // READ ORDER 2.1.1 + container container-in-list { + leaf name { + type string; + } + + // WRITE ORDER 6 + // READ ORDER 2.1.1.1 + list nested-list { + key "nested-id"; + ordered-by "user"; + + leaf nested-id { + type string; + } + + leaf nested-name { + type string; + } + } + } + } + } + + // WRITE ORDER 4 + container container-with-choice { + leaf name { + type string; + } + + uses some-attributes; + + choice choice { + leaf c1 { + type string; + } + + leaf c2 { + type string; + } + + // WRITE ORDER: 2 + container c3 { + leaf name { + type string; + } + } + } + } + + // WRITE ORDER: 2 + // READ ORDER 1.1 + augment "/hct:simple-container" { + ext:augment-identifier "simple-augment"; + + leaf simple-augment-leaf { + type string; + } + } + + // WRITE UNORDERED + // READ ORDER 1.2 + augment "/hct:simple-container" { + ext:augment-identifier "complex-augment"; + + // WRITE ORDER: 1 + // READ ORDER 1.2.1 + container complex-augment-container { + leaf some-leaf { + type string; + } + } + } + +} -- cgit 1.2.3-korg