View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck.MSG_INVALID_PATTERN;
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.fail;
26  
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.List;
30  import java.util.stream.Collectors;
31  
32  import org.junit.Assert;
33  import org.junit.Test;
34  import org.powermock.reflect.Whitebox;
35  
36  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
37  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
38  import com.puppycrawl.tools.checkstyle.TreeWalker;
39  import com.puppycrawl.tools.checkstyle.TreeWalkerAuditEvent;
40  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41  import com.puppycrawl.tools.checkstyle.api.Configuration;
42  import com.puppycrawl.tools.checkstyle.api.FileContents;
43  import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
44  import com.puppycrawl.tools.checkstyle.checks.coding.IllegalCatchCheck;
45  import com.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck;
46  import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
47  import com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck;
48  import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
49  import nl.jqno.equalsverifier.EqualsVerifier;
50  
51  public class SuppressionCommentFilterTest
52      extends AbstractModuleTestSupport {
53      private static final String[] ALL_MESSAGES = {
54          "13:17: "
55              + getCheckMessage(AbstractNameCheck.class,
56                  MSG_INVALID_PATTERN, "I", "^[a-z][a-zA-Z0-9]*$"),
57          "16:17: "
58              + getCheckMessage(AbstractNameCheck.class,
59                  MSG_INVALID_PATTERN, "J", "^[a-z][a-zA-Z0-9]*$"),
60          "19:17: "
61              + getCheckMessage(AbstractNameCheck.class,
62                  MSG_INVALID_PATTERN, "K", "^[a-z][a-zA-Z0-9]*$"),
63          "22:17: "
64              + getCheckMessage(AbstractNameCheck.class,
65                  MSG_INVALID_PATTERN, "L", "^[a-z][a-zA-Z0-9]*$"),
66          "23:30: "
67              + getCheckMessage(AbstractNameCheck.class,
68                  MSG_INVALID_PATTERN, "m", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
69          "27:17: "
70              + getCheckMessage(AbstractNameCheck.class,
71                  MSG_INVALID_PATTERN, "M2", "^[a-z][a-zA-Z0-9]*$"),
72          "28:30: "
73              + getCheckMessage(AbstractNameCheck.class,
74                  MSG_INVALID_PATTERN, "n", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
75          "32:17: "
76              + getCheckMessage(AbstractNameCheck.class,
77                  MSG_INVALID_PATTERN, "P", "^[a-z][a-zA-Z0-9]*$"),
78          "35:17: "
79              + getCheckMessage(AbstractNameCheck.class,
80                  MSG_INVALID_PATTERN, "Q", "^[a-z][a-zA-Z0-9]*$"),
81          "38:17: "
82              + getCheckMessage(AbstractNameCheck.class,
83                  MSG_INVALID_PATTERN, "R", "^[a-z][a-zA-Z0-9]*$"),
84          "39:30: "
85              + getCheckMessage(AbstractNameCheck.class,
86                  MSG_INVALID_PATTERN, "s", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
87          "43:17: "
88              + getCheckMessage(AbstractNameCheck.class,
89                  MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
90          "64:23: "
91              + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
92          "71:11: "
93              + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
94          "77:11: "
95              + getCheckMessage(IllegalCatchCheck.class,
96                  IllegalCatchCheck.MSG_KEY, "RuntimeException"),
97          "78:11: "
98              + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
99          "86:31: "
100             + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
101     };
102 
103     @Override
104     protected String getPackageLocation() {
105         return "com/puppycrawl/tools/checkstyle/filters/suppressioncommentfilter";
106     }
107 
108     @Test
109     public void testNone() throws Exception {
110         final DefaultConfiguration filterConfig = null;
111         final String[] suppressed = CommonUtils.EMPTY_STRING_ARRAY;
112         verifySuppressed(filterConfig, suppressed);
113     }
114 
115     //Suppress all checks between default comments
116     @Test
117     public void testDefault() throws Exception {
118         final DefaultConfiguration filterConfig =
119             createModuleConfig(SuppressionCommentFilter.class);
120         final String[] suppressed = {
121             "16:17: "
122                 + getCheckMessage(AbstractNameCheck.class,
123                     MSG_INVALID_PATTERN, "J", "^[a-z][a-zA-Z0-9]*$"),
124             "43:17: "
125                 + getCheckMessage(AbstractNameCheck.class,
126                     MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
127             "64:23: "
128                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
129             "71:11: "
130                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
131             "86:31: "
132                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
133         };
134         verifySuppressed(filterConfig, suppressed);
135     }
136 
137     @Test
138     public void testCheckC() throws Exception {
139         final DefaultConfiguration filterConfig =
140             createModuleConfig(SuppressionCommentFilter.class);
141         filterConfig.addAttribute("checkC", "false");
142         final String[] suppressed = {
143             "43:17: "
144                 + getCheckMessage(AbstractNameCheck.class,
145                     MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
146             "64:23: "
147                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
148             "71:11: "
149                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
150         };
151         verifySuppressed(filterConfig, suppressed);
152     }
153 
154     @Test
155     public void testCheckCpp() throws Exception {
156         final DefaultConfiguration filterConfig =
157             createModuleConfig(SuppressionCommentFilter.class);
158         filterConfig.addAttribute("checkCPP", "false");
159         final String[] suppressed = {
160             "16:17: "
161                 + getCheckMessage(AbstractNameCheck.class,
162                     MSG_INVALID_PATTERN, "J", "^[a-z][a-zA-Z0-9]*$"),
163             "86:31: "
164                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
165         };
166         verifySuppressed(filterConfig, suppressed);
167     }
168 
169     //Suppress all checks between CS_OFF and CS_ON
170     @Test
171     public void testOffFormat() throws Exception {
172         final DefaultConfiguration filterConfig =
173             createModuleConfig(SuppressionCommentFilter.class);
174         filterConfig.addAttribute("offCommentFormat", "CS_OFF");
175         filterConfig.addAttribute("onCommentFormat", "CS_ON");
176         final String[] suppressed = {
177             "32:17: "
178                 + getCheckMessage(AbstractNameCheck.class,
179                     MSG_INVALID_PATTERN, "P", "^[a-z][a-zA-Z0-9]*$"),
180             "38:17: "
181                 + getCheckMessage(AbstractNameCheck.class,
182                     MSG_INVALID_PATTERN, "R", "^[a-z][a-zA-Z0-9]*$"),
183             "39:30: "
184                 + getCheckMessage(AbstractNameCheck.class,
185                     MSG_INVALID_PATTERN, "s", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
186             "42:17: "
187                 + getCheckMessage(AbstractNameCheck.class,
188                     MSG_INVALID_PATTERN, "T", "^[a-z][a-zA-Z0-9]*$"),
189         };
190         verifySuppressed(filterConfig, suppressed);
191     }
192 
193     //Test suppression of checks of only one type
194     //Suppress only ConstantNameCheck between CS_OFF and CS_ON
195     @Test
196     public void testOffFormatCheck() throws Exception {
197         final DefaultConfiguration filterConfig =
198             createModuleConfig(SuppressionCommentFilter.class);
199         filterConfig.addAttribute("offCommentFormat", "CS_OFF");
200         filterConfig.addAttribute("onCommentFormat", "CS_ON");
201         filterConfig.addAttribute("checkFormat", "ConstantNameCheck");
202         final String[] suppressed = {
203             "39:30: "
204                 + getCheckMessage(AbstractNameCheck.class,
205                     MSG_INVALID_PATTERN, "s", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
206         };
207         verifySuppressed(filterConfig, suppressed);
208     }
209 
210     @Test
211     public void testArgumentSuppression() throws Exception {
212         final DefaultConfiguration filterConfig =
213             createModuleConfig(SuppressionCommentFilter.class);
214         filterConfig.addAttribute("offCommentFormat", "IllegalCatchCheck OFF\\: (\\w+)");
215         filterConfig.addAttribute("onCommentFormat", "IllegalCatchCheck ON\\: (\\w+)");
216         filterConfig.addAttribute("checkFormat", "IllegalCatchCheck");
217         // -@cs[CheckstyleTestMakeup] need to test dynamic property
218         filterConfig.addAttribute("messageFormat",
219                 "^" + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "$1")
220                         + "*$");
221         final String[] suppressed = {
222             "78:11: "
223                 + getCheckMessage(IllegalCatchCheck.class, IllegalCatchCheck.MSG_KEY, "Exception"),
224         };
225         verifySuppressed(filterConfig, suppressed);
226     }
227 
228     @Test
229     public void testExpansion() throws Exception {
230         final DefaultConfiguration filterConfig =
231             createModuleConfig(SuppressionCommentFilter.class);
232         filterConfig.addAttribute("offCommentFormat", "CSOFF\\: ([\\w\\|]+)");
233         filterConfig.addAttribute("onCommentFormat", "CSON\\: ([\\w\\|]+)");
234         filterConfig.addAttribute("checkFormat", "$1");
235         final String[] suppressed = {
236             "22:17: "
237                 + getCheckMessage(AbstractNameCheck.class,
238                     MSG_INVALID_PATTERN, "L", "^[a-z][a-zA-Z0-9]*$"),
239             "23:30: "
240                 + getCheckMessage(AbstractNameCheck.class,
241                     MSG_INVALID_PATTERN, "m", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
242             "28:30: "
243                 + getCheckMessage(AbstractNameCheck.class,
244                     MSG_INVALID_PATTERN, "n", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
245         };
246         verifySuppressed(filterConfig, suppressed);
247     }
248 
249     @Test
250     public void testMessage() throws Exception {
251         final DefaultConfiguration filterConfig =
252             createModuleConfig(SuppressionCommentFilter.class);
253         filterConfig.addAttribute("onCommentFormat", "UNUSED ON\\: (\\w+)");
254         filterConfig.addAttribute("offCommentFormat", "UNUSED OFF\\: (\\w+)");
255         filterConfig.addAttribute("checkFormat", "Unused");
256         filterConfig.addAttribute("messageFormat", "^Unused \\w+ '$1'.$");
257         final String[] suppressed = CommonUtils.EMPTY_STRING_ARRAY;
258         verifySuppressed(filterConfig, suppressed);
259     }
260 
261     private void verifySuppressed(Configuration moduleConfig,
262             String... aSuppressed)
263             throws Exception {
264         verifySuppressed(moduleConfig, getPath("InputSuppressionCommentFilter.java"),
265                ALL_MESSAGES, aSuppressed);
266     }
267 
268     private void verifySuppressed(Configuration moduleConfig, String fileName,
269             String[] expectedViolations, String... suppressedViolations) throws Exception {
270         final DefaultConfiguration memberNameCheckConfig =
271                 createModuleConfig(MemberNameCheck.class);
272         memberNameCheckConfig.addAttribute("id", "ignore");
273 
274         final DefaultConfiguration constantNameCheckConfig =
275             createModuleConfig(ConstantNameCheck.class);
276         constantNameCheckConfig.addAttribute("id", null);
277 
278         final DefaultConfiguration treewalkerConfig = createModuleConfig(TreeWalker.class);
279         treewalkerConfig.addChild(memberNameCheckConfig);
280         treewalkerConfig.addChild(constantNameCheckConfig);
281         treewalkerConfig.addChild(createModuleConfig(IllegalCatchCheck.class));
282 
283         if (moduleConfig != null) {
284             treewalkerConfig.addChild(moduleConfig);
285         }
286 
287         final DefaultConfiguration checkerConfig = createRootConfig(treewalkerConfig);
288 
289         verify(checkerConfig, fileName,
290                 removeSuppressed(expectedViolations, suppressedViolations));
291     }
292 
293     private static String[] removeSuppressed(String[] from, String... remove) {
294         final Collection<String> coll = Arrays.stream(from).collect(Collectors.toList());
295         coll.removeAll(Arrays.asList(remove));
296         return coll.toArray(new String[coll.size()]);
297     }
298 
299     @Test
300     public void testEqualsAndHashCodeOfTagClass() {
301         EqualsVerifier.forClass(SuppressionCommentFilter.Tag.class).usingGetClass().verify();
302     }
303 
304     @Test
305     public void testToStringOfTagClass() {
306         final SuppressionCommentFilter.Tag tag = new SuppressionCommentFilter.Tag(
307                 0, 1, "text",
308                 SuppressionCommentFilter.TagType.OFF, new SuppressionCommentFilter()
309         );
310 
311         assertEquals("Invalid toString result",
312             "Tag[text='text', line=0, column=1, type=OFF,"
313                     + " tagCheckRegexp=.*, tagMessageRegexp=null]", tag.toString());
314     }
315 
316     @Test
317     public void testInvalidCheckFormat() throws Exception {
318         final DefaultConfiguration filterConfig =
319             createModuleConfig(SuppressionCommentFilter.class);
320         filterConfig.addAttribute("checkFormat", "e[l");
321 
322         try {
323             final String[] suppressed = CommonUtils.EMPTY_STRING_ARRAY;
324             verifySuppressed(filterConfig, suppressed);
325             fail("Exception is expected");
326         }
327         catch (CheckstyleException ex) {
328             final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause();
329             assertEquals("Invalid exception message",
330                 "unable to parse expanded comment e[l", cause.getMessage());
331         }
332     }
333 
334     @Test
335     public void testInvalidMessageFormat() throws Exception {
336         final DefaultConfiguration filterConfig =
337             createModuleConfig(SuppressionCommentFilter.class);
338         filterConfig.addAttribute("messageFormat", "e[l");
339 
340         try {
341             final String[] suppressed = CommonUtils.EMPTY_STRING_ARRAY;
342             verifySuppressed(filterConfig, suppressed);
343             fail("Exception is expected");
344         }
345         catch (CheckstyleException ex) {
346             final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause();
347             assertEquals("Invalid exception message",
348                 "unable to parse expanded comment e[l", cause.getMessage());
349         }
350 
351     }
352 
353     @Test
354     public void testAcceptNullLocalizedMessage() {
355         final SuppressionCommentFilter filter = new SuppressionCommentFilter();
356         final TreeWalkerAuditEvent auditEvent = new TreeWalkerAuditEvent(null, null, null, null);
357         Assert.assertTrue("Filter should accept audit event", filter.accept(auditEvent));
358         Assert.assertNull("File name should not be null", auditEvent.getFileName());
359     }
360 
361     @Test
362     public void testSuppressById() throws Exception {
363         final DefaultConfiguration filterConfig =
364             createModuleConfig(SuppressionCommentFilter.class);
365         filterConfig.addAttribute("offCommentFormat", "CSOFF (\\w+) \\(\\w+\\)");
366         filterConfig.addAttribute("onCommentFormat", "CSON (\\w+)");
367         filterConfig.addAttribute("checkFormat", "$1");
368         final String[] suppressedViolationMessages = {
369             "6:17: "
370                 + getCheckMessage(AbstractNameCheck.class,
371                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
372             "12:9: "
373                 + getCheckMessage(AbstractNameCheck.class,
374                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
375             };
376         final String[] expectedViolationMessages = {
377             "6:17: "
378                 + getCheckMessage(AbstractNameCheck.class,
379                     MSG_INVALID_PATTERN, "A1", "^[a-z][a-zA-Z0-9]*$"),
380             "9:30: "
381                 + getCheckMessage(AbstractNameCheck.class,
382                     MSG_INVALID_PATTERN, "abc", "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"),
383             "12:9: "
384                 + getCheckMessage(AbstractNameCheck.class,
385                     MSG_INVALID_PATTERN, "line_length", "^[a-z][a-zA-Z0-9]*$"),
386             "15:18: "
387                 + getCheckMessage(AbstractNameCheck.class,
388                     MSG_INVALID_PATTERN, "ID", "^[a-z][a-zA-Z0-9]*$"),
389             };
390 
391         verifySuppressed(filterConfig, getPath("InputSuppressionCommentFilterSuppressById.java"),
392                 expectedViolationMessages, suppressedViolationMessages);
393     }
394 
395     @Test
396     public void testFindNearestMatchDontAllowSameColumn() {
397         final SuppressionCommentFilter suppressionCommentFilter = new SuppressionCommentFilter();
398         final FileContents contents =
399                 new FileContents("filename", "//CHECKSTYLE:OFF: ConstantNameCheck", "line2");
400         contents.reportSingleLineComment(1, 0);
401         final TreeWalkerAuditEvent dummyEvent = new TreeWalkerAuditEvent(contents, "filename",
402                 new LocalizedMessage(1, null, null, null, null, Object.class, null), null);
403         final boolean result = suppressionCommentFilter.accept(dummyEvent);
404         assertFalse("Filter should not accept event", result);
405     }
406 
407     @Test
408     public void testTagsAreClearedEachRun() {
409         final SuppressionCommentFilter suppressionCommentFilter = new SuppressionCommentFilter();
410         final FileContents contents =
411                 new FileContents("filename", "//CHECKSTYLE:OFF", "line2");
412         contents.reportSingleLineComment(1, 0);
413         final TreeWalkerAuditEvent dummyEvent = new TreeWalkerAuditEvent(contents, "filename",
414                 new LocalizedMessage(1, null, null, null, null, Object.class, null), null);
415         suppressionCommentFilter.accept(dummyEvent);
416         final FileContents contents2 =
417                 new FileContents("filename2", "some line", "//CHECKSTYLE:OFF");
418         contents2.reportSingleLineComment(2, 0);
419         final TreeWalkerAuditEvent dummyEvent2 = new TreeWalkerAuditEvent(contents2, "filename",
420                 new LocalizedMessage(1, null, null, null, null, Object.class, null), null);
421         suppressionCommentFilter.accept(dummyEvent2);
422         final List<SuppressionCommentFilter.Tag> tags =
423                 Whitebox.getInternalState(suppressionCommentFilter, "tags");
424         assertEquals("Invalid tags size", 1, tags.size());
425     }
426 }