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.naming.AbstractNameCheck.MSG_INVALID_PATTERN;
24  import static org.mockito.Mockito.mock;
25  import static org.mockito.Mockito.when;
26  
27  import java.io.File;
28  import java.util.Arrays;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.regex.Pattern;
32  
33  import org.junit.jupiter.api.Test;
34  
35  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
36  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
37  import com.puppycrawl.tools.checkstyle.JavaParser;
38  import com.puppycrawl.tools.checkstyle.TreeWalker;
39  import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
40  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41  import com.puppycrawl.tools.checkstyle.api.DetailAST;
42  import com.puppycrawl.tools.checkstyle.api.FileContents;
43  import com.puppycrawl.tools.checkstyle.api.FileText;
44  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
45  import com.puppycrawl.tools.checkstyle.api.TextBlock;
46  import com.puppycrawl.tools.checkstyle.api.Violation;
47  import com.puppycrawl.tools.checkstyle.checks.coding.IllegalCatchCheck;
48  import com.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck;
49  import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
50  import com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck;
51  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
52  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
53  import nl.jqno.equalsverifier.EqualsVerifier;
54  import nl.jqno.equalsverifier.EqualsVerifierReport;
55  
56  public class SuppressionCommentFilterTest
57      extends AbstractModuleTestSupport {
58  
59      private static final String[] ALL_MESSAGES = {
60          "45:17: "
61              + getCheckMessage(AbstractNameCheck.class,
62                  MSG_INVALID_PATTERN, "I", "^[a-z][a-zA-Z0-9]*$"),
63          "48:17: "
64              + getCheckMessage(AbstractNameCheck.class,
65                  MSG_INVALID_PATTERN, "J", "^[a-z][a-zA-Z0-9]*$"),
66          "51:17: "
67              + getCheckMessage(AbstractNameCheck.class,
68                  MSG_INVALID_PATTERN, "K", "^[a-z][a-zA-Z0-9]*$"),
69          "54:17: "
70              + getCheckMessage(AbstractNameCheck.class,
71                  MSG_INVALID_PATTERN, "L", "^[a-z][a-zA-Z0-9]*$"),
72          "55:30: "
73              + getCheckMessage(AbstractNameCheck.class,
74                  MSG_INVALID_PATTERN, "m", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
75          "59:17: "
76              + getCheckMessage(AbstractNameCheck.class,
77                  MSG_INVALID_PATTERN, "M2", "^[a-z][a-zA-Z0-9]*$"),
78          "60:30: "
79              + getCheckMessage(AbstractNameCheck.class,
80                  MSG_INVALID_PATTERN, "n", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
81          "64:17: "
82              + getCheckMessage(AbstractNameCheck.class,
83                  MSG_INVALID_PATTERN, "P", "^[a-z][a-zA-Z0-9]*$"),
84          "67:17: "
85              + getCheckMessage(AbstractNameCheck.class,
86                  MSG_INVALID_PATTERN, "Q", "^[a-z][a-zA-Z0-9]*$"),
87          "70:17: "
88              + getCheckMessage(AbstractNameCheck.class,
89                  MSG_INVALID_PATTERN, "R", "^[a-z][a-zA-Z0-9]*$"),
90          "71:30: "
91              + getCheckMessage(AbstractNameCheck.class,
92                  MSG_INVALID_PATTERN, "s", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
93          "75:17: "
94              + getCheckMessage(AbstractNameCheck.class,
95                  MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
96          "96:23: "
97              + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
98          "103:11: "
99              + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
100         "109:11: "
101             + getCheckMessage(IllegalCatchCheck.class,
102                 IllegalCatchCheck.MSG_KEY, "RuntimeException"),
103         "110:11: "
104             + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
105         "118:31: "
106             + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
107     };
108 
109     @Override
110     public String getPackageLocation() {
111         return "com/puppycrawl/tools/checkstyle/filters/suppressioncommentfilter";
112     }
113 
114     @Test
115     public void testNone() throws Exception {
116         final String[] messages = {
117             "35:17: "
118                 + getCheckMessage(AbstractNameCheck.class,
119                     MSG_INVALID_PATTERN, "I", "^[a-z][a-zA-Z0-9]*$"),
120             "38:17: "
121                 + getCheckMessage(AbstractNameCheck.class,
122                     MSG_INVALID_PATTERN, "J", "^[a-z][a-zA-Z0-9]*$"),
123             "41:17: "
124                 + getCheckMessage(AbstractNameCheck.class,
125                     MSG_INVALID_PATTERN, "K", "^[a-z][a-zA-Z0-9]*$"),
126             "44:17: "
127                 + getCheckMessage(AbstractNameCheck.class,
128                     MSG_INVALID_PATTERN, "L", "^[a-z][a-zA-Z0-9]*$"),
129             "45:30: "
130                 + getCheckMessage(AbstractNameCheck.class,
131                     MSG_INVALID_PATTERN, "m", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
132             "49:17: "
133                 + getCheckMessage(AbstractNameCheck.class,
134                     MSG_INVALID_PATTERN, "M2", "^[a-z][a-zA-Z0-9]*$"),
135             "50:30: "
136                 + getCheckMessage(AbstractNameCheck.class,
137                     MSG_INVALID_PATTERN, "n", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
138             "54:17: "
139                 + getCheckMessage(AbstractNameCheck.class,
140                     MSG_INVALID_PATTERN, "P", "^[a-z][a-zA-Z0-9]*$"),
141             "57:17: "
142                 + getCheckMessage(AbstractNameCheck.class,
143                     MSG_INVALID_PATTERN, "Q", "^[a-z][a-zA-Z0-9]*$"),
144             "60:17: "
145                 + getCheckMessage(AbstractNameCheck.class,
146                     MSG_INVALID_PATTERN, "R", "^[a-z][a-zA-Z0-9]*$"),
147             "61:30: "
148                 + getCheckMessage(AbstractNameCheck.class,
149                     MSG_INVALID_PATTERN, "s", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
150             "65:17: "
151                 + getCheckMessage(AbstractNameCheck.class,
152                     MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
153             "86:23: "
154                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
155             "93:11: "
156                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
157             "99:11: "
158                 + getCheckMessage(IllegalCatchCheck.class,
159                     IllegalCatchCheck.MSG_KEY, "RuntimeException"),
160             "100:11: "
161                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
162             "108:31: "
163                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
164         };
165         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
166 
167         verifySuppressedWithParser(getPath("InputSuppressionCommentFilter.java"),
168                 messages, suppressed);
169     }
170 
171     // Suppress all checks between default comments
172     @Test
173     public void testDefault() throws Exception {
174         final String[] suppressed = {
175             "48:17: "
176                 + getCheckMessage(AbstractNameCheck.class,
177                     MSG_INVALID_PATTERN, "J", "^[a-z][a-zA-Z0-9]*$"),
178             "75:17: "
179                 + getCheckMessage(AbstractNameCheck.class,
180                     MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
181             "96:23: "
182                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
183             "103:11: "
184                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
185             "118:31: "
186                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
187         };
188         verifySuppressedWithParser("InputSuppressionCommentFilter2.java", suppressed);
189     }
190 
191     @Test
192     public void testCheckC() throws Exception {
193         final String[] suppressed = {
194             "75:17: "
195                 + getCheckMessage(AbstractNameCheck.class,
196                     MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
197             "96:23: "
198                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
199             "103:11: "
200                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
201         };
202         verifySuppressedWithParser("InputSuppressionCommentFilter3.java", suppressed);
203     }
204 
205     @Test
206     public void testCheckCpp() throws Exception {
207         final String[] suppressed = {
208             "48:17: "
209                 + getCheckMessage(AbstractNameCheck.class,
210                     MSG_INVALID_PATTERN, "J", "^[a-z][a-zA-Z0-9]*$"),
211             "118:31: "
212                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
213         };
214         verifySuppressedWithParser("InputSuppressionCommentFilter4.java", suppressed);
215     }
216 
217     // Suppress all checks between CS_OFF and CS_ON
218     @Test
219     public void testOffFormat() throws Exception {
220         final String[] suppressed = {
221             "64:17: "
222                 + getCheckMessage(AbstractNameCheck.class,
223                     MSG_INVALID_PATTERN, "P", "^[a-z][a-zA-Z0-9]*$"),
224             "70:17: "
225                 + getCheckMessage(AbstractNameCheck.class,
226                     MSG_INVALID_PATTERN, "R", "^[a-z][a-zA-Z0-9]*$"),
227             "71:30: "
228                 + getCheckMessage(AbstractNameCheck.class,
229                     MSG_INVALID_PATTERN, "s", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
230             "74:17: "
231                 + getCheckMessage(AbstractNameCheck.class,
232                     MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
233         };
234         verifySuppressedWithParser("InputSuppressionCommentFilter5.java", suppressed);
235     }
236 
237     // Test suppression of checks of only one type
238     //  Suppress only ConstantNameCheck between CS_OFF and CS_ON
239     @Test
240     public void testOffFormatCheck() throws Exception {
241         final String[] suppressed = {
242             "71:30: "
243                 + getCheckMessage(AbstractNameCheck.class,
244                     MSG_INVALID_PATTERN, "s", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
245         };
246         verifySuppressedWithParser("InputSuppressionCommentFilter6.java", suppressed);
247     }
248 
249     @Test
250     public void testArgumentSuppression() throws Exception {
251         final String[] suppressed = {
252             "110:11: "
253                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
254         };
255         verifySuppressedWithParser("InputSuppressionCommentFilter7.java", suppressed);
256     }
257 
258     @Test
259     public void testExpansion() throws Exception {
260         final String[] suppressed = {
261             "54:17: "
262                 + getCheckMessage(AbstractNameCheck.class,
263                     MSG_INVALID_PATTERN, "L", "^[a-z][a-zA-Z0-9]*$"),
264             "55:30: "
265                 + getCheckMessage(AbstractNameCheck.class,
266                     MSG_INVALID_PATTERN, "m", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
267             "60:30: "
268                 + getCheckMessage(AbstractNameCheck.class,
269                     MSG_INVALID_PATTERN, "n", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
270         };
271         verifySuppressedWithParser("InputSuppressionCommentFilter8.java", suppressed);
272     }
273 
274     @Test
275     public void testMessage() throws Exception {
276         final String[] suppressed = CommonUtil.EMPTY_STRING_ARRAY;
277         verifySuppressedWithParser("InputSuppressionCommentFilter9.java", suppressed);
278     }
279 
280     private void verifySuppressedWithParser(String fileName, String... suppressed)
281             throws Exception {
282         verifyFilterWithInlineConfigParser(getPath(fileName), ALL_MESSAGES,
283                 removeSuppressed(ALL_MESSAGES, suppressed));
284     }
285 
286     private void verifySuppressedWithParser(String fileName, String[] messages,
287                                             String... suppressed)
288             throws Exception {
289         verifyFilterWithInlineConfigParser(fileName, messages,
290                 removeSuppressed(messages, suppressed));
291     }
292 
293     @Test
294     public void testEqualsAndHashCodeOfTagClass() {
295         final Object tag = getTagsAfterExecutionOnDefaultFilter("//CHECKSTYLE:OFF").getFirst();
296         final EqualsVerifierReport ev = EqualsVerifier.forClass(tag.getClass())
297                 .usingGetClass().report();
298         assertWithMessage("Error: %s", ev.getMessage())
299                 .that(ev.isSuccessful())
300                 .isTrue();
301     }
302 
303     @Test
304     public void testToStringOfTagClass() {
305         final Object tag = getTagsAfterExecutionOnDefaultFilter("//CHECKSTYLE:OFF").getFirst();
306         assertWithMessage("Invalid toString result")
307             .that(tag.toString())
308             .isEqualTo("Tag[text='CHECKSTYLE:OFF', line=1, column=0, type=OFF,"
309                     + " tagCheckRegexp=.*, tagMessageRegexp=null, tagIdRegexp=null]");
310     }
311 
312     @Test
313     public void testToStringOfTagClassWithMessage() {
314         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
315         filter.setMessageFormat(".*");
316         final Object tag =
317                 getTagsAfterExecution(filter, "filename", "//CHECKSTYLE:ON").getFirst();
318         assertWithMessage("Invalid toString result")
319             .that(tag.toString())
320             .isEqualTo("Tag[text='CHECKSTYLE:ON', line=1, column=0, type=ON,"
321                 + " tagCheckRegexp=.*, tagMessageRegexp=.*, tagIdRegexp=null]");
322     }
323 
324     @Test
325     public void testToStringOfTagClassWithIdFormat() {
326         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
327         filter.setIdFormat("id");
328         final Object tag =
329                 getTagsAfterExecution(filter, "filename", "//CHECKSTYLE:OFF").getFirst();
330         assertWithMessage("Invalid toString result")
331             .that(tag.toString())
332             .isEqualTo("Tag[text='CHECKSTYLE:OFF', line=1, column=0, type=OFF,"
333                     + " tagCheckRegexp=.*, tagMessageRegexp=null, tagIdRegexp=id]");
334     }
335 
336     @Test
337     public void testCompareToOfTagClass() {
338         final List<Comparable<Object>> tags1 =
339                 getTagsAfterExecutionOnDefaultFilter("//CHECKSTYLE:OFF", " //CHECKSTYLE:ON");
340         final Comparable<Object> tag1 = tags1.getFirst();
341         final Comparable<Object> tag2 = tags1.get(1);
342 
343         final List<Comparable<Object>> tags2 =
344                 getTagsAfterExecutionOnDefaultFilter(" //CHECKSTYLE:OFF");
345         final Comparable<Object> tag3 = tags2.getFirst();
346 
347         final List<Comparable<Object>> tags3 =
348                 getTagsAfterExecutionOnDefaultFilter("//CHECKSTYLE:ON");
349         final Comparable<Object> tag4 = tags3.getFirst();
350 
351         assertWithMessage("Invalid comparing result")
352                 .that(tag1.compareTo(tag2) < 0)
353                 .isTrue();
354         assertWithMessage("Invalid comparing result")
355                 .that(tag2.compareTo(tag1) > 0)
356                 .isTrue();
357         assertWithMessage("Invalid comparing result")
358                 .that(tag1.compareTo(tag3) < 0)
359                 .isTrue();
360         assertWithMessage("Invalid comparing result")
361                 .that(tag3.compareTo(tag1) > 0)
362                 .isTrue();
363         final int actual = tag1.compareTo(tag4);
364         assertWithMessage("Invalid comparing result")
365             .that(actual)
366             .isEqualTo(0);
367     }
368 
369     @Test
370     public void testInvalidCheckFormat() throws Exception {
371         final DefaultConfiguration treeWalkerConfig =
372             createModuleConfig(TreeWalker.class);
373         final DefaultConfiguration filterConfig =
374             createModuleConfig(SuppressionCommentFilter.class);
375         filterConfig.addProperty("checkFormat", "e[l");
376         final DefaultConfiguration checkConfig =
377             createModuleConfig(ConstantNameCheck.class);
378         treeWalkerConfig.addChild(filterConfig);
379         treeWalkerConfig.addChild(checkConfig);
380 
381         try {
382             execute(treeWalkerConfig, getPath("InputSuppressionCommentFilter10.java"));
383             assertWithMessage("Exception is expected").fail();
384         }
385         catch (CheckstyleException exc) {
386             final IllegalArgumentException cause = (IllegalArgumentException) exc.getCause();
387             assertWithMessage("Invalid exception message")
388                 .that(cause)
389                 .hasMessageThat()
390                 .isEqualTo("unable to parse expanded comment e[l");
391         }
392     }
393 
394     @Test
395     public void testInvalidMessageFormat() throws Exception {
396         final DefaultConfiguration treeWalkerConfig =
397             createModuleConfig(TreeWalker.class);
398         final DefaultConfiguration filterConfig =
399             createModuleConfig(SuppressionCommentFilter.class);
400         filterConfig.addProperty("messageFormat", "e[l");
401         final DefaultConfiguration checkConfig =
402             createModuleConfig(ConstantNameCheck.class);
403         treeWalkerConfig.addChild(filterConfig);
404         treeWalkerConfig.addChild(checkConfig);
405 
406         try {
407             execute(treeWalkerConfig, getPath("InputSuppressionCommentFilter11.java"));
408             assertWithMessage("Exception is expected").fail();
409         }
410         catch (CheckstyleException exc) {
411             final IllegalArgumentException cause = (IllegalArgumentException) exc.getCause();
412             assertWithMessage("Invalid exception message")
413                 .that(cause)
414                 .hasMessageThat()
415                 .isEqualTo("unable to parse expanded comment e[l");
416         }
417     }
418 
419     @Test
420     public void testAcceptNullViolation() {
421         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
422         final FileContents contents = new FileContents(new FileText(new File("filename"),
423                 Arrays.asList("//CHECKSTYLE:OFF: ConstantNameCheck", "line2")));
424         contents.reportSingleLineComment(1, 0);
425         final TreeWalkerAuditEvent auditEvent =
426                 new TreeWalkerAuditEvent(contents, null, null, null);
427         assertWithMessage("Filter should accept audit event")
428             .that(filter.accept(auditEvent))
429                 .isTrue();
430         assertWithMessage("File name should not be null")
431             .that(auditEvent.fileName())
432             .isNull();
433     }
434 
435     @Test
436     public void testAcceptNullFileContents() {
437         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
438         final FileContents contents = null;
439         final TreeWalkerAuditEvent auditEvent = new TreeWalkerAuditEvent(contents, null,
440                 new Violation(1, null, null, null, null, Object.class, null), null);
441         assertWithMessage("Filter should accept audit event")
442                 .that(filter.accept(auditEvent))
443                 .isTrue();
444     }
445 
446     @Test
447     public void testSuppressByCheck() throws Exception {
448         final String[] suppressedViolation = {
449             "42:17: "
450                 + getCheckMessage(AbstractNameCheck.class,
451                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
452             "48:9: "
453                 + getCheckMessage(AbstractNameCheck.class,
454                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
455         };
456         final String[] expectedViolation = {
457             "42:17: "
458                 + getCheckMessage(AbstractNameCheck.class,
459                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
460             "45:30: "
461                 + getCheckMessage(AbstractNameCheck.class,
462                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
463             "48:9: "
464                 + getCheckMessage(AbstractNameCheck.class,
465                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
466             "51:18: "
467                 + getCheckMessage(AbstractNameCheck.class,
468                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
469             "54:17: "
470                 + getCheckMessage(AbstractNameCheck.class,
471                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
472             "57:17: "
473                 + getCheckMessage(AbstractNameCheck.class,
474                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
475             };
476 
477         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById.java"),
478                 expectedViolation, suppressedViolation);
479     }
480 
481     @Test
482     public void testSuppressById() throws Exception {
483         final String[] suppressedViolation = {
484             "42:17: "
485                 + getCheckMessage(AbstractNameCheck.class,
486                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
487             "48:9: "
488                 + getCheckMessage(AbstractNameCheck.class,
489                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
490         };
491         final String[] expectedViolation = {
492             "42:17: "
493                 + getCheckMessage(AbstractNameCheck.class,
494                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
495             "45:30: "
496                 + getCheckMessage(AbstractNameCheck.class,
497                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
498             "48:9: "
499                 + getCheckMessage(AbstractNameCheck.class,
500                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
501             "51:18: "
502                 + getCheckMessage(AbstractNameCheck.class,
503                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
504             "54:17: "
505                 + getCheckMessage(AbstractNameCheck.class,
506                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
507             "57:17: "
508                 + getCheckMessage(AbstractNameCheck.class,
509                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
510             };
511 
512         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById2.java"),
513                 expectedViolation, suppressedViolation);
514     }
515 
516     @Test
517     public void testSuppressByCheckAndId() throws Exception {
518         final String[] suppressedViolation = {
519             "42:17: "
520                 + getCheckMessage(AbstractNameCheck.class,
521                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
522             "48:9: "
523                 + getCheckMessage(AbstractNameCheck.class,
524                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
525             };
526         final String[] expectedViolation = {
527             "42:17: "
528                 + getCheckMessage(AbstractNameCheck.class,
529                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
530             "45:30: "
531                 + getCheckMessage(AbstractNameCheck.class,
532                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
533             "48:9: "
534                 + getCheckMessage(AbstractNameCheck.class,
535                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
536             "51:18: "
537                 + getCheckMessage(AbstractNameCheck.class,
538                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
539             "54:17: "
540                 + getCheckMessage(AbstractNameCheck.class,
541                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
542             "57:17: "
543                 + getCheckMessage(AbstractNameCheck.class,
544                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
545             };
546 
547         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById3.java"),
548                 expectedViolation, suppressedViolation);
549     }
550 
551     @Test
552     public void testSuppressByIdAndMessage() throws Exception {
553         final String[] suppressedViolation = {
554             "54:17: "
555                 + getCheckMessage(AbstractNameCheck.class,
556                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
557         };
558         final String[] expectedViolation = {
559             "42:17: "
560                 + getCheckMessage(AbstractNameCheck.class,
561                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
562             "45:30: "
563                 + getCheckMessage(AbstractNameCheck.class,
564                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
565             "48:9: "
566                 + getCheckMessage(AbstractNameCheck.class,
567                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
568             "51:18: "
569                 + getCheckMessage(AbstractNameCheck.class,
570                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
571             "54:17: "
572                 + getCheckMessage(AbstractNameCheck.class,
573                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
574             "57:17: "
575                 + getCheckMessage(AbstractNameCheck.class,
576                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
577             };
578 
579         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById4.java"),
580                 expectedViolation, suppressedViolation);
581     }
582 
583     @Test
584     public void testSuppressByCheckAndMessage() throws Exception {
585         final String[] suppressedViolation = {
586             "54:17: "
587                 + getCheckMessage(AbstractNameCheck.class,
588                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
589             };
590         final String[] expectedViolation = {
591             "42:17: "
592                 + getCheckMessage(AbstractNameCheck.class,
593                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
594             "45:30: "
595                 + getCheckMessage(AbstractNameCheck.class,
596                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
597             "48:9: "
598                 + getCheckMessage(AbstractNameCheck.class,
599                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
600             "51:18: "
601                 + getCheckMessage(AbstractNameCheck.class,
602                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
603             "54:17: "
604                 + getCheckMessage(AbstractNameCheck.class,
605                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
606             "57:17: "
607                 + getCheckMessage(AbstractNameCheck.class,
608                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
609             };
610 
611         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById5.java"),
612                 expectedViolation, suppressedViolation);
613     }
614 
615     @Test
616     public void testFindNearestMatchDontAllowSameColumn() {
617         final SuppressionCommentFilter suppressionCommentFilter = new SuppressionCommentFilter();
618         final FileContents contents = new FileContents(new FileText(new File("filename"),
619                 Arrays.asList("//CHECKSTYLE:OFF: ConstantNameCheck", "line2")));
620         contents.reportSingleLineComment(1, 0);
621         final TreeWalkerAuditEvent dummyEvent = new TreeWalkerAuditEvent(contents, "filename",
622                 new Violation(1, null, null, null, null, Object.class, null), null);
623         final boolean result = suppressionCommentFilter.accept(dummyEvent);
624         assertWithMessage("Filter should not accept event")
625             .that(result)
626             .isFalse();
627     }
628 
629     @Test
630     public void testTagsAreClearedEachRun() {
631         final SuppressionCommentFilter suppressionCommentFilter = new SuppressionCommentFilter();
632         final List<?> tags1 = getTagsAfterExecution(suppressionCommentFilter,
633                 "filename1", "//CHECKSTYLE:OFF", "line2");
634         assertWithMessage("Invalid tags size")
635             .that(tags1)
636             .hasSize(1);
637         final List<?> tags2 = getTagsAfterExecution(suppressionCommentFilter,
638                 "filename2", "No comments in this file");
639         assertWithMessage("Invalid tags size")
640             .that(tags2)
641             .isEmpty();
642     }
643 
644     private static List<Comparable<Object>> getTagsAfterExecutionOnDefaultFilter(String... lines) {
645         return getTagsAfterExecution(new SuppressionCommentFilter(), "filename", lines);
646     }
647 
648     /**
649      * Calls the filter with a minimal set of inputs and returns a list of
650      * {@link SuppressionCommentFilter} internal type {@code Tag}.
651      * Our goal is 100% test coverage, for this we use white-box testing.
652      * So we need access to the implementation details. For this reason,
653      * it is necessary to use reflection to gain access to the inner field here.
654      *
655      * @return {@code Tag} list
656      */
657     private static List<Comparable<Object>> getTagsAfterExecution(SuppressionCommentFilter filter,
658             String filename, String... lines) {
659         final FileContents contents = new FileContents(
660                 new FileText(new File(filename), Arrays.asList(lines)));
661         for (int lineNo = 0; lineNo < lines.length; lineNo++) {
662             final int colNo = lines[lineNo].indexOf("//");
663             if (colNo >= 0) {
664                 contents.reportSingleLineComment(lineNo + 1, colNo);
665             }
666         }
667         final TreeWalkerAuditEvent dummyEvent = new TreeWalkerAuditEvent(contents, filename,
668                 new Violation(1, null, null, null, null, Object.class, ""), null);
669         filter.accept(dummyEvent);
670         return TestUtil.getInternalStateListComparable(filter, "tags");
671     }
672 
673     @Test
674     public void testCachingByFileContentsInstance() throws Exception {
675 
676         final File file = new File(getPath("InputSuppressionCommentFilterSuppressById6.java"));
677         final DetailAST rootAst = JavaParser.parseFile(file, JavaParser.Options.WITH_COMMENTS);
678         final String[] lines = {"//CSOFF", "//CSON"};
679         final FileContents fileContents = new FileContents(
680                 new FileText(file, Arrays.asList(lines)));
681         for (int lineNo = 0; lineNo < lines.length; lineNo++) {
682             final int colNo = lines[lineNo].indexOf("//");
683             if (colNo >= 0) {
684                 fileContents.reportSingleLineComment(lineNo + 1, colNo);
685             }
686         }
687 
688         final FileContents mockedContents = mock();
689         final Map<Integer, TextBlock> returnValue = fileContents.getSingleLineComments();
690         when(mockedContents.getSingleLineComments())
691                 .thenReturn(returnValue)
692                 .thenThrow(new IllegalStateException("Second call is not allowed"));
693 
694         final String[] args = {"line_length", "^[a-z][a-zA-Z0-9]*$"};
695         final Violation violation = new Violation(27, 9, 58,
696                 "com.puppycrawl.tools.checkstyle.checks.naming.messages",
697                 "name.invalidPattern",
698                 args,
699                 SeverityLevel.ERROR,
700                 "ignore",
701                 MemberNameCheck.class,
702                 null);
703 
704         final TreeWalkerAuditEvent event = new TreeWalkerAuditEvent(
705                 mockedContents, file.getAbsolutePath(), violation, rootAst);
706 
707         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
708         filter.setOffCommentFormat(Pattern.compile("CSOFF"));
709         filter.setOnCommentFormat(Pattern.compile("CSON"));
710         filter.setCheckFormat("MemberNameCheck");
711         filter.finishLocalSetup();
712 
713         assertWithMessage("should accept")
714                 .that(filter.accept(event)).isTrue();
715         // due to caching in filter second execution should not do parsing of comments in file
716         // so exception is not expected
717         assertWithMessage("should accept")
718                 .that(filter.accept(event)).isTrue();
719     }
720 
721 }