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;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.Checker.EXCEPTION_MSG;
24  import static com.puppycrawl.tools.checkstyle.DefaultLogger.AUDIT_FINISHED_MESSAGE;
25  import static com.puppycrawl.tools.checkstyle.DefaultLogger.AUDIT_STARTED_MESSAGE;
26  import static com.puppycrawl.tools.checkstyle.checks.NewlineAtEndOfFileCheck.MSG_KEY_NO_NEWLINE_EOF;
27  import static com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck.MSG_KEY;
28  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
29  
30  import java.io.BufferedReader;
31  import java.io.BufferedWriter;
32  import java.io.ByteArrayInputStream;
33  import java.io.ByteArrayOutputStream;
34  import java.io.File;
35  import java.io.IOError;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.io.InputStreamReader;
39  import java.io.LineNumberReader;
40  import java.io.OutputStream;
41  import java.io.Serial;
42  import java.io.UnsupportedEncodingException;
43  import java.nio.charset.StandardCharsets;
44  import java.nio.file.Files;
45  import java.util.ArrayList;
46  import java.util.Arrays;
47  import java.util.Collections;
48  import java.util.HashSet;
49  import java.util.List;
50  import java.util.Locale;
51  import java.util.Objects;
52  import java.util.Properties;
53  import java.util.Set;
54  import java.util.SortedSet;
55  import java.util.TreeSet;
56  import java.util.UUID;
57  
58  import org.apache.tools.ant.BuildException;
59  import org.apache.tools.ant.Project;
60  import org.junit.jupiter.api.Test;
61  import org.junit.jupiter.api.io.TempDir;
62  
63  import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
64  import com.puppycrawl.tools.checkstyle.ant.CheckstyleAntTask;
65  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
66  import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
67  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
68  import com.puppycrawl.tools.checkstyle.api.AuditListener;
69  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
70  import com.puppycrawl.tools.checkstyle.api.Configuration;
71  import com.puppycrawl.tools.checkstyle.api.Context;
72  import com.puppycrawl.tools.checkstyle.api.DetailAST;
73  import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
74  import com.puppycrawl.tools.checkstyle.api.FileText;
75  import com.puppycrawl.tools.checkstyle.api.Filter;
76  import com.puppycrawl.tools.checkstyle.api.FilterSet;
77  import com.puppycrawl.tools.checkstyle.api.MessageDispatcher;
78  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
79  import com.puppycrawl.tools.checkstyle.api.Violation;
80  import com.puppycrawl.tools.checkstyle.checks.NewlineAtEndOfFileCheck;
81  import com.puppycrawl.tools.checkstyle.checks.TranslationCheck;
82  import com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck;
83  import com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck;
84  import com.puppycrawl.tools.checkstyle.filefilters.BeforeExecutionExclusionFileFilter;
85  import com.puppycrawl.tools.checkstyle.filters.SuppressionFilter;
86  import com.puppycrawl.tools.checkstyle.internal.testmodules.CheckWhichThrowsError;
87  import com.puppycrawl.tools.checkstyle.internal.testmodules.DebugAuditAdapter;
88  import com.puppycrawl.tools.checkstyle.internal.testmodules.DebugFilter;
89  import com.puppycrawl.tools.checkstyle.internal.testmodules.TestBeforeExecutionFileFilter;
90  import com.puppycrawl.tools.checkstyle.internal.testmodules.TestFileSetCheck;
91  import com.puppycrawl.tools.checkstyle.internal.utils.CloseAndFlushTestByteArrayOutputStream;
92  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
93  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
94  import de.thetaphi.forbiddenapis.SuppressForbidden;
95  
96  /**
97   * CheckerTest.
98   *
99   * @noinspection ClassWithTooManyDependencies
100  * @noinspectionreason ClassWithTooManyDependencies - complex tests require a large number
101  *      of imports
102  */
103 public class CheckerTest extends AbstractModuleTestSupport {
104 
105     @TempDir
106     public File temporaryFolder;
107 
108     private File createTempFile(String prefix) throws IOException {
109         return createTempFile(prefix, ".tmp");
110     }
111 
112     private File createTempFile(String prefix, String suffix) throws IOException {
113         final String name = Objects.requireNonNull(prefix)
114                 + UUID.randomUUID()
115                 + Objects.requireNonNull(suffix);
116         return Files.createFile(temporaryFolder.toPath().resolve(name)).toFile();
117     }
118 
119     private static void invokeFireAuditFinished(Checker checker)
120             throws ReflectiveOperationException {
121         TestUtil.invokeVoidMethod(checker, "fireAuditFinished");
122     }
123 
124     private static void invokeFireAuditStartedMethod(Checker checker)
125             throws ReflectiveOperationException {
126         TestUtil.invokeVoidMethod(checker, "fireAuditStarted");
127     }
128 
129     @Override
130     public String getPackageLocation() {
131         return "com/puppycrawl/tools/checkstyle/checker";
132     }
133 
134     private String getLocalizedMessage(String messageKey, Object... args) {
135         final LocalizedMessage localizedMessage = new LocalizedMessage(
136             Definitions.CHECKSTYLE_BUNDLE, getClass(),
137                     messageKey, args);
138 
139         return localizedMessage.getMessage();
140     }
141 
142     @Test
143     public void testDestroy() throws Exception {
144         final Checker checker = new Checker();
145         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
146         checker.addListener(auditAdapter);
147         final TestFileSetCheck fileSet = new TestFileSetCheck();
148         checker.addFileSetCheck(fileSet);
149         final DebugFilter filter = new DebugFilter();
150         checker.addFilter(filter);
151         final TestBeforeExecutionFileFilter fileFilter = new TestBeforeExecutionFileFilter();
152         checker.addBeforeExecutionFileFilter(fileFilter);
153 
154         // should remove all listeners, file sets, and filters
155         checker.destroy();
156 
157         final File tempFile = createTempFile("junit");
158         checker.process(Collections.singletonList(tempFile));
159         final SortedSet<Violation> violations = new TreeSet<>();
160         violations.add(new Violation(1, 0, "a Bundle", "message.key",
161                 new Object[] {"arg"}, null, getClass(), null));
162         checker.fireErrors("Some File Name", violations);
163 
164         assertWithMessage("Checker.destroy() doesn't remove listeners.")
165                 .that(auditAdapter.wasCalled())
166                 .isFalse();
167         assertWithMessage("Checker.destroy() doesn't remove file sets.")
168                 .that(fileSet.wasCalled())
169                 .isFalse();
170         assertWithMessage("Checker.destroy() doesn't remove filters.")
171                 .that(filter.wasCalled())
172                 .isFalse();
173         assertWithMessage("Checker.destroy() doesn't remove file filters.")
174                 .that(fileFilter.wasCalled())
175                 .isFalse();
176     }
177 
178     @Test
179     public void testAddListener() throws Exception {
180         final Checker checker = new Checker();
181         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
182         checker.addListener(auditAdapter);
183 
184         // Let's try fire some events
185         invokeFireAuditStartedMethod(checker);
186         assertWithMessage("Checker.fireAuditStarted() doesn't call listener")
187                 .that(auditAdapter.wasCalled())
188                 .isTrue();
189         assertWithMessage("Checker.fireAuditStarted() doesn't pass event")
190                 .that(auditAdapter.wasEventPassed())
191                 .isTrue();
192 
193         auditAdapter.resetListener();
194         invokeFireAuditFinished(checker);
195         assertWithMessage("Checker.fireAuditFinished() doesn't call listener")
196                 .that(auditAdapter.wasCalled())
197                 .isTrue();
198         assertWithMessage("Checker.fireAuditFinished() doesn't pass event")
199                 .that(auditAdapter.wasEventPassed())
200                 .isTrue();
201 
202         auditAdapter.resetListener();
203         checker.fireFileStarted("Some File Name");
204         assertWithMessage("Checker.fireFileStarted() doesn't call listener")
205                 .that(auditAdapter.wasCalled())
206                 .isTrue();
207         assertWithMessage("Checker.fireFileStarted() doesn't pass event")
208                 .that(auditAdapter.wasEventPassed())
209                 .isTrue();
210 
211         auditAdapter.resetListener();
212         checker.fireFileFinished("Some File Name");
213         assertWithMessage("Checker.fireFileFinished() doesn't call listener")
214                 .that(auditAdapter.wasCalled())
215                 .isTrue();
216         assertWithMessage("Checker.fireFileFinished() doesn't pass event")
217                 .that(auditAdapter.wasEventPassed())
218                 .isTrue();
219 
220         auditAdapter.resetListener();
221         final SortedSet<Violation> violations = new TreeSet<>();
222         violations.add(new Violation(1, 0, "a Bundle", "message.key",
223                 new Object[] {"arg"}, null, getClass(), null));
224         checker.fireErrors("Some File Name", violations);
225         assertWithMessage("Checker.fireErrors() doesn't call listener")
226                 .that(auditAdapter.wasCalled())
227                 .isTrue();
228         assertWithMessage("Checker.fireErrors() doesn't pass event")
229                 .that(auditAdapter.wasEventPassed())
230                 .isTrue();
231     }
232 
233     @Test
234     public void testRemoveListener() throws Exception {
235         final Checker checker = new Checker();
236         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
237         final DebugAuditAdapter aa2 = new DebugAuditAdapter();
238         checker.addListener(auditAdapter);
239         checker.addListener(aa2);
240         checker.removeListener(auditAdapter);
241 
242         // Let's try fire some events
243         invokeFireAuditStartedMethod(checker);
244         assertWithMessage("Checker.fireAuditStarted() doesn't call listener")
245                 .that(aa2.wasCalled())
246                 .isTrue();
247         assertWithMessage("Checker.fireAuditStarted() does call removed listener")
248                 .that(auditAdapter.wasCalled())
249                 .isFalse();
250 
251         aa2.resetListener();
252         invokeFireAuditFinished(checker);
253         assertWithMessage("Checker.fireAuditFinished() doesn't call listener")
254                 .that(aa2.wasCalled())
255                 .isTrue();
256         assertWithMessage("Checker.fireAuditFinished() does call removed listener")
257                 .that(auditAdapter.wasCalled())
258                 .isFalse();
259 
260         aa2.resetListener();
261         checker.fireFileStarted("Some File Name");
262         assertWithMessage("Checker.fireFileStarted() doesn't call listener")
263                 .that(aa2.wasCalled())
264                 .isTrue();
265         assertWithMessage("Checker.fireFileStarted() does call removed listener")
266                 .that(auditAdapter.wasCalled())
267                 .isFalse();
268 
269         aa2.resetListener();
270         checker.fireFileFinished("Some File Name");
271         assertWithMessage("Checker.fireFileFinished() doesn't call listener")
272                 .that(aa2.wasCalled())
273                 .isTrue();
274         assertWithMessage("Checker.fireFileFinished() does call removed listener")
275                 .that(auditAdapter.wasCalled())
276                 .isFalse();
277 
278         aa2.resetListener();
279         final SortedSet<Violation> violations = new TreeSet<>();
280         violations.add(new Violation(1, 0, "a Bundle", "message.key",
281                 new Object[] {"arg"}, null, getClass(), null));
282         checker.fireErrors("Some File Name", violations);
283         assertWithMessage("Checker.fireErrors() doesn't call listener")
284                 .that(aa2.wasCalled())
285                 .isTrue();
286         assertWithMessage("Checker.fireErrors() does call removed listener")
287                 .that(auditAdapter.wasCalled())
288                 .isFalse();
289     }
290 
291     @Test
292     public void testAddBeforeExecutionFileFilter() throws Exception {
293         final Checker checker = new Checker();
294         final TestBeforeExecutionFileFilter filter = new TestBeforeExecutionFileFilter();
295 
296         checker.addBeforeExecutionFileFilter(filter);
297 
298         filter.resetFilter();
299         checker.process(Collections.singletonList(new File("dummy.java")));
300         assertWithMessage("Checker.acceptFileStarted() doesn't call filter")
301                 .that(filter.wasCalled())
302                 .isTrue();
303     }
304 
305     @Test
306     public void testRemoveBeforeExecutionFileFilter() throws Exception {
307         final Checker checker = new Checker();
308         final TestBeforeExecutionFileFilter filter = new TestBeforeExecutionFileFilter();
309         final TestBeforeExecutionFileFilter f2 = new TestBeforeExecutionFileFilter();
310         checker.addBeforeExecutionFileFilter(filter);
311         checker.addBeforeExecutionFileFilter(f2);
312         checker.removeBeforeExecutionFileFilter(filter);
313 
314         f2.resetFilter();
315         checker.process(Collections.singletonList(new File("dummy.java")));
316         assertWithMessage("Checker.acceptFileStarted() doesn't call filter")
317                 .that(f2.wasCalled())
318                 .isTrue();
319         assertWithMessage("Checker.acceptFileStarted() does call removed filter")
320                 .that(filter.wasCalled())
321                 .isFalse();
322     }
323 
324     @Test
325     public void testAddFilter() {
326         final Checker checker = new Checker();
327         final DebugFilter filter = new DebugFilter();
328 
329         checker.addFilter(filter);
330 
331         filter.resetFilter();
332         final SortedSet<Violation> violations = new TreeSet<>();
333         violations.add(new Violation(1, 0, "a Bundle", "message.key",
334                 new Object[] {"arg"}, null, getClass(), null));
335         checker.fireErrors("Some File Name", violations);
336         assertWithMessage("Checker.fireErrors() doesn't call filter")
337                 .that(filter.wasCalled())
338                 .isTrue();
339     }
340 
341     @Test
342     public void testRemoveFilter() {
343         final Checker checker = new Checker();
344         final DebugFilter filter = new DebugFilter();
345         final DebugFilter f2 = new DebugFilter();
346         checker.addFilter(filter);
347         checker.addFilter(f2);
348         checker.removeFilter(filter);
349 
350         f2.resetFilter();
351         final SortedSet<Violation> violations = new TreeSet<>();
352         violations.add(new Violation(1, 0, "a Bundle", "message.key",
353                 new Object[] {"arg"}, null, getClass(), null));
354         checker.fireErrors("Some File Name", violations);
355         assertWithMessage("Checker.fireErrors() doesn't call filter")
356                 .that(f2.wasCalled())
357                 .isTrue();
358         assertWithMessage("Checker.fireErrors() does call removed filter")
359                 .that(filter.wasCalled())
360                 .isFalse();
361     }
362 
363     @Test
364     public void testFileExtensions() throws Exception {
365         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
366         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
367         checkerConfig.addProperty("cacheFile", createTempFile("junit").getPath());
368 
369         final Checker checker = new Checker();
370         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
371         checker.configure(checkerConfig);
372 
373         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
374         checker.addListener(auditAdapter);
375 
376         final List<File> files = new ArrayList<>();
377         final File file = new File("file.pdf");
378         files.add(file);
379         final File otherFile = new File("file.java");
380         files.add(otherFile);
381         final String[] fileExtensions = {"java", "xml", "properties"};
382         checker.setFileExtensions(fileExtensions);
383         checker.setCacheFile(createTempFile("junit").getPath());
384         final int counter = checker.process(files);
385 
386         // comparing to 1 as there is only one legal file in input
387         final int numLegalFiles = 1;
388         final PropertyCacheFile cache = TestUtil.getInternalState(checker,
389                 "cacheFile", PropertyCacheFile.class);
390         assertWithMessage("There were more legal files than expected")
391             .that(counter)
392             .isEqualTo(numLegalFiles);
393         assertWithMessage("Audit was started on larger amount of files than expected")
394             .that(auditAdapter.getNumFilesStarted())
395             .isEqualTo(numLegalFiles);
396         assertWithMessage("Audit was finished on larger amount of files than expected")
397             .that(auditAdapter.getNumFilesFinished())
398             .isEqualTo(numLegalFiles);
399         assertWithMessage("Cache shout not contain any file")
400             .that(cache.get(new File("file.java").getCanonicalPath()))
401             .isNull();
402     }
403 
404     @Test
405     public void testIgnoredFileExtensions() throws Exception {
406         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
407         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
408         final File tempFile = createTempFile("junit");
409         checkerConfig.addProperty("cacheFile", tempFile.getPath());
410 
411         final Checker checker = new Checker();
412         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
413         checker.configure(checkerConfig);
414 
415         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
416         checker.addListener(auditAdapter);
417 
418         final List<File> allIgnoredFiles = new ArrayList<>();
419         final File ignoredFile = new File("file.pdf");
420         allIgnoredFiles.add(ignoredFile);
421         final String[] fileExtensions = {"java", "xml", "properties"};
422         checker.setFileExtensions(fileExtensions);
423         checker.setCacheFile(createTempFile("junit").getPath());
424         final int counter = checker.process(allIgnoredFiles);
425 
426         // comparing to 0 as there is no legal file in input
427         final int numLegalFiles = 0;
428         assertWithMessage("There were more legal files than expected")
429             .that(counter)
430             .isEqualTo(numLegalFiles);
431         assertWithMessage("Audit was started on larger amount of files than expected")
432             .that(auditAdapter.getNumFilesStarted())
433             .isEqualTo(numLegalFiles);
434         assertWithMessage("Audit was finished on larger amount of files than expected")
435             .that(auditAdapter.getNumFilesFinished())
436             .isEqualTo(numLegalFiles);
437     }
438 
439     @Test
440     public void testSetters() {
441         // all  that is set by reflection, so just make code coverage be happy
442         final Checker checker = new Checker();
443         checker.setBasedir("some");
444         checker.setSeverity("ignore");
445 
446         final PackageObjectFactory factory = new PackageObjectFactory(
447             new HashSet<>(), Thread.currentThread().getContextClassLoader());
448         checker.setModuleFactory(factory);
449 
450         checker.setFileExtensions((String[]) null);
451         checker.setFileExtensions(".java", "xml");
452 
453         final UnsupportedEncodingException exc =
454                 getExpectedThrowable(UnsupportedEncodingException.class, () -> {
455                     checker.setCharset("UNKNOWN-CHARSET");
456                 }, "Exception is expected");
457         assertWithMessage("Error message is not expected")
458             .that(exc.getMessage())
459             .isEqualTo("unsupported charset: 'UNKNOWN-CHARSET'");
460     }
461 
462     @Test
463     public void testSetSeverity() throws Exception {
464         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
465 
466         verifyWithInlineXmlConfig(getPath("InputCheckerTestSeverity.java"), expected);
467     }
468 
469     @Test
470     public void testNoClassLoaderNoModuleFactory() {
471         final Checker checker = new Checker();
472 
473         final CheckstyleException exc =
474                 getExpectedThrowable(CheckstyleException.class,
475                     checker::finishLocalSetup, "Exception is expected");
476         assertWithMessage("Error message is not expected")
477             .that(exc.getMessage())
478             .isEqualTo("if no custom moduleFactory is set,"
479                 + " moduleClassLoader must be specified");
480     }
481 
482     @Test
483     public void testNoModuleFactory() throws Exception {
484         final Checker checker = new Checker();
485         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
486 
487         checker.setModuleClassLoader(classLoader);
488         checker.finishLocalSetup();
489         final Context actualCtx = TestUtil.getInternalState(checker, "childContext", Context.class);
490 
491         assertWithMessage("Default module factory should be created when it is not specified")
492             .that(actualCtx.get("moduleFactory"))
493             .isNotNull();
494     }
495 
496     @Test
497     public void testFinishLocalSetupFullyInitialized() throws Exception {
498         final Checker checker = new Checker();
499         final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
500         checker.setModuleClassLoader(contextClassLoader);
501         final PackageObjectFactory factory = new PackageObjectFactory(
502             new HashSet<>(), contextClassLoader);
503         checker.setModuleFactory(factory);
504         checker.setBasedir("testBaseDir");
505         checker.setLocaleLanguage("it");
506         checker.setLocaleCountry("IT");
507         checker.finishLocalSetup();
508 
509         final Context context = TestUtil.getInternalState(checker, "childContext", Context.class);
510         final String encoding = StandardCharsets.UTF_8.name();
511         assertWithMessage("Charset was different than expected")
512             .that(context.get("charset"))
513             .isEqualTo(encoding);
514         assertWithMessage("Severity is set to unexpected value")
515             .that(context.get("severity"))
516             .isEqualTo("error");
517         assertWithMessage("Basedir is set to unexpected value")
518             .that(context.get("basedir"))
519             .isEqualTo("testBaseDir");
520 
521         final Locale locale = TestUtil.getInternalStaticState(LocalizedMessage.class,
522                 "messageLocale", Locale.class);
523         assertWithMessage("Locale is set to unexpected value")
524             .that(locale)
525             .isEqualTo(Locale.ITALY);
526     }
527 
528     @Test
529     public void testSetupChildExceptions() {
530         final Checker checker = new Checker();
531         final PackageObjectFactory factory = new PackageObjectFactory(
532             new HashSet<>(), Thread.currentThread().getContextClassLoader());
533         checker.setModuleFactory(factory);
534 
535         final Configuration config = new DefaultConfiguration("java.lang.String");
536         final CheckstyleException exc =
537                 getExpectedThrowable(CheckstyleException.class, () -> {
538                     checker.setupChild(config);
539                 }, "Exception is expected");
540         assertWithMessage("Error message is not expected")
541             .that(exc.getMessage())
542             .isEqualTo("java.lang.String is not allowed as a child in Checker");
543     }
544 
545     @Test
546     public void testSetupChildInvalidProperty() {
547         final DefaultConfiguration checkConfig = createModuleConfig(HiddenFieldCheck.class);
548         checkConfig.addProperty("$$No such property", null);
549         final CheckstyleException exc =
550                 getExpectedThrowable(CheckstyleException.class, () -> {
551                     createChecker(checkConfig);
552                 }, "Exception is expected");
553         assertWithMessage("Error message is not expected")
554             .that(exc.getMessage())
555             .isEqualTo("cannot initialize module com.puppycrawl.tools.checkstyle.TreeWalker"
556                     + " - cannot initialize module " + checkConfig.getName());
557     }
558 
559     @Test
560     public void testSetupChildListener() throws Exception {
561         final Checker checker = new Checker();
562         final PackageObjectFactory factory = new PackageObjectFactory(
563             new HashSet<>(), Thread.currentThread().getContextClassLoader());
564         checker.setModuleFactory(factory);
565 
566         final Configuration config = new DefaultConfiguration(
567             DebugAuditAdapter.class.getCanonicalName());
568         checker.setupChild(config);
569 
570         final List<AuditListener> listeners = TestUtil.getInternalStateListAuditListener(checker,
571                 "listeners");
572         assertWithMessage("Invalid child listener class")
573                 .that(listeners.getLast() instanceof DebugAuditAdapter)
574                 .isTrue();
575     }
576 
577     @Test
578     public void testDestroyCheckerWithWrongCacheFileNameLength() throws Exception {
579         final Checker checker = new Checker();
580         final PackageObjectFactory factory = new PackageObjectFactory(
581             new HashSet<>(), Thread.currentThread().getContextClassLoader());
582         checker.setModuleFactory(factory);
583         checker.configure(new DefaultConfiguration("default config"));
584         // We set wrong file name length in order to reproduce IOException on OS Linux, OS Windows.
585         // The maximum file name length which is allowed in most UNIX, Windows file systems is 255.
586         // See https://en.wikipedia.org/wiki/Filename;
587         checker.setCacheFile(String.format(Locale.ENGLISH, "%0300d", 0));
588         final IllegalStateException exc =
589                 getExpectedThrowable(IllegalStateException.class,
590                     checker::destroy, "Exception did not happen");
591         assertWithMessage("Cause of exception differs from IOException")
592             .that(exc.getCause())
593             .isInstanceOf(IOException.class);
594         assertWithMessage("Exception message differ")
595             .that(exc.getMessage())
596             .isEqualTo(getLocalizedMessage(
597                     "Checker.cacheFilesException"));
598     }
599 
600     /**
601      * It is OK to have long test method name here as it describes the test purpose.
602      */
603     @Test
604     public void testCacheAndCheckWhichDoesNotImplementExternalResourceHolderInterface()
605             throws Exception {
606         assertWithMessage("ExternalResourceHolder has changed his parent")
607                 .that(ExternalResourceHolder.class.isAssignableFrom(HiddenFieldCheck.class))
608                 .isFalse();
609         final DefaultConfiguration checkConfig = createModuleConfig(HiddenFieldCheck.class);
610 
611         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
612         treeWalkerConfig.addChild(checkConfig);
613 
614         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
615         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
616 
617         final File cacheFile = createTempFile("junit");
618         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
619 
620         final File tmpFile = createTempFile("file", ".java");
621 
622         execute(checkerConfig, tmpFile.getPath());
623         final Properties cacheAfterFirstRun = new Properties();
624         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
625             cacheAfterFirstRun.load(reader);
626         }
627 
628         // one more time to reuse cache
629         execute(checkerConfig, tmpFile.getPath());
630         final Properties cacheAfterSecondRun = new Properties();
631         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
632             cacheAfterSecondRun.load(reader);
633         }
634 
635         assertWithMessage("Cache from first run differs from second run cache")
636             .that(cacheAfterSecondRun)
637             .isEqualTo(cacheAfterFirstRun);
638     }
639 
640     @Test
641     public void testWithCacheWithNoViolation() throws Exception {
642         final Checker checker = new Checker();
643         final PackageObjectFactory factory = new PackageObjectFactory(
644             new HashSet<>(), Thread.currentThread().getContextClassLoader());
645         checker.setModuleFactory(factory);
646         checker.configure(createModuleConfig(TranslationCheck.class));
647 
648         final File cacheFile = createTempFile("junit");
649         checker.setCacheFile(cacheFile.getPath());
650 
651         checker.setupChild(createModuleConfig(TranslationCheck.class));
652         final File tmpFile = createTempFile("file", ".java");
653         final List<File> files = new ArrayList<>(1);
654         files.add(tmpFile);
655         checker.process(files);
656 
657         // invoke destroy to persist cache
658         checker.destroy();
659 
660         final Properties cache = new Properties();
661         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
662             cache.load(reader);
663         }
664 
665         // There should 2 objects in cache: processed file (file.java) and checker configuration.
666         final int expectedNumberOfObjectsInCache = 2;
667         assertWithMessage("Cache has unexpected size")
668             .that(cache)
669             .hasSize(expectedNumberOfObjectsInCache);
670 
671         final String expectedConfigHash = "D581D4A2BD482D4E1EF1F82459356BA2D8A3B" + "FC3";
672         assertWithMessage("Cache has unexpected hash")
673             .that(cache.getProperty(PropertyCacheFile.CONFIG_HASH_KEY))
674             .isEqualTo(expectedConfigHash);
675 
676         assertWithMessage("Cache file has null path")
677             .that(cache.getProperty(tmpFile.getPath()))
678             .isNotNull();
679     }
680 
681     @Test
682     public void testClearExistingCache() throws Exception {
683         final DefaultConfiguration checkerConfig = createRootConfig(null);
684         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
685         final File cacheFile = createTempFile("junit");
686         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
687 
688         final Checker checker = new Checker();
689         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
690         checker.configure(checkerConfig);
691         checker.addListener(getBriefUtLogger());
692 
693         checker.clearCache();
694         // invoke destroy to persist cache
695         checker.destroy();
696 
697         final Properties cacheAfterClear = new Properties();
698         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
699             cacheAfterClear.load(reader);
700         }
701 
702         assertWithMessage("Cache has unexpected size")
703             .that(cacheAfterClear)
704             .hasSize(1);
705         assertWithMessage("Cache has null hash")
706             .that(cacheAfterClear.getProperty(PropertyCacheFile.CONFIG_HASH_KEY))
707             .isNotNull();
708 
709         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
710 
711         // file that should be audited is not in cache
712         execute(checkerConfig, pathToEmptyFile);
713         final Properties cacheAfterSecondRun = new Properties();
714         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
715             cacheAfterSecondRun.load(reader);
716         }
717 
718         assertWithMessage("Cache has null path")
719             .that(cacheAfterSecondRun.getProperty(pathToEmptyFile))
720             .isNotNull();
721         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
722         assertWithMessage("Cash have changed it hash")
723             .that(cacheHash)
724             .isEqualTo(cacheAfterClear.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
725         final int expectedNumberOfObjectsInCacheAfterSecondRun = 2;
726         assertWithMessage("Cache has changed number of items")
727             .that(cacheAfterSecondRun)
728             .hasSize(expectedNumberOfObjectsInCacheAfterSecondRun);
729     }
730 
731     @Test
732     public void testClearCache() throws Exception {
733         final DefaultConfiguration violationCheck =
734                 createModuleConfig(DummyFileSetViolationCheck.class);
735         final DefaultConfiguration checkerConfig = new DefaultConfiguration("myConfig");
736         checkerConfig.addProperty("charset", "UTF-8");
737         final File cacheFile = createTempFile("junit");
738         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
739         checkerConfig.addChild(violationCheck);
740         final Checker checker = new Checker();
741         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
742         checker.configure(checkerConfig);
743         checker.addListener(getBriefUtLogger());
744 
745         checker.process(Collections.singletonList(new File("dummy.java")));
746         checker.clearCache();
747         // invoke destroy to persist cache
748         final PropertyCacheFile cache = TestUtil.getInternalState(checker,
749                 "cacheFile", PropertyCacheFile.class);
750         cache.persist();
751 
752         final Properties cacheAfterClear = new Properties();
753         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
754             cacheAfterClear.load(reader);
755         }
756 
757         assertWithMessage("Cache has unexpected size")
758             .that(cacheAfterClear)
759             .hasSize(1);
760     }
761 
762     @Test
763     public void setFileExtension() {
764         final Checker checker = new Checker();
765         checker.setFileExtensions(".test1", "test2");
766         final String[] actual = TestUtil.getInternalState(checker,
767                 "fileExtensions", String[].class);
768         assertWithMessage("Extensions are not expected")
769             .that(actual)
770             .isEqualTo(new String[] {".test1", ".test2"});
771     }
772 
773     @Test
774     public void testClearCacheWhenCacheFileIsNotSet() {
775         // The idea of the test is to check that when cache file is not set,
776         // the invocation of clearCache method does not throw an exception.
777         final Checker checker = new Checker();
778         checker.clearCache();
779         final PropertyCacheFile cache = TestUtil.getInternalState(checker,
780                 "cacheFile", PropertyCacheFile.class);
781         assertWithMessage("If cache file is not set the cache should default to null")
782             .that(cache)
783             .isNull();
784     }
785 
786     /**
787      * Test doesn't need to be serialized.
788      *
789      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
790      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
791      *      for test does not require serialization
792      */
793     @Test
794     public void testCatchErrorInProcessFilesMethod() {
795         // Assume that I/O error is happened when we try to invoke 'lastModified()' method.
796         final String errorMessage = "Java Virtual Machine is broken"
797             + " or has run out of resources necessary for it to continue operating.";
798         final Error expectedError = new IOError(new InternalError(errorMessage));
799 
800         final File mock = new File("testFile") {
801             @Serial
802             private static final long serialVersionUID = 1L;
803 
804             /**
805              * Test is checking catch clause when exception is thrown.
806              *
807              * @noinspection ProhibitedExceptionThrown
808              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
809              *      throw exception as part of test
810              */
811             @Override
812             public long lastModified() {
813                 throw expectedError;
814             }
815         };
816 
817         final Checker checker = new Checker();
818         final List<File> filesToProcess = new ArrayList<>();
819         filesToProcess.add(mock);
820         final Error error =
821                 getExpectedThrowable(Error.class, () -> {
822                     checker.process(filesToProcess);
823                 }, "IOError is expected!");
824         assertWithMessage("Error cause differs from IOError")
825             .that(error.getCause())
826             .isInstanceOf(IOError.class);
827         assertWithMessage("Error cause is not InternalError")
828             .that(error.getCause().getCause())
829             .isInstanceOf(InternalError.class);
830         assertWithMessage("Error message is not expected")
831             .that(error)
832             .hasCauseThat()
833             .hasCauseThat()
834             .hasMessageThat()
835             .isEqualTo(errorMessage);
836         assertWithMessage("Error message differs")
837             .that(error.getMessage())
838             .isEqualTo(getLocalizedMessage(
839                     "Checker.error", mock.getPath()));
840     }
841 
842     /**
843      * Test doesn't need to be serialized.
844      *
845      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
846      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
847      *      for test does not require serialization
848      */
849     @Test
850     public void testCatchErrorWithNoFileName() {
851         // Assume that I/O error is happened when we try to invoke 'lastModified()' method.
852         final String errorMessage = "Java Virtual Machine is broken"
853             + " or has run out of resources necessary for it to continue operating.";
854         final Error expectedError = new IOError(new InternalError(errorMessage));
855 
856         final File mock = new File("testFile") {
857             @Serial
858             private static final long serialVersionUID = 1L;
859             /**
860              * Test is checking catch clause when exception is thrown.
861              *
862              * @noinspection ProhibitedExceptionThrown
863              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
864              *      throw exception as part of test
865              */
866             @Override
867             public long lastModified() {
868                 throw expectedError;
869             }
870 
871             @Override
872             public String getAbsolutePath() {
873                 return null;
874             }
875         };
876 
877         final Checker checker = new Checker();
878         final List<File> filesToProcess = new ArrayList<>();
879         filesToProcess.add(mock);
880         final Error error =
881                 getExpectedThrowable(Error.class, () -> {
882                     checker.process(filesToProcess);
883                 }, "IOError is expected!");
884         assertWithMessage("Error cause differs from IOError")
885             .that(error)
886             .hasCauseThat()
887             .isInstanceOf(IOError.class);
888         assertWithMessage("Error cause is not InternalError")
889             .that(error)
890             .hasCauseThat()
891             .hasCauseThat()
892             .isInstanceOf(InternalError.class);
893         assertWithMessage("Error message is not expected")
894             .that(error)
895             .hasCauseThat()
896             .hasCauseThat()
897             .hasMessageThat()
898             .isEqualTo(errorMessage);
899     }
900 
901     /**
902      * It is OK to have long test method name here as it describes the test purpose.
903      */
904     @Test
905     public void testCacheAndFilterWhichDoesNotImplementExternalResourceHolderInterface()
906             throws Exception {
907         assertWithMessage("ExternalResourceHolder has changed its parent")
908                 .that(ExternalResourceHolder.class.isAssignableFrom(DummyFilter.class))
909                 .isFalse();
910         final DefaultConfiguration filterConfig = createModuleConfig(DummyFilter.class);
911 
912         final DefaultConfiguration checkerConfig = createRootConfig(filterConfig);
913         final File cacheFile = createTempFile("junit");
914         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
915 
916         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
917 
918         execute(checkerConfig, pathToEmptyFile);
919         final Properties cacheAfterFirstRun = new Properties();
920         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
921             cacheAfterFirstRun.load(reader);
922         }
923 
924         // One more time to use cache.
925         execute(checkerConfig, pathToEmptyFile);
926         final Properties cacheAfterSecondRun = new Properties();
927         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
928             cacheAfterSecondRun.load(reader);
929         }
930 
931         final String cacheFilePath = cacheAfterSecondRun.getProperty(pathToEmptyFile);
932         assertWithMessage("Cache file has changed its path")
933             .that(cacheFilePath)
934             .isEqualTo(cacheAfterFirstRun.getProperty(pathToEmptyFile));
935         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
936         assertWithMessage("Cache has changed its hash")
937             .that(cacheHash)
938             .isEqualTo(cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
939         final int expectedNumberOfObjectsInCache = 2;
940         assertWithMessage("Number of items in cache differs from expected")
941             .that(cacheAfterFirstRun)
942             .hasSize(expectedNumberOfObjectsInCache);
943         assertWithMessage("Number of items in cache differs from expected")
944             .that(cacheAfterSecondRun)
945             .hasSize(expectedNumberOfObjectsInCache);
946     }
947 
948     /**
949      * It is OK to have long test method name here as it describes the test purpose.
950      */
951     // -@cs[ExecutableStatementCount] This test needs to verify many things.
952     @Test
953     public void testCacheAndCheckWhichAddsNewResourceLocationButKeepsSameCheckerInstance()
954             throws Exception {
955         // Use case (https://github.com/checkstyle/checkstyle/pull/3092#issuecomment-218162436):
956         // Imagine that cache exists in a file. New version of Checkstyle appear.
957         // New release contains update to a some check to have additional external resource.
958         // User update his configuration and run validation as usually.
959         // Cache should not be reused.
960 
961         final DynamicalResourceHolderCheck check = new DynamicalResourceHolderCheck();
962         final String firstExternalResourceLocation = getPath("InputCheckerImportControlOne.xml");
963         final String firstExternalResourceKey = PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
964                 + firstExternalResourceLocation;
965         check.setFirstExternalResourceLocation(firstExternalResourceLocation);
966 
967         final DefaultConfiguration checkerConfig = createRootConfig(null);
968         final File cacheFile = createTempFile("junit");
969         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
970 
971         final Checker checker = new Checker();
972         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
973         checker.addFileSetCheck(check);
974         checker.addFilter(new DummyFilterSet());
975         checker.configure(checkerConfig);
976         checker.addListener(getBriefUtLogger());
977 
978         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
979 
980         execute(checker, pathToEmptyFile);
981         final Properties cacheAfterFirstRun = new Properties();
982         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
983             cacheAfterFirstRun.load(reader);
984         }
985 
986         final int expectedNumberOfObjectsInCacheAfterFirstRun = 4;
987         assertWithMessage("Number of items in cache differs from expected")
988             .that(cacheAfterFirstRun)
989             .hasSize(expectedNumberOfObjectsInCacheAfterFirstRun);
990 
991         // Change a list of external resources which are used by the check
992         final String secondExternalResourceLocation = "InputCheckerImportControlTwo.xml";
993         final String secondExternalResourceKey = PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
994                 + secondExternalResourceLocation;
995         check.setSecondExternalResourceLocation(secondExternalResourceLocation);
996 
997         checker.addFileSetCheck(check);
998         checker.configure(checkerConfig);
999 
1000         execute(checker, pathToEmptyFile);
1001         final Properties cacheAfterSecondRun = new Properties();
1002         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1003             cacheAfterSecondRun.load(reader);
1004         }
1005 
1006         final String cacheFilePath = cacheAfterSecondRun.getProperty(pathToEmptyFile);
1007         assertWithMessage("Cache file has changed its path")
1008             .that(cacheFilePath)
1009             .isEqualTo(cacheAfterFirstRun.getProperty(pathToEmptyFile));
1010         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
1011         assertWithMessage("Cache has changed its hash")
1012             .that(cacheHash)
1013             .isEqualTo(cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
1014         final String resourceKey = cacheAfterSecondRun.getProperty(firstExternalResourceKey);
1015         assertWithMessage("Cache has changed its resource key")
1016             .that(resourceKey)
1017             .isEqualTo(cacheAfterFirstRun.getProperty(firstExternalResourceKey));
1018         assertWithMessage("Cache has null as a resource key")
1019             .that(cacheAfterFirstRun.getProperty(firstExternalResourceKey))
1020             .isNotNull();
1021         final int expectedNumberOfObjectsInCacheAfterSecondRun = 4;
1022         assertWithMessage("Number of items in cache differs from expected")
1023             .that(cacheAfterSecondRun)
1024             .hasSize(expectedNumberOfObjectsInCacheAfterSecondRun);
1025         assertWithMessage("Cache has not null as a resource key")
1026             .that(cacheAfterFirstRun.getProperty(secondExternalResourceKey))
1027             .isNull();
1028         assertWithMessage("Cache has null as a resource key")
1029             .that(cacheAfterSecondRun.getProperty(secondExternalResourceKey))
1030             .isNotNull();
1031     }
1032 
1033     @Test
1034     public void testClearLazyLoadCacheInDetailAST() throws Exception {
1035 
1036         final String filePath = getPath("InputCheckerClearDetailAstLazyLoadCache.java");
1037 
1038         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
1039         verifyWithInlineConfigParser(filePath, expected);
1040     }
1041 
1042     @Test
1043     public void testCacheOnViolationSuppression() throws Exception {
1044         final File cacheFile = createTempFile("junit");
1045         final DefaultConfiguration violationCheck =
1046                 createModuleConfig(DummyFileSetViolationCheck.class);
1047 
1048         final DefaultConfiguration filterConfig = createModuleConfig(SuppressionFilter.class);
1049         filterConfig.addProperty("file", getPath("InputCheckerSuppressAll.xml"));
1050 
1051         final DefaultConfiguration checkerConfig = createRootConfig(violationCheck);
1052         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1053         checkerConfig.addChild(filterConfig);
1054 
1055         final String fileViolationPath = createTempFile("ViolationFile", ".java").getPath();
1056 
1057         execute(checkerConfig, fileViolationPath);
1058 
1059         try (InputStream input = Files.newInputStream(cacheFile.toPath())) {
1060             final Properties details = new Properties();
1061             details.load(input);
1062 
1063             assertWithMessage("suppressed violation file saved in cache")
1064                 .that(details.getProperty(fileViolationPath))
1065                 .isNotNull();
1066         }
1067     }
1068 
1069     @Test
1070     public void testHaltOnException() throws Exception {
1071         final DefaultConfiguration checkConfig =
1072             createModuleConfig(CheckWhichThrowsError.class);
1073         final String filePath = getPath("InputChecker.java");
1074         final CheckstyleException exc =
1075                 getExpectedThrowable(CheckstyleException.class, () -> {
1076                     execute(checkConfig, filePath);
1077                 }, "Exception is expected");
1078         assertWithMessage("Error message is not expected")
1079             .that(exc.getMessage())
1080             .isEqualTo("Exception was thrown while processing " + filePath);
1081     }
1082 
1083     @Test
1084     public void testExceptionWithCache() throws Exception {
1085         final File cacheFile = createTempFile("junit");
1086 
1087         final DefaultConfiguration checkConfig =
1088                 createModuleConfig(CheckWhichThrowsError.class);
1089 
1090         final DefaultConfiguration treewalkerConfig =
1091                 createModuleConfig(TreeWalker.class);
1092         treewalkerConfig.addChild(checkConfig);
1093 
1094         final DefaultConfiguration checkerConfig = createRootConfig(treewalkerConfig);
1095         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1096         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1097         checkerConfig.addChild(treewalkerConfig);
1098 
1099         final Checker checker = createChecker(checkerConfig);
1100 
1101         final String filePath = getPath("InputChecker.java");
1102         final CheckstyleException exc =
1103                 getExpectedThrowable(CheckstyleException.class, () -> {
1104                     checker.process(Collections.singletonList(new File(filePath)));
1105                 }, "Exception is expected");
1106         assertWithMessage("Error message is not expected")
1107             .that(exc.getMessage())
1108             .isEqualTo("Exception was thrown while processing " + filePath);
1109 
1110         // destroy is called by Main
1111         checker.destroy();
1112 
1113         final Properties cache = new Properties();
1114         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1115             cache.load(reader);
1116         }
1117 
1118         assertWithMessage("Cache has unexpected size")
1119             .that(cache)
1120             .hasSize(1);
1121         assertWithMessage("testFile is not in cache")
1122             .that(cache.getProperty(filePath))
1123             .isNull();
1124     }
1125 
1126     /**
1127      * Test doesn't need to be serialized.
1128      *
1129      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1130      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1131      *      for test does not require serialization
1132      */
1133     @Test
1134     public void testCatchErrorWithCache() throws Exception {
1135         final File cacheFile = createTempFile("junit");
1136 
1137         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1138         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1139         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1140 
1141         final String errorMessage = "Java Virtual Machine is broken"
1142             + " or has run out of resources necessary for it to continue operating.";
1143         final Error expectedError = new IOError(new InternalError(errorMessage));
1144 
1145         final File mock = new File("testFile") {
1146             @Serial
1147             private static final long serialVersionUID = 1L;
1148             @Override
1149             public String getAbsolutePath() {
1150                 return "testFile";
1151             }
1152 
1153             /**
1154              * Test is checking catch clause when exception is thrown.
1155              *
1156              * @noinspection ProhibitedExceptionThrown
1157              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1158              *      throw exception as part of test
1159              */
1160             @Override
1161             public File getAbsoluteFile() {
1162                 throw expectedError;
1163             }
1164         };
1165 
1166         final Checker checker = new Checker();
1167         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1168         checker.configure(checkerConfig);
1169         final List<File> filesToProcess = new ArrayList<>();
1170         filesToProcess.add(mock);
1171         final Error error =
1172                 getExpectedThrowable(Error.class, () -> {
1173                     checker.process(filesToProcess);
1174                 }, "IOError is expected!");
1175         assertWithMessage("Error cause differs from IOError")
1176             .that(error.getCause())
1177             .isInstanceOf(IOError.class);
1178         assertWithMessage("Error message is not expected")
1179             .that(error)
1180             .hasCauseThat()
1181             .hasCauseThat()
1182             .hasMessageThat()
1183             .isEqualTo(errorMessage);
1184 
1185         // destroy is called by Main
1186         checker.destroy();
1187 
1188         final Properties cache = new Properties();
1189         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1190             cache.load(reader);
1191         }
1192 
1193         assertWithMessage("Cache has unexpected size")
1194             .that(cache)
1195             .hasSize(1);
1196         assertWithMessage("testFile is not in cache")
1197             .that(cache.getProperty("testFile"))
1198             .isNull();
1199     }
1200 
1201     /**
1202      * Test doesn't need to be serialized.
1203      *
1204      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1205      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1206      *      for test does not require serialization
1207      */
1208     @Test
1209     public void testCatchErrorWithCacheWithNoFileName() throws Exception {
1210         final File cacheFile = createTempFile("junit");
1211 
1212         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1213         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1214         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1215 
1216         final String errorMessage = "Java Virtual Machine is broken"
1217             + " or has run out of resources necessary for it to continue operating.";
1218         final Error expectedError = new IOError(new InternalError(errorMessage));
1219 
1220         final File mock = new File("testFile") {
1221             @Serial
1222             private static final long serialVersionUID = 1L;
1223 
1224             /**
1225              * Test is checking catch clause when exception is thrown.
1226              *
1227              * @noinspection ProhibitedExceptionThrown
1228              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1229              *      throw exception as part of test
1230              */
1231             @Override
1232             public String getAbsolutePath() {
1233                 throw expectedError;
1234             }
1235         };
1236 
1237         final Checker checker = new Checker();
1238         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1239         checker.configure(checkerConfig);
1240         final List<File> filesToProcess = new ArrayList<>();
1241         filesToProcess.add(mock);
1242         final Error error =
1243                 getExpectedThrowable(Error.class, () -> {
1244                     checker.process(filesToProcess);
1245                 }, "IOError is expected!");
1246         assertWithMessage("Error cause differs from IOError")
1247             .that(error)
1248             .hasCauseThat()
1249             .isInstanceOf(IOError.class);
1250         assertWithMessage("Error message is not expected")
1251             .that(error)
1252             .hasCauseThat()
1253             .hasCauseThat()
1254             .hasMessageThat()
1255             .isEqualTo(errorMessage);
1256 
1257         // destroy is called by Main
1258         checker.destroy();
1259 
1260         final Properties cache = new Properties();
1261         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1262             cache.load(reader);
1263         }
1264 
1265         assertWithMessage("Cache has unexpected size")
1266             .that(cache)
1267             .hasSize(1);
1268     }
1269 
1270     /**
1271      * Test doesn't need to be serialized.
1272      *
1273      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1274      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1275      *      for test does not require serialization
1276      */
1277     @Test
1278     public void testExceptionWithNoFileName() {
1279         final String errorMessage = "Security Exception";
1280         final RuntimeException expectedError = new SecurityException(errorMessage);
1281 
1282         final File mock = new File("testFile") {
1283             @Serial
1284             private static final long serialVersionUID = 1L;
1285 
1286             /**
1287              * Test is checking catch clause when exception is thrown.
1288              *
1289              * @noinspection ProhibitedExceptionThrown
1290              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1291              *      throw exception as part of test
1292              */
1293             @Override
1294             public String getAbsolutePath() {
1295                 throw expectedError;
1296             }
1297         };
1298 
1299         final Checker checker = new Checker();
1300         final List<File> filesToProcess = new ArrayList<>();
1301         filesToProcess.add(mock);
1302         final CheckstyleException exc =
1303                 getExpectedThrowable(CheckstyleException.class, () -> {
1304                     checker.process(filesToProcess);
1305                 }, "SecurityException is expected!");
1306         assertWithMessage("Error cause differs from SecurityException")
1307             .that(exc)
1308             .hasCauseThat()
1309             .isInstanceOf(SecurityException.class);
1310         assertWithMessage("Error message is not expected")
1311             .that(exc)
1312             .hasCauseThat()
1313             .hasMessageThat()
1314             .isEqualTo(errorMessage);
1315     }
1316 
1317     /**
1318      * Test doesn't need to be serialized.
1319      *
1320      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1321      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1322      *      for test does not require serialization
1323      */
1324     @Test
1325     public void testExceptionWithCacheAndNoFileName() throws Exception {
1326         final File cacheFile = createTempFile("junit");
1327 
1328         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1329         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1330         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1331 
1332         final String errorMessage = "Security Exception";
1333         final RuntimeException expectedError = new SecurityException(errorMessage);
1334 
1335         final File mock = new File("testFile") {
1336             @Serial
1337             private static final long serialVersionUID = 1L;
1338 
1339             /**
1340              * Test is checking catch clause when exception is thrown.
1341              *
1342              * @noinspection ProhibitedExceptionThrown
1343              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1344              *      throw exception as part of test
1345              */
1346             @Override
1347             public String getAbsolutePath() {
1348                 throw expectedError;
1349             }
1350         };
1351 
1352         final Checker checker = new Checker();
1353         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1354         checker.configure(checkerConfig);
1355         final List<File> filesToProcess = new ArrayList<>();
1356         filesToProcess.add(mock);
1357         final CheckstyleException exc =
1358                 getExpectedThrowable(CheckstyleException.class, () -> {
1359                     checker.process(filesToProcess);
1360                 }, "SecurityException is expected!");
1361         assertWithMessage("Error cause differs from SecurityException")
1362             .that(exc)
1363             .hasCauseThat()
1364             .isInstanceOf(SecurityException.class);
1365         assertWithMessage("Error message is not expected")
1366             .that(exc)
1367             .hasCauseThat()
1368             .hasMessageThat()
1369             .isEqualTo(errorMessage);
1370 
1371         // destroy is called by Main
1372         checker.destroy();
1373 
1374         final Properties cache = new Properties();
1375         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1376             cache.load(reader);
1377         }
1378 
1379         assertWithMessage("Cache has unexpected size")
1380             .that(cache)
1381             .hasSize(1);
1382     }
1383 
1384     @Test
1385     public void testHaltOnExceptionOff() throws Exception {
1386         final String filePath = getPath("InputChecker.java");
1387         final String[] expected = {
1388             "1: " + getCheckMessage(EXCEPTION_MSG, "java.lang.IndexOutOfBoundsException: test"),
1389         };
1390 
1391         verifyWithInlineXmlConfig(filePath, expected);
1392     }
1393 
1394     @Test
1395     public void testTabViolationDefault() throws Exception {
1396         final String[] expected = {
1397             "17:17: violation",
1398             "21:49: violation",
1399         };
1400         verifyWithInlineConfigParser(getPath("InputCheckerTabCharacter.java"),
1401             expected);
1402     }
1403 
1404     @Test
1405     public void testTabViolationCustomWidth() throws Exception {
1406         final String[] expected = {
1407             "18:17: violation",
1408             "22:37: violation",
1409         };
1410 
1411         verifyWithInlineXmlConfig(getPath("InputCheckerTabCharacterCustomWidth.java"), expected);
1412     }
1413 
1414     @Test
1415     public void testCheckerProcessCallAllNeededMethodsOfFileSets() throws Exception {
1416         final DummyFileSet fileSet = new DummyFileSet();
1417         final Checker checker = new Checker();
1418         checker.addFileSetCheck(fileSet);
1419         checker.process(Collections.singletonList(new File("dummy.java")));
1420         final List<String> expected =
1421             Arrays.asList("beginProcessing", "finishProcessing", "destroy");
1422         assertWithMessage("Method calls were not expected")
1423             .that(fileSet.getMethodCalls())
1424             .isEqualTo(expected);
1425     }
1426 
1427     @Test
1428     public void testSetFileSetCheckSetsMessageDispatcher() {
1429         final DummyFileSet fileSet = new DummyFileSet();
1430         final Checker checker = new Checker();
1431         checker.addFileSetCheck(fileSet);
1432         assertWithMessage("Message dispatcher was not expected")
1433             .that(fileSet.getInternalMessageDispatcher())
1434             .isEqualTo(checker);
1435     }
1436 
1437     @Test
1438     public void testAddAuditListenerAsChild() throws Exception {
1439         final Checker checker = new Checker();
1440         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
1441         final PackageObjectFactory factory = new PackageObjectFactory(
1442                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1443             @Override
1444             public Object createModule(String name) throws CheckstyleException {
1445                 Object adapter = auditAdapter;
1446                 if (!name.equals(DebugAuditAdapter.class.getName())) {
1447                     adapter = super.createModule(name);
1448                 }
1449                 return adapter;
1450             }
1451         };
1452         checker.setModuleFactory(factory);
1453         checker.setupChild(createModuleConfig(DebugAuditAdapter.class));
1454         // Let's try fire some events
1455         checker.process(Collections.singletonList(new File("dummy.java")));
1456         assertWithMessage("Checker.fireAuditStarted() doesn't call listener")
1457                 .that(auditAdapter.wasCalled())
1458                 .isTrue();
1459     }
1460 
1461     @Test
1462     public void testAddBeforeExecutionFileFilterAsChild() throws Exception {
1463         final Checker checker = new Checker();
1464         final TestBeforeExecutionFileFilter fileFilter = new TestBeforeExecutionFileFilter();
1465         final PackageObjectFactory factory = new PackageObjectFactory(
1466                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1467             @Override
1468             public Object createModule(String name) throws CheckstyleException {
1469                 Object filter = fileFilter;
1470                 if (!name.equals(TestBeforeExecutionFileFilter.class.getName())) {
1471                     filter = super.createModule(name);
1472                 }
1473                 return filter;
1474             }
1475         };
1476         checker.setModuleFactory(factory);
1477         checker.setupChild(createModuleConfig(TestBeforeExecutionFileFilter.class));
1478         checker.process(Collections.singletonList(new File("dummy.java")));
1479         assertWithMessage("Checker.acceptFileStarted() doesn't call listener")
1480                 .that(fileFilter.wasCalled())
1481                 .isTrue();
1482     }
1483 
1484     @Test
1485     public void testFileSetCheckInitWhenAddedAsChild() throws Exception {
1486         final Checker checker = new Checker();
1487         final DummyFileSet fileSet = new DummyFileSet();
1488         final PackageObjectFactory factory = new PackageObjectFactory(
1489                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1490             @Override
1491             public Object createModule(String name) throws CheckstyleException {
1492                 Object check = fileSet;
1493                 if (!name.equals(DummyFileSet.class.getName())) {
1494                     check = super.createModule(name);
1495                 }
1496                 return check;
1497             }
1498         };
1499         checker.setModuleFactory(factory);
1500         checker.finishLocalSetup();
1501         checker.setupChild(createModuleConfig(DummyFileSet.class));
1502         assertWithMessage("FileSetCheck.init() wasn't called")
1503                 .that(fileSet.isInitCalled())
1504                 .isTrue();
1505     }
1506 
1507     // -@cs[CheckstyleTestMakeup] must use raw class to directly initialize DefaultLogger
1508     @Test
1509     public void testDefaultLoggerClosesItStreams() throws Exception {
1510         final Checker checker = new Checker();
1511         try (CloseAndFlushTestByteArrayOutputStream testInfoOutputStream =
1512                 new CloseAndFlushTestByteArrayOutputStream();
1513             CloseAndFlushTestByteArrayOutputStream testErrorOutputStream =
1514                 new CloseAndFlushTestByteArrayOutputStream()) {
1515             checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1516             checker.addListener(new DefaultLogger(testInfoOutputStream,
1517                 OutputStreamOptions.CLOSE, testErrorOutputStream, OutputStreamOptions.CLOSE));
1518 
1519             final File tmpFile = createTempFile("file", ".java");
1520 
1521             execute(checker, tmpFile.getPath());
1522 
1523             assertWithMessage("Output stream close count")
1524                     .that(testInfoOutputStream.getCloseCount())
1525                     .isEqualTo(1);
1526             assertWithMessage("Output stream flush count")
1527                     .that(testInfoOutputStream.getFlushCount())
1528                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(3));
1529             assertWithMessage("Error stream close count")
1530                     .that(testErrorOutputStream.getCloseCount())
1531                     .isEqualTo(1);
1532             assertWithMessage("Error stream flush count")
1533                     .that(testErrorOutputStream.getFlushCount())
1534                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(1));
1535         }
1536     }
1537 
1538     // -@cs[CheckstyleTestMakeup] must use raw class to directly initialize DefaultLogger
1539     @Test
1540     public void testXmlLoggerClosesItStreams() throws Exception {
1541         final Checker checker = new Checker();
1542         try (CloseAndFlushTestByteArrayOutputStream testInfoOutputStream =
1543                 new CloseAndFlushTestByteArrayOutputStream()) {
1544             checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1545             checker.addListener(new XMLLogger(testInfoOutputStream, OutputStreamOptions.CLOSE));
1546 
1547             final File tmpFile = createTempFile("file", ".java");
1548 
1549             execute(checker, tmpFile.getPath(), tmpFile.getPath());
1550 
1551             assertWithMessage("Output stream close count")
1552                     .that(testInfoOutputStream.getCloseCount())
1553                     .isEqualTo(1);
1554             assertWithMessage("Output stream flush count")
1555                     .that(testInfoOutputStream.getFlushCount())
1556                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(0));
1557         }
1558     }
1559 
1560     @Test
1561     public void testDuplicatedModule() throws Exception {
1562         // we need to test a module with two instances, one with id and the other not
1563         final DefaultConfiguration moduleConfig1 =
1564                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1565         final DefaultConfiguration moduleConfig2 =
1566                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1567         moduleConfig2.addProperty("id", "ModuleId");
1568         final DefaultConfiguration root = new DefaultConfiguration("root");
1569         root.addChild(moduleConfig1);
1570         root.addChild(moduleConfig2);
1571         final Checker checker = new Checker();
1572         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1573         checker.configure(root);
1574         // BriefUtLogger does not print the module name or id postfix,
1575         // so we need to set logger manually
1576         final ByteArrayOutputStream out = TestUtil.getInternalState(this,
1577                 "stream", ByteArrayOutputStream.class);
1578         final DefaultLogger logger = new DefaultLogger(out, OutputStreamOptions.CLOSE, out,
1579                 OutputStreamOptions.NONE, new AuditEventDefaultFormatter());
1580         checker.addListener(logger);
1581 
1582         final File tempFile = createTempFile("file", ".java");
1583         try (BufferedWriter bufferedWriter = Files.newBufferedWriter(tempFile.toPath())) {
1584             bufferedWriter.write(';');
1585         }
1586         final String path = tempFile.getPath();
1587         final String violationMessage =
1588                 getCheckMessage(NewlineAtEndOfFileCheck.class, MSG_KEY_NO_NEWLINE_EOF);
1589         final String[] expected = {
1590             "1: " + violationMessage + " [NewlineAtEndOfFile]",
1591             "1: " + violationMessage + " [ModuleId]",
1592         };
1593 
1594         // super.verify does not work here, for we change the logger
1595         out.flush();
1596         final int errs = checker.process(Collections.singletonList(new File(path)));
1597         try (ByteArrayInputStream inputStream =
1598                 new ByteArrayInputStream(out.toByteArray());
1599             LineNumberReader lnr = new LineNumberReader(
1600                 new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
1601             // we need to ignore the unrelated lines
1602             final List<String> actual = lnr.lines()
1603                     .filter(line -> !getCheckMessage(AUDIT_STARTED_MESSAGE).equals(line))
1604                     .filter(line -> !getCheckMessage(AUDIT_FINISHED_MESSAGE).equals(line))
1605                     .limit(expected.length)
1606                     .sorted()
1607                     .toList();
1608             Arrays.sort(expected);
1609 
1610             for (int i = 0; i < expected.length; i++) {
1611                 final String expectedResult = "[ERROR] " + path + ":" + expected[i];
1612                 assertWithMessage("error message %s", i)
1613                         .that(actual.get(i))
1614                         .isEqualTo(expectedResult);
1615             }
1616 
1617             assertWithMessage("unexpected output: %s", lnr.readLine())
1618                     .that(errs)
1619                     .isEqualTo(expected.length);
1620         }
1621 
1622         checker.destroy();
1623     }
1624 
1625     @Test
1626     public void testCachedFile() throws Exception {
1627         final Checker checker = createChecker(createModuleConfig(TranslationCheck.class));
1628         final OutputStream infoStream = new ByteArrayOutputStream();
1629         final OutputStream errorStream = new ByteArrayOutputStream();
1630         final DefaultLoggerWithCounter loggerWithCounter =
1631             new DefaultLoggerWithCounter(infoStream, OutputStreamOptions.CLOSE,
1632                                          errorStream, OutputStreamOptions.CLOSE);
1633         checker.addListener(loggerWithCounter);
1634         final File cacheFile = createTempFile("cacheFile", ".txt");
1635         checker.setCacheFile(cacheFile.getAbsolutePath());
1636 
1637         final File testFile = createTempFile("testFile", ".java");
1638         final List<File> files = List.of(testFile, testFile);
1639         checker.process(files);
1640 
1641         assertWithMessage("Cached file should not be processed twice")
1642             .that(loggerWithCounter.fileStartedCount)
1643             .isEqualTo(1);
1644 
1645         checker.destroy();
1646     }
1647 
1648     @Test
1649     public void testUnmappableCharacters() throws Exception {
1650         final String[] expected = {
1651             "14: " + getCheckMessage(LineLengthCheck.class, MSG_KEY, 80, 225),
1652         };
1653 
1654         verifyWithInlineXmlConfig(getPath("InputCheckerTestCharset.java"),
1655                 expected);
1656     }
1657 
1658     /**
1659      * This tests uses 'verify' method, because it needs some config
1660      * to be executed on non-existing Input file,
1661      * but BDD style methods need config in existing file.
1662      *
1663      * @throws Exception exception
1664      */
1665     @SuppressForbidden
1666     @Test
1667     public void testViolationMessageOnIoException() throws Exception {
1668         final DefaultConfiguration checkConfig =
1669                 createModuleConfig(CheckWhichThrowsError.class);
1670 
1671         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
1672         treeWalkerConfig.addChild(checkConfig);
1673 
1674         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
1675         checkerConfig.addChild(treeWalkerConfig);
1676 
1677         checkerConfig.addProperty("haltOnException", "false");
1678         final File file = new File("InputNonChecker.java");
1679         final String filePath = file.getAbsolutePath();
1680         final String[] expected = {
1681             "1: " + getCheckMessage(EXCEPTION_MSG, filePath
1682                         + " (No such file or directory)"),
1683         };
1684 
1685         verify(checkerConfig, filePath, expected);
1686     }
1687 
1688     /**
1689      * Reason of non-Input based testing:
1690      * There are bunch of asserts that expects full path to file,
1691      * usage of "basedir" make it stripped and we need put everywhere code like
1692      * <pre>CommonUtil.relativizePath(checker.getConfiguration().getProperty("basedir"), file)</pre>
1693      * but Checker object is not always available in code.
1694      * Propagating it in all code methods will complicate code.
1695      */
1696     @Test
1697     public void testRelativizedFileExclusion() throws Exception {
1698         final DefaultConfiguration newLineAtEndOfFileConfig =
1699                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1700 
1701         final DefaultConfiguration beforeExecutionExclusionFileFilterConfig =
1702                 createModuleConfig(BeforeExecutionExclusionFileFilter.class);
1703 
1704         beforeExecutionExclusionFileFilterConfig.addProperty("fileNamePattern",
1705                         "^(?!InputCheckerTestExcludeRelativizedFile.*\\.java).*");
1706 
1707         final DefaultConfiguration checkerConfig = createRootConfig(null);
1708         checkerConfig.addChild(newLineAtEndOfFileConfig);
1709         checkerConfig.addChild(beforeExecutionExclusionFileFilterConfig);
1710 
1711         // -@cs[CheckstyleTestMakeup] Needs to be fixed.
1712         checkerConfig.addProperty("basedir",
1713                 temporaryFolder.getPath());
1714 
1715         final String violationMessage =
1716                 getCheckMessage(NewlineAtEndOfFileCheck.class, MSG_KEY_NO_NEWLINE_EOF);
1717 
1718         final String[] expected = {
1719             "1: " + violationMessage,
1720         };
1721 
1722         final File tempFile = createTempFile("InputCheckerTestExcludeRelativizedFile", ".java");
1723         try (BufferedWriter bufferedWriter = Files.newBufferedWriter(tempFile.toPath())) {
1724             bufferedWriter.write(';');
1725         }
1726 
1727         final File[] processedFiles = {tempFile};
1728 
1729         verify(createChecker(checkerConfig), processedFiles,
1730                 tempFile.getName(), expected);
1731     }
1732 
1733     @Test
1734     public void testBasedirPathMismatchExceptionMessage() throws IOException {
1735         final CheckstyleAntTask antTask = new CheckstyleAntTask();
1736         antTask.setProject(new Project());
1737         antTask.setConfig(getPath("InputCheckerTestConfigBasedirMismatch.xml"));
1738 
1739         final File testFile = Files.createFile(
1740             temporaryFolder.toPath().resolve("TestBasedirMismatch.java"))
1741             .toFile();
1742 
1743         antTask.setFile(testFile);
1744 
1745         final BuildException ex = getExpectedThrowable(
1746             BuildException.class,
1747             antTask::execute,
1748             "BuildException is expected");
1749 
1750         final IllegalStateException relativizeException = findRelativizePathException(ex);
1751         final String message = relativizeException.getMessage();
1752 
1753         assertWithMessage("Exception message should mention failed relativization")
1754             .that(message)
1755             .contains("Failed to relativize path");
1756 
1757         assertWithMessage("Exception message should mention base directory")
1758             .that(message)
1759             .contains("base directory");
1760 
1761         assertWithMessage("Exception message should contain the file path")
1762             .that(message)
1763             .contains(testFile.getAbsolutePath());
1764     }
1765 
1766     private static IllegalStateException findRelativizePathException(Throwable throwable) {
1767         Throwable current = throwable;
1768         while (current != null) {
1769             if (current instanceof IllegalStateException exception
1770                     && current.getCause() instanceof IllegalArgumentException) {
1771                 return exception;
1772             }
1773             current = current.getCause();
1774         }
1775         throw new AssertionError(
1776             "IllegalStateException from CommonUtil.relativizePath not found in cause chain");
1777     }
1778 
1779     public static class DefaultLoggerWithCounter extends DefaultLogger {
1780 
1781         private int fileStartedCount;
1782 
1783         public DefaultLoggerWithCounter(OutputStream infoStream,
1784                                         OutputStreamOptions infoStreamOptions,
1785                                         OutputStream errorStream,
1786                                         OutputStreamOptions errorStreamOptions) {
1787             super(infoStream, infoStreamOptions, errorStream, errorStreamOptions);
1788         }
1789 
1790         @Override
1791         public void fileStarted(AuditEvent event) {
1792             fileStartedCount++;
1793         }
1794     }
1795 
1796     public static class DummyFilter implements Filter {
1797 
1798         @Override
1799         public boolean accept(AuditEvent event) {
1800             return false;
1801         }
1802 
1803     }
1804 
1805     public static class DummyFileSetViolationCheck extends AbstractFileSetCheck
1806         implements ExternalResourceHolder {
1807 
1808         @Override
1809         protected void processFiltered(File file, FileText fileText) {
1810             log(1, "test");
1811         }
1812 
1813         @Override
1814         public Set<String> getExternalResourceLocations() {
1815             final Set<String> externalResourceLocation = new HashSet<>(1);
1816             externalResourceLocation.add("non_existent_external_resource.xml");
1817             return externalResourceLocation;
1818         }
1819 
1820     }
1821 
1822     public static class DummyFilterSet extends FilterSet implements ExternalResourceHolder {
1823 
1824         @Override
1825         public Set<String> getExternalResourceLocations() {
1826             final Set<String> strings = new HashSet<>();
1827             strings.add("test");
1828             return strings;
1829         }
1830 
1831     }
1832 
1833     public static final class DynamicalResourceHolderCheck extends AbstractFileSetCheck
1834         implements ExternalResourceHolder {
1835 
1836         private String firstExternalResourceLocation;
1837         private String secondExternalResourceLocation;
1838 
1839         public void setFirstExternalResourceLocation(String firstExternalResourceLocation) {
1840             this.firstExternalResourceLocation = firstExternalResourceLocation;
1841         }
1842 
1843         public void setSecondExternalResourceLocation(String secondExternalResourceLocation) {
1844             this.secondExternalResourceLocation = secondExternalResourceLocation;
1845         }
1846 
1847         @Override
1848         protected void processFiltered(File file, FileText fileText) {
1849             // there is no need in implementation of the method
1850         }
1851 
1852         @Override
1853         public Set<String> getExternalResourceLocations() {
1854             final Set<String> locations = new HashSet<>();
1855             locations.add(firstExternalResourceLocation);
1856             // Attempt to change the behaviour of the check dynamically
1857             if (secondExternalResourceLocation != null) {
1858                 locations.add(secondExternalResourceLocation);
1859             }
1860             return locations;
1861         }
1862 
1863     }
1864 
1865     public static class CheckWhichDoesNotRequireCommentNodes extends AbstractCheck {
1866 
1867         /** Number of children of method definition token. */
1868         private static final int METHOD_DEF_CHILD_COUNT = 7;
1869 
1870         @Override
1871         public int[] getDefaultTokens() {
1872             return new int[] {TokenTypes.METHOD_DEF};
1873         }
1874 
1875         @Override
1876         public int[] getAcceptableTokens() {
1877             return new int[] {TokenTypes.METHOD_DEF};
1878         }
1879 
1880         @Override
1881         public int[] getRequiredTokens() {
1882             return new int[] {TokenTypes.METHOD_DEF};
1883         }
1884 
1885         @Override
1886         public void visitToken(DetailAST ast) {
1887             if (ast.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(
1888                     TokenTypes.BLOCK_COMMENT_BEGIN) != null) {
1889                 log(ast, "AST has incorrect structure structure."
1890                     + " The check does not require comment nodes but there were comment nodes"
1891                     + " in the AST.");
1892             }
1893             final int childCount = ast.getChildCount();
1894             if (childCount != METHOD_DEF_CHILD_COUNT) {
1895                 final String msg = String.format(Locale.ENGLISH,
1896                     "AST node in no comment tree has wrong number of children. "
1897                             + "Expected is %d but was %d",
1898                     METHOD_DEF_CHILD_COUNT, childCount);
1899                 log(ast, msg);
1900             }
1901             // count children where comment lives
1902             int actualChildCount = 0;
1903             for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child =
1904                     child.getNextSibling()) {
1905                 actualChildCount++;
1906             }
1907             final int cacheChildCount = ast.getFirstChild().getChildCount();
1908             if (cacheChildCount != actualChildCount) {
1909                 final String msg = String.format(Locale.ENGLISH,
1910                         "AST node with no comment has wrong number of children. "
1911                                 + "Expected is %d but was %d",
1912                         cacheChildCount, actualChildCount);
1913                 log(ast, msg);
1914             }
1915         }
1916 
1917     }
1918 
1919     public static class CheckWhichRequiresCommentNodes extends AbstractCheck {
1920 
1921         /** Number of children of method definition token. */
1922         private static final int METHOD_DEF_CHILD_COUNT = 7;
1923 
1924         @Override
1925         public boolean isCommentNodesRequired() {
1926             return true;
1927         }
1928 
1929         @Override
1930         public int[] getDefaultTokens() {
1931             return new int[] {TokenTypes.METHOD_DEF};
1932         }
1933 
1934         @Override
1935         public int[] getAcceptableTokens() {
1936             return new int[] {TokenTypes.METHOD_DEF};
1937         }
1938 
1939         @Override
1940         public int[] getRequiredTokens() {
1941             return new int[] {TokenTypes.METHOD_DEF};
1942         }
1943 
1944         // Locale.ENGLISH until #12104
1945         @Override
1946         public void visitToken(DetailAST ast) {
1947             if (ast.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(
1948                     TokenTypes.BLOCK_COMMENT_BEGIN) == null) {
1949                 log(ast, "Incorrect AST structure.");
1950             }
1951             final int childCount = ast.getChildCount();
1952             if (childCount != METHOD_DEF_CHILD_COUNT) {
1953                 final String msg = String.format(Locale.ENGLISH,
1954                     "AST node in comment tree has wrong number of children. "
1955                             + "Expected is %d but was %d",
1956                     METHOD_DEF_CHILD_COUNT, childCount);
1957                 log(ast, msg);
1958             }
1959             // count children where comment lives
1960             int actualChildCount = 0;
1961             for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child =
1962                     child.getNextSibling()) {
1963                 actualChildCount++;
1964             }
1965             final int cacheChildCount = ast.getFirstChild().getChildCount();
1966             if (cacheChildCount != actualChildCount) {
1967                 final String msg = String.format(Locale.ENGLISH,
1968                         "AST node with comment has wrong number of children. "
1969                                 + "Expected is %d but was %d",
1970                         cacheChildCount, actualChildCount);
1971                 log(ast, msg);
1972             }
1973         }
1974 
1975     }
1976 
1977     public static final class DummyFileSet extends AbstractFileSetCheck {
1978 
1979         private final List<String> methodCalls = new ArrayList<>();
1980 
1981         private boolean initCalled;
1982 
1983         @Override
1984         public void init() {
1985             super.init();
1986             initCalled = true;
1987         }
1988 
1989         @Override
1990         public void beginProcessing(String charset) {
1991             methodCalls.add("beginProcessing");
1992             super.beginProcessing(charset);
1993         }
1994 
1995         @Override
1996         public void finishProcessing() {
1997             methodCalls.add("finishProcessing");
1998             super.finishProcessing();
1999         }
2000 
2001         @Override
2002         protected void processFiltered(File file, FileText fileText) {
2003             methodCalls.add("processFiltered");
2004         }
2005 
2006         @Override
2007         public void destroy() {
2008             methodCalls.add("destroy");
2009             super.destroy();
2010         }
2011 
2012         public List<String> getMethodCalls() {
2013             return Collections.unmodifiableList(methodCalls);
2014         }
2015 
2016         public boolean isInitCalled() {
2017             return initCalled;
2018         }
2019 
2020         public MessageDispatcher getInternalMessageDispatcher() {
2021             return getMessageDispatcher();
2022         }
2023 
2024     }
2025 
2026 }