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.assertThrows;
21  import static org.mockito.Mockito.mock;
22  
23  import java.util.LinkedList;
24  import java.util.List;
25  
26  import org.junit.jupiter.api.Test;
27  
28  /**
29   * Test class for {@code NodeTreeWalker}.
30   */
31  public class TestNodeTreeWalker {
32  
33      /**
34       * A visitor implementation used for testing purposes. The visitor produces a list with the names of the nodes visited
35       * in the order it was called. With this it can be tested whether the nodes were visited in the correct order.
36       */
37      private static final class TestVisitor implements ConfigurationNodeVisitor<ImmutableNode> {
38  
39          /** A list with the names of the visited nodes. */
40          private final List<String> visitedNodes = new LinkedList<>();
41  
42          /** The maximum number of nodes to be visited. */
43          private int maxNodeCount = Integer.MAX_VALUE;
44  
45          /**
46           * Returns the maximum number of nodes visited by this visitor.
47           *
48           * @return the maximum number of nodes
49           */
50          public int getMaxNodeCount() {
51              return maxNodeCount;
52          }
53  
54          /**
55           * Returns the list with the names of the visited nodes.
56           *
57           * @return the visit list
58           */
59          public List<String> getVisitedNodes() {
60              return visitedNodes;
61          }
62  
63          /**
64           * Sets the maximum number of nodes to be visited. After this the terminate flag is set.
65           *
66           * @param maxNodeCount the maximum number of nodes
67           */
68          public void setMaxNodeCount(final int maxNodeCount) {
69              this.maxNodeCount = maxNodeCount;
70          }
71  
72          @Override
73          public boolean terminate() {
74              return visitedNodes.size() >= getMaxNodeCount();
75          }
76  
77          @Override
78          public void visitAfterChildren(final ImmutableNode node, final NodeHandler<ImmutableNode> handler) {
79              visitedNodes.add(visitAfterName(handler.nodeName(node)));
80          }
81  
82          @Override
83          public void visitBeforeChildren(final ImmutableNode node, final NodeHandler<ImmutableNode> handler) {
84              visitedNodes.add(handler.nodeName(node));
85          }
86      }
87  
88      /**
89       * Creates a dummy node handler.
90       *
91       * @return the node handler
92       */
93      private static NodeHandler<ImmutableNode> createHandler() {
94          return new InMemoryNodeModel().getNodeHandler();
95      }
96  
97      /**
98       * Creates a mock for a node handler.
99       *
100      * @return the handler mock
101      */
102     @SuppressWarnings("unchecked")
103     private static NodeHandler<ImmutableNode> handlerMock() {
104         return mock(NodeHandler.class);
105     }
106 
107     /**
108      * Generates a name which indicates that the corresponding node was visited after its children.
109      *
110      * @param name the node name to be decorated
111      * @return the name with the after indicator
112      */
113     private static String visitAfterName(final String name) {
114         return "->" + name;
115     }
116 
117     /**
118      * Creates a mock for a visitor.
119      *
120      * @return the visitor mock
121      */
122     @SuppressWarnings("unchecked")
123     private static ConfigurationNodeVisitor<ImmutableNode> visitorMock() {
124         return mock(ConfigurationNodeVisitor.class);
125     }
126 
127     /**
128      * Prepares a list with the names of nodes encountered during a BFS walk.
129      *
130      * @return the expected node names in BFS mode
131      */
132     private List<String> expectBFS() {
133         final List<String> expected = new LinkedList<>();
134         final List<String> works = new LinkedList<>();
135         final List<String> personae = new LinkedList<>();
136         expected.add(NodeStructureHelper.ROOT_AUTHORS_TREE.getNodeName());
137         for (int authorIdx = 0; authorIdx < NodeStructureHelper.authorsLength(); authorIdx++) {
138             expected.add(NodeStructureHelper.author(authorIdx));
139             for (int workIdx = 0; workIdx < NodeStructureHelper.worksLength(authorIdx); workIdx++) {
140                 works.add(NodeStructureHelper.work(authorIdx, workIdx));
141                 for (int personIdx = 0; personIdx < NodeStructureHelper.personaeLength(authorIdx, workIdx); personIdx++) {
142                     personae.add(NodeStructureHelper.persona(authorIdx, workIdx, personIdx));
143                 }
144             }
145         }
146         expected.addAll(works);
147         expected.addAll(personae);
148         return expected;
149     }
150 
151     /**
152      * Prepares a list with the names of nodes encountered during a DFS walk.
153      *
154      * @return the expected node names in DFS mode
155      */
156     private List<String> expectDFS() {
157         final List<String> expected = new LinkedList<>();
158         expected.add(NodeStructureHelper.ROOT_AUTHORS_TREE.getNodeName());
159         for (int authorIdx = 0; authorIdx < NodeStructureHelper.authorsLength(); authorIdx++) {
160             expected.add(NodeStructureHelper.author(authorIdx));
161             for (int workIdx = 0; workIdx < NodeStructureHelper.worksLength(authorIdx); workIdx++) {
162                 expected.add(NodeStructureHelper.work(authorIdx, workIdx));
163                 for (int personaIdx = 0; personaIdx < NodeStructureHelper.personaeLength(authorIdx, workIdx); personaIdx++) {
164                     final String persona = NodeStructureHelper.persona(authorIdx, workIdx, personaIdx);
165                     expected.add(persona);
166                     expected.add(visitAfterName(persona));
167                 }
168                 expected.add(visitAfterName(NodeStructureHelper.work(authorIdx, workIdx)));
169             }
170             expected.add(visitAfterName(NodeStructureHelper.author(authorIdx)));
171         }
172         expected.add(visitAfterName(NodeStructureHelper.ROOT_AUTHORS_TREE.getNodeName()));
173         return expected;
174     }
175 
176     /**
177      * Tests a traversal in BFS mode.
178      */
179     @Test
180     void testWalkBFS() {
181         final List<String> expected = expectBFS();
182         final TestVisitor visitor = new TestVisitor();
183         NodeTreeWalker.INSTANCE.walkBFS(NodeStructureHelper.ROOT_AUTHORS_TREE, visitor, createHandler());
184         assertEquals(expected, visitor.getVisitedNodes());
185     }
186 
187     /**
188      * Tests a BFS walk if node is passed in.
189      */
190     @Test
191     void testWalkBFSNoNode() {
192         final ConfigurationNodeVisitor<ImmutableNode> visitor = visitorMock();
193         final NodeHandler<ImmutableNode> handler = handlerMock();
194         NodeTreeWalker.INSTANCE.walkBFS(null, visitor, handler);
195     }
196 
197     /**
198      * Tests whether the terminate flag is evaluated in BFS mode.
199      */
200     @Test
201     void testWalkBFSTerminate() {
202         final TestVisitor visitor = new TestVisitor();
203         final int nodeCount = 9;
204         visitor.setMaxNodeCount(nodeCount);
205         NodeTreeWalker.INSTANCE.walkBFS(NodeStructureHelper.ROOT_AUTHORS_TREE, visitor, createHandler());
206         assertEquals(nodeCount, visitor.getVisitedNodes().size());
207     }
208 
209     /**
210      * Tests a DFS traversal.
211      */
212     @Test
213     void testWalkDFS() {
214         final List<String> expected = expectDFS();
215         final TestVisitor visitor = new TestVisitor();
216         NodeTreeWalker.INSTANCE.walkDFS(NodeStructureHelper.ROOT_AUTHORS_TREE, visitor, createHandler());
217         assertEquals(expected, visitor.getVisitedNodes());
218     }
219 
220     /**
221      * Tests whether walkDFS() can handle a null node.
222      */
223     @Test
224     void testWalkDFSNoNode() {
225         final ConfigurationNodeVisitor<ImmutableNode> visitor = visitorMock();
226         final NodeHandler<ImmutableNode> handler = handlerMock();
227         NodeTreeWalker.INSTANCE.walkDFS(null, visitor, handler);
228     }
229 
230     /**
231      * Tests whether the terminate flag is taken into account during a DFS walk.
232      */
233     @Test
234     void testWalkDFSTerminate() {
235         final TestVisitor visitor = new TestVisitor();
236         final int nodeCount = 5;
237         visitor.setMaxNodeCount(nodeCount);
238         NodeTreeWalker.INSTANCE.walkDFS(NodeStructureHelper.ROOT_AUTHORS_TREE, visitor, createHandler());
239         assertEquals(nodeCount, visitor.getVisitedNodes().size());
240     }
241 
242     /**
243      * Tries a walk() operation without a node handler.
244      */
245     @Test
246     void testWalkNoNodeHandler() {
247         final TestVisitor visitor = new TestVisitor();
248         assertThrows(IllegalArgumentException.class, () -> NodeTreeWalker.INSTANCE.walkDFS(NodeStructureHelper.ROOT_AUTHORS_TREE, visitor, null));
249     }
250 
251     /**
252      * Tries a walk operation without a visitor.
253      */
254     @Test
255     void testWalkNoVisitor() {
256         final NodeHandler<ImmutableNode> handler = createHandler();
257         assertThrows(IllegalArgumentException.class, () -> NodeTreeWalker.INSTANCE.walkDFS(NodeStructureHelper.ROOT_AUTHORS_TREE, null, handler));
258     }
259 }