diff options
Diffstat (limited to 'infra/data-impl/src/test/java')
4 files changed, 589 insertions, 150 deletions
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorBaseTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorBaseTest.java new file mode 100644 index 000000000..5690e78c3 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorBaseTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.google.common.util.concurrent.Futures; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.AbstractMap; +import java.util.Map; +import org.junit.Before; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +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.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; + +abstract class ModifiableDataTreeDelegatorBaseTest extends ModificationBaseTest { + + @Mock + WriterRegistry writer; + @Mock + BindingNormalizedNodeSerializer serializer; + @Mock + org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification; + @Mock + DataBroker contextBroker; + @Mock + org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx; + + @Captor + ArgumentCaptor<WriteContext> writeContextCaptor; + + @Captor + ArgumentCaptor<WriterRegistry.DataObjectUpdates> updatesCaptor; + + final InstanceIdentifier<?> DEFAULT_ID = InstanceIdentifier.create(DataObject.class); + final DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class); + + DataTree dataTree; + ModifiableDataTreeManager configDataTree; + DataObjectUpdate update = DataObjectUpdate.create(DEFAULT_ID, null, DEFAULT_DATA_OBJECT); + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + dataTree = 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<InstanceIdentifier<?>, 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, getSchemaCtx(), writer, contextBroker); + + additionalSetup(); + } + + void additionalSetup() throws Exception { + } + + private static DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) { + final DataObject dataBefore = mock(classToMock, name); + doReturn(classToMock).when(dataBefore).getImplementedInterface(); + return dataBefore; + } + + abstract static class DataObject1 implements DataObject { + } + + abstract static class DataObject2 implements DataObject { + } + + abstract static class DataObject3 implements DataObject { + } + + <D extends DataObject> D mockDataObject(final YangInstanceIdentifier yid1, + final InstanceIdentifier iid1, + final NormalizedNode nn1B, + final Class<D> type) { + final D do1B = mock(type); + when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B)); + return do1B; + } + + NormalizedNode mockNormalizedNode(final QName nn1) { + final NormalizedNode nn1B = mock(NormalizedNode.class); + when(nn1B.getNodeType()).thenReturn(nn1); + return nn1B; + } + + InstanceIdentifier mockIid(final YangInstanceIdentifier yid1, + final Class<? extends DataObject> type) { + final InstanceIdentifier iid1 = InstanceIdentifier.create(type); + when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1); + return iid1; + } + + 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/data/impl/ModifiableDataTreeDelegatorRevertTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorRevertTest.java new file mode 100644 index 000000000..2787cdf1f --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorRevertTest.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.fd.honeycomb.data.DataModification; +import io.fd.honeycomb.translate.TranslationException; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.registry.UpdateFailedException; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.Collections; +import java.util.List; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; + +public class ModifiableDataTreeDelegatorRevertTest extends ModifiableDataTreeDelegatorBaseTest { + + /** + * Test scenario when commit fails, but there's nothing to revert because very first crud operation failed + */ + @Test + public void testCommitFailedNoRevert() throws Exception { + final MapNode nestedList = getNestedList("listEntry", "listValue"); + + // Fail on update: + final TranslationException failedOnUpdateException = new TranslationException("update failed"); + doThrow(new UpdateFailedException(failedOnUpdateException, Collections.emptyList(), update))// fail on update + .when(writer) + .processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + // Run the test + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + fail("UpdateFailedException was expected"); + } catch (UpdateFailedException e) { + // writer was called only one for update, and it was only one operation so no revert needed + // exception was just rethrown + verify(writer).processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + assertEquals(e.getFailed().getId(), DEFAULT_ID); + assertTrue(e.getProcessed().isEmpty()); + } + } + + /** + * Test whether + * - Correct operations were invoked(when creating and reverting) + * - Create and revert both failed + * - Correct exception has been thrown + * Steps: + * - Prepares state with nested list written + * - Attempts to rewrite that list with new list with value with different key + * - Simulates fail(both on modification and revert) + * - Checks modifications + * Asserts + * - index 0 - Represents create of original data + * - index 1 - Represents override with new list(fails)(include delete of data created by index 0 and create of new) + * - index 2 - Represents revert of removal of original data + */ + @Test + public void testCommitWithRevertFailed() throws Exception { + // configure initial state + final MapNode originalList = getNestedList("listEntryOriginal", "listValueOriginal"); + + final DataModification preModification = configDataTree.newModification(); + preModification.write(NESTED_LIST_ID, originalList); + preModification.validate(); + preModification.commit(); + + // then test + final MapNode nestedList = getNestedList("listEntry", "listValueNew"); + + // Fail on update: + final TranslationException failedOnUpdateException = new TranslationException("update failed"); + final DataObjectUpdate.DataObjectDelete mockRevertData = mock(DataObjectUpdate.DataObjectDelete.class); + final DataObjectUpdate.DataObjectDelete mockRevertDataReverted = mock(DataObjectUpdate.DataObjectDelete.class); + when(mockRevertData.getId()).thenReturn((InstanceIdentifier) InstanceIdentifier.create(DataObject.class)); + when(mockRevertDataReverted.getId()) + .thenReturn((InstanceIdentifier) InstanceIdentifier.create(DataObject.class)); + when(mockRevertData.getDataBefore()).thenReturn(DEFAULT_DATA_OBJECT);// to simulate that delete of original data + //should be reverted + when(mockRevertDataReverted.getDataAfter()) + .thenReturn(DEFAULT_DATA_OBJECT);// to simulate that delete of original data + //should be reverted + when(mockRevertData.reverse()).thenReturn(mockRevertDataReverted); + + final UpdateFailedException cause = + new UpdateFailedException(failedOnUpdateException, + Collections.singletonList(mockRevertData),//fail on new one + update); + doThrow(cause) + .when(writer) + .processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + // Run the test + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + fail("WriterRegistry.Reverter.RevertFailedException was expected"); + } catch (Reverter.RevertFailedException e) { + assertRewriteModificationWithRevert(writer, updatesCaptor, DEFAULT_DATA_OBJECT); + assertEquals(cause, e.getCause()); + } + } + + /** + * Test whether + * - Correct operations were invoked(when creating and reverting) + * - Create failed and revert passed + * - Correct exception has been thrown + * Steps: + * - Prepares state with nested list written + * - Attempts to rewrite that list with new list with value with different key + * - Simulates fail on create + * - Passes on revert + * - Checks modifications + * Asserts + * - index 0 - Represents create of original data + * - index 1 - Represents override with new list(fails)(include delete of data created by index 0 and create of new) + * - index 2 - Represents revert of removal of original data + */ + @Test + public void testCommitWithRevertSuccessfull() throws Exception { + // configure initial state + final MapNode originalList = getNestedList("listEntryOriginal", "listValueOriginal"); + + final DataModification preModification = configDataTree.newModification(); + preModification.write(NESTED_LIST_ID, originalList); + preModification.validate(); + preModification.commit(); + + // then test + final MapNode nestedList = getNestedList("listEntry", "listValueNew"); + + // Fail on update: + final TranslationException failedOnUpdateException = new TranslationException("update failed"); + final DataObjectUpdate.DataObjectDelete mockRevertData = mock(DataObjectUpdate.DataObjectDelete.class); + final DataObjectUpdate.DataObjectDelete mockRevertDataReverted = mock(DataObjectUpdate.DataObjectDelete.class); + when(mockRevertData.getId()).thenReturn((InstanceIdentifier) InstanceIdentifier.create(DataObject.class)); + when(mockRevertDataReverted.getId()) + .thenReturn((InstanceIdentifier) InstanceIdentifier.create(DataObject.class)); + when(mockRevertData.getDataBefore()).thenReturn(DEFAULT_DATA_OBJECT);// to simulate that delete of original data + //should be reverted + when(mockRevertDataReverted.getDataAfter()) + .thenReturn(DEFAULT_DATA_OBJECT);// to simulate that delete of original data + //should be reverted + when(mockRevertData.reverse()).thenReturn(mockRevertDataReverted); + + final UpdateFailedException cause = + new UpdateFailedException(failedOnUpdateException, + Collections.singletonList(mockRevertData),//fail on new one + update); + doThrow(cause) // fails on create + .doNothing()//to pass on revert + .when(writer) + .processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + // Run the test + final DataModification dataModification = configDataTree.newModification(); + dataModification.write(NESTED_LIST_ID, nestedList); + dataModification.validate(); + dataModification.commit(); + fail("WriterRegistry.Reverter.RevertFailedException was expected"); + } catch (Reverter.RevertSuccessException e) { + assertRewriteModificationWithRevert(writer, updatesCaptor, DEFAULT_DATA_OBJECT); + assertNull(e.getCause()); + } + } + + private static void assertRewriteModificationWithRevert(final WriterRegistry writer, + final ArgumentCaptor<WriterRegistry.DataObjectUpdates> updatesCaptor, + final DataObject DEFAULT_DATA_OBJECT) + throws TranslationException { + verify(writer, times(3)).processModifications(updatesCaptor.capture(), any(WriteContext.class)); + final List<WriterRegistry.DataObjectUpdates> allUpdates = updatesCaptor.getAllValues(); + assertEquals(3, allUpdates.size()); + + // represent create of original data + final WriterRegistry.DataObjectUpdates originalCreate = allUpdates.get(0); + assertContainsOnlySingleUpdate(originalCreate); + + final DataObjectUpdate createOriginalData = originalCreate.getUpdates().values().iterator().next(); + assertCreateWithData(createOriginalData, DEFAULT_DATA_OBJECT); + + // delete of original data was successful + // create of new data - failed + final WriterRegistry.DataObjectUpdates originalDelete = allUpdates.get(1); + assertConstainsSingleUpdateAndDelete(originalDelete); + + final DataObjectUpdate.DataObjectDelete deleteOriginalData = + originalDelete.getDeletes().values().iterator().next(); + assertDeleteWithData(deleteOriginalData, DEFAULT_DATA_OBJECT); + + final DataObjectUpdate newCreate = originalDelete.getUpdates().values().iterator().next(); + assertCreateWithData(newCreate, DEFAULT_DATA_OBJECT); + + final WriterRegistry.DataObjectUpdates revert = allUpdates.get(2); + assertContainsOnlySingleUpdate(revert); + } + + private static void assertDeleteWithData(final DataObjectUpdate.DataObjectDelete deleteOriginalData, + final DataObject DEFAULT_DATA_OBJECT) { + assertNull(deleteOriginalData.getDataAfter()); + assertEquals(DEFAULT_DATA_OBJECT, deleteOriginalData.getDataBefore()); + } + + private static void assertCreateWithData(final DataObjectUpdate newCreate, final DataObject DEFAULT_DATA_OBJECT) { + assertNull(newCreate.getDataBefore()); + assertEquals(DEFAULT_DATA_OBJECT, newCreate.getDataAfter()); + } + + private static void assertContainsOnlySingleUpdate(final WriterRegistry.DataObjectUpdates originalCreate) { + assertThat(originalCreate.getDeletes().size(), is(0)); + assertThat(originalCreate.getUpdates().size(), is(1)); + } + + private static void assertConstainsSingleUpdateAndDelete(final WriterRegistry.DataObjectUpdates originalDelete) { + assertThat(originalDelete.getDeletes().size(), is(1)); + assertThat(originalDelete.getUpdates().size(), is(1)); + } + +} diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java index ccd35a93d..56c0c610f 100644 --- a/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java @@ -22,84 +22,32 @@ 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.data.DataModification; -import io.fd.honeycomb.translate.TranslationException; import io.fd.honeycomb.translate.write.DataObjectUpdate; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.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.ArgumentCaptor; -import org.mockito.Captor; -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 extends ModificationBaseTest { - - @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; - - @Captor - private ArgumentCaptor<WriteContext> writeContextCaptor; - - private ModifiableDataTreeManager configDataTree; - private final DataObjectUpdate update = DataObjectUpdate.create(DEFAULT_ID, null, DEFAULT_DATA_OBJECT); - - 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 = 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<InstanceIdentifier<?>, 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, getSchemaCtx(), writer, contextBroker); - } +public class ModifiableDataTreeDelegatorTest extends ModifiableDataTreeDelegatorBaseTest { @Test public void testRead() throws Exception { @@ -134,70 +82,6 @@ public class ModifiableDataTreeDelegatorTest extends ModificationBaseTest { assertEquals(nestedList, dataTree.takeSnapshot().readNode(NESTED_LIST_ID).get()); } - private static DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) { - final DataObject dataBefore = mock(classToMock, name); - doReturn(classToMock).when(dataBefore).getImplementedInterface(); - return dataBefore; - } - - @Test - public void testCommitUndoSuccessful() throws Exception { - final MapNode nestedList = 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(DEFAULT_ID, update, Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException)) - .when(writer).processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - - try { - // Run the test - final DataModification dataModification = configDataTree.newModification(); - dataModification.write(NESTED_LIST_ID, nestedList); - dataModification.validate(); - dataModification.commit(); - fail("WriterRegistry.RevertSuccessException was expected"); - } catch (WriterRegistry.Reverter.RevertSuccessException e) { - verify(writer).processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - assertThat(e.getFailedIds(), hasItem(DEFAULT_ID)); - verify(reverter).revert(any(WriteContext.class)); - } - } - - @Test - public void testCommitUndoFailed() throws Exception { - final MapNode nestedList = getNestedList("listEntry", "listValue"); - - // Fail on update: - final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class); - final TranslationException failedOnUpdateException = new TranslationException("update failed"); - final WriterRegistry.BulkUpdateException bulkFailEx = - new WriterRegistry.BulkUpdateException(DEFAULT_ID, update, Collections.singleton(DEFAULT_ID), reverter, - failedOnUpdateException); - doThrow(bulkFailEx).when(writer).processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - - // Fail on revert: - doThrow(new WriterRegistry.Reverter.RevertFailedException(bulkFailEx)) - .when(reverter).revert(any(WriteContext.class)); - - try { - // Run the test - final DataModification dataModification = configDataTree.newModification(); - dataModification.write(NESTED_LIST_ID, nestedList); - dataModification.validate(); - dataModification.commit(); - fail("WriterRegistry.Reverter.RevertFailedException was expected"); - } catch (WriterRegistry.Reverter.RevertFailedException e) { - verify(writer).processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); - verify(reverter).revert(any(WriteContext.class)); - assertEquals(bulkFailEx, 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); @@ -214,7 +98,7 @@ public class ModifiableDataTreeDelegatorTest extends ModificationBaseTest { // create final QName nn2 = QName.create("namespace", "nn1"); final YangInstanceIdentifier yid2 = mockYid(nn2); - final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);; + final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class); final NormalizedNode nn2A = mockNormalizedNode(nn2); final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class); biNodes.put(yid2, NormalizedNodeUpdate.create(yid2, null, nn2A)); @@ -226,7 +110,7 @@ public class ModifiableDataTreeDelegatorTest extends ModificationBaseTest { 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);; + final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class); biNodes.put(yid3, NormalizedNodeUpdate.create(yid3, nn3B, nn3A)); final WriterRegistry.DataObjectUpdates dataObjectUpdates = @@ -262,7 +146,7 @@ public class ModifiableDataTreeDelegatorTest extends ModificationBaseTest { // create final QName nn2 = QName.create("namespace", "nn1"); final YangInstanceIdentifier yid2 = mockYid(nn2); - final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);; + final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class); final NormalizedNode nn2A = mockNormalizedNode(nn2); final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class); biNodes.put(yid2, NormalizedNodeUpdate.create(yid2, null, nn2A)); @@ -274,7 +158,7 @@ public class ModifiableDataTreeDelegatorTest extends ModificationBaseTest { 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);; + final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class); biNodes.put(yid3, NormalizedNodeUpdate.create(yid3, nn3B, nn3A)); final WriterRegistry.DataObjectUpdates dataObjectUpdates = @@ -289,39 +173,11 @@ public class ModifiableDataTreeDelegatorTest extends ModificationBaseTest { ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid3, do3B, null)))); assertThat(dataObjectUpdates.getUpdates().size(), is(2)); - assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems( (InstanceIdentifier<?>) iid2, (InstanceIdentifier<?>) iid3)); + assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier<?>) iid2, (InstanceIdentifier<?>) iid3)); assertThat(dataObjectUpdates.getUpdates().values(), hasItems( DataObjectUpdate.create(iid2, null, do2A), DataObjectUpdate.create(iid3, null, do3A))); assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3)); } - - private <D extends DataObject> D mockDataObject(final YangInstanceIdentifier yid1, - final InstanceIdentifier iid1, - final NormalizedNode nn1B, - final Class<D> type) { - final D do1B = mock(type); - when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B)); - return do1B; - } - - private 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<? extends DataObject> 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/data/impl/ReverterTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReverterTest.java new file mode 100644 index 000000000..db3fdbaf5 --- /dev/null +++ b/infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReverterTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.data.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import com.google.common.collect.ImmutableList; +import io.fd.honeycomb.translate.TranslationException; +import io.fd.honeycomb.translate.write.DataObjectUpdate; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.registry.UpdateFailedException; +import io.fd.honeycomb.translate.write.registry.WriterRegistry; +import java.util.Collections; +import java.util.Iterator; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class ReverterTest { + + private static final InstanceIdentifier<DataObject> IID_0 = InstanceIdentifier.create(DataObject.class); + private static final InstanceIdentifier<DataObject1> IID_1 = InstanceIdentifier.create(DataObject1.class); + private static final InstanceIdentifier<DataObject2> IID_2 = InstanceIdentifier.create(DataObject2.class); + + @Mock + private WriterRegistry registry; + + @Mock + private WriteContext writeContext; + + @Captor + private ArgumentCaptor<WriterRegistry.DataObjectUpdates> updateCaptor; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void revertSingle() throws Exception { + final DataObjectUpdate create = DataObjectUpdate.create(IID_0, null, mock(DataObject.class)); + + new Reverter(ImmutableList.of(create), registry).revert(writeContext); + assertSingleRevert(create); + } + + @Test + public void revertSingleFailed() throws TranslationException { + final DataObjectUpdate create = DataObjectUpdate.create(IID_0, null, mock(DataObject.class)); + final UpdateFailedException ex = + new UpdateFailedException(new IllegalStateException(), Collections.emptyList(), create); + doThrow(ex).when(registry) + .processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + new Reverter(ImmutableList.of(create), registry).revert(writeContext); + } catch (Reverter.RevertFailedException e) { + assertEquals(ex, e.getCause()); + assertSingleRevert(create); + return; + } + fail("Reverter.RevertFailedException was expected"); + } + + + @Test + public void revertSingleFailedWithUnexpectedEx() throws TranslationException { + final DataObjectUpdate create = DataObjectUpdate.create(IID_0, null, mock(DataObject.class)); + final IllegalStateException ex = new IllegalStateException(); + doThrow(ex).when(registry) + .processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + new Reverter(ImmutableList.of(create), registry).revert(writeContext); + } catch (Reverter.RevertFailedException e) { + assertEquals(ex, e.getCause()); + assertSingleRevert(create); + return; + } + fail("IllegalStateException was expected"); + } + + + @Test + public void revertMultiple() throws Exception { + final DataObjectUpdate create = DataObjectUpdate.create(IID_0, null, mock(DataObject.class)); + final DataObjectUpdate update = + DataObjectUpdate.create(IID_1, mock(DataObject1.class), mock(DataObject1.class)); + final DataObjectUpdate delete = DataObjectUpdate.create(IID_2, mock(DataObject2.class), null); + + new Reverter(ImmutableList.of(create, update, delete), registry).revert(writeContext); + assertMultiRevert(create, update, delete); + } + + + @Test + public void revertMultipleFailed() throws Exception { + final DataObjectUpdate create = DataObjectUpdate.create(IID_0, null, mock(DataObject.class)); + final DataObjectUpdate update = + DataObjectUpdate.create(IID_1, mock(DataObject1.class), mock(DataObject1.class)); + final DataObjectUpdate delete = DataObjectUpdate.create(IID_2, mock(DataObject2.class), null); + + final UpdateFailedException ex = + new UpdateFailedException(new IllegalStateException(), ImmutableList.of(create, update), create); + doThrow(ex).when(registry) + .processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + new Reverter(ImmutableList.of(create, update, delete), registry).revert(writeContext); + } catch (Reverter.RevertFailedException e) { + assertEquals(ex, e.getCause()); + assertMultiRevert(create, update, delete); + return; + } + fail("Reverter.RevertFailedException was expected"); + } + + @Test + public void revertMultipleFailedWithUnnexpectedException() throws Exception { + final DataObjectUpdate create = DataObjectUpdate.create(IID_0, null, mock(DataObject.class)); + final DataObjectUpdate update = + DataObjectUpdate.create(IID_1, mock(DataObject1.class), mock(DataObject1.class)); + final DataObjectUpdate delete = DataObjectUpdate.create(IID_2, mock(DataObject2.class), null); + + final IllegalStateException ex = new IllegalStateException(); + doThrow(ex).when(registry) + .processModifications(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class)); + + try { + new Reverter(ImmutableList.of(create, update, delete), registry).revert(writeContext); + } catch (Reverter.RevertFailedException e) { + assertEquals(ex, e.getCause()); + assertMultiRevert(create, update, delete); + return; + } + fail("IllegalStateException was expected"); + } + + + private void assertSingleRevert(final DataObjectUpdate create) throws TranslationException { + verify(registry, times(1)).processModifications(updateCaptor.capture(), eq(writeContext)); + final WriterRegistry.DataObjectUpdates updates = updateCaptor.getValue(); + assertTrue(updates.getDeletes().containsValue(create.reverse())); + assertTrue(updates.getUpdates().isEmpty()); + } + + private void assertMultiRevert(final DataObjectUpdate create, final DataObjectUpdate update, + final DataObjectUpdate delete) throws TranslationException { + verify(registry, times(1)).processModifications(updateCaptor.capture(), eq(writeContext)); + final WriterRegistry.DataObjectUpdates updates = updateCaptor.getValue(); + final Iterator<DataObjectUpdate.DataObjectDelete> deletesIterator = updates.getDeletes().values().iterator(); + final Iterator<DataObjectUpdate> updatesIterator = updates.getUpdates().values().iterator(); + + assertEquals(updatesIterator.next(), delete.reverse()); + assertEquals(updatesIterator.next(), update.reverse()); + assertEquals(deletesIterator.next(), create.reverse()); + } + + + private interface DataObject1 extends DataObject { + } + + private interface DataObject2 extends DataObject { + } +}
\ No newline at end of file |