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