HONEYCOMB-359 - Wildcarded writers
[honeycomb.git] / infra / it / it-test / src / test / java / io / fd / honeycomb / data / impl / HoneycombWriteInfraTest.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.data.impl;
18
19 import static org.junit.Assert.fail;
20 import static org.mockito.Matchers.any;
21 import static org.mockito.Matchers.eq;
22 import static org.mockito.Mockito.atLeastOnce;
23 import static org.mockito.Mockito.inOrder;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.verifyNoMoreInteractions;
28 import static org.mockito.Mockito.when;
29
30 import com.google.common.collect.Lists;
31 import com.google.common.collect.Sets;
32 import io.fd.honeycomb.data.DataModification;
33 import io.fd.honeycomb.test.model.Ids;
34 import io.fd.honeycomb.translate.impl.write.registry.FlatWriterRegistryBuilder;
35 import io.fd.honeycomb.translate.util.YangDAG;
36 import io.fd.honeycomb.translate.write.WriteContext;
37 import io.fd.honeycomb.translate.write.WriteFailedException;
38 import io.fd.honeycomb.translate.write.Writer;
39 import io.fd.honeycomb.translate.write.registry.WriterRegistry;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.List;
44 import java.util.Map;
45 import javax.annotation.Nonnull;
46 import javax.annotation.Nullable;
47 import org.junit.Test;
48 import org.mockito.InOrder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoiceBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.Choice;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3Builder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGroupingBuilder;
74 import org.opendaylight.yangtools.yang.binding.DataObject;
75 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
76 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
77 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
78 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
80 import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
81 import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
82
83 /**
84  * Testing honeycomb writes from data tree up to mocked writers.
85  */
86 public class HoneycombWriteInfraTest extends AbstractInfraTest {
87
88     private TipProducingDataTree dataTree;
89     private WriterRegistry writerRegistry;
90
91     Writer<SimpleContainer> simpleContainerWriter = mockWriter(Ids.SIMPLE_CONTAINER_ID);
92     Writer<ComplexAugment> complexAugmentWriter = mockWriter(Ids.COMPLEX_AUGMENT_ID);
93     Writer<ComplexAugmentContainer> complexAugmentContainerWriter = mockWriter(Ids.COMPLEX_AUGMENT_CONTAINER_ID);
94     Writer<SimpleAugment> simpleAugmentWriter = mockWriter(Ids.SIMPLE_AUGMENT_ID);
95
96     Writer<ContainerWithList> containerWithListWriter = mockWriter(Ids.CONTAINER_WITH_LIST_ID);
97     Writer<ListInContainer> listInContainerWriter = mockWriter(Ids.LIST_IN_CONTAINER_ID);
98     Writer<ContainerInList> containerInListWriter = mockWriter(Ids.CONTAINER_IN_LIST_ID);
99     Writer<NestedList> nestedListWriter = mockWriter(Ids.NESTED_LIST_ID);
100
101     Writer<ContainerWithChoice> containerWithChoiceWriter = mockWriter(Ids.CONTAINER_WITH_CHOICE_ID);
102     Writer<ContainerFromGrouping> containerFromGroupingWriter = mockWriter(Ids.CONTAINER_FROM_GROUPING_ID);
103     Writer<C3> c3Writer = mockWriter(Ids.C3_ID);
104
105
106     private static <D extends DataObject> Writer<D> mockWriter(final InstanceIdentifier<D> id) {
107         final Writer<D> mock = (Writer<D>) mock(Writer.class);
108         when(mock.getManagedDataObjectType()).thenReturn(id);
109         //TODO - HONEYCOMB-412 - to call default impl of canProcess()
110         when(mock.canProcess(any())).thenAnswer(invocationOnMock -> {
111             final Writer writer = Writer.class.cast(invocationOnMock.getMock());
112             final Writer delegatingWriter = new Writer() {
113                 @Nonnull
114                 @Override
115                 public InstanceIdentifier getManagedDataObjectType() {
116                     return writer.getManagedDataObjectType();
117                 }
118
119                 @Override
120                 public boolean supportsDirectUpdate() {
121                     return writer.supportsDirectUpdate();
122                 }
123
124                 @Override
125                 public void processModification(@Nonnull final InstanceIdentifier id,
126                                                 @Nullable final DataObject dataBefore,
127                                                 @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx)
128                         throws WriteFailedException {
129                     writer.processModification(id, dataBefore, dataAfter, ctx);
130                 }
131             };
132             return delegatingWriter.canProcess(InstanceIdentifier.class.cast(invocationOnMock.getArguments()[0]));
133         });
134         return mock;
135     }
136
137     @Override
138     void postSetup() {
139         initDataTree();
140         initWriterRegistry();
141     }
142
143     private void initDataTree() {
144         dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
145         dataTree.setSchemaContext(schemaContext);
146     }
147
148     private void initWriterRegistry() {
149         writerRegistry = new FlatWriterRegistryBuilder(new YangDAG())
150                 .add(complexAugmentWriter) // unordered
151                 .add(nestedListWriter) // 6
152                 .addAfter(listInContainerWriter, Ids.NESTED_LIST_ID) // 7
153                 .addAfter(containerInListWriter, Ids.LIST_IN_CONTAINER_ID) // 8
154                 .addAfter(containerWithListWriter, Ids.CONTAINER_IN_LIST_ID) // 9
155                 .addBefore(containerFromGroupingWriter, Ids.NESTED_LIST_ID) // 5
156                 .addBefore(containerWithChoiceWriter, Ids.CONTAINER_FROM_GROUPING_ID) // 4
157                 .addBefore(simpleContainerWriter, Ids.CONTAINER_WITH_CHOICE_ID) // 3
158                 .addBefore(c3Writer, Ids.SIMPLE_CONTAINER_ID) // 2
159                 .addBefore(simpleAugmentWriter, Ids.SIMPLE_CONTAINER_ID) // 2
160                 .addBefore(complexAugmentContainerWriter, Sets.newHashSet(Ids.C3_ID, Ids.SIMPLE_AUGMENT_ID)) // 1
161                 .build();
162     }
163
164     @Test
165     public void testWriteEmptyNonPresenceContainer() throws Exception {
166         final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
167                 new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
168
169         final DataModification dataModification = modifiableDataTreeDelegator.newModification();
170         final SimpleContainer data = new SimpleContainerBuilder()
171                 .build();
172
173         final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
174                 serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, data);
175         dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
176
177         dataModification.commit();
178
179         verify(simpleContainerWriter).getManagedDataObjectType();
180         verifyNoMoreInteractions(simpleContainerWriter);
181     }
182
183     @Test
184     public void testWriteEverything() throws Exception {
185         final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
186                 new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
187
188         final DataModification dataModification = modifiableDataTreeDelegator.newModification();
189         // Now write everything we can
190         writeSimpleContainer(dataModification);
191         writeContainerWithChoice(dataModification);
192         writeContainerWithList(dataModification);
193         dataModification.commit();
194
195         final Writer<?>[] orderedWriters = getOrderedWriters();
196         final InOrder inOrder = inOrder(orderedWriters);
197         verifyOrderedWrites(orderedWriters, inOrder);
198     }
199
200     private void verifyOrderedWrites(final Writer<?>[] orderedWriters, final InOrder inOrder)
201             throws WriteFailedException {
202         // Modifications are not produced for nodes that do not contain any actual leaves (except when choice is a child)
203         // Unordered
204         // verify(complexAugmentWriter).update(eq(COMPLEX_AUGMENT_ID), eq(null), eq(getComplexAugment()), any(WriteContext.class));
205         // 1
206         inOrder.verify(complexAugmentContainerWriter)
207                 .processModification(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(null), eq(getComplexAugmentContainer()), any(WriteContext.class));
208         // 2
209         inOrder.verify(c3Writer)
210                 .processModification(eq(Ids.C3_ID), eq(null), eq(getC3()), any(WriteContext.class));
211         // 2
212         verify(simpleAugmentWriter)
213                 .processModification(eq(Ids.SIMPLE_AUGMENT_ID), eq(null), eq(getSimpleAugment()), any(WriteContext.class));
214         // 3
215         inOrder.verify(simpleContainerWriter)
216                 .processModification(eq(Ids.SIMPLE_CONTAINER_ID), eq(null), eq(getSimpleContainer()), any(WriteContext.class));
217         // 4
218         inOrder.verify(containerWithChoiceWriter)
219                 .processModification(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(getContainerWithChoiceWithComplexCase()), any(WriteContext.class));
220         // 5
221         inOrder.verify(containerFromGroupingWriter)
222                 .processModification(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(null), eq(getContainerFromGrouping()), any(WriteContext.class));
223
224         final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer1 =
225                 Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1));
226         final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList1 =
227                 keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1"));
228         final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer2 =
229                 Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2));
230         final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList2 =
231                 keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2"));
232
233         // 6 - two items
234         inOrder.verify(nestedListWriter)
235                 .processModification(eq(keyedNestedList1), eq(null), eq(getSingleNestedList("1")), any(WriteContext.class));
236         verify(nestedListWriter)
237                 .processModification(eq(keyedNestedList2), eq(null), eq(getSingleNestedList("2")), any(WriteContext.class));
238
239         // 7 - two items
240         inOrder.verify(listInContainerWriter)
241                 .processModification(eq(keyedListInContainer1), eq(null), eq(getSingleListInContainer((long)1)), any(WriteContext.class));
242         verify(listInContainerWriter)
243                 .processModification(eq(keyedListInContainer2), eq(null), eq(getSingleListInContainer((long)2)), any(WriteContext.class));
244
245         // 8
246         inOrder.verify(containerInListWriter)
247                 .processModification(eq(keyedListInContainer1.child(ContainerInList.class)), eq(null), eq(getContainerInList("1")), any(WriteContext.class));
248         verify(containerInListWriter)
249                 .processModification(eq(keyedListInContainer2.child(ContainerInList.class)), eq(null), eq(getContainerInList("2")), any(WriteContext.class));
250
251         // 9 - Ignored because the container has no leaves, only complex child nodes
252         // inOrder.verify(containerWithListWriter)
253         // .update(eq(CONTAINER_WITH_LIST_ID), eq(null), eq(getContainerWithList()), any(WriteContext.class));
254
255         for (Writer<?> orderedWriter : orderedWriters) {
256             verify(orderedWriter).getManagedDataObjectType();
257             //TODO - HONEYCOMB-412
258             //verifyNoMoreInteractions(orderedWriter);
259         }
260
261         verify(complexAugmentContainerWriter, times(1)).processModification(any(), any(), any(), any());
262         verify(c3Writer, times(1)).processModification(any(), any(), any(), any());
263         verify(simpleAugmentWriter, times(1)).processModification(any(), any(), any(), any());
264         verify(simpleContainerWriter, times(1)).processModification(any(), any(), any(), any());
265         verify(containerWithChoiceWriter, times(1)).processModification(any(), any(), any(), any());
266         verify(containerFromGroupingWriter, times(1)).processModification(any(), any(), any(), any());
267         verify(nestedListWriter, times(2)).processModification(any(), any(), any(), any());
268         verify(listInContainerWriter, times(2)).processModification(any(), any(), any(), any());
269         verify(containerInListWriter, times(2)).processModification(any(), any(), any(), any());
270     }
271
272     private Writer<?>[] getOrderedWriters() {
273         return new Writer<?>[]{complexAugmentWriter, // Unordered
274                     complexAugmentContainerWriter, // 1
275                     c3Writer, // 2
276                     simpleAugmentWriter, // 2
277                     simpleContainerWriter, // 3
278                     containerWithChoiceWriter, // 4
279                     containerFromGroupingWriter, // 5
280                     nestedListWriter, // 6
281                     listInContainerWriter, // 7
282                     containerInListWriter, // 8
283                     containerWithListWriter};
284     }
285
286     @Test
287     public void testDeletes() throws Exception {
288         final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
289                 new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
290
291         DataModification dataModification = modifiableDataTreeDelegator.newModification();
292         // Now write everything we can
293         writeSimpleContainer(dataModification);
294         writeContainerWithChoice(dataModification);
295         writeContainerWithList(dataModification);
296         dataModification.commit();
297         // Verify writes to be able to verifyNoMore interactions at the end
298         verifyOrderedWrites(getOrderedWriters(), inOrder(getOrderedWriters()));
299
300         dataModification = modifiableDataTreeDelegator.newModification();
301         deleteSimpleContainer(dataModification);
302         deleteContainerWithChoice(dataModification);
303         deleteContainerWithList(dataModification);
304         dataModification.commit();
305
306         final Writer<?>[] orderedWriters = getOrderedWriters();
307         Collections.reverse(Arrays.asList(orderedWriters));
308         final InOrder inOrder = inOrder(orderedWriters);
309
310         final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer1 =
311                 Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1));
312         final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList1 =
313                 keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1"));
314         final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer2 =
315                 Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2));
316         final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList2 =
317                 keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2"));
318
319         // Deletes are handled in reverse order
320         // 1
321         inOrder.verify(containerInListWriter)
322                 .processModification(eq(keyedListInContainer1.child(ContainerInList.class)), eq(getContainerInList("1")), eq(null), any(WriteContext.class));
323         verify(containerInListWriter)
324                 .processModification(eq(keyedListInContainer2.child(ContainerInList.class)), eq(getContainerInList("2")), eq(null), any(WriteContext.class));
325
326         // 2
327         inOrder.verify(listInContainerWriter)
328                 .processModification(eq(keyedListInContainer1), eq(getSingleListInContainer((long)1)), eq(null),  any(WriteContext.class));
329         verify(listInContainerWriter)
330                 .processModification(eq(keyedListInContainer2), eq(getSingleListInContainer((long)2)), eq(null), any(WriteContext.class));
331
332         // 3
333         inOrder.verify(nestedListWriter)
334                 .processModification(eq(keyedNestedList1), eq(getSingleNestedList("1")), eq(null), any(WriteContext.class));
335         verify(nestedListWriter)
336                 .processModification(eq(keyedNestedList2), eq(getSingleNestedList("2")), eq(null), any(WriteContext.class));
337         // 4
338         inOrder.verify(containerFromGroupingWriter)
339                 .processModification(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(getContainerFromGrouping()), eq(null), any(WriteContext.class));
340         // 5
341         inOrder.verify(containerWithChoiceWriter)
342                 .processModification(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(getContainerWithChoiceWithComplexCase()), eq(null), any(WriteContext.class));
343         // 6
344         inOrder.verify(simpleContainerWriter)
345                 .processModification(eq(Ids.SIMPLE_CONTAINER_ID), eq(getSimpleContainer()), eq(null), any(WriteContext.class));
346         // 7
347         verify(simpleAugmentWriter)
348                 .processModification(eq(Ids.SIMPLE_AUGMENT_ID), eq(getSimpleAugment()), eq(null), any(WriteContext.class));
349         // 8
350         inOrder.verify(c3Writer)
351                 .processModification(eq(Ids.C3_ID), eq(getC3()), eq(null), any(WriteContext.class));
352         // 9
353         inOrder.verify(complexAugmentContainerWriter)
354                 .processModification(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(getComplexAugmentContainer()), eq(null), any(WriteContext.class));
355
356         for (Writer<?> orderedWriter : orderedWriters) {
357             verify(orderedWriter).getManagedDataObjectType();
358             //TODO - HONEYCOMB-412
359             // verifyNoMoreInteractions(orderedWriter);
360         }
361
362         verify(complexAugmentContainerWriter, times(2)).processModification(any(), any(), any(), any());
363         verify(c3Writer, times(2)).processModification(any(), any(), any(), any());
364         verify(simpleAugmentWriter, times(2)).processModification(any(), any(), any(), any());
365         verify(simpleContainerWriter, times(2)).processModification(any(), any(), any(), any());
366         verify(containerWithChoiceWriter, times(2)).processModification(any(), any(), any(), any());
367         verify(containerFromGroupingWriter, times(2)).processModification(any(), any(), any(), any());
368         verify(nestedListWriter, times(4)).processModification(any(), any(), any(), any());
369         verify(listInContainerWriter, times(4)).processModification(any(), any(), any(), any());
370         verify(containerInListWriter, times(4)).processModification(any(), any(), any(), any());
371     }
372
373     private void writeContainerWithList(final DataModification dataModification) {
374         final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
375                 serializer.toNormalizedNode(Ids.CONTAINER_WITH_LIST_ID, getContainerWithList());
376         dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
377     }
378
379     private void deleteContainerWithList(final DataModification dataModification) {
380         dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_LIST_ID));
381     }
382
383     private ContainerWithList getContainerWithList() {
384         return new ContainerWithListBuilder()
385                 .setListInContainer(getListInContainer((long)1, (long)2))
386                 .build();
387     }
388
389     private List<ListInContainer> getListInContainer(final Long... keys) {
390         final ArrayList<ListInContainer> objects = Lists.newArrayList();
391         for (Long key : keys) {
392             objects.add(getSingleListInContainer(key));
393         }
394         return objects;
395     }
396
397     private ListInContainer getSingleListInContainer(final Long key) {
398         return new ListInContainerBuilder()
399                 .setId(key)
400                 .setContainerInList(getContainerInList(Long.toString(key)))
401                 .build();
402     }
403
404     private ContainerInList getContainerInList(String... nestedKeys) {
405         return new ContainerInListBuilder()
406                 .setName("inlist")
407                 .setNestedList(getNestedList(nestedKeys))
408                 .build();
409     }
410
411     private List<NestedList> getNestedList(String... keys) {
412         final ArrayList<NestedList> nestedList = new ArrayList<>();
413         for (String key : keys) {
414             nestedList.add(getSingleNestedList(key));
415         }
416         return nestedList;
417     }
418
419     private NestedList getSingleNestedList(final String key) {
420         return new NestedListBuilder()
421                 .setNestedId(key)
422                 .setNestedName(key)
423                 .build();
424     }
425
426     private void writeContainerWithChoice(final DataModification dataModification) {
427         writeContainerWithChoice(dataModification, getContainerWithChoiceWithComplexCase());
428     }
429
430
431     private void deleteContainerWithChoice(final DataModification dataModification) {
432         dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_CHOICE_ID));
433     }
434
435     private void writeContainerWithChoice(final DataModification dataModification,
436                                           final ContainerWithChoice containerWithChoice) {
437         final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
438                 serializer.toNormalizedNode(Ids.CONTAINER_WITH_CHOICE_ID, containerWithChoice);
439         dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
440     }
441
442     private ContainerWithChoice getContainerWithChoiceWithComplexCase() {
443         return new ContainerWithChoiceBuilder()
444                 .setLeafFromGrouping("fromG")
445                 .setName("name")
446                 .setContainerFromGrouping(getContainerFromGrouping())
447                 .setChoice(getComplexCase())
448                 .build();
449     }
450
451     private Choice getComplexCase() {
452         return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.C3Builder()
453                 .setC3(getC3())
454                 .build();
455     }
456
457     private C3 getC3() {
458         return new C3Builder()
459                 .setName("c3")
460                 .build();
461     }
462
463     private void writeContainerFromGrouping(final DataModification dataModification) {
464         final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
465                 serializer.toNormalizedNode(Ids.CONTAINER_FROM_GROUPING_ID, getContainerFromGrouping());
466         dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
467     }
468
469     private ContainerFromGrouping getContainerFromGrouping() {
470         return new ContainerFromGroupingBuilder()
471                 .setLeafInContainerFromGrouping(111)
472                 .build();
473     }
474
475
476     private void writeSimpleContainer(final DataModification dataModification) {
477         final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
478                 serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, getSimpleContainer());
479         dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
480     }
481
482     private void deleteSimpleContainer(final DataModification dataModification) {
483         final YangInstanceIdentifier yangId =
484                 serializer.toYangInstanceIdentifier(Ids.SIMPLE_CONTAINER_ID);
485         dataModification.delete(yangId);
486     }
487
488     private SimpleContainer getSimpleContainer() {
489         return new SimpleContainerBuilder()
490                     .setSimpleContainerName("n")
491                     .addAugmentation(SimpleAugment.class, getSimpleAugment())
492                     .addAugmentation(ComplexAugment.class, getComplexAugment())
493                     .build();
494     }
495
496     private ComplexAugment getComplexAugment() {
497         return new ComplexAugmentBuilder()
498                 .setComplexAugmentContainer(getComplexAugmentContainer())
499                 .build();
500     }
501
502     private ComplexAugmentContainer getComplexAugmentContainer() {
503         return new ComplexAugmentContainerBuilder().setSomeLeaf("s").build();
504     }
505
506     private SimpleAugment getSimpleAugment() {
507         return new SimpleAugmentBuilder().setSimpleAugmentLeaf("a").build();
508     }
509
510     @Test
511     public void testWriteAndDeleteInTx() throws Exception {
512         final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
513                 new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
514
515         final DataModification dataModification = modifiableDataTreeDelegator.newModification();
516         // Now write everything we can
517         writeSimpleContainer(dataModification);
518         deleteSimpleContainer(dataModification);
519         dataModification.commit();
520
521         verify(simpleContainerWriter).getManagedDataObjectType();
522         // No modification
523         verifyNoMoreInteractions(simpleContainerWriter);
524     }
525
526     @Test
527     public void testSubtreeWriter() throws Exception {
528         writerRegistry = new FlatWriterRegistryBuilder(new YangDAG())
529                 // Handles also container from grouping
530                 .subtreeAdd(Sets.newHashSet(Ids.CONTAINER_FROM_GROUPING_ID), containerWithChoiceWriter)
531                 .build();
532
533         final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
534                 new ModifiableDataTreeDelegator(serializer, dataTree, schemaContext, writerRegistry, contextBroker);
535
536         final ContainerWithChoice containerWithChoice =
537                 new ContainerWithChoiceBuilder().setContainerFromGrouping(getContainerFromGrouping()).build();
538
539         // Test write subtree node
540         DataModification dataModification = modifiableDataTreeDelegator.newModification();
541         writeContainerFromGrouping(dataModification);
542         dataModification.commit();
543
544         verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType();
545         verify(containerWithChoiceWriter)
546                 .processModification(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(containerWithChoice), any(WriteContext.class));
547         verifyNoMoreInteractions(containerWithChoiceWriter);
548
549         // Test delete sub-node
550         dataModification = modifiableDataTreeDelegator.newModification();
551         final ContainerWithChoice containerWithChoiceEmpty = new ContainerWithChoiceBuilder().build();
552         deleteContainerFromGrouping(dataModification);
553         dataModification.commit();
554
555         verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType();
556         verify(containerWithChoiceWriter)
557                 .processModification(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(containerWithChoice), eq(containerWithChoiceEmpty), any(WriteContext.class));
558         verifyNoMoreInteractions(containerWithChoiceWriter);
559
560         // Test write with subtree node that's not handled by subtree writer
561         dataModification = modifiableDataTreeDelegator.newModification();
562         writeContainerWithChoice(dataModification);
563         try {
564             dataModification.commit();
565             fail("Missing writer for C3 should occur");
566         } catch (IllegalArgumentException e) {
567             return;
568         }
569     }
570
571     private void deleteContainerFromGrouping(final DataModification dataModification) {
572         dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_FROM_GROUPING_ID));
573     }
574 }