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