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