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