View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.utils;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
24  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.isUtilsClassHasPrivateConstructor;
25  
26  import java.lang.reflect.Field;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Optional;
32  import java.util.TreeMap;
33  
34  import org.junit.jupiter.api.Test;
35  
36  import com.puppycrawl.tools.checkstyle.DetailAstImpl;
37  import com.puppycrawl.tools.checkstyle.api.DetailAST;
38  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
39  
40  public class TokenUtilTest {
41  
42      @Test
43      public void testIsProperUtilsClass() throws ReflectiveOperationException {
44          assertWithMessage("Constructor is not private")
45                  .that(isUtilsClassHasPrivateConstructor(TokenUtil.class))
46                  .isTrue();
47      }
48  
49      @Test
50      public void testGetIntFromAccessibleField() throws NoSuchFieldException {
51          final Field field = Integer.class.getField("MAX_VALUE");
52          final int maxValue = TokenUtil.getIntFromField(field, 0);
53  
54          assertWithMessage("Invalid getIntFromField result")
55              .that(maxValue)
56              .isEqualTo(Integer.MAX_VALUE);
57      }
58  
59      @Test
60      public void testGetIntFromInaccessibleField() throws NoSuchFieldException {
61          final Field field = Integer.class.getDeclaredField("value");
62  
63          final IllegalStateException expected =
64              getExpectedThrowable(IllegalStateException.class, () -> {
65                  TokenUtil.getIntFromField(field, 0);
66              });
67  
68          // The exception message may vary depending on the version of the JDK.
69          // It will definitely contain the TokenUtil class name and the Integer class name.
70          final String message = expected.getMessage();
71          assertWithMessage("Invalid exception message: %s", message)
72                  .that(message.startsWith("java.lang.IllegalAccessException: ")
73                          && message.contains("com.puppycrawl.tools.checkstyle.utils.TokenUtil")
74                          && message.contains("access a member of class java.lang.Integer"))
75                  .isTrue();
76      }
77  
78      @Test
79      public void testNameToValueMapFromPublicIntFields() {
80          final Map<String, Integer> actualMap =
81              TokenUtil.nameToValueMapFromPublicIntFields(Integer.class);
82          final Map<String, Integer> expectedMap = new TreeMap<>();
83          expectedMap.put("BYTES", Integer.BYTES);
84          expectedMap.put("SIZE", Integer.SIZE);
85          expectedMap.put("MAX_VALUE", Integer.MAX_VALUE);
86          expectedMap.put("MIN_VALUE", Integer.MIN_VALUE);
87  
88          assertWithMessage("Unexpected name to value map")
89              .that(actualMap)
90              .isEqualTo(expectedMap);
91      }
92  
93      @Test
94      public void testInvertMap() {
95          final Map<String, Integer> map = new TreeMap<>();
96          map.put("ZERO", 0);
97          map.put("ONE", 1);
98          map.put("TWO", 2);
99          map.put("NEGATIVE", -1);
100 
101         final Map<Integer, String> invertedMap = TokenUtil.invertMap(map);
102 
103         assertWithMessage("Key set of 'map' and values of 'invertedMap' should be the same.")
104                 .that(invertedMap.values())
105                 .containsExactlyElementsIn(map.keySet());
106         assertWithMessage("Values of 'map' and key set of 'invertedMap' should be the same.")
107                 .that(invertedMap.keySet())
108                 .containsExactlyElementsIn(map.values());
109     }
110 
111     @Test
112     public void testTokenValueIncorrect() throws IllegalAccessException {
113         int maxId = 0;
114         final Field[] fields = TokenTypes.class.getDeclaredFields();
115         for (final Field field : fields) {
116             if (field.getType() != Integer.TYPE) {
117                 continue;
118             }
119 
120             final String name = field.getName();
121             final int id = field.getInt(name);
122             if (id > maxId) {
123                 maxId = id;
124             }
125         }
126 
127         final int nextAfterMaxId = maxId + 1;
128         final IllegalArgumentException expectedException =
129             getExpectedThrowable(IllegalArgumentException.class, () -> {
130                 TokenUtil.getTokenName(nextAfterMaxId);
131             });
132 
133         assertWithMessage("Invalid exception message")
134             .that(expectedException.getMessage())
135             .isEqualTo("unknown TokenTypes id '" + nextAfterMaxId + "'");
136     }
137 
138     @Test
139     public void testTokenValueCorrect() throws IllegalAccessException {
140         final Field[] fields = TokenTypes.class.getDeclaredFields();
141         for (final Field field : fields) {
142             // Only process the int declarations.
143             if (field.getType() != Integer.TYPE) {
144                 continue;
145             }
146 
147             final String name = field.getName();
148             final int id = field.getInt(name);
149 
150             assertWithMessage("Invalid token name")
151                 .that(TokenUtil.getTokenName(id))
152                 .isEqualTo(name);
153         }
154     }
155 
156     @Test
157     public void testTokenValueIncorrect2() {
158         final int id = 0;
159         final IllegalArgumentException expected =
160             getExpectedThrowable(IllegalArgumentException.class, () -> {
161                 TokenUtil.getTokenName(id);
162             });
163 
164         assertWithMessage("Invalid exception message")
165             .that(expected.getMessage())
166             .isEqualTo("unknown TokenTypes id '" + id + "'");
167     }
168 
169     @Test
170     public void testTokenIdIncorrect() {
171         final String id = "NON_EXISTENT_VALUE";
172         final IllegalArgumentException expected =
173             getExpectedThrowable(IllegalArgumentException.class, () -> {
174                 TokenUtil.getTokenId(id);
175             });
176 
177         assertWithMessage("Invalid exception message")
178             .that(expected.getMessage())
179             .isEqualTo("unknown TokenTypes value '" + id + "'");
180     }
181 
182     @Test
183     public void testShortDescriptionIncorrect() {
184         final String id = "NON_EXISTENT_VALUE";
185         final IllegalArgumentException expected =
186             getExpectedThrowable(IllegalArgumentException.class, () -> {
187                 TokenUtil.getShortDescription(id);
188             });
189 
190         assertWithMessage("Invalid exception message")
191             .that(expected.getMessage())
192             .isEqualTo("unknown TokenTypes value '" + id + "'");
193     }
194 
195     @Test
196     public void testIsCommentType() {
197         assertWithMessage("Should return true when valid type passed")
198                 .that(TokenUtil.isCommentType(TokenTypes.SINGLE_LINE_COMMENT))
199                 .isTrue();
200         assertWithMessage("Should return true when valid type passed")
201                 .that(TokenUtil.isCommentType(TokenTypes.BLOCK_COMMENT_BEGIN))
202                 .isTrue();
203         assertWithMessage("Should return true when valid type passed")
204                 .that(TokenUtil.isCommentType(TokenTypes.BLOCK_COMMENT_END))
205                 .isTrue();
206         assertWithMessage("Should return true when valid type passed")
207                 .that(TokenUtil.isCommentType(TokenTypes.COMMENT_CONTENT))
208                 .isTrue();
209         assertWithMessage("Should return true when valid type passed")
210                 .that(TokenUtil.isCommentType("COMMENT_CONTENT"))
211                 .isTrue();
212         assertWithMessage("Should return false when invalid type passed")
213                 .that(TokenUtil.isCommentType(TokenTypes.CLASS_DEF))
214                 .isFalse();
215         assertWithMessage("Should return false when invalid type passed")
216                 .that(TokenUtil.isCommentType("CLASS_DEF"))
217                 .isFalse();
218     }
219 
220     @Test
221     public void testGetTokenTypesTotalNumber() {
222         final int tokenTypesTotalNumber = TokenUtil.getTokenTypesTotalNumber();
223 
224         assertWithMessage("Invalid token total number")
225             .that(tokenTypesTotalNumber)
226             .isEqualTo(192);
227     }
228 
229     @Test
230     public void testGetAllTokenIds() {
231         final int[] allTokenIds = TokenUtil.getAllTokenIds();
232         final int sum = Arrays.stream(allTokenIds).sum();
233 
234         assertWithMessage("Invalid token length")
235             .that(allTokenIds.length)
236             .isEqualTo(192);
237         assertWithMessage("invalid sum")
238             .that(sum)
239             .isEqualTo(20501);
240     }
241 
242     @Test
243     public void testGetTokenNameWithGreatestPossibleId() {
244         final int id = TokenTypes.COMMENT_CONTENT;
245         final String tokenName = TokenUtil.getTokenName(id);
246 
247         assertWithMessage("Invalid token name")
248             .that(tokenName)
249             .isEqualTo("COMMENT_CONTENT");
250     }
251 
252     @Test
253     public void testCorrectBehaviourOfGetTokenId() {
254         final String id = "COMPILATION_UNIT";
255 
256         assertWithMessage("Invalid token id")
257             .that(TokenUtil.getTokenId(id))
258             .isEqualTo(TokenTypes.COMPILATION_UNIT);
259     }
260 
261     @Test
262     public void testCorrectBehaviourOfShortDescription() {
263         final String id = "COMPILATION_UNIT";
264         final String shortDescription = TokenUtil.getShortDescription(id);
265 
266         assertWithMessage("Invalid short description")
267             .that(shortDescription)
268             .isEqualTo("This is the root node for the source file.");
269     }
270 
271     @Test
272     public void testFindFirstTokenByPredicate() {
273         final DetailAstImpl astForTest = new DetailAstImpl();
274         final DetailAstImpl child = new DetailAstImpl();
275         final DetailAstImpl firstSibling = new DetailAstImpl();
276         final DetailAstImpl secondSibling = new DetailAstImpl();
277         final DetailAstImpl thirdSibling = new DetailAstImpl();
278         firstSibling.setText("first");
279         secondSibling.setText("second");
280         thirdSibling.setText("third");
281         secondSibling.setNextSibling(thirdSibling);
282         firstSibling.setNextSibling(secondSibling);
283         child.setNextSibling(firstSibling);
284         astForTest.setFirstChild(child);
285         final Optional<DetailAST> result = TokenUtil.findFirstTokenByPredicate(astForTest,
286             ast -> "second".equals(ast.getText()));
287 
288         assertWithMessage("Invalid second sibling")
289             .that(result.orElseThrow())
290             .isEqualTo(secondSibling);
291     }
292 
293     @Test
294     public void testForEachChild() {
295         final DetailAstImpl astForTest = new DetailAstImpl();
296         final DetailAstImpl child = new DetailAstImpl();
297         final DetailAstImpl firstSibling = new DetailAstImpl();
298         final DetailAstImpl secondSibling = new DetailAstImpl();
299         final DetailAstImpl thirdSibling = new DetailAstImpl();
300         firstSibling.setType(TokenTypes.DOT);
301         secondSibling.setType(TokenTypes.CLASS_DEF);
302         thirdSibling.setType(TokenTypes.IDENT);
303         secondSibling.setNextSibling(thirdSibling);
304         firstSibling.setNextSibling(secondSibling);
305         child.setNextSibling(firstSibling);
306         astForTest.setFirstChild(child);
307         final List<DetailAST> children = new ArrayList<>();
308         TokenUtil.forEachChild(astForTest, TokenTypes.CLASS_DEF, children::add);
309         final DetailAST firstChild = children.getFirst();
310 
311         assertWithMessage("Must be one match")
312             .that(children)
313             .hasSize(1);
314         assertWithMessage("Mismatched child node")
315             .that(firstChild)
316             .isEqualTo(secondSibling);
317     }
318 
319     @Test
320     public void testIsTypeDeclaration() {
321         assertWithMessage("Should return true when valid type passed")
322                 .that(TokenUtil.isTypeDeclaration(TokenTypes.CLASS_DEF))
323                 .isTrue();
324         assertWithMessage("Should return true when valid type passed")
325                 .that(TokenUtil.isTypeDeclaration(TokenTypes.INTERFACE_DEF))
326                 .isTrue();
327         assertWithMessage("Should return true when valid type passed")
328                 .that(TokenUtil.isTypeDeclaration(TokenTypes.ANNOTATION_DEF))
329                 .isTrue();
330         assertWithMessage("Should return true when valid type passed")
331                 .that(TokenUtil.isTypeDeclaration(TokenTypes.ENUM_DEF))
332                 .isTrue();
333         assertWithMessage("Should return true when valid type passed")
334                 .that(TokenUtil.isTypeDeclaration(TokenTypes.RECORD_DEF))
335                 .isTrue();
336     }
337 
338     @Test
339     public void testIsOfTypeTrue() {
340         final int type = TokenTypes.LITERAL_CATCH;
341         final DetailAstImpl astForTest = new DetailAstImpl();
342         astForTest.setType(type);
343         final boolean result1 = TokenUtil.isOfType(type, TokenTypes.LITERAL_FOR,
344                                 TokenTypes.LITERAL_IF, TokenTypes.LITERAL_CATCH);
345         final boolean result2 = TokenUtil.isOfType(astForTest, TokenTypes.LITERAL_FOR,
346                                 TokenTypes.LITERAL_IF, TokenTypes.LITERAL_CATCH);
347 
348         assertWithMessage("Token type did not match")
349                 .that(result1)
350                 .isTrue();
351         assertWithMessage("Token type did not match")
352                 .that(result2)
353                 .isTrue();
354     }
355 
356     @Test
357     public void testIsOfTypeFalse() {
358         final int type = TokenTypes.LITERAL_CATCH;
359         final DetailAstImpl astForTest1 = new DetailAstImpl();
360         final DetailAstImpl astForTest2 = null;
361         astForTest1.setType(type);
362         final boolean result1 = TokenUtil.isOfType(type, TokenTypes.LITERAL_FOR,
363                                 TokenTypes.LITERAL_IF, TokenTypes.LITERAL_ELSE);
364         final boolean result2 = TokenUtil.isOfType(astForTest1, TokenTypes.LITERAL_FOR,
365                                 TokenTypes.LITERAL_IF, TokenTypes.LITERAL_ELSE);
366         final boolean result3 = TokenUtil.isOfType(astForTest2, TokenTypes.LITERAL_FOR,
367                                 TokenTypes.LITERAL_IF, TokenTypes.LITERAL_ELSE);
368 
369         assertWithMessage("Token type should not match")
370                 .that(result1)
371                 .isFalse();
372         assertWithMessage("Token type should not match")
373                 .that(result2)
374                 .isFalse();
375         assertWithMessage("Token type should not match")
376                 .that(result3)
377                 .isFalse();
378     }
379 
380     @Test
381     public void testIsBooleanLiteralType() {
382         assertWithMessage("Result is not expected")
383                 .that(TokenUtil.isBooleanLiteralType(TokenTypes.LITERAL_TRUE))
384                 .isTrue();
385         assertWithMessage("Result is not expected")
386                 .that(TokenUtil.isBooleanLiteralType(TokenTypes.LITERAL_FALSE))
387                 .isTrue();
388         assertWithMessage("Result is not expected")
389                 .that(TokenUtil.isBooleanLiteralType(TokenTypes.LOR))
390                 .isFalse();
391     }
392 
393 }