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.checks.sizes.LineLengthCheck.MSG_KEY;
25  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
26  
27  import java.io.File;
28  import java.io.FileNotFoundException;
29  import java.io.IOException;
30  import java.nio.file.Files;
31  import java.nio.file.StandardCopyOption;
32  import java.util.List;
33  
34  import org.junit.jupiter.api.Test;
35  import org.junit.jupiter.api.io.TempDir;
36  
37  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
38  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
39  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
40  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
41  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
42  import com.puppycrawl.tools.checkstyle.api.Violation;
43  import com.puppycrawl.tools.checkstyle.checks.coding.MagicNumberCheck;
44  import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
45  import com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineCheck;
46  import com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck;
47  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
48  
49  public class SuppressWithNearbyTextFilterTest extends AbstractModuleTestSupport {
50  
51      private static final String REGEXP_SINGLELINE_CHECK_FORMAT = "this should not appear";
52  
53      @TempDir
54      public File temporaryFolder;
55  
56      @Override
57      public String getPackageLocation() {
58          return "com/puppycrawl/tools/checkstyle/filters/suppresswithnearbytextfilter";
59      }
60  
61      @Test
62      public void testDefaultConfig() throws Exception {
63          final int expectedLineLength = 90;
64          final String pattern = "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$";
65  
66          final String[] violationMessages = {
67              "29: " + getLineLengthCheckMessage(expectedLineLength, 94),
68              "31: " + getLineLengthCheckMessage(expectedLineLength, 97),
69              "41: " + getLineLengthCheckMessage(expectedLineLength, 94),
70              "44:22: " + getCheckMessage(ConstantNameCheck.class,
71                      MSG_INVALID_PATTERN, "badConstant", pattern),
72              "47:22: " + getCheckMessage(ConstantNameCheck.class,
73                      MSG_INVALID_PATTERN, "badConstant1", pattern),
74          };
75  
76          final String[] suppressedMessages = {
77              "31: " + getLineLengthCheckMessage(expectedLineLength, 97),
78              "41: " + getLineLengthCheckMessage(expectedLineLength, 94),
79              "47:22: " + getCheckMessage(ConstantNameCheck.class,
80                      MSG_INVALID_PATTERN, "badConstant1", pattern),
81          };
82  
83          verifyFilterWithInlineConfigParser(
84              getPath("InputSuppressWithNearbyTextFilterDefaultConfig.java"),
85              violationMessages, removeSuppressed(violationMessages, suppressedMessages)
86          );
87      }
88  
89      @Test
90      public void testNearbyTextPattern() throws Exception {
91          final int expectedLineLength = 90;
92  
93          final String[] violationMessages = {
94              "15: " + getRegexpSinglelineCheckMessage(),
95              "28: " + getLineLengthCheckMessage(expectedLineLength, 94),
96              "33: " + getLineLengthCheckMessage(expectedLineLength, 93),
97              "33: " + getRegexpSinglelineCheckMessage(),
98              "39: " + getLineLengthCheckMessage(expectedLineLength, 93),
99              "44: " + getRegexpSinglelineCheckMessage(),
100             "49: " + getLineLengthCheckMessage(expectedLineLength, 95),
101             "54: " + getRegexpSinglelineCheckMessage(),
102             "58: " + getLineLengthCheckMessage(expectedLineLength, 97),
103         };
104 
105         final String[] suppressedMessages = {
106             "33: " + getLineLengthCheckMessage(expectedLineLength, 93),
107             "33: " + getRegexpSinglelineCheckMessage(),
108             "39: " + getLineLengthCheckMessage(expectedLineLength, 93),
109             "49: " + getLineLengthCheckMessage(expectedLineLength, 95),
110             "54: " + getRegexpSinglelineCheckMessage(),
111             "58: " + getLineLengthCheckMessage(expectedLineLength, 97),
112         };
113 
114         verifyFilterWithInlineConfigParser(
115             getPath("InputSuppressWithNearbyTextFilterNearbyTextPattern.css.txt"),
116             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
117         );
118     }
119 
120     @Test
121     public void testCheckPattern() throws Exception {
122         final int expectedLineLength = 80;
123 
124         final String[] violationMessages = {
125             "15: " + getRegexpSinglelineCheckMessage(),
126             "28: " + getLineLengthCheckMessage(expectedLineLength, 89),
127             "29: " + getRegexpSinglelineCheckMessage(),
128             "35: " + getLineLengthCheckMessage(expectedLineLength, 87),
129         };
130 
131         final String[] suppressedMessages = {
132             "28: " + getLineLengthCheckMessage(expectedLineLength, 89),
133             "35: " + getLineLengthCheckMessage(expectedLineLength, 87),
134         };
135 
136         verifyFilterWithInlineConfigParser(
137             getPath("InputSuppressWithNearbyTextFilterCheckPattern.bash.txt"),
138             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
139         );
140     }
141 
142     @Test
143     public void testMessagePattern() throws Exception {
144         final int expectedLineLength = 90;
145 
146         final String[] violationMessages = {
147             "15: " + getRegexpSinglelineCheckMessage(),
148             "33: " + getRegexpSinglelineCheckMessage(),
149             "38: " + getLineLengthCheckMessage(expectedLineLength, 98),
150             "42: " + getLineLengthCheckMessage(expectedLineLength, 96),
151         };
152 
153         final String[] suppressedMessages = {
154             "38: " + getLineLengthCheckMessage(expectedLineLength, 98),
155             "42: " + getLineLengthCheckMessage(expectedLineLength, 96),
156         };
157 
158         verifyFilterWithInlineConfigParser(
159             getPath("InputSuppressWithNearbyTextFilterMessagePattern.xml.txt"),
160             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
161         );
162     }
163 
164     @Test
165     public void testIdPattern() throws Exception {
166         final int expectedLineLength = 80;
167 
168         final String[] violationMessages = {
169             "16: " + getRegexpSinglelineCheckMessage(),
170             "29: " + getRegexpSinglelineCheckMessage(),
171             "34: " + getLineLengthCheckMessage(expectedLineLength, 83),
172             "38: " + getLineLengthCheckMessage(expectedLineLength, 84),
173         };
174 
175         final String[] suppressedMessages = {
176             "34: " + getLineLengthCheckMessage(expectedLineLength, 83),
177             "38: " + getLineLengthCheckMessage(expectedLineLength, 84),
178         };
179 
180         verifyFilterWithInlineConfigParser(
181             getPath("InputSuppressWithNearbyTextFilterIdPattern.html.txt"),
182             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
183         );
184     }
185 
186     @Test
187     public void testLineRangePositive3() throws Exception {
188         final int expectedLineLength = 92;
189 
190         final String[] violationMessages = {
191             "15: " + getRegexpSinglelineCheckMessage(),
192             "27: " + getLineLengthCheckMessage(expectedLineLength, 98),
193             "28: " + getLineLengthCheckMessage(expectedLineLength, 98),
194             "29: " + getLineLengthCheckMessage(expectedLineLength, 98),
195             "30: " + getLineLengthCheckMessage(expectedLineLength, 93),
196             "33: " + getRegexpSinglelineCheckMessage(),
197             "34: " + getRegexpSinglelineCheckMessage(),
198             "35: " + getRegexpSinglelineCheckMessage(),
199             "36: " + getRegexpSinglelineCheckMessage(),
200         };
201 
202         final String[] suppressedMessages = {
203             "27: " + getLineLengthCheckMessage(expectedLineLength, 98),
204             "28: " + getLineLengthCheckMessage(expectedLineLength, 98),
205             "29: " + getLineLengthCheckMessage(expectedLineLength, 98),
206             "33: " + getRegexpSinglelineCheckMessage(),
207             "34: " + getRegexpSinglelineCheckMessage(),
208             "35: " + getRegexpSinglelineCheckMessage(),
209             "36: " + getRegexpSinglelineCheckMessage(),
210         };
211 
212         verifyFilterWithInlineConfigParser(
213             getPath("InputSuppressWithNearbyTextFilterLineRangePositive3.sql.txt"),
214             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
215         );
216     }
217 
218     @Test
219     public void testLineRangeNegative2() throws Exception {
220         final int expectedLineLength = 91;
221 
222         final String[] violationMessages = {
223             "15: " + getRegexpSinglelineCheckMessage(),
224             "27: " + getLineLengthCheckMessage(expectedLineLength, 96),
225             "28: " + getLineLengthCheckMessage(expectedLineLength, 94),
226             "29: " + getLineLengthCheckMessage(expectedLineLength, 94),
227             "30: " + getLineLengthCheckMessage(expectedLineLength, 98),
228             "33: " + getRegexpSinglelineCheckMessage(),
229             "34: " + getRegexpSinglelineCheckMessage(),
230             "35: " + getRegexpSinglelineCheckMessage(),
231             "36: " + getRegexpSinglelineCheckMessage(),
232         };
233 
234         final String[] suppressedMessages = {
235             "28: " + getLineLengthCheckMessage(expectedLineLength, 94),
236             "29: " + getLineLengthCheckMessage(expectedLineLength, 94),
237             "30: " + getLineLengthCheckMessage(expectedLineLength, 98),
238             "34: " + getRegexpSinglelineCheckMessage(),
239             "35: " + getRegexpSinglelineCheckMessage(),
240             "36: " + getRegexpSinglelineCheckMessage(),
241         };
242 
243         verifyFilterWithInlineConfigParser(
244             getPath("InputSuppressWithNearbyTextFilterLineRangeNegative2.txt"),
245             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
246         );
247     }
248 
249     @Test
250     public void testVariableCheckPatternAndLineRange() throws Exception {
251         final int expectedLineLength = 85;
252 
253         final String[] violationMessages = {
254             "19: " + getLineLengthCheckMessage(expectedLineLength, 89),
255             "20: " + getLineLengthCheckMessage(expectedLineLength, 89),
256             "21: " + getLineLengthCheckMessage(expectedLineLength, 89),
257             "22: " + getLineLengthCheckMessage(expectedLineLength, 87),
258             "24: " + getLineLengthCheckMessage(expectedLineLength, 87),
259             "25: " + getLineLengthCheckMessage(expectedLineLength, 89),
260             "26: " + getLineLengthCheckMessage(expectedLineLength, 89),
261         };
262 
263         final String[] suppressedMessages = {
264             "19: " + getLineLengthCheckMessage(expectedLineLength, 89),
265             "20: " + getLineLengthCheckMessage(expectedLineLength, 89),
266             "21: " + getLineLengthCheckMessage(expectedLineLength, 89),
267             "25: " + getLineLengthCheckMessage(expectedLineLength, 89),
268             "26: " + getLineLengthCheckMessage(expectedLineLength, 89),
269         };
270 
271         verifyFilterWithInlineConfigParser(
272             getPath("InputSuppressWithNearbyTextFilter"
273                     + "VariableNearbyTextPatternAndLineRange.xml.txt"),
274             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
275         );
276     }
277 
278     @Test
279     public void testNearbyTextPatternAny() throws Exception {
280         final int expectedLineLength = 76;
281 
282         final String[] violationMessages = {
283             "18: " + getLineLengthCheckMessage(expectedLineLength, 80),
284         };
285 
286         final String[] suppressedMessages = {
287             "18: " + getLineLengthCheckMessage(expectedLineLength, 80),
288         };
289 
290         verifyFilterWithInlineConfigParser(
291             getPath("InputSuppressWithNearbyTextFilterNearbyTextPatternAny.txt"),
292             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
293         );
294     }
295 
296     @Test
297     public void testNearbyTextPatternCompactVariableCheckPattern() throws Exception {
298         final String[] violationMessages = {
299             "26:13: " + getCheckMessage(MagicNumberCheck.class, MagicNumberCheck.MSG_KEY, "42"),
300             "27:13: " + getCheckMessage(MagicNumberCheck.class, MagicNumberCheck.MSG_KEY, "43"),
301         };
302 
303         final String[] suppressedMessages = {
304             "26:13: " + getCheckMessage(MagicNumberCheck.class, MagicNumberCheck.MSG_KEY, "42"),
305         };
306 
307         verifyFilterWithInlineConfigParser(
308                 getPath("InputSuppressWithNearbyTextFilterNearbyTextPattern"
309                         + "CompactVariableCheckPattern.java"),
310                 violationMessages, removeSuppressed(violationMessages, suppressedMessages)
311         );
312     }
313 
314     @Test
315     public void testNearbyTextPatternUrlLineLengthSuppression() throws Exception {
316         final int expectedLineLength = 90;
317 
318         final String[] violationMessages = {
319             "32: " + getLineLengthCheckMessage(expectedLineLength, 98),
320             "39: " + getLineLengthCheckMessage(expectedLineLength, 97),
321         };
322 
323         final String[] suppressedMessages = {
324             "32: " + getLineLengthCheckMessage(expectedLineLength, 98),
325             "39: " + getLineLengthCheckMessage(expectedLineLength, 97),
326         };
327 
328         verifyFilterWithInlineConfigParser(
329             getPath("InputSuppressWithNearbyTextFilterNearbyTextPatternUrlLineLengthSuppression"
330                     + ".java"),
331             violationMessages, removeSuppressed(violationMessages, suppressedMessages)
332         );
333     }
334 
335     @Test
336     public void testInvalidCheckPattern() {
337         final String[] violationAndSuppressedMessages = {
338             "18: " + getLineLengthCheckMessage(80, 93),
339         };
340 
341         final CheckstyleException exc = getExpectedThrowable(
342                 CheckstyleException.class,
343                 () -> {
344                     verifyFilterWithInlineConfigParser(
345                         getPath("InputSuppressWithNearbyTextFilterInvalidCheckPattern.txt"),
346                         violationAndSuppressedMessages
347                     );
348                 });
349         final IllegalArgumentException cause = (IllegalArgumentException) exc.getCause();
350         assertWithMessage("Invalid exception message")
351             .that(cause)
352             .hasMessageThat()
353             .isEqualTo("unable to parse expanded comment a![b");
354     }
355 
356     @Test
357     public void testInvalidIdPattern() {
358         final String[] violationAndSuppressedMessages = {
359             "18: " + getLineLengthCheckMessage(80, 93),
360         };
361 
362         final CheckstyleException exc = getExpectedThrowable(
363                 CheckstyleException.class,
364                 () -> {
365                     verifyFilterWithInlineConfigParser(
366                         getPath("InputSuppressWithNearbyTextFilterInvalidIdPattern.txt"),
367                         violationAndSuppressedMessages
368                     );
369                 });
370         final IllegalArgumentException cause = (IllegalArgumentException) exc.getCause();
371         assertWithMessage("Invalid exception message")
372             .that(cause)
373             .hasMessageThat()
374             .isEqualTo("unable to parse expanded comment a![b");
375     }
376 
377     @Test
378     public void testInvalidMessagePattern() {
379         final String[] violationAndSuppressedMessages = {
380             "18: " + getLineLengthCheckMessage(80, 93),
381         };
382 
383         final CheckstyleException exc = getExpectedThrowable(
384                 CheckstyleException.class,
385                 () -> {
386                     verifyFilterWithInlineConfigParser(
387                         getPath("InputSuppressWithNearbyTextFilterInvalidMessagePattern.txt"),
388                         violationAndSuppressedMessages
389                     );
390                 });
391         final IllegalArgumentException cause = (IllegalArgumentException) exc.getCause();
392         assertWithMessage("Invalid exception message")
393             .that(cause)
394             .hasMessageThat()
395             .isEqualTo("unable to parse expanded comment a![b");
396     }
397 
398     @Test
399     public void testInvalidLineRange() {
400         final String[] violationAndSuppressedMessages = {
401             "18: " + getLineLengthCheckMessage(80, 93),
402         };
403 
404         final CheckstyleException exc = getExpectedThrowable(
405                 CheckstyleException.class,
406                 () -> {
407                     verifyFilterWithInlineConfigParser(
408                         getPath("InputSuppressWithNearbyTextFilterInvalidLineRange.txt"),
409                         violationAndSuppressedMessages
410                     );
411                 });
412         assertWithMessage("Invalid exception message")
413             .that(exc)
414             .hasCauseThat()
415             .hasMessageThat()
416             .isEqualTo("unable to parse line range"
417                     + " from 'SUPPRESS CHECKSTYLE LineLengthCheck' using a!b");
418     }
419 
420     /**
421      * Calls the filter twice and removes input file in between to ensure that
422      * the cached file is not read twice.
423      * We cannot use {@link AbstractModuleTestSupport#verifyFilterWithInlineConfigParser}
424      * because to kill pitest survival we need to remove target file between
425      * filter execution to accept violation
426      */
427     @Test
428     public void testCachingExecution() throws Exception {
429         final SuppressWithNearbyTextFilter suppressFilter = new SuppressWithNearbyTextFilter();
430         final String inputPath =
431                 getPath("InputSuppressWithNearbyTextFilterDefaultConfig.java");
432         final File tempFile = new File(temporaryFolder,
433                 "InputSuppressWithNearbyTextFilterDefaultConfig.java");
434         Files.copy(new File(inputPath).toPath(), tempFile.toPath(),
435                 StandardCopyOption.REPLACE_EXISTING);
436 
437         final AuditEvent auditEvent1 = new AuditEvent(
438                 tempFile.getPath(), tempFile.getPath(),
439                 new Violation(1, null, null, null, null,
440                         Object.class, null)
441         );
442         suppressFilter.accept(auditEvent1);
443         final boolean deleted = tempFile.delete();
444         assertWithMessage("Temporary file should be deleted.")
445                 .that(deleted).isTrue();
446         final AuditEvent auditEvent2 = new AuditEvent(
447                 tempFile.getPath(), tempFile.getPath(),
448                 new Violation(2, null, null, null, null,
449                         Object.class, null)
450         );
451         suppressFilter.accept(auditEvent2);
452 
453         assertWithMessage("Cache should handle missing file.")
454                 .that(tempFile.exists()).isFalse();
455     }
456 
457     /**
458      * Calls the filter with an audit event without a violation and asserts that the filter
459      * accepts the event. Our goal is 100% test coverage and it is not possible to create
460      * null violation with {@link AbstractModuleTestSupport#verifyFilterWithInlineConfigParser}.
461      */
462     @Test
463     public void testAcceptNullViolation() {
464         final SuppressWithNearbyTextFilter filter = new SuppressWithNearbyTextFilter();
465         final AuditEvent auditEvent = new AuditEvent(this);
466         assertWithMessage("Filter should accept audit event")
467                 .that(filter.accept(auditEvent))
468                 .isTrue();
469         assertWithMessage("File name should not be null")
470             .that(auditEvent.getFileName())
471             .isNull();
472     }
473 
474     /**
475      * Calls the filter with an audit event for a non-existing file and asserts that the filter
476      * throws an exception. Our goal is 100% test coverage and we cannot use
477      * {@link AbstractModuleTestSupport#verifyFilterWithInlineConfigParser} because
478      * {@code InlineConfigParser} does not allow to specify a non-existing file.
479      */
480     @Test
481     public void testThrowsIllegalStateExceptionWhenFileNotFound() {
482         final Violation message = new Violation(1, 1, 1, TokenTypes.CLASS_DEF,
483             "messages.properties", "key", null, SeverityLevel.ERROR, null, getClass(), null);
484         final String fileName = "nonexisting_file";
485         final AuditEvent auditEvent = new AuditEvent(this, fileName, message);
486         final SuppressWithNearbyTextFilter filter = new SuppressWithNearbyTextFilter();
487 
488         final IllegalStateException exc = getExpectedThrowable(
489                 IllegalStateException.class,
490                 () -> {
491                     filter.accept(auditEvent);
492                 });
493         assertWithMessage("Invalid exception message")
494             .that(exc.getMessage())
495             .isEqualTo("Cannot read source file: " + fileName);
496 
497         final Throwable cause = exc.getCause();
498         assertWithMessage("Exception cause has invalid type")
499                 .that(cause)
500                 .isInstanceOf(FileNotFoundException.class);
501         assertWithMessage("Invalid exception message")
502             .that(cause)
503             .hasMessageThat()
504             .isEqualTo(fileName + " (No such file or directory)");
505     }
506 
507     /**
508      * Calls the filter with an audit event for a directory and asserts that the filter accepts the
509      * event. Some violations can be on a directory instead of a file. Our goal is 100% test
510      * coverage and we cannot use
511      * {@link AbstractModuleTestSupport#verifyFilterWithInlineConfigParser} because
512      * it is impossible to specify a directory with input file configuration.
513      *
514      * @throws IOException if an error occurs while formatting the path to the input file.
515      */
516     @Test
517     public void testFilterWithDirectory() throws IOException {
518         final SuppressWithNearbyTextFilter filter = new SuppressWithNearbyTextFilter();
519         final AuditEvent event = new AuditEvent(this, getPath(""), new Violation(1, 1,
520                 "bundle", "key", null, SeverityLevel.ERROR, "moduleId", getClass(),
521                 "customMessage"));
522 
523         assertWithMessage("filter should accept directory")
524                 .that(filter.accept(event))
525                 .isTrue();
526     }
527 
528     /**
529      * Calls the filter on two consecutive real input files and asserts that the 'suppressions'
530      * internal field is cleared after each run.
531      * Our goal is 100% test coverage and we need access to the implementation details
532      * so {@link AbstractModuleTestSupport#verifyFilterWithInlineConfigParser} is not used.
533      *
534      * @throws IOException if an error occurs while formatting the path to the input file.
535      */
536     @Test
537     public void testSuppressionsAreClearedEachRun() throws IOException {
538         final SuppressWithNearbyTextFilter filter = new SuppressWithNearbyTextFilter();
539 
540         final List<?> suppressions1 = getSuppressionsAfterExecution(filter,
541                 getPath("InputSuppressWithNearbyTextFilterDefaultConfig.java"));
542         assertWithMessage("Invalid suppressions size")
543                 .that(suppressions1)
544                 .hasSize(3);
545 
546         final List<?> suppressions2 = getSuppressionsAfterExecution(filter,
547                 getPath("InputSuppressWithNearbyTextFilterOneLineText.txt"));
548         assertWithMessage("Invalid suppressions size")
549                 .that(suppressions2)
550                 .isEmpty();
551     }
552 
553     /**
554      * Calls the filter on two consecutive real input files and asserts that the
555      * 'cachedFileAbsolutePath' internal field has changed after the first run.
556      * Our goal is 100% test coverage and we need access to the implementation details
557      * so {@link AbstractModuleTestSupport#verifyFilterWithInlineConfigParser} is not used.
558      *
559      * @throws IOException if an error occurs while formatting the path to the input file.
560      */
561     @Test
562     public void testCachedFileAbsolutePathHasChangedEachRun() throws IOException {
563         final SuppressWithNearbyTextFilter filter = new SuppressWithNearbyTextFilter();
564 
565         final String cachedFileAbsolutePath1 = getCachedFileAbsolutePathAfterExecution(filter,
566                 getPath("InputSuppressWithNearbyTextFilterDefaultConfig.java"));
567         final String cachedFileAbsolutePath2 = getCachedFileAbsolutePathAfterExecution(filter,
568                 getPath("InputSuppressWithNearbyTextFilterOneLineText.txt"));
569 
570         assertWithMessage("cachedFileAbsolutePath has not changed")
571                 .that(cachedFileAbsolutePath1)
572                 .isNotEqualTo(cachedFileAbsolutePath2);
573     }
574 
575     /**
576      * Calls the filter with a real input file and returns a list of identified suppressions.
577      *
578      * @return {@code Suppression} list.
579      */
580     private static List<?> getSuppressionsAfterExecution(SuppressWithNearbyTextFilter filter,
581                                                          String filename) {
582         final AuditEvent dummyEvent = buildDummyAuditEvent(filename);
583         filter.accept(dummyEvent);
584         return TestUtil.getInternalState(filter, "suppressions", List.class);
585     }
586 
587     /**
588      * Calls the filter with a real input file and returns the
589      * state of the 'cachedFileAbsolutePath' field.
590      *
591      * @return {@code cachedFileAbsolutePath} value.
592      */
593     private static String getCachedFileAbsolutePathAfterExecution(SuppressWithNearbyTextFilter
594                                                                            filter,
595                                                                   String filename) {
596         final AuditEvent dummyEvent = buildDummyAuditEvent(filename);
597         filter.accept(dummyEvent);
598         return TestUtil.getInternalState(filter, "cachedFileAbsolutePath", String.class);
599     }
600 
601     /**
602      * Builds a dummy audit event with a violation for the given file name.
603      *
604      * @param filename input file name.
605      * @return dummy {@code AuditEvent} instance.
606      */
607     private static AuditEvent buildDummyAuditEvent(String filename) {
608         final Violation violation = new Violation(1, null, null,
609                 null, null, Object.class, null);
610         return new AuditEvent("", filename, violation);
611     }
612 
613     private static String getLineLengthCheckMessage(int expectedLength, int actualLength) {
614         return getCheckMessage(LineLengthCheck.class, MSG_KEY, expectedLength, actualLength);
615     }
616 
617     private static String getRegexpSinglelineCheckMessage() {
618         final String msgRegexExceeded = "regexp.exceeded";
619         return getCheckMessage(RegexpSinglelineCheck.class,
620                 msgRegexExceeded, REGEXP_SINGLELINE_CHECK_FORMAT);
621     }
622 }