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