Add ImmutableNode.newXYXBuilder() methods
[yangtools.git] / data / yang-data-tree-ri / src / test / java / org / opendaylight / yangtools / yang / data / tree / impl / Bug4454Test.java
1 /*
2  * Copyright (c) 2015 Pantheon Technologies s.r.o. 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.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertFalse;
12 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
13 import static org.junit.jupiter.api.Assertions.assertTrue;
14 import static org.junit.jupiter.api.Assertions.fail;
15
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.Optional;
19 import org.junit.jupiter.api.AfterAll;
20 import org.junit.jupiter.api.BeforeAll;
21 import org.junit.jupiter.api.BeforeEach;
22 import org.junit.jupiter.api.Test;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
28 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
30 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
32 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
33 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
34 import org.opendaylight.yangtools.yang.data.tree.api.DataTree;
35 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeConfiguration;
36 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
37 import org.opendaylight.yangtools.yang.data.tree.api.DataValidationFailedException;
38 import org.opendaylight.yangtools.yang.data.tree.impl.di.InMemoryDataTreeFactory;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
41
42 class Bug4454Test {
43
44     private static final QName MASTER_CONTAINER_QNAME = QName
45             .create("urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model", "2015-02-02",
46                     "master-container");
47     private static final QName MIN_MAX_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-list");
48     private static final QName MIN_MAX_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-leaf-list");
49     private static final QName MIN_MAX_LIST_QNAME_NO_MINMAX = QName
50             .create(MASTER_CONTAINER_QNAME, "min-max-list-no-minmax");
51     private static final QName MIN_MAX_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-key-leaf");
52     private static final QName MIN_MAX_VALUE_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-value-leaf");
53     private static final QName PRESENCE_QNAME = QName.create(MASTER_CONTAINER_QNAME, "presence");
54
55     private static final YangInstanceIdentifier MASTER_CONTAINER_PATH =
56         YangInstanceIdentifier.of(MASTER_CONTAINER_QNAME);
57     private static final YangInstanceIdentifier MIN_MAX_LIST_PATH =
58         YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME).build();
59     private static final YangInstanceIdentifier PRESENCE_PATH = YangInstanceIdentifier.of(PRESENCE_QNAME);
60     private static final YangInstanceIdentifier PRESENCE_MIN_MAX_LIST_PATH = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME);
61     private static final YangInstanceIdentifier MIN_MAX_LIST_NO_MINMAX_PATH =
62         YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME_NO_MINMAX).build();
63     private static final YangInstanceIdentifier MIN_MAX_LEAF_LIST_PATH =
64         YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LEAF_LIST_QNAME).build();
65
66     private final MapEntryNode fooEntryNodeWithValue = ImmutableNodes.newMapEntryBuilder()
67         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo"))
68         .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "footest"))
69         .build();
70     private final MapEntryNode bazEntryNodeWithValue = ImmutableNodes.newMapEntryBuilder()
71         .withNodeIdentifier(NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz"))
72         .withChild(ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "baztest"))
73         .build();
74     private final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
75             "foo");
76     private final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
77             "bar");
78     private final MapEntryNode bazEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME,
79             "baz");
80     private final SystemMapNode mapNodeBazFuzWithNodes = ImmutableNodes.newSystemMapBuilder()
81             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
82             .withChild(bazEntryNode).withChild(bazEntryNodeWithValue).withChild(fooEntryNode)
83             .build();
84     private final SystemMapNode mapNodeFooWithNodes = ImmutableNodes.newSystemMapBuilder()
85             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
86             .withChild(fooEntryNode).withChild(fooEntryNodeWithValue).withChild(barEntryNode).withChild(bazEntryNode)
87             .build();
88     private final SystemMapNode mapNodeBar = ImmutableNodes.newSystemMapBuilder()
89             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
90             .withChild(barEntryNode).build();
91     private final SystemMapNode mapNodeBaz = ImmutableNodes.newSystemMapBuilder()
92             .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME))
93             .withChild(bazEntryNode).build();
94
95     private static EffectiveModelContext schemaContext;
96
97     private DataTree inMemoryDataTree;
98
99     @BeforeAll
100     static void beforeClass() {
101         schemaContext = YangParserTestUtils.parseYang("""
102             module Bug4454Test {
103               yang-version 1;
104               namespace "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model";
105               prefix "list-constraints-validation";
106
107               revision "2015-02-02" {
108                 description "Initial revision.";
109               }
110
111               container master-container {
112                 list min-max-list {
113                   min-elements 1;
114                   max-elements 3;
115                   key "min-max-key-leaf";
116                   leaf min-max-key-leaf {
117                     type string;
118                   }
119                   leaf min-max-value-leaf {
120                     type string;
121                   }
122                 }
123
124                 list min-max-list-no-minmax {
125                   key "min-max-key-leaf";
126                   leaf min-max-key-leaf {
127                     type string;
128                   }
129                 }
130
131                 leaf-list min-max-leaf-list {
132                   min-elements 0;
133                   max-elements 10;
134                   type string;
135                 }
136               }
137
138               container presence {
139                 presence "anchor point";
140
141                 list min-max-list {
142                   min-elements 2;
143                   max-elements 3;
144
145                   key "min-max-key-leaf";
146
147                   leaf min-max-key-leaf {
148                     type string;
149                   }
150                 }
151               }
152             }""");
153     }
154
155     @AfterAll
156     static void afterClass() {
157         schemaContext = null;
158     }
159
160     @BeforeEach
161     void prepare() throws DataValidationFailedException {
162         inMemoryDataTree =  new InMemoryDataTreeFactory().create(DataTreeConfiguration.DEFAULT_OPERATIONAL,
163             schemaContext);
164         final var initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
165         final var modificationTree = initialDataTreeSnapshot.newModification();
166
167         modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.newContainerBuilder()
168             .withNodeIdentifier(new NodeIdentifier(MASTER_CONTAINER_QNAME))
169             .build());
170         modificationTree.ready();
171         inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
172     }
173
174     @Test
175     void minMaxListDeleteWriteTest() throws DataValidationFailedException {
176         final var modificationTree1 = inMemoryDataTree.takeSnapshot().newModification();
177
178         final var key = new HashMap<QName, Object>();
179         key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
180
181         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , key);
182
183         final var minMaxLeafFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
184                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
185
186         key.clear();
187         key.put(MIN_MAX_KEY_LEAF_QNAME, "NON-EXISTING-LEAF");
188
189         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, key);
190
191         final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
192                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2).build();
193
194         final var keyTemp = new HashMap<QName, Object>();
195         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "baz");
196
197         var mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
198
199         final var pathToBaz = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
200                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
201
202         keyTemp.clear();
203         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "bar");
204
205         mapEntryPathTest = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME , keyTemp);
206
207         final var pathToBar = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
208                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTest).node(MIN_MAX_VALUE_LEAF_QNAME).build();
209
210         keyTemp.clear();
211         keyTemp.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
212
213         final var mapEntryPathTestKey = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
214             keyTemp);
215
216         final var pathToKeyFoo = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
217                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPathTestKey).node(MIN_MAX_KEY_LEAF_QNAME).build();
218
219         final var newNode = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test");
220         final var newNode1 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test1");
221         final var newNode2 = ImmutableNodes.leafNode(MIN_MAX_VALUE_LEAF_QNAME, "test2");
222         final var newNodekey = ImmutableNodes.leafNode(MIN_MAX_KEY_LEAF_QNAME, "foo");
223
224         assertFalse(inMemoryDataTree.toString().contains("list"));
225
226         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
227         var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
228         assertFalse(minMaxListRead.isPresent());
229
230         modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
231         modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
232         modificationTree1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
233         modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBar);
234         modificationTree1.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
235         modificationTree1.write(pathToKeyFoo, newNodekey);
236         modificationTree1.write(pathToBaz, newNode2);
237         modificationTree1.write(pathToBaz, newNode1);
238         modificationTree1.write(pathToBaz, newNode);
239         modificationTree1.delete(minMaxLeafFoo);
240         modificationTree1.delete(minMaxLeafNel);
241
242         modificationTree1.ready();
243         inMemoryDataTree.validate(modificationTree1);
244         final var prepare = inMemoryDataTree.prepare(modificationTree1);
245         inMemoryDataTree.commit(prepare);
246
247         final var test = inMemoryDataTree.takeSnapshot();
248         testLoop(test, "bar", "test");
249
250         final var tempMod = test.newModification();
251         tempMod.write(pathToBaz, newNode2);
252         tempMod.write(pathToBaz, newNode1);
253         tempMod.merge(pathToBaz, newNode2);
254         tempMod.write(pathToBaz, newNode1);
255
256         tempMod.ready();
257         inMemoryDataTree.validate(tempMod);
258         final var prepare1 = inMemoryDataTree.prepare(tempMod);
259         inMemoryDataTree.commit(prepare1);
260
261         final var test1 = inMemoryDataTree.takeSnapshot();
262         testLoop(test1, "bar", "test1");
263
264         final var tempMod1 = test1.newModification();
265         tempMod1.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
266
267         tempMod1.ready();
268         inMemoryDataTree.validate(tempMod1);
269         final var prepare2 = inMemoryDataTree.prepare(tempMod1);
270         inMemoryDataTree.commit(prepare2);
271
272         final var test2 = inMemoryDataTree.takeSnapshot();
273         minMaxListRead = test2.readNode(MIN_MAX_LIST_PATH);
274         assertTrue(minMaxListRead.isPresent());
275         assertEquals(3, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
276
277         final var tempMod2 = test2.newModification();
278         tempMod2.write(MIN_MAX_LIST_PATH, mapNodeBaz);
279         tempMod2.write(pathToBaz, newNode2);
280
281         tempMod2.ready();
282         inMemoryDataTree.validate(tempMod2);
283         final var prepare3 = inMemoryDataTree.prepare(tempMod2);
284         inMemoryDataTree.commit(prepare3);
285
286         final var test3 = inMemoryDataTree.takeSnapshot();
287         minMaxListRead = test3.readNode(MIN_MAX_LIST_PATH);
288         assertTrue(minMaxListRead.isPresent());
289         assertEquals(1, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
290         assertTrue(minMaxListRead.orElseThrow().body().toString().contains("test2"));
291
292         final var tempMod3 = test3.newModification();
293         tempMod3.merge(MIN_MAX_LIST_PATH, mapNodeBar);
294         tempMod3.merge(pathToBar, newNode1);
295
296         tempMod3.ready();
297         inMemoryDataTree.validate(tempMod3);
298         final var prepare4 = inMemoryDataTree.prepare(tempMod3);
299         inMemoryDataTree.commit(prepare4);
300
301         final var test4 = inMemoryDataTree.takeSnapshot();
302         testLoop(test4, "test1", "test2");
303     }
304
305     @Test
306     void minMaxLeafListPass() throws DataValidationFailedException {
307         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
308
309         final var barPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "bar");
310         final var gooPath = new NodeWithValue<>(MIN_MAX_LIST_QNAME, "goo");
311
312         final var barLeafSetEntry = ImmutableNodes.leafSetEntry(barPath);
313         final var gooLeafSetEntry = ImmutableNodes.leafSetEntry(gooPath);
314
315         final var fooLeafSetNode = ImmutableNodes.newSystemLeafSetBuilder()
316                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
317                 .withChildValue("foo")
318                 .build();
319
320         modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
321         modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
322         modificationTree.ready();
323
324         inMemoryDataTree.validate(modificationTree);
325         final var prepare1 = inMemoryDataTree.prepare(modificationTree);
326         inMemoryDataTree.commit(prepare1);
327
328         final var test1 = inMemoryDataTree.takeSnapshot();
329
330         final var tempMod1 = test1.newModification();
331         tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
332         tempMod1.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
333         tempMod1.ready();
334
335         inMemoryDataTree.validate(tempMod1);
336         final var prepare2 = inMemoryDataTree.prepare(tempMod1);
337         inMemoryDataTree.commit(prepare2);
338
339         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
340         final var masterContainer = snapshotAfterCommit.readNode(MASTER_CONTAINER_PATH);
341         assertTrue(masterContainer.isPresent());
342         final var leafList = ((DistinctNodeContainer) masterContainer.orElseThrow())
343                 .findChildByArg(new NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME));
344         assertTrue(leafList.isPresent());
345         assertEquals(3, ((Optional<NormalizedNodeContainer<?>>) leafList).orElseThrow().size());
346     }
347
348     @Test
349     void minMaxListDeleteTest() throws DataValidationFailedException {
350         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
351
352
353         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
354             MIN_MAX_KEY_LEAF_QNAME, "foo");
355
356         final var minMaxLeafFoo = MASTER_CONTAINER_PATH
357                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
358
359         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
360
361         final var minMaxLeafBar = MASTER_CONTAINER_PATH
362                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
363
364         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
365
366         final var minMaxLeafBaz = MASTER_CONTAINER_PATH
367                 .node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
368
369         modificationTree.write(MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
370         modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBar);
371         modificationTree.merge(MIN_MAX_LIST_PATH, mapNodeBaz);
372         modificationTree.delete(minMaxLeafFoo);
373         modificationTree.delete(minMaxLeafBar);
374         modificationTree.delete(minMaxLeafBaz);
375
376         modificationTree.ready();
377
378         inMemoryDataTree.validate(modificationTree);
379         final var prepare = inMemoryDataTree.prepare(modificationTree);
380         inMemoryDataTree.commit(prepare);
381
382         // Empty list should have disappeared, along with the container, as we are not enforcing root
383         final var data = inMemoryDataTree.takeSnapshot().readNode(YangInstanceIdentifier.of()).orElseThrow();
384         assertInstanceOf(ContainerNode.class, data);
385         assertEquals(0, ((ContainerNode) data).size());
386     }
387
388     @Test
389     void minMaxListDeleteExceptionTest() throws DataValidationFailedException {
390         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
391
392         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME,
393             MIN_MAX_KEY_LEAF_QNAME, "foo");
394
395         final var minMaxLeafFoo = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
396
397         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
398
399         final var minMaxLeafBar = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
400
401         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "baz");
402
403         final var minMaxLeafBaz = PRESENCE_PATH.node(MIN_MAX_LIST_QNAME).node(mapEntryPath2);
404
405         modificationTree.write(PRESENCE_PATH,
406             ImmutableNodes.newContainerBuilder().withNodeIdentifier(new NodeIdentifier(PRESENCE_QNAME)).build());
407         modificationTree.write(PRESENCE_MIN_MAX_LIST_PATH, mapNodeFooWithNodes);
408         modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBar);
409         modificationTree.merge(PRESENCE_MIN_MAX_LIST_PATH, mapNodeBaz);
410         modificationTree.delete(minMaxLeafFoo);
411         modificationTree.delete(minMaxLeafBar);
412         modificationTree.delete(minMaxLeafBaz);
413
414         try {
415             // Unlike minMaxListDeleteTest(), presence container enforces the list to be present
416             modificationTree.ready();
417             fail("Should have failed with IAE");
418         } catch (IllegalArgumentException e) {
419             assertEquals("""
420                 Node (urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model?revision=2015-02-02)\
421                 presence is missing mandatory descendant /(urn:opendaylight:params:xml:ns:yang:list-constraints-\
422                 validation-test-model?revision=2015-02-02)min-max-list""", e.getMessage());
423         }
424     }
425
426     @Test
427     void minMaxListNoMinMaxDeleteTest() throws DataValidationFailedException {
428         final var fooEntryNoMinMaxNode =
429                 ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME_NO_MINMAX, MIN_MAX_KEY_LEAF_QNAME, "foo");
430         final var mapNode1 = ImmutableNodes.newSystemMapBuilder()
431                 .withNodeIdentifier(new NodeIdentifier(MIN_MAX_LIST_QNAME_NO_MINMAX))
432                 .withChild(fooEntryNoMinMaxNode).build();
433
434         final var modificationTree = inMemoryDataTree.takeSnapshot().newModification();
435
436         final var key = new HashMap<QName, Object>();
437         key.put(MIN_MAX_KEY_LEAF_QNAME, "foo");
438
439         var mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
440
441         final var minMaxLeafFoo = MASTER_CONTAINER_PATH
442                 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2);
443
444         key.clear();
445         key.put(MIN_MAX_KEY_LEAF_QNAME, "non-existing-leaf");
446
447         mapEntryPath2 = NodeIdentifierWithPredicates.of(MIN_MAX_LIST_QNAME_NO_MINMAX, key);
448
449         final var minMaxLeafNel = YangInstanceIdentifier.builder(MASTER_CONTAINER_PATH)
450                 .node(MIN_MAX_LIST_QNAME_NO_MINMAX).node(mapEntryPath2).build();
451
452         modificationTree.write(MIN_MAX_LIST_NO_MINMAX_PATH, mapNode1);
453         modificationTree.delete(minMaxLeafFoo);
454         modificationTree.delete(minMaxLeafNel);
455
456         modificationTree.ready();
457
458         inMemoryDataTree.validate(modificationTree);
459         final var prepare = inMemoryDataTree.prepare(modificationTree);
460         inMemoryDataTree.commit(prepare);
461
462         final var snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
463         final var minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_NO_MINMAX_PATH);
464
465         // Empty list should have disappeared
466         assertFalse(minMaxListRead.isPresent());
467     }
468
469     private static void testLoop(final DataTreeSnapshot snapshot, final String first, final String second) {
470         final var minMaxListRead = snapshot.readNode(MIN_MAX_LIST_PATH);
471         assertTrue(minMaxListRead.isPresent());
472         assertEquals(2, ((NormalizedNodeContainer<?>) minMaxListRead.orElseThrow()).size());
473
474         for (var collectionChild : (Collection<?>) minMaxListRead.orElseThrow().body()) {
475             if (collectionChild.toString().contains(first)) {
476                 assertTrue(collectionChild.toString().contains(first));
477             } else {
478                 assertTrue(collectionChild.toString().contains(second));
479             }
480         }
481     }
482 }