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