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.filters;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck.MSG_KEY;
24  import static com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocMethodCheck.MSG_JAVADOC_MISSING;
25  import static com.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck.MSG_INVALID_PATTERN;
26  
27  import java.io.File;
28  import java.nio.charset.StandardCharsets;
29  import java.util.regex.Pattern;
30  import java.util.regex.PatternSyntaxException;
31  
32  import org.junit.jupiter.api.Test;
33  
34  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
35  import com.puppycrawl.tools.checkstyle.JavaParser;
36  import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
37  import com.puppycrawl.tools.checkstyle.api.FileContents;
38  import com.puppycrawl.tools.checkstyle.api.FileText;
39  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
40  import com.puppycrawl.tools.checkstyle.api.Violation;
41  import com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck;
42  import com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocTypeCheck;
43  import com.puppycrawl.tools.checkstyle.checks.naming.TypeNameCheck;
44  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
45  
46  public class SuppressionXpathSingleFilterTest
47          extends AbstractModuleTestSupport {
48  
49      @Override
50      protected String getPackageLocation() {
51          return "com/puppycrawl/tools/checkstyle/filters/suppressionxpathsinglefilter";
52      }
53  
54      @Test
55      public void testMatching() throws Exception {
56          final String[] expected = {
57              "19:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
58          };
59  
60          final String[] suppressed = {
61              "19:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
62          };
63  
64          verifyFilterWithInlineConfigParser(
65              getPath("InputSuppressionXpathSingleFilterMatchingTokenType.java"), expected,
66              removeSuppressed(expected, suppressed));
67      }
68  
69      @Test
70      public void testNonMatchingTokenType() throws Exception {
71          final String[] expected = {
72              "19:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
73          };
74  
75          final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
76  
77          verifyFilterWithInlineConfigParser(
78              getPath("InputSuppressionXpathSingleFilterNonMatchingTokenType.java"), expected,
79              removeSuppressed(expected, suppressed));
80      }
81  
82      @Test
83      public void testNonMatchingLineNumber() throws Exception {
84          final String[] expected = {
85              "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
86              "21:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
87          };
88  
89          final String[] suppressed = {
90              "21:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
91          };
92  
93          verifyFilterWithInlineConfigParser(
94              getPath("InputSuppressionXpathSingleFilterNonMatchingLineNumber.java"), expected,
95              removeSuppressed(expected, suppressed));
96      }
97  
98      @Test
99      public void testNonMatchingColumnNumber() throws Exception {
100         final String[] expected = {
101             "23:11: " + getCheckMessage(TypeNameCheck.class, MSG_INVALID_PATTERN,
102                                         "testClass", "^[A-Z][a-zA-Z0-9]*$"),
103             "26:11: " + getCheckMessage(TypeNameCheck.class, MSG_INVALID_PATTERN,
104                                         "anotherTestClass", "^[A-Z][a-zA-Z0-9]*$"),
105         };
106         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
107 
108         verifyFilterWithInlineConfigParser(
109             getPath("InputSuppressionXpathSingleFilterNonMatchingColumnNumber.java"), expected,
110             removeSuppressed(expected, suppressed));
111     }
112 
113     @Test
114     public void testComplexQuery() throws Exception {
115         final String[] expected = {
116             "27:21: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "3.14"),
117             "28:16: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "123"),
118             "32:28: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "123"),
119         };
120         final String[] suppressed = {
121             "27:21: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "3.14"),
122         };
123         verifyFilterWithInlineConfigParser(
124             getPath("InputSuppressionXpathSingleFilterComplexQuery.java"), expected,
125             removeSuppressed(expected, suppressed));
126     }
127 
128     @Test
129     public void testIncorrectQuery() {
130         final String xpath = "1@#";
131         try {
132             final Object test = createSuppressionXpathSingleFilter(
133                     "InputSuppressionXpathSingleFilterComplexQuery", "Test",
134                     null, null, xpath);
135             assertWithMessage("Exception was expected but got " + test).fail();
136         }
137         catch (IllegalArgumentException ex) {
138             assertWithMessage("Message should be: Unexpected xpath query")
139                     .that(ex.getMessage())
140                     .contains("Incorrect xpath query");
141         }
142     }
143 
144     @Test
145     public void testNoQuery() throws Exception {
146         final String[] expected = {
147             "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
148         };
149 
150         final String[] suppressed = {
151             "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
152         };
153 
154         verifyFilterWithInlineConfigParser(
155             getPath("InputSuppressionXpathSingleFilterNoQuery.java"), expected,
156             removeSuppressed(expected, suppressed));
157     }
158 
159     @Test
160     public void testNullFileName() throws Exception {
161         final String[] expected = {
162             "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
163         };
164 
165         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
166 
167         verifyFilterWithInlineConfigParser(
168             getPath("InputSuppressionXpathSingleFilterNullFileName.java"), expected,
169             removeSuppressed(expected, suppressed));
170     }
171 
172     @Test
173     public void testNonMatchingFileRegexp() throws Exception {
174         final String[] expected = {
175             "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
176         };
177 
178         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
179 
180         verifyFilterWithInlineConfigParser(
181             getPath("InputSuppressionXpathSingleFilterNonMatchingFileRegexp.java"), expected,
182             removeSuppressed(expected, suppressed));
183     }
184 
185     @Test
186     public void testInvalidFileRegexp() {
187         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
188         try {
189             filter.setFiles("e[l");
190             assertWithMessage("PatternSyntaxException is expected").fail();
191         }
192         catch (PatternSyntaxException ex) {
193             assertWithMessage("Message should be: Unclosed character class")
194                     .that(ex.getMessage())
195                     .contains("Unclosed character class");
196         }
197     }
198 
199     @Test
200     public void testInvalidCheckRegexp() {
201         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
202         try {
203             filter.setChecks("e[l");
204             assertWithMessage("PatternSyntaxException is expected").fail();
205         }
206         catch (PatternSyntaxException ex) {
207             assertWithMessage("Message should be: Unclosed character class")
208                     .that(ex.getMessage())
209                     .contains("Unclosed character class");
210         }
211     }
212 
213     @Test
214     public void testNullViolation() throws Exception {
215         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
216         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
217 
218         verifyFilterWithInlineConfigParser(
219             getPath("InputSuppressionXpathSingleFilterNullViolation.java"), expected,
220             removeSuppressed(expected, suppressed));
221     }
222 
223     @Test
224     public void testNonMatchingModuleId() throws Exception {
225         final String[] expected = {
226             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
227         };
228 
229         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
230 
231         verifyFilterWithInlineConfigParser(
232             getPath("InputSuppressionXpathSingleFilterNonMatchingModuleId.java"), expected,
233             removeSuppressed(expected, suppressed));
234     }
235 
236     @Test
237     public void testMatchingModuleId() throws Exception {
238         final String[] expected = {
239             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
240         };
241 
242         final String[] suppressed = {
243             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
244         };
245 
246         verifyFilterWithInlineConfigParser(
247             getPath("InputSuppressionXpathSingleFilterMatchingModuleId.java"), expected,
248             removeSuppressed(expected, suppressed));
249     }
250 
251     @Test
252     public void testNonMatchingChecks() throws Exception {
253         final String[] expected = {
254             "19:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
255         };
256 
257         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
258 
259         verifyFilterWithInlineConfigParser(
260             getPath("InputSuppressionXpathSingleFilterNonMatchingCheck.java"), expected,
261             removeSuppressed(expected, suppressed));
262     }
263 
264     @Test
265     public void testNonMatchingFileNameModuleIdAndCheck() throws Exception {
266         final String[] expected = {
267             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
268         };
269         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
270 
271         verifyFilterWithInlineConfigParser(
272             getPath("InputSuppressionXpathSingleFilterNonMatchingFileNameModuleIdAndCheck.java"),
273             expected, removeSuppressed(expected, suppressed));
274     }
275 
276     @Test
277     public void testNullModuleIdAndNonMatchingChecks() throws Exception {
278         final String[] expected = {
279             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
280         };
281         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
282 
283         verifyFilterWithInlineConfigParser(
284             getPath("InputSuppressionXpathSingleFilterNullModuleIdAndNonMatchingCheck.java"),
285             expected, removeSuppressed(expected, suppressed));
286     }
287 
288     @Test
289     public void testDecideByMessage() throws Exception {
290         final String[] expected = {
291             "28:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
292             "30:21: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "3.14"),
293             "31:16: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "123"),
294             "35:28: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "123"),
295         };
296 
297         final String[] suppressed = {
298             "28:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
299         };
300 
301         verifyFilterWithInlineConfigParser(
302             getPath("InputSuppressionXpathSingleFilterDecideByMessage.java"),
303             expected, removeSuppressed(expected, suppressed));
304     }
305 
306     @Test
307     public void testThrowException() throws Exception {
308         final String xpath = "//CLASS_DEF[@text='InputSuppressionXpathSingleFilterComplexQuery']";
309         final SuppressionXpathSingleFilter filter =
310                 createSuppressionXpathSingleFilter("InputSuppressionXpathSingleFilterComplexQuery",
311                         "Test", null, null, xpath);
312         final Violation message =
313                 new Violation(3, 0, TokenTypes.CLASS_DEF, "",
314                         "", null, null, "id19",
315                         getClass(), null);
316         final FileContents fileContents = new FileContents(new FileText(
317             new File(getPath("InputSuppressionXpathSingleFilterComplexQuery.java")),
318             StandardCharsets.UTF_8.name()));
319         final TreeWalkerAuditEvent ev = new TreeWalkerAuditEvent(fileContents,
320                 "InputSuppressionXpathSingleFilterComplexQuery.java", message, null);
321         try {
322             filter.accept(ev);
323             assertWithMessage("Exception is expected").fail();
324         }
325         catch (IllegalStateException ex) {
326             assertWithMessage("Exception message does not match expected one")
327                     .that(ex.getMessage())
328                     .contains("Cannot initialize context and evaluate query");
329         }
330     }
331 
332     @Test
333     public void testAllNullConfiguration() throws Exception {
334         final String[] expected = {
335             "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
336         };
337 
338         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
339 
340         verifyFilterWithInlineConfigParser(
341                 getPath("InputSuppressionXpathSingleFilterAllNullConfiguration.java"),
342                 expected, removeSuppressed(expected, suppressed));
343     }
344 
345     @Test
346     public void testDecideByIdAndExpression() throws Exception {
347         final String[] expected = {
348             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
349         };
350 
351         final String[] suppressed = {
352             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
353         };
354 
355         verifyFilterWithInlineConfigParser(
356                 getPath("InputSuppressionXpathSingleFilterDecideByIdAndExpression.java"),
357                 expected, removeSuppressed(expected, suppressed));
358     }
359 
360     @Test
361     public void testDefaultFileProperty() throws Exception {
362         final String[] expected = {
363             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
364         };
365 
366         final String[] suppressed = {
367             "20:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
368         };
369 
370         verifyFilterWithInlineConfigParser(
371                 getPath("InputSuppressionXpathSingleFilterDefaultFileProperty.java"),
372                 expected, removeSuppressed(expected, suppressed));
373     }
374 
375     @Test
376     public void testDecideByCheck() throws Exception {
377         final String[] expected = {
378             "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
379         };
380 
381         final String[] suppressed = {
382             "18:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
383         };
384 
385         verifyFilterWithInlineConfigParser(
386                 getPath("InputSuppressionXpathSingleFilterDecideByCheck.java"),
387                 expected, removeSuppressed(expected, suppressed));
388     }
389 
390     @Test
391     public void testDecideById() throws Exception {
392         final String[] expected = {
393             "19:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
394         };
395 
396         final String[] suppressed = {
397             "19:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
398         };
399 
400         verifyFilterWithInlineConfigParser(
401                 getPath("InputSuppressionXpathSingleFilterDecideById.java"),
402                 expected, removeSuppressed(expected, suppressed));
403     }
404 
405     /**
406      * This test is required to cover pitest mutation
407      * for reset of 'file' field in SuppressionXpathSingleFilter.
408      * This not possible to reproduce by natural execution of Checkstyle
409      * as config is never changed in runtime.
410      * Projects that use us by api can reproduce this case.
411      * We need to allow users to reset module property to default state.
412      *
413      * @throws Exception when there is problem to load Input file
414      */
415     @Test
416     public void testUpdateFilterSettingsInRunTime() throws Exception {
417         final File file = new File(getPath("InputSuppressionXpathSingleFilterComplexQuery.java"));
418 
419         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
420         filter.setChecks("MagicNumber");
421         filter.finishLocalSetup();
422 
423         final Violation violation = new Violation(27, 21, TokenTypes.NUM_DOUBLE, "",
424                         "", null, null, null,
425                         MagicNumberCheck.class, null);
426 
427         final FileContents fileContents =
428                 new FileContents(new FileText(file, StandardCharsets.UTF_8.name()));
429 
430         final TreeWalkerAuditEvent ev = new TreeWalkerAuditEvent(fileContents, file.getName(),
431                 violation, JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS));
432 
433         assertWithMessage("match ia expected as 'files' is defaulted")
434                 .that(filter.accept(ev))
435                 .isFalse();
436 
437         // set non-default value for files
438         filter.setFiles(Pattern.quote(file.getPath() + ".never.match"));
439         filter.finishLocalSetup();
440 
441         assertWithMessage("no match is expected due to weird value in 'files'")
442                 .that(filter.accept(ev))
443                 .isTrue();
444 
445         // reset files to default value of filter
446         filter.setFiles(null);
447         filter.finishLocalSetup();
448 
449         assertWithMessage("match ia expected as 'files' is defaulted")
450                 .that(filter.accept(ev))
451                 .isFalse();
452     }
453 
454     private static SuppressionXpathSingleFilter createSuppressionXpathSingleFilter(
455             String files, String checks, String message, String moduleId, String query) {
456         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
457         filter.setFiles(files);
458         filter.setChecks(checks);
459         filter.setMessage(message);
460         filter.setId(moduleId);
461         filter.setQuery(query);
462         filter.finishLocalSetup();
463         return filter;
464     }
465 
466 }