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.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     protected 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").get(0);
296         final EqualsVerifierReport ev = EqualsVerifier.forClass(tag.getClass())
297                 .usingGetClass().report();
298         assertWithMessage("Error: " + ev.getMessage())
299                 .that(ev.isSuccessful())
300                 .isTrue();
301     }
302 
303     @Test
304     public void testToStringOfTagClass() {
305         final Object tag = getTagsAfterExecutionOnDefaultFilter("//CHECKSTYLE:OFF").get(0);
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").get(0);
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 testCompareToOfTagClass() {
326         final List<Comparable<Object>> tags1 =
327                 getTagsAfterExecutionOnDefaultFilter("//CHECKSTYLE:OFF", " //CHECKSTYLE:ON");
328         final Comparable<Object> tag1 = tags1.get(0);
329         final Comparable<Object> tag2 = tags1.get(1);
330 
331         final List<Comparable<Object>> tags2 =
332                 getTagsAfterExecutionOnDefaultFilter(" //CHECKSTYLE:OFF");
333         final Comparable<Object> tag3 = tags2.get(0);
334 
335         final List<Comparable<Object>> tags3 =
336                 getTagsAfterExecutionOnDefaultFilter("//CHECKSTYLE:ON");
337         final Comparable<Object> tag4 = tags3.get(0);
338 
339         assertWithMessage("Invalid comparing result")
340                 .that(tag1.compareTo(tag2) < 0)
341                 .isTrue();
342         assertWithMessage("Invalid comparing result")
343                 .that(tag2.compareTo(tag1) > 0)
344                 .isTrue();
345         assertWithMessage("Invalid comparing result")
346                 .that(tag1.compareTo(tag3) < 0)
347                 .isTrue();
348         assertWithMessage("Invalid comparing result")
349                 .that(tag3.compareTo(tag1) > 0)
350                 .isTrue();
351         final int actual = tag1.compareTo(tag4);
352         assertWithMessage("Invalid comparing result")
353             .that(actual)
354             .isEqualTo(0);
355     }
356 
357     @Test
358     public void testInvalidCheckFormat() throws Exception {
359         final DefaultConfiguration treeWalkerConfig =
360             createModuleConfig(TreeWalker.class);
361         final DefaultConfiguration filterConfig =
362             createModuleConfig(SuppressionCommentFilter.class);
363         filterConfig.addProperty("checkFormat", "e[l");
364         final DefaultConfiguration checkConfig =
365             createModuleConfig(ConstantNameCheck.class);
366         treeWalkerConfig.addChild(filterConfig);
367         treeWalkerConfig.addChild(checkConfig);
368 
369         try {
370             execute(treeWalkerConfig, getPath("InputSuppressionCommentFilter10.java"));
371             assertWithMessage("Exception is expected").fail();
372         }
373         catch (CheckstyleException ex) {
374             final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause();
375             assertWithMessage("Invalid exception message")
376                 .that(cause)
377                 .hasMessageThat()
378                 .isEqualTo("unable to parse expanded comment e[l");
379         }
380     }
381 
382     @Test
383     public void testInvalidMessageFormat() throws Exception {
384         final DefaultConfiguration treeWalkerConfig =
385             createModuleConfig(TreeWalker.class);
386         final DefaultConfiguration filterConfig =
387             createModuleConfig(SuppressionCommentFilter.class);
388         filterConfig.addProperty("messageFormat", "e[l");
389         final DefaultConfiguration checkConfig =
390             createModuleConfig(ConstantNameCheck.class);
391         treeWalkerConfig.addChild(filterConfig);
392         treeWalkerConfig.addChild(checkConfig);
393 
394         try {
395             execute(treeWalkerConfig, getPath("InputSuppressionCommentFilter11.java"));
396             assertWithMessage("Exception is expected").fail();
397         }
398         catch (CheckstyleException ex) {
399             final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause();
400             assertWithMessage("Invalid exception message")
401                 .that(cause)
402                 .hasMessageThat()
403                 .isEqualTo("unable to parse expanded comment e[l");
404         }
405     }
406 
407     @Test
408     public void testAcceptNullViolation() {
409         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
410         final FileContents contents = new FileContents(new FileText(new File("filename"),
411                 Arrays.asList("//CHECKSTYLE:OFF: ConstantNameCheck", "line2")));
412         contents.reportSingleLineComment(1, 0);
413         final TreeWalkerAuditEvent auditEvent =
414                 new TreeWalkerAuditEvent(contents, null, null, null);
415         assertWithMessage("Filter should accept audit event")
416             .that(filter.accept(auditEvent))
417                 .isTrue();
418         assertWithMessage("File name should not be null")
419             .that(auditEvent.getFileName())
420             .isNull();
421     }
422 
423     @Test
424     public void testAcceptNullFileContents() {
425         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
426         final FileContents contents = null;
427         final TreeWalkerAuditEvent auditEvent = new TreeWalkerAuditEvent(contents, null,
428                 new Violation(1, null, null, null, null, Object.class, null), null);
429         assertWithMessage("Filter should accept audit event")
430                 .that(filter.accept(auditEvent))
431                 .isTrue();
432     }
433 
434     @Test
435     public void testSuppressByCheck() throws Exception {
436         final String[] suppressedViolation = {
437             "42:17: "
438                 + getCheckMessage(AbstractNameCheck.class,
439                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
440             "48:9: "
441                 + getCheckMessage(AbstractNameCheck.class,
442                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
443         };
444         final String[] expectedViolation = {
445             "42:17: "
446                 + getCheckMessage(AbstractNameCheck.class,
447                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
448             "45:30: "
449                 + getCheckMessage(AbstractNameCheck.class,
450                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
451             "48:9: "
452                 + getCheckMessage(AbstractNameCheck.class,
453                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
454             "51:18: "
455                 + getCheckMessage(AbstractNameCheck.class,
456                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
457             "54:17: "
458                 + getCheckMessage(AbstractNameCheck.class,
459                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
460             "57:17: "
461                 + getCheckMessage(AbstractNameCheck.class,
462                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
463             };
464 
465         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById.java"),
466                 expectedViolation, suppressedViolation);
467     }
468 
469     @Test
470     public void testSuppressById() throws Exception {
471         final String[] suppressedViolation = {
472             "42:17: "
473                 + getCheckMessage(AbstractNameCheck.class,
474                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
475             "48:9: "
476                 + getCheckMessage(AbstractNameCheck.class,
477                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
478         };
479         final String[] expectedViolation = {
480             "42:17: "
481                 + getCheckMessage(AbstractNameCheck.class,
482                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
483             "45:30: "
484                 + getCheckMessage(AbstractNameCheck.class,
485                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
486             "48:9: "
487                 + getCheckMessage(AbstractNameCheck.class,
488                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
489             "51:18: "
490                 + getCheckMessage(AbstractNameCheck.class,
491                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
492             "54:17: "
493                 + getCheckMessage(AbstractNameCheck.class,
494                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
495             "57:17: "
496                 + getCheckMessage(AbstractNameCheck.class,
497                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
498             };
499 
500         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById2.java"),
501                 expectedViolation, suppressedViolation);
502     }
503 
504     @Test
505     public void testSuppressByCheckAndId() throws Exception {
506         final String[] suppressedViolation = {
507             "42:17: "
508                 + getCheckMessage(AbstractNameCheck.class,
509                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
510             "48:9: "
511                 + getCheckMessage(AbstractNameCheck.class,
512                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
513             };
514         final String[] expectedViolation = {
515             "42:17: "
516                 + getCheckMessage(AbstractNameCheck.class,
517                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
518             "45:30: "
519                 + getCheckMessage(AbstractNameCheck.class,
520                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
521             "48:9: "
522                 + getCheckMessage(AbstractNameCheck.class,
523                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
524             "51:18: "
525                 + getCheckMessage(AbstractNameCheck.class,
526                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
527             "54:17: "
528                 + getCheckMessage(AbstractNameCheck.class,
529                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
530             "57:17: "
531                 + getCheckMessage(AbstractNameCheck.class,
532                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
533             };
534 
535         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById3.java"),
536                 expectedViolation, suppressedViolation);
537     }
538 
539     @Test
540     public void testSuppressByIdAndMessage() throws Exception {
541         final String[] suppressedViolation = {
542             "54:17: "
543                 + getCheckMessage(AbstractNameCheck.class,
544                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
545         };
546         final String[] expectedViolation = {
547             "42:17: "
548                 + getCheckMessage(AbstractNameCheck.class,
549                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
550             "45:30: "
551                 + getCheckMessage(AbstractNameCheck.class,
552                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
553             "48:9: "
554                 + getCheckMessage(AbstractNameCheck.class,
555                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
556             "51:18: "
557                 + getCheckMessage(AbstractNameCheck.class,
558                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
559             "54:17: "
560                 + getCheckMessage(AbstractNameCheck.class,
561                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
562             "57:17: "
563                 + getCheckMessage(AbstractNameCheck.class,
564                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
565             };
566 
567         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById4.java"),
568                 expectedViolation, suppressedViolation);
569     }
570 
571     @Test
572     public void testSuppressByCheckAndMessage() throws Exception {
573         final String[] suppressedViolation = {
574             "54:17: "
575                 + getCheckMessage(AbstractNameCheck.class,
576                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
577             };
578         final String[] expectedViolation = {
579             "42:17: "
580                 + getCheckMessage(AbstractNameCheck.class,
581                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
582             "45:30: "
583                 + getCheckMessage(AbstractNameCheck.class,
584                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
585             "48:9: "
586                 + getCheckMessage(AbstractNameCheck.class,
587                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
588             "51:18: "
589                 + getCheckMessage(AbstractNameCheck.class,
590                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
591             "54:17: "
592                 + getCheckMessage(AbstractNameCheck.class,
593                     MSG_INVALID_PATTERN, "DEF", "^[a-z][a-zA-Z0-9]*$"),
594             "57:17: "
595                 + getCheckMessage(AbstractNameCheck.class,
596                     MSG_INVALID_PATTERN, "XYZ", "^[a-z][a-zA-Z0-9]*$"),
597             };
598 
599         verifySuppressedWithParser(getPath("InputSuppressionCommentFilterSuppressById5.java"),
600                 expectedViolation, suppressedViolation);
601     }
602 
603     @Test
604     public void testFindNearestMatchDontAllowSameColumn() {
605         final SuppressionCommentFilter suppressionCommentFilter = new SuppressionCommentFilter();
606         final FileContents contents = new FileContents(new FileText(new File("filename"),
607                 Arrays.asList("//CHECKSTYLE:OFF: ConstantNameCheck", "line2")));
608         contents.reportSingleLineComment(1, 0);
609         final TreeWalkerAuditEvent dummyEvent = new TreeWalkerAuditEvent(contents, "filename",
610                 new Violation(1, null, null, null, null, Object.class, null), null);
611         final boolean result = suppressionCommentFilter.accept(dummyEvent);
612         assertWithMessage("Filter should not accept event")
613             .that(result)
614             .isFalse();
615     }
616 
617     @Test
618     public void testTagsAreClearedEachRun() {
619         final SuppressionCommentFilter suppressionCommentFilter = new SuppressionCommentFilter();
620         final List<?> tags1 = getTagsAfterExecution(suppressionCommentFilter,
621                 "filename1", "//CHECKSTYLE:OFF", "line2");
622         assertWithMessage("Invalid tags size")
623             .that(tags1)
624             .hasSize(1);
625         final List<?> tags2 = getTagsAfterExecution(suppressionCommentFilter,
626                 "filename2", "No comments in this file");
627         assertWithMessage("Invalid tags size")
628             .that(tags2)
629             .isEmpty();
630     }
631 
632     private static List<Comparable<Object>> getTagsAfterExecutionOnDefaultFilter(String... lines) {
633         return getTagsAfterExecution(new SuppressionCommentFilter(), "filename", lines);
634     }
635 
636     /**
637      * Calls the filter with a minimal set of inputs and returns a list of
638      * {@link SuppressionCommentFilter} internal type {@code Tag}.
639      * Our goal is 100% test coverage, for this we use white-box testing.
640      * So we need access to the implementation details. For this reason,
641      * it is necessary to use reflection to gain access to the inner field here.
642      *
643      * @return {@code Tag} list
644      */
645     private static List<Comparable<Object>> getTagsAfterExecution(SuppressionCommentFilter filter,
646             String filename, String... lines) {
647         final FileContents contents = new FileContents(
648                 new FileText(new File(filename), Arrays.asList(lines)));
649         for (int lineNo = 0; lineNo < lines.length; lineNo++) {
650             final int colNo = lines[lineNo].indexOf("//");
651             if (colNo >= 0) {
652                 contents.reportSingleLineComment(lineNo + 1, colNo);
653             }
654         }
655         final TreeWalkerAuditEvent dummyEvent = new TreeWalkerAuditEvent(contents, filename,
656                 new Violation(1, null, null, null, null, Object.class, ""), null);
657         filter.accept(dummyEvent);
658         return TestUtil.getInternalState(filter, "tags");
659     }
660 
661     @Test
662     public void testCachingByFileContentsInstance() throws Exception {
663 
664         final File file = new File(getPath("InputSuppressionCommentFilterSuppressById6.java"));
665         final DetailAST rootAst = JavaParser.parseFile(file, JavaParser.Options.WITH_COMMENTS);
666         final String[] lines = {"//CSOFF", "//CSON"};
667         final FileContents fileContents = new FileContents(
668                 new FileText(file, Arrays.asList(lines)));
669         for (int lineNo = 0; lineNo < lines.length; lineNo++) {
670             final int colNo = lines[lineNo].indexOf("//");
671             if (colNo >= 0) {
672                 fileContents.reportSingleLineComment(lineNo + 1, colNo);
673             }
674         }
675 
676         final FileContents mockedContents = mock();
677         final Map<Integer, TextBlock> returnValue = fileContents.getSingleLineComments();
678         when(mockedContents.getSingleLineComments())
679                 .thenReturn(returnValue)
680                 .thenThrow(new IllegalStateException("Second call is not allowed"));
681 
682         final String[] args = {"line_length", "^[a-z][a-zA-Z0-9]*$"};
683         final Violation violation = new Violation(27, 9, 58,
684                 "com.puppycrawl.tools.checkstyle.checks.naming.messages",
685                 "name.invalidPattern",
686                 args,
687                 SeverityLevel.ERROR,
688                 "ignore",
689                 MemberNameCheck.class,
690                 null);
691 
692         final TreeWalkerAuditEvent event = new TreeWalkerAuditEvent(
693                 mockedContents, file.getAbsolutePath(), violation, rootAst);
694 
695         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
696         filter.setOffCommentFormat(Pattern.compile("CSOFF"));
697         filter.setOnCommentFormat(Pattern.compile("CSON"));
698         filter.setCheckFormat("MemberNameCheck");
699         filter.finishLocalSetup();
700 
701         assertWithMessage("should accept")
702                 .that(filter.accept(event)).isTrue();
703         // due to caching in filter second execution should not do parsing of comments in file
704         // so exception is not expected
705         assertWithMessage("should accept")
706                 .that(filter.accept(event)).isTrue();
707     }
708 
709 }