1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23 import static com.puppycrawl.tools.checkstyle.checks.coding.UnusedLocalVariableCheck.MSG_UNUSED_LOCAL_VARIABLE;
24
25 import java.io.File;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.Method;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Optional;
31
32 import org.junit.jupiter.api.AfterEach;
33 import org.junit.jupiter.api.Test;
34
35 import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
36 import com.puppycrawl.tools.checkstyle.Checker;
37 import com.puppycrawl.tools.checkstyle.DetailAstImpl;
38 import com.puppycrawl.tools.checkstyle.JavaParser;
39 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
40 import com.puppycrawl.tools.checkstyle.api.DetailAST;
41 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
42 import com.puppycrawl.tools.checkstyle.api.Violation;
43 import com.puppycrawl.tools.checkstyle.checks.coding.UnusedLocalVariableCheck;
44 import com.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck;
45 import com.puppycrawl.tools.checkstyle.checks.naming.ConstantNameCheck;
46 import com.puppycrawl.tools.checkstyle.checks.naming.MemberNameCheck;
47 import com.puppycrawl.tools.checkstyle.checks.sizes.ParameterNumberCheck;
48 import com.puppycrawl.tools.checkstyle.checks.whitespace.AbstractParenPadCheck;
49 import com.puppycrawl.tools.checkstyle.checks.whitespace.TypecastParenPadCheck;
50 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
51 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
52
53 public class SuppressWarningsHolderTest extends AbstractModuleTestSupport {
54
55 @Override
56 protected String getPackageLocation() {
57 return "com/puppycrawl/tools/checkstyle/checks/suppresswarningsholder";
58 }
59
60 @AfterEach
61 public void cleanUp() {
62
63
64 new SuppressWarningsHolder().beginTree(null);
65
66 final Map<String, String> map = TestUtil.getInternalStaticState(
67 SuppressWarningsHolder.class, "CHECK_ALIAS_MAP");
68 map.clear();
69 }
70
71 @Test
72 public void testGet() {
73 final SuppressWarningsHolder checkObj = new SuppressWarningsHolder();
74 final int[] expected = {TokenTypes.ANNOTATION};
75 assertWithMessage("Required token array differs from expected")
76 .that(checkObj.getRequiredTokens())
77 .isEqualTo(expected);
78 assertWithMessage("Required token array differs from expected")
79 .that(checkObj.getAcceptableTokens())
80 .isEqualTo(expected);
81 }
82
83 @Test
84 public void testOnComplexAnnotations() throws Exception {
85 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
86
87 verifyWithInlineConfigParser(getPath("InputSuppressWarningsHolder.java"), expected);
88 }
89
90 @Test
91 public void testOnComplexAnnotationsNonConstant() throws Exception {
92 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
93
94 verifyWithInlineConfigParser(
95 getNonCompilablePath("InputSuppressWarningsHolderNonConstant.java"), expected);
96 }
97
98 @Test
99 public void testCustomAnnotation() throws Exception {
100 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
101
102 verifyWithInlineConfigParser(getPath("InputSuppressWarningsHolder5.java"), expected);
103 }
104
105 @Test
106 public void testAll() throws Exception {
107 final String[] expected = {
108 "21:23: "
109 + getCheckMessage(TypecastParenPadCheck.class,
110 AbstractParenPadCheck.MSG_WS_NOT_PRECEDED, ")"),
111 };
112
113 verifyWithInlineConfigParser(getPath("InputSuppressWarningsHolder6.java"), expected);
114 }
115
116 @Test
117 public void testGetDefaultAlias() {
118 assertWithMessage("Default alias differs from expected")
119 .that(SuppressWarningsHolder.getDefaultAlias("SomeName"))
120 .isEqualTo("somename");
121 assertWithMessage("Default alias differs from expected")
122 .that(SuppressWarningsHolder.getDefaultAlias("SomeNameCheck"))
123 .isEqualTo("somename");
124 }
125
126 @Test
127 public void testSetAliasListEmpty() {
128 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
129 holder.setAliasList("");
130 assertWithMessage("Empty alias list should not be set")
131 .that(SuppressWarningsHolder.getAlias(""))
132 .isEqualTo("");
133 }
134
135 @Test
136 public void testSetAliasListCorrect() {
137 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
138 holder.setAliasList("alias=value");
139 assertWithMessage("Alias differs from expected")
140 .that(SuppressWarningsHolder.getAlias("alias"))
141 .isEqualTo("value");
142 }
143
144 @Test
145 public void testSetAliasListWrong() {
146 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
147
148 try {
149 holder.setAliasList("=SomeAlias");
150 assertWithMessage("Exception expected").fail();
151 }
152 catch (IllegalArgumentException ex) {
153 assertWithMessage("Error message is unexpected")
154 .that(ex.getMessage())
155 .isEqualTo("'=' expected in alias list item: =SomeAlias");
156 }
157 }
158
159 @Test
160 public void testIsSuppressed() throws Exception {
161 populateHolder("MockEntry", 100, 100, 350, 350);
162 final AuditEvent event = createAuditEvent("check", 100, 10);
163
164 assertWithMessage("Event is not suppressed")
165 .that(SuppressWarningsHolder.isSuppressed(event))
166 .isFalse();
167 }
168
169 @Test
170 public void testIsSuppressedByName() throws Exception {
171 populateHolder("check", 100, 100, 350, 350);
172 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
173 final AuditEvent event = createAuditEvent("id", 110, 10);
174 holder.setAliasList(MemberNameCheck.class.getName() + "=check");
175
176 assertWithMessage("Event is not suppressed")
177 .that(SuppressWarningsHolder.isSuppressed(event))
178 .isTrue();
179 }
180
181 @Test
182 public void testIsSuppressedByModuleId() throws Exception {
183 populateHolder("check", 100, 100, 350, 350);
184 final AuditEvent event = createAuditEvent("check", 350, 350);
185
186 assertWithMessage("Event is not suppressed")
187 .that(SuppressWarningsHolder.isSuppressed(event))
188 .isTrue();
189 }
190
191 @Test
192 public void testIsSuppressedAfterEventEnd() throws Exception {
193 populateHolder("check", 100, 100, 350, 350);
194 final AuditEvent event = createAuditEvent("check", 350, 352);
195
196 assertWithMessage("Event is not suppressed")
197 .that(SuppressWarningsHolder.isSuppressed(event))
198 .isFalse();
199 }
200
201 @Test
202 public void testIsSuppressedAfterEventEnd2() throws Exception {
203 populateHolder("check", 100, 100, 350, 350);
204 final AuditEvent event = createAuditEvent("check", 400, 10);
205
206 assertWithMessage("Event is not suppressed")
207 .that(SuppressWarningsHolder.isSuppressed(event))
208 .isFalse();
209 }
210
211 @Test
212 public void testIsSuppressedAfterEventStart() throws Exception {
213 populateHolder("check", 100, 100, 350, 350);
214 final AuditEvent event = createAuditEvent("check", 100, 100);
215
216 assertWithMessage("Event is not suppressed")
217 .that(SuppressWarningsHolder.isSuppressed(event))
218 .isTrue();
219 }
220
221 @Test
222 public void testIsSuppressedAfterEventStart2() throws Exception {
223 populateHolder("check", 100, 100, 350, 350);
224 final AuditEvent event = createAuditEvent("check", 100, 0);
225
226 assertWithMessage("Event is not suppressed")
227 .that(SuppressWarningsHolder.isSuppressed(event))
228 .isTrue();
229 }
230
231 @Test
232 public void testIsSuppressedWithAllArgument() throws Exception {
233 populateHolder("all", 100, 100, 350, 350);
234
235 final Checker source = new Checker();
236 final Violation firstViolationForTest =
237 new Violation(100, 10, null, null, null, "id", MemberNameCheck.class, "msg");
238 final AuditEvent firstEventForTest =
239 new AuditEvent(source, "fileName", firstViolationForTest);
240 assertWithMessage("Event is suppressed")
241 .that(SuppressWarningsHolder.isSuppressed(firstEventForTest))
242 .isFalse();
243
244 final Violation secondViolationForTest =
245 new Violation(100, 150, null, null, null, "id", MemberNameCheck.class, "msg");
246 final AuditEvent secondEventForTest =
247 new AuditEvent(source, "fileName", secondViolationForTest);
248 assertWithMessage("Event is not suppressed")
249 .that(SuppressWarningsHolder.isSuppressed(secondEventForTest))
250 .isTrue();
251
252 final Violation thirdViolationForTest =
253 new Violation(200, 1, null, null, null, "id", MemberNameCheck.class, "msg");
254 final AuditEvent thirdEventForTest =
255 new AuditEvent(source, "fileName", thirdViolationForTest);
256 assertWithMessage("Event is not suppressed")
257 .that(SuppressWarningsHolder.isSuppressed(thirdEventForTest))
258 .isTrue();
259 }
260
261 @Test
262 public void testAnnotationInTry() throws Exception {
263 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
264
265 verifyWithInlineConfigParser(getPath("InputSuppressWarningsHolder2.java"), expected);
266 }
267
268 @Test
269 public void testEmptyAnnotation() throws Exception {
270 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
271
272 verifyWithInlineConfigParser(getPath("InputSuppressWarningsHolder3.java"), expected);
273 }
274
275 @Test
276 public void testGetAllAnnotationValuesWrongArg() throws ReflectiveOperationException {
277 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
278 final Method getAllAnnotationValues = holder.getClass()
279 .getDeclaredMethod("getAllAnnotationValues", DetailAST.class);
280 getAllAnnotationValues.setAccessible(true);
281
282 final DetailAstImpl methodDef = new DetailAstImpl();
283 methodDef.setType(TokenTypes.METHOD_DEF);
284 methodDef.setText("Method Def");
285 methodDef.setLineNo(0);
286 methodDef.setColumnNo(0);
287
288 final DetailAstImpl lparen = new DetailAstImpl();
289 lparen.setType(TokenTypes.LPAREN);
290
291 final DetailAstImpl parent = new DetailAstImpl();
292 parent.addChild(lparen);
293 parent.addChild(methodDef);
294
295 try {
296 getAllAnnotationValues.invoke(holder, parent);
297 assertWithMessage("Exception expected").fail();
298 }
299 catch (ReflectiveOperationException ex) {
300 assertWithMessage("Error type is unexpected")
301 .that(ex)
302 .hasCauseThat()
303 .isInstanceOf(IllegalArgumentException.class);
304 assertWithMessage("Error message is unexpected")
305 .that(ex)
306 .hasCauseThat()
307 .hasMessageThat()
308 .isEqualTo("Unexpected AST: Method Def[0x0]");
309 }
310 }
311
312 @Test
313 public void testGetAnnotationValuesWrongArg() throws ReflectiveOperationException {
314 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
315 final Method getAllAnnotationValues = holder.getClass()
316 .getDeclaredMethod("getAnnotationValues", DetailAST.class);
317 getAllAnnotationValues.setAccessible(true);
318
319 final DetailAstImpl methodDef = new DetailAstImpl();
320 methodDef.setType(TokenTypes.METHOD_DEF);
321 methodDef.setText("Method Def");
322 methodDef.setLineNo(0);
323 methodDef.setColumnNo(0);
324
325 try {
326 getAllAnnotationValues.invoke(holder, methodDef);
327 assertWithMessage("Exception expected").fail();
328 }
329 catch (ReflectiveOperationException ex) {
330 assertWithMessage("Error type is unexpected")
331 .that(ex)
332 .hasCauseThat()
333 .isInstanceOf(IllegalArgumentException.class);
334 assertWithMessage("Error message is unexpected")
335 .that(ex)
336 .hasCauseThat()
337 .hasMessageThat()
338 .isEqualTo("Expression or annotation array initializer AST expected: "
339 + "Method Def[0x0]");
340 }
341 }
342
343 @Test
344 public void testGetAnnotationTargetWrongArg() throws ReflectiveOperationException {
345 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
346 final Method getAnnotationTarget = holder.getClass()
347 .getDeclaredMethod("getAnnotationTarget", DetailAST.class);
348 getAnnotationTarget.setAccessible(true);
349
350 final DetailAstImpl methodDef = new DetailAstImpl();
351 methodDef.setType(TokenTypes.METHOD_DEF);
352 methodDef.setText("Method Def");
353
354 final DetailAstImpl parent = new DetailAstImpl();
355 parent.setType(TokenTypes.ASSIGN);
356 parent.setText("Parent ast");
357 parent.addChild(methodDef);
358 parent.setLineNo(0);
359 parent.setColumnNo(0);
360
361 try {
362 getAnnotationTarget.invoke(holder, methodDef);
363 assertWithMessage("Exception expected").fail();
364 }
365 catch (ReflectiveOperationException ex) {
366 assertWithMessage("Error type is unexpected")
367 .that(ex)
368 .hasCauseThat()
369 .isInstanceOf(IllegalArgumentException.class);
370 assertWithMessage("Error message is unexpected")
371 .that(ex)
372 .hasCauseThat()
373 .hasMessageThat()
374 .isEqualTo("Unexpected container AST: Parent ast[0x0]");
375 }
376 }
377
378 @Test
379 public void testAstWithoutChildren() {
380 final SuppressWarningsHolder holder = new SuppressWarningsHolder();
381 final DetailAstImpl methodDef = new DetailAstImpl();
382 methodDef.setType(TokenTypes.METHOD_DEF);
383
384 try {
385 holder.visitToken(methodDef);
386 assertWithMessage("Exception expected").fail();
387 }
388 catch (IllegalArgumentException ex) {
389 assertWithMessage("Error message is unexpected")
390 .that(ex.getMessage())
391 .isEqualTo("Identifier AST expected, but get null.");
392 }
393 }
394
395 @Test
396 public void testAnnotationWithFullName() throws Exception {
397 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
398
399 verifyWithInlineConfigParser(getPath("InputSuppressWarningsHolder4.java"), expected);
400 }
401
402 @Test
403 public void testSuppressWarningsAsAnnotationProperty() throws Exception {
404 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
405
406 verifyWithInlineConfigParser(getPath("InputSuppressWarningsHolder7.java"), expected);
407 }
408
409 @Test
410 @SuppressWarnings("unchecked")
411 public void testClearState() throws Exception {
412 final SuppressWarningsHolder check = new SuppressWarningsHolder();
413
414 final Optional<DetailAST> annotationDef = TestUtil.findTokenInAstByPredicate(
415 JavaParser.parseFile(
416 new File(getPath("InputSuppressWarningsHolder.java")),
417 JavaParser.Options.WITHOUT_COMMENTS),
418 ast -> ast.getType() == TokenTypes.ANNOTATION);
419
420 assertWithMessage("Ast should contain ANNOTATION")
421 .that(annotationDef.isPresent())
422 .isTrue();
423 assertWithMessage("State is not cleared on beginTree")
424 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check,
425 annotationDef.orElseThrow(), "ENTRIES",
426 entries -> ((ThreadLocal<List<Object>>) entries).get().isEmpty()))
427 .isTrue();
428 }
429
430 private static void populateHolder(String checkName, int firstLine,
431 int firstColumn, int lastLine,
432 int lastColumn) throws Exception {
433 final Class<?> entry = Class
434 .forName("com.puppycrawl.tools.checkstyle.checks.SuppressWarningsHolder$Entry");
435 final Constructor<?> entryConstr = entry.getDeclaredConstructor(String.class, int.class,
436 int.class, int.class, int.class);
437 entryConstr.setAccessible(true);
438
439 final Object entryInstance = entryConstr.newInstance(checkName, firstLine,
440 firstColumn, lastLine, lastColumn);
441
442 final ThreadLocal<List<Object>> entries = TestUtil
443 .getInternalStaticState(SuppressWarningsHolder.class, "ENTRIES");
444 entries.get().add(entryInstance);
445 }
446
447 private static AuditEvent createAuditEvent(String moduleId, int line, int column) {
448 final Checker source = new Checker();
449 final Violation violation = new Violation(line, column, null, null, null,
450 moduleId, MemberNameCheck.class, "violation");
451 return new AuditEvent(source, "filename", violation);
452 }
453
454 @Test
455 public void testSuppressWarningsTextBlocks() throws Exception {
456 final String pattern = "^[a-z][a-zA-Z0-9]*$";
457
458 final String[] expected = {
459 "31:12: " + getCheckMessage(MemberNameCheck.class,
460 AbstractNameCheck.MSG_INVALID_PATTERN, "STRING3", pattern),
461 "33:12: " + getCheckMessage(MemberNameCheck.class,
462 AbstractNameCheck.MSG_INVALID_PATTERN, "STRING4", pattern),
463 "61:12: " + getCheckMessage(MemberNameCheck.class,
464 AbstractNameCheck.MSG_INVALID_PATTERN, "STRING8", pattern),
465 };
466
467 verifyWithInlineConfigParser(
468 getNonCompilablePath("InputSuppressWarningsHolderTextBlocks.java"), expected);
469
470 }
471
472 @Test
473 public void testWithAndWithoutCheckSuffixDifferentCases() throws Exception {
474 final String pattern = "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$";
475 final String[] expected = {
476 "16:30: " + getCheckMessage(ConstantNameCheck.class,
477 AbstractNameCheck.MSG_INVALID_PATTERN, "a", pattern),
478 };
479
480 verifyWithInlineConfigParser(
481 getPath("InputSuppressWarningsHolderWithAndWithoutCheckSuffixDifferentCases.java"),
482 expected);
483 }
484
485 @Test
486 public void testAliasList() throws Exception {
487 final String[] expected = {
488 "16:17: " + getCheckMessage(ParameterNumberCheck.class,
489 ParameterNumberCheck.MSG_KEY, 7, 8),
490 "28:17: " + getCheckMessage(ParameterNumberCheck.class,
491 ParameterNumberCheck.MSG_KEY, 7, 8),
492 };
493 verifyWithInlineConfigParser(
494 getPath("InputSuppressWarningsHolderAlias.java"),
495 expected);
496 }
497
498 @Test
499 public void testAliasList2() throws Exception {
500 final String pattern = "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$";
501 final String[] expected = {
502 "16:29: " + getCheckMessage(ConstantNameCheck.class,
503 AbstractNameCheck.MSG_INVALID_PATTERN, "a", pattern),
504 "19:30: " + getCheckMessage(ConstantNameCheck.class,
505 AbstractNameCheck.MSG_INVALID_PATTERN, "b", pattern),
506 };
507
508 verifyWithInlineConfigParser(
509 getPath("InputSuppressWarningsHolderAlias2.java"),
510 expected);
511 }
512
513 @Test
514 public void testIdent() throws Exception {
515 final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
516 verifyWithInlineConfigParser(
517 getNonCompilablePath("InputSuppressWarningsHolder1.java"),
518 expected);
519 }
520
521 @Test
522 public void testIdent2() throws Exception {
523 final String[] expected = {
524 "37:9: " + getCheckMessage(UnusedLocalVariableCheck.class,
525 MSG_UNUSED_LOCAL_VARIABLE, "a"),
526 "42:9: " + getCheckMessage(UnusedLocalVariableCheck.class,
527 MSG_UNUSED_LOCAL_VARIABLE, "a"),
528 };
529 verifyWithInlineConfigParser(
530 getNonCompilablePath("InputSuppressWarningsHolder2.java"),
531 expected);
532 }
533
534 @Test
535 public void test3() throws Exception {
536 final String pattern = "^[a-z][a-zA-Z0-9]*$";
537
538 final String[] expected = {
539 "18:16: " + getCheckMessage(MemberNameCheck.class,
540 AbstractNameCheck.MSG_INVALID_PATTERN, "K", pattern),
541 };
542 verifyWithInlineConfigParser(
543 getPath("InputSuppressWarningsHolder8.java"),
544 expected);
545 }
546 }