View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 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      public 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 %s", test).fail();
136         }
137         catch (IllegalArgumentException exc) {
138             assertWithMessage("Message should be: Unexpected xpath query")
139                     .that(exc.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 exc) {
193             assertWithMessage("Message should be: Unclosed character class")
194                     .that(exc.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 exc) {
207             assertWithMessage("Message should be: Unclosed character class")
208                     .that(exc.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             "31:21: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "3.14"),
293             "32:16: " + getCheckMessage(MagicNumberCheck.class, MSG_KEY, "123"),
294             "36: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 exc) {
326             assertWithMessage("Exception message does not match expected one")
327                     .that(exc.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     @Test
406     public void testNonMatchingCheckRegexp() throws Exception {
407         final String[] expected = {
408             "19:1: " + getCheckMessage(MissingJavadocTypeCheck.class, MSG_JAVADOC_MISSING),
409         };
410 
411         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
412 
413         verifyFilterWithInlineConfigParser(
414             getPath("InputSuppressionXpathSingleFilterNonMatchingCheckRegexp.java"), expected,
415             removeSuppressed(expected, suppressed));
416     }
417 
418     /**
419      * This test is required to cover pitest mutation
420      * for reset of 'file' field in SuppressionXpathSingleFilter.
421      * This not possible to reproduce by natural execution of Checkstyle
422      * as config is never changed in runtime.
423      * Projects that use us by api can reproduce this case.
424      * We need to allow users to reset module property to default state.
425      *
426      * @throws Exception when there is problem to load Input file
427      */
428     @Test
429     public void testUpdateFilterFileSettingInRunTime() throws Exception {
430         final File file = new File(getPath("InputSuppressionXpathSingleFilterComplexQuery.java"));
431 
432         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
433         filter.setChecks("MagicNumber");
434         filter.finishLocalSetup();
435 
436         final Violation violation = new Violation(27, 21, TokenTypes.NUM_DOUBLE, "",
437                         "", null, null, null,
438                         MagicNumberCheck.class, null);
439 
440         final FileContents fileContents =
441                 new FileContents(new FileText(file, StandardCharsets.UTF_8.name()));
442 
443         final TreeWalkerAuditEvent ev = new TreeWalkerAuditEvent(fileContents, file.getName(),
444                 violation, JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS));
445 
446         assertWithMessage("match ia expected as 'files' is defaulted")
447                 .that(filter.accept(ev))
448                 .isFalse();
449 
450         // set non-default value for files
451         filter.setFiles(Pattern.quote(file.getPath() + ".never.match"));
452         filter.finishLocalSetup();
453 
454         assertWithMessage("no match is expected due to weird value in 'files'")
455                 .that(filter.accept(ev))
456                 .isTrue();
457 
458         // reset files to default value of filter
459         filter.setFiles(null);
460         filter.finishLocalSetup();
461 
462         assertWithMessage("match is expected as 'files' is defaulted")
463                 .that(filter.accept(ev))
464                 .isFalse();
465     }
466 
467     /**
468      * This test is required to cover pitest mutation
469      * for reset of 'checks' field in SuppressionXpathSingleFilter.
470      * This not possible to reproduce by natural execution of Checkstyle
471      * as config is never changed in runtime.
472      * Projects that use us by api can reproduce this case.
473      * We need to allow users to reset module property to default state.
474      *
475      * @throws Exception when there is problem to load Input file
476      */
477     @Test
478     public void testUpdateFilterChecksSettingInRunTime() throws Exception {
479         final File file = new File(getPath("InputSuppressionXpathSingleFilterComplexQuery.java"));
480 
481         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
482         filter.setChecks("MagicNumber");
483         filter.finishLocalSetup();
484 
485         final Violation violation = new Violation(27, 21, TokenTypes.NUM_DOUBLE, "",
486                 "", null, null, null,
487                 MagicNumberCheck.class, null);
488 
489         final FileContents fileContents =
490                 new FileContents(new FileText(file, StandardCharsets.UTF_8.name()));
491 
492         final TreeWalkerAuditEvent ev = new TreeWalkerAuditEvent(fileContents, file.getName(),
493                 violation, JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS));
494 
495         assertWithMessage("match is expected as 'checks' is set")
496                 .that(filter.accept(ev))
497                 .isFalse();
498 
499         // reset checks to default value of filter
500         filter.setChecks(null);
501         filter.finishLocalSetup();
502 
503         assertWithMessage("no match is expected as whole filter is defaulted (empty)")
504                 .that(filter.accept(ev))
505                 .isTrue();
506     }
507 
508     /**
509      * This test is required to cover pitest mutation
510      * for reset of 'message' field in SuppressionXpathSingleFilter.
511      * This not possible to reproduce by natural execution of Checkstyle
512      * as config is never changed in runtime.
513      * Projects that use us by api can reproduce this case.
514      * We need to allow users to reset module property to default state.
515      *
516      * @throws Exception when there is problem to load Input file
517      */
518 
519     @Test
520     public void testSetMessageHandlesNullCorrectly() throws Exception {
521         final File file = new File(getPath("InputSuppressionXpathSingleFilterComplexQuery.java"));
522 
523         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
524         filter.setMessage("MagicNumber");
525         filter.finishLocalSetup();
526 
527         final Violation violation = new Violation(27, 21, TokenTypes.NUM_DOUBLE, "",
528                 "", null, null, null,
529                 MagicNumberCheck.class, "MagicNumber");
530 
531         final FileContents fileContents =
532                 new FileContents(new FileText(file, StandardCharsets.UTF_8.name()));
533 
534         final TreeWalkerAuditEvent ev = new TreeWalkerAuditEvent(fileContents, file.getName(),
535                 violation, JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS));
536 
537         assertWithMessage("match is expected as 'message' is set")
538                 .that(filter.accept(ev))
539                 .isFalse();
540 
541         filter.setMessage(null);
542         filter.finishLocalSetup();
543 
544         assertWithMessage("no match is expected as whole filter is defaulted (empty)")
545                 .that(filter.accept(ev))
546                 .isTrue();
547     }
548 
549     private static SuppressionXpathSingleFilter createSuppressionXpathSingleFilter(
550             String files, String checks, String message, String moduleId, String query) {
551         final SuppressionXpathSingleFilter filter = new SuppressionXpathSingleFilter();
552         filter.setFiles(files);
553         filter.setChecks(checks);
554         filter.setMessage(message);
555         filter.setId(moduleId);
556         filter.setQuery(query);
557         filter.finishLocalSetup();
558         return filter;
559     }
560 
561 }