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