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.Matchers.hasSize;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertThat;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 import static org.mockito.Matchers.any;
25 import static org.mockito.Mockito.doThrow;
26 import static org.mockito.Mockito.inOrder;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.times;
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.WriteFailedException;
44 import io.fd.honeycomb.translate.write.Writer;
45 import io.fd.honeycomb.translate.write.registry.UpdateFailedException;
46 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
47 import java.util.List;
48 import javax.annotation.Nonnull;
49 import javax.annotation.Nullable;
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.mockito.InOrder;
53 import org.mockito.Mock;
54 import org.mockito.MockitoAnnotations;
55 import org.mockito.stubbing.Answer;
56 import org.opendaylight.yangtools.yang.binding.DataObject;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
59 public class FlatWriterRegistryTest {
62 private Writer<DataObject1> writer1;
64 private Writer<DataObject2> writer2;
66 private Writer<DataObjects.DataObject3> writer3;
68 private Writer<DataObjects.DataObject1ChildK> writer4;
70 private WriteContext ctx;
72 private WriteContext revertWriteContext;
75 public void setUp() throws Exception {
76 MockitoAnnotations.initMocks(this);
77 when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
78 when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
79 when(writer3.getManagedDataObjectType()).thenReturn(DataObjects.DataObject3.IID);
80 when(writer4.getManagedDataObjectType()).thenReturn(DataObjects.DataObject1ChildK.IID);
81 // TODO - HONEYCOMB-412 - thenCallRealMethod doest work with default methods
82 // https://stackoverflow.com/questions/27663252/can-you-make-mockito-1-10-17-work-with-default-methods-in-interfaces
83 when(writer1.canProcess(any())).thenAnswer(answerWithImpl());
84 when(writer2.canProcess(any())).thenAnswer(answerWithImpl());
85 when(writer3.canProcess(any())).thenAnswer(answerWithImpl());
86 when(writer4.canProcess(any())).thenAnswer(answerWithImpl());
89 private static Answer<Object> answerWithImpl() {
90 return invocationOnMock -> new CheckedMockWriter(Writer.class.cast(invocationOnMock.getMock())).canProcess(
91 InstanceIdentifier.class.cast(invocationOnMock.getArguments()[0]));
95 public void testMultipleUpdatesForSingleWriter() throws Exception {
96 final FlatWriterRegistry flatWriterRegistry =
97 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
99 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
100 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
101 final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class);
102 final DataObject1 dataObject = mock(DataObject1.class);
103 updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
104 updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject));
105 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
107 verify(writer1).processModification(iid, dataObject, dataObject, ctx);
108 verify(writer1).processModification(iid2, dataObject, dataObject, ctx);
109 // Invoked when registry is being created
110 verifyNoMoreInteractions(writer1);
111 verifyZeroInteractions(writer2);
115 public void testMultipleUpdatesForMultipleWriters() throws Exception {
116 final FlatWriterRegistry flatWriterRegistry =
117 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
119 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
120 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
121 final DataObject1 dataObject = mock(DataObject1.class);
122 updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
123 final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
124 final DataObject2 dataObject2 = mock(DataObject2.class);
125 updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
126 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
128 final InOrder inOrder = inOrder(writer1, writer2);
129 inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
130 inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
132 // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
133 // we are really interested just in invocations of processModification(),so adding specific verify to check that
134 verify(writer1,times(1)).processModification(any(),any(),any(),any());
135 verify(writer2,times(1)).processModification(any(),any(),any(),any());
136 //verifyNoMoreInteractions(writer1);
137 //verifyNoMoreInteractions(writer2);
141 public void testMultipleDeletesForMultipleWriters() throws Exception {
142 final FlatWriterRegistry flatWriterRegistry =
143 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
145 final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
146 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
147 final DataObject1 dataObject = mock(DataObject1.class);
148 deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
149 final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
150 final DataObject2 dataObject2 = mock(DataObject2.class);
152 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
153 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx);
155 final InOrder inOrder = inOrder(writer1, writer2);
156 // Reversed order of invocation, first writer2 and then writer1
157 inOrder.verify(writer2).processModification(iid2, dataObject2, null, ctx);
158 inOrder.verify(writer1).processModification(iid, dataObject, null, ctx);
160 // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
161 // we are really interested just in invocations of processModification(),so adding specific verify to check that
162 verify(writer1,times(1)).processModification(any(),any(),any(),any());
163 verify(writer2,times(1)).processModification(any(),any(),any(),any());
164 //verifyNoMoreInteractions(writer1);
165 //verifyNoMoreInteractions(writer2);
169 public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception {
170 final FlatWriterRegistry flatWriterRegistry =
171 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
173 final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
174 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
175 final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
176 final DataObject1 dataObject = mock(DataObject1.class);
178 deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
180 updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
181 final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
182 final DataObject2 dataObject2 = mock(DataObject2.class);
185 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
187 updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
188 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx);
190 final InOrder inOrder = inOrder(writer1, writer2);
191 // Reversed order of invocation, first writer2 and then writer1 for deletes
192 inOrder.verify(writer2).processModification(iid2, dataObject2, null, ctx);
193 inOrder.verify(writer1).processModification(iid, dataObject, null, ctx);
194 // Then also updates are processed
195 inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
196 inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
198 // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
199 // we are really interested just in invocations of processModification(),so adding specific verify to check that
200 verify(writer1,times(2)).processModification(any(),any(),any(),any());
201 verify(writer2,times(2)).processModification(any(),any(),any(),any());
202 //verifyNoMoreInteractions(writer1);
203 //verifyNoMoreInteractions(writer2);
206 @Test(expected = IllegalArgumentException.class)
207 public void testMultipleUpdatesOneMissing() throws Exception {
208 final FlatWriterRegistry flatWriterRegistry =
209 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
211 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
212 addUpdate(updates, DataObject1.class);
213 addUpdate(updates, DataObject2.class);
214 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
218 public void testMultipleUpdatesFirstFailing() throws Exception {
219 final FlatWriterRegistry flatWriterRegistry =
220 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
222 // Writer1 always fails
223 doThrow(new RuntimeException()).when(writer1)
224 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
226 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
227 addUpdate(updates, DataObject1.class);
228 addUpdate(updates, DataObject2.class);
231 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
232 fail("Bulk update should have failed on writer1 with UpdateFailedException");
233 } catch (UpdateFailedException e) {
234 assertThat(e.getProcessed(), hasSize(0));// very first update failed
239 public void testMultipleUpdatesSecondFailing() throws Exception {
240 final FlatWriterRegistry flatWriterRegistry =
241 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
243 // Writer2 always fails
244 doThrow(new RuntimeException()).when(writer2)
245 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
247 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
248 addUpdate(updates, DataObject1.class);
249 addUpdate(updates, DataObject2.class);
252 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
253 fail("Bulk update should have failed on writer1 with UpdateFailedException");
254 } catch (UpdateFailedException e) {
255 final List<DataObjectUpdate> alreadyProcessed = e.getProcessed();
256 assertThat(alreadyProcessed, hasSize(1));// very first update failed
257 assertEquals(updateData(DataObject1.class, DataObject1.IID),
258 e.getProcessed().iterator().next());
263 public void testMultipleUpdatesLastFailing() throws Exception {
264 final FlatWriterRegistry flatWriterRegistry =
265 new FlatWriterRegistry(
266 ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3));
268 // Writer1 always fails
269 doThrow(new RuntimeException()).when(writer3)
270 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
272 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
273 addUpdate(updates, DataObject1.class);
274 addUpdate(updates, DataObject2.class);
275 addUpdate(updates, DataObjects.DataObject3.class);
278 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
279 fail("Bulk update should have failed on writer1 with UpdateFailedException");
280 } catch (UpdateFailedException e) {
281 final List<DataObjectUpdate> alreadyProcessed = e.getProcessed();
282 assertEquals(2, alreadyProcessed.size());
283 assertTrue(alreadyProcessed.contains(updateData(DataObject1.class, DataObject1.IID)));
284 assertTrue(alreadyProcessed.contains(updateData(DataObject2.class, DataObject2.IID)));
289 public void testMutlipleUpdatesWithOneKeyedContainer() throws Exception {
290 final InstanceIdentifier internallyKeyedIdentifier = InstanceIdentifier.create(DataObject1.class)
291 .child(DataObjects.DataObject1ChildK.class, new DataObjects.DataObject1ChildKey());
293 final FlatWriterRegistry flatWriterRegistry =
294 new FlatWriterRegistry(
295 ImmutableMap.of(DataObject1.IID, writer1, DataObjects.DataObject1ChildK.IID, writer4));
297 // Writer1 always fails
298 doThrow(new RuntimeException()).when(writer1)
299 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class),
300 any(WriteContext.class));
302 final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
303 addKeyedUpdate(updates, DataObjects.DataObject1ChildK.class);
304 addUpdate(updates, DataObject1.class);
306 flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
307 fail("Bulk update should have failed on writer1 with UpdateFailedException");
308 } catch (UpdateFailedException e) {
309 assertTrue(e.getProcessed().isEmpty());
313 private <D extends DataObject> void addKeyedUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
314 final Class<D> type) throws Exception {
315 final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
316 final InstanceIdentifier<D> keyedIid = (InstanceIdentifier<D>) type.getDeclaredField("INTERNALLY_KEYED_IID").get(null);
317 updates.put(iid, DataObjectUpdate.create(keyedIid, mock(type), mock(type)));
320 private <D extends DataObject> void addUpdate(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 updates.put(iid, updateData(type, iid));
326 private static <D extends DataObject> DataObjectUpdate updateData(final Class<D> type,
327 final InstanceIdentifier<D> iid) {
328 return DataObjectUpdate.create(iid, mock(type), mock(type));
331 //TODO - HONEYCOMB-412 - remove after
333 * Used to utilize default implementation of canProcess()
335 static class CheckedMockWriter implements Writer{
337 private final Writer mockedWriter;
339 CheckedMockWriter(final Writer mockedWriter) {
340 this.mockedWriter = mockedWriter;
344 public void processModification(@Nonnull final InstanceIdentifier id, @Nullable final DataObject dataBefore,
345 @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx)
346 throws WriteFailedException {
347 mockedWriter.processModification(id,dataBefore,dataAfter,ctx);
351 public boolean supportsDirectUpdate() {
352 return mockedWriter.supportsDirectUpdate();
357 public InstanceIdentifier getManagedDataObjectType() {
358 return mockedWriter.getManagedDataObjectType();