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