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.UnsupportedEncodingException;
41  import java.lang.reflect.Field;
42  import java.lang.reflect.Method;
43  import java.nio.charset.StandardCharsets;
44  import java.nio.file.Files;
45  import java.util.ArrayList;
46  import java.util.Arrays;
47  import java.util.Collections;
48  import java.util.HashSet;
49  import java.util.List;
50  import java.util.Locale;
51  import java.util.Objects;
52  import java.util.Properties;
53  import java.util.Set;
54  import java.util.SortedSet;
55  import java.util.TreeSet;
56  import java.util.UUID;
57  import java.util.stream.Collectors;
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             private static final long serialVersionUID = 1L;
802 
803             /**
804              * Test is checking catch clause when exception is thrown.
805              *
806              * @noinspection ProhibitedExceptionThrown
807              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
808              *      throw exception as part of test
809              */
810             @Override
811             public long lastModified() {
812                 throw expectedError;
813             }
814         };
815 
816         final Checker checker = new Checker();
817         final List<File> filesToProcess = new ArrayList<>();
818         filesToProcess.add(mock);
819         try {
820             checker.process(filesToProcess);
821             assertWithMessage("IOError is expected!").fail();
822         }
823         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
824         catch (Error error) {
825             assertWithMessage("Error cause differs from IOError")
826                     .that(error.getCause())
827                     .isInstanceOf(IOError.class);
828             assertWithMessage("Error cause is not InternalError")
829                     .that(error.getCause().getCause())
830                     .isInstanceOf(InternalError.class);
831             assertWithMessage("Error message is not expected")
832                     .that(error)
833                     .hasCauseThat()
834                     .hasCauseThat()
835                     .hasMessageThat()
836                     .isEqualTo(errorMessage);
837         }
838     }
839 
840     /**
841      * Test doesn't need to be serialized.
842      *
843      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
844      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
845      *      for test does not require serialization
846      */
847     @Test
848     public void testCatchErrorWithNoFileName() throws Exception {
849         // Assume that I/O error is happened when we try to invoke 'lastModified()' method.
850         final String errorMessage = "Java Virtual Machine is broken"
851             + " or has run out of resources necessary for it to continue operating.";
852         final Error expectedError = new IOError(new InternalError(errorMessage));
853 
854         final File mock = new File("testFile") {
855             private static final long serialVersionUID = 1L;
856 
857             /**
858              * Test is checking catch clause when exception is thrown.
859              *
860              * @noinspection ProhibitedExceptionThrown
861              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
862              *      throw exception as part of test
863              */
864             @Override
865             public long lastModified() {
866                 throw expectedError;
867             }
868 
869             @Override
870             public String getAbsolutePath() {
871                 return null;
872             }
873         };
874 
875         final Checker checker = new Checker();
876         final List<File> filesToProcess = new ArrayList<>();
877         filesToProcess.add(mock);
878         try {
879             checker.process(filesToProcess);
880             assertWithMessage("IOError is expected!").fail();
881         }
882         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
883         catch (Error error) {
884             assertWithMessage("Error cause differs from IOError")
885                     .that(error)
886                     .hasCauseThat()
887                     .isInstanceOf(IOError.class);
888             assertWithMessage("Error cause is not InternalError")
889                     .that(error)
890                     .hasCauseThat()
891                     .hasCauseThat()
892                     .isInstanceOf(InternalError.class);
893             assertWithMessage("Error message is not expected")
894                     .that(error)
895                     .hasCauseThat()
896                     .hasCauseThat()
897                     .hasMessageThat()
898                     .isEqualTo(errorMessage);
899         }
900     }
901 
902     /**
903      * It is OK to have long test method name here as it describes the test purpose.
904      */
905     @Test
906     public void testCacheAndFilterWhichDoesNotImplementExternalResourceHolderInterface()
907             throws Exception {
908         assertWithMessage("ExternalResourceHolder has changed its parent")
909                 .that(ExternalResourceHolder.class.isAssignableFrom(DummyFilter.class))
910                 .isFalse();
911         final DefaultConfiguration filterConfig = createModuleConfig(DummyFilter.class);
912 
913         final DefaultConfiguration checkerConfig = createRootConfig(filterConfig);
914         final File cacheFile = createTempFile("junit");
915         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
916 
917         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
918 
919         execute(checkerConfig, pathToEmptyFile);
920         final Properties cacheAfterFirstRun = new Properties();
921         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
922             cacheAfterFirstRun.load(reader);
923         }
924 
925         // One more time to use cache.
926         execute(checkerConfig, pathToEmptyFile);
927         final Properties cacheAfterSecondRun = new Properties();
928         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
929             cacheAfterSecondRun.load(reader);
930         }
931 
932         final String cacheFilePath = cacheAfterSecondRun.getProperty(pathToEmptyFile);
933         assertWithMessage("Cache file has changed its path")
934             .that(cacheFilePath)
935             .isEqualTo(cacheAfterFirstRun.getProperty(pathToEmptyFile));
936         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
937         assertWithMessage("Cache has changed its hash")
938             .that(cacheHash)
939             .isEqualTo(cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
940         final int expectedNumberOfObjectsInCache = 2;
941         assertWithMessage("Number of items in cache differs from expected")
942             .that(cacheAfterFirstRun)
943             .hasSize(expectedNumberOfObjectsInCache);
944         assertWithMessage("Number of items in cache differs from expected")
945             .that(cacheAfterSecondRun)
946             .hasSize(expectedNumberOfObjectsInCache);
947     }
948 
949     /**
950      * It is OK to have long test method name here as it describes the test purpose.
951      */
952     // -@cs[ExecutableStatementCount] This test needs to verify many things.
953     @Test
954     public void testCacheAndCheckWhichAddsNewResourceLocationButKeepsSameCheckerInstance()
955             throws Exception {
956         // Use case (https://github.com/checkstyle/checkstyle/pull/3092#issuecomment-218162436):
957         // Imagine that cache exists in a file. New version of Checkstyle appear.
958         // New release contains update to a some check to have additional external resource.
959         // User update his configuration and run validation as usually.
960         // Cache should not be reused.
961 
962         final DynamicalResourceHolderCheck check = new DynamicalResourceHolderCheck();
963         final String firstExternalResourceLocation = getPath("InputCheckerImportControlOne.xml");
964         final String firstExternalResourceKey = PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
965                 + firstExternalResourceLocation;
966         check.setFirstExternalResourceLocation(firstExternalResourceLocation);
967 
968         final DefaultConfiguration checkerConfig = createRootConfig(null);
969         final File cacheFile = createTempFile("junit");
970         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
971 
972         final Checker checker = new Checker();
973         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
974         checker.addFileSetCheck(check);
975         checker.addFilter(new DummyFilterSet());
976         checker.configure(checkerConfig);
977         checker.addListener(getBriefUtLogger());
978 
979         final String pathToEmptyFile = createTempFile("file", ".java").getPath();
980 
981         execute(checker, pathToEmptyFile);
982         final Properties cacheAfterFirstRun = new Properties();
983         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
984             cacheAfterFirstRun.load(reader);
985         }
986 
987         final int expectedNumberOfObjectsInCacheAfterFirstRun = 4;
988         assertWithMessage("Number of items in cache differs from expected")
989             .that(cacheAfterFirstRun)
990             .hasSize(expectedNumberOfObjectsInCacheAfterFirstRun);
991 
992         // Change a list of external resources which are used by the check
993         final String secondExternalResourceLocation = "InputCheckerImportControlTwo.xml";
994         final String secondExternalResourceKey = PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
995                 + secondExternalResourceLocation;
996         check.setSecondExternalResourceLocation(secondExternalResourceLocation);
997 
998         checker.addFileSetCheck(check);
999         checker.configure(checkerConfig);
1000 
1001         execute(checker, pathToEmptyFile);
1002         final Properties cacheAfterSecondRun = new Properties();
1003         try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1004             cacheAfterSecondRun.load(reader);
1005         }
1006 
1007         final String cacheFilePath = cacheAfterSecondRun.getProperty(pathToEmptyFile);
1008         assertWithMessage("Cache file has changed its path")
1009             .that(cacheFilePath)
1010             .isEqualTo(cacheAfterFirstRun.getProperty(pathToEmptyFile));
1011         final String cacheHash = cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY);
1012         assertWithMessage("Cache has changed its hash")
1013             .that(cacheHash)
1014             .isEqualTo(cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY));
1015         final String resourceKey = cacheAfterSecondRun.getProperty(firstExternalResourceKey);
1016         assertWithMessage("Cache has changed its resource key")
1017             .that(resourceKey)
1018             .isEqualTo(cacheAfterFirstRun.getProperty(firstExternalResourceKey));
1019         assertWithMessage("Cache has null as a resource key")
1020             .that(cacheAfterFirstRun.getProperty(firstExternalResourceKey))
1021             .isNotNull();
1022         final int expectedNumberOfObjectsInCacheAfterSecondRun = 4;
1023         assertWithMessage("Number of items in cache differs from expected")
1024             .that(cacheAfterSecondRun)
1025             .hasSize(expectedNumberOfObjectsInCacheAfterSecondRun);
1026         assertWithMessage("Cache has not null as a resource key")
1027             .that(cacheAfterFirstRun.getProperty(secondExternalResourceKey))
1028             .isNull();
1029         assertWithMessage("Cache has null as a resource key")
1030             .that(cacheAfterSecondRun.getProperty(secondExternalResourceKey))
1031             .isNotNull();
1032     }
1033 
1034     @Test
1035     public void testClearLazyLoadCacheInDetailAST() throws Exception {
1036 
1037         final String filePath = getPath("InputCheckerClearDetailAstLazyLoadCache.java");
1038 
1039         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
1040         verifyWithInlineConfigParser(filePath, expected);
1041     }
1042 
1043     @Test
1044     public void testCacheOnViolationSuppression() throws Exception {
1045         final File cacheFile = createTempFile("junit");
1046         final DefaultConfiguration violationCheck =
1047                 createModuleConfig(DummyFileSetViolationCheck.class);
1048 
1049         final DefaultConfiguration filterConfig = createModuleConfig(SuppressionFilter.class);
1050         filterConfig.addProperty("file", getPath("InputCheckerSuppressAll.xml"));
1051 
1052         final DefaultConfiguration checkerConfig = createRootConfig(violationCheck);
1053         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1054         checkerConfig.addChild(filterConfig);
1055 
1056         final String fileViolationPath = createTempFile("ViolationFile", ".java").getPath();
1057 
1058         execute(checkerConfig, fileViolationPath);
1059 
1060         try (InputStream input = Files.newInputStream(cacheFile.toPath())) {
1061             final Properties details = new Properties();
1062             details.load(input);
1063 
1064             assertWithMessage("suppressed violation file saved in cache")
1065                 .that(details.getProperty(fileViolationPath))
1066                 .isNotNull();
1067         }
1068     }
1069 
1070     @Test
1071     public void testHaltOnException() throws Exception {
1072         final DefaultConfiguration checkConfig =
1073             createModuleConfig(CheckWhichThrowsError.class);
1074         final String filePath = getPath("InputChecker.java");
1075         try {
1076             execute(checkConfig, filePath);
1077             assertWithMessage("Exception is expected").fail();
1078         }
1079         catch (CheckstyleException exc) {
1080             assertWithMessage("Error message is not expected")
1081                 .that(exc.getMessage())
1082                 .isEqualTo("Exception was thrown while processing " + filePath);
1083         }
1084     }
1085 
1086     @Test
1087     public void testExceptionWithCache() throws Exception {
1088         final File cacheFile = createTempFile("junit");
1089 
1090         final DefaultConfiguration checkConfig =
1091                 createModuleConfig(CheckWhichThrowsError.class);
1092 
1093         final DefaultConfiguration treewalkerConfig =
1094                 createModuleConfig(TreeWalker.class);
1095         treewalkerConfig.addChild(checkConfig);
1096 
1097         final DefaultConfiguration checkerConfig = createRootConfig(treewalkerConfig);
1098         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1099         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1100         checkerConfig.addChild(treewalkerConfig);
1101 
1102         final Checker checker = createChecker(checkerConfig);
1103 
1104         final String filePath = getPath("InputChecker.java");
1105         try {
1106             checker.process(Collections.singletonList(new File(filePath)));
1107             assertWithMessage("Exception is expected").fail();
1108         }
1109         catch (CheckstyleException exc) {
1110             assertWithMessage("Error message is not expected")
1111                 .that(exc.getMessage())
1112                 .isEqualTo("Exception was thrown while processing " + filePath);
1113 
1114             // destroy is called by Main
1115             checker.destroy();
1116 
1117             final Properties cache = new Properties();
1118             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1119                 cache.load(reader);
1120             }
1121 
1122             assertWithMessage("Cache has unexpected size")
1123                 .that(cache)
1124                 .hasSize(1);
1125             assertWithMessage("testFile is not in cache")
1126                 .that(cache.getProperty(filePath))
1127                 .isNull();
1128         }
1129     }
1130 
1131     /**
1132      * Test doesn't need to be serialized.
1133      *
1134      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1135      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1136      *      for test does not require serialization
1137      */
1138     @Test
1139     public void testCatchErrorWithCache() throws Exception {
1140         final File cacheFile = createTempFile("junit");
1141 
1142         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1143         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1144         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1145 
1146         final String errorMessage = "Java Virtual Machine is broken"
1147             + " or has run out of resources necessary for it to continue operating.";
1148         final Error expectedError = new IOError(new InternalError(errorMessage));
1149 
1150         final File mock = new File("testFile") {
1151             private static final long serialVersionUID = 1L;
1152 
1153             @Override
1154             public String getAbsolutePath() {
1155                 return "testFile";
1156             }
1157 
1158             /**
1159              * Test is checking catch clause when exception is thrown.
1160              *
1161              * @noinspection ProhibitedExceptionThrown
1162              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1163              *      throw exception as part of test
1164              */
1165             @Override
1166             public File getAbsoluteFile() {
1167                 throw expectedError;
1168             }
1169         };
1170 
1171         final Checker checker = new Checker();
1172         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1173         checker.configure(checkerConfig);
1174         final List<File> filesToProcess = new ArrayList<>();
1175         filesToProcess.add(mock);
1176         try {
1177             checker.process(filesToProcess);
1178             assertWithMessage("IOError is expected!").fail();
1179         }
1180         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
1181         catch (Error error) {
1182             assertWithMessage("Error cause differs from IOError")
1183                     .that(error.getCause())
1184                     .isInstanceOf(IOError.class);
1185             assertWithMessage("Error message is not expected")
1186                     .that(error)
1187                     .hasCauseThat()
1188                     .hasCauseThat()
1189                     .hasMessageThat()
1190                     .isEqualTo(errorMessage);
1191 
1192             // destroy is called by Main
1193             checker.destroy();
1194 
1195             final Properties cache = new Properties();
1196             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1197                 cache.load(reader);
1198             }
1199 
1200             assertWithMessage("Cache has unexpected size")
1201                     .that(cache)
1202                     .hasSize(1);
1203             assertWithMessage("testFile is not in cache")
1204                 .that(cache.getProperty("testFile"))
1205                 .isNull();
1206         }
1207     }
1208 
1209     /**
1210      * Test doesn't need to be serialized.
1211      *
1212      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1213      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1214      *      for test does not require serialization
1215      */
1216     @Test
1217     public void testCatchErrorWithCacheWithNoFileName() throws Exception {
1218         final File cacheFile = createTempFile("junit");
1219 
1220         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1221         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1222         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1223 
1224         final String errorMessage = "Java Virtual Machine is broken"
1225             + " or has run out of resources necessary for it to continue operating.";
1226         final Error expectedError = new IOError(new InternalError(errorMessage));
1227 
1228         final File mock = new File("testFile") {
1229             private static final long serialVersionUID = 1L;
1230 
1231             /**
1232              * Test is checking catch clause when exception is thrown.
1233              *
1234              * @noinspection ProhibitedExceptionThrown
1235              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1236              *      throw exception as part of test
1237              */
1238             @Override
1239             public String getAbsolutePath() {
1240                 throw expectedError;
1241             }
1242         };
1243 
1244         final Checker checker = new Checker();
1245         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1246         checker.configure(checkerConfig);
1247         final List<File> filesToProcess = new ArrayList<>();
1248         filesToProcess.add(mock);
1249         try {
1250             checker.process(filesToProcess);
1251             assertWithMessage("IOError is expected!").fail();
1252         }
1253         // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage.
1254         catch (Error error) {
1255             assertWithMessage("Error cause differs from IOError")
1256                     .that(error)
1257                     .hasCauseThat()
1258                     .isInstanceOf(IOError.class);
1259             assertWithMessage("Error message is not expected")
1260                     .that(error)
1261                     .hasCauseThat()
1262                     .hasCauseThat()
1263                     .hasMessageThat()
1264                     .isEqualTo(errorMessage);
1265 
1266             // destroy is called by Main
1267             checker.destroy();
1268 
1269             final Properties cache = new Properties();
1270             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1271                 cache.load(reader);
1272             }
1273 
1274             assertWithMessage("Cache has unexpected size")
1275                     .that(cache)
1276                     .hasSize(1);
1277         }
1278     }
1279 
1280     /**
1281      * Test doesn't need to be serialized.
1282      *
1283      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1284      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1285      *      for test does not require serialization
1286      */
1287     @Test
1288     public void testExceptionWithNoFileName() {
1289         final String errorMessage = "Security Exception";
1290         final RuntimeException expectedError = new SecurityException(errorMessage);
1291 
1292         final File mock = new File("testFile") {
1293             private static final long serialVersionUID = 1L;
1294 
1295             /**
1296              * Test is checking catch clause when exception is thrown.
1297              *
1298              * @noinspection ProhibitedExceptionThrown
1299              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1300              *      throw exception as part of test
1301              */
1302             @Override
1303             public String getAbsolutePath() {
1304                 throw expectedError;
1305             }
1306         };
1307 
1308         final Checker checker = new Checker();
1309         final List<File> filesToProcess = new ArrayList<>();
1310         filesToProcess.add(mock);
1311         try {
1312             checker.process(filesToProcess);
1313             assertWithMessage("SecurityException is expected!").fail();
1314         }
1315         catch (CheckstyleException exc) {
1316             assertWithMessage("Error cause differs from SecurityException")
1317                     .that(exc)
1318                     .hasCauseThat()
1319                     .isInstanceOf(SecurityException.class);
1320             assertWithMessage("Error message is not expected")
1321                     .that(exc)
1322                     .hasCauseThat()
1323                     .hasMessageThat()
1324                     .isEqualTo(errorMessage);
1325         }
1326     }
1327 
1328     /**
1329      * Test doesn't need to be serialized.
1330      *
1331      * @noinspection SerializableInnerClassWithNonSerializableOuterClass
1332      * @noinspectionreason SerializableInnerClassWithNonSerializableOuterClass - mocked file
1333      *      for test does not require serialization
1334      */
1335     @Test
1336     public void testExceptionWithCacheAndNoFileName() throws Exception {
1337         final File cacheFile = createTempFile("junit");
1338 
1339         final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration");
1340         checkerConfig.addProperty("charset", StandardCharsets.UTF_8.name());
1341         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
1342 
1343         final String errorMessage = "Security Exception";
1344         final RuntimeException expectedError = new SecurityException(errorMessage);
1345 
1346         final File mock = new File("testFile") {
1347             private static final long serialVersionUID = 1L;
1348 
1349             /**
1350              * Test is checking catch clause when exception is thrown.
1351              *
1352              * @noinspection ProhibitedExceptionThrown
1353              * @noinspectionreason ProhibitedExceptionThrown - we require mocked file to
1354              *      throw exception as part of test
1355              */
1356             @Override
1357             public String getAbsolutePath() {
1358                 throw expectedError;
1359             }
1360         };
1361 
1362         final Checker checker = new Checker();
1363         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1364         checker.configure(checkerConfig);
1365         final List<File> filesToProcess = new ArrayList<>();
1366         filesToProcess.add(mock);
1367         try {
1368             checker.process(filesToProcess);
1369             assertWithMessage("SecurityException is expected!").fail();
1370         }
1371         catch (CheckstyleException exc) {
1372             assertWithMessage("Error cause differs from SecurityException")
1373                     .that(exc)
1374                     .hasCauseThat()
1375                     .isInstanceOf(SecurityException.class);
1376             assertWithMessage("Error message is not expected")
1377                     .that(exc)
1378                     .hasCauseThat()
1379                     .hasMessageThat()
1380                     .isEqualTo(errorMessage);
1381 
1382             // destroy is called by Main
1383             checker.destroy();
1384 
1385             final Properties cache = new Properties();
1386             try (BufferedReader reader = Files.newBufferedReader(cacheFile.toPath())) {
1387                 cache.load(reader);
1388             }
1389 
1390             assertWithMessage("Cache has unexpected size")
1391                     .that(cache)
1392                     .hasSize(1);
1393         }
1394     }
1395 
1396     @Test
1397     public void testHaltOnExceptionOff() throws Exception {
1398         final String filePath = getPath("InputChecker.java");
1399         final String[] expected = {
1400             "1: " + getCheckMessage(EXCEPTION_MSG, "java.lang.IndexOutOfBoundsException: test"),
1401         };
1402 
1403         verifyWithInlineXmlConfig(filePath, expected);
1404     }
1405 
1406     @Test
1407     public void testTabViolationDefault() throws Exception {
1408         final String[] expected = {
1409             "17:17: violation",
1410             "21:49: violation",
1411         };
1412         verifyWithInlineConfigParser(getPath("InputCheckerTabCharacter.java"),
1413             expected);
1414     }
1415 
1416     @Test
1417     public void testTabViolationCustomWidth() throws Exception {
1418         final String[] expected = {
1419             "18:17: violation",
1420             "22:37: violation",
1421         };
1422 
1423         verifyWithInlineXmlConfig(getPath("InputCheckerTabCharacterCustomWidth.java"), expected);
1424     }
1425 
1426     @Test
1427     public void testCheckerProcessCallAllNeededMethodsOfFileSets() throws Exception {
1428         final DummyFileSet fileSet = new DummyFileSet();
1429         final Checker checker = new Checker();
1430         checker.addFileSetCheck(fileSet);
1431         checker.process(Collections.singletonList(new File("dummy.java")));
1432         final List<String> expected =
1433             Arrays.asList("beginProcessing", "finishProcessing", "destroy");
1434         assertWithMessage("Method calls were not expected")
1435             .that(fileSet.getMethodCalls())
1436             .isEqualTo(expected);
1437     }
1438 
1439     @Test
1440     public void testSetFileSetCheckSetsMessageDispatcher() {
1441         final DummyFileSet fileSet = new DummyFileSet();
1442         final Checker checker = new Checker();
1443         checker.addFileSetCheck(fileSet);
1444         assertWithMessage("Message dispatcher was not expected")
1445             .that(fileSet.getInternalMessageDispatcher())
1446             .isEqualTo(checker);
1447     }
1448 
1449     @Test
1450     public void testAddAuditListenerAsChild() throws Exception {
1451         final Checker checker = new Checker();
1452         final DebugAuditAdapter auditAdapter = new DebugAuditAdapter();
1453         final PackageObjectFactory factory = new PackageObjectFactory(
1454                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1455             @Override
1456             public Object createModule(String name) throws CheckstyleException {
1457                 Object adapter = auditAdapter;
1458                 if (!name.equals(DebugAuditAdapter.class.getName())) {
1459                     adapter = super.createModule(name);
1460                 }
1461                 return adapter;
1462             }
1463         };
1464         checker.setModuleFactory(factory);
1465         checker.setupChild(createModuleConfig(DebugAuditAdapter.class));
1466         // Let's try fire some events
1467         checker.process(Collections.singletonList(new File("dummy.java")));
1468         assertWithMessage("Checker.fireAuditStarted() doesn't call listener")
1469                 .that(auditAdapter.wasCalled())
1470                 .isTrue();
1471     }
1472 
1473     @Test
1474     public void testAddBeforeExecutionFileFilterAsChild() throws Exception {
1475         final Checker checker = new Checker();
1476         final TestBeforeExecutionFileFilter fileFilter = new TestBeforeExecutionFileFilter();
1477         final PackageObjectFactory factory = new PackageObjectFactory(
1478                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1479             @Override
1480             public Object createModule(String name) throws CheckstyleException {
1481                 Object filter = fileFilter;
1482                 if (!name.equals(TestBeforeExecutionFileFilter.class.getName())) {
1483                     filter = super.createModule(name);
1484                 }
1485                 return filter;
1486             }
1487         };
1488         checker.setModuleFactory(factory);
1489         checker.setupChild(createModuleConfig(TestBeforeExecutionFileFilter.class));
1490         checker.process(Collections.singletonList(new File("dummy.java")));
1491         assertWithMessage("Checker.acceptFileStarted() doesn't call listener")
1492                 .that(fileFilter.wasCalled())
1493                 .isTrue();
1494     }
1495 
1496     @Test
1497     public void testFileSetCheckInitWhenAddedAsChild() throws Exception {
1498         final Checker checker = new Checker();
1499         final DummyFileSet fileSet = new DummyFileSet();
1500         final PackageObjectFactory factory = new PackageObjectFactory(
1501                 new HashSet<>(), Thread.currentThread().getContextClassLoader()) {
1502             @Override
1503             public Object createModule(String name) throws CheckstyleException {
1504                 Object check = fileSet;
1505                 if (!name.equals(DummyFileSet.class.getName())) {
1506                     check = super.createModule(name);
1507                 }
1508                 return check;
1509             }
1510         };
1511         checker.setModuleFactory(factory);
1512         checker.finishLocalSetup();
1513         checker.setupChild(createModuleConfig(DummyFileSet.class));
1514         assertWithMessage("FileSetCheck.init() wasn't called")
1515                 .that(fileSet.isInitCalled())
1516                 .isTrue();
1517     }
1518 
1519     // -@cs[CheckstyleTestMakeup] must use raw class to directly initialize DefaultLogger
1520     @Test
1521     public void testDefaultLoggerClosesItStreams() throws Exception {
1522         final Checker checker = new Checker();
1523         try (CloseAndFlushTestByteArrayOutputStream testInfoOutputStream =
1524                 new CloseAndFlushTestByteArrayOutputStream();
1525             CloseAndFlushTestByteArrayOutputStream testErrorOutputStream =
1526                 new CloseAndFlushTestByteArrayOutputStream()) {
1527             checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1528             checker.addListener(new DefaultLogger(testInfoOutputStream,
1529                 OutputStreamOptions.CLOSE, testErrorOutputStream, OutputStreamOptions.CLOSE));
1530 
1531             final File tmpFile = createTempFile("file", ".java");
1532 
1533             execute(checker, tmpFile.getPath());
1534 
1535             assertWithMessage("Output stream close count")
1536                     .that(testInfoOutputStream.getCloseCount())
1537                     .isEqualTo(1);
1538             assertWithMessage("Output stream flush count")
1539                     .that(testInfoOutputStream.getFlushCount())
1540                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(3));
1541             assertWithMessage("Error stream close count")
1542                     .that(testErrorOutputStream.getCloseCount())
1543                     .isEqualTo(1);
1544             assertWithMessage("Error stream flush count")
1545                     .that(testErrorOutputStream.getFlushCount())
1546                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(1));
1547         }
1548     }
1549 
1550     // -@cs[CheckstyleTestMakeup] must use raw class to directly initialize DefaultLogger
1551     @Test
1552     public void testXmlLoggerClosesItStreams() throws Exception {
1553         final Checker checker = new Checker();
1554         try (CloseAndFlushTestByteArrayOutputStream testInfoOutputStream =
1555                 new CloseAndFlushTestByteArrayOutputStream()) {
1556             checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1557             checker.addListener(new XMLLogger(testInfoOutputStream, OutputStreamOptions.CLOSE));
1558 
1559             final File tmpFile = createTempFile("file", ".java");
1560 
1561             execute(checker, tmpFile.getPath(), tmpFile.getPath());
1562 
1563             assertWithMessage("Output stream close count")
1564                     .that(testInfoOutputStream.getCloseCount())
1565                     .isEqualTo(1);
1566             assertWithMessage("Output stream flush count")
1567                     .that(testInfoOutputStream.getFlushCount())
1568                     .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(0));
1569         }
1570     }
1571 
1572     @Test
1573     public void testDuplicatedModule() throws Exception {
1574         // we need to test a module with two instances, one with id and the other not
1575         final DefaultConfiguration moduleConfig1 =
1576                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1577         final DefaultConfiguration moduleConfig2 =
1578                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1579         moduleConfig2.addProperty("id", "ModuleId");
1580         final DefaultConfiguration root = new DefaultConfiguration("root");
1581         root.addChild(moduleConfig1);
1582         root.addChild(moduleConfig2);
1583         final Checker checker = new Checker();
1584         checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
1585         checker.configure(root);
1586         // BriefUtLogger does not print the module name or id postfix,
1587         // so we need to set logger manually
1588         final ByteArrayOutputStream out = TestUtil.getInternalState(this, "stream");
1589         final DefaultLogger logger = new DefaultLogger(out, OutputStreamOptions.CLOSE, out,
1590                 OutputStreamOptions.NONE, new AuditEventDefaultFormatter());
1591         checker.addListener(logger);
1592 
1593         final File tempFile = createTempFile("file", ".java");
1594         try (BufferedWriter bufferedWriter = Files.newBufferedWriter(tempFile.toPath())) {
1595             bufferedWriter.write(';');
1596         }
1597         final String path = tempFile.getPath();
1598         final String violationMessage =
1599                 getCheckMessage(NewlineAtEndOfFileCheck.class, MSG_KEY_NO_NEWLINE_EOF);
1600         final String[] expected = {
1601             "1: " + violationMessage + " [NewlineAtEndOfFile]",
1602             "1: " + violationMessage + " [ModuleId]",
1603         };
1604 
1605         // super.verify does not work here, for we change the logger
1606         out.flush();
1607         final int errs = checker.process(Collections.singletonList(new File(path)));
1608         try (ByteArrayInputStream inputStream =
1609                 new ByteArrayInputStream(out.toByteArray());
1610             LineNumberReader lnr = new LineNumberReader(
1611                 new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
1612             // we need to ignore the unrelated lines
1613             final List<String> actual = lnr.lines()
1614                     .filter(line -> !getCheckMessage(AUDIT_STARTED_MESSAGE).equals(line))
1615                     .filter(line -> !getCheckMessage(AUDIT_FINISHED_MESSAGE).equals(line))
1616                     .limit(expected.length)
1617                     .sorted()
1618                     .collect(Collectors.toUnmodifiableList());
1619             Arrays.sort(expected);
1620 
1621             for (int i = 0; i < expected.length; i++) {
1622                 final String expectedResult = "[ERROR] " + path + ":" + expected[i];
1623                 assertWithMessage("error message " + i)
1624                         .that(actual.get(i))
1625                         .isEqualTo(expectedResult);
1626             }
1627 
1628             assertWithMessage("unexpected output: " + lnr.readLine())
1629                     .that(errs)
1630                     .isEqualTo(expected.length);
1631         }
1632 
1633         checker.destroy();
1634     }
1635 
1636     @Test
1637     public void testCachedFile() throws Exception {
1638         final Checker checker = createChecker(createModuleConfig(TranslationCheck.class));
1639         final OutputStream infoStream = new ByteArrayOutputStream();
1640         final OutputStream errorStream = new ByteArrayOutputStream();
1641         final DefaultLoggerWithCounter loggerWithCounter =
1642             new DefaultLoggerWithCounter(infoStream, OutputStreamOptions.CLOSE,
1643                                          errorStream, OutputStreamOptions.CLOSE);
1644         checker.addListener(loggerWithCounter);
1645         final File cacheFile = createTempFile("cacheFile", ".txt");
1646         checker.setCacheFile(cacheFile.getAbsolutePath());
1647 
1648         final File testFile = createTempFile("testFile", ".java");
1649         final List<File> files = List.of(testFile, testFile);
1650         checker.process(files);
1651 
1652         assertWithMessage("Cached file should not be processed twice")
1653             .that(loggerWithCounter.fileStartedCount)
1654             .isEqualTo(1);
1655 
1656         checker.destroy();
1657     }
1658 
1659     @Test
1660     public void testUnmappableCharacters() throws Exception {
1661         final String[] expected = {
1662             "14: " + getCheckMessage(LineLengthCheck.class, MSG_KEY, 80, 225),
1663         };
1664 
1665         verifyWithInlineXmlConfig(getPath("InputCheckerTestCharset.java"),
1666                 expected);
1667     }
1668 
1669     /**
1670      * This tests uses 'verify' method, because it needs some config
1671      * to be executed on non-existing Input file,
1672      * but BDD style methods need config in existing file.
1673      *
1674      * @throws Exception exception
1675      */
1676     @SuppressForbidden
1677     @Test
1678     public void testViolationMessageOnIoException() throws Exception {
1679         final DefaultConfiguration checkConfig =
1680                 createModuleConfig(CheckWhichThrowsError.class);
1681 
1682         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
1683         treeWalkerConfig.addChild(checkConfig);
1684 
1685         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
1686         checkerConfig.addChild(treeWalkerConfig);
1687 
1688         checkerConfig.addProperty("haltOnException", "false");
1689         final File file = new File("InputNonChecker.java");
1690         final String filePath = file.getAbsolutePath();
1691         final String[] expected = {
1692             "1: " + getCheckMessage(EXCEPTION_MSG, filePath
1693                         + " (No such file or directory)"),
1694         };
1695 
1696         verify(checkerConfig, filePath, expected);
1697     }
1698 
1699     /**
1700      * Reason of non-Input based testing:
1701      * There are bunch of asserts that expects full path to file,
1702      * usage of "basedir" make it stripped and we need put everywhere code like
1703      * <pre>CommonUtil.relativizePath(checker.getConfiguration().getProperty("basedir"), file)</pre>
1704      * but Checker object is not always available in code.
1705      * Propagating it in all code methods will complicate code.
1706      */
1707     @Test
1708     public void testRelativizedFileExclusion() throws Exception {
1709         final DefaultConfiguration newLineAtEndOfFileConfig =
1710                 createModuleConfig(NewlineAtEndOfFileCheck.class);
1711 
1712         final DefaultConfiguration beforeExecutionExclusionFileFilterConfig =
1713                 createModuleConfig(BeforeExecutionExclusionFileFilter.class);
1714 
1715         beforeExecutionExclusionFileFilterConfig.addProperty("fileNamePattern",
1716                         "^(?!InputCheckerTestExcludeRelativizedFile.*\\.java).*");
1717 
1718         final DefaultConfiguration checkerConfig = createRootConfig(null);
1719         checkerConfig.addChild(newLineAtEndOfFileConfig);
1720         checkerConfig.addChild(beforeExecutionExclusionFileFilterConfig);
1721 
1722         // -@cs[CheckstyleTestMakeup] Needs to be fixed.
1723         checkerConfig.addProperty("basedir",
1724                 temporaryFolder.getPath());
1725 
1726         final String violationMessage =
1727                 getCheckMessage(NewlineAtEndOfFileCheck.class, MSG_KEY_NO_NEWLINE_EOF);
1728 
1729         final String[] expected = {
1730             "1: " + violationMessage,
1731         };
1732 
1733         final File tempFile = createTempFile("InputCheckerTestExcludeRelativizedFile", ".java");
1734         try (BufferedWriter bufferedWriter = Files.newBufferedWriter(tempFile.toPath())) {
1735             bufferedWriter.write(';');
1736         }
1737 
1738         final File[] processedFiles = {tempFile};
1739 
1740         verify(createChecker(checkerConfig), processedFiles,
1741                 tempFile.getName(), expected);
1742     }
1743 
1744     public static class DefaultLoggerWithCounter extends DefaultLogger {
1745 
1746         private int fileStartedCount;
1747 
1748         public DefaultLoggerWithCounter(OutputStream infoStream,
1749                                         OutputStreamOptions infoStreamOptions,
1750                                         OutputStream errorStream,
1751                                         OutputStreamOptions errorStreamOptions) {
1752             super(infoStream, infoStreamOptions, errorStream, errorStreamOptions);
1753         }
1754 
1755         @Override
1756         public void fileStarted(AuditEvent event) {
1757             fileStartedCount++;
1758         }
1759     }
1760 
1761     public static class DummyFilter implements Filter {
1762 
1763         @Override
1764         public boolean accept(AuditEvent event) {
1765             return false;
1766         }
1767 
1768     }
1769 
1770     public static class DummyFileSetViolationCheck extends AbstractFileSetCheck
1771         implements ExternalResourceHolder {
1772 
1773         @Override
1774         protected void processFiltered(File file, FileText fileText) {
1775             log(1, "test");
1776         }
1777 
1778         @Override
1779         public Set<String> getExternalResourceLocations() {
1780             final Set<String> externalResourceLocation = new HashSet<>(1);
1781             externalResourceLocation.add("non_existent_external_resource.xml");
1782             return externalResourceLocation;
1783         }
1784 
1785     }
1786 
1787     public static class DummyFilterSet extends FilterSet implements ExternalResourceHolder {
1788 
1789         @Override
1790         public Set<String> getExternalResourceLocations() {
1791             final Set<String> strings = new HashSet<>();
1792             strings.add("test");
1793             return strings;
1794         }
1795 
1796     }
1797 
1798     public static final class DynamicalResourceHolderCheck extends AbstractFileSetCheck
1799         implements ExternalResourceHolder {
1800 
1801         private String firstExternalResourceLocation;
1802         private String secondExternalResourceLocation;
1803 
1804         public void setFirstExternalResourceLocation(String firstExternalResourceLocation) {
1805             this.firstExternalResourceLocation = firstExternalResourceLocation;
1806         }
1807 
1808         public void setSecondExternalResourceLocation(String secondExternalResourceLocation) {
1809             this.secondExternalResourceLocation = secondExternalResourceLocation;
1810         }
1811 
1812         @Override
1813         protected void processFiltered(File file, FileText fileText) {
1814             // there is no need in implementation of the method
1815         }
1816 
1817         @Override
1818         public Set<String> getExternalResourceLocations() {
1819             final Set<String> locations = new HashSet<>();
1820             locations.add(firstExternalResourceLocation);
1821             // Attempt to change the behaviour of the check dynamically
1822             if (secondExternalResourceLocation != null) {
1823                 locations.add(secondExternalResourceLocation);
1824             }
1825             return locations;
1826         }
1827 
1828     }
1829 
1830     public static class CheckWhichDoesNotRequireCommentNodes extends AbstractCheck {
1831 
1832         /** Number of children of method definition token. */
1833         private static final int METHOD_DEF_CHILD_COUNT = 7;
1834 
1835         @Override
1836         public int[] getDefaultTokens() {
1837             return new int[] {TokenTypes.METHOD_DEF};
1838         }
1839 
1840         @Override
1841         public int[] getAcceptableTokens() {
1842             return new int[] {TokenTypes.METHOD_DEF};
1843         }
1844 
1845         @Override
1846         public int[] getRequiredTokens() {
1847             return new int[] {TokenTypes.METHOD_DEF};
1848         }
1849 
1850         @Override
1851         public void visitToken(DetailAST ast) {
1852             if (ast.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(
1853                     TokenTypes.BLOCK_COMMENT_BEGIN) != null) {
1854                 log(ast, "AST has incorrect structure structure."
1855                     + " The check does not require comment nodes but there were comment nodes"
1856                     + " in the AST.");
1857             }
1858             final int childCount = ast.getChildCount();
1859             if (childCount != METHOD_DEF_CHILD_COUNT) {
1860                 final String msg = String.format(Locale.ENGLISH,
1861                     "AST node in no comment tree has wrong number of children. "
1862                             + "Expected is %d but was %d",
1863                     METHOD_DEF_CHILD_COUNT, childCount);
1864                 log(ast, msg);
1865             }
1866             // count children where comment lives
1867             int actualChildCount = 0;
1868             for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child =
1869                     child.getNextSibling()) {
1870                 actualChildCount++;
1871             }
1872             final int cacheChildCount = ast.getFirstChild().getChildCount();
1873             if (cacheChildCount != actualChildCount) {
1874                 final String msg = String.format(Locale.ENGLISH,
1875                         "AST node with no comment has wrong number of children. "
1876                                 + "Expected is %d but was %d",
1877                         cacheChildCount, actualChildCount);
1878                 log(ast, msg);
1879             }
1880         }
1881 
1882     }
1883 
1884     public static class CheckWhichRequiresCommentNodes extends AbstractCheck {
1885 
1886         /** Number of children of method definition token. */
1887         private static final int METHOD_DEF_CHILD_COUNT = 7;
1888 
1889         @Override
1890         public boolean isCommentNodesRequired() {
1891             return true;
1892         }
1893 
1894         @Override
1895         public int[] getDefaultTokens() {
1896             return new int[] {TokenTypes.METHOD_DEF};
1897         }
1898 
1899         @Override
1900         public int[] getAcceptableTokens() {
1901             return new int[] {TokenTypes.METHOD_DEF};
1902         }
1903 
1904         @Override
1905         public int[] getRequiredTokens() {
1906             return new int[] {TokenTypes.METHOD_DEF};
1907         }
1908 
1909         // Locale.ENGLISH until #12104
1910         @Override
1911         public void visitToken(DetailAST ast) {
1912             if (ast.findFirstToken(TokenTypes.MODIFIERS).findFirstToken(
1913                     TokenTypes.BLOCK_COMMENT_BEGIN) == null) {
1914                 log(ast, "Incorrect AST structure.");
1915             }
1916             final int childCount = ast.getChildCount();
1917             if (childCount != METHOD_DEF_CHILD_COUNT) {
1918                 final String msg = String.format(Locale.ENGLISH,
1919                     "AST node in comment tree has wrong number of children. "
1920                             + "Expected is %d but was %d",
1921                     METHOD_DEF_CHILD_COUNT, childCount);
1922                 log(ast, msg);
1923             }
1924             // count children where comment lives
1925             int actualChildCount = 0;
1926             for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child =
1927                     child.getNextSibling()) {
1928                 actualChildCount++;
1929             }
1930             final int cacheChildCount = ast.getFirstChild().getChildCount();
1931             if (cacheChildCount != actualChildCount) {
1932                 final String msg = String.format(Locale.ENGLISH,
1933                         "AST node with comment has wrong number of children. "
1934                                 + "Expected is %d but was %d",
1935                         cacheChildCount, actualChildCount);
1936                 log(ast, msg);
1937             }
1938         }
1939 
1940     }
1941 
1942     public static final class DummyFileSet extends AbstractFileSetCheck {
1943 
1944         private final List<String> methodCalls = new ArrayList<>();
1945 
1946         private boolean initCalled;
1947 
1948         @Override
1949         public void init() {
1950             super.init();
1951             initCalled = true;
1952         }
1953 
1954         @Override
1955         public void beginProcessing(String charset) {
1956             methodCalls.add("beginProcessing");
1957             super.beginProcessing(charset);
1958         }
1959 
1960         @Override
1961         public void finishProcessing() {
1962             methodCalls.add("finishProcessing");
1963             super.finishProcessing();
1964         }
1965 
1966         @Override
1967         protected void processFiltered(File file, FileText fileText) {
1968             methodCalls.add("processFiltered");
1969         }
1970 
1971         @Override
1972         public void destroy() {
1973             methodCalls.add("destroy");
1974             super.destroy();
1975         }
1976 
1977         public List<String> getMethodCalls() {
1978             return Collections.unmodifiableList(methodCalls);
1979         }
1980 
1981         public boolean isInitCalled() {
1982             return initCalled;
1983         }
1984 
1985         public MessageDispatcher getInternalMessageDispatcher() {
1986             return getMessageDispatcher();
1987         }
1988 
1989     }
1990 
1991 }