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.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNull;
22  import static org.junit.jupiter.api.Assertions.assertThrows;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.junit.jupiter.api.BeforeEach;
34  import org.junit.jupiter.api.Test;
35  
36  /**
37   * A test class for {@code InMemoryNodeModel} which tests functionality related to node references. This test class
38   * creates a model for the test structure with authors data. Each node is associated a string reference object with the
39   * node name. It can then be checked whether updates of the hierarchy do not affect the references.
40   */
41  public class TestInMemoryNodeModelReferences {
42  
43      /** A mock resolver. */
44      private NodeKeyResolver<ImmutableNode> resolver;
45  
46      /** The test model. */
47      private InMemoryNodeModel model;
48  
49      /**
50       * Returns a flat collection of all nodes contained in the specified nodes hierarchy.
51       *
52       * @param root the root node of the hierarchy
53       * @return a collection with all nodes in this hierarchy
54       */
55      private Collection<ImmutableNode> collectNodes(final ImmutableNode root) {
56          final Set<ImmutableNode> nodes = new HashSet<>();
57          NodeTreeWalker.INSTANCE.walkBFS(root, new ConfigurationNodeVisitorAdapter<ImmutableNode>() {
58              @Override
59              public void visitBeforeChildren(final ImmutableNode node, final NodeHandler<ImmutableNode> handler) {
60                  nodes.add(node);
61              }
62          }, model.getNodeHandler());
63          return nodes;
64      }
65  
66      /**
67       * Creates the initial reference data for the test hierarchy.
68       *
69       * @return the map with reference data
70       */
71      private Map<ImmutableNode, String> createReferences() {
72          final Collection<ImmutableNode> nodes = collectNodes(NodeStructureHelper.ROOT_AUTHORS_TREE);
73          nodes.remove(NodeStructureHelper.ROOT_AUTHORS_TREE);
74          final Map<ImmutableNode, String> refs = new HashMap<>();
75          for (final ImmutableNode node : nodes) {
76              refs.put(node, node.getNodeName());
77          }
78          return refs;
79      }
80  
81      @BeforeEach
82      public void setUp() throws Exception {
83          resolver = NodeStructureHelper.createResolverMock();
84          NodeStructureHelper.prepareResolveKeyForQueries(resolver);
85          NodeStructureHelper.prepareResolveAddKeys(resolver);
86          model = new InMemoryNodeModel();
87          final Map<ImmutableNode, String> references = createReferences();
88          model.mergeRoot(NodeStructureHelper.ROOT_AUTHORS_TREE, null, references, NodeStructureHelper.ROOT_AUTHORS_TREE.getNodeName(), resolver);
89      }
90  
91      /**
92       * Tests whether the name of the root node can be changed during a merge operation.
93       */
94      @Test
95      void testMergeRootOverrideName() {
96          final ImmutableNode node = NodeStructureHelper.createNode("newNode", null);
97          final String newName = "newRootNode";
98  
99          model.mergeRoot(node, newName, null, null, resolver);
100         final ImmutableNode root = model.getNodeHandler().getRootNode();
101         assertEquals(newName, root.getNodeName());
102     }
103 
104     /**
105      * Tests whether mergeRoot() handles an explicit reference object for the root node correctly.
106      */
107     @Test
108     void testMergeRootReference() {
109         final Object rootRef = 20140404210508L;
110         final ImmutableNode node = NodeStructureHelper.createNode("newNode", null);
111 
112         model.mergeRoot(node, null, null, rootRef, resolver);
113         final ReferenceNodeHandler refHandler = model.getReferenceNodeHandler();
114         final ImmutableNode checkNode = NodeStructureHelper.nodeForKey(model, "Simmons/Ilium");
115         assertEquals(checkNode.getNodeName(), refHandler.getReference(checkNode));
116         assertEquals(rootRef, refHandler.getReference(refHandler.getRootNode()));
117     }
118 
119     /**
120      * Tests whether attributes are taken into account by a merge operation.
121      */
122     @Test
123     void testMergeRootWithAttributes() {
124         final ImmutableNode node = new ImmutableNode.Builder().addAttribute("key", "value").create();
125         model.mergeRoot(node, null, null, null, resolver);
126         final ImmutableNode root = model.getNodeHandler().getRootNode();
127         assertEquals(Collections.singletonMap("key", "value"), root.getAttributes());
128     }
129 
130     /**
131      * Tests whether a value is taken into account when the root node is merged.
132      */
133     @Test
134     void testMergeRootWithValue() {
135         final ImmutableNode node = NodeStructureHelper.createNode("newNode", "test");
136         model.mergeRoot(node, null, null, null, resolver);
137         final ImmutableNode root = model.getNodeHandler().getRootNode();
138         assertEquals(NodeStructureHelper.ROOT_AUTHORS_TREE.getNodeName(), root.getNodeName());
139         assertEquals("test", root.getValue());
140     }
141 
142     /**
143      * Tests whether the stored references can be queried.
144      */
145     @Test
146     void testQueryReferences() {
147         final ReferenceNodeHandler handler = model.getReferenceNodeHandler();
148         final Collection<ImmutableNode> nodes = collectNodes(handler.getRootNode());
149         for (final ImmutableNode node : nodes) {
150             assertEquals(node.getNodeName(), handler.getReference(node));
151         }
152     }
153 
154     /**
155      * Tests whether references can be queried after an update operation.
156      */
157     @Test
158     void testQueryReferencesAfterUpdate() {
159         model.addProperty("Simmons.Hyperion", Collections.singleton("Lamia"), resolver);
160         final ReferenceNodeHandler handler = model.getReferenceNodeHandler();
161         assertEquals("Hyperion", handler.getReference(NodeStructureHelper.nodeForKey(model, "Simmons/Hyperion")));
162         assertEquals("Simmons", handler.getReference(NodeStructureHelper.nodeForKey(model, "Simmons")));
163     }
164 
165     /**
166      * Tests the reference returned for an unknown node.
167      */
168     @Test
169     void testQueryReferenceUnknown() {
170         final ReferenceNodeHandler handler = model.getReferenceNodeHandler();
171         assertNull(handler.getReference(new ImmutableNode.Builder().create()));
172     }
173 
174     /**
175      * Tests whether removed references can be queried.
176      */
177     @Test
178     void testQueryRemovedReferencesAfterRemove() {
179         model.clearTree("Simmons", resolver);
180         final ReferenceNodeHandler handler = model.getReferenceNodeHandler();
181         final List<Object> removedRefs = handler.removedReferences();
182         final int authorIdx = 2;
183         for (int i = 0; i < NodeStructureHelper.worksLength(authorIdx); i++) {
184             assertTrue(removedRefs.contains(NodeStructureHelper.work(authorIdx, i)), "Work not found: " + i);
185             for (int j = 0; j < NodeStructureHelper.personaeLength(authorIdx, i); j++) {
186                 assertTrue(removedRefs.contains(NodeStructureHelper.persona(authorIdx, i, j)), "Persona not found: " + j);
187             }
188         }
189     }
190 
191     /**
192      * Tests whether the removed references can be queried if there are none.
193      */
194     @Test
195     void testQueryRemovedReferencesEmpty() {
196         final ReferenceNodeHandler handler = model.getReferenceNodeHandler();
197         assertTrue(handler.removedReferences().isEmpty());
198     }
199 
200     /**
201      * Tests that the list with removed references cannot be modified.
202      */
203     @Test
204     void testRemovedReferencesModify() {
205         model.clearTree("Simmons", resolver);
206         final ReferenceNodeHandler handler = model.getReferenceNodeHandler();
207         final List<Object> removedRefs = handler.removedReferences();
208         assertThrows(UnsupportedOperationException.class, () -> removedRefs.add("another one"));
209     }
210 
211     /**
212      * Tests whether the root node of the model can be replaced.
213      */
214     @Test
215     void testReplaceRoot() {
216         final NodeSelector selector = new NodeSelector("Simmons.Hyperion");
217         model.trackNode(selector, resolver);
218         final ImmutableNode trackedNode = model.getTrackedNode(selector);
219         model.addProperty("Simmons.Hyperion.Lamia", Collections.singleton("new person"), resolver);
220 
221         model.replaceRoot(NodeStructureHelper.ROOT_AUTHORS_TREE, resolver);
222         final ImmutableNode node = model.getTrackedNode(selector);
223         assertEquals(trackedNode, node);
224         assertFalse(model.isTrackedNodeDetached(selector));
225         assertNull(model.getReferenceNodeHandler().getReference(trackedNode));
226     }
227 
228     /**
229      * Tries to call replaceRoot() with a null node.
230      */
231     @Test
232     void testReplaceRootNull() {
233         assertThrows(IllegalArgumentException.class, () -> model.replaceRoot(null, resolver));
234     }
235 }