2 * Copyright (c) 2016 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.translate.impl.write.registry;
19 import static org.hamcrest.CoreMatchers.hasItem;
20 import static org.hamcrest.CoreMatchers.is;
21 import static org.hamcrest.Matchers.hasSize;
22 import static org.junit.Assert.assertThat;
23 import static org.junit.Assert.fail;
24 import static org.mockito.Matchers.any;
25 import static org.mockito.Matchers.eq;
26 import static org.mockito.Mockito.doThrow;
27 import static org.mockito.Mockito.inOrder;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.verifyNoMoreInteractions;
31 import static org.mockito.Mockito.verifyZeroInteractions;
32 import static org.mockito.Mockito.when;
34 import com.google.common.collect.HashMultimap;
35 import com.google.common.collect.ImmutableMap;
36 import com.google.common.collect.ImmutableMultimap;
37 import com.google.common.collect.Multimap;
38 import io.fd.honeycomb.translate.util.DataObjects;
39 import io.fd.honeycomb.translate.util.DataObjects.DataObject1;
40 import io.fd.honeycomb.translate.util.DataObjects.DataObject2;
41 import io.fd.honeycomb.translate.write.DataObjectUpdate;
42 import io.fd.honeycomb.translate.write.WriteContext;
43 import io.fd.honeycomb.translate.write.Writer;
44 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.mockito.InOrder;
48 import org.mockito.Mock;
49 import org.mockito.MockitoAnnotations;
50 import org.opendaylight.yangtools.yang.binding.DataObject;
51 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 public class FlatWriterRegistryTest {
56 private Writer<DataObject1> writer1;
58 private Writer<DataObject2> writer2;
60 private Writer<DataObjects.DataObject3> writer3;
62 private Writer<DataObjects.DataObject1ChildK> writer4;
64 private WriteContext ctx;
66 private WriteContext revertWriteContext;
69 public void setUp() throws Exception {
70 MockitoAnnotations.initMocks(this);
71 when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
72 when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
73 when(writer3.getManagedDataObjectType()).thenReturn(DataObjects.DataObject3.IID);
77 public void testMultipleUpdatesForSingleWriter() throws Exception {
78 final FlatWriterRegistry flatWriterRegistry =
79 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
81 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
82 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
83 final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class);
84 final DataObject1 dataObject = mock(DataObject1.class);
85 updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
86 updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject));
87 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
89 verify(writer1).update(iid, dataObject, dataObject, ctx);
90 verify(writer1).update(iid2, dataObject, dataObject, ctx);
91 // Invoked when registry is being created
92 verifyNoMoreInteractions(writer1);
93 verifyZeroInteractions(writer2);
97 public void testMultipleUpdatesForMultipleWriters() throws Exception {
98 final FlatWriterRegistry flatWriterRegistry =
99 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
101 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
102 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
103 final DataObject1 dataObject = mock(DataObject1.class);
104 updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
105 final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
106 final DataObject2 dataObject2 = mock(DataObject2.class);
107 updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
108 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
110 final InOrder inOrder = inOrder(writer1, writer2);
111 inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
112 inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
114 verifyNoMoreInteractions(writer1);
115 verifyNoMoreInteractions(writer2);
119 public void testMultipleDeletesForMultipleWriters() throws Exception {
120 final FlatWriterRegistry flatWriterRegistry =
121 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
123 final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
124 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
125 final DataObject1 dataObject = mock(DataObject1.class);
126 deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
127 final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
128 final DataObject2 dataObject2 = mock(DataObject2.class);
130 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
131 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx);
133 final InOrder inOrder = inOrder(writer1, writer2);
134 // Reversed order of invocation, first writer2 and then writer1
135 inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
136 inOrder.verify(writer1).update(iid, dataObject, null, ctx);
138 verifyNoMoreInteractions(writer1);
139 verifyNoMoreInteractions(writer2);
143 public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception {
144 final FlatWriterRegistry flatWriterRegistry =
145 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
147 final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
148 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
149 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
150 final DataObject1 dataObject = mock(DataObject1.class);
152 deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
154 updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
155 final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
156 final DataObject2 dataObject2 = mock(DataObject2.class);
159 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
161 updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
162 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx);
164 final InOrder inOrder = inOrder(writer1, writer2);
165 // Reversed order of invocation, first writer2 and then writer1 for deletes
166 inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
167 inOrder.verify(writer1).update(iid, dataObject, null, ctx);
168 // Then also updates are processed
169 inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
170 inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
172 verifyNoMoreInteractions(writer1);
173 verifyNoMoreInteractions(writer2);
176 @Test(expected = IllegalArgumentException.class)
177 public void testMultipleUpdatesOneMissing() throws Exception {
178 final FlatWriterRegistry flatWriterRegistry =
179 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
181 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
182 addUpdate(updates, DataObject1.class);
183 addUpdate(updates, DataObject2.class);
184 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
188 public void testMultipleUpdatesOneFailing() throws Exception {
189 final FlatWriterRegistry flatWriterRegistry =
190 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
192 // Writer1 always fails
193 doThrow(new RuntimeException()).when(writer1)
194 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
196 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
197 addUpdate(updates, DataObject1.class);
198 addUpdate(updates, DataObject2.class);
201 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
202 fail("Bulk update should have failed on writer1");
203 } catch (WriterRegistry.BulkUpdateException e) {
204 assertThat(e.getUnrevertedSubtrees(), hasSize(2));
205 assertThat(e.getUnrevertedSubtrees(), hasItem(InstanceIdentifier.create(DataObject2.class)));
206 assertThat(e.getUnrevertedSubtrees(), hasItem(InstanceIdentifier.create(DataObject1.class)));
211 public void testMultipleUpdatesOneFailingThenRevertWithSuccess() throws Exception {
212 final FlatWriterRegistry flatWriterRegistry =
213 new FlatWriterRegistry(
214 ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3));
216 // Writer1 always fails
217 doThrow(new RuntimeException()).when(writer3)
218 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
220 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
221 addUpdate(updates, DataObject1.class);
222 addUpdate(updates, DataObjects.DataObject3.class);
223 final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
224 final DataObject2 before2 = mock(DataObject2.class);
225 final DataObject2 after2 = mock(DataObject2.class);
226 updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, before2, after2));
229 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
230 fail("Bulk update should have failed on writer1");
231 } catch (WriterRegistry.BulkUpdateException e) {
232 assertThat(e.getUnrevertedSubtrees().size(), is(1));
234 final InOrder inOrder = inOrder(writer1, writer2, writer3);
235 inOrder.verify(writer1)
236 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
237 inOrder.verify(writer2)
238 .update(iid2, before2, after2, ctx);
239 inOrder.verify(writer3)
240 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
242 e.revertChanges(revertWriteContext);
243 // Revert changes. Successful updates are iterated in reverse
244 // also binding other write context,to verify if update context is not reused
245 inOrder.verify(writer2)
246 .update(iid2, after2, before2, revertWriteContext);
247 inOrder.verify(writer1)
248 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), eq(revertWriteContext));
249 verifyNoMoreInteractions(writer3);
254 public void testMultipleUpdatesOneFailingThenRevertWithFail() throws Exception {
255 final FlatWriterRegistry flatWriterRegistry =
256 new FlatWriterRegistry(
257 ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3));
259 // Writer1 always fails
260 doThrow(new RuntimeException()).when(writer3)
261 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
263 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
264 addUpdate(updates, DataObject1.class);
265 addUpdate(updates, DataObject2.class);
266 addUpdate(updates, DataObjects.DataObject3.class);
269 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
270 fail("Bulk update should have failed on writer1");
271 } catch (WriterRegistry.BulkUpdateException e) {
272 // Writer1 always fails from now
273 doThrow(new RuntimeException()).when(writer1)
274 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
276 e.revertChanges(revertWriteContext);
277 } catch (WriterRegistry.Reverter.RevertFailedException e1) {
278 assertThat(e1.getNotRevertedChanges().size(), is(1));
279 assertThat(e1.getNotRevertedChanges(),
280 hasItem(InstanceIdentifier.create(DataObject1.class)));
286 public void testMutlipleUpdatesWithOneKeyedContainer() throws Exception {
287 final InstanceIdentifier internallyKeyedIdentifier = InstanceIdentifier.create(DataObject1.class)
288 .child(DataObjects.DataObject1ChildK.class, new DataObjects.DataObject1ChildKey());
290 final FlatWriterRegistry flatWriterRegistry =
291 new FlatWriterRegistry(
292 ImmutableMap.of(DataObject1.IID, writer1, DataObjects.DataObject1ChildK.IID,writer4));
294 // Writer1 always fails
295 doThrow(new RuntimeException()).when(writer1)
296 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class),
297 any(WriteContext.class));
299 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
300 addKeyedUpdate(updates,DataObjects.DataObject1ChildK.class);
301 addUpdate(updates, DataObject1.class);
303 flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
304 fail("Bulk update should have failed on writer1");
305 } catch (WriterRegistry.BulkUpdateException e) {
306 // Writer1 always fails from now
307 doThrow(new RuntimeException()).when(writer1)
308 .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class),
309 any(WriteContext.class));
311 e.revertChanges(revertWriteContext);
312 } catch (WriterRegistry.Reverter.RevertFailedException e1) {
313 assertThat(e1.getNotRevertedChanges().size(), is(1));
314 assertThat(e1.getNotRevertedChanges(),
315 hasItem(InstanceIdentifier.create(DataObject1.class)));
320 private <D extends DataObject> void addKeyedUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
321 final Class<D> type) throws Exception {
322 final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
323 final InstanceIdentifier<D> keyedIid = (InstanceIdentifier<D>) type.getDeclaredField("INTERNALLY_KEYED_IID").get(null);
324 updates.put(iid, DataObjectUpdate.create(keyedIid, mock(type), mock(type)));
327 private <D extends DataObject> void addUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
328 final Class<D> type) throws Exception {
329 final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
330 updates.put(iid, DataObjectUpdate.create(iid, mock(type), mock(type)));