View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.checks.javadoc;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  
24  import org.junit.jupiter.api.Test;
25  
26  import com.puppycrawl.tools.checkstyle.DetailAstImpl;
27  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
29  
30  public class JavadocTagInfoTest {
31  
32      /* Additional test for jacoco, since valueOf()
33       * is generated by javac and jacoco reports that
34       * valueOf() is uncovered.
35       */
36      @Test
37      public void testJavadocTagInfoValueOf() {
38          final JavadocTagInfo tag = JavadocTagInfo.valueOf("AUTHOR");
39          assertWithMessage("Invalid valueOf result")
40              .that(tag)
41              .isEqualTo(JavadocTagInfo.AUTHOR);
42      }
43  
44      /* Additional test for jacoco, since valueOf()
45       * is generated by javac and jacoco reports that
46       * valueOf() is uncovered.
47       */
48      @Test
49      public void testTypeValueOf() {
50          final JavadocTagInfo.Type type = JavadocTagInfo.Type.valueOf("BLOCK");
51          assertWithMessage("Invalid valueOf result")
52              .that(type)
53              .isEqualTo(JavadocTagInfo.Type.BLOCK);
54      }
55  
56      /* Additional test for jacoco, since values()
57       * is generated by javac and jacoco reports that
58       * values() is uncovered.
59       */
60      @Test
61      public void testTypeValues() {
62          final JavadocTagInfo.Type[] expected = {
63              JavadocTagInfo.Type.BLOCK,
64              JavadocTagInfo.Type.INLINE,
65          };
66          final JavadocTagInfo.Type[] actual = JavadocTagInfo.Type.values();
67          assertWithMessage("Invalid Type values")
68              .that(actual)
69              .isEqualTo(expected);
70      }
71  
72      @Test
73      public void testAuthor() {
74          final DetailAstImpl ast = new DetailAstImpl();
75  
76          final int[] validTypes = {
77              TokenTypes.PACKAGE_DEF,
78              TokenTypes.CLASS_DEF,
79              TokenTypes.INTERFACE_DEF,
80              TokenTypes.ENUM_DEF,
81              TokenTypes.ANNOTATION_DEF,
82          };
83          for (int type: validTypes) {
84              ast.setType(type);
85              assertWithMessage("Invalid ast type for current tag: " + ast.getType())
86                      .that(JavadocTagInfo.AUTHOR.isValidOn(ast))
87                      .isTrue();
88          }
89  
90          ast.setType(TokenTypes.LAMBDA);
91          assertWithMessage("Should return false when ast type is invalid for current tag")
92                  .that(JavadocTagInfo.AUTHOR.isValidOn(ast))
93                  .isFalse();
94      }
95  
96      @Test
97      public void testOthers() throws ReflectiveOperationException {
98          final JavadocTagInfo[] tags = {
99              JavadocTagInfo.CODE,
100             JavadocTagInfo.DOC_ROOT,
101             JavadocTagInfo.LINK,
102             JavadocTagInfo.LINKPLAIN,
103             JavadocTagInfo.LITERAL,
104             JavadocTagInfo.SEE,
105             JavadocTagInfo.SINCE,
106             JavadocTagInfo.VALUE,
107         };
108         for (JavadocTagInfo tagInfo : tags) {
109             final DetailAstImpl astParent = new DetailAstImpl();
110             astParent.setType(TokenTypes.LITERAL_CATCH);
111 
112             final DetailAstImpl ast = new DetailAstImpl();
113             TestUtil.invokeVoidMethod(ast, "setParent", astParent);
114 
115             final int[] validTypes = {
116                 TokenTypes.PACKAGE_DEF,
117                 TokenTypes.CLASS_DEF,
118                 TokenTypes.INTERFACE_DEF,
119                 TokenTypes.ENUM_DEF,
120                 TokenTypes.ANNOTATION_DEF,
121                 TokenTypes.METHOD_DEF,
122                 TokenTypes.CTOR_DEF,
123                 TokenTypes.VARIABLE_DEF,
124             };
125             for (int type: validTypes) {
126                 ast.setType(type);
127                 assertWithMessage("Invalid ast type for current tag: " + ast.getType())
128                         .that(tagInfo.isValidOn(ast))
129                         .isTrue();
130             }
131 
132             astParent.setType(TokenTypes.SLIST);
133             ast.setType(TokenTypes.VARIABLE_DEF);
134             assertWithMessage("Should return false when ast type is invalid for current tag")
135                     .that(tagInfo.isValidOn(ast))
136                     .isFalse();
137 
138             ast.setType(TokenTypes.PARAMETER_DEF);
139             assertWithMessage("Should return false when ast type is invalid for current tag")
140                     .that(tagInfo.isValidOn(ast))
141                     .isFalse();
142         }
143     }
144 
145     @Test
146     public void testDeprecated() throws ReflectiveOperationException {
147         final DetailAstImpl ast = new DetailAstImpl();
148         final DetailAstImpl astParent = new DetailAstImpl();
149         astParent.setType(TokenTypes.LITERAL_CATCH);
150         TestUtil.invokeVoidMethod(ast, "setParent", astParent);
151 
152         final int[] validTypes = {
153             TokenTypes.CLASS_DEF,
154             TokenTypes.INTERFACE_DEF,
155             TokenTypes.ENUM_DEF,
156             TokenTypes.ANNOTATION_DEF,
157             TokenTypes.METHOD_DEF,
158             TokenTypes.CTOR_DEF,
159             TokenTypes.ENUM_CONSTANT_DEF,
160             TokenTypes.ANNOTATION_FIELD_DEF,
161             TokenTypes.VARIABLE_DEF,
162         };
163         for (int type: validTypes) {
164             ast.setType(type);
165             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
166                     .that(JavadocTagInfo.DEPRECATED.isValidOn(ast))
167                     .isTrue();
168         }
169 
170         astParent.setType(TokenTypes.SLIST);
171         ast.setType(TokenTypes.VARIABLE_DEF);
172         assertWithMessage("Should return false when ast type is invalid for current tag")
173                 .that(JavadocTagInfo.DEPRECATED.isValidOn(ast))
174                 .isFalse();
175 
176         ast.setType(TokenTypes.PARAMETER_DEF);
177         assertWithMessage("Should return false when ast type is invalid for current tag")
178                 .that(JavadocTagInfo.DEPRECATED.isValidOn(ast))
179                 .isFalse();
180     }
181 
182     @Test
183     public void testSerial() throws ReflectiveOperationException {
184         final DetailAstImpl ast = new DetailAstImpl();
185         final DetailAstImpl astParent = new DetailAstImpl();
186         astParent.setType(TokenTypes.LITERAL_CATCH);
187         TestUtil.invokeVoidMethod(ast, "setParent", astParent);
188 
189         final int[] validTypes = {
190             TokenTypes.VARIABLE_DEF,
191         };
192         for (int type: validTypes) {
193             ast.setType(type);
194             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
195                     .that(JavadocTagInfo.SERIAL.isValidOn(ast))
196                     .isTrue();
197         }
198 
199         astParent.setType(TokenTypes.SLIST);
200         ast.setType(TokenTypes.VARIABLE_DEF);
201         assertWithMessage("Should return false when ast type is invalid for current tag")
202                 .that(JavadocTagInfo.SERIAL.isValidOn(ast))
203                 .isFalse();
204 
205         ast.setType(TokenTypes.PARAMETER_DEF);
206         assertWithMessage("Should return false when ast type is invalid for current tag")
207                 .that(JavadocTagInfo.SERIAL.isValidOn(ast))
208                 .isFalse();
209     }
210 
211     @Test
212     public void testException() {
213         final DetailAstImpl ast = new DetailAstImpl();
214 
215         final int[] validTypes = {
216             TokenTypes.METHOD_DEF,
217             TokenTypes.CTOR_DEF,
218         };
219         for (int type: validTypes) {
220             ast.setType(type);
221             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
222                     .that(JavadocTagInfo.EXCEPTION.isValidOn(ast))
223                     .isTrue();
224         }
225 
226         ast.setType(TokenTypes.LAMBDA);
227         assertWithMessage("Should return false when ast type is invalid for current tag")
228                 .that(JavadocTagInfo.EXCEPTION.isValidOn(ast))
229                 .isFalse();
230     }
231 
232     @Test
233     public void testThrows() {
234         final DetailAstImpl ast = new DetailAstImpl();
235 
236         final int[] validTypes = {
237             TokenTypes.METHOD_DEF,
238             TokenTypes.CTOR_DEF,
239         };
240         for (int type: validTypes) {
241             ast.setType(type);
242             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
243                     .that(JavadocTagInfo.THROWS.isValidOn(ast))
244                     .isTrue();
245         }
246 
247         ast.setType(TokenTypes.LAMBDA);
248         assertWithMessage("Should return false when ast type is invalid for current tag")
249                 .that(JavadocTagInfo.THROWS.isValidOn(ast))
250                 .isFalse();
251     }
252 
253     @Test
254     public void testVersions() {
255         final DetailAstImpl ast = new DetailAstImpl();
256 
257         final int[] validTypes = {
258             TokenTypes.PACKAGE_DEF,
259             TokenTypes.CLASS_DEF,
260             TokenTypes.INTERFACE_DEF,
261             TokenTypes.ENUM_DEF,
262             TokenTypes.ANNOTATION_DEF,
263         };
264         for (int type: validTypes) {
265             ast.setType(type);
266             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
267                     .that(JavadocTagInfo.VERSION.isValidOn(ast))
268                     .isTrue();
269         }
270 
271         ast.setType(TokenTypes.LAMBDA);
272         assertWithMessage("Should return false when ast type is invalid for current tag")
273                 .that(JavadocTagInfo.VERSION.isValidOn(ast))
274                 .isFalse();
275     }
276 
277     @Test
278     public void testParam() {
279         final DetailAstImpl ast = new DetailAstImpl();
280 
281         final int[] validTypes = {
282             TokenTypes.CLASS_DEF,
283             TokenTypes.INTERFACE_DEF,
284             TokenTypes.METHOD_DEF,
285             TokenTypes.CTOR_DEF,
286         };
287         for (int type: validTypes) {
288             ast.setType(type);
289             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
290                     .that(JavadocTagInfo.PARAM.isValidOn(ast))
291                     .isTrue();
292         }
293 
294         ast.setType(TokenTypes.LAMBDA);
295         assertWithMessage("Should return false when ast type is invalid for current tag")
296                 .that(JavadocTagInfo.PARAM.isValidOn(ast))
297                 .isFalse();
298     }
299 
300     @Test
301     public void testReturn() {
302         final DetailAstImpl ast = new DetailAstImpl();
303         final DetailAstImpl astChild = new DetailAstImpl();
304         astChild.setType(TokenTypes.TYPE);
305         ast.setFirstChild(astChild);
306         final DetailAstImpl astChild2 = new DetailAstImpl();
307         astChild2.setType(TokenTypes.LITERAL_INT);
308         astChild.setFirstChild(astChild2);
309 
310         final int[] validTypes = {
311             TokenTypes.METHOD_DEF,
312         };
313         for (int type: validTypes) {
314             ast.setType(type);
315             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
316                     .that(JavadocTagInfo.RETURN.isValidOn(ast))
317                     .isTrue();
318         }
319 
320         astChild2.setType(TokenTypes.LITERAL_VOID);
321         assertWithMessage("Should return false when ast type is invalid for current tag")
322                 .that(JavadocTagInfo.RETURN.isValidOn(ast))
323                 .isFalse();
324 
325         ast.setType(TokenTypes.LAMBDA);
326         assertWithMessage("Should return false when ast type is invalid for current tag")
327                 .that(JavadocTagInfo.RETURN.isValidOn(ast))
328                 .isFalse();
329 
330         astChild2.setType(TokenTypes.LITERAL_INT);
331         assertWithMessage("Should return false when ast type is invalid for current tag")
332                 .that(JavadocTagInfo.RETURN.isValidOn(ast))
333                 .isFalse();
334     }
335 
336     @Test
337     public void testSerialField() {
338         final DetailAstImpl ast = new DetailAstImpl();
339         final DetailAstImpl astChild = new DetailAstImpl();
340         astChild.setType(TokenTypes.TYPE);
341         ast.setFirstChild(astChild);
342         final DetailAstImpl astChild2 = new DetailAstImpl();
343         astChild2.setType(TokenTypes.ARRAY_DECLARATOR);
344         astChild2.setText("ObjectStreamField");
345         astChild.setFirstChild(astChild2);
346 
347         final int[] validTypes = {
348             TokenTypes.VARIABLE_DEF,
349         };
350         for (int type: validTypes) {
351             ast.setType(type);
352             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
353                     .that(JavadocTagInfo.SERIAL_FIELD.isValidOn(ast))
354                     .isTrue();
355         }
356 
357         final int[] invalidTypes = {
358             TokenTypes.METHOD_DEF,
359             TokenTypes.CLASS_DEF,
360             TokenTypes.INTERFACE_DEF,
361             TokenTypes.ENUM_DEF,
362         };
363         for (int type: invalidTypes) {
364             ast.setType(type);
365             assertWithMessage("@serialField should only be valid on VARIABLE_DEF, not on others")
366                 .that(JavadocTagInfo.SERIAL_FIELD.isValidOn(ast))
367                 .isFalse();
368         }
369 
370         astChild2.setText("1111");
371         assertWithMessage("Should return false when ast type is invalid for current tag")
372                 .that(JavadocTagInfo.SERIAL_FIELD.isValidOn(ast))
373                 .isFalse();
374 
375         astChild2.setType(TokenTypes.LITERAL_VOID);
376         assertWithMessage("Should return false when ast type is invalid for current tag")
377                 .that(JavadocTagInfo.SERIAL_FIELD.isValidOn(ast))
378                 .isFalse();
379 
380         ast.setType(TokenTypes.LAMBDA);
381         assertWithMessage("Should return false when ast type is invalid for current tag")
382                 .that(JavadocTagInfo.SERIAL_FIELD.isValidOn(ast))
383                 .isFalse();
384 
385         ast.setType(TokenTypes.VARIABLE_DEF);
386         astChild2.setType(TokenTypes.IDENT);
387         astChild2.setText("ObjectStreamField");
388         assertWithMessage("Should return false when type is not ARRAY_DECLARATOR ")
389                 .that(JavadocTagInfo.SERIAL_FIELD.isValidOn(ast))
390                 .isFalse();
391 
392         ast.setType(TokenTypes.VARIABLE_DEF);
393         astChild2.setType(TokenTypes.ARRAY_DECLARATOR);
394         astChild2.setText("WrongField");
395         assertWithMessage("Should return false when text is not ObjectStreamField")
396                 .that(JavadocTagInfo.SERIAL_FIELD.isValidOn(ast))
397                 .isFalse();
398     }
399 
400     @Test
401     public void testSerialData() {
402         final DetailAstImpl ast = new DetailAstImpl();
403         ast.setType(TokenTypes.METHOD_DEF);
404         final DetailAstImpl astChild = new DetailAstImpl();
405         astChild.setType(TokenTypes.IDENT);
406         astChild.setText("writeObject");
407         ast.setFirstChild(astChild);
408 
409         final String[] validNames = {
410             "writeObject",
411             "readObject",
412             "writeExternal",
413             "readExternal",
414             "writeReplace",
415             "readResolve",
416         };
417         for (String name: validNames) {
418             astChild.setText(name);
419             assertWithMessage("Invalid ast type for current tag: " + ast.getType())
420                     .that(JavadocTagInfo.SERIAL_DATA.isValidOn(ast))
421                     .isTrue();
422         }
423 
424         astChild.setText("1111");
425         assertWithMessage("Should return false when ast type is invalid for current tag")
426                 .that(JavadocTagInfo.SERIAL_DATA.isValidOn(ast))
427                 .isFalse();
428 
429         ast.setType(TokenTypes.LAMBDA);
430         assertWithMessage("Should return false when ast type is invalid for current tag")
431                 .that(JavadocTagInfo.SERIAL_DATA.isValidOn(ast))
432                 .isFalse();
433 
434         astChild.setText("writeObject");
435         assertWithMessage("Should return false when ast type is invalid for current tag")
436                  .that(JavadocTagInfo.SERIAL_DATA.isValidOn(ast))
437                  .isFalse();
438     }
439 
440     @Test
441     public void testCoverage() {
442         assertWithMessage("Invalid type")
443             .that(JavadocTagInfo.VERSION.getType())
444             .isEqualTo(JavadocTagInfo.Type.BLOCK);
445 
446         assertWithMessage("Invalid toString result")
447             .that(JavadocTagInfo.VERSION.toString())
448             .isEqualTo("text [@version] name [version] type [BLOCK]");
449 
450         try {
451             JavadocTagInfo.fromName(null);
452             assertWithMessage("IllegalArgumentException is expected").fail();
453         }
454         catch (IllegalArgumentException exc) {
455             assertWithMessage("Invalid exception message")
456                 .that(exc.getMessage())
457                 .isEqualTo("the name is null");
458         }
459 
460         try {
461             JavadocTagInfo.fromName("myname");
462             assertWithMessage("IllegalArgumentException is expected").fail();
463         }
464         catch (IllegalArgumentException exc) {
465             assertWithMessage("Invalid exception message")
466                 .that(exc.getMessage())
467                 .isEqualTo("the name [myname] is not a valid Javadoc tag name");
468         }
469 
470         try {
471             JavadocTagInfo.fromText(null);
472             assertWithMessage("IllegalArgumentException is expected").fail();
473         }
474         catch (IllegalArgumentException exc) {
475             assertWithMessage("Invalid exception message")
476                 .that(exc.getMessage())
477                 .isEqualTo("the text is null");
478         }
479 
480         try {
481             JavadocTagInfo.fromText("myname");
482             assertWithMessage("IllegalArgumentException is expected").fail();
483         }
484         catch (IllegalArgumentException exc) {
485             assertWithMessage("Invalid exception message")
486                 .that(exc.getMessage())
487                 .isEqualTo("the text [myname] is not a valid Javadoc tag text");
488         }
489 
490         assertWithMessage("Invalid fromText result")
491             .that(JavadocTagInfo.fromText("@version"))
492             .isEqualTo(JavadocTagInfo.VERSION);
493     }
494 
495 }