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 --- .../honeycomb/v3po/data/impl/DataBrokerTest.java | 111 ++++++ .../data/impl/ModifiableDataTreeDelegatorTest.java | 269 +++++++++++++ .../v3po/data/impl/ModificationDiffTest.java | 427 +++++++++++++++++++++ .../data/impl/PersistingDataTreeAdapterTest.java | 69 ++++ .../v3po/data/impl/ReadOnlyTransactionTest.java | 68 ++++ .../v3po/data/impl/ReadWriteTransactionTest.java | 111 ++++++ .../data/impl/ReadableDataTreeDelegatorTest.java | 192 +++++++++ .../v3po/data/impl/WriteTransactionTest.java | 128 ++++++ 8 files changed, 1375 insertions(+) create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java create mode 100644 infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java (limited to 'infra/data-impl/src/test/java/io/fd/honeycomb') diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java new file mode 100644 index 000000000..55b92b50b --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java @@ -0,0 +1,111 @@ +/* + * 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.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.ModifiableDataManager; +import io.fd.honeycomb.v3po.data.DataModification; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension; +import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +public class DataBrokerTest { + + @Mock + private ReadableDataManager operationalData; + @Mock + private ModifiableDataManager confiDataTree; + @Mock + private DataModification configSnapshot; + private DataBroker broker; + + @Before + public void setUp() { + initMocks(this); + when(confiDataTree.newModification()).thenReturn(configSnapshot); + broker = DataBroker.create(confiDataTree, operationalData); + } + + @Test + public void testNewReadWriteTransaction() { + final DOMDataReadWriteTransaction readWriteTx = broker.newReadWriteTransaction(); + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + readWriteTx.read(LogicalDatastoreType.CONFIGURATION, path); + + // verify that read and write transactions use the same config snapshot + verify(configSnapshot).read(path); + verify(confiDataTree).newModification(); + } + + @Test + public void testNewWriteOnlyTransaction() { + final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction(); + + // verify that write transactions use config snapshot + verify(confiDataTree).newModification(); + } + + @Test + public void testNewReadOnlyTransaction() { + final DOMDataReadOnlyTransaction readTx = broker.newReadOnlyTransaction(); + + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + readTx.read(LogicalDatastoreType.CONFIGURATION, path); + + // verify that read transactions use config snapshot + verify(configSnapshot).read(path); + } + + @Test(expected = UnsupportedOperationException.class) + public void testRegisterDataChangeListener() { + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + final DOMDataChangeListener listener = mock(DOMDataChangeListener.class); + broker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, listener, + AsyncDataBroker.DataChangeScope.BASE); + } + + @Test(expected = UnsupportedOperationException.class) + public void testCreateTransactionChain() { + final TransactionChainListener listener = mock(TransactionChainListener.class); + broker.createTransactionChain(listener); + } + + @Test + public void testGetSupportedExtensions() { + final Map, DOMDataBrokerExtension> supportedExtensions = + broker.getSupportedExtensions(); + assertTrue(supportedExtensions.isEmpty()); + } + + +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java new file mode 100644 index 000000000..915d738e9 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java @@ -0,0 +1,269 @@ +/* + * 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.hasItem; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.base.Optional; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.v3po.data.DataModification; +import io.fd.honeycomb.v3po.translate.TranslationException; +import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate; +import io.fd.honeycomb.v3po.translate.write.WriteContext; +import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry; +import java.util.AbstractMap; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; + +public class ModifiableDataTreeDelegatorTest { + + @Mock + private WriterRegistry writer; + @Mock + private BindingNormalizedNodeSerializer serializer; + private DataTree dataTree; + @Mock + private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification; + @Mock + private DataBroker contextBroker; + @Mock + private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx; + + private ModifiableDataTreeManager configDataTree; + + static final InstanceIdentifier DEFAULT_ID = InstanceIdentifier.create(DataObject.class); + static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class); + + @Before + public void setUp() throws Exception { + initMocks(this); + dataTree = ModificationDiffTest.getDataTree(); + when(contextBroker.newReadWriteTransaction()).thenReturn(tx); + when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null)); + + when(serializer.fromYangInstanceIdentifier(any(YangInstanceIdentifier.class))).thenReturn(((InstanceIdentifier) DEFAULT_ID)); + final Map.Entry, DataObject> parsed = new AbstractMap.SimpleEntry<>(DEFAULT_ID, DEFAULT_DATA_OBJECT); + when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class))).thenReturn(parsed); + + configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker); + } + + @Test + public void testRead() throws Exception { + final ContainerNode topContainer = ModificationDiffTest.getTopContainer("topContainer"); + ModificationDiffTest.addNodeToTree(dataTree, topContainer, ModificationDiffTest.TOP_CONTAINER_ID); + final CheckedFuture>, ReadFailedException> read = + configDataTree.read(ModificationDiffTest.TOP_CONTAINER_ID); + final CheckedFuture>, ReadFailedException> read2 = + configDataTree.newModification().read(ModificationDiffTest.TOP_CONTAINER_ID); + final Optional> normalizedNodeOptional = read.get(); + final Optional> normalizedNodeOptional2 = read2.get(); + + assertEquals(normalizedNodeOptional, normalizedNodeOptional2); + assertTrue(normalizedNodeOptional.isPresent()); + assertEquals(topContainer, normalizedNodeOptional.get()); + assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional); + } + + @Test + public void testCommitSuccessful() throws Exception { + final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); + + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + + final Multimap, DataObjectUpdate> map = HashMultimap.create(); + map.put(DEFAULT_ID, DataObjectUpdate.create(DEFAULT_ID, DEFAULT_DATA_OBJECT, DEFAULT_DATA_OBJECT)); + verify(writer).update(eq(new WriterRegistry.DataObjectUpdates(map, ImmutableMultimap.of())), any(WriteContext.class)); + assertEquals(nestedList, dataTree.takeSnapshot().readNode(ModificationDiffTest.NESTED_LIST_ID).get()); + } + + private static DataObject mockDataObject(final String name, final Class classToMock) { + final DataObject dataBefore = mock(classToMock, name); + doReturn(classToMock).when(dataBefore).getImplementedInterface(); + return dataBefore; + } + + @Test + public void testCommitUndoSuccessful() throws Exception { + final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); + + // Fail on update: + final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class); + final TranslationException failedOnUpdateException = new TranslationException("update failed"); + doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException)) + .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + // Run the test + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + fail("WriterRegistry.BulkUpdateException was expected"); + } catch (WriterRegistry.BulkUpdateException e) { + verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + assertThat(e.getFailedIds(), hasItem(DEFAULT_ID)); + verify(reverter).revert(); + assertEquals(failedOnUpdateException, e.getCause()); + } + } + + @Test + public void testCommitUndoFailed() throws Exception { + final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue"); + + // Fail on update: + final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class); + final TranslationException failedOnUpdateException = new TranslationException("update failed"); + doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException)) + .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + // Fail on revert: + final TranslationException failedOnRevertException = new TranslationException("revert failed"); + doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException)) + .when(reverter).revert(); + + try { + // Run the test + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + fail("WriterRegistry.Reverter.RevertFailedException was expected"); + } catch (WriterRegistry.Reverter.RevertFailedException e) { + verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + verify(reverter).revert(); + assertEquals(failedOnRevertException, e.getCause()); + } + } + + private abstract static class DataObject1 implements DataObject {} + private abstract static class DataObject2 implements DataObject {} + private abstract static class DataObject3 implements DataObject {} + + @Test + public void testToBindingAware() throws Exception { + when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null); + + final Map biNodes = new HashMap<>(); + // delete + final QName nn1 = QName.create("namespace", "nn1"); + final YangInstanceIdentifier yid1 = mockYid(nn1); + final InstanceIdentifier iid1 = mockIid(yid1, DataObject1.class); + final NormalizedNode nn1B = mockNormalizedNode(nn1); + final DataObject1 do1B = mockDataObject(yid1, iid1, nn1B, DataObject1.class); + biNodes.put(yid1, ModificationDiff.NormalizedNodeUpdate.create(yid1, nn1B, null)); + + // create + final QName nn2 = QName.create("namespace", "nn1"); + final YangInstanceIdentifier yid2 = mockYid(nn2); + final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);; + final NormalizedNode nn2A = mockNormalizedNode(nn2); + final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class); + biNodes.put(yid2, ModificationDiff.NormalizedNodeUpdate.create(yid2, null, nn2A)); + + // update + final QName nn3 = QName.create("namespace", "nn1"); + final YangInstanceIdentifier yid3 = mockYid(nn3); + final InstanceIdentifier iid3 = mockIid(yid3, DataObject3.class); + final NormalizedNode nn3B = mockNormalizedNode(nn3); + final DataObject3 do3B = mockDataObject(yid3, iid3, nn3B, DataObject3.class); + final NormalizedNode nn3A = mockNormalizedNode(nn3); + final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class);; + biNodes.put(yid3, ModificationDiff.NormalizedNodeUpdate.create(yid3, nn3B, nn3A)); + + final WriterRegistry.DataObjectUpdates dataObjectUpdates = + ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer); + + assertThat(dataObjectUpdates.getDeletes().size(), is(1)); + assertThat(dataObjectUpdates.getDeletes().keySet(), hasItem(((InstanceIdentifier) iid1))); + assertThat(dataObjectUpdates.getDeletes().values(), hasItem( + ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid1, do1B, null)))); + + assertThat(dataObjectUpdates.getUpdates().size(), is(2)); + assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier) iid2, (InstanceIdentifier) iid3)); + assertThat(dataObjectUpdates.getUpdates().values(), hasItems( + DataObjectUpdate.create(iid2, null, do2A), + DataObjectUpdate.create(iid3, do3B, do3A))); + + assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3)); + } + + private D mockDataObject(final YangInstanceIdentifier yid1, + final InstanceIdentifier iid1, + final NormalizedNode nn1B, + final Class type) { + final D do1B = mock(type); + when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B)); + return do1B; + } + + private NormalizedNode mockNormalizedNode(final QName nn1) { + final NormalizedNode nn1B = mock(NormalizedNode.class); + when(nn1B.getNodeType()).thenReturn(nn1); + return nn1B; + } + + private InstanceIdentifier mockIid(final YangInstanceIdentifier yid1, + final Class type) { + final InstanceIdentifier iid1 = InstanceIdentifier.create(type); + when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1); + return iid1; + } + + private YangInstanceIdentifier mockYid(final QName nn1) { + final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class); + when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1)); + return yid1; + } +} diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java new file mode 100644 index 000000000..bc7582e93 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java @@ -0,0 +1,427 @@ +package io.fd.honeycomb.v3po.data.impl; + +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import java.util.Map; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; + +public class ModificationDiffTest { + + static final QName TOP_CONTAINER_QNAME = + QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container"); + static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string"); + static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name"); + static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text"); + static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list"); + static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list"); + + static final QName WITH_CHOICE_CONTAINER_QNAME = + QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "with-choice"); + static final QName CHOICE_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "choice"); + static final QName IN_CASE1_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case1"); + static final QName IN_CASE2_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case2"); + + static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); + static final YangInstanceIdentifier NESTED_LIST_ID = TOP_CONTAINER_ID.node(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + + @Test + public void testInitialWrite() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainer = getTopContainer("string1"); + final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME); + dataTreeModification.write(TOP_CONTAINER_ID, topContainer); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final ModificationDiff modificationDiff = getModificationDiff(prepare); + + assertThat(modificationDiff.getUpdates().size(), is(1)); + assertThat(modificationDiff.getUpdates().values().size(), is(1)); + assertUpdate(modificationDiff.getUpdates().values().iterator().next(), TOP_CONTAINER_ID, null, topContainer); + } + + @Test + public void testInitialWriteForContainerWithChoice() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final DataTreeModification dataTreeModification = getModification(dataTree); + final ContainerNode containerWithChoice = Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(WITH_CHOICE_CONTAINER_QNAME)) + .withChild(Builders.choiceBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME)) + .withChild(ImmutableNodes.leafNode(IN_CASE1_LEAF_QNAME, "withinCase1")) + .build()) + .build(); + final YangInstanceIdentifier WITH_CHOICE_CONTAINER_ID = YangInstanceIdentifier.of(WITH_CHOICE_CONTAINER_QNAME); + dataTreeModification.write(WITH_CHOICE_CONTAINER_ID, containerWithChoice); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), + WITH_CHOICE_CONTAINER_ID, null, containerWithChoice); + } + + private DataTreeModification getModification(final TipProducingDataTree dataTree) { + final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + return dataTreeSnapshot.newModification(); + } + + @Test + public void testWriteNonPresenceEmptyContainer() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainer = ImmutableNodes.containerNode(TOP_CONTAINER_QNAME); + dataTreeModification.write(TOP_CONTAINER_ID, topContainer); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final ModificationDiff modificationDiff = getModificationDiff(prepare); + + assertThat(modificationDiff.getUpdates().size(), is(0)); + } + + private DataTreeCandidateTip prepareModification(final TipProducingDataTree dataTree, + final DataTreeModification dataTreeModification) + throws DataValidationFailedException { + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + return dataTree.prepare(dataTreeModification); + } + + @Test + public void testUpdateWrite() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final ContainerNode topContainer = getTopContainer("string1"); + addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); + + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainerAfter = getTopContainer("string2"); + dataTreeModification.write(TOP_CONTAINER_ID, topContainerAfter); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(1)); + assertThat(updates.values().size(), is(1)); + assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter); + } + + private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) { + return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode()); + } + + @Test + public void testUpdateMerge() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final ContainerNode topContainer = getTopContainer("string1"); + addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); + + final DataTreeModification dataTreeModification = getModification(dataTree); + final NormalizedNode topContainerAfter = getTopContainer("string2"); + dataTreeModification.merge(TOP_CONTAINER_ID, topContainerAfter); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertThat(updates.values().size(), is(1)); + assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter); + } + + @Test + public void testUpdateDelete() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + final ContainerNode topContainer = getTopContainer("string1"); + addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID); + + final DataTreeModification dataTreeModification = getModification(dataTree); + dataTreeModification.delete(TOP_CONTAINER_ID); + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertThat(updates.values().size(), is(1)); + assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, null); + } + + @Test + public void testWriteAndUpdateInnerList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier()); + dataTreeModification.write(listId, mapNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), + listEntryId, null, mapNode.getValue().iterator().next()); + + // Commit so that update can be tested next + dataTree.commit(prepare); + + YangInstanceIdentifier listItemId = listId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + MapEntryNode mapEntryNode = + getNestedList("name1", "text-update").getValue().iterator().next(); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.write(listItemId, mapEntryNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + + updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1 /*Actual list entry*/)); + } +// + private void assertUpdate(final ModificationDiff.NormalizedNodeUpdate update, + final YangInstanceIdentifier idExpected, + final NormalizedNode beforeExpected, + final NormalizedNode afterExpected) { + assertThat(update.getId(), is(idExpected)); + assertThat(update.getDataBefore(), is(beforeExpected)); + assertThat(update.getDataAfter(), is(afterExpected)); + } + + @Test + public void testWriteTopContainerAndInnerList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + + final ContainerNode topContainer = getTopContainer("string1"); + dataTreeModification.write(TOP_CONTAINER_ID, topContainer); + + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier()); + + dataTreeModification.write(listId, mapNode); + + final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + + assertThat(updates.size(), is(2)); + assertThat(updates.values().size(), is(2)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), TOP_CONTAINER_ID, null, + Builders.containerBuilder(topContainer).withChild(mapNode).build()); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), listEntryId, null, mapNode.getValue().iterator().next()); + // Assert that keys of the updates map are not wildcarded YID + assertThat(updates.keySet(), hasItems( + TOP_CONTAINER_ID, + listEntryId)); + } + + private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForAfterType( + final Map updates, + final Class> containerNodeClass) { + return updates.values().stream() + .filter(update -> containerNodeClass.isAssignableFrom(update.getDataAfter().getClass())) + .findFirst().get(); + } + + private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForBeforeType( + final Map updates, + final Class> containerNodeClass) { + return updates.values().stream() + .filter(update -> containerNodeClass.isAssignableFrom(update.getDataBefore().getClass())) + .findFirst().get(); + } + + @Test + public void testWriteDeepList() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + + YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + + final YangInstanceIdentifier.NodeIdentifierWithPredicates nestedListNodeId = + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"); + listId = YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME), + nestedListNodeId); + final YangInstanceIdentifier deepListId = + listId.node(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)); + final YangInstanceIdentifier deepListEntryId = deepListId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + + final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next(); + // Merge parent list, just to see no modifications on it + dataTreeModification.merge( + listId, + Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build()); + dataTreeModification.merge( + deepListId, + Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) + .build()); + dataTreeModification.merge( + deepListEntryId, + deepListEntry); + + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), deepListEntryId, null, deepListEntry); + } + + @Test + public void testDeleteInnerListItem() throws Exception { + final TipProducingDataTree dataTree = getDataTree(); + + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + final YangInstanceIdentifier listId = + YangInstanceIdentifier.create( + new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME), + new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)); + + final MapNode mapNode = getNestedList("name1", "text"); + dataTreeModification.write(listId, mapNode); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification); + + // Commit so that update can be tested next + dataTree.commit(prepare); + + YangInstanceIdentifier listItemId = listId.node( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1")); + + dataTreeSnapshot = dataTree.takeSnapshot(); + dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.delete(listItemId); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + prepare = dataTree.prepare(dataTreeModification); + + final Map updates = getModificationDiff(prepare).getUpdates(); + assertThat(updates.size(), is(1)); + assertUpdate(getNormalizedNodeUpdateForBeforeType(updates, MapEntryNode.class), listItemId, mapNode.getValue().iterator().next(), null); + } + + static void addNodeToTree(final DataTree dataTree, final NormalizedNode node, + final YangInstanceIdentifier id) + throws DataValidationFailedException { + DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot(); + DataTreeModification dataTreeModification = dataTreeSnapshot.newModification(); + dataTreeModification.write(id, node); + dataTreeModification.ready(); + dataTree.validate(dataTreeModification); + DataTreeCandidate prepare = dataTree.prepare(dataTreeModification); + dataTree.commit(prepare); + } + + static TipProducingDataTree getDataTree() throws ReactorException { + final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION); + dataTree.setSchemaContext(getSchemaCtx()); + return dataTree; + } + + static ContainerNode getTopContainer(final String stringValue) { + return Builders.containerBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME)) + .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue)) + .build(); + } + + static MapNode getNestedList(final String listItemName, final String text) { + return Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)) + .withChild( + Builders.mapEntryBuilder() + .withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, + NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(TEXT_LEAF_QNAME, text)) + .build() + ) + .build(); + } + + private MapNode getDeepList(final String listItemName) { + return Builders.mapBuilder() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME)) + .withChild( + Builders.mapEntryBuilder() + .withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, + NAME_LEAF_QNAME, listItemName)) + .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName)) + .build() + ) + .build(); + } + + private static SchemaContext getSchemaCtx() throws ReactorException { + final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild(); + buildAction.addSource(new YangStatementSourceImpl(ModificationDiffTest.class.getResourceAsStream("/test-diff.yang"))); + return buildAction.buildEffective(); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java new file mode 100644 index 000000000..523d9dd70 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java @@ -0,0 +1,69 @@ +/* + * 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.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; + +public class PersistingDataTreeAdapterTest { + + @Mock + private DataTree delegatingDataTree; + @Mock + private SchemaService schemaService; + @Mock + private DataTreeSnapshot snapshot; + + private Path tmpPersistFile; + + private PersistingDataTreeAdapter persistingDataTreeAdapter; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + tmpPersistFile = Files.createTempFile("testing-hc-persistence", "json"); + persistingDataTreeAdapter = new PersistingDataTreeAdapter(delegatingDataTree, schemaService, tmpPersistFile); + } + + @Test + public void testNoPersistOnFailure() throws Exception { + doThrow(new IllegalStateException("testing errors")).when(delegatingDataTree).commit(any(DataTreeCandidate.class)); + + try { + persistingDataTreeAdapter.commit(null); + fail("Exception expected"); + } catch (IllegalStateException e) { + verify(delegatingDataTree, times(0)).takeSnapshot(); + verify(delegatingDataTree).commit(any(DataTreeCandidate.class)); + } + } + +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java new file mode 100644 index 000000000..a13621725 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java @@ -0,0 +1,68 @@ +/* + * 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.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.ReadableDataManager; +import io.fd.honeycomb.v3po.data.DataModification; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class ReadOnlyTransactionTest { + + @Mock + private ReadableDataManager operationalData; + @Mock + private DataModification configSnapshot; + + private ReadOnlyTransaction readOnlyTx; + + @Before + public void setUp() { + initMocks(this); + readOnlyTx = ReadOnlyTransaction.create(configSnapshot, operationalData); + } + + @Test + public void testExists() { + final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class); + final CheckedFuture>, ReadFailedException> + future = mock(CheckedFuture.class); + when(operationalData.read(path)).thenReturn(future); + + readOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path); + + verify(operationalData).read(path); + } + + @Test + public void testGetIdentifier() { + assertNotNull(readOnlyTx.getIdentifier()); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java new file mode 100644 index 000000000..1b67cd967 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java @@ -0,0 +1,111 @@ +/* + * 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.assertNotNull; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class ReadWriteTransactionTest { + + @Mock + private DOMDataReadOnlyTransaction readTx; + + @Mock + private DOMDataWriteTransaction writeTx; + + private LogicalDatastoreType store; + + @Mock + private YangInstanceIdentifier path; + + @Mock + private NormalizedNode data; + + private ReadWriteTransaction readWriteTx; + + @Before + public void setUp() { + initMocks(this); + store = LogicalDatastoreType.CONFIGURATION; + readWriteTx = new ReadWriteTransaction(readTx, writeTx); + } + + @Test + public void testCancel() { + readWriteTx.cancel(); + verify(writeTx).cancel(); + } + + @Test + public void testPut() { + readWriteTx.put(store, path, data); + verify(writeTx).put(store, path, data); + } + + @Test + public void testMerge() { + readWriteTx.merge(store, path, data); + verify(writeTx).merge(store, path, data); + } + + @Test + public void testDelete() { + readWriteTx.delete(store, path); + verify(writeTx).delete(store, path); + } + + @Test + public void testSubmit() throws Exception { + readWriteTx.submit(); + verify(writeTx).submit(); + } + + + @SuppressWarnings("deprecation") + @Test + public void testCommit() throws Exception { + readWriteTx.commit(); + verify(writeTx).commit(); + } + + @Test + public void testRead() { + readWriteTx.read(store, path); + verify(readTx).read(store, path); + } + + @Test + public void testExists() { + readWriteTx.exists(store, path); + verify(readTx).exists(store, path); + } + + @Test + public void testGetIdentifier() throws Exception { + assertNotNull(readWriteTx.getIdentifier()); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java new file mode 100644 index 000000000..455050ab0 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.v3po.data.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.v3po.translate.read.ReadContext; +import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry; +import java.util.Collections; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class ReadableDataTreeDelegatorTest { + + @Mock + private BindingNormalizedNodeSerializer serializer; + @Mock + private ReaderRegistry reader; + + private ReadableDataTreeDelegator operationalData; + + @Mock + private InstanceIdentifier id; + @Mock + private Map.Entry> entry; + @Mock + private SchemaContext globalContext; + @Mock + private DataSchemaNode schemaNode; + @Mock + private ReadContext readCtx; + @Mock + private DOMDataBroker netconfMonitoringBroker; + @Mock + private DOMDataReadOnlyTransaction domDataReadOnlyTransaction; + @Mock + private DataBroker contextBroker; + + @Before + public void setUp() { + initMocks(this); + operationalData = new ReadableDataTreeDelegator(serializer, globalContext, reader, contextBroker); + doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class)); + + doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction(); + doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction) + .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class)); + + final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock( + org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class); + doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction(); + doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit(); + } + + @Test + public void testReadNode() throws Exception { + final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class); + final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class); + doReturn(pArg).when(yangId).getLastPathArgument(); + doReturn(Collections.singletonList(pArg)).when(yangId).getPathArguments(); + + doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType(); + doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId); + + final DataObject dataObject = mock(DataObject.class); + doReturn(Optional.of(dataObject)).when(reader).read(same(id), any(ReadContext.class)); + + when(serializer.toNormalizedNode(id, dataObject)).thenReturn(entry); + final DataContainerChild expectedValue = mock(DataContainerChild.class); + doReturn(expectedValue).when(entry).getValue(); + + final CheckedFuture>, ReadFailedException> future = operationalData.read(yangId); + + verify(serializer).fromYangInstanceIdentifier(yangId); + verify(reader).read(same(id), any(ReadContext.class)); + final Optional> result = future.get(); + assertTrue(result.isPresent()); + assertEquals(expectedValue, result.get()); + } + + @Test + public void testReadNonExistingNode() throws Exception { + final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class); + doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId); + doReturn(Optional.absent()).when(reader).read(same(id), any(ReadContext.class)); + + final CheckedFuture>, ReadFailedException> future = operationalData.read(yangId); + + verify(serializer).fromYangInstanceIdentifier(yangId); + verify(reader).read(same(id), any(ReadContext.class)); + final Optional> result = future.get(); + assertFalse(result.isPresent()); + } + + @Test + public void testReadFailed() throws Exception { + doThrow(io.fd.honeycomb.v3po.translate.read.ReadFailedException.class).when(reader).readAll(any(ReadContext.class)); + + final CheckedFuture>, ReadFailedException> future = + operationalData.read( YangInstanceIdentifier.EMPTY); + + try { + future.checkedGet(); + } catch (ReadFailedException e) { + assertTrue(e.getCause() instanceof io.fd.honeycomb.v3po.translate.read.ReadFailedException); + return; + } + fail("ReadFailedException was expected"); + } + + @Test + public void testReadRootWithOneNonListElement() throws Exception { + // Prepare data + final InstanceIdentifier vppStateII = InstanceIdentifier.create(DataObject.class); + final DataObject vppState = mock(DataObject.class); + Multimap, DataObject> dataObjects = LinkedListMultimap.create(); + dataObjects.put(vppStateII, vppState); + doReturn(dataObjects).when(reader).readAll(any(ReadContext.class)); + + // Init serializer + final YangInstanceIdentifier vppYangId = YangInstanceIdentifier.builder().node(QName.create("n", "d")).build(); + when(serializer.toYangInstanceIdentifier(vppStateII)).thenReturn(vppYangId); + when(serializer.toNormalizedNode(vppStateII, vppState)).thenReturn(entry); + final DataContainerChild vppStateContainer = mock(DataContainerChild.class); + doReturn(vppStateContainer).when(entry).getValue(); + doReturn(vppYangId.getLastPathArgument()).when(vppStateContainer).getIdentifier(); + + // Read root + final CheckedFuture>, ReadFailedException> future = + operationalData.read(YangInstanceIdentifier.EMPTY); + + verify(reader).readAll(any(ReadContext.class)); + verify(serializer).toYangInstanceIdentifier(vppStateII); + verify(serializer).toNormalizedNode(vppStateII, vppState); + + // Check the result is an ContainerNode with only one child + final Optional> result = future.get(); + assertTrue(result.isPresent()); + + final ContainerNode rootNode = (ContainerNode) result.get(); + assertEquals(SchemaContext.NAME, rootNode.getIdentifier().getNodeType()); + assertEquals(vppStateContainer, Iterables.getOnlyElement(rootNode.getValue())); + } +} \ No newline at end of file diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java new file mode 100644 index 000000000..9cde27d2b --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java @@ -0,0 +1,128 @@ +/* + * 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 junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.util.concurrent.CheckedFuture; +import io.fd.honeycomb.v3po.data.DataModification; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +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.DataValidationFailedException; + +public class WriteTransactionTest { + + @Mock + private DataModification configSnapshot; + @Mock + private YangInstanceIdentifier path; + @Mock + private NormalizedNode data; + + private WriteTransaction writeTx; + + @Before + public void setUp() { + initMocks(this); + writeTx = WriteTransaction.createConfigOnly(configSnapshot); + } + + @Test + public void testPut() { + writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); + verify(configSnapshot).write(path, data); + } + + @Test(expected = IllegalArgumentException.class) + public void testPutOperational() { + writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data); + verify(configSnapshot).write(path, data); + } + + @Test(expected = IllegalStateException.class) + public void testOnFinishedTx() { + writeTx.submit(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data); + verify(configSnapshot).write(path, data); + } + + @Test + public void testMerge() { + writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, data); + verify(configSnapshot).merge(path, data); + } + + @Test + public void testCancel() { + assertTrue(writeTx.cancel()); + } + + @Test + public void testCancelFinished() { + writeTx.submit(); + assertFalse(writeTx.cancel()); + } + + @Test + public void testDelete() { + writeTx.delete(LogicalDatastoreType.CONFIGURATION, path); + verify(configSnapshot).delete(path); + } + + @Test + public void testSubmit() throws Exception { + writeTx.submit(); + verify(configSnapshot).validate(); + verify(configSnapshot).commit(); + } + + @Test + public void testSubmitFailed() throws Exception { + doThrow(mock(DataValidationFailedException.class)).when(configSnapshot).commit(); + final CheckedFuture future = writeTx.submit(); + try { + future.get(); + } catch (Exception e) { + assertTrue(e.getCause() instanceof TransactionCommitFailedException); + return; + } + fail("Expected exception to be thrown"); + + } + + @Test(expected = UnsupportedOperationException.class) + public void testCommit() { + writeTx.commit(); + } + + @Test + public void testGetIdentifier() { + assertNotNull(writeTx.getIdentifier()); + } +} \ No newline at end of file -- cgit 1.2.3-korg