Rename opendaylight.mdsal.binding.runtime.spi
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / ConcurrentTreeModificationTest.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.tree.impl;
9
10 import static org.junit.jupiter.api.Assertions.assertFalse;
11 import static org.junit.jupiter.api.Assertions.assertNotNull;
12 import static org.junit.jupiter.api.Assertions.assertTrue;
13 import static org.junit.jupiter.api.Assertions.fail;
14
15 import java.util.Optional;
16 import org.junit.jupiter.api.BeforeEach;
17 import org.junit.jupiter.api.Test;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
22 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
24 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
25 import org.opendaylight.yangtools.yang.data.tree.api.ConflictingModificationAppliedException;
26 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
27 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeCandidate;
28 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
29 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
30 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 class ConcurrentTreeModificationTest extends AbstractTestModelTest {
35     private static final Logger LOG = LoggerFactory.getLogger(ConcurrentTreeModificationTest.class);
36
37     private static final Short ONE_ID = 1;
38     private static final Short TWO_ID = 2;
39
40     private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(
41         TestModel.OUTER_LIST_PATH)
42             .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID)
43             .build();
44
45     private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(
46         TestModel.OUTER_LIST_PATH)
47             .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID)
48             .build();
49
50     private static final MapEntryNode FOO_NODE = ImmutableNodes.newMapEntryBuilder()
51         .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID))
52         .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, ONE_ID))
53         .withChild(ImmutableNodes.newSystemMapBuilder()
54             .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_LIST_QNAME))
55             .build())
56         .build();
57
58     private static final MapEntryNode BAR_NODE = ImmutableNodes.newMapEntryBuilder()
59         .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID))
60         .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, TWO_ID))
61         .withChild(ImmutableNodes.newSystemMapBuilder()
62             .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_LIST_QNAME))
63             .build())
64         .build();
65
66     private DataTree inMemoryDataTree;
67
68
69     @BeforeEach
70     void prepare() {
71         inMemoryDataTree = new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
72             SCHEMA_CONTEXT);
73     }
74
75     private static ContainerNode createFooTestContainerNode() {
76         return ImmutableNodes.newContainerBuilder()
77             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
78             .withChild(ImmutableNodes.newSystemMapBuilder()
79                 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
80                 .withChild(FOO_NODE)
81                 .build())
82             .build();
83     }
84
85     private static ContainerNode createBarTestContainerNode() {
86         return ImmutableNodes.newContainerBuilder()
87             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
88             .withChild(ImmutableNodes.newSystemMapBuilder()
89                 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
90                 .withChild(BAR_NODE)
91                 .build())
92             .build();
93     }
94
95     private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
96         assertNotNull(potential);
97         assertTrue(potential.isPresent());
98         assertTrue(type.isInstance(potential.orElseThrow()));
99         return type.cast(potential.orElseThrow());
100     }
101
102     @Test
103     void writeWrite1stLevelEmptyTreeTest() throws DataValidationFailedException {
104         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
105
106         final var modificationTree1 = initialDataTreeSnapshot.newModification();
107         final var modificationTree2 = initialDataTreeSnapshot.newModification();
108
109         modificationTree1.write(TestModel.TEST_PATH,
110             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build());
111         modificationTree2.write(TestModel.TEST_PATH,
112             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build());
113
114         modificationTree1.ready();
115         modificationTree2.ready();
116         inMemoryDataTree.validate(modificationTree1);
117         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
118         inMemoryDataTree.commit(prepare1);
119
120         try {
121             inMemoryDataTree.validate(modificationTree2);
122             fail("Exception should have been thrown.");
123         } catch (final ConflictingModificationAppliedException ex) {
124             LOG.debug("ConflictingModificationAppliedException - was thrown as expected", ex);
125         }
126         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
127         inMemoryDataTree.commit(prepare2);
128
129         final var testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
130         assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
131     }
132
133     @Test
134     void writeMerge1stLevelEmptyTreeTest() throws DataValidationFailedException {
135         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
136
137         final var modificationTree1 = initialDataTreeSnapshot.newModification();
138         final var modificationTree2 = initialDataTreeSnapshot.newModification();
139
140         modificationTree1.write(TestModel.TEST_PATH,
141             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build());
142         modificationTree2.merge(TestModel.TEST_PATH,
143             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME)).build());
144
145         modificationTree1.ready();
146         modificationTree2.ready();
147
148         inMemoryDataTree.validate(modificationTree1);
149         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
150         inMemoryDataTree.commit(prepare1);
151
152         inMemoryDataTree.validate(modificationTree2);
153         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
154         inMemoryDataTree.commit(prepare2);
155
156         final var testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
157         assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
158     }
159
160     @Test
161     void writeWriteFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
162         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
163
164         final var modificationTree1 = initialDataTreeSnapshot.newModification();
165         final var modificationTree2 = initialDataTreeSnapshot.newModification();
166
167         modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
168         modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
169         modificationTree1.ready();
170         modificationTree2.ready();
171
172         inMemoryDataTree.validate(modificationTree1);
173         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
174         inMemoryDataTree.commit(prepare1);
175
176         try {
177             inMemoryDataTree.validate(modificationTree2);
178             fail("Exception should have been thrown.");
179             final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
180             inMemoryDataTree.commit(prepare2);
181         } catch (final ConflictingModificationAppliedException ex) {
182             LOG.debug("ConflictingModificationAppliedException - was thrown as expected", ex);
183         }
184
185         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
186         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
187         assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
188     }
189
190     @Test
191     void writeMergeFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
192         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
193
194         final var modificationTree1 = initialDataTreeSnapshot.newModification();
195         final var modificationTree2 = initialDataTreeSnapshot.newModification();
196
197         modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
198         modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
199         modificationTree1.ready();
200         modificationTree2.ready();
201
202         inMemoryDataTree.validate(modificationTree1);
203         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
204         inMemoryDataTree.commit(prepare1);
205
206         inMemoryDataTree.validate(modificationTree2);
207         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
208         inMemoryDataTree.commit(prepare2);
209
210         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
211         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
212         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
213     }
214
215     @Test
216     void mergeWriteFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
217         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
218
219         final var modificationTree1 = initialDataTreeSnapshot.newModification();
220         final var modificationTree2 = initialDataTreeSnapshot.newModification();
221
222         modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
223         modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
224         modificationTree1.ready();
225         modificationTree2.ready();
226
227         inMemoryDataTree.validate(modificationTree1);
228         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
229         inMemoryDataTree.commit(prepare1);
230
231         try {
232             inMemoryDataTree.validate(modificationTree2);
233             fail("Exception should have been thrown.");
234             final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
235             inMemoryDataTree.commit(prepare2);
236         } catch (final ConflictingModificationAppliedException ex) {
237             LOG.debug("ConflictingModificationAppliedException - was thrown as expected", ex);
238         }
239
240         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
241         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
242         assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
243     }
244
245     @Test
246     void mergeMergeFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
247         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
248
249         final var modificationTree1 = initialDataTreeSnapshot.newModification();
250         final var modificationTree2 = initialDataTreeSnapshot.newModification();
251
252         modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
253         modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
254         modificationTree1.ready();
255         modificationTree2.ready();
256
257         inMemoryDataTree.validate(modificationTree1);
258         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
259         inMemoryDataTree.commit(prepare1);
260
261         inMemoryDataTree.validate(modificationTree2);
262         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
263         inMemoryDataTree.commit(prepare2);
264
265         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
266         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
267         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
268     }
269
270     @Test
271     void writeWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
272         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
273         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
274         initialDataTreeModification.ready();
275         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
276         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
277
278         final var modificationTree1 = initialDataTreeSnapshot.newModification();
279         final var modificationTree2 = initialDataTreeSnapshot.newModification();
280
281         modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
282         modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
283         modificationTree1.ready();
284         modificationTree2.ready();
285
286         inMemoryDataTree.validate(modificationTree1);
287         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
288         inMemoryDataTree.commit(prepare1);
289
290         try {
291             inMemoryDataTree.validate(modificationTree2);
292             fail("Exception should have been thrown.");
293             final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
294             inMemoryDataTree.commit(prepare2);
295         } catch (final ConflictingModificationAppliedException ex) {
296             LOG.debug("ConflictingModificationAppliedException was thrown as expected", ex);
297         }
298
299         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
300         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
301         assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
302     }
303
304     @Test
305     void writeMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
306         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
307         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
308         initialDataTreeModification.ready();
309         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
310         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
311
312         final var modificationTree1 = initialDataTreeSnapshot.newModification();
313         final var modificationTree2 = initialDataTreeSnapshot.newModification();
314
315         modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
316         modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
317         modificationTree1.ready();
318         modificationTree2.ready();
319
320         inMemoryDataTree.validate(modificationTree1);
321         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
322         inMemoryDataTree.commit(prepare1);
323
324         inMemoryDataTree.validate(modificationTree2);
325         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
326         inMemoryDataTree.commit(prepare2);
327
328         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
329         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
330         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
331     }
332
333     @Test
334     void mergeWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
335         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
336         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
337         initialDataTreeModification.ready();
338         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
339         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
340
341         final var modificationTree1 = initialDataTreeSnapshot.newModification();
342         final var modificationTree2 = initialDataTreeSnapshot.newModification();
343
344         modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
345         modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
346         modificationTree1.ready();
347         modificationTree2.ready();
348
349         inMemoryDataTree.validate(modificationTree1);
350         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
351         inMemoryDataTree.commit(prepare1);
352
353         try {
354             inMemoryDataTree.validate(modificationTree2);
355             fail("Exception should have been thrown.");
356             final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
357             inMemoryDataTree.commit(prepare2);
358         } catch (final ConflictingModificationAppliedException ex) {
359             LOG.debug("ConflictingModificationAppliedException was thrown as expected", ex);
360         }
361
362
363         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
364         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
365         assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
366     }
367
368     @Test
369     void mergeMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
370         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
371         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
372         initialDataTreeModification.ready();
373         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
374         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
375
376         final var modificationTree1 = initialDataTreeSnapshot.newModification();
377         final var modificationTree2 = initialDataTreeSnapshot.newModification();
378
379         modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
380         modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
381         modificationTree1.ready();
382         modificationTree2.ready();
383
384         inMemoryDataTree.validate(modificationTree1);
385         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
386         inMemoryDataTree.commit(prepare1);
387
388         inMemoryDataTree.validate(modificationTree2);
389         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
390         inMemoryDataTree.commit(prepare2);
391
392         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
393         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
394         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
395     }
396
397     @Test
398     void deleteWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
399         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
400         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
401         initialDataTreeModification.ready();
402         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
403         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
404
405         final var modificationTree1 = initialDataTreeSnapshot.newModification();
406         final var modificationTree2 = initialDataTreeSnapshot.newModification();
407
408         modificationTree1.delete(TestModel.TEST_PATH);
409         modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
410         modificationTree1.ready();
411         modificationTree2.ready();
412
413         inMemoryDataTree.validate(modificationTree1);
414         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
415         inMemoryDataTree.commit(prepare1);
416
417         try {
418             inMemoryDataTree.validate(modificationTree2);
419             fail("Exception should have been thrown.");
420             final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
421             inMemoryDataTree.commit(prepare2);
422         } catch (final ConflictingModificationAppliedException ex) {
423             LOG.debug("ConflictingModificationAppliedException was thrown as expected", ex);
424         }
425
426
427         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
428         assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
429     }
430
431     @Test
432     void deleteMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
433         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
434         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
435         initialDataTreeModification.ready();
436         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
437         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
438
439         final var modificationTree1 = initialDataTreeSnapshot.newModification();
440         final var modificationTree2 = initialDataTreeSnapshot.newModification();
441
442         modificationTree1.delete(TestModel.TEST_PATH);
443         modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
444         modificationTree1.ready();
445         modificationTree2.ready();
446
447         inMemoryDataTree.validate(modificationTree1);
448         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
449         inMemoryDataTree.commit(prepare1);
450
451         inMemoryDataTree.validate(modificationTree2);
452         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
453         inMemoryDataTree.commit(prepare2);
454
455         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
456         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
457     }
458
459     @Test
460     void writeWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
461         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
462         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
463         initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.newSystemMapBuilder()
464             .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
465             .build());
466         initialDataTreeModification.ready();
467         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
468         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
469
470         final var modificationTree1 = initialDataTreeSnapshot.newModification();
471         final var modificationTree2 = initialDataTreeSnapshot.newModification();
472
473         modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
474         modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
475         modificationTree1.ready();
476         modificationTree2.ready();
477
478         inMemoryDataTree.validate(modificationTree1);
479         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
480         inMemoryDataTree.commit(prepare1);
481
482         inMemoryDataTree.validate(modificationTree2);
483         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
484         inMemoryDataTree.commit(prepare2);
485
486         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
487         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
488         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
489     }
490
491     @Test
492     void writeMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
493         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
494         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
495         initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.newSystemMapBuilder()
496             .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
497             .build());
498         initialDataTreeModification.ready();
499         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
500         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
501
502         final var modificationTree1 = initialDataTreeSnapshot.newModification();
503         final var modificationTree2 = initialDataTreeSnapshot.newModification();
504
505         modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
506         modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
507         modificationTree1.ready();
508         modificationTree2.ready();
509
510         inMemoryDataTree.validate(modificationTree1);
511         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
512         inMemoryDataTree.commit(prepare1);
513
514         inMemoryDataTree.validate(modificationTree2);
515         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
516         inMemoryDataTree.commit(prepare2);
517
518         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
519         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
520         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
521     }
522
523     @Test
524     void mergeWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
525         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
526         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
527         initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.newSystemMapBuilder()
528             .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
529             .build());
530         initialDataTreeModification.ready();
531         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
532         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
533
534         final var modificationTree1 = initialDataTreeSnapshot.newModification();
535         final var modificationTree2 = initialDataTreeSnapshot.newModification();
536
537         modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
538         modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
539         modificationTree1.ready();
540         modificationTree2.ready();
541
542         inMemoryDataTree.validate(modificationTree1);
543         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
544         inMemoryDataTree.commit(prepare1);
545
546         inMemoryDataTree.validate(modificationTree2);
547         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
548         inMemoryDataTree.commit(prepare2);
549
550         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
551         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
552         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
553     }
554
555     @Test
556     void mergeMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
557         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
558         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
559         initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.newSystemMapBuilder()
560             .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
561             .build());
562         initialDataTreeModification.ready();
563         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
564         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
565
566         final var modificationTree1 = initialDataTreeSnapshot.newModification();
567         final var modificationTree2 = initialDataTreeSnapshot.newModification();
568
569         modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
570         modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
571         modificationTree1.ready();
572         modificationTree2.ready();
573
574         inMemoryDataTree.validate(modificationTree1);
575         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
576         inMemoryDataTree.commit(prepare1);
577
578         inMemoryDataTree.validate(modificationTree2);
579         final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
580         inMemoryDataTree.commit(prepare2);
581
582         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
583         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
584         assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
585     }
586
587     @Test
588     void deleteWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
589         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
590         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
591         initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.newSystemMapBuilder()
592             .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
593             .build());
594         initialDataTreeModification.ready();
595         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
596         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
597
598         final var modificationTree1 = initialDataTreeSnapshot.newModification();
599         final var modificationTree2 = initialDataTreeSnapshot.newModification();
600
601         modificationTree1.delete(TestModel.TEST_PATH);
602         modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
603         modificationTree1.ready();
604         modificationTree2.ready();
605
606         inMemoryDataTree.validate(modificationTree1);
607         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
608         inMemoryDataTree.commit(prepare1);
609
610         try {
611             inMemoryDataTree.validate(modificationTree2);
612             final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
613             inMemoryDataTree.commit(prepare2);
614             fail("Exception should have been thrown");
615         } catch (final ConflictingModificationAppliedException e) {
616             LOG.debug("Exception was thrown because path no longer exist in tree", e);
617         }
618
619         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
620         assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
621     }
622
623     @Test
624     void deleteMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
625         final var initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
626         initialDataTreeModification.write(TestModel.TEST_PATH, emptyContainer(TestModel.TEST_QNAME));
627         initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.newSystemMapBuilder()
628             .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
629             .build());
630         initialDataTreeModification.ready();
631         inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
632         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
633
634         final var modificationTree1 = initialDataTreeSnapshot.newModification();
635         final var modificationTree2 = initialDataTreeSnapshot.newModification();
636
637         modificationTree1.delete(TestModel.TEST_PATH);
638         modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
639         modificationTree1.ready();
640         modificationTree2.ready();
641
642         inMemoryDataTree.validate(modificationTree1);
643         final var prepare1 = inMemoryDataTree.prepare(modificationTree1);
644         inMemoryDataTree.commit(prepare1);
645
646         try {
647             inMemoryDataTree.validate(modificationTree2);
648             final var prepare2 = inMemoryDataTree.prepare(modificationTree2);
649             inMemoryDataTree.commit(prepare2);
650             fail("Exception should have been thrown");
651         } catch (final ConflictingModificationAppliedException e) {
652             LOG.debug("Exception was thrown because path no longer exist in tree", e);
653         }
654
655         final var snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
656         assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
657     }
658
659     private static ContainerNode emptyContainer(final QName name) {
660         return ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(name)).build();
661     }
662 }