View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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     protected 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         try {
454             checker.setCharset("UNKNOWN-CHARSET");
455             assertWithMessage("Exception is expected").fail();
456         }
457         catch (UnsupportedEncodingException exc) {
458             assertWithMessage("Error message is not expected")
459                 .that(exc.getMessage())
460                 .isEqualTo("unsupported charset: 'UNKNOWN-CHARSET'");
461         }
462     }
463 
464     @Test
465     public void testSetSeverity() throws Exception {
466         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
467 
468         verifyWithInlineXmlConfig(getPath("InputCheckerTestSeverity.java"), expected);
469     }
470 
471     @Test
472     public void testNoClassLoaderNoModuleFactory() {
473         final Checker checker = new Checker();
474 
475         try {
476             checker.finishLocalSetup();
477             assertWithMessage("Exception is expected").fail();
478         }
479         catch (CheckstyleException exc) {
480             assertWithMessage("Error message is not expected")
481                 .that(exc.getMessage())
482                 .isEqualTo("if no custom moduleFactory is set,"
483                     + " moduleClassLoader must be specified");
484         }
485     }
486 
487     @Test
488     public void testNoModuleFactory() throws Exception {
489         final Checker checker = new Checker();
490         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
491 
492         checker.setModuleClassLoader(classLoader);
493         checker.finishLocalSetup();
494         final Context actualCtx = TestUtil.getInternalState(checker, "childContext", Context.class);
495 
496         assertWithMessage("Default module factory should be created when it is not specified")
497             .that(actualCtx.get("moduleFactory"))
498             .isNotNull();
499     }
500 
501     @Test
502     public void testFinishLocalSetupFullyInitialized() throws Exception {
503         final Checker checker = new Checker();
504         final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
505         checker.setModuleClassLoader(contextClassLoader);
506         final PackageObjectFactory factory = new PackageObjectFactory(
507             new HashSet<>(), contextClassLoader);
508         checker.setModuleFactory(factory);
509         checker.setBasedir("testBaseDir");
510         checker.setLocaleLanguage("it");
511         checker.setLocaleCountry("IT");
512         checker.finishLocalSetup();
513 
514         final Context context = TestUtil.getInternalState(checker, "childContext", Context.class);
515         final String encoding = StandardCharsets.UTF_8.name();
516         assertWithMessage("Charset was different than expected")
517             .that(context.get("charset"))
518             .isEqualTo(encoding);
519         assertWithMessage("Severity is set to unexpected value")
520             .that(context.get("severity"))
521             .isEqualTo("error");
522         assertWithMessage("Basedir is set to unexpected value")
523             .that(context.get("basedir"))
524             .isEqualTo("testBaseDir");
525 
526         final Locale locale =
527                 TestUtil.getInternalStaticState(LocalizedMessage.class, "sLocale", Locale.class);
528         assertWithMessage("Locale is set to unexpected value")
529             .that(locale)
530             .isEqualTo(Locale.ITALY);
531     }
532 
533     @Test
534     public void testSetupChildExceptions() {
535         final Checker checker = new Checker();
536         final PackageObjectFactory factory = new PackageObjectFactory(
537             new HashSet<>(), Thread.currentThread().getContextClassLoader());
538         checker.setModuleFactory(factory);
539 
540         final Configuration config = new DefaultConfiguration("java.lang.String");
541         try {
542             checker.setupChild(config);
543             assertWithMessage("Exception is expected").fail();
544         }
545         catch (CheckstyleException exc) {
546             assertWithMessage("Error message is not expected")
547                 .that(exc.getMessage())
548                 .isEqualTo("java.lang.String is not allowed as a child in Checker");
549         }
550     }
551 
552     @Test
553     public void testSetupChildInvalidProperty() throws Exception {
554         final DefaultConfiguration checkConfig = createModuleConfig(HiddenFieldCheck.class);
555         checkConfig.addProperty("$$No such property", null);
556         try {
557             createChecker(checkConfig);
558             assertWithMessage("Exception is expected").fail();
559         }
560         catch (CheckstyleException exc) {
561             assertWithMessage("Error message is not expected")
562                 .that(exc.getMessage())
563                 .isEqualTo("cannot initialize module com.puppycrawl.tools.checkstyle.TreeWalker"
564                         + " - cannot initialize module " + checkConfig.getName()
565                         + " - Property '$$No such property'"
566                         + " does not exist, please check the documentation");
567         }
568     }
569 
570     @Test
571     public void testSetupChildListener() throws Exception {
572         final Checker checker = new Checker();
573         final PackageObjectFactory factory = new PackageObjectFactory(
574             new HashSet<>(), Thread.currentThread().getContextClassLoader());
575         checker.setModuleFactory(factory);
576 
577         final Configuration config = new DefaultConfiguration(
578             DebugAuditAdapter.class.getCanonicalName());
579         checker.setupChild(config);
580 
581         final List<AuditListener> listeners = TestUtil.getInternalStateListAuditListener(checker,
582                 "listeners");
583         assertWithMessage("Invalid child listener class")
584                 .that(listeners.get(listeners.size() - 1) instanceof DebugAuditAdapter)
585                 .isTrue();
586     }
587 
588     @Test
589     public void testDestroyCheckerWithWrongCacheFileNameLength() throws Exception {
590         final Checker checker = new Checker();
591         final PackageObjectFactory factory = new PackageObjectFactory(
592             new HashSet<>(), Thread.currentThread().getContextClassLoader());
593         checker.setModuleFactory(factory);
594         checker.configure(new DefaultConfiguration("default config"));
595         // We set wrong file name length in order to reproduce IOException on OS Linux, OS Windows.
596         // The maximum file name length which is allowed in most UNIX, Windows file systems is 255.
597         // See https://en.wikipedia.org/wiki/Filename;
598         checker.setCacheFile(String.format(Locale.ENGLISH, "%0300d", 0));
599         try {
600             checker.destroy();
601             assertWithMessage("Exception did not happen").fail();
602         }
603         catch (IllegalStateException exc) {
604             assertWithMessage("Cause of exception differs from IOException")
605                     .that(exc.getCause())
606                     .isInstanceOf(IOException.class);
607 
608             assertWithMessage("Exception message differ")
609                     .that(exc.getMessage())
610                     .isEqualTo(getLocalizedMessage(
611                             "Checker.cacheFilesException"));
612         }
613     }
614 
615     /**
616      * It is OK to have long test method name here as it describes the test purpose.
617      */
618     @Test
619     public void testCacheAndCheckWhichDoesNotImplementExternalResourceHolderInterface()
620             throws Exception {
621         assertWithMessage("ExternalResourceHolder has changed his parent")
622                 .that(ExternalResourceHolder.class.isAssignableFrom(HiddenFieldCheck.class))
623                 .isFalse();
624         final DefaultConfiguration checkConfig = createModuleConfig(HiddenFieldCheck.class);
625 
626         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
627         treeWalkerConfig.addChild(checkConfig);
628 
629         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
630         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
631 
632         final File cacheFile = createTempFile("junit");
633         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
634 
635         final File tmpFile = createTempFile("file", ".java");
636 
637         execute(checkerConfig, tmpFile.getPath());
638         final Properties cacheAfterFirstRun = new Properties();
639         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
640             cacheAfterFirstRun.load(reader);
641         }
642 
643         // one more time to reuse cache
644         execute(checkerConfig, tmpFile.getPath());
645         final Properties cacheAfterSecondRun = new Properties();
646         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
647             cacheAfterSecondRun.load(reader);
648         }
649 
650         assertWithMessage("Cache from first run differs from second run cache")
651             .that(cacheAfterSecondRun)
652             .isEqualTo(cacheAfterFirstRun);
653     }
654 
655     @Test
656     public void testWithCacheWithNoViolation() throws Exception {
657         final Checker checker = new Checker();
658         final PackageObjectFactory factory = new PackageObjectFactory(
659             new HashSet<>(), Thread.currentThread().getContextClassLoader());
660         checker.setModuleFactory(factory);
661         checker.configure(createModuleConfig(TranslationCheck.class));
662 
663         final File cacheFile = createTempFile("junit");
664         checker.setCacheFile(cacheFile.getPath());
665 
666         checker.setupChild(createModuleConfig(TranslationCheck.class));
667         final File tmpFile = createTempFile("file", ".java");
668         final List<File> files = new ArrayList<>(1);
669         files.add(tmpFile);
670         checker.process(files);
671 
672         // invoke destroy to persist cache
673         checker.destroy();
674 
675         final Properties cache = new Properties();
676         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
677             cache.load(reader);
678         }
679 
680         // There should 2 objects in cache: processed file (file.java) and checker configuration.
681         final int expectedNumberOfObjectsInCache = 2;
682         assertWithMessage("Cache has unexpected size")
683             .that(cache)
684             .hasSize(expectedNumberOfObjectsInCache);
685 
686         final String expectedConfigHash = "D581D4A2BD482D4E1EF1F82459356BA2D8A3B" + "FC3";
687         assertWithMessage("Cache has unexpected hash")
688             .that(cache.getProperty(PropertyCacheFile.CONFIG_HASH_KEY))
689             .isEqualTo(expectedConfigHash);
690 
691         assertWithMessage("Cache file has null path")
692             .that(cache.getProperty(tmpFile.getPath()))
693             .isNotNull();
694     }
695 
696     @Test
697     public void testClearExistingCache() throws Exception {
698         final DefaultConfiguration checkerConfig = createRootConfig(null);
699         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
700         final File cacheFile = createTempFile("junit");
701         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
702 
703         final Checker checker = new Checker();
704         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
705         checker.configure(checkerConfig);
706         checker.addListener(getBriefUtLogger());
707 
708         checker.clearCache();
709         // invoke destroy to persist cache
710         checker.destroy();
711 
712         final Properties cacheAfterClear = new Properties();
713         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
714             cacheAfterClear.load(reader);
715         }
716 
717         assertWithMessage("Cache has unexpected size")
718             .that(cacheAfterClear)
719             .hasSize(1);
720         assertWithMessage("Cache has null hash")
721             .that(cacheAfterClear.getProperty(PropertyCacheFile.CONFIG_HASH_KEY))
722             .isNotNull();
723 
724         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
725 
726         // file that should be audited is not in cache
727         execute(checkerConfig, pathToEmptyFile);
728         final Properties cacheAfterSecondRun = new Properties();
729         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
730             cacheAfterSecondRun.load(reader);
731         }
732 
733         assertWithMessage("Cache has null path")
734             .that(cacheAfterSecondRun.getProperty(pathToEmptyFile))
735             .isNotNull();
736         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
737         assertWithMessage("Cash have changed it hash")
738             .that(cacheHash)
739             .isEqualTo(cacheAfterClear.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
740         final int expectedNumberOfObjectsInCacheAfterSecondRun = 2;
741         assertWithMessage("Cache has changed number of items")
742             .that(cacheAfterSecondRun)
743             .hasSize(expectedNumberOfObjectsInCacheAfterSecondRun);
744     }
745 
746     @Test
747     public void testClearCache() throws Exception {
748         final DefaultConfiguration violationCheck =
749                 createModuleConfig(DummyFileSetViolationCheck.class);
750         final DefaultConfiguration checkerConfig = new DefaultConfiguration("myConfig");
751         checkerConfig.addProperty("charset", "UTF-8");
752         final File cacheFile = createTempFile("junit");
753         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
754         checkerConfig.addChild(violationCheck);
755         final Checker checker = new Checker();
756         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
757         checker.configure(checkerConfig);
758         checker.addListener(getBriefUtLogger());
759 
760         checker.process(Collections.singletonList(new File("dummy.java")));
761         checker.clearCache();
762         // invoke destroy to persist cache
763         final PropertyCacheFile cache = TestUtil.getInternalState(checker,
764                 "cacheFile", PropertyCacheFile.class);
765         cache.persist();
766 
767         final Properties cacheAfterClear = new Properties();
768         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
769             cacheAfterClear.load(reader);
770         }
771 
772         assertWithMessage("Cache has unexpected size")
773             .that(cacheAfterClear)
774             .hasSize(1);
775     }
776 
777     @Test
778     public void setFileExtension() {
779         final Checker checker = new Checker();
780         checker.setFileExtensions(".test1", "test2");
781         final String[] actual = TestUtil.getInternalState(checker,
782                 "fileExtensions", String[].class);
783         assertWithMessage("Extensions are not expected")
784             .that(actual)
785             .isEqualTo(new String[] {".test1", ".test2"});
786     }
787 
788     @Test
789     public void testClearCacheWhenCacheFileIsNotSet() {
790         // The idea of the test is to check that when cache file is not set,
791         // the invocation of clearCache method does not throw an exception.
792         final Checker checker = new Checker();
793         checker.clearCache();
794         final PropertyCacheFile cache = TestUtil.getInternalState(checker,
795                 "cacheFile", PropertyCacheFile.class);
796         assertWithMessage("If cache file is not set the cache should default to null")
797             .that(cache)
798             .isNull();
799     }
800 
801     /**
802      * Test doesn't need to be serialized.
803      *
804      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
805      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
806      *      for test does not require serialization
807      */
808     @Test
809     public void testCatchErrorInProcessFilesMethod() throws Exception {
810         // Assume that I/O error is happened when we try to invoke 'lastModified()' method.
811         final String errorMessage = "Java Virtual Machine is broken"
812             + " or has run out of resources necessary for it to continue operating.";
813         final Error expectedError = new IOError(new InternalError(errorMessage));
814 
815         final File mock = new File("testFile") {
816             @Serial
817             private static final long serialVersionUID = 1L;
818 
819             /**
820              * Test is checking catch clause when exception is thrown.
821              *
822              * @noinspection ProhibitedExceptionThrown
823              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
824              *      throw exception as part of test
825              */
826             @Override
827             public long lastModified() {
828                 throw expectedError;
829             }
830         };
831 
832         final Checker checker = new Checker();
833         final List<File> filesToProcess = new ArrayList<>();
834         filesToProcess.add(mock);
835         try {
836             checker.process(filesToProcess);
837             assertWithMessage("IOError is expected!").fail();
838         }
839         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
840         catch (Error error) {
841             assertWithMessage("Error cause differs from IOError")
842                     .that(error.getCause())
843                     .isInstanceOf(IOError.class);
844             assertWithMessage("Error cause is not InternalError")
845                     .that(error.getCause().getCause())
846                     .isInstanceOf(InternalError.class);
847             assertWithMessage("Error message is not expected")
848                     .that(error)
849                     .hasCauseThat()
850                     .hasCauseThat()
851                     .hasMessageThat()
852                     .isEqualTo(errorMessage);
853             assertWithMessage("Error message differs")
854                     .that(error.getMessage())
855                     .isEqualTo(getLocalizedMessage(
856                             "Checker.error", mock.getPath()));
857 
858         }
859     }
860 
861     /**
862      * Test doesn't need to be serialized.
863      *
864      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
865      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
866      *      for test does not require serialization
867      */
868     @Test
869     public void testCatchErrorWithNoFileName() throws Exception {
870         // Assume that I/O error is happened when we try to invoke 'lastModified()' method.
871         final String errorMessage = "Java Virtual Machine is broken"
872             + " or has run out of resources necessary for it to continue operating.";
873         final Error expectedError = new IOError(new InternalError(errorMessage));
874 
875         final File mock = new File("testFile") {
876             @Serial
877             private static final long serialVersionUID = 1L;
878             /**
879              * Test is checking catch clause when exception is thrown.
880              *
881              * @noinspection ProhibitedExceptionThrown
882              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
883              *      throw exception as part of test
884              */
885             @Override
886             public long lastModified() {
887                 throw expectedError;
888             }
889 
890             @Override
891             public String getAbsolutePath() {
892                 return null;
893             }
894         };
895 
896         final Checker checker = new Checker();
897         final List<File> filesToProcess = new ArrayList<>();
898         filesToProcess.add(mock);
899         try {
900             checker.process(filesToProcess);
901             assertWithMessage("IOError is expected!").fail();
902         }
903         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
904         catch (Error error) {
905             assertWithMessage("Error cause differs from IOError")
906                     .that(error)
907                     .hasCauseThat()
908                     .isInstanceOf(IOError.class);
909             assertWithMessage("Error cause is not InternalError")
910                     .that(error)
911                     .hasCauseThat()
912                     .hasCauseThat()
913                     .isInstanceOf(InternalError.class);
914             assertWithMessage("Error message is not expected")
915                     .that(error)
916                     .hasCauseThat()
917                     .hasCauseThat()
918                     .hasMessageThat()
919                     .isEqualTo(errorMessage);
920         }
921     }
922 
923     /**
924      * It is OK to have long test method name here as it describes the test purpose.
925      */
926     @Test
927     public void testCacheAndFilterWhichDoesNotImplementExternalResourceHolderInterface()
928             throws Exception {
929         assertWithMessage("ExternalResourceHolder has changed its parent")
930                 .that(ExternalResourceHolder.class.isAssignableFrom(DummyFilter.class))
931                 .isFalse();
932         final DefaultConfiguration filterConfig = createModuleConfig(DummyFilter.class);
933 
934         final DefaultConfiguration checkerConfig = createRootConfig(filterConfig);
935         final File cacheFile = createTempFile("junit");
936         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
937 
938         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
939 
940         execute(checkerConfig, pathToEmptyFile);
941         final Properties cacheAfterFirstRun = new Properties();
942         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
943             cacheAfterFirstRun.load(reader);
944         }
945 
946         // One more time to use cache.
947         execute(checkerConfig, pathToEmptyFile);
948         final Properties cacheAfterSecondRun = new Properties();
949         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
950             cacheAfterSecondRun.load(reader);
951         }
952 
953         final String cacheFilePath = cacheAfterSecondRun.getProperty(pathToEmptyFile);
954         assertWithMessage("Cache file has changed its path")
955             .that(cacheFilePath)
956             .isEqualTo(cacheAfterFirstRun.getProperty(pathToEmptyFile));
957         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
958         assertWithMessage("Cache has changed its hash")
959             .that(cacheHash)
960             .isEqualTo(cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
961         final int expectedNumberOfObjectsInCache = 2;
962         assertWithMessage("Number of items in cache differs from expected")
963             .that(cacheAfterFirstRun)
964             .hasSize(expectedNumberOfObjectsInCache);
965         assertWithMessage("Number of items in cache differs from expected")
966             .that(cacheAfterSecondRun)
967             .hasSize(expectedNumberOfObjectsInCache);
968     }
969 
970     /**
971      * It is OK to have long test method name here as it describes the test purpose.
972      */
973     // -@cs[ExecutableStatementCount] This test needs to verify many things.
974     @Test
975     public void testCacheAndCheckWhichAddsNewResourceLocationButKeepsSameCheckerInstance()
976             throws Exception {
977         // Use case (https://github.com/checkstyle/checkstyle/pull/3092#issuecomment-218162436):
978         // Imagine that cache exists in a file. New version of Checkstyle appear.
979         // New release contains update to a some check to have additional external resource.
980         // User update his configuration and run validation as usually.
981         // Cache should not be reused.
982 
983         final DynamicalResourceHolderCheck check = new DynamicalResourceHolderCheck();
984         final String firstExternalResourceLocation = getPath("InputCheckerImportControlOne.xml");
985         final String firstExternalResourceKey = PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
986                 + firstExternalResourceLocation;
987         check.setFirstExternalResourceLocation(firstExternalResourceLocation);
988 
989         final DefaultConfiguration checkerConfig = createRootConfig(null);
990         final File cacheFile = createTempFile("junit");
991         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
992 
993         final Checker checker = new Checker();
994         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
995         checker.addFileSetCheck(check);
996         checker.addFilter(new DummyFilterSet());
997         checker.configure(checkerConfig);
998         checker.addListener(getBriefUtLogger());
999 
1000         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
1001 
1002         execute(checker, pathToEmptyFile);
1003         final Properties cacheAfterFirstRun = new Properties();
1004         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1005             cacheAfterFirstRun.load(reader);
1006         }
1007 
1008         final int expectedNumberOfObjectsInCacheAfterFirstRun = 4;
1009         assertWithMessage("Number of items in cache differs from expected")
1010             .that(cacheAfterFirstRun)
1011             .hasSize(expectedNumberOfObjectsInCacheAfterFirstRun);
1012 
1013         // Change a list of external resources which are used by the check
1014         final String secondExternalResourceLocation = "InputCheckerImportControlTwo.xml";
1015         final String secondExternalResourceKey = PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
1016                 + secondExternalResourceLocation;
1017         check.setSecondExternalResourceLocation(secondExternalResourceLocation);
1018 
1019         checker.addFileSetCheck(check);
1020         checker.configure(checkerConfig);
1021 
1022         execute(checker, pathToEmptyFile);
1023         final Properties cacheAfterSecondRun = new Properties();
1024         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1025             cacheAfterSecondRun.load(reader);
1026         }
1027 
1028         final String cacheFilePath = cacheAfterSecondRun.getProperty(pathToEmptyFile);
1029         assertWithMessage("Cache file has changed its path")
1030             .that(cacheFilePath)
1031             .isEqualTo(cacheAfterFirstRun.getProperty(pathToEmptyFile));
1032         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
1033         assertWithMessage("Cache has changed its hash")
1034             .that(cacheHash)
1035             .isEqualTo(cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
1036         final String resourceKey = cacheAfterSecondRun.getProperty(firstExternalResourceKey);
1037         assertWithMessage("Cache has changed its resource key")
1038             .that(resourceKey)
1039             .isEqualTo(cacheAfterFirstRun.getProperty(firstExternalResourceKey));
1040         assertWithMessage("Cache has null as a resource key")
1041             .that(cacheAfterFirstRun.getProperty(firstExternalResourceKey))
1042             .isNotNull();
1043         final int expectedNumberOfObjectsInCacheAfterSecondRun = 4;
1044         assertWithMessage("Number of items in cache differs from expected")
1045             .that(cacheAfterSecondRun)
1046             .hasSize(expectedNumberOfObjectsInCacheAfterSecondRun);
1047         assertWithMessage("Cache has not null as a resource key")
1048             .that(cacheAfterFirstRun.getProperty(secondExternalResourceKey))
1049             .isNull();
1050         assertWithMessage("Cache has null as a resource key")
1051             .that(cacheAfterSecondRun.getProperty(secondExternalResourceKey))
1052             .isNotNull();
1053     }
1054 
1055     @Test
1056     public void testClearLazyLoadCacheInDetailAST() throws Exception {
1057 
1058         final String filePath = getPath("InputCheckerClearDetailAstLazyLoadCache.java");
1059 
1060         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
1061         verifyWithInlineConfigParser(filePath, expected);
1062     }
1063 
1064     @Test
1065     public void testCacheOnViolationSuppression() throws Exception {
1066         final File cacheFile = createTempFile("junit");
1067         final DefaultConfiguration violationCheck =
1068                 createModuleConfig(DummyFileSetViolationCheck.class);
1069 
1070         final DefaultConfiguration filterConfig = createModuleConfig(SuppressionFilter.class);
1071         filterConfig.addProperty("file", getPath("InputCheckerSuppressAll.xml"));
1072 
1073         final DefaultConfiguration checkerConfig = createRootConfig(violationCheck);
1074         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1075         checkerConfig.addChild(filterConfig);
1076 
1077         final String fileViolationPath = createTempFile("ViolationFile", ".java").getPath();
1078 
1079         execute(checkerConfig, fileViolationPath);
1080 
1081         try (InputStream input = Files.newInputStream(cacheFile.toPath())) {
1082             final Properties details = new Properties();
1083             details.load(input);
1084 
1085             assertWithMessage("suppressed violation file saved in cache")
1086                 .that(details.getProperty(fileViolationPath))
1087                 .isNotNull();
1088         }
1089     }
1090 
1091     @Test
1092     public void testHaltOnException() throws Exception {
1093         final DefaultConfiguration checkConfig =
1094             createModuleConfig(CheckWhichThrowsError.class);
1095         final String filePath = getPath("InputChecker.java");
1096         try {
1097             execute(checkConfig, filePath);
1098             assertWithMessage("Exception is expected").fail();
1099         }
1100         catch (CheckstyleException exc) {
1101             assertWithMessage("Error message is not expected")
1102                 .that(exc.getMessage())
1103                 .isEqualTo("Exception was thrown while processing " + filePath);
1104         }
1105     }
1106 
1107     @Test
1108     public void testExceptionWithCache() throws Exception {
1109         final File cacheFile = createTempFile("junit");
1110 
1111         final DefaultConfiguration checkConfig =
1112                 createModuleConfig(CheckWhichThrowsError.class);
1113 
1114         final DefaultConfiguration treewalkerConfig =
1115                 createModuleConfig(TreeWalker.class);
1116         treewalkerConfig.addChild(checkConfig);
1117 
1118         final DefaultConfiguration checkerConfig = createRootConfig(treewalkerConfig);
1119         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1120         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1121         checkerConfig.addChild(treewalkerConfig);
1122 
1123         final Checker checker = createChecker(checkerConfig);
1124 
1125         final String filePath = getPath("InputChecker.java");
1126         try {
1127             checker.process(Collections.singletonList(new File(filePath)));
1128             assertWithMessage("Exception is expected").fail();
1129         }
1130         catch (CheckstyleException exc) {
1131             assertWithMessage("Error message is not expected")
1132                 .that(exc.getMessage())
1133                 .isEqualTo("Exception was thrown while processing " + filePath);
1134 
1135             // destroy is called by Main
1136             checker.destroy();
1137 
1138             final Properties cache = new Properties();
1139             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1140                 cache.load(reader);
1141             }
1142 
1143             assertWithMessage("Cache has unexpected size")
1144                 .that(cache)
1145                 .hasSize(1);
1146             assertWithMessage("testFile is not in cache")
1147                 .that(cache.getProperty(filePath))
1148                 .isNull();
1149         }
1150     }
1151 
1152     /**
1153      * Test doesn't need to be serialized.
1154      *
1155      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1156      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1157      *      for test does not require serialization
1158      */
1159     @Test
1160     public void testCatchErrorWithCache() throws Exception {
1161         final File cacheFile = createTempFile("junit");
1162 
1163         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1164         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1165         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1166 
1167         final String errorMessage = "Java Virtual Machine is broken"
1168             + " or has run out of resources necessary for it to continue operating.";
1169         final Error expectedError = new IOError(new InternalError(errorMessage));
1170 
1171         final File mock = new File("testFile") {
1172             @Serial
1173             private static final long serialVersionUID = 1L;
1174             @Override
1175             public String getAbsolutePath() {
1176                 return "testFile";
1177             }
1178 
1179             /**
1180              * Test is checking catch clause when exception is thrown.
1181              *
1182              * @noinspection ProhibitedExceptionThrown
1183              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1184              *      throw exception as part of test
1185              */
1186             @Override
1187             public File getAbsoluteFile() {
1188                 throw expectedError;
1189             }
1190         };
1191 
1192         final Checker checker = new Checker();
1193         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1194         checker.configure(checkerConfig);
1195         final List<File> filesToProcess = new ArrayList<>();
1196         filesToProcess.add(mock);
1197         try {
1198             checker.process(filesToProcess);
1199             assertWithMessage("IOError is expected!").fail();
1200         }
1201         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
1202         catch (Error error) {
1203             assertWithMessage("Error cause differs from IOError")
1204                     .that(error.getCause())
1205                     .isInstanceOf(IOError.class);
1206             assertWithMessage("Error message is not expected")
1207                     .that(error)
1208                     .hasCauseThat()
1209                     .hasCauseThat()
1210                     .hasMessageThat()
1211                     .isEqualTo(errorMessage);
1212 
1213             // destroy is called by Main
1214             checker.destroy();
1215 
1216             final Properties cache = new Properties();
1217             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1218                 cache.load(reader);
1219             }
1220 
1221             assertWithMessage("Cache has unexpected size")
1222                     .that(cache)
1223                     .hasSize(1);
1224             assertWithMessage("testFile is not in cache")
1225                 .that(cache.getProperty("testFile"))
1226                 .isNull();
1227         }
1228     }
1229 
1230     /**
1231      * Test doesn't need to be serialized.
1232      *
1233      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1234      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1235      *      for test does not require serialization
1236      */
1237     @Test
1238     public void testCatchErrorWithCacheWithNoFileName() throws Exception {
1239         final File cacheFile = createTempFile("junit");
1240 
1241         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1242         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1243         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1244 
1245         final String errorMessage = "Java Virtual Machine is broken"
1246             + " or has run out of resources necessary for it to continue operating.";
1247         final Error expectedError = new IOError(new InternalError(errorMessage));
1248 
1249         final File mock = new File("testFile") {
1250             @Serial
1251             private static final long serialVersionUID = 1L;
1252 
1253             /**
1254              * Test is checking catch clause when exception is thrown.
1255              *
1256              * @noinspection ProhibitedExceptionThrown
1257              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1258              *      throw exception as part of test
1259              */
1260             @Override
1261             public String getAbsolutePath() {
1262                 throw expectedError;
1263             }
1264         };
1265 
1266         final Checker checker = new Checker();
1267         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1268         checker.configure(checkerConfig);
1269         final List<File> filesToProcess = new ArrayList<>();
1270         filesToProcess.add(mock);
1271         try {
1272             checker.process(filesToProcess);
1273             assertWithMessage("IOError is expected!").fail();
1274         }
1275         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
1276         catch (Error error) {
1277             assertWithMessage("Error cause differs from IOError")
1278                     .that(error)
1279                     .hasCauseThat()
1280                     .isInstanceOf(IOError.class);
1281             assertWithMessage("Error message is not expected")
1282                     .that(error)
1283                     .hasCauseThat()
1284                     .hasCauseThat()
1285                     .hasMessageThat()
1286                     .isEqualTo(errorMessage);
1287 
1288             // destroy is called by Main
1289             checker.destroy();
1290 
1291             final Properties cache = new Properties();
1292             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1293                 cache.load(reader);
1294             }
1295 
1296             assertWithMessage("Cache has unexpected size")
1297                     .that(cache)
1298                     .hasSize(1);
1299         }
1300     }
1301 
1302     /**
1303      * Test doesn't need to be serialized.
1304      *
1305      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1306      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1307      *      for test does not require serialization
1308      */
1309     @Test
1310     public void testExceptionWithNoFileName() {
1311         final String errorMessage = "Security Exception";
1312         final RuntimeException expectedError = new SecurityException(errorMessage);
1313 
1314         final File mock = new File("testFile") {
1315             @Serial
1316             private static final long serialVersionUID = 1L;
1317 
1318             /**
1319              * Test is checking catch clause when exception is thrown.
1320              *
1321              * @noinspection ProhibitedExceptionThrown
1322              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1323              *      throw exception as part of test
1324              */
1325             @Override
1326             public String getAbsolutePath() {
1327                 throw expectedError;
1328             }
1329         };
1330 
1331         final Checker checker = new Checker();
1332         final List<File> filesToProcess = new ArrayList<>();
1333         filesToProcess.add(mock);
1334         try {
1335             checker.process(filesToProcess);
1336             assertWithMessage("SecurityException is expected!").fail();
1337         }
1338         catch (CheckstyleException exc) {
1339             assertWithMessage("Error cause differs from SecurityException")
1340                     .that(exc)
1341                     .hasCauseThat()
1342                     .isInstanceOf(SecurityException.class);
1343             assertWithMessage("Error message is not expected")
1344                     .that(exc)
1345                     .hasCauseThat()
1346                     .hasMessageThat()
1347                     .isEqualTo(errorMessage);
1348         }
1349     }
1350 
1351     /**
1352      * Test doesn't need to be serialized.
1353      *
1354      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1355      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1356      *      for test does not require serialization
1357      */
1358     @Test
1359     public void testExceptionWithCacheAndNoFileName() throws Exception {
1360         final File cacheFile = createTempFile("junit");
1361 
1362         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1363         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1364         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1365 
1366         final String errorMessage = "Security Exception";
1367         final RuntimeException expectedError = new SecurityException(errorMessage);
1368 
1369         final File mock = new File("testFile") {
1370             @Serial
1371             private static final long serialVersionUID = 1L;
1372 
1373             /**
1374              * Test is checking catch clause when exception is thrown.
1375              *
1376              * @noinspection ProhibitedExceptionThrown
1377              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1378              *      throw exception as part of test
1379              */
1380             @Override
1381             public String getAbsolutePath() {
1382                 throw expectedError;
1383             }
1384         };
1385 
1386         final Checker checker = new Checker();
1387         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1388         checker.configure(checkerConfig);
1389         final List<File> filesToProcess = new ArrayList<>();
1390         filesToProcess.add(mock);
1391         try {
1392             checker.process(filesToProcess);
1393             assertWithMessage("SecurityException is expected!").fail();
1394         }
1395         catch (CheckstyleException exc) {
1396             assertWithMessage("Error cause differs from SecurityException")
1397                     .that(exc)
1398                     .hasCauseThat()
1399                     .isInstanceOf(SecurityException.class);
1400             assertWithMessage("Error message is not expected")
1401                     .that(exc)
1402                     .hasCauseThat()
1403                     .hasMessageThat()
1404                     .isEqualTo(errorMessage);
1405 
1406             // destroy is called by Main
1407             checker.destroy();
1408 
1409             final Properties cache = new Properties();
1410             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1411                 cache.load(reader);
1412             }
1413 
1414             assertWithMessage("Cache has unexpected size")
1415                     .that(cache)
1416                     .hasSize(1);
1417         }
1418     }
1419 
1420     @Test
1421     public void testHaltOnExceptionOff() throws Exception {
1422         final String filePath = getPath("InputChecker.java");
1423         final String[] expected = {
1424             "1: " + getCheckMessage(EXCEPTION_MSG, "java.lang.IndexOutOfBoundsException: test"),
1425         };
1426 
1427         verifyWithInlineXmlConfig(filePath, expected);
1428     }
1429 
1430     @Test
1431     public void testTabViolationDefault() throws Exception {
1432         final String[] expected = {
1433             "17:17: violation",
1434             "21:49: violation",
1435         };
1436         verifyWithInlineConfigParser(getPath("InputCheckerTabCharacter.java"),
1437             expected);
1438     }
1439 
1440     @Test
1441     public void testTabViolationCustomWidth() throws Exception {
1442         final String[] expected = {
1443             "18:17: violation",
1444             "22:37: violation",
1445         };
1446 
1447         verifyWithInlineXmlConfig(getPath("InputCheckerTabCharacterCustomWidth.java"), expected);
1448     }
1449 
1450     @Test
1451     public void testCheckerProcessCallAllNeededMethodsOfFileSets() throws Exception {
1452         final DummyFileSet fileSet = new DummyFileSet();
1453         final Checker checker = new Checker();
1454         checker.addFileSetCheck(fileSet);
1455         checker.process(Collections.singletonList(new File("dummy.java")));
1456         final List<String> expected =
1457             Arrays.asList("beginProcessing", "finishProcessing", "destroy");
1458         assertWithMessage("Method calls were not expected")
1459             .that(fileSet.getMethodCalls())
1460             .isEqualTo(expected);
1461     }
1462 
1463     @Test
1464     public void testSetFileSetCheckSetsMessageDispatcher() {
1465         final DummyFileSet fileSet = new DummyFileSet();
1466         final Checker checker = new Checker();
1467         checker.addFileSetCheck(fileSet);
1468         assertWithMessage("Message dispatcher was not expected")
1469             .that(fileSet.getInternalMessageDispatcher())
1470             .isEqualTo(checker);
1471     }
1472 
1473     @Test
1474     public void testAddAuditListenerAsChild() throws Exception {
1475         final Checker checker = new Checker();
1476         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
1477         final PackageObjectFactory factory = new PackageObjectFactory(
1478                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1479             @Override
1480             public Object createModule(String name) throws CheckstyleException {
1481                 Object adapter = auditAdapter;
1482                 if (!name.equals(DebugAuditAdapter.class.getName())) {
1483                     adapter = super.createModule(name);
1484                 }
1485                 return adapter;
1486             }
1487         };
1488         checker.setModuleFactory(factory);
1489         checker.setupChild(createModuleConfig(DebugAuditAdapter.class));
1490         // Let's try fire some events
1491         checker.process(Collections.singletonList(new File("dummy.java")));
1492         assertWithMessage("Checker.fireAuditStarted() doesn't call listener")
1493                 .that(auditAdapter.wasCalled())
1494                 .isTrue();
1495     }
1496 
1497     @Test
1498     public void testAddBeforeExecutionFileFilterAsChild() throws Exception {
1499         final Checker checker = new Checker();
1500         final TestBeforeExecutionFileFilter fileFilter = new TestBeforeExecutionFileFilter();
1501         final PackageObjectFactory factory = new PackageObjectFactory(
1502                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1503             @Override
1504             public Object createModule(String name) throws CheckstyleException {
1505                 Object filter = fileFilter;
1506                 if (!name.equals(TestBeforeExecutionFileFilter.class.getName())) {
1507                     filter = super.createModule(name);
1508                 }
1509                 return filter;
1510             }
1511         };
1512         checker.setModuleFactory(factory);
1513         checker.setupChild(createModuleConfig(TestBeforeExecutionFileFilter.class));
1514         checker.process(Collections.singletonList(new File("dummy.java")));
1515         assertWithMessage("Checker.acceptFileStarted() doesn't call listener")
1516                 .that(fileFilter.wasCalled())
1517                 .isTrue();
1518     }
1519 
1520     @Test
1521     public void testFileSetCheckInitWhenAddedAsChild() throws Exception {
1522         final Checker checker = new Checker();
1523         final DummyFileSet fileSet = new DummyFileSet();
1524         final PackageObjectFactory factory = new PackageObjectFactory(
1525                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1526             @Override
1527             public Object createModule(String name) throws CheckstyleException {
1528                 Object check = fileSet;
1529                 if (!name.equals(DummyFileSet.class.getName())) {
1530                     check = super.createModule(name);
1531                 }
1532                 return check;
1533             }
1534         };
1535         checker.setModuleFactory(factory);
1536         checker.finishLocalSetup();
1537         checker.setupChild(createModuleConfig(DummyFileSet.class));
1538         assertWithMessage("FileSetCheck.init() wasn't called")
1539                 .that(fileSet.isInitCalled())
1540                 .isTrue();
1541     }
1542 
1543     // -@cs[CheckstyleTestMakeup] must use raw class to directly initialize DefaultLogger
1544     @Test
1545     public void testDefaultLoggerClosesItStreams() throws Exception {
1546         final Checker checker = new Checker();
1547         try (CloseAndFlushTestByteArrayOutputStream testInfoOutputStream =
1548                 new CloseAndFlushTestByteArrayOutputStream();
1549             CloseAndFlushTestByteArrayOutputStream testErrorOutputStream =
1550                 new CloseAndFlushTestByteArrayOutputStream()) {
1551             checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1552             checker.addListener(new DefaultLogger(testInfoOutputStream,
1553                 OutputStreamOptions.CLOSE, testErrorOutputStream, OutputStreamOptions.CLOSE));
1554 
1555             final File tmpFile = createTempFile("file", ".java");
1556 
1557             execute(checker, tmpFile.getPath());
1558 
1559             assertWithMessage("Output stream close count")
1560                     .that(testInfoOutputStream.getCloseCount())
1561                     .isEqualTo(1);
1562             assertWithMessage("Output stream flush count")
1563                     .that(testInfoOutputStream.getFlushCount())
1564                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(3));
1565             assertWithMessage("Error stream close count")
1566                     .that(testErrorOutputStream.getCloseCount())
1567                     .isEqualTo(1);
1568             assertWithMessage("Error stream flush count")
1569                     .that(testErrorOutputStream.getFlushCount())
1570                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(1));
1571         }
1572     }
1573 
1574     // -@cs[CheckstyleTestMakeup] must use raw class to directly initialize DefaultLogger
1575     @Test
1576     public void testXmlLoggerClosesItStreams() throws Exception {
1577         final Checker checker = new Checker();
1578         try (CloseAndFlushTestByteArrayOutputStream testInfoOutputStream =
1579                 new CloseAndFlushTestByteArrayOutputStream()) {
1580             checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1581             checker.addListener(new XMLLogger(testInfoOutputStream, OutputStreamOptions.CLOSE));
1582 
1583             final File tmpFile = createTempFile("file", ".java");
1584 
1585             execute(checker, tmpFile.getPath(), tmpFile.getPath());
1586 
1587             assertWithMessage("Output stream close count")
1588                     .that(testInfoOutputStream.getCloseCount())
1589                     .isEqualTo(1);
1590             assertWithMessage("Output stream flush count")
1591                     .that(testInfoOutputStream.getFlushCount())
1592                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(0));
1593         }
1594     }
1595 
1596     @Test
1597     public void testDuplicatedModule() throws Exception {
1598         // we need to test a module with two instances, one with id and the other not
1599         final DefaultConfiguration moduleConfig1 =
1600                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1601         final DefaultConfiguration moduleConfig2 =
1602                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1603         moduleConfig2.addProperty("id", "ModuleId");
1604         final DefaultConfiguration root = new DefaultConfiguration("root");
1605         root.addChild(moduleConfig1);
1606         root.addChild(moduleConfig2);
1607         final Checker checker = new Checker();
1608         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1609         checker.configure(root);
1610         // BriefUtLogger does not print the module name or id postfix,
1611         // so we need to set logger manually
1612         final ByteArrayOutputStream out = TestUtil.getInternalState(this,
1613                 "stream", ByteArrayOutputStream.class);
1614         final DefaultLogger logger = new DefaultLogger(out, OutputStreamOptions.CLOSE, out,
1615                 OutputStreamOptions.NONE, new AuditEventDefaultFormatter());
1616         checker.addListener(logger);
1617 
1618         final File tempFile = createTempFile("file", ".java");
1619         try (BufferedWriter bufferedWriter = Files.newBufferedWriter(tempFile.toPath())) {
1620             bufferedWriter.write(';');
1621         }
1622         final String path = tempFile.getPath();
1623         final String violationMessage =
1624                 getCheckMessage(NewlineAtEndOfFileCheck.class, MSG_KEY_NO_NEWLINE_EOF);
1625         final String[] expected = {
1626             "1: " + violationMessage + " [NewlineAtEndOfFile]",
1627             "1: " + violationMessage + " [ModuleId]",
1628         };
1629 
1630         // super.verify does not work here, for we change the logger
1631         out.flush();
1632         final int errs = checker.process(Collections.singletonList(new File(path)));
1633         try (ByteArrayInputStream inputStream =
1634                 new ByteArrayInputStream(out.toByteArray());
1635             LineNumberReader lnr = new LineNumberReader(
1636                 new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
1637             // we need to ignore the unrelated lines
1638             final List<String> actual = lnr.lines()
1639                     .filter(line -> !getCheckMessage(AUDIT_STARTED_MESSAGE).equals(line))
1640                     .filter(line -> !getCheckMessage(AUDIT_FINISHED_MESSAGE).equals(line))
1641                     .limit(expected.length)
1642                     .sorted()
1643                     .toList();
1644             Arrays.sort(expected);
1645 
1646             for (int i = 0; i < expected.length; i++) {
1647                 final String expectedResult = "[ERROR] " + path + ":" + expected[i];
1648                 assertWithMessage("error message " + i)
1649                         .that(actual.get(i))
1650                         .isEqualTo(expectedResult);
1651             }
1652 
1653             assertWithMessage("unexpected output: " + lnr.readLine())
1654                     .that(errs)
1655                     .isEqualTo(expected.length);
1656         }
1657 
1658         checker.destroy();
1659     }
1660 
1661     @Test
1662     public void testCachedFile() throws Exception {
1663         final Checker checker = createChecker(createModuleConfig(TranslationCheck.class));
1664         final OutputStream infoStream = new ByteArrayOutputStream();
1665         final OutputStream errorStream = new ByteArrayOutputStream();
1666         final DefaultLoggerWithCounter loggerWithCounter =
1667             new DefaultLoggerWithCounter(infoStream, OutputStreamOptions.CLOSE,
1668                                          errorStream, OutputStreamOptions.CLOSE);
1669         checker.addListener(loggerWithCounter);
1670         final File cacheFile = createTempFile("cacheFile", ".txt");
1671         checker.setCacheFile(cacheFile.getAbsolutePath());
1672 
1673         final File testFile = createTempFile("testFile", ".java");
1674         final List<File> files = List.of(testFile, testFile);
1675         checker.process(files);
1676 
1677         assertWithMessage("Cached file should not be processed twice")
1678             .that(loggerWithCounter.fileStartedCount)
1679             .isEqualTo(1);
1680 
1681         checker.destroy();
1682     }
1683 
1684     @Test
1685     public void testUnmappableCharacters() throws Exception {
1686         final String[] expected = {
1687             "14: " + getCheckMessage(LineLengthCheck.class, MSG_KEY, 80, 225),
1688         };
1689 
1690         verifyWithInlineXmlConfig(getPath("InputCheckerTestCharset.java"),
1691                 expected);
1692     }
1693 
1694     /**
1695      * This tests uses 'verify' method, because it needs some config
1696      * to be executed on non-existing Input file,
1697      * but BDD style methods need config in existing file.
1698      *
1699      * @throws Exception exception
1700      */
1701     @SuppressForbidden
1702     @Test
1703     public void testViolationMessageOnIoException() throws Exception {
1704         final DefaultConfiguration checkConfig =
1705                 createModuleConfig(CheckWhichThrowsError.class);
1706 
1707         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
1708         treeWalkerConfig.addChild(checkConfig);
1709 
1710         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
1711         checkerConfig.addChild(treeWalkerConfig);
1712 
1713         checkerConfig.addProperty("haltOnException", "false");
1714         final File file = new File("InputNonChecker.java");
1715         final String filePath = file.getAbsolutePath();
1716         final String[] expected = {
1717             "1: " + getCheckMessage(EXCEPTION_MSG, filePath
1718                         + " (No such file or directory)"),
1719         };
1720 
1721         verify(checkerConfig, filePath, expected);
1722     }
1723 
1724     /**
1725      * Reason of non-Input based testing:
1726      * There are bunch of asserts that expects full path to file,
1727      * usage of "basedir" make it stripped and we need put everywhere code like
1728      * <pre>CommonUtil.relativizePath(checker.getConfiguration().getProperty("basedir"), file)</pre>
1729      * but Checker object is not always available in code.
1730      * Propagating it in all code methods will complicate code.
1731      */
1732     @Test
1733     public void testRelativizedFileExclusion() throws Exception {
1734         final DefaultConfiguration newLineAtEndOfFileConfig =
1735                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1736 
1737         final DefaultConfiguration beforeExecutionExclusionFileFilterConfig =
1738                 createModuleConfig(BeforeExecutionExclusionFileFilter.class);
1739 
1740         beforeExecutionExclusionFileFilterConfig.addProperty("fileNamePattern",
1741                         "^(?!InputCheckerTestExcludeRelativizedFile.*\\.java).*");
1742 
1743         final DefaultConfiguration checkerConfig = createRootConfig(null);
1744         checkerConfig.addChild(newLineAtEndOfFileConfig);
1745         checkerConfig.addChild(beforeExecutionExclusionFileFilterConfig);
1746 
1747         // -@cs[CheckstyleTestMakeup] Needs to be fixed.
1748         checkerConfig.addProperty("basedir",
1749                 temporaryFolder.getPath());
1750 
1751         final String violationMessage =
1752                 getCheckMessage(NewlineAtEndOfFileCheck.class, MSG_KEY_NO_NEWLINE_EOF);
1753 
1754         final String[] expected = {
1755             "1: " + violationMessage,
1756         };
1757 
1758         final File tempFile = createTempFile("InputCheckerTestExcludeRelativizedFile", ".java");
1759         try (BufferedWriter bufferedWriter = Files.newBufferedWriter(tempFile.toPath())) {
1760             bufferedWriter.write(';');
1761         }
1762 
1763         final File[] processedFiles = {tempFile};
1764 
1765         verify(createChecker(checkerConfig), processedFiles,
1766                 tempFile.getName(), expected);
1767     }
1768 
1769     @Test
1770     public void testBasedirPathMismatchExceptionMessage() throws IOException {
1771         final CheckstyleAntTask antTask = new CheckstyleAntTask();
1772         antTask.setProject(new Project());
1773         antTask.setConfig(getPath("InputCheckerTestConfigBasedirMismatch.xml"));
1774 
1775         final File testFile = Files.createFile(
1776             temporaryFolder.toPath().resolve("TestBasedirMismatch.java"))
1777             .toFile();
1778 
1779         antTask.setFile(testFile);
1780 
1781         final BuildException ex = getExpectedThrowable(
1782             BuildException.class,
1783             antTask::execute,
1784             "BuildException is expected");
1785 
1786         final IllegalStateException relativizeException = findRelativizePathException(ex);
1787         final String message = relativizeException.getMessage();
1788 
1789         assertWithMessage("Exception message should mention failed relativization")
1790             .that(message)
1791             .contains("Failed to relativize path");
1792 
1793         assertWithMessage("Exception message should mention base directory")
1794             .that(message)
1795             .contains("base directory");
1796 
1797         assertWithMessage("Exception message should contain the file path")
1798             .that(message)
1799             .contains(testFile.getAbsolutePath());
1800     }
1801 
1802     private static IllegalStateException findRelativizePathException(Throwable throwable) {
1803         Throwable current = throwable;
1804         while (current != null) {
1805             if (current instanceof IllegalStateException exception
1806                     && current.getCause() instanceof IllegalArgumentException) {
1807                 return exception;
1808             }
1809             current = current.getCause();
1810         }
1811         throw new AssertionError(
1812             "IllegalStateException from CommonUtil.relativizePath not found in cause chain");
1813     }
1814 
1815     public static class DefaultLoggerWithCounter extends DefaultLogger {
1816 
1817         private int fileStartedCount;
1818 
1819         public DefaultLoggerWithCounter(OutputStream infoStream,
1820                                         OutputStreamOptions infoStreamOptions,
1821                                         OutputStream errorStream,
1822                                         OutputStreamOptions errorStreamOptions) {
1823             super(infoStream, infoStreamOptions, errorStream, errorStreamOptions);
1824         }
1825 
1826         @Override
1827         public void fileStarted(AuditEvent event) {
1828             fileStartedCount++;
1829         }
1830     }
1831 
1832     public static class DummyFilter implements Filter {
1833 
1834         @Override
1835         public boolean accept(AuditEvent event) {
1836             return false;
1837         }
1838 
1839     }
1840 
1841     public static class DummyFileSetViolationCheck extends AbstractFileSetCheck
1842         implements ExternalResourceHolder {
1843 
1844         @Override
1845         protected void processFiltered(File file, FileText fileText) {
1846             log(1, "test");
1847         }
1848 
1849         @Override
1850         public Set<String> getExternalResourceLocations() {
1851             final Set<String> externalResourceLocation = new HashSet<>(1);
1852             externalResourceLocation.add("non_existent_external_resource.xml");
1853             return externalResourceLocation;
1854         }
1855 
1856     }
1857 
1858     public static class DummyFilterSet extends FilterSet implements ExternalResourceHolder {
1859 
1860         @Override
1861         public Set<String> getExternalResourceLocations() {
1862             final Set<String> strings = new HashSet<>();
1863             strings.add("test");
1864             return strings;
1865         }
1866 
1867     }
1868 
1869     public static final class DynamicalResourceHolderCheck extends AbstractFileSetCheck
1870         implements ExternalResourceHolder {
1871 
1872         private String firstExternalResourceLocation;
1873         private String secondExternalResourceLocation;
1874 
1875         public void setFirstExternalResourceLocation(String firstExternalResourceLocation) {
1876             this.firstExternalResourceLocation = firstExternalResourceLocation;
1877         }
1878 
1879         public void setSecondExternalResourceLocation(String secondExternalResourceLocation) {
1880             this.secondExternalResourceLocation = secondExternalResourceLocation;
1881         }
1882 
1883         @Override
1884         protected void processFiltered(File file, FileText fileText) {
1885             // there is no need in implementation of the method
1886         }
1887 
1888         @Override
1889         public Set<String> getExternalResourceLocations() {
1890             final Set<String> locations = new HashSet<>();
1891             locations.add(firstExternalResourceLocation);
1892             // Attempt to change the behaviour of the check dynamically
1893             if (secondExternalResourceLocation != null) {
1894                 locations.add(secondExternalResourceLocation);
1895             }
1896             return locations;
1897         }
1898 
1899     }
1900 
1901     public static class CheckWhichDoesNotRequireCommentNodes extends AbstractCheck {
1902 
1903         /** Number of children of method definition token. */
1904         private static final int METHOD_DEF_CHILD_COUNT = 7;
1905 
1906         @Override
1907         public int[] getDefaultTokens() {
1908             return new int[] {TokenTypes.METHOD_DEF};
1909         }
1910 
1911         @Override
1912         public int[] getAcceptableTokens() {
1913             return new int[] {TokenTypes.METHOD_DEF};
1914         }
1915 
1916         @Override
1917         public int[] getRequiredTokens() {
1918             return new int[] {TokenTypes.METHOD_DEF};
1919         }
1920 
1921         @Override
1922         public void visitToken(DetailAST ast) {
1923             if (ast.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(
1924                     TokenTypes.BLOCK_COMMENT_BEGIN) != null) {
1925                 log(ast, "AST has incorrect structure structure."
1926                     + " The check does not require comment nodes but there were comment nodes"
1927                     + " in the AST.");
1928             }
1929             final int childCount = ast.getChildCount();
1930             if (childCount != METHOD_DEF_CHILD_COUNT) {
1931                 final String msg = String.format(Locale.ENGLISH,
1932                     "AST node in no comment tree has wrong number of children. "
1933                             + "Expected is %d but was %d",
1934                     METHOD_DEF_CHILD_COUNT, childCount);
1935                 log(ast, msg);
1936             }
1937             // count children where comment lives
1938             int actualChildCount = 0;
1939             for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child =
1940                     child.getNextSibling()) {
1941                 actualChildCount++;
1942             }
1943             final int cacheChildCount = ast.getFirstChild().getChildCount();
1944             if (cacheChildCount != actualChildCount) {
1945                 final String msg = String.format(Locale.ENGLISH,
1946                         "AST node with no comment has wrong number of children. "
1947                                 + "Expected is %d but was %d",
1948                         cacheChildCount, actualChildCount);
1949                 log(ast, msg);
1950             }
1951         }
1952 
1953     }
1954 
1955     public static class CheckWhichRequiresCommentNodes extends AbstractCheck {
1956 
1957         /** Number of children of method definition token. */
1958         private static final int METHOD_DEF_CHILD_COUNT = 7;
1959 
1960         @Override
1961         public boolean isCommentNodesRequired() {
1962             return true;
1963         }
1964 
1965         @Override
1966         public int[] getDefaultTokens() {
1967             return new int[] {TokenTypes.METHOD_DEF};
1968         }
1969 
1970         @Override
1971         public int[] getAcceptableTokens() {
1972             return new int[] {TokenTypes.METHOD_DEF};
1973         }
1974 
1975         @Override
1976         public int[] getRequiredTokens() {
1977             return new int[] {TokenTypes.METHOD_DEF};
1978         }
1979 
1980         // Locale.ENGLISH until #12104
1981         @Override
1982         public void visitToken(DetailAST ast) {
1983             if (ast.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(
1984                     TokenTypes.BLOCK_COMMENT_BEGIN) == null) {
1985                 log(ast, "Incorrect AST structure.");
1986             }
1987             final int childCount = ast.getChildCount();
1988             if (childCount != METHOD_DEF_CHILD_COUNT) {
1989                 final String msg = String.format(Locale.ENGLISH,
1990                     "AST node in comment tree has wrong number of children. "
1991                             + "Expected is %d but was %d",
1992                     METHOD_DEF_CHILD_COUNT, childCount);
1993                 log(ast, msg);
1994             }
1995             // count children where comment lives
1996             int actualChildCount = 0;
1997             for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child =
1998                     child.getNextSibling()) {
1999                 actualChildCount++;
2000             }
2001             final int cacheChildCount = ast.getFirstChild().getChildCount();
2002             if (cacheChildCount != actualChildCount) {
2003                 final String msg = String.format(Locale.ENGLISH,
2004                         "AST node with comment has wrong number of children. "
2005                                 + "Expected is %d but was %d",
2006                         cacheChildCount, actualChildCount);
2007                 log(ast, msg);
2008             }
2009         }
2010 
2011     }
2012 
2013     public static final class DummyFileSet extends AbstractFileSetCheck {
2014 
2015         private final List<String> methodCalls = new ArrayList<>();
2016 
2017         private boolean initCalled;
2018 
2019         @Override
2020         public void init() {
2021             super.init();
2022             initCalled = true;
2023         }
2024 
2025         @Override
2026         public void beginProcessing(String charset) {
2027             methodCalls.add("beginProcessing");
2028             super.beginProcessing(charset);
2029         }
2030 
2031         @Override
2032         public void finishProcessing() {
2033             methodCalls.add("finishProcessing");
2034             super.finishProcessing();
2035         }
2036 
2037         @Override
2038         protected void processFiltered(File file, FileText fileText) {
2039             methodCalls.add("processFiltered");
2040         }
2041 
2042         @Override
2043         public void destroy() {
2044             methodCalls.add("destroy");
2045             super.destroy();
2046         }
2047 
2048         public List<String> getMethodCalls() {
2049             return Collections.unmodifiableList(methodCalls);
2050         }
2051 
2052         public boolean isInitCalled() {
2053             return initCalled;
2054         }
2055 
2056         public MessageDispatcher getInternalMessageDispatcher() {
2057             return getMessageDispatcher();
2058         }
2059 
2060     }
2061 
2062 }