View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck.MSG_INVALID_PATTERN;
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertTrue;
26  import static org.junit.Assert.fail;
27  
28  import java.io.BufferedWriter;
29  import java.io.File;
30  import java.io.FileOutputStream;
31  import java.io.OutputStreamWriter;
32  import java.io.Writer;
33  import java.lang.reflect.Method;
34  import java.nio.charset.StandardCharsets;
35  import java.nio.file.Files;
36  import java.util.ArrayList;
37  import java.util.Arrays;
38  import java.util.Collection;
39  import java.util.HashSet;
40  import java.util.List;
41  import java.util.Optional;
42  import java.util.regex.Matcher;
43  import java.util.regex.Pattern;
44  
45  import org.junit.Rule;
46  import org.junit.Test;
47  import org.junit.rules.TemporaryFolder;
48  import org.mockito.internal.util.Checks;
49  import org.powermock.reflect.Whitebox;
50  
51  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
52  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
53  import com.puppycrawl.tools.checkstyle.api.Configuration;
54  import com.puppycrawl.tools.checkstyle.api.Context;
55  import com.puppycrawl.tools.checkstyle.api.DetailAST;
56  import com.puppycrawl.tools.checkstyle.api.FileText;
57  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
58  import com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck;
59  import com.puppycrawl.tools.checkstyle.checks.indentation.CommentsIndentationCheck;
60  import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocPackageCheck;
61  import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocParagraphCheck;
62  import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
63  import com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck;
64  import com.puppycrawl.tools.checkstyle.checks.naming.TypeNameCheck;
65  import com.puppycrawl.tools.checkstyle.filters.SuppressionCommentFilter;
66  import com.puppycrawl.tools.checkstyle.filters.SuppressionXpathFilter;
67  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
68  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
69  
70  public class TreeWalkerTest extends AbstractModuleTestSupport {
71  
72      @Rule
73      public final TemporaryFolder temporaryFolder = new TemporaryFolder();
74  
75      @Override
76      protected String getPackageLocation() {
77          return "com/puppycrawl/tools/checkstyle/treewalker";
78      }
79  
80      @Test
81      public void testProperFileExtension() throws Exception {
82          final DefaultConfiguration checkConfig =
83                  createModuleConfig(ConstantNameCheck.class);
84          final File file = temporaryFolder.newFile("file.java");
85          try (Writer writer = new BufferedWriter(
86                  new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
87              final String content = "public class Main { public static final int k = 5 + 4; }";
88              writer.write(content);
89          }
90          final String[] expected1 = {
91              "1:45: " + getCheckMessage(ConstantNameCheck.class,
92                      MSG_INVALID_PATTERN, "k", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
93          };
94          verify(checkConfig, file.getPath(), expected1);
95      }
96  
97      @Test
98      public void testImproperFileExtension() throws Exception {
99          final DefaultConfiguration checkConfig =
100                 createModuleConfig(ConstantNameCheck.class);
101         final File file = temporaryFolder.newFile("file.pdf");
102         try (BufferedWriter writer = new BufferedWriter(
103                 new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
104             final String content = "public class Main { public static final int k = 5 + 4; }";
105             writer.write(content);
106         }
107         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
108         verify(checkConfig, file.getPath(), expected);
109     }
110 
111     @Test
112     public void testAcceptableTokens()
113             throws Exception {
114         final DefaultConfiguration checkConfig =
115             createModuleConfig(HiddenFieldCheck.class);
116         checkConfig.addAttribute("tokens", "VARIABLE_DEF, ENUM_DEF, CLASS_DEF, METHOD_DEF,"
117                 + "IMPORT");
118         try {
119             final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
120             verify(checkConfig, getPath("InputTreeWalker.java"), expected);
121             fail("CheckstyleException is expected");
122         }
123         catch (CheckstyleException ex) {
124             final String errorMsg = ex.getMessage();
125             final Pattern expected = Pattern.compile(Pattern.quote("cannot initialize module"
126                     + " com.puppycrawl.tools.checkstyle.TreeWalker - Token ")
127                     + "\"(ENUM_DEF|CLASS_DEF|METHOD_DEF|IMPORT)\""
128                     + Pattern.quote(" was not found in Acceptable tokens list in check"
129                     + " com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck"));
130 
131             final Matcher errorMsgMatcher = expected.matcher(errorMsg);
132             assertTrue("Failure for: " + errorMsg, errorMsgMatcher.matches());
133         }
134     }
135 
136     @Test
137     public void testOnEmptyFile() throws Exception {
138         final DefaultConfiguration checkConfig = createModuleConfig(HiddenFieldCheck.class);
139         final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath();
140         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
141 
142         verify(checkConfig, pathToEmptyFile, expected);
143     }
144 
145     @Test
146     public void testWithCheckNotHavingTreeWalkerAsParent() throws Exception {
147         final DefaultConfiguration checkConfig = createModuleConfig(JavadocPackageCheck.class);
148 
149         try {
150             final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
151             verify(createChecker(checkConfig, ModuleCreationOption.IN_TREEWALKER),
152                     temporaryFolder.newFile().getPath(), expected);
153             fail("CheckstyleException is expected");
154         }
155         catch (CheckstyleException exception) {
156             assertTrue("Error message is unexpected",
157                     exception.getMessage().contains("TreeWalker is not allowed as a parent of"));
158         }
159     }
160 
161     @Test
162     public void testSetupChildExceptions() {
163         final TreeWalker treeWalker = new TreeWalker();
164         final PackageObjectFactory factory = new PackageObjectFactory(
165                 new HashSet<>(), Thread.currentThread().getContextClassLoader());
166         treeWalker.setModuleFactory(factory);
167 
168         final Configuration config = new DefaultConfiguration("java.lang.String");
169         try {
170             treeWalker.setupChild(config);
171             fail("Exception is expected");
172         }
173         catch (CheckstyleException ex) {
174             assertEquals("Error message is not expected",
175                     "TreeWalker is not allowed as a parent of java.lang.String Please review "
176                             + "'Parent Module' section for this Check in web documentation if "
177                             + "Check is standard.", ex.getMessage());
178         }
179     }
180 
181     @Test
182     public void testSettersForParameters() throws Exception {
183         final TreeWalker treeWalker = new TreeWalker();
184         final DefaultConfiguration config = new DefaultConfiguration("default config");
185         treeWalker.setTabWidth(1);
186         treeWalker.configure(config);
187         treeWalker.setCacheFile(temporaryFolder.newFile().getPath());
188 
189         assertEquals("Invalid setter result", 1,
190                 (int) Whitebox.getInternalState(treeWalker, "tabWidth"));
191         assertEquals("Invalid configuration", config,
192             Whitebox.getInternalState(treeWalker, "configuration"));
193     }
194 
195     @Test
196     public void testForInvalidCheckImplementation() throws Exception {
197         final DefaultConfiguration checkConfig = createModuleConfig(BadJavaDocCheck.class);
198         final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath();
199 
200         try {
201             final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
202             verify(checkConfig, pathToEmptyFile, expected);
203             fail("Exception is expected");
204         }
205         catch (CheckstyleException ex) {
206             assertTrue("Error message is unexpected",
207                     ex.getMessage().contains("isCommentNodesRequired"));
208         }
209     }
210 
211     @Test
212     public void testProcessNonJavaFiles() throws Exception {
213         final TreeWalker treeWalker = new TreeWalker();
214         final PackageObjectFactory factory = new PackageObjectFactory(
215             new HashSet<>(), Thread.currentThread().getContextClassLoader());
216         treeWalker.setModuleFactory(factory);
217         treeWalker.configure(new DefaultConfiguration("default config"));
218         final DefaultConfiguration childConfig = createModuleConfig(JavadocParagraphCheck.class);
219         treeWalker.setupChild(childConfig);
220         final File file = new File("input.java");
221         final List<String> lines =
222             new ArrayList<>(Arrays.asList("package com.puppycrawl.tools.checkstyle;", "",
223                 "error public class InputTreeWalkerFileWithViolation {}"));
224         final FileText fileText = new FileText(file, lines);
225         try {
226             treeWalker.processFiltered(file, fileText);
227             fail("Exception expected");
228         }
229         catch (CheckstyleException ex) {
230             assertEquals("Invalid exception message",
231                 "MismatchedTokenException occurred during the analysis of file input.java.",
232                 ex.getMessage());
233         }
234     }
235 
236     @Test
237     public void testProcessNonJavaFilesWithoutException() throws Exception {
238         final TreeWalker treeWalker = new TreeWalker();
239         treeWalker.setTabWidth(1);
240         treeWalker.configure(new DefaultConfiguration("default config"));
241         final File file = new File(getPath("InputTreeWalkerNotJava.xml"));
242         final FileText fileText = new FileText(file, StandardCharsets.ISO_8859_1.name());
243         treeWalker.processFiltered(file, fileText);
244         final Collection<Checks> checks = Whitebox.getInternalState(treeWalker, "ordinaryChecks");
245         assertTrue("No checks -> No parsing", checks.isEmpty());
246     }
247 
248     @Test
249     public void testWithCacheWithNoViolation() throws Exception {
250         final DefaultConfiguration checkConfig = createModuleConfig(HiddenFieldCheck.class);
251         final Checker checker = createChecker(checkConfig);
252         final PackageObjectFactory factory = new PackageObjectFactory(
253             new HashSet<>(), Thread.currentThread().getContextClassLoader());
254         checker.setModuleFactory(factory);
255         final File file = temporaryFolder.newFile("file.java");
256         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
257 
258         verify(checker, file.getPath(), expected);
259     }
260 
261     @Test
262     public void testProcessWithParserThrowable() throws Exception {
263         final TreeWalker treeWalker = new TreeWalker();
264         treeWalker.configure(createModuleConfig(TypeNameCheck.class));
265         final PackageObjectFactory factory = new PackageObjectFactory(
266             new HashSet<>(), Thread.currentThread().getContextClassLoader());
267         treeWalker.setModuleFactory(factory);
268         treeWalker.setupChild(createModuleConfig(TypeNameCheck.class));
269         final File file = temporaryFolder.newFile("file.java");
270         final List<String> lines = new ArrayList<>();
271         lines.add(" classD a {} ");
272         final FileText fileText = new FileText(file, lines);
273         try {
274             treeWalker.processFiltered(file, fileText);
275             fail("Exception is expected");
276         }
277         catch (CheckstyleException exception) {
278             assertTrue("Error message is unexpected",
279                     exception.getMessage().contains(
280                     "occurred during the analysis of file"));
281         }
282     }
283 
284     @Test
285     public void testProcessWithRecognitionException() throws Exception {
286         final TreeWalker treeWalker = new TreeWalker();
287         treeWalker.configure(createModuleConfig(TypeNameCheck.class));
288         final PackageObjectFactory factory = new PackageObjectFactory(
289             new HashSet<>(), Thread.currentThread().getContextClassLoader());
290         treeWalker.setModuleFactory(factory);
291         treeWalker.setupChild(createModuleConfig(TypeNameCheck.class));
292         final File file = temporaryFolder.newFile("file.java");
293         final List<String> lines = new ArrayList<>();
294         lines.add(" class a%$# {} ");
295         final FileText fileText = new FileText(file, lines);
296         try {
297             treeWalker.processFiltered(file, fileText);
298             fail("Exception is expected");
299         }
300         catch (CheckstyleException exception) {
301             assertTrue("Error message is unexpected",
302                     exception.getMessage().contains(
303                     "TokenStreamRecognitionException occurred during the analysis of file"));
304         }
305     }
306 
307     @Test
308     public void testRequiredTokenIsNotInDefaultTokens() throws Exception {
309         final DefaultConfiguration checkConfig =
310             createModuleConfig(RequiredTokenIsNotInDefaultsCheck.class);
311         final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath();
312 
313         try {
314             final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
315             verify(checkConfig, pathToEmptyFile, expected);
316             fail("CheckstyleException is expected");
317         }
318         catch (CheckstyleException ex) {
319             assertTrue("Error message is unexpected",
320                     ex.getMessage().startsWith("cannot initialize module"
321                 + " com.puppycrawl.tools.checkstyle.TreeWalker - Token \""
322                 + TokenTypes.ASSIGN + "\" from required"
323                 + " tokens was not found in default tokens list in check"));
324         }
325     }
326 
327     @Test
328     public void testRequiredTokenIsEmptyIntArray() throws Exception {
329         final DefaultConfiguration checkConfig =
330             createModuleConfig(RequiredTokenIsEmptyIntArray.class);
331         final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath();
332 
333         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
334         verify(checkConfig, pathToEmptyFile, expected);
335     }
336 
337     @Test
338     public void testBehaviourWithZeroChecks() throws Exception {
339         final TreeWalker treeWalker = new TreeWalker();
340         final PackageObjectFactory factory = new PackageObjectFactory(
341                 new HashSet<>(), Thread.currentThread().getContextClassLoader());
342         treeWalker.setModuleFactory(factory);
343         // create file that should throw exception
344         final File file = temporaryFolder.newFile("file.java");
345         final FileText fileText = new FileText(file, new ArrayList<>());
346 
347         treeWalker.processFiltered(file, fileText);
348         final Collection<Checks> checks = Whitebox.getInternalState(treeWalker, "ordinaryChecks");
349         assertTrue("No checks -> No parsing", checks.isEmpty());
350     }
351 
352     @Test
353     public void testBehaviourWithOnlyOrdinaryChecks() throws Exception {
354         final TreeWalker treeWalker = new TreeWalker();
355         treeWalker.configure(createModuleConfig(TypeNameCheck.class));
356         final PackageObjectFactory factory = new PackageObjectFactory(
357                 new HashSet<>(), Thread.currentThread().getContextClassLoader());
358         treeWalker.setModuleFactory(factory);
359         treeWalker.setupChild(createModuleConfig(TypeNameCheck.class));
360         final File file = temporaryFolder.newFile("file.java");
361         final List<String> lines = new ArrayList<>();
362         lines.add(" class a%$# {} ");
363         final FileText fileText = new FileText(file, lines);
364 
365         try {
366             treeWalker.processFiltered(file, fileText);
367             fail("file is not compilable, exception is expected");
368         }
369         catch (CheckstyleException exception) {
370             final String message =
371                     "TokenStreamRecognitionException occurred during the analysis of file";
372             assertTrue("Error message is unexpected",
373                     exception.getMessage().contains(message));
374         }
375     }
376 
377     @Test
378     public void testBehaviourWithOnlyCommentChecks() throws Exception {
379         final TreeWalker treeWalker = new TreeWalker();
380         treeWalker.configure(createModuleConfig(CommentsIndentationCheck.class));
381         final PackageObjectFactory factory = new PackageObjectFactory(
382                 new HashSet<>(), Thread.currentThread().getContextClassLoader());
383         treeWalker.setModuleFactory(factory);
384         treeWalker.setupChild(createModuleConfig(CommentsIndentationCheck.class));
385         final File file = temporaryFolder.newFile("file.java");
386         final List<String> lines = new ArrayList<>();
387         lines.add(" class a%$# {} ");
388         final FileText fileText = new FileText(file, lines);
389 
390         try {
391             treeWalker.processFiltered(file, fileText);
392             fail("file is not compilable, exception is expected");
393         }
394         catch (CheckstyleException exception) {
395             final String message =
396                     "TokenStreamRecognitionException occurred during the analysis of file";
397             assertTrue("Error message is unexpected",
398                     exception.getMessage().contains(message));
399         }
400     }
401 
402     @Test
403     public void testBehaviourWithOrdinaryAndCommentChecks() throws Exception {
404         final TreeWalker treeWalker = new TreeWalker();
405         treeWalker.configure(createModuleConfig(TypeNameCheck.class));
406         treeWalker.configure(createModuleConfig(CommentsIndentationCheck.class));
407         final PackageObjectFactory factory = new PackageObjectFactory(
408                 new HashSet<>(), Thread.currentThread().getContextClassLoader());
409         treeWalker.setModuleFactory(factory);
410         treeWalker.setupChild(createModuleConfig(TypeNameCheck.class));
411         treeWalker.setupChild(createModuleConfig(CommentsIndentationCheck.class));
412         final File file = temporaryFolder.newFile("file.java");
413         final List<String> lines = new ArrayList<>();
414         lines.add(" class a%$# {} ");
415         final FileText fileText = new FileText(file, lines);
416 
417         try {
418             treeWalker.processFiltered(file, fileText);
419             fail("file is not compilable, exception is expected");
420         }
421         catch (CheckstyleException exception) {
422             final String message =
423                     "TokenStreamRecognitionException occurred during the analysis of file";
424             assertTrue("Error message is unexpected",
425                     exception.getMessage().contains(message));
426         }
427     }
428 
429     @Test
430     public void testBehaviourWithChecksAndFilters() throws Exception {
431         final DefaultConfiguration filterConfig =
432                 createModuleConfig(SuppressionCommentFilter.class);
433         filterConfig.addAttribute("checkCPP", "false");
434 
435         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
436         treeWalkerConfig.addChild(createModuleConfig(MemberNameCheck.class));
437         treeWalkerConfig.addChild(filterConfig);
438 
439         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
440 
441         final File file = new File(getPath("InputTreeWalkerSuppressionCommentFilter.java"));
442 
443         final String[] expected = {
444             "9:17: " + getCheckMessage(MemberNameCheck.class, "name.invalidPattern", "P",
445                     "^[a-z][a-zA-Z0-9]*$"),
446             "4:17: " + getCheckMessage(MemberNameCheck.class, "name.invalidPattern", "I",
447                     "^[a-z][a-zA-Z0-9]*$"),
448         };
449 
450         verify(checkerConfig,
451                 file.getPath(),
452                 expected);
453     }
454 
455     @Test
456     public void testAppendHiddenBlockCommentNodes() throws Exception {
457         final DetailAST root =
458             TestUtil.parseFile(new File(getPath("InputTreeWalkerHiddenComments.java")));
459 
460         final Optional<DetailAST> blockComment = TestUtil.findTokenInAstByPredicate(root,
461             ast -> ast.getType() == TokenTypes.BLOCK_COMMENT_BEGIN);
462 
463         assertTrue("Block comment should be present", blockComment.isPresent());
464 
465         final DetailAST commentContent = blockComment.get().getFirstChild();
466         final DetailAST commentEnd = blockComment.get().getLastChild();
467 
468         assertEquals("Unexpected line number", 3, commentContent.getLineNo());
469         assertEquals("Unexpected column number", 2, commentContent.getColumnNo());
470         assertEquals("Unexpected line number", 9, commentEnd.getLineNo());
471         assertEquals("Unexpected column number", 1, commentEnd.getColumnNo());
472     }
473 
474     @Test
475     public void testAppendHiddenSingleLineCommentNodes() throws Exception {
476         final DetailAST root =
477             TestUtil.parseFile(new File(getPath("InputTreeWalkerHiddenComments.java")));
478 
479         final Optional<DetailAST> singleLineComment = TestUtil.findTokenInAstByPredicate(root,
480             ast -> ast.getType() == TokenTypes.SINGLE_LINE_COMMENT);
481         assertTrue("Single line comment should be present", singleLineComment.isPresent());
482 
483         final DetailAST commentContent = singleLineComment.get().getFirstChild();
484 
485         assertEquals("Unexpected token type", TokenTypes.COMMENT_CONTENT, commentContent.getType());
486         assertEquals("Unexpected line number", 13, commentContent.getLineNo());
487         assertEquals("Unexpected column number", 2, commentContent.getColumnNo());
488         assertTrue("Unexpected comment content",
489             commentContent.getText().startsWith(" inline comment"));
490     }
491 
492     @Test
493     public void testFinishLocalSetupFullyInitialized() {
494         final TreeWalker treeWalker = new TreeWalker();
495         final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
496         treeWalker.setClassLoader(contextClassLoader);
497         treeWalker.setSeverity("error");
498         treeWalker.setTabWidth(100);
499         treeWalker.finishLocalSetup();
500 
501         final Context context = Whitebox.getInternalState(treeWalker, "childContext");
502         assertEquals("Classloader object differs from expected",
503                 contextClassLoader, context.get("classLoader"));
504         assertEquals("Severity differs from expected",
505                 "error", context.get("severity"));
506         assertEquals("Tab width differs from expected",
507                 String.valueOf(100), context.get("tabWidth"));
508     }
509 
510     @Test
511     public void testCheckInitIsCalledInTreeWalker() throws Exception {
512         final DefaultConfiguration checkConfig =
513                 createModuleConfig(VerifyInitCheck.class);
514         final File file = temporaryFolder.newFile("file.pdf");
515         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
516         verify(checkConfig, file.getPath(), expected);
517         assertTrue("Init was not called", VerifyInitCheck.isInitWasCalled());
518     }
519 
520     @Test
521     public void testCheckDestroyIsCalledInTreeWalker() throws Exception {
522         VerifyDestroyCheck.resetDestroyWasCalled();
523         final DefaultConfiguration checkConfig =
524                 createModuleConfig(VerifyDestroyCheck.class);
525         final File file = temporaryFolder.newFile("file.pdf");
526         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
527         verify(checkConfig, file.getPath(), expected);
528         assertTrue("Destroy was not called", VerifyDestroyCheck.isDestroyWasCalled());
529     }
530 
531     @Test
532     public void testCommentCheckDestroyIsCalledInTreeWalker() throws Exception {
533         VerifyDestroyCheck.resetDestroyWasCalled();
534         final DefaultConfiguration checkConfig =
535                 createModuleConfig(VerifyDestroyCommentCheck.class);
536         final File file = temporaryFolder.newFile("file.pdf");
537         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
538         verify(checkConfig, file.getPath(), expected);
539         assertTrue("Destroy was not called", VerifyDestroyCheck.isDestroyWasCalled());
540     }
541 
542     @Test
543     public void testCacheWhenFileExternalResourceContentDoesNotChange() throws Exception {
544         final DefaultConfiguration filterConfig = createModuleConfig(SuppressionXpathFilter.class);
545         filterConfig.addAttribute("file", getPath("InputTreeWalkerSuppressionXpathFilter.xml"));
546         final DefaultConfiguration treeWalkerConfig = createModuleConfig(TreeWalker.class);
547         treeWalkerConfig.addChild(filterConfig);
548 
549         final DefaultConfiguration checkerConfig = createRootConfig(treeWalkerConfig);
550         final File cacheFile = temporaryFolder.newFile();
551         checkerConfig.addAttribute("cacheFile", cacheFile.getPath());
552 
553         final String filePath = temporaryFolder.newFile("file.java").getPath();
554         final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
555 
556         verify(checkerConfig, filePath, expected);
557         // One more time to use cache.
558         verify(checkerConfig, filePath, expected);
559 
560         assertTrue("External resource is not present in cache",
561                 new String(Files.readAllBytes(cacheFile.toPath()),
562                         StandardCharsets.UTF_8).contains(
563                                 "InputTreeWalkerSuppressionXpathFilter.xml"));
564     }
565 
566     /**
567      * Could not find proper test case to test pitest mutations functionally.
568      * Should be rewritten during grammar update.
569      *
570      * @throws Exception when code tested throws exception
571      */
572     @Test
573     public void testIsPositionGreater() throws Exception {
574         final DetailAST ast1 = createAst(1, 3);
575         final DetailAST ast2 = createAst(1, 2);
576         final DetailAST ast3 = createAst(2, 2);
577 
578         final TreeWalker treeWalker = new TreeWalker();
579         final Method isPositionGreater = Whitebox.getMethod(TreeWalker.class,
580                 "isPositionGreater", DetailAST.class, DetailAST.class);
581 
582         assertTrue("Should return true when lines are equal and column is greater",
583                 (boolean) isPositionGreater.invoke(treeWalker, ast1, ast2));
584         assertFalse("Should return false when lines are equal columns are equal",
585                 (boolean) isPositionGreater.invoke(treeWalker, ast1, ast1));
586         assertTrue("Should return true when line is greater",
587                 (boolean) isPositionGreater.invoke(treeWalker, ast3, ast1));
588     }
589 
590     private static DetailAST createAst(int line, int column) {
591         final DetailAST ast = new DetailAST();
592         ast.setLineNo(line);
593         ast.setColumnNo(column);
594         return ast;
595     }
596 
597     private static class BadJavaDocCheck extends AbstractCheck {
598         @Override
599         public int[] getDefaultTokens() {
600             return getAcceptableTokens();
601         }
602 
603         @Override
604         public int[] getAcceptableTokens() {
605             return new int[] {TokenTypes.SINGLE_LINE_COMMENT};
606         }
607 
608         @Override
609         public int[] getRequiredTokens() {
610             return getAcceptableTokens();
611         }
612     }
613 
614     private static class VerifyInitCheck extends AbstractCheck {
615         private static boolean initWasCalled;
616 
617         @Override
618         public int[] getDefaultTokens() {
619             return CommonUtils.EMPTY_INT_ARRAY;
620         }
621 
622         @Override
623         public int[] getAcceptableTokens() {
624             return getDefaultTokens();
625         }
626 
627         @Override
628         public int[] getRequiredTokens() {
629             return getDefaultTokens();
630         }
631 
632         @Override
633         public void init() {
634             super.init();
635             initWasCalled = true;
636         }
637 
638         public static boolean isInitWasCalled() {
639             return initWasCalled;
640         }
641     }
642 
643     private static class VerifyDestroyCheck extends AbstractCheck {
644         private static boolean destroyWasCalled;
645 
646         @Override
647         public int[] getDefaultTokens() {
648             return CommonUtils.EMPTY_INT_ARRAY;
649         }
650 
651         @Override
652         public int[] getAcceptableTokens() {
653             return getDefaultTokens();
654         }
655 
656         @Override
657         public int[] getRequiredTokens() {
658             return getDefaultTokens();
659         }
660 
661         @Override
662         public void destroy() {
663             super.destroy();
664             destroyWasCalled = true;
665         }
666 
667         public static void resetDestroyWasCalled() {
668             destroyWasCalled = false;
669         }
670 
671         public static boolean isDestroyWasCalled() {
672             return destroyWasCalled;
673         }
674     }
675 
676     private static class VerifyDestroyCommentCheck extends VerifyDestroyCheck {
677         @Override
678         public boolean isCommentNodesRequired() {
679             return true;
680         }
681     }
682 
683     private static class RequiredTokenIsNotInDefaultsCheck extends AbstractCheck {
684         @Override
685         public int[] getRequiredTokens() {
686             return new int[] {TokenTypes.ASSIGN};
687         }
688 
689         @Override
690         public int[] getDefaultTokens() {
691             return new int[] {TokenTypes.ANNOTATION};
692         }
693 
694         @Override
695         public int[] getAcceptableTokens() {
696             return CommonUtils.EMPTY_INT_ARRAY;
697         }
698     }
699 
700     private static class RequiredTokenIsEmptyIntArray extends AbstractCheck {
701         @Override
702         public int[] getRequiredTokens() {
703             return CommonUtils.EMPTY_INT_ARRAY;
704         }
705 
706         @Override
707         public int[] getDefaultTokens() {
708             return new int[] {TokenTypes.ANNOTATION};
709         }
710 
711         @Override
712         public int[] getAcceptableTokens() {
713             return CommonUtils.EMPTY_INT_ARRAY;
714         }
715     }
716 }