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