View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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.utils;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.annotation.SuppressWarningsCheck.MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED;
24  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.isUtilsClassHasPrivateConstructor;
25  
26  import java.util.Set;
27  
28  import org.junit.jupiter.api.Test;
29  
30  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
31  import com.puppycrawl.tools.checkstyle.DetailAstImpl;
32  import com.puppycrawl.tools.checkstyle.api.DetailAST;
33  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
34  import com.puppycrawl.tools.checkstyle.checks.annotation.SuppressWarningsCheck;
35  
36  public class AnnotationUtilTest extends AbstractModuleTestSupport {
37  
38      @Override
39      protected String getPackageLocation() {
40          return "com/puppycrawl/tools/checkstyle/utils/annotationutil";
41      }
42  
43      @Test
44      public void testIsProperUtilsClass() {
45          try {
46              isUtilsClassHasPrivateConstructor(AnnotationUtil.class);
47              assertWithMessage("Exception is expected").fail();
48          }
49          catch (ReflectiveOperationException ex) {
50              assertWithMessage("Invalid exception message")
51                  .that(ex)
52                  .hasCauseThat()
53                  .hasMessageThat()
54                  .isEqualTo("do not instantiate.");
55          }
56      }
57  
58      @Test
59      public void testContainsAnnotationNull() {
60          try {
61              AnnotationUtil.containsAnnotation(null);
62              assertWithMessage("IllegalArgumentException is expected").fail();
63          }
64          catch (IllegalArgumentException ex) {
65              assertWithMessage("Invalid exception message")
66                  .that(ex.getMessage())
67                  .isEqualTo("the ast is null");
68          }
69      }
70  
71      @Test
72      public void testContainsAnnotationNull2() {
73          try {
74              AnnotationUtil.containsAnnotation(null, "");
75              assertWithMessage("IllegalArgumentException is expected").fail();
76          }
77          catch (IllegalArgumentException ex) {
78              assertWithMessage("Invalid exception message")
79                  .that(ex.getMessage())
80                  .isEqualTo("the ast is null");
81          }
82      }
83  
84      @Test
85      public void testContainsAnnotationFalse() {
86          final DetailAstImpl ast = new DetailAstImpl();
87          ast.setType(1);
88          assertWithMessage("AnnotationUtil should not contain " + ast)
89                  .that(AnnotationUtil.containsAnnotation(ast))
90                  .isFalse();
91      }
92  
93      @Test
94      public void testContainsAnnotationFalse2() {
95          final DetailAstImpl ast = new DetailAstImpl();
96          ast.setType(1);
97          final DetailAstImpl ast2 = new DetailAstImpl();
98          ast2.setType(TokenTypes.MODIFIERS);
99          ast.addChild(ast2);
100         assertWithMessage("AnnotationUtil should not contain " + ast)
101                 .that(AnnotationUtil.containsAnnotation(ast))
102                 .isFalse();
103     }
104 
105     @Test
106     public void testContainsAnnotationTrue() {
107         final DetailAstImpl ast = new DetailAstImpl();
108         ast.setType(1);
109         final DetailAstImpl ast2 = new DetailAstImpl();
110         ast2.setType(TokenTypes.MODIFIERS);
111         ast.addChild(ast2);
112         final DetailAstImpl ast3 = new DetailAstImpl();
113         ast3.setType(TokenTypes.ANNOTATION);
114         ast2.addChild(ast3);
115         assertWithMessage("AnnotationUtil should contain " + ast)
116                 .that(AnnotationUtil.containsAnnotation(ast))
117                 .isTrue();
118     }
119 
120     @Test
121     public void testAnnotationHolderNull() {
122         try {
123             AnnotationUtil.getAnnotationHolder(null);
124             assertWithMessage("IllegalArgumentException is expected").fail();
125         }
126         catch (IllegalArgumentException ex) {
127             assertWithMessage("Invalid exception message")
128                 .that(ex.getMessage())
129                 .isEqualTo("the ast is null");
130         }
131     }
132 
133     @Test
134     public void testAnnotationNull() {
135         try {
136             AnnotationUtil.getAnnotation(null, null);
137             assertWithMessage("IllegalArgumentException is expected").fail();
138         }
139         catch (IllegalArgumentException ex) {
140             assertWithMessage("Invalid exception message")
141                 .that(ex.getMessage())
142                 .isEqualTo("the ast is null");
143         }
144     }
145 
146     @Test
147     public void testAnnotationNull2() {
148         try {
149             AnnotationUtil.getAnnotation(new DetailAstImpl(), null);
150             assertWithMessage("IllegalArgumentException is expected").fail();
151         }
152         catch (IllegalArgumentException ex) {
153             assertWithMessage("Invalid exception message")
154                 .that(ex.getMessage())
155                 .isEqualTo("the annotation is null");
156         }
157     }
158 
159     @Test
160     public void testAnnotationEmpty() {
161         try {
162             AnnotationUtil.getAnnotation(new DetailAstImpl(), "");
163             assertWithMessage("IllegalArgumentException is expected").fail();
164         }
165         catch (IllegalArgumentException ex) {
166             assertWithMessage("Invalid exception message")
167                 .that(ex.getMessage())
168                 .isEqualTo("the annotation is empty or spaces");
169         }
170     }
171 
172     @Test
173     public void testContainsAnnotationWithNull() {
174         try {
175             AnnotationUtil.getAnnotation(null, "");
176             assertWithMessage("IllegalArgumentException is expected").fail();
177         }
178         catch (IllegalArgumentException ex) {
179             assertWithMessage("Invalid exception message")
180                 .that(ex.getMessage())
181                 .isEqualTo("the ast is null");
182         }
183     }
184 
185     @Test
186     public void testContainsAnnotationListWithNullAst() {
187         try {
188             AnnotationUtil.containsAnnotation(null, Set.of("Override"));
189             assertWithMessage("IllegalArgumentException is expected").fail();
190         }
191         catch (IllegalArgumentException ex) {
192             assertWithMessage("Invalid exception message")
193                 .that(ex.getMessage())
194                 .isEqualTo("the ast is null");
195         }
196     }
197 
198     @Test
199     public void testContainsAnnotationListWithNullList() {
200         final DetailAST ast = new DetailAstImpl();
201         final Set<String> annotations = null;
202         try {
203             AnnotationUtil.containsAnnotation(ast, annotations);
204             assertWithMessage("IllegalArgumentException is expected").fail();
205         }
206         catch (IllegalArgumentException ex) {
207             assertWithMessage("Invalid exception message")
208                 .that(ex.getMessage())
209                 .isEqualTo("annotations cannot be null");
210         }
211     }
212 
213     @Test
214     public void testContainsAnnotationListWithEmptyList() {
215         final DetailAST ast = new DetailAstImpl();
216         final Set<String> annotations = Set.of();
217         final boolean result = AnnotationUtil.containsAnnotation(ast, annotations);
218         assertWithMessage("An empty set should lead to a false result")
219             .that(result)
220             .isFalse();
221     }
222 
223     @Test
224     public void testContainsAnnotationListWithNoAnnotationNode() {
225         final DetailAstImpl ast = new DetailAstImpl();
226         final DetailAstImpl modifiersAst = new DetailAstImpl();
227         modifiersAst.setType(TokenTypes.MODIFIERS);
228         ast.addChild(modifiersAst);
229         final Set<String> annotations = Set.of("Override");
230         final boolean result = AnnotationUtil.containsAnnotation(ast, annotations);
231         assertWithMessage("An empty ast should lead to a false result")
232             .that(result)
233             .isFalse();
234     }
235 
236     @Test
237     public void testContainsAnnotationListWithNoMatchingAnnotation() {
238         final DetailAstImpl ast = new DetailAstImpl();
239         final DetailAstImpl modifiersAst = create(
240                 TokenTypes.MODIFIERS,
241                 create(
242                         TokenTypes.ANNOTATION,
243                         create(
244                                 TokenTypes.DOT,
245                                 create(
246                                         TokenTypes.IDENT,
247                                         "Override")
248                         )
249                 )
250         );
251         ast.addChild(modifiersAst);
252         final Set<String> annotations = Set.of("Deprecated");
253         final boolean result = AnnotationUtil.containsAnnotation(ast, annotations);
254         assertWithMessage("No matching annotation found")
255             .that(result)
256             .isFalse();
257     }
258 
259     @Test
260     public void testContainsAnnotation() {
261         final DetailAstImpl astForTest = new DetailAstImpl();
262         astForTest.setType(TokenTypes.PACKAGE_DEF);
263         final DetailAstImpl child = new DetailAstImpl();
264         final DetailAstImpl annotations = new DetailAstImpl();
265         final DetailAstImpl annotation = new DetailAstImpl();
266         final DetailAstImpl annotationNameHolder = new DetailAstImpl();
267         final DetailAstImpl annotationName = new DetailAstImpl();
268         annotations.setType(TokenTypes.ANNOTATIONS);
269         annotation.setType(TokenTypes.ANNOTATION);
270         annotationNameHolder.setType(TokenTypes.AT);
271         annotationName.setText("Annotation");
272 
273         annotationNameHolder.setNextSibling(annotationName);
274         annotation.setFirstChild(annotationNameHolder);
275         annotations.setFirstChild(annotation);
276         child.setNextSibling(annotations);
277         astForTest.setFirstChild(child);
278 
279         assertWithMessage("Annotation should contain " + astForTest)
280                 .that(AnnotationUtil.containsAnnotation(astForTest, "Annotation"))
281                 .isTrue();
282     }
283 
284     @Test
285     public void testContainsAnnotationWithStringFalse() {
286         final DetailAstImpl astForTest = new DetailAstImpl();
287         astForTest.setType(TokenTypes.PACKAGE_DEF);
288         final DetailAstImpl child = new DetailAstImpl();
289         final DetailAstImpl annotations = new DetailAstImpl();
290         final DetailAstImpl annotation = new DetailAstImpl();
291         final DetailAstImpl annotationNameHolder = new DetailAstImpl();
292         final DetailAstImpl annotationName = new DetailAstImpl();
293         annotations.setType(TokenTypes.ANNOTATIONS);
294         annotation.setType(TokenTypes.ANNOTATION);
295         annotationNameHolder.setType(TokenTypes.AT);
296         annotationName.setText("Annotation");
297 
298         annotationNameHolder.setNextSibling(annotationName);
299         annotation.setFirstChild(annotationNameHolder);
300         annotations.setFirstChild(annotation);
301         child.setNextSibling(annotations);
302         astForTest.setFirstChild(child);
303 
304         assertWithMessage("Annotation should not contain " + astForTest)
305                 .that(AnnotationUtil.containsAnnotation(astForTest, "AnnotationBad"))
306                 .isFalse();
307     }
308 
309     @Test
310     public void testContainsAnnotationWithComment() {
311         final DetailAstImpl astForTest = new DetailAstImpl();
312         astForTest.setType(TokenTypes.PACKAGE_DEF);
313         final DetailAstImpl child = new DetailAstImpl();
314         final DetailAstImpl annotations = new DetailAstImpl();
315         final DetailAstImpl annotation = new DetailAstImpl();
316         final DetailAstImpl annotationNameHolder = new DetailAstImpl();
317         final DetailAstImpl annotationName = new DetailAstImpl();
318         final DetailAstImpl comment = new DetailAstImpl();
319         annotations.setType(TokenTypes.ANNOTATIONS);
320         annotation.setType(TokenTypes.ANNOTATION);
321         annotationNameHolder.setType(TokenTypes.AT);
322         comment.setType(TokenTypes.BLOCK_COMMENT_BEGIN);
323         annotationName.setText("Annotation");
324 
325         annotationNameHolder.setNextSibling(annotationName);
326         annotation.setFirstChild(comment);
327         comment.setNextSibling(annotationNameHolder);
328         annotations.setFirstChild(annotation);
329         child.setNextSibling(annotations);
330         astForTest.setFirstChild(child);
331 
332         assertWithMessage("Annotation should contain " + astForTest)
333                 .that(AnnotationUtil.containsAnnotation(astForTest, "Annotation"))
334                 .isTrue();
335     }
336 
337     @Test
338     public void testCompactNoUnchecked() throws Exception {
339 
340         final String[] expected = {
341             "16:20: " + getCheckMessage(SuppressWarningsCheck.class,
342                     MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED, "unchecked"),
343             "22:28: " + getCheckMessage(SuppressWarningsCheck.class,
344                     MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED, "unchecked"),
345             "40:36: " + getCheckMessage(SuppressWarningsCheck.class,
346                     MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED, "unchecked"),
347             "69:28: " + getCheckMessage(SuppressWarningsCheck.class,
348                     MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED, "unchecked"),
349             "72:49: " + getCheckMessage(SuppressWarningsCheck.class,
350                     MSG_KEY_SUPPRESSED_WARNING_NOT_ALLOWED, "unchecked"),
351         };
352 
353         verifyWithInlineConfigParser(
354                 getPath("InputAnnotationUtil1.java"), expected);
355     }
356 
357     @Test
358     public void testValuePairAnnotation() throws Exception {
359 
360         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
361 
362         verifyWithInlineConfigParser(
363                 getPath("InputAnnotationUtil2.java"), expected);
364     }
365 
366     private static DetailAstImpl create(int tokenType) {
367         final DetailAstImpl ast = new DetailAstImpl();
368         ast.setType(tokenType);
369         return ast;
370     }
371 
372     private static DetailAstImpl create(int tokenType, String text) {
373         final DetailAstImpl ast = create(tokenType);
374         ast.setText(text);
375         return ast;
376     }
377 
378     private static DetailAstImpl create(int tokenType, DetailAstImpl child) {
379         final DetailAstImpl ast = create(tokenType);
380         ast.addChild(child);
381         return ast;
382     }
383 }