1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.checks.coding.EqualsAvoidNullCheck.MSG_EQUALS_AVOID_NULL;
24 import static com.puppycrawl.tools.checkstyle.checks.coding.MultipleVariableDeclarationsCheck.MSG_MULTIPLE;
25 import static com.puppycrawl.tools.checkstyle.checks.coding.NestedIfDepthCheck.MSG_KEY;
26 import static com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMethodCheck.MSG_EXPECTED_TAG;
27 import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.findTokenInAstByPredicate;
28 import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
29 import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.isUtilsClassHasPrivateConstructor;
30
31 import java.nio.file.Path;
32 import java.util.Arrays;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Optional;
36 import java.util.Set;
37
38 import org.junit.jupiter.api.Test;
39
40 import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
41 import com.puppycrawl.tools.checkstyle.DetailAstImpl;
42 import com.puppycrawl.tools.checkstyle.JavaParser;
43 import com.puppycrawl.tools.checkstyle.api.DetailAST;
44 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
45 import com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck;
46 import com.puppycrawl.tools.checkstyle.checks.coding.MultipleVariableDeclarationsCheck;
47 import com.puppycrawl.tools.checkstyle.checks.coding.NestedIfDepthCheck;
48 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocMethodCheck;
49 import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
50
51 public class CheckUtilTest extends AbstractModuleTestSupport {
52
53 @Override
54 public String getPackageLocation() {
55 return "com/puppycrawl/tools/checkstyle/utils/checkutil";
56 }
57
58 @Test
59 public void testIsProperUtilsClass() throws ReflectiveOperationException {
60 assertWithMessage("Constructor is not private")
61 .that(isUtilsClassHasPrivateConstructor(CheckUtil.class))
62 .isTrue();
63 }
64
65 @Test
66 public void testParseDoubleWithIncorrectToken() {
67 final double parsedDouble = CheckUtil.parseDouble("1_02", TokenTypes.ASSIGN);
68 assertWithMessage("Invalid parse result")
69 .that(parsedDouble)
70 .isEqualTo(Double.NaN);
71 }
72
73 @Test
74 public void testEquals() {
75 final DetailAstImpl litStatic = new DetailAstImpl();
76 litStatic.setType(TokenTypes.LITERAL_STATIC);
77
78 final DetailAstImpl modifiers = new DetailAstImpl();
79 modifiers.setType(TokenTypes.MODIFIERS);
80 modifiers.addChild(litStatic);
81
82 final DetailAstImpl metDef = new DetailAstImpl();
83 metDef.setType(TokenTypes.METHOD_DEF);
84 metDef.addChild(modifiers);
85
86 assertWithMessage("Invalid result: ast is not equals method")
87 .that(CheckUtil.isEqualsMethod(metDef))
88 .isFalse();
89
90 metDef.removeChildren();
91
92 final DetailAstImpl metName = new DetailAstImpl();
93 metName.setType(TokenTypes.IDENT);
94 metName.setText("equals");
95 metDef.addChild(metName);
96
97 final DetailAstImpl modifiers2 = new DetailAstImpl();
98 modifiers2.setType(TokenTypes.MODIFIERS);
99 metDef.addChild(modifiers2);
100
101 final DetailAstImpl parameter1 = new DetailAstImpl();
102 final DetailAstImpl parameter2 = new DetailAstImpl();
103
104 final DetailAstImpl parameters = new DetailAstImpl();
105 parameters.setType(TokenTypes.PARAMETERS);
106
107 parameters.addChild(parameter2);
108
109 parameters.addChild(parameter1);
110 metDef.addChild(parameters);
111
112 assertWithMessage("Invalid result: ast is not equals method")
113 .that(CheckUtil.isEqualsMethod(metDef))
114 .isFalse();
115 }
116
117 @Test
118 public void testGetAccessModifierFromModifiersTokenWrongTokenType() {
119 final DetailAstImpl modifiers = new DetailAstImpl();
120 modifiers.setType(TokenTypes.METHOD_DEF);
121
122 final IllegalArgumentException exc =
123 getExpectedThrowable(IllegalArgumentException.class, () -> {
124 CheckUtil.getAccessModifierFromModifiersToken(modifiers);
125 });
126
127 final String expectedExceptionMsg = "expected non-null AST-token with type 'MODIFIERS'";
128 final String actualExceptionMsg = exc.getMessage();
129 assertWithMessage("Invalid exception message")
130 .that(actualExceptionMsg)
131 .isEqualTo(expectedExceptionMsg);
132 }
133
134 @Test
135 public void testGetTypeParameterNames() throws Exception {
136 final DetailAST parameterizedClassNode = getNodeFromFile(TokenTypes.CLASS_DEF);
137 final List<String> expected = Arrays.asList("V", "C");
138 final List<String> actual = CheckUtil.getTypeParameterNames(parameterizedClassNode);
139
140 assertWithMessage("Invalid type parameters")
141 .that(actual)
142 .isEqualTo(expected);
143 }
144
145 @Test
146 public void testGetTypeParameterNamesFromNonGenericClass() throws Exception {
147 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
148 verifyWithInlineConfigParser(
149 getPath("InputCheckUtilJavadocTypeNonGenericClass.java"), expected);
150 }
151
152 @Test
153 public void testGetTypeParameters() throws Exception {
154 final DetailAST parameterizedClassNode = getNodeFromFile(TokenTypes.CLASS_DEF);
155 final DetailAST firstTypeParameter =
156 getNode(parameterizedClassNode, TokenTypes.TYPE_PARAMETER);
157 final List<DetailAST> expected = Arrays.asList(firstTypeParameter,
158 firstTypeParameter.getNextSibling().getNextSibling());
159 final List<DetailAST> actual = CheckUtil.getTypeParameters(parameterizedClassNode);
160
161 assertWithMessage("Invalid type parameters")
162 .that(actual)
163 .isEqualTo(expected);
164 }
165
166 @Test
167 public void testIsEqualsMethod() throws Exception {
168 final DetailAST equalsMethodNode = getNodeFromFile(TokenTypes.METHOD_DEF);
169 final DetailAST someOtherMethod = equalsMethodNode.getNextSibling();
170
171 assertWithMessage("Invalid result: AST provided is not equals method")
172 .that(CheckUtil.isEqualsMethod(equalsMethodNode))
173 .isTrue();
174 assertWithMessage("Invalid result: AST provided is equals method")
175 .that(CheckUtil.isEqualsMethod(someOtherMethod))
176 .isFalse();
177 }
178
179 @Test
180 public void testIsNonVoidMethod() throws Exception {
181 final DetailAST nonVoidMethod = getNodeFromFile(TokenTypes.METHOD_DEF);
182 final DetailAST voidMethod = nonVoidMethod.getNextSibling();
183
184 assertWithMessage("Invalid result: AST provided is void method")
185 .that(CheckUtil.isNonVoidMethod(nonVoidMethod))
186 .isTrue();
187 assertWithMessage("Invalid result: AST provided is non void method")
188 .that(CheckUtil.isNonVoidMethod(voidMethod))
189 .isFalse();
190 }
191
192 @Test
193 public void testGetAccessModifierFromModifiersToken() throws Exception {
194 final DetailAST interfaceDef = getNodeFromFile(TokenTypes.INTERFACE_DEF);
195 final AccessModifierOption modifierInterface = CheckUtil
196 .getAccessModifierFromModifiersToken(interfaceDef
197 .findFirstToken(TokenTypes.OBJBLOCK)
198 .findFirstToken(TokenTypes.METHOD_DEF));
199 assertWithMessage("Invalid access modifier")
200 .that(modifierInterface)
201 .isEqualTo(AccessModifierOption.PUBLIC);
202
203 final DetailAST privateVariable = getNodeFromFile(TokenTypes.VARIABLE_DEF);
204 final AccessModifierOption modifierPrivate =
205 CheckUtil.getAccessModifierFromModifiersToken(privateVariable);
206 assertWithMessage("Invalid access modifier")
207 .that(modifierPrivate)
208 .isEqualTo(AccessModifierOption.PRIVATE);
209
210 final DetailAST protectedVariable = privateVariable.getNextSibling();
211 final AccessModifierOption modifierProtected =
212 CheckUtil.getAccessModifierFromModifiersToken(protectedVariable);
213 assertWithMessage("Invalid access modifier")
214 .that(modifierProtected)
215 .isEqualTo(AccessModifierOption.PROTECTED);
216
217 final DetailAST publicVariable = protectedVariable.getNextSibling();
218 final AccessModifierOption modifierPublic =
219 CheckUtil.getAccessModifierFromModifiersToken(publicVariable);
220 assertWithMessage("Invalid access modifier")
221 .that(modifierPublic)
222 .isEqualTo(AccessModifierOption.PUBLIC);
223
224 final DetailAST packageVariable = publicVariable.getNextSibling();
225 final AccessModifierOption modifierPackage =
226 CheckUtil.getAccessModifierFromModifiersToken(packageVariable);
227 assertWithMessage("Invalid access modifier")
228 .that(modifierPackage)
229 .isEqualTo(AccessModifierOption.PACKAGE);
230
231 final DetailAST enumConstantDefinition = getNodeFromFile(TokenTypes.ENUM_CONSTANT_DEF);
232 final AccessModifierOption modifierEnumConstant = CheckUtil
233 .getAccessModifierFromModifiersToken(enumConstantDefinition);
234 assertWithMessage("Invalid access modifier")
235 .that(modifierEnumConstant)
236 .isEqualTo(AccessModifierOption.PUBLIC);
237 }
238
239 @Test
240 public void testGetFirstNode() throws Exception {
241 final DetailAST classDef = getNodeFromFile(TokenTypes.CLASS_DEF);
242
243 final DetailAST firstChild = classDef.getFirstChild().getFirstChild();
244 final DetailAST firstNode = CheckUtil.getFirstNode(classDef);
245 assertWithMessage("Invalid first node")
246 .that(firstNode)
247 .isEqualTo(firstChild);
248 }
249
250 @Test
251 public void testGetFirstNode1() {
252 final DetailAstImpl child = new DetailAstImpl();
253 child.setLineNo(5);
254 child.setColumnNo(6);
255
256 final DetailAstImpl root = new DetailAstImpl();
257 root.setLineNo(5);
258 root.setColumnNo(6);
259
260 root.addChild(child);
261
262 final DetailAST firstNode = CheckUtil.getFirstNode(root);
263 assertWithMessage("Unexpected node")
264 .that(firstNode)
265 .isEqualTo(root);
266 }
267
268 @Test
269 public void testGetFirstNode2() {
270 final DetailAstImpl child = new DetailAstImpl();
271 child.setLineNo(6);
272 child.setColumnNo(5);
273
274 final DetailAstImpl root = new DetailAstImpl();
275 root.setLineNo(5);
276 root.setColumnNo(6);
277
278 root.addChild(child);
279
280 final DetailAST firstNode = CheckUtil.getFirstNode(root);
281 assertWithMessage("Unexpected node")
282 .that(firstNode)
283 .isEqualTo(root);
284 }
285
286 @Test
287 public void testParseDoubleFloatingPointValues() {
288 assertWithMessage("Invalid parse result")
289 .that(CheckUtil.parseDouble("-0.05f", TokenTypes.NUM_FLOAT))
290 .isEqualTo(-0.05);
291 assertWithMessage("Invalid parse result")
292 .that(CheckUtil.parseDouble("10.0", TokenTypes.NUM_DOUBLE))
293 .isEqualTo(10.0);
294 assertWithMessage("Invalid parse result")
295 .that(CheckUtil.parseDouble("1.23e3", TokenTypes.NUM_DOUBLE))
296 .isEqualTo(1230);
297 assertWithMessage("Invalid parse result")
298 .that(CheckUtil.parseDouble("-3.21E2", TokenTypes.NUM_DOUBLE))
299 .isEqualTo(-321);
300 assertWithMessage("Invalid parse result")
301 .that(CheckUtil.parseDouble("-0.0", TokenTypes.NUM_DOUBLE))
302 .isEqualTo(-0.0);
303 assertWithMessage("Invalid parse result")
304 .that(CheckUtil.parseDouble("NaN", TokenTypes.NUM_DOUBLE))
305 .isEqualTo(Double.NaN);
306 }
307
308 @Test
309 public void testParseDoubleIntegerValues() {
310 assertWithMessage("Invalid parse result")
311 .that(CheckUtil.parseDouble("0L", TokenTypes.NUM_LONG))
312 .isEqualTo(0.0);
313 assertWithMessage("Invalid parse result")
314 .that(CheckUtil.parseDouble("0B101", TokenTypes.NUM_INT))
315 .isEqualTo(0b101);
316 assertWithMessage("Invalid parse result")
317 .that(CheckUtil.parseDouble("0b10001010001011010000101000101L", TokenTypes.NUM_LONG))
318 .isEqualTo(289_775_941);
319 assertWithMessage("Invalid parse result")
320 .that(CheckUtil.parseDouble("1", TokenTypes.NUM_INT))
321 .isEqualTo(1.0);
322 assertWithMessage("Invalid parse result")
323 .that(CheckUtil.parseDouble("8L", TokenTypes.NUM_LONG))
324 .isEqualTo(8.0);
325 assertWithMessage("Invalid parse result")
326 .that(CheckUtil.parseDouble("-21474836480", TokenTypes.NUM_LONG))
327 .isEqualTo(-2.147_483_648e10);
328 assertWithMessage("Invalid parse result")
329 .that(CheckUtil.parseDouble("-2", TokenTypes.NUM_INT))
330 .isEqualTo(-2);
331 assertWithMessage("Invalid parse result")
332 .that(CheckUtil.parseDouble("0xffffffff", TokenTypes.NUM_INT))
333 .isEqualTo(-1);
334 assertWithMessage("Invalid parse result")
335 .that(CheckUtil.parseDouble("0x0B63", TokenTypes.NUM_INT))
336 .isEqualTo(2915.0);
337 assertWithMessage("Invalid parse result")
338 .that(CheckUtil.parseDouble("21474836470", TokenTypes.NUM_LONG))
339 .isEqualTo(2.147_483_647e10);
340 assertWithMessage("Invalid parse result")
341 .that(CheckUtil.parseDouble("073l", TokenTypes.NUM_LONG))
342 .isEqualTo(59.0);
343 }
344
345 @Test
346 public void testParseClassNames() {
347 final Set<String> actual = CheckUtil.parseClassNames(
348 "I.am.class.name.with.dot.in.the.end.", "ClassOnly", "my.Class");
349 final Set<String> expected = new HashSet<>();
350 expected.add("I.am.class.name.with.dot.in.the.end.");
351 expected.add("ClassOnly");
352 expected.add("my.Class");
353 expected.add("Class");
354 assertWithMessage("Result is not expected")
355 .that(actual)
356 .isEqualTo(expected);
357 }
358
359 @Test
360 public void testEqualsAvoidNullCheck() throws Exception {
361
362 final String[] expected = {
363 "14:28: " + getCheckMessage(EqualsAvoidNullCheck.class, MSG_EQUALS_AVOID_NULL),
364 "21:17: " + getCheckMessage(EqualsAvoidNullCheck.class, MSG_EQUALS_AVOID_NULL),
365 };
366 verifyWithInlineConfigParser(
367 getPath("InputCheckUtil1.java"), expected);
368 }
369
370 @Test
371 public void testMultipleVariableDeclarationsCheck() throws Exception {
372 final String[] expected = {
373 "11:5: " + getCheckMessage(MultipleVariableDeclarationsCheck.class, MSG_MULTIPLE),
374 "14:5: " + getCheckMessage(MultipleVariableDeclarationsCheck.class, MSG_MULTIPLE),
375 };
376 verifyWithInlineConfigParser(
377 getPath("InputCheckUtil2.java"),
378 expected);
379 }
380
381 @Test
382 public void testNestedIfDepth() throws Exception {
383 final String[] expected = {
384 "26:17: " + getCheckMessage(NestedIfDepthCheck.class, MSG_KEY, 2, 1),
385 "52:17: " + getCheckMessage(NestedIfDepthCheck.class, MSG_KEY, 2, 1),
386 };
387 verifyWithInlineConfigParser(
388 getPath("InputCheckUtil3.java"), expected);
389 }
390
391 @Test
392 public void testJavaDocMethod() throws Exception {
393 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
394 verifyWithInlineConfigParser(
395 getPath("InputCheckUtil4.java"), expected);
396 }
397
398 @Test
399 public void testJavaDocMethod2() throws Exception {
400 final String[] expected = {
401 "21:25: " + getCheckMessage(JavadocMethodCheck.class,
402 MSG_EXPECTED_TAG, "@param", "i"),
403 };
404 verifyWithInlineConfigParser(
405 getPath("InputCheckUtil5.java"), expected);
406 }
407
408 @Test
409 public void testJavadoc() throws Exception {
410 final String[] expected = {
411 "26:39: " + getCheckMessage(JavadocMethodCheck.class,
412 MSG_EXPECTED_TAG, "@param", "i"),
413 };
414 verifyWithInlineConfigParser(
415 getPath("InputCheckUtil7.java"), expected);
416 }
417
418 private DetailAST getNodeFromFile(int type) throws Exception {
419 final Path path = Path.of(getPath("InputCheckUtilTest.java"));
420 return getNode(JavaParser.parseFile(path.toFile(), JavaParser.Options.WITH_COMMENTS), type);
421 }
422
423
424
425
426
427
428
429
430
431
432 public static DetailAST getNode(Path filePath, int type) throws Exception {
433 return getNode(JavaParser.parseFile(filePath.toFile(),
434 JavaParser.Options.WITH_COMMENTS), type);
435 }
436
437
438
439
440
441
442
443
444 private static DetailAST getNode(DetailAST root, int type) {
445 final Optional<DetailAST> node = findTokenInAstByPredicate(root,
446 ast -> ast.getType() == type);
447
448 assertWithMessage("Cannot find node of specified type: %s", type)
449 .that(node.isPresent())
450 .isTrue();
451
452 return node.orElseThrow();
453 }
454
455 @Test
456 public void testPackageInfo() {
457 assertWithMessage("Expected isPackageInfo() to return false for ('/')")
458 .that(CheckUtil.isPackageInfo("/"))
459 .isFalse();
460
461 assertWithMessage("Expected isPackageInfo() to return true for package-info.java file")
462 .that(CheckUtil.isPackageInfo("src/main/java/com/example/package-info.java"))
463 .isTrue();
464
465 assertWithMessage("Expected isPackageInfo() to return false for regular Java file")
466 .that(CheckUtil.isPackageInfo("src/main/java/com/example/SomeClass.java"))
467 .isFalse();
468 }
469
470 }