HONEYCOMB-431: add validation support to Writers
[honeycomb.git] / infra / translate-impl / src / test / java / io / fd / honeycomb / translate / impl / write / registry / FlatWriterRegistryTest.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package io.fd.honeycomb.translate.impl.write.registry;
18
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;
33
34 import com.google.common.base.Optional;
35 import com.google.common.collect.HashMultimap;
36 import com.google.common.collect.ImmutableMap;
37 import com.google.common.collect.ImmutableMultimap;
38 import com.google.common.collect.Lists;
39 import com.google.common.collect.Multimap;
40 import com.google.common.collect.Sets;
41 import io.fd.honeycomb.translate.util.DataObjects;
42 import io.fd.honeycomb.translate.util.DataObjects.DataObject1;
43 import io.fd.honeycomb.translate.util.DataObjects.DataObject2;
44 import io.fd.honeycomb.translate.write.DataObjectUpdate;
45 import io.fd.honeycomb.translate.write.WriteContext;
46 import io.fd.honeycomb.translate.write.WriteFailedException;
47 import io.fd.honeycomb.translate.write.Writer;
48 import io.fd.honeycomb.translate.write.registry.UpdateFailedException;
49 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.List;
53 import javax.annotation.Nonnull;
54 import javax.annotation.Nullable;
55 import org.junit.Before;
56 import org.junit.Test;
57 import org.mockito.InOrder;
58 import org.mockito.Mock;
59 import org.mockito.MockitoAnnotations;
60 import org.mockito.stubbing.Answer;
61 import org.opendaylight.yangtools.yang.binding.DataObject;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
64
65 public class FlatWriterRegistryTest {
66
67     @Mock
68     private Writer<DataObject1> writer1;
69     @Mock
70     private Writer<DataObject2> writer2;
71     @Mock
72     private Writer<DataObjects.DataObject3> writer3;
73     @Mock
74     private Writer<DataObjects.DataObject1ChildK> writer4;
75     @Mock
76     private WriteContext ctx;
77     @Mock
78     private WriteContext revertWriteContext;
79
80     @Before
81     public void setUp() throws Exception {
82         MockitoAnnotations.initMocks(this);
83         when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
84         when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
85         when(writer3.getManagedDataObjectType()).thenReturn(DataObjects.DataObject3.IID);
86         when(writer4.getManagedDataObjectType()).thenReturn(DataObjects.DataObject1ChildK.IID);
87         // TODO - HONEYCOMB-412 - thenCallRealMethod doest work with default methods
88         // https://stackoverflow.com/questions/27663252/can-you-make-mockito-1-10-17-work-with-default-methods-in-interfaces
89         when(writer1.canProcess(any())).thenAnswer(answerWithImpl());
90         when(writer2.canProcess(any())).thenAnswer(answerWithImpl());
91         when(writer3.canProcess(any())).thenAnswer(answerWithImpl());
92         when(writer4.canProcess(any())).thenAnswer(answerWithImpl());
93     }
94
95     private static Answer<Object> answerWithImpl() {
96         return invocationOnMock -> new CheckedMockWriter(Writer.class.cast(invocationOnMock.getMock())).canProcess(
97                 InstanceIdentifier.class.cast(invocationOnMock.getArguments()[0]));
98     }
99
100     @Test
101     public void testSubtreeWriterUpdateAggregation() throws Exception {
102         Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
103
104         when(ctx.readAfter(DataObject1.IID)).thenReturn(Optional.of(mock(DataObject1.class)));
105         when(ctx.readBefore(DataObject1.IID)).thenReturn(Optional.of(mock(DataObject1.class)));
106
107         Writer<?> writer = SubtreeWriter.createForWriter(Collections.singleton(DataObjects.DataObject1ChildK.IID), writer1);
108
109         InstanceIdentifier<DataObjects.DataObject1ChildK> update1Id = DataObject1.IID.child(DataObjects.DataObject1ChildK.class, new DataObjects.DataObject1ChildKey());
110         InstanceIdentifier<DataObjects.DataObject1ChildK> update2Id = DataObject1.IID.child(DataObjects.DataObject1ChildK.class, new DataObjects.DataObject1ChildKey());
111         updates.putAll(DataObjects.DataObject1ChildK.IID,
112                 Lists.newArrayList(
113                         DataObjectUpdate.create(update1Id, mock(DataObjects.DataObject1ChildK.class), mock(DataObjects.DataObject1ChildK.class)),
114                         DataObjectUpdate.create(update2Id, mock(DataObjects.DataObject1ChildK.class), mock(DataObjects.DataObject1ChildK.class))));
115
116         Collection<DataObjectUpdate> parentDataObjectUpdate = FlatWriterRegistry.getParentDataObjectUpdate(ctx, updates, writer);
117         // Just a single update, since there are 2 child updates for a container, they get reduced
118         assertEquals(1, parentDataObjectUpdate.size());
119     }
120
121     @Test
122     public void testSubtreeWriterUpdateAggregationForList() throws Exception {
123         Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
124
125         KeyedInstanceIdentifier<DataObjects.DataObject1ChildK, DataObjects.DataObject1ChildKey> parentKeyedId1 =
126                 DataObject1.IID.child(DataObjects.DataObject1ChildK.class, new DataObjects.DataObject1ChildKey());
127         KeyedInstanceIdentifier<DataObjects.DataObject1ChildK, DataObjects.DataObject1ChildKey> parentKeyedId2 =
128                 DataObject1.IID.child(DataObjects.DataObject1ChildK.class, new DataObjects.DataObject1ChildKey());
129
130         when(ctx.readBefore(parentKeyedId1)).thenReturn(Optional.of(mock(DataObjects.DataObject1ChildK.class)));
131         when(ctx.readAfter(parentKeyedId1)).thenReturn(Optional.of(mock(DataObjects.DataObject1ChildK.class)));
132         when(ctx.readBefore(parentKeyedId2)).thenReturn(Optional.of(mock(DataObjects.DataObject1ChildK.class)));
133         when(ctx.readAfter(parentKeyedId2)).thenReturn(Optional.of(mock(DataObjects.DataObject1ChildK.class)));
134
135         Writer<?> writer = SubtreeWriter.createForWriter(Sets.newHashSet(
136                 InstanceIdentifier.create(DataObjects.DataObject1ChildK.class).child(DataObjects.DataObject1ChildK.DataObject1ChildKNested.class),
137                 InstanceIdentifier.create(DataObjects.DataObject1ChildK.class).child(DataObjects.DataObject1ChildK.DataObject1ChildKNested2.class)),
138                 writer4);
139
140         InstanceIdentifier<DataObjects.DataObject1ChildK.DataObject1ChildKNested> updateList1Id = parentKeyedId1.child(DataObjects.DataObject1ChildK.DataObject1ChildKNested.class);
141         InstanceIdentifier<DataObjects.DataObject1ChildK.DataObject1ChildKNested> updateList2Id = parentKeyedId2.child(DataObjects.DataObject1ChildK.DataObject1ChildKNested.class);
142         updates.putAll(DataObjects.DataObject1ChildK.DataObject1ChildKNested.IID,
143                 Lists.newArrayList(
144                         DataObjectUpdate.create(updateList1Id, mock(DataObjects.DataObject1ChildK.DataObject1ChildKNested.class), mock(DataObjects.DataObject1ChildK.DataObject1ChildKNested.class)),
145                         DataObjectUpdate.create(updateList2Id, mock(DataObjects.DataObject1ChildK.DataObject1ChildKNested.class), mock(DataObjects.DataObject1ChildK.DataObject1ChildKNested.class))));
146
147         Collection<DataObjectUpdate> parentDataObjectUpdate = FlatWriterRegistry.getParentDataObjectUpdate(ctx, updates, writer);
148         // 2 updates for 2 different list items
149         assertEquals(2, parentDataObjectUpdate.size());
150     }
151
152     @Test
153     public void testMultipleUpdatesForSingleWriter() throws Exception {
154         final FlatWriterRegistry flatWriterRegistry =
155                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
156
157         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
158         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
159         final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class);
160         final DataObject1 dataObject = mock(DataObject1.class);
161         updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
162         updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject));
163         flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
164
165         verify(writer1).processModification(iid, dataObject, dataObject, ctx);
166         verify(writer1).processModification(iid2, dataObject, dataObject, ctx);
167         // Invoked when registry is being created
168         verifyNoMoreInteractions(writer1);
169         verifyZeroInteractions(writer2);
170     }
171
172     @Test
173     public void testMultipleUpdatesForMultipleWriters() throws Exception {
174         final FlatWriterRegistry flatWriterRegistry =
175                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
176
177         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
178         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
179         final DataObject1 dataObject = mock(DataObject1.class);
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);
183         updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
184         flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
185
186         final InOrder inOrder = inOrder(writer1, writer2);
187         inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
188         inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
189
190         // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
191         // we are really interested just in invocations of processModification(),so adding specific verify to check that
192         verify(writer1,times(1)).processModification(any(),any(),any(),any());
193         verify(writer2,times(1)).processModification(any(),any(),any(),any());
194         //verifyNoMoreInteractions(writer1);
195         //verifyNoMoreInteractions(writer2);
196     }
197
198     @Test
199     public void testMultipleDeletesForMultipleWriters() throws Exception {
200         final FlatWriterRegistry flatWriterRegistry =
201                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
202
203         final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
204         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
205         final DataObject1 dataObject = mock(DataObject1.class);
206         deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
207         final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
208         final DataObject2 dataObject2 = mock(DataObject2.class);
209         deletes.put(
210                 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
211         flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx);
212
213         final InOrder inOrder = inOrder(writer1, writer2);
214         // Reversed order of invocation, first writer2 and then writer1
215         inOrder.verify(writer2).processModification(iid2, dataObject2, null, ctx);
216         inOrder.verify(writer1).processModification(iid, dataObject, null, ctx);
217
218         // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
219         // we are really interested just in invocations of processModification(),so adding specific verify to check that
220         verify(writer1,times(1)).processModification(any(),any(),any(),any());
221         verify(writer2,times(1)).processModification(any(),any(),any(),any());
222         //verifyNoMoreInteractions(writer1);
223         //verifyNoMoreInteractions(writer2);
224     }
225
226     @Test
227     public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception {
228         final FlatWriterRegistry flatWriterRegistry =
229                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
230
231         final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
232         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
233         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
234         final DataObject1 dataObject = mock(DataObject1.class);
235         // Writer 1 delete
236         deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
237         // Writer 1 update
238         updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
239         final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
240         final DataObject2 dataObject2 = mock(DataObject2.class);
241         // Writer 2 delete
242         deletes.put(
243                 DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
244         // Writer 2 update
245         updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
246         flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx);
247
248         final InOrder inOrder = inOrder(writer1, writer2);
249         // Reversed order of invocation, first writer2 and then writer1 for deletes
250         inOrder.verify(writer2).processModification(iid2, dataObject2, null, ctx);
251         inOrder.verify(writer1).processModification(iid, dataObject, null, ctx);
252         // Then also updates are processed
253         inOrder.verify(writer1).processModification(iid, dataObject, dataObject, ctx);
254         inOrder.verify(writer2).processModification(iid2, dataObject2, dataObject2, ctx);
255
256         // TODO - HONEYCOMB-412 -reintroduce verifyNoMoreInteractions and remove manual verify
257         // we are really interested just in invocations of processModification(),so adding specific verify to check that
258         verify(writer1,times(2)).processModification(any(),any(),any(),any());
259         verify(writer2,times(2)).processModification(any(),any(),any(),any());
260         //verifyNoMoreInteractions(writer1);
261         //verifyNoMoreInteractions(writer2);
262     }
263
264     @Test(expected = IllegalArgumentException.class)
265     public void testMultipleUpdatesOneMissing() throws Exception {
266         final FlatWriterRegistry flatWriterRegistry =
267                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
268
269         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
270         addUpdate(updates, DataObject1.class);
271         addUpdate(updates, DataObject2.class);
272         flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
273     }
274
275     @Test
276     public void testMultipleUpdatesFirstFailing() throws Exception {
277         final FlatWriterRegistry flatWriterRegistry =
278                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
279
280         // Writer1 always fails
281         doThrow(new RuntimeException()).when(writer1)
282                 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
283
284         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
285         addUpdate(updates, DataObject1.class);
286         addUpdate(updates, DataObject2.class);
287
288         try {
289             flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
290             fail("Bulk update should have failed on writer1 with UpdateFailedException");
291         } catch (UpdateFailedException e) {
292             assertThat(e.getProcessed(), hasSize(0));// very first update failed
293         }
294     }
295
296     @Test
297     public void testMultipleUpdatesSecondFailing() throws Exception {
298         final FlatWriterRegistry flatWriterRegistry =
299                 new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
300
301         // Writer2 always fails
302         doThrow(new RuntimeException()).when(writer2)
303                 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
304
305         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
306         addUpdate(updates, DataObject1.class);
307         addUpdate(updates, DataObject2.class);
308
309         try {
310             flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
311             fail("Bulk update should have failed on writer1 with UpdateFailedException");
312         } catch (UpdateFailedException e) {
313             final List<DataObjectUpdate> alreadyProcessed = e.getProcessed();
314             assertThat(alreadyProcessed, hasSize(1));// very first update failed
315             assertEquals(updateData(DataObject1.class, DataObject1.IID),
316                     e.getProcessed().iterator().next());
317         }
318     }
319
320     @Test
321     public void testMultipleUpdatesLastFailing() throws Exception {
322         final FlatWriterRegistry flatWriterRegistry =
323                 new FlatWriterRegistry(
324                         ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObjects.DataObject3.IID, writer3));
325
326         // Writer1 always fails
327         doThrow(new RuntimeException()).when(writer3)
328                 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
329
330         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
331         addUpdate(updates, DataObject1.class);
332         addUpdate(updates, DataObject2.class);
333         addUpdate(updates, DataObjects.DataObject3.class);
334
335         try {
336             flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
337             fail("Bulk update should have failed on writer1 with UpdateFailedException");
338         } catch (UpdateFailedException e) {
339             final List<DataObjectUpdate> alreadyProcessed = e.getProcessed();
340             assertEquals(2, alreadyProcessed.size());
341             assertTrue(alreadyProcessed.contains(updateData(DataObject1.class, DataObject1.IID)));
342             assertTrue(alreadyProcessed.contains(updateData(DataObject2.class, DataObject2.IID)));
343         }
344     }
345
346     @Test
347     public void testMutlipleUpdatesWithOneKeyedContainer() throws Exception {
348         final FlatWriterRegistry flatWriterRegistry =
349                 new FlatWriterRegistry(
350                         ImmutableMap.of(DataObject1.IID, writer1, DataObjects.DataObject1ChildK.IID, writer4));
351
352         // Writer1 always fails
353         doThrow(new RuntimeException()).when(writer1)
354                 .processModification(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class),
355                         any(WriteContext.class));
356
357         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
358         addKeyedUpdate(updates, DataObjects.DataObject1ChildK.class);
359         addUpdate(updates, DataObject1.class);
360         try {
361             flatWriterRegistry.processModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
362             fail("Bulk update should have failed on writer1 with UpdateFailedException");
363         } catch (UpdateFailedException e) {
364             assertTrue(e.getProcessed().isEmpty());
365         }
366     }
367
368     @Test(expected = IllegalArgumentException.class)
369     public void testValidateMissingWriter() throws Exception {
370         final FlatWriterRegistry flatWriterRegistry =
371             new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
372
373         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
374         addUpdate(updates, DataObject1.class);
375         addUpdate(updates, DataObject2.class);
376         flatWriterRegistry.validateModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
377     }
378
379     @Test
380     public void testValidateSingleWriter() throws Exception {
381         final FlatWriterRegistry flatWriterRegistry =
382             new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
383
384         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
385         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
386         final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class);
387         final DataObject1 dataObject = mock(DataObject1.class);
388         updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
389         updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject));
390         flatWriterRegistry
391             .validateModifications(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
392
393         verify(writer1).validate(iid, dataObject, dataObject, ctx);
394         verify(writer1).validate(iid2, dataObject, dataObject, ctx);
395         // Invoked when registry is being created
396         verifyNoMoreInteractions(writer1);
397         verifyZeroInteractions(writer2);
398     }
399
400     @Test
401     public void testValidateMultipleWriters() throws Exception {
402         final FlatWriterRegistry flatWriterRegistry =
403             new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
404
405         final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
406         final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
407         final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
408         final DataObject1 dataObject = mock(DataObject1.class);
409         // Writer 1 delete
410         deletes.put(DataObject1.IID,
411             ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
412         // Writer 1 create
413         updates.put(DataObject1.IID, DataObjectUpdate.create(iid, null, dataObject));
414         final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
415         final DataObject2 dataObject2 = mock(DataObject2.class);
416         // Writer 2 delete
417         deletes.put(DataObject2.IID,
418             ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
419         // Writer 2 update
420         updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
421         flatWriterRegistry.validateModifications(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx);
422
423         // Ignore order
424         verify(writer1).validate(iid, dataObject, null, ctx);
425         verify(writer1).validate(iid, null, dataObject, ctx);
426         verify(writer2).validate(iid2, dataObject2, null, ctx);
427         verify(writer2).validate(iid2, dataObject2, dataObject2, ctx);
428
429         verifyNoMoreInteractions(writer1);
430         verifyNoMoreInteractions(writer2);
431     }
432
433     private <D extends DataObject> void addKeyedUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
434                                                        final Class<D> type) throws Exception {
435         final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
436         final InstanceIdentifier<D> keyedIid = (InstanceIdentifier<D>) type.getDeclaredField("INTERNALLY_KEYED_IID").get(null);
437         updates.put(iid, DataObjectUpdate.create(keyedIid, mock(type), mock(type)));
438     }
439
440     private <D extends DataObject> void addUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
441                                                   final Class<D> type) throws Exception {
442         final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
443         updates.put(iid, updateData(type, iid));
444     }
445
446     private static <D extends DataObject> DataObjectUpdate updateData(final Class<D> type,
447                                                                       final InstanceIdentifier<D> iid) {
448         return DataObjectUpdate.create(iid, mock(type), mock(type));
449     }
450
451     //TODO - HONEYCOMB-412 - remove after
452     /**
453      * Used to utilize default implementation of canProcess()
454      * */
455     static class CheckedMockWriter implements Writer{
456
457         private final Writer mockedWriter;
458
459         CheckedMockWriter(final Writer mockedWriter) {
460             this.mockedWriter = mockedWriter;
461         }
462
463         @Override
464         public void processModification(@Nonnull final InstanceIdentifier id, @Nullable final DataObject dataBefore,
465                                         @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx)
466                 throws WriteFailedException {
467             mockedWriter.processModification(id,dataBefore,dataAfter,ctx);
468         }
469
470         @Override
471         public boolean supportsDirectUpdate() {
472             return mockedWriter.supportsDirectUpdate();
473         }
474
475         @Nonnull
476         @Override
477         public InstanceIdentifier getManagedDataObjectType() {
478             return mockedWriter.getManagedDataObjectType();
479         }
480     }
481 }