summaryrefslogtreecommitdiffstats
path: root/infra/data-impl/src/test/java/io
diff options
context:
space:
mode:
authorJan Srnicek <jsrnicek@cisco.com>2017-10-23 10:57:13 +0200
committerMarek Gradzki <mgradzki@cisco.com>2017-10-23 12:26:02 +0000
commit5503731d866d318e9d5a2183608092a9d332dfe6 (patch)
tree10470b27b8ddb1a7776f78c733546be4d1a48b29 /infra/data-impl/src/test/java/io
parent0762f9aa1a7894056c2ddbc72421b933e9ea8dcf (diff)
HONEYCOMB-405 - Revert fix for indirect updates
If indirect update(delete+create) fails in a way, that delete passed, but update part failed, delete part must be reverted Moves reverter creation to MDTG and test cases related too it to ModifiableDataTreeDelegatorRevertTest Fixes tracking of allready processed changes by tracking them from perspective of processModifications() method Introduces UpdateFailedException as replacement for BulkUpdateException(now thrown also for single updates) Separates ReverterImpl from FlatWriterRegistry and ads unit tests Change-Id: If0066d0716d9476be89b1d99985b6745becac15e Signed-off-by: Jan Srnicek <jsrnicek@cisco.com>
Diffstat (limited to 'infra/data-impl/src/test/java/io')
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorBaseTest.java134
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorRevertTest.java257
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ModifiableDataTreeDelegatorTest.java156
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/data/impl/ReverterTest.java192
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