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