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;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23 import static com.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck.MSG_INVALID_PATTERN;
24 import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.Mockito.CALLS_REAL_METHODS;
27 import static org.mockito.Mockito.doThrow;
28 import static org.mockito.Mockito.mock;
29
30 import java.io.ByteArrayOutputStream;
31 import java.io.File;
32 import java.io.Writer;
33 import java.nio.charset.StandardCharsets;
34 import java.nio.file.Files;
35 import java.nio.file.StandardCopyOption;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Objects;
45 import java.util.Set;
46 import java.util.UUID;
47 import java.util.regex.Matcher;
48 import java.util.regex.Pattern;
49
50 import org.junit.jupiter.api.Test;
51 import org.junit.jupiter.api.io.TempDir;
52 import org.mockito.MockedConstruction;
53 import org.mockito.MockedStatic;
54 import org.mockito.Mockito;
55 import org.mockito.internal.util.Checks;
56
57 import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
58 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
59 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
60 import com.puppycrawl.tools.checkstyle.api.Configuration;
61 import com.puppycrawl.tools.checkstyle.api.Context;
62 import com.puppycrawl.tools.checkstyle.api.DetailAST;
63 import com.puppycrawl.tools.checkstyle.api.FileContents;
64 import com.puppycrawl.tools.checkstyle.api.FileText;
65 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
66 import com.puppycrawl.tools.checkstyle.checks.NoCodeInFileCheck;
67 import com.puppycrawl.tools.checkstyle.checks.coding.EmptyStatementCheck;
68 import com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck;
69 import com.puppycrawl.tools.checkstyle.checks.design.OneTopLevelClassCheck;
70 import com.puppycrawl.tools.checkstyle.checks.indentation.CommentsIndentationCheck;
71 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocPackageCheck;
72 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck;
73 import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
74 import com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck;
75 import com.puppycrawl.tools.checkstyle.checks.naming.ParameterNameCheck;
76 import com.puppycrawl.tools.checkstyle.checks.naming.TypeNameCheck;
77 import com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAfterCheck;
78 import com.puppycrawl.tools.checkstyle.checks.whitespace.WhitespaceAroundCheck;
79 import com.puppycrawl.tools.checkstyle.filters.SuppressWithNearbyCommentFilter;
80 import com.puppycrawl.tools.checkstyle.filters.SuppressionXpathFilter;
81 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
82 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
83
84
85
86
87
88
89
90
91 public class TreeWalkerTest extends AbstractModuleTestSupport {
92
93 @TempDir
94 public File temporaryFolder;
95
96 @Override
97 public String getPackageLocation() {
98 return "com/puppycrawl/tools/checkstyle/treewalker";
99 }
100
101 @Test
102 public void testProperFileExtension() throws Exception {
103 final String path = getPath("InputTreeWalkerProperFileExtension.java");
104 final String[] expected = {
105 "15:27: " + getCheckMessage(ConstantNameCheck.class,
106 MSG_INVALID_PATTERN, "k", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
107 };
108 verifyWithInlineConfigParserTwice(path, expected);
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 @Test
134 public void testNoAuditEventsWithoutFilters() throws Exception {
135 final String[] expected = {
136 "10:1: " + getCheckMessage(OneTopLevelClassCheck.class,
137 OneTopLevelClassCheck.MSG_KEY, "InputTreeWalkerInner"),
138 };
139 try (MockedConstruction<TreeWalkerAuditEvent> mocked =
140 Mockito.mockConstruction(TreeWalkerAuditEvent.class, (mock, context) -> {
141 throw new CheckstyleException("No audit events expected");
142 })) {
143 verifyWithInlineConfigParserTwice(getPath("InputTreeWalker.java"), expected);
144 }
145 }
146
147
148
149
150
151 @Test
152 public void testConditionRequiredWithoutOrdinaryChecks() throws Exception {
153 final String[] expected = {
154 "10:5: " + getCheckMessage(JavadocParagraphCheck.class,
155 JavadocParagraphCheck.MSG_REDUNDANT_PARAGRAPH),
156 };
157 final String path = getPath("InputTreeWalkerJavadoc.java");
158 final DetailAST mockAst = mock();
159 final DetailAST realAst = JavaParser.parseFile(new File(path),
160 JavaParser.Options.WITH_COMMENTS);
161
162 doThrow(IllegalStateException.class).when(mockAst).getFirstChild();
163 try (MockedStatic<JavaParser> parser = Mockito.mockStatic(JavaParser.class)) {
164 parser.when(() -> JavaParser.parse(any(FileContents.class))).thenReturn(mockAst);
165
166 parser.when(() -> JavaParser.appendHiddenCommentNodes(mockAst)).thenReturn(realAst);
167
168 verifyWithInlineConfigParserTwice(path, expected);
169 }
170 }
171
172
173
174
175
176 @Test
177 public void testConditionRequiredWithoutCommentChecks() throws Exception {
178 final String[] expected = {
179 "10:1: " + getCheckMessage(OneTopLevelClassCheck.class,
180 OneTopLevelClassCheck.MSG_KEY, "InputTreeWalkerInner"),
181 };
182 try (MockedStatic<JavaParser> parser =
183 Mockito.mockStatic(JavaParser.class, CALLS_REAL_METHODS)) {
184
185 parser.when(() -> JavaParser.appendHiddenCommentNodes(any(DetailAST.class)))
186 .thenThrow(IllegalStateException.class);
187
188 verifyWithInlineConfigParserTwice(getPath("InputTreeWalker.java"), expected);
189 }
190 }
191
192 @Test
193 public void testImproperFileExtension() throws Exception {
194 final String regularFilePath = getPath("InputTreeWalkerImproperFileExtension.java");
195 final File originalFile = new File(regularFilePath);
196 final File tempFile = new File(temporaryFolder, "file.pdf");
197 Files.copy(originalFile.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
198 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
199 verifyWithInlineConfigParserTwice(tempFile.getPath(), expected);
200 }
201
202 @Test
203 public void testAcceptableTokens() {
204 final DefaultConfiguration checkConfig =
205 createModuleConfig(HiddenFieldCheck.class);
206 checkConfig.addProperty("tokens", "VARIABLE_DEF, ENUM_DEF, CLASS_DEF, METHOD_DEF,"
207 + "IMPORT");
208 final CheckstyleException exc =
209 getExpectedThrowable(CheckstyleException.class, () -> {
210 execute(checkConfig, getPath("InputTreeWalker.java"));
211 }, "CheckstyleException is expected");
212 final String errorMsg = exc.getMessage();
213 final Pattern expected = Pattern.compile(Pattern.quote("cannot initialize module"
214 + " com.puppycrawl.tools.checkstyle.TreeWalker - Token ")
215 + "\"(ENUM_DEF|CLASS_DEF|METHOD_DEF|IMPORT)\""
216 + Pattern.quote(" was not found in Acceptable tokens list in check"
217 + " com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck"));
218 final Matcher errorMsgMatcher = expected.matcher(errorMsg);
219 assertWithMessage("Failure for: %s", errorMsg)
220 .that(errorMsgMatcher.matches())
221 .isTrue();
222 }
223
224 @Test
225 public void testOnEmptyFile() throws Exception {
226 final DefaultConfiguration checkConfig = createModuleConfig(HiddenFieldCheck.class);
227 final String uniqueFileName = "file_" + UUID.randomUUID() + ".java";
228 final File emptyFile = new File(temporaryFolder, uniqueFileName);
229 emptyFile.createNewFile();
230 execute(checkConfig, emptyFile.getPath());
231 final long fileSize = Files.size(emptyFile.toPath());
232 assertWithMessage("File should be empty")
233 .that(fileSize)
234 .isEqualTo(0);
235 }
236
237 @Test
238 public void testWithCheckNotHavingTreeWalkerAsParent() {
239 final DefaultConfiguration checkConfig = createModuleConfig(JavadocPackageCheck.class);
240
241 final String uniqueFileName = "junit_" + UUID.randomUUID() + ".java";
242 final File filePath = new File(temporaryFolder, uniqueFileName);
243 final CheckstyleException exception =
244 getExpectedThrowable(CheckstyleException.class, () -> {
245 execute(createTreeWalkerConfig(checkConfig), filePath.toString());
246 }, "CheckstyleException is expected");
247 assertWithMessage("Error message is unexpected")
248 .that(exception.getMessage())
249 .contains("TreeWalker is not allowed as a parent of");
250 }
251
252 @Test
253 public void testSetupChildExceptions() {
254 final TreeWalker treeWalker = new TreeWalker();
255 final PackageObjectFactory factory = new PackageObjectFactory(
256 new HashSet<>(), Thread.currentThread().getContextClassLoader());
257 treeWalker.setModuleFactory(factory);
258
259 final Configuration config = new DefaultConfiguration("java.lang.String");
260 final CheckstyleException exc =
261 getExpectedThrowable(CheckstyleException.class, () -> {
262 treeWalker.setupChild(config);
263 }, "Exception is expected");
264 assertWithMessage("Error message is not expected")
265 .that(exc.getMessage())
266 .isEqualTo("TreeWalker is not allowed as a parent of java.lang.String "
267 + "Please review 'Parent Module' section for this Check in "
268 + "web documentation if Check is standard.");
269 }
270
271 @Test
272 public void testSettersForParameters() throws Exception {
273 final TreeWalker treeWalker = new TreeWalker();
274 final DefaultConfiguration config = new DefaultConfiguration("default config");
275 treeWalker.setTabWidth(1);
276 treeWalker.configure(config);
277
278 final int tabWidth = TestUtil.getInternalState(treeWalker, "tabWidth", Integer.class);
279 assertWithMessage("Invalid setter result")
280 .that(tabWidth)
281 .isEqualTo(1);
282 final Object configuration = TestUtil.getInternalState(treeWalker, "configuration",
283 Object.class);
284 assertWithMessage("Invalid configuration")
285 .that(configuration)
286 .isEqualTo(config);
287 }
288
289 @Test
290 public void testForInvalidCheckImplementation() {
291 final DefaultConfiguration checkConfig = createModuleConfig(BadJavaDocCheck.class);
292 final String uniqueFileName = "file_" + UUID.randomUUID() + ".java";
293 final File pathToEmptyFile = new File(temporaryFolder, uniqueFileName);
294
295 final CheckstyleException exc =
296 getExpectedThrowable(CheckstyleException.class, () -> {
297 execute(checkConfig, pathToEmptyFile.toString());
298 }, "Exception is expected");
299 assertWithMessage("Error message is unexpected")
300 .that(exc.getMessage())
301 .isEqualTo("cannot initialize module com.puppycrawl.tools.checkstyle."
302 + "TreeWalker - Check 'com.puppycrawl.tools.checkstyle."
303 + "TreeWalkerTest$BadJavaDocCheck' waits for comment type token "
304 + "('SINGLE_LINE_COMMENT') and should override "
305 + "'isCommentNodesRequired()' method to return 'true'");
306 assertWithMessage("Error message is unexpected")
307 .that(exc.getMessage())
308 .contains("isCommentNodesRequired");
309 }
310
311 @Test
312 public void testProcessNonJavaFiles() throws Exception {
313 final TreeWalker treeWalker = new TreeWalker();
314 final PackageObjectFactory factory = new PackageObjectFactory(
315 new HashSet<>(), Thread.currentThread().getContextClassLoader());
316 treeWalker.setModuleFactory(factory);
317 treeWalker.configure(new DefaultConfiguration("default config"));
318 final DefaultConfiguration childConfig = createModuleConfig(JavadocParagraphCheck.class);
319 treeWalker.setupChild(childConfig);
320 final File file = new File("input.java");
321 final List<String> lines =
322 new ArrayList<>(Arrays.asList("package com.puppycrawl.tools.checkstyle;", "",
323 "error public class InputTreeWalkerFileWithViolation {}"));
324 final FileText fileText = new FileText(file, lines);
325 treeWalker.setFileContents(new FileContents(fileText));
326 final CheckstyleException exc =
327 getExpectedThrowable(CheckstyleException.class, () -> {
328 treeWalker.processFiltered(file, fileText);
329 }, "Exception expected");
330 assertWithMessage("Invalid exception message")
331 .that(exc.getMessage())
332 .isEqualTo("IllegalStateException occurred while parsing file input.java.");
333 }
334
335 @Test
336 public void testProcessNonJavaFilesWithoutException() throws Exception {
337 final TreeWalker treeWalker = new TreeWalker();
338 treeWalker.setTabWidth(1);
339 treeWalker.configure(new DefaultConfiguration("default config"));
340 final File file = new File(getPath("InputTreeWalkerNotJava.xml"));
341 final FileText fileText = new FileText(file, StandardCharsets.ISO_8859_1.name());
342 treeWalker.processFiltered(file, fileText);
343 final Collection<Checks> checks =
344 TestUtil.getInternalStateCollectionChecks(treeWalker, "ordinaryChecks");
345 assertWithMessage("No checks -> No parsing")
346 .that(checks)
347 .isEmpty();
348 }
349
350 @Test
351 public void testWithCacheWithNoViolation() throws Exception {
352 final String path = getPath("InputTreeWalkerWithCacheWithNoViolation.java");
353 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
354 verifyWithInlineConfigParserTwice(path, expected);
355 }
356
357 @Test
358 public void testProcessWithParserThrowable() throws Exception {
359 final TreeWalker treeWalker = new TreeWalker();
360 treeWalker.configure(createModuleConfig(TypeNameCheck.class));
361 final PackageObjectFactory factory = new PackageObjectFactory(
362 new HashSet<>(), Thread.currentThread().getContextClassLoader());
363 treeWalker.setModuleFactory(factory);
364 treeWalker.setupChild(createModuleConfig(TypeNameCheck.class));
365 final File file = new File(temporaryFolder, "file.java");
366 final List<String> lines = new ArrayList<>();
367 lines.add(" classD a {} ");
368 final FileText fileText = new FileText(file, lines);
369 treeWalker.setFileContents(new FileContents(fileText));
370 final CheckstyleException exception =
371 getExpectedThrowable(CheckstyleException.class, () -> {
372 treeWalker.processFiltered(file, fileText);
373 }, "Exception is expected");
374 assertWithMessage("Error message is unexpected")
375 .that(exception.getMessage())
376 .contains("occurred while parsing file");
377 }
378
379 @Test
380 public void testProcessWithRecognitionException() throws Exception {
381 final TreeWalker treeWalker = new TreeWalker();
382 treeWalker.configure(createModuleConfig(TypeNameCheck.class));
383 final PackageObjectFactory factory = new PackageObjectFactory(
384 new HashSet<>(), Thread.currentThread().getContextClassLoader());
385 treeWalker.setModuleFactory(factory);
386 treeWalker.setupChild(createModuleConfig(TypeNameCheck.class));
387 final File file = new File(temporaryFolder, "file.java");
388 final List<String> lines = new ArrayList<>();
389 lines.add(" class a%$# {} ");
390 final FileText fileText = new FileText(file, lines);
391 treeWalker.setFileContents(new FileContents(fileText));
392 final CheckstyleException exception =
393 getExpectedThrowable(CheckstyleException.class, () -> {
394 treeWalker.processFiltered(file, fileText);
395 }, "Exception is expected");
396 assertWithMessage("Error message is unexpected")
397 .that(exception.getMessage())
398 .contains("IllegalStateException occurred while parsing file");
399 }
400
401 @Test
402 public void testRequiredTokenIsEmptyIntArray() throws Exception {
403 final File file = new File(temporaryFolder, "file.java");
404 try (Writer writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
405 final String configComment =
406 """
407
408
409
410
411
412 """;
413 writer.write(configComment);
414 }
415 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
416 verifyWithInlineConfigParserTwice(file.getPath(), expected);
417 }
418
419 @Test
420 public void testBehaviourWithZeroChecks() throws Exception {
421 final TreeWalker treeWalker = new TreeWalker();
422 final PackageObjectFactory factory = new PackageObjectFactory(
423 new HashSet<>(), Thread.currentThread().getContextClassLoader());
424 treeWalker.setModuleFactory(factory);
425
426 final File file = new File(temporaryFolder, "file.java");
427 final FileText fileText = new FileText(file, new ArrayList<>());
428
429 treeWalker.processFiltered(file, fileText);
430 final Collection<Checks> checks =
431 TestUtil.getInternalStateCollectionChecks(treeWalker, "ordinaryChecks");
432 assertWithMessage("No checks -> No parsing")
433 .that(checks)
434 .isEmpty();
435 }
436
437 @Test
438 public void testBehaviourWithOrdinaryAndCommentChecks() throws Exception {
439 final TreeWalker treeWalker = new TreeWalker();
440 treeWalker.configure(createModuleConfig(TypeNameCheck.class));
441 treeWalker.configure(createModuleConfig(CommentsIndentationCheck.class));
442 final PackageObjectFactory factory = new PackageObjectFactory(
443 new HashSet<>(), Thread.currentThread().getContextClassLoader());
444 treeWalker.setModuleFactory(factory);
445 treeWalker.setupChild(createModuleConfig(TypeNameCheck.class));
446 treeWalker.setupChild(createModuleConfig(CommentsIndentationCheck.class));
447 final File file = new File(temporaryFolder, "file.java");
448 final List<String> lines = new ArrayList<>();
449 lines.add(" class a%$# {} ");
450 final FileText fileText = new FileText(file, lines);
451 treeWalker.setFileContents(new FileContents(fileText));
452
453 final CheckstyleException exception =
454 getExpectedThrowable(CheckstyleException.class, () -> {
455 treeWalker.processFiltered(file, fileText);
456 }, "file is not compilable, exception is expected");
457 final String message = "IllegalStateException occurred while parsing file";
458 assertWithMessage("Error message is unexpected")
459 .that(exception.getMessage())
460 .contains(message);
461 }
462
463 @Test
464 public void testSetupChild() throws Exception {
465 final TreeWalker treeWalker = new TreeWalker();
466 final PackageObjectFactory factory = new PackageObjectFactory(
467 new HashSet<>(), Thread.currentThread().getContextClassLoader());
468 treeWalker.setModuleFactory(factory);
469 treeWalker.setTabWidth(99);
470 treeWalker.finishLocalSetup();
471
472 final Configuration config = new DefaultConfiguration(
473 XpathFileGeneratorAstFilter.class.getName());
474
475 treeWalker.setupChild(config);
476
477 final Set<TreeWalkerFilter> filters =
478 TestUtil.getInternalStateSetTreeWalkerFilter(treeWalker, "filters");
479 final int tabWidth = TestUtil.getInternalState(filters.iterator().next(),
480 "tabWidth", Integer.class);
481
482 assertWithMessage("expected tab width")
483 .that(tabWidth)
484 .isEqualTo(99);
485 }
486
487 @Test
488 public void testBehaviourWithChecksAndFilters() throws Exception {
489
490 final String[] expected = {
491 "22:17: " + getCheckMessage(MemberNameCheck.class, "name.invalidPattern", "P",
492 "^[a-z][a-zA-Z0-9]*$"),
493 "17:17: " + getCheckMessage(MemberNameCheck.class, "name.invalidPattern", "I",
494 "^[a-z][a-zA-Z0-9]*$"),
495 };
496
497 verifyWithInlineConfigParserTwice(
498 getPath("InputTreeWalkerSuppressionCommentFilter.java"),
499 expected);
500 }
501
502 @Test
503 public void testMultiCheckOrder() throws Exception {
504
505 final String[] expected = {
506 "29:9: " + getCheckMessage(WhitespaceAfterCheck.class, "ws.notFollowed", "if"),
507 "29:9: " + getCheckMessage(WhitespaceAroundCheck.class, "ws.notFollowed", "if"),
508 };
509
510 verifyWithInlineConfigParserTwice(
511 getPath("InputTreeWalkerMultiCheckOrder.java"),
512 expected);
513 }
514
515 @Test
516 public void testMultiCheckOfSameTypeNoIdResultsInOrderingByHash() throws Exception {
517
518 final String[] expected = {
519 "17:28: " + getCheckMessage(ParameterNameCheck.class,
520 "name.invalidPattern", "V2", "^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"),
521 "19:25: " + getCheckMessage(ParameterNameCheck.class,
522 "name.invalidPattern", "b", "^[a-z][a-z0-9][a-zA-Z0-9]*$"),
523 };
524
525 verifyWithInlineConfigParserTwice(
526 getPath("InputTreeWalkerMultiCheckOrder2.java"),
527 expected);
528 }
529
530 @Test
531 public void testFinishLocalSetupFullyInitialized() {
532 final TreeWalker treeWalker = new TreeWalker();
533 treeWalker.setSeverity("error");
534 treeWalker.setTabWidth(100);
535 treeWalker.finishLocalSetup();
536
537 final Context context = TestUtil.getInternalState(treeWalker, "childContext",
538 Context.class);
539 assertWithMessage("Severity differs from expected")
540 .that(context.get("severity"))
541 .isEqualTo("error");
542 assertWithMessage("Tab width differs from expected")
543 .that(context.get("tabWidth"))
544 .isEqualTo(String.valueOf(100));
545 }
546
547 @Test
548 public void testCheckInitIsCalledInTreeWalker() throws Exception {
549 final DefaultConfiguration checkConfig =
550 createModuleConfig(VerifyInitCheck.class);
551 final String uniqueFileName = "file_" + UUID.randomUUID() + ".pdf";
552 final File file = new File(temporaryFolder, uniqueFileName);
553 execute(checkConfig, file.getPath());
554 assertWithMessage("Init was not called")
555 .that(VerifyInitCheck.isInitWasCalled())
556 .isTrue();
557 }
558
559 @Test
560 public void testCheckDestroyIsCalledInTreeWalker() throws Exception {
561 VerifyDestroyCheck.resetDestroyWasCalled();
562 final DefaultConfiguration checkConfig =
563 createModuleConfig(VerifyDestroyCheck.class);
564 final String uniqueFileName = "file_" + UUID.randomUUID() + ".pdf";
565 final File file = new File(temporaryFolder, uniqueFileName);
566 execute(checkConfig, file.getPath());
567 assertWithMessage("Destroy was not called")
568 .that(VerifyDestroyCheck.isDestroyWasCalled())
569 .isTrue();
570 }
571
572 @Test
573 public void testCommentCheckDestroyIsCalledInTreeWalker() throws Exception {
574 VerifyDestroyCheck.resetDestroyWasCalled();
575 final DefaultConfiguration checkConfig =
576 createModuleConfig(VerifyDestroyCommentCheck.class);
577 final String uniqueFileName = "file_" + UUID.randomUUID() + ".pdf";
578 final File file = new File(temporaryFolder, uniqueFileName);
579 execute(checkConfig, file.getPath());
580 assertWithMessage("Destroy was not called")
581 .that(VerifyDestroyCheck.isDestroyWasCalled())
582 .isTrue();
583 }
584
585 @Test
586 public void testCacheWhenFileExternalResourceContentDoesNotChange() throws Exception {
587 final DefaultConfiguration filterConfig = createModuleConfig(SuppressionXpathFilter.class);
588 filterConfig.addProperty("file", getPath("InputTreeWalkerSuppressionXpathFilter.xml"));
589 final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
590 treeWalkerConfig.addChild(filterConfig);
591
592 final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
593 final String uniqueFileName1 = "junit_" + UUID.randomUUID() + ".java";
594 final File cacheFile = new File(temporaryFolder, uniqueFileName1);
595 checkerConfig.addProperty("cacheFile", cacheFile.getPath());
596
597 final String uniqueFileName2 = "file_" + UUID.randomUUID() + ".java";
598 final File filePath = new File(temporaryFolder, uniqueFileName2);
599
600 execute(checkerConfig, filePath.toString());
601
602 execute(checkerConfig, filePath.toString());
603
604 assertWithMessage("External resource is not present in cache")
605 .that(Files.readString(cacheFile.toPath()))
606 .contains("InputTreeWalkerSuppressionXpathFilter.xml");
607 }
608
609 @Test
610 public void testTreeWalkerFilterAbsolutePath() throws Exception {
611
612 final String filePath = "src/test/resources/" + getPackageLocation()
613 + "/InputTreeWalkerSuppressionXpathFilterAbsolute.java";
614
615 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
616 verifyWithInlineConfigParserTwice(filePath, expected);
617 }
618
619 @Test
620 public void testExternalResourceFiltersWithNoExternalResource() throws Exception {
621 final DefaultConfiguration checkConfig = createModuleConfig(EmptyStatementCheck.class);
622 final DefaultConfiguration filterConfig =
623 createModuleConfig(SuppressWithNearbyCommentFilter.class);
624 final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
625 treeWalkerConfig.addChild(checkConfig);
626 treeWalkerConfig.addChild(filterConfig);
627
628 final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
629 final String uniqueFileName1 = "junit_" + UUID.randomUUID() + ".java";
630 final File cacheFile = new File(temporaryFolder, uniqueFileName1);
631 checkerConfig.addProperty("cacheFile", cacheFile.getPath());
632 final String uniqueFileName2 = "junit_" + UUID.randomUUID() + ".java";
633 final File filePath = new File(temporaryFolder, uniqueFileName2);
634
635 execute(checkerConfig, filePath.toString());
636
637 final long cacheSize = Files.size(cacheFile.toPath());
638 assertWithMessage("cacheFile should not be empty")
639 .that(cacheSize)
640 .isNotEqualTo(0);
641 }
642
643
644
645
646
647
648 @Test
649 public void testOrderOfCheckExecution() throws Exception {
650
651 final DefaultConfiguration configuration1 = createModuleConfig(AaCheck.class);
652 configuration1.addProperty("id", "2");
653 final DefaultConfiguration configuration2 = createModuleConfig(BbCheck.class);
654 configuration2.addProperty("id", "1");
655
656 final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
657 treeWalkerConfig.addChild(configuration2);
658 treeWalkerConfig.addChild(configuration1);
659
660 final List<File> files =
661 Collections.singletonList(new File(getPath("InputTreeWalker2.java")));
662 final Checker checker = createChecker(treeWalkerConfig);
663
664 final CheckstyleException exception =
665 getExpectedThrowable(CheckstyleException.class, () -> {
666 checker.process(files);
667 }, "exception is expected");
668 assertWithMessage("wrong order of Check executions")
669 .that(exception.getCause().getMessage())
670 .isEqualTo(AaCheck.class.toString());
671 }
672
673 @Test
674 public void testCreateNewCheckSortedSetOrdersByIdBeforeHashCode() throws Exception {
675 TestCheck.clearExecutionOrder();
676
677
678
679
680 final DefaultConfiguration config1 = createModuleConfig(TestCheck.class);
681 config1.addProperty("id", "alpha");
682
683 final DefaultConfiguration config2 = createModuleConfig(TestCheck.class);
684 config2.addProperty("id", "beta");
685
686 final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
687
688 treeWalkerConfig.addChild(config2);
689 treeWalkerConfig.addChild(config1);
690
691 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
692 verify(createChecker(treeWalkerConfig), getPath("InputTreeWalker2.java"), expected);
693
694 final List<String> executionOrder = TestCheck.getExecutionOrder();
695
696
697
698 assertWithMessage("Checks should be sorted by ID (alpha before beta), not by hashCode")
699 .that(executionOrder)
700 .containsExactly("alpha", "beta")
701 .inOrder();
702 }
703
704 @Test
705 public void testSkipFileOnJavaParseExceptionTrue() throws Exception {
706 final DefaultConfiguration config = createModuleConfig(TreeWalker.class);
707 config.addProperty("skipFileOnJavaParseException", "true");
708 config.addProperty("javaParseExceptionSeverity", "ignore");
709 config.addChild(createModuleConfig(ConstantNameCheck.class));
710
711 final File[] files = {
712 new File(getNonCompilablePath("InputTreeWalkerSkipParsingException.java")),
713 new File(getPath("InputTreeWalkerProperFileExtension.java")),
714 new File(getNonCompilablePath("InputTreeWalkerSkipParsingException2.java")),
715 };
716
717 final Checker checker = createChecker(config);
718 final Map<String, List<String>> expectedViolation = new HashMap<>();
719 expectedViolation.put(getPath("InputTreeWalkerProperFileExtension.java"),
720 Collections.singletonList(
721 "15:27: " + getCheckMessage(ConstantNameCheck.class,
722 MSG_INVALID_PATTERN, "k", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$")));
723 verify(checker, files, expectedViolation);
724 }
725
726
727
728
729
730
731
732
733
734
735 @Test
736 public void testSkipFileOnJavaParseExceptionFalse() throws Exception {
737 final DefaultConfiguration config = createModuleConfig(TreeWalker.class);
738 config.addProperty("skipFileOnJavaParseException", "false");
739 config.addChild(createModuleConfig(ConstantNameCheck.class));
740
741 final String[] files = {
742 getNonCompilablePath("InputTreeWalkerSkipParsingException2.java"),
743 getPath("InputTreeWalkerProperFileExtension.java"),
744 getNonCompilablePath("InputTreeWalkerSkipParsingException.java"),
745 };
746 final Exception ex = getExpectedThrowable(CheckstyleException.class,
747 () -> execute(config, files),
748 "Exception is expected");
749 assertWithMessage("Error message is unexpected")
750 .that(ex.getMessage())
751 .contains("Exception was thrown while processing");
752 }
753
754 @Test
755 public void testSkipFileOnJavaParseExceptionConfigSeverityIgnore() throws Exception {
756 final String path =
757 getNonCompilablePath(
758 "InputTreeWalkerSkipParsingExceptionConfigSeverityIgnore.java");
759 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
760 verifyWithInlineXmlConfig(path, expected);
761 }
762
763 @Test
764 public void testSkipFileOnJavaParseExceptionConfigSeverityDefault() throws Exception {
765 final String path =
766 getNonCompilablePath(
767 "InputTreeWalkerSkipParsingExceptionConfigSeverityDefault.java");
768 final String[] expected = {
769 "1: " + getCheckMessage(TreeWalker.PARSE_EXCEPTION_MSG, "IllegalStateException")
770 + " occurred while parsing file " + path + ".",
771 };
772 verifyWithInlineXmlConfig(path, expected);
773 }
774
775 @Test
776 public void testSkipFileOnJavaParseExceptionSkipChecks() throws Exception {
777 final DefaultConfiguration config = createModuleConfig(TreeWalker.class);
778 config.addProperty("skipFileOnJavaParseException", "true");
779 config.addProperty("javaParseExceptionSeverity", "ignore");
780 config.addChild(createModuleConfig(NoCodeInFileCheck.class));
781
782 final Checker checker = createChecker(config);
783
784 final File[] files = {
785 new File(getNonCompilablePath("InputTreeWalkerSkipParsingException.java")),
786 new File(getPath("InputTreeWalkerProperFileExtension.java")),
787 new File(getNonCompilablePath("InputTreeWalkerSkipParsingException2.java")),
788 };
789 final Map<String, List<String>> expectedViolation = new HashMap<>();
790 expectedViolation.put(getPath("InputTreeWalkerProperFileExtension.java"),
791 new ArrayList<>());
792
793 verify(checker, files, expectedViolation);
794 }
795
796 @Test
797 public void testJavaParseExceptionSeverityDefaultError() throws Exception {
798 final DefaultConfiguration config = createModuleConfig(TreeWalker.class);
799 config.addProperty("skipFileOnJavaParseException", "true");
800 config.addChild(createModuleConfig(NoCodeInFileCheck.class));
801
802 final Checker checker = createChecker(config);
803
804 final File[] files = {
805 new File(getNonCompilablePath("InputTreeWalkerSkipParsingException.java")),
806 new File(getPath("InputTreeWalkerProperFileExtension.java")),
807 };
808
809 final Map<String, List<String>> expectedViolation = new HashMap<>();
810
811 expectedViolation.put(getPath("InputTreeWalkerProperFileExtension.java"),
812 new ArrayList<>());
813 expectedViolation.put(getNonCompilablePath("InputTreeWalkerSkipParsingException.java"),
814 List.of("1: Java specific (TreeWalker-based) modules are skipped due to an "
815 + "exception during parsing - "
816 + "IllegalStateException occurred while parsing file "
817 + getNonCompilablePath("InputTreeWalkerSkipParsingException.java") + "."));
818
819 verify(checker, files, expectedViolation);
820 }
821
822 @Test
823 public void testJavaParseExceptionSeverityDefault() throws Exception {
824 final String inputFile = "InputTreeWalkerSkipParsingExceptionConfigSeverityDefault.java";
825 final String expectedInfoFile = "InputTreeWalkerExpectedInfo.txt";
826 final String expectedErrorFile = "InputTreeWalkerExpectedError.txt";
827
828 final ByteArrayOutputStream infoStream = new ByteArrayOutputStream();
829 final ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
830 final DefaultLogger dl = new DefaultLogger(infoStream, OutputStreamOptions.CLOSE,
831 errorStream, OutputStreamOptions.CLOSE);
832
833 verifyWithInlineConfigParserAndDefaultLogger(
834 getNonCompilablePath(inputFile),
835 getPath(expectedInfoFile),
836 getPath(expectedErrorFile),
837 dl, infoStream, errorStream);
838 }
839
840 public static class BadJavaDocCheck extends AbstractCheck {
841
842 @Override
843 public int[] getDefaultTokens() {
844 return getAcceptableTokens();
845 }
846
847 @Override
848 public int[] getAcceptableTokens() {
849 return new int[] {TokenTypes.SINGLE_LINE_COMMENT};
850 }
851
852 @Override
853 public int[] getRequiredTokens() {
854 return getAcceptableTokens();
855 }
856
857 }
858
859 public static class VerifyInitCheck extends AbstractCheck {
860
861 private static boolean initWasCalled;
862
863 @Override
864 public int[] getDefaultTokens() {
865 return CommonUtil.EMPTY_INT_ARRAY;
866 }
867
868 @Override
869 public int[] getAcceptableTokens() {
870 return getDefaultTokens();
871 }
872
873 @Override
874 public int[] getRequiredTokens() {
875 return getDefaultTokens();
876 }
877
878 @Override
879 public void init() {
880 super.init();
881 initWasCalled = true;
882 }
883
884 public static boolean isInitWasCalled() {
885 return initWasCalled;
886 }
887
888 }
889
890 public static class VerifyDestroyCheck extends AbstractCheck {
891
892 private static boolean destroyWasCalled;
893
894 @Override
895 public int[] getDefaultTokens() {
896 return CommonUtil.EMPTY_INT_ARRAY;
897 }
898
899 @Override
900 public int[] getAcceptableTokens() {
901 return getDefaultTokens();
902 }
903
904 @Override
905 public int[] getRequiredTokens() {
906 return getDefaultTokens();
907 }
908
909 @Override
910 public void destroy() {
911 super.destroy();
912 destroyWasCalled = true;
913 }
914
915 public static void resetDestroyWasCalled() {
916 destroyWasCalled = false;
917 }
918
919 public static boolean isDestroyWasCalled() {
920 return destroyWasCalled;
921 }
922
923 }
924
925 public static class VerifyDestroyCommentCheck extends VerifyDestroyCheck {
926
927 @Override
928 public boolean isCommentNodesRequired() {
929 return true;
930 }
931
932 }
933
934 public static class AaCheck extends AbstractCheck {
935
936 @Override
937 public int[] getDefaultTokens() {
938 return new int[0];
939 }
940
941 @Override
942 public int[] getAcceptableTokens() {
943 return new int[0];
944 }
945
946 @Override
947 public int[] getRequiredTokens() {
948 return new int[0];
949 }
950
951 @Override
952 public void beginTree(DetailAST rootAST) {
953 throw new IllegalStateException(AaCheck.class.toString());
954 }
955
956 }
957
958 public static class BbCheck extends AbstractCheck {
959
960 @Override
961 public int[] getDefaultTokens() {
962 return new int[0];
963 }
964
965 @Override
966 public int[] getAcceptableTokens() {
967 return new int[0];
968 }
969
970 @Override
971 public int[] getRequiredTokens() {
972 return new int[0];
973 }
974
975 @Override
976 public void beginTree(DetailAST rootAST) {
977 throw new IllegalStateException(BbCheck.class.toString());
978 }
979
980 }
981
982 public static class RequiredTokenIsEmptyIntArray extends AbstractCheck {
983
984 @Override
985 public int[] getRequiredTokens() {
986 return CommonUtil.EMPTY_INT_ARRAY;
987 }
988
989 @Override
990 public int[] getDefaultTokens() {
991 return new int[] {TokenTypes.ANNOTATION};
992 }
993
994 @Override
995 public int[] getAcceptableTokens() {
996 return CommonUtil.EMPTY_INT_ARRAY;
997 }
998
999 }
1000
1001
1002
1003
1004
1005 public static class TestCheck extends AbstractCheck {
1006 private static final List<String> EXECUTION_ORDER = new ArrayList<>();
1007
1008 @Override
1009 public void beginTree(DetailAST rootAST) {
1010 EXECUTION_ORDER.add(getId());
1011 }
1012
1013 @Override
1014 public boolean equals(Object obj) {
1015 if (this == obj) {
1016 return true;
1017 }
1018 if (obj == null || getClass() != obj.getClass()) {
1019 return false;
1020 }
1021 final TestCheck other = (TestCheck) obj;
1022 return Objects.equals(getId(), other.getId());
1023 }
1024
1025 @Override
1026 public int hashCode() {
1027 return Objects.hash(getId());
1028 }
1029
1030 @Override
1031 public int[] getDefaultTokens() {
1032 return CommonUtil.EMPTY_INT_ARRAY;
1033 }
1034
1035 @Override
1036 public int[] getAcceptableTokens() {
1037 return CommonUtil.EMPTY_INT_ARRAY;
1038 }
1039
1040 @Override
1041 public int[] getRequiredTokens() {
1042 return CommonUtil.EMPTY_INT_ARRAY;
1043 }
1044
1045
1046
1047
1048
1049 static void clearExecutionOrder() {
1050 EXECUTION_ORDER.clear();
1051 }
1052
1053
1054
1055
1056
1057
1058
1059 static List<String> getExecutionOrder() {
1060 return new ArrayList<>(EXECUTION_ORDER);
1061 }
1062 }
1063
1064 }