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