View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.tree;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNotSame;
21  import static org.junit.jupiter.api.Assertions.assertSame;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.junit.jupiter.api.Test;
33  
34  /**
35   * Test class for {@code ImmutableNode}
36   */
37  public class TestImmutableNode {
38  
39      /** Constant for a test node name. */
40      private static final String NAME = "testNode";
41  
42      /** Constant for an attribute key. */
43      private static final String ATTR = "attr";
44  
45      /** Constant for an attribute value. */
46      private static final String ATTR_VALUE = "attrValue";
47  
48      /** Constant for a test node value. */
49      private static final Integer VALUE = 42;
50  
51      /**
52       * Checks whether a node has the expected attributes.
53       *
54       * @param node the node to be checked
55       * @param expAttrs the expected attributes
56       */
57      private static void checkAttributes(final ImmutableNode node, final Map<String, ?> expAttrs) {
58          assertEquals(expAttrs, node.getAttributes());
59      }
60  
61      /**
62       * Helper method for testing whether a node contains all the attributes in the specified map.
63       *
64       * @param node the node to be checked
65       * @param attributes the map with expected attributes
66       */
67      private static void checkAttributesContained(final ImmutableNode node, final Map<String, Object> attributes) {
68          for (final Map.Entry<String, Object> e : attributes.entrySet()) {
69              assertEquals(e.getValue(), node.getAttributes().get(e.getKey()));
70          }
71      }
72  
73      /**
74       * Checks whether a node has exactly the specified children.
75       *
76       * @param parent the parent node to be checked
77       * @param children the expected children
78       */
79      private static void checkChildNodes(final ImmutableNode parent, final ImmutableNode... children) {
80          checkChildNodes(parent, Arrays.asList(children));
81      }
82  
83      /**
84       * Checks whether a node has the expected children.
85       *
86       * @param node the node to be checked
87       * @param expChildren the collection with the expected children
88       */
89      private static void checkChildNodes(final ImmutableNode node, final List<ImmutableNode> expChildren) {
90          assertEquals(expChildren, node.getChildren());
91      }
92  
93      /**
94       * Checks whether an updated node has the expected basic properties.
95       *
96       * @param org the original node
97       * @param updated the updated node
98       */
99      private static void checkUpdatedNode(final ImmutableNode org, final ImmutableNode updated) {
100         assertNotSame(org, updated);
101         assertEquals(NAME, updated.getNodeName());
102         assertEquals(VALUE, updated.getValue());
103     }
104 
105     /**
106      * Sets up a builder with default settings.
107      *
108      * @return the default builder
109      */
110     private static ImmutableNode.Builder setUpBuilder() {
111         final ImmutableNode.Builder builder = new ImmutableNode.Builder();
112         builder.name(NAME).value(VALUE);
113         return builder;
114     }
115 
116     /** A counter for generating unique child node names. */
117     private int childCounter;
118 
119     /**
120      * Helper method for testing a setAttributes() operation which has no effect.
121      *
122      * @param attributes the map with attributes
123      */
124     private void checkSetAttributesNoOp(final Map<String, Object> attributes) {
125         final ImmutableNode node = createDefaultNode(VALUE);
126         assertSame(node, node.setAttributes(attributes));
127     }
128 
129     /**
130      * Creates a default child node with a unique name.
131      *
132      * @return the new child node
133      */
134     private ImmutableNode createChild() {
135         final int idx = childCounter++;
136         return new ImmutableNode.Builder().name("Child" + idx).value("childValue" + idx).create();
137     }
138 
139     /**
140      * Creates a default node instance which can be used by tests for updating properties.
141      *
142      * @param value the value of the node
143      * @return the default node instance
144      */
145     private ImmutableNode createDefaultNode(final Object value) {
146         return createDefaultNode(NAME, value);
147     }
148 
149     /**
150      * Creates a default node instance with a variable name and value that can be used by tests for updating properties.
151      *
152      * @param name the name of the node
153      * @param value the value of the node
154      * @return the default node instance
155      */
156     private ImmutableNode createDefaultNode(final String name, final Object value) {
157         final ImmutableNode.Builder builder = new ImmutableNode.Builder(1);
158         return builder.name(name).addChild(createChild()).addAttribute("testAttr", "anotherTest").value(value).create();
159     }
160 
161     /**
162      * Tests whether addAttributes() handles null input.
163      */
164     @Test
165     void testAddAttributesNull() {
166         final ImmutableNode.Builder builder = setUpBuilder();
167         builder.addAttributes(null);
168         final ImmutableNode node = builder.create();
169         assertTrue(node.getAttributes().isEmpty());
170     }
171 
172     /**
173      * Tests whether a child node can be added.
174      */
175     @Test
176     void testAddChild() {
177         final ImmutableNode node = createDefaultNode(VALUE);
178         final ImmutableNode child2 = new ImmutableNode.Builder().name("child2").create();
179         final ImmutableNode node2 = node.addChild(child2);
180         checkUpdatedNode(node, node2);
181         checkChildNodes(node2, node.getChildren().get(0), child2);
182     }
183 
184     /**
185      * Tests whether a new null child node is rejected.
186      */
187     @Test
188     void testAddChildNull() {
189         final ImmutableNode node = createDefaultNode(VALUE);
190         assertThrows(IllegalArgumentException.class, () -> node.addChild(null));
191     }
192 
193     /**
194      * Tests whether addChildren() can deal with null input.
195      */
196     @Test
197     void testAddChildrenNull() {
198         final ImmutableNode.Builder builder = setUpBuilder();
199         builder.addChildren(null);
200         final ImmutableNode node = builder.create();
201         assertTrue(node.getChildren().isEmpty());
202     }
203 
204     /**
205      * Tests whether null entries in a collection with new child nodes are ignored.
206      */
207     @Test
208     void testAddChildrenNullElement() {
209         final ImmutableNode.Builder builder = setUpBuilder();
210         final List<ImmutableNode> children = Arrays.asList(createChild(), null, createChild());
211         builder.addChildren(children);
212         final ImmutableNode node = builder.create();
213         checkChildNodes(node, children.get(0), children.get(2));
214     }
215 
216     /**
217      * Tests that a node's attributes cannot be directly manipulated.
218      */
219     @Test
220     void testAttributesImmutable() {
221         final ImmutableNode node = setUpBuilder().create();
222         final Map<String, Object> attributes = node.getAttributes();
223         assertThrows(UnsupportedOperationException.class, () -> attributes.put("test", VALUE));
224     }
225 
226     /**
227      * Tests that a node's children cannot be manipulated.
228      */
229     @Test
230     void testChildrenImmutable() {
231         final ImmutableNode node = setUpBuilder().create();
232         final List<ImmutableNode> children = node.getChildren();
233         assertThrows(UnsupportedOperationException.class, () -> children.add(null));
234     }
235 
236     /**
237      * Tests getting named children.
238      */
239     @Test
240     void testGetChildrenByMissingName() {
241         final ImmutableNode node = createDefaultNode(VALUE);
242         final ImmutableNode child2 = new ImmutableNode.Builder().name("child2").create();
243         final ImmutableNode node2 = node.addChild(child2);
244         checkUpdatedNode(node, node2);
245         assertTrue(node2.getChildren("NotFound").isEmpty());
246     }
247 
248     /**
249      * Tests getting named children.
250      */
251     @Test
252     void testGetChildrenByName() {
253         final ImmutableNode node = createDefaultNode(VALUE);
254         final ImmutableNode child2 = new ImmutableNode.Builder().name("child2").create();
255         final ImmutableNode node2 = node.addChild(child2);
256         checkUpdatedNode(node, node2);
257         assertEquals("child2", node2.getChildren("child2").get(0).getNodeName());
258         assertEquals(child2, node2.getChildren("child2").get(0));
259     }
260 
261     /**
262      * Tests getting named children.
263      */
264     @Test
265     void testGetChildrenByNullName() {
266         final ImmutableNode node = createDefaultNode(VALUE);
267         final ImmutableNode child2 = new ImmutableNode.Builder().name("child2").create();
268         final ImmutableNode node2 = node.addChild(child2);
269         checkUpdatedNode(node, node2);
270         assertTrue(node2.getChildren(null).isEmpty());
271     }
272 
273     /**
274      * Tests whether multiple child nodes can be added to a builder.
275      */
276     @Test
277     void testNodeWithAddMultipleChildren() {
278         final int childCount = 4;
279         final List<ImmutableNode> childNodes = new ArrayList<>(childCount);
280         for (int i = 0; i < childCount; i++) {
281             final ImmutableNode.Builder childBuilder = new ImmutableNode.Builder();
282             final ImmutableNode child = childBuilder.name(NAME + i).value(i).create();
283             childNodes.add(child);
284         }
285         final ImmutableNode.Builder builder = setUpBuilder();
286         final ImmutableNode node = builder.addChildren(childNodes).create();
287         checkChildNodes(node, childNodes);
288     }
289 
290     /**
291      * Tests whether a node with attributes can be created.
292      */
293     @Test
294     void testNodeWithAttributes() {
295         final ImmutableNode.Builder builder = setUpBuilder();
296         final int attrCount = 4;
297         final Map<String, Object> attrs = new HashMap<>();
298         for (int i = 0; i < attrCount; i++) {
299             final String attrName = NAME + i;
300             attrs.put(attrName, i);
301             builder.addAttribute(attrName, i);
302         }
303         final ImmutableNode node = builder.create();
304         checkAttributes(node, attrs);
305     }
306 
307     /**
308      * Tests that the map of attributes cannot be changed by a later manipulation of the builder.
309      */
310     @Test
311     void testNodeWithAttributesManipulateLater() {
312         final ImmutableNode.Builder builder = setUpBuilder();
313         builder.addAttribute(ATTR, ATTR_VALUE);
314         final ImmutableNode node = builder.create();
315         builder.addAttribute("attr2", "a2");
316         assertEquals(1, node.getAttributes().size());
317         assertEquals(ATTR_VALUE, node.getAttributes().get(ATTR));
318     }
319 
320     /**
321      * Tests whether child nodes can be added.
322      */
323     @Test
324     void testNodeWithChildren() {
325         final int childCount = 8;
326         final List<ImmutableNode> childNodes = new ArrayList<>(childCount);
327         final ImmutableNode.Builder builder = new ImmutableNode.Builder(childCount);
328         for (int i = 0; i < childCount; i++) {
329             final ImmutableNode.Builder childBuilder = new ImmutableNode.Builder();
330             final ImmutableNode child = childBuilder.name(NAME + i).value(i).create();
331             builder.addChild(child);
332             childNodes.add(child);
333         }
334         final ImmutableNode node = builder.name(NAME).create();
335         checkChildNodes(node, childNodes);
336     }
337 
338     /**
339      * Tests that the list of children cannot be changed by a later manipulation of the builder.
340      */
341     @Test
342     void testNodeWithChildrenManipulateLater() {
343         final ImmutableNode.Builder builder = setUpBuilder();
344         final ImmutableNode child = new ImmutableNode.Builder().name("Child").create();
345         final ImmutableNode node = builder.addChild(child).create();
346         builder.addChild(new ImmutableNode.Builder().name("AnotherChild").create());
347         checkChildNodes(node, Collections.singletonList(child));
348     }
349 
350     /**
351      * Tests whether multiple attributes can be added in a single operation.
352      */
353     @Test
354     void testNodeWithMultipleAttributes() {
355         final ImmutableNode.Builder builder = setUpBuilder();
356         final int attrCount = 4;
357         final Map<String, Object> attrs = new HashMap<>();
358         for (int i = 0; i < attrCount; i++) {
359             final String attrName = NAME + i;
360             attrs.put(attrName, i);
361         }
362         final ImmutableNode node = builder.addAttributes(attrs).create();
363         checkAttributes(node, attrs);
364     }
365 
366     /**
367      * Tests whether the builder ignores a null child node.
368      */
369     @Test
370     void testNodeWithNullChild() {
371         final ImmutableNode.Builder builder = setUpBuilder();
372         builder.addChild(null);
373         final ImmutableNode node = builder.create();
374         checkChildNodes(node);
375     }
376 
377     /**
378      * Tests whether an existing attribute can be removed.
379      */
380     @Test
381     void testRemoveAttributeExisting() {
382         final ImmutableNode node = createDefaultNode(VALUE);
383         final String attrName = node.getAttributes().keySet().iterator().next();
384         final ImmutableNode node2 = node.removeAttribute(attrName);
385         checkUpdatedNode(node, node2);
386         assertSame(node.getChildren(), node2.getChildren());
387         assertTrue(node2.getAttributes().isEmpty());
388     }
389 
390     /**
391      * Tests removeAttribute() if the attribute does not exist.
392      */
393     @Test
394     void testRemoveAttributeNotExisting() {
395         final ImmutableNode node = createDefaultNode(VALUE);
396         assertSame(node, node.removeAttribute(ATTR));
397     }
398 
399     /**
400      * Tests whether a child node can be removed.
401      */
402     @Test
403     void testRemoveChildExisting() {
404         final ImmutableNode node = createDefaultNode(VALUE);
405         final ImmutableNode child = node.getChildren().get(0);
406         final ImmutableNode node2 = node.removeChild(child);
407         checkUpdatedNode(node, node2);
408         checkChildNodes(node2);
409     }
410 
411     /**
412      * Tests whether the correct child node is removed if there are multiple.
413      */
414     @Test
415     void testRemoveChildMultiple() {
416         final ImmutableNode childRemove = createChild();
417         final ImmutableNode node = createDefaultNode(VALUE).addChild(createChild()).addChild(childRemove).addChild(createChild());
418         final ImmutableNode node2 = node.removeChild(childRemove);
419         checkChildNodes(node2, node.getChildren().get(0), node.getChildren().get(1), node.getChildren().get(3));
420     }
421 
422     /**
423      * Tests whether the behavior of removeChildNode() if the node in question is not found.
424      */
425     @Test
426     void testRemoveChildNodeNotExisting() {
427         final ImmutableNode node = createDefaultNode(VALUE);
428         assertSame(node, node.removeChild(null));
429     }
430 
431     /**
432      * Tests whether a child node can be replaced by another one.
433      */
434     @Test
435     void testReplaceChildExisting() {
436         final ImmutableNode childRemove = createChild();
437         final ImmutableNode childReplace = createChild();
438         final ImmutableNode node = createDefaultNode(VALUE).addChild(childRemove);
439         final ImmutableNode node2 = node.replaceChild(childRemove, childReplace);
440         checkUpdatedNode(node, node2);
441         checkChildNodes(node2, node.getChildren().get(0), childReplace);
442     }
443 
444     /**
445      * Tests replaceChild() if the child node cannot be found.
446      */
447     @Test
448     void testReplaceChildNotExisting() {
449         final ImmutableNode node = createDefaultNode(VALUE);
450         assertSame(node, node.replaceChild(createChild(), createChild()));
451     }
452 
453     /**
454      * Tries to replace a child node by null.
455      */
456     @Test
457     void testReplaceChildNull() {
458         final ImmutableNode node = createDefaultNode(VALUE);
459         final ImmutableNode child = createChild();
460         assertThrows(IllegalArgumentException.class, () -> node.replaceChild(child, null));
461     }
462 
463     /**
464      * Tests whether all children can be replaced at once.
465      */
466     @Test
467     void testReplaceChildren() {
468         final int childCount = 8;
469         final List<ImmutableNode> newChildren = new ArrayList<>(childCount);
470         for (int i = 0; i < childCount; i++) {
471             newChildren.add(createChild());
472         }
473         final ImmutableNode node = createDefaultNode(VALUE);
474         final ImmutableNode node2 = node.replaceChildren(newChildren);
475         checkUpdatedNode(node, node2);
476         checkChildNodes(node2, newChildren);
477     }
478 
479     /**
480      * Tests whether a node's children can be replaced by a null collection.
481      */
482     @Test
483     void testReplaceChildrenNullCollection() {
484         final ImmutableNode node = createDefaultNode(VALUE);
485         final ImmutableNode node2 = node.replaceChildren(null);
486         checkUpdatedNode(node, node2);
487         checkChildNodes(node2);
488     }
489 
490     /**
491      * Tests whether attribute values can be set.
492      */
493     @Test
494     void testSetAttribute() {
495         final ImmutableNode node = createDefaultNode(VALUE);
496         final ImmutableNode node2 = node.setAttribute("attr", ATTR_VALUE);
497         checkUpdatedNode(node, node2);
498         assertSame(node.getChildren(), node2.getChildren());
499         final Map<String, Object> newAttrs = new HashMap<>(node.getAttributes());
500         newAttrs.put(ATTR, ATTR_VALUE);
501         checkAttributes(node2, newAttrs);
502     }
503 
504     /**
505      * Tests whether an attribute can be overridden.
506      */
507     @Test
508     void testSetAttributeOverride() {
509         final ImmutableNode.Builder builder = setUpBuilder();
510         final String attr2 = ATTR + "_other";
511         final Map<String, Object> attrs = new HashMap<>();
512         attrs.put(ATTR, ATTR_VALUE);
513         attrs.put(attr2, "someValue");
514         final ImmutableNode node = builder.addAttributes(attrs).create();
515         final ImmutableNode node2 = node.setAttribute(attr2, VALUE);
516         attrs.put(attr2, VALUE);
517         checkAttributes(node2, attrs);
518     }
519 
520     /**
521      * Tests whether multiple attributes can be set.
522      */
523     @Test
524     void testSetAttributes() {
525         final ImmutableNode node = createDefaultNode(VALUE);
526         final Map<String, Object> attributes = new HashMap<>();
527         attributes.put("newAttribute1", "value1");
528         attributes.put("newAttribute2", "value2");
529         final ImmutableNode node2 = node.setAttributes(attributes);
530         assertEquals(attributes.size() + node.getAttributes().size(), node2.getAttributes().size());
531         checkAttributesContained(node2, attributes);
532         checkAttributesContained(node2, node.getAttributes());
533     }
534 
535     /**
536      * Tests setAttributes() if an empty map is passed in.
537      */
538     @Test
539     void testSetAttributesEmpty() {
540         checkSetAttributesNoOp(new HashMap<>());
541     }
542 
543     /**
544      * Tests setAttributes() for null input.
545      */
546     @Test
547     void testSetAttributesNull() {
548         checkSetAttributesNoOp(null);
549     }
550 
551     /**
552      * Tests whether the name of a node can be changed for a new instance.
553      */
554     @Test
555     void testSetName() {
556         final ImmutableNode node = createDefaultNode("anotherName", VALUE);
557         final ImmutableNode node2 = node.setName(NAME);
558         checkUpdatedNode(node, node2);
559         assertSame(node.getChildren(), node2.getChildren());
560         assertSame(node.getAttributes(), node2.getAttributes());
561     }
562 
563     /**
564      * Tests whether a new node with a changed value can be created.
565      */
566     @Test
567     void testSetValue() {
568         final ImmutableNode node = createDefaultNode("test");
569         final ImmutableNode node2 = node.setValue(VALUE);
570         checkUpdatedNode(node, node2);
571         assertSame(node.getChildren(), node2.getChildren());
572         assertSame(node.getAttributes(), node2.getAttributes());
573     }
574 
575     /**
576      * Tests whether a node with basic properties can be created.
577      */
578     @Test
579     void testSimpleProperties() {
580         final ImmutableNode node = setUpBuilder().create();
581         assertEquals(NAME, node.getNodeName());
582         assertTrue(node.getChildren().isEmpty());
583         assertTrue(node.getAttributes().isEmpty());
584     }
585 
586     /**
587      * Tests streaming children.
588      */
589     @Test
590     void testStream() {
591         final ImmutableNode node = createDefaultNode(VALUE);
592         final ImmutableNode child2 = new ImmutableNode.Builder().name("child2").create();
593         final ImmutableNode node2 = node.addChild(child2);
594         checkUpdatedNode(node, node2);
595         assertEquals(1, node.stream().count());
596         assertEquals(0, child2.stream().count());
597         assertEquals(2, node2.stream().count());
598         assertEquals(1, node.stream().count());
599     }
600 
601 }