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.design;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.design.VisibilityModifierCheck.MSG_KEY;
24  
25  import java.io.File;
26  
27  import org.antlr.v4.runtime.CommonToken;
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.JavaParser;
33  import com.puppycrawl.tools.checkstyle.api.DetailAST;
34  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
35  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
36  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
37  
38  public class VisibilityModifierCheckTest
39      extends AbstractModuleTestSupport {
40  
41      @Override
42      protected String getPackageLocation() {
43          return "com/puppycrawl/tools/checkstyle/checks/design/visibilitymodifier";
44      }
45  
46      @Test
47      public void testGetRequiredTokens() {
48          final VisibilityModifierCheck checkObj = new VisibilityModifierCheck();
49          final int[] expected = {
50              TokenTypes.VARIABLE_DEF,
51              TokenTypes.IMPORT,
52          };
53          assertWithMessage("Default required tokens are invalid")
54              .that(checkObj.getRequiredTokens())
55              .isEqualTo(expected);
56      }
57  
58      @Test
59      public void testInner()
60              throws Exception {
61          final String[] expected = {
62              "47:24: " + getCheckMessage(MSG_KEY, "rData"),
63              "50:27: " + getCheckMessage(MSG_KEY, "protectedVariable"),
64              "53:17: " + getCheckMessage(MSG_KEY, "packageVariable"),
65              "58:29: " + getCheckMessage(MSG_KEY, "sWeird"),
66              "60:19: " + getCheckMessage(MSG_KEY, "sWeird2"),
67              "94:20: " + getCheckMessage(MSG_KEY, "someValue"),
68              "97:11: " + getCheckMessage(MSG_KEY, "fSerialVersionUID"),
69          };
70          verifyWithInlineConfigParser(
71                  getPath("InputVisibilityModifierInner.java"), expected);
72      }
73  
74      @Test
75      public void testIgnoreAccess()
76              throws Exception {
77          final String[] expected = {
78              "34:20: " + getCheckMessage(MSG_KEY, "fData"),
79              "94:20: " + getCheckMessage(MSG_KEY, "someValue"),
80          };
81          verifyWithInlineConfigParser(
82                  getPath("InputVisibilityModifierInner1.java"), expected);
83      }
84  
85      @Test
86      public void testSimple1() throws Exception {
87          final String[] expected = {
88              "49:19: " + getCheckMessage(MSG_KEY, "mNumCreated2"),
89              "59:23: " + getCheckMessage(MSG_KEY, "sTest1"),
90              "61:26: " + getCheckMessage(MSG_KEY, "sTest3"),
91              "63:16: " + getCheckMessage(MSG_KEY, "sTest2"),
92              "66:9: " + getCheckMessage(MSG_KEY, "mTest1"),
93              "68:16: " + getCheckMessage(MSG_KEY, "mTest2"),
94          };
95          verifyWithInlineConfigParser(
96                  getPath("InputVisibilityModifierSimple.java"), expected);
97      }
98  
99      @Test
100     public void testSimple2() throws Exception {
101         final String[] expected = {
102         };
103         verifyWithInlineConfigParser(
104                 getPath("InputVisibilityModifierSimple2.java"), expected);
105     }
106 
107     @Test
108     public void testStrictJavadoc() throws Exception {
109         final String[] expected = {
110             "49:9: " + getCheckMessage(MSG_KEY, "mLen"),
111             "50:19: " + getCheckMessage(MSG_KEY, "mDeer"),
112             "51:16: " + getCheckMessage(MSG_KEY, "aFreddo"),
113         };
114         verifyWithInlineConfigParser(
115                 getPath("InputVisibilityModifierPublicOnly.java"), expected);
116     }
117 
118     @Test
119     public void testAllowPublicFinalFieldsInImmutableClass() throws Exception {
120         final String[] expected = {
121             "33:39: " + getCheckMessage(MSG_KEY, "includes"),
122             "34:39: " + getCheckMessage(MSG_KEY, "excludes"),
123             "37:23: " + getCheckMessage(MSG_KEY, "list"),
124             "55:20: " + getCheckMessage(MSG_KEY, "value"),
125             "57:24: " + getCheckMessage(MSG_KEY, "bValue"),
126             "58:31: " + getCheckMessage(MSG_KEY, "longValue"),
127             "62:19: " + getCheckMessage(MSG_KEY, "C_D_E"),
128         };
129         verifyWithInlineConfigParser(
130                 getPath("InputVisibilityModifierImmutable.java"), expected);
131     }
132 
133     @Test
134     public void testAllowPublicFinalFieldsInImmutableClassWithNonCanonicalClasses()
135             throws Exception {
136         final String[] expected = {
137             "28:39: " + getCheckMessage(MSG_KEY, "includes"),
138             "29:39: " + getCheckMessage(MSG_KEY, "excludes"),
139             "31:29: " + getCheckMessage(MSG_KEY, "money"),
140             "32:23: " + getCheckMessage(MSG_KEY, "list"),
141             "48:35: " + getCheckMessage(MSG_KEY, "uri"),
142             "49:35: " + getCheckMessage(MSG_KEY, "file"),
143             "50:20: " + getCheckMessage(MSG_KEY, "value"),
144             "51:35: " + getCheckMessage(MSG_KEY, "url"),
145             "52:24: " + getCheckMessage(MSG_KEY, "bValue"),
146             "53:21: " + getCheckMessage(MSG_KEY, "longValue"),
147             "57:19: " + getCheckMessage(MSG_KEY, "C_D_E"),
148         };
149         verifyWithInlineConfigParser(
150                 getPath("InputVisibilityModifierImmutable2.java"), expected);
151     }
152 
153     @Test
154     public void testDisAllowPublicFinalAndImmutableFieldsInImmutableClass() throws Exception {
155         final String[] expected = {
156             "32:22: " + getCheckMessage(MSG_KEY, "someIntValue"),
157             "33:39: " + getCheckMessage(MSG_KEY, "includes"),
158             "34:39: " + getCheckMessage(MSG_KEY, "excludes"),
159             "35:25: " + getCheckMessage(MSG_KEY, "notes"),
160             "36:29: " + getCheckMessage(MSG_KEY, "money"),
161             "37:23: " + getCheckMessage(MSG_KEY, "list"),
162             "51:28: " + getCheckMessage(MSG_KEY, "f"),
163             "52:30: " + getCheckMessage(MSG_KEY, "bool"),
164             "53:35: " + getCheckMessage(MSG_KEY, "uri"),
165             "54:35: " + getCheckMessage(MSG_KEY, "file"),
166             "55:20: " + getCheckMessage(MSG_KEY, "value"),
167             "56:35: " + getCheckMessage(MSG_KEY, "url"),
168             "57:24: " + getCheckMessage(MSG_KEY, "bValue"),
169             "58:21: " + getCheckMessage(MSG_KEY, "longValue"),
170             "62:19: " + getCheckMessage(MSG_KEY, "C_D_E"),
171         };
172         verifyWithInlineConfigParser(
173                 getPath("InputVisibilityModifierImmutable3.java"), expected);
174     }
175 
176     @Test
177     public void testAllowPublicFinalFieldsInNonFinalClass() throws Exception {
178         final String[] expected = {
179             "55:20: " + getCheckMessage(MSG_KEY, "value"),
180             "57:24: " + getCheckMessage(MSG_KEY, "bValue"),
181             "58:21: " + getCheckMessage(MSG_KEY, "longValue"),
182         };
183         verifyWithInlineConfigParser(
184                 getPath("InputVisibilityModifierImmutable4.java"), expected);
185     }
186 
187     @Test
188     public void testUserSpecifiedImmutableClassesList() throws Exception {
189         final String[] expected = {
190             "30:29: " + getCheckMessage(MSG_KEY, "money"),
191             "47:35: " + getCheckMessage(MSG_KEY, "uri"),
192             "48:35: " + getCheckMessage(MSG_KEY, "file"),
193             "49:20: " + getCheckMessage(MSG_KEY, "value"),
194             "50:35: " + getCheckMessage(MSG_KEY, "url"),
195             "51:24: " + getCheckMessage(MSG_KEY, "bValue"),
196             "52:21: " + getCheckMessage(MSG_KEY, "longValue"),
197             "56:19: " + getCheckMessage(MSG_KEY, "C_D_E"),
198         };
199         verifyWithInlineConfigParser(
200                 getPath("InputVisibilityModifierImmutable5.java"), expected);
201     }
202 
203     @Test
204     public void testImmutableSpecifiedSameTypeName() throws Exception {
205         final String[] expected = {
206             "23:46: " + getCheckMessage(MSG_KEY, "calendar"),
207             "26:36: " + getCheckMessage(MSG_KEY, "address"),
208             "27:36: " + getCheckMessage(MSG_KEY, "adr"),
209         };
210         verifyWithInlineConfigParser(
211                 getPath("InputVisibilityModifierImmutableSameTypeName.java"),
212                 expected);
213     }
214 
215     @Test
216     public void testImmutableValueSameTypeName() throws Exception {
217         final String[] expected = {
218             "28:46: " + getCheckMessage(MSG_KEY, "calendar"),
219             "29:59: " + getCheckMessage(MSG_KEY, "calendar2"),
220             "30:59: " + getCheckMessage(MSG_KEY, "calendar3"),
221         };
222         verifyWithInlineConfigParser(
223                 getPath("InputVisibilityModifierImmutableSameTypeName2.java"),
224                 expected);
225     }
226 
227     @Test
228     public void testImmutableStarImportFalseNegative() throws Exception {
229         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
230         verifyWithInlineConfigParser(
231                 getPath("InputVisibilityModifierImmutableStarImport.java"), expected);
232     }
233 
234     @Test
235     public void testImmutableStarImportNoWarn() throws Exception {
236         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
237         verifyWithInlineConfigParser(
238                 getPath("InputVisibilityModifierImmutableStarImport2.java"),
239                 expected);
240     }
241 
242     @Test
243     public void testDefaultAnnotationPatterns() throws Exception {
244         final String[] expected = {
245             "61:19: " + getCheckMessage(MSG_KEY, "customAnnotatedPublic"),
246             "64:12: " + getCheckMessage(MSG_KEY, "customAnnotatedPackage"),
247             "67:22: " + getCheckMessage(MSG_KEY, "customAnnotatedProtected"),
248             "69:19: " + getCheckMessage(MSG_KEY, "unannotatedPublic"),
249             "70:12: " + getCheckMessage(MSG_KEY, "unannotatedPackage"),
250             "71:22: " + getCheckMessage(MSG_KEY, "unannotatedProtected"),
251         };
252         verifyWithInlineConfigParser(
253                 getPath("InputVisibilityModifierAnnotated.java"), expected);
254     }
255 
256     @Test
257     public void testCustomAnnotationPatterns() throws Exception {
258         final String[] expected = {
259             "37:28: " + getCheckMessage(MSG_KEY, "publicJUnitRule"),
260             "40:28: " + getCheckMessage(MSG_KEY, "fqPublicJUnitRule"),
261             "43:19: " + getCheckMessage(MSG_KEY, "googleCommonsAnnotatedPublic"),
262             "46:12: " + getCheckMessage(MSG_KEY, "googleCommonsAnnotatedPackage"),
263             "49:22: " + getCheckMessage(MSG_KEY, "googleCommonsAnnotatedProtected"),
264             "52:19: " + getCheckMessage(MSG_KEY, "fqGoogleCommonsAnnotatedPublic"),
265             "55:12: " + getCheckMessage(MSG_KEY, "fqGoogleCommonsAnnotatedPackage"),
266             "58:22: " + getCheckMessage(MSG_KEY, "fqGoogleCommonsAnnotatedProtected"),
267             "69:19: " + getCheckMessage(MSG_KEY, "unannotatedPublic"),
268             "70:12: " + getCheckMessage(MSG_KEY, "unannotatedPackage"),
269             "71:22: " + getCheckMessage(MSG_KEY, "unannotatedProtected"),
270             "80:35: " + getCheckMessage(MSG_KEY, "publicJUnitClassRule"),
271             "83:35: " + getCheckMessage(MSG_KEY, "fqPublicJUnitClassRule"),
272         };
273         verifyWithInlineConfigParser(
274                 getPath("InputVisibilityModifierAnnotated2.java"), expected);
275     }
276 
277     @Test
278     public void testIgnoreAnnotationNoPattern() throws Exception {
279         final String[] expected = {
280             "36:28: " + getCheckMessage(MSG_KEY, "publicJUnitRule"),
281             "39:28: " + getCheckMessage(MSG_KEY, "fqPublicJUnitRule"),
282             "42:19: " + getCheckMessage(MSG_KEY, "googleCommonsAnnotatedPublic"),
283             "45:12: " + getCheckMessage(MSG_KEY, "googleCommonsAnnotatedPackage"),
284             "48:22: " + getCheckMessage(MSG_KEY, "googleCommonsAnnotatedProtected"),
285             "51:19: " + getCheckMessage(MSG_KEY, "fqGoogleCommonsAnnotatedPublic"),
286             "54:12: " + getCheckMessage(MSG_KEY, "fqGoogleCommonsAnnotatedPackage"),
287             "57:22: " + getCheckMessage(MSG_KEY, "fqGoogleCommonsAnnotatedProtected"),
288             "60:19: " + getCheckMessage(MSG_KEY, "customAnnotatedPublic"),
289             "63:12: " + getCheckMessage(MSG_KEY, "customAnnotatedPackage"),
290             "66:22: " + getCheckMessage(MSG_KEY, "customAnnotatedProtected"),
291             "68:19: " + getCheckMessage(MSG_KEY, "unannotatedPublic"),
292             "69:12: " + getCheckMessage(MSG_KEY, "unannotatedPackage"),
293             "70:22: " + getCheckMessage(MSG_KEY, "unannotatedProtected"),
294             "79:35: " + getCheckMessage(MSG_KEY, "publicJUnitClassRule"),
295             "82:35: " + getCheckMessage(MSG_KEY, "fqPublicJUnitClassRule"),
296         };
297         verifyWithInlineConfigParser(
298                 getPath("InputVisibilityModifierAnnotated3.java"), expected);
299     }
300 
301     @Test
302     public void testIgnoreAnnotationSameName() throws Exception {
303         final String[] expected = {
304             "33:28: " + getCheckMessage(MSG_KEY, "publicJUnitRule"),
305             "36:28: " + getCheckMessage(MSG_KEY, "publicJUnitClassRule"),
306         };
307         verifyWithInlineConfigParser(
308                 getPath("InputVisibilityModifierAnnotatedSameTypeName.java"),
309                 expected);
310     }
311 
312     @Test
313     public void testGetAcceptableTokens() {
314         final VisibilityModifierCheck obj = new VisibilityModifierCheck();
315         final int[] expected = {
316             TokenTypes.VARIABLE_DEF,
317             TokenTypes.IMPORT,
318         };
319         assertWithMessage("Default acceptable tokens are invalid")
320             .that(obj.getAcceptableTokens())
321             .isEqualTo(expected);
322     }
323 
324     @Test
325     public void testPublicImmutableFieldsNotAllowed() throws Exception {
326         final String[] expected = {
327             "31:22: " + getCheckMessage(MSG_KEY, "someIntValue"),
328             "32:39: " + getCheckMessage(MSG_KEY, "includes"),
329             "33:35: " + getCheckMessage(MSG_KEY, "notes"),
330             "34:29: " + getCheckMessage(MSG_KEY, "value"),
331             "35:23: " + getCheckMessage(MSG_KEY, "list"),
332         };
333         verifyWithInlineConfigParser(
334                 getPath("InputVisibilityModifiersPublicImmutable.java"), expected);
335     }
336 
337     @Test
338     public void testPublicFinalFieldsNotAllowed() throws Exception {
339         final String[] expected = {
340             "31:22: " + getCheckMessage(MSG_KEY, "someIntValue"),
341             "32:39: " + getCheckMessage(MSG_KEY, "includes"),
342             "33:25: " + getCheckMessage(MSG_KEY, "notes"),
343             "34:29: " + getCheckMessage(MSG_KEY, "value"),
344             "35:23: " + getCheckMessage(MSG_KEY, "list"),
345         };
346         verifyWithInlineConfigParser(
347                 getPath("InputVisibilityModifiersPublicImmutable2.java"), expected);
348     }
349 
350     @Test
351     public void testPublicFinalFieldsAllowed() throws Exception {
352         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
353         verifyWithInlineConfigParser(
354                 getPath("InputVisibilityModifiersPublicImmutable3.java"), expected);
355     }
356 
357     @Test
358     public void testPublicFinalFieldInEnum() throws Exception {
359         final String[] expected = {
360             "35:23: " + getCheckMessage(MSG_KEY, "hole"),
361         };
362         verifyWithInlineConfigParser(
363                 getPath("InputVisibilityModifiersEnumIsSealed.java"), expected);
364     }
365 
366     @Test
367     public void testWrongTokenType() {
368         final VisibilityModifierCheck obj = new VisibilityModifierCheck();
369         final DetailAstImpl ast = new DetailAstImpl();
370         ast.initialize(new CommonToken(TokenTypes.CLASS_DEF, "class"));
371         try {
372             obj.visitToken(ast);
373             assertWithMessage("exception expected").fail();
374         }
375         catch (IllegalArgumentException exc) {
376             assertWithMessage("Invalid exception message")
377                 .that(exc.getMessage())
378                 .isEqualTo("Unexpected token type: class");
379         }
380     }
381 
382     @Test
383     public void testNullModifiers() throws Exception {
384         final String[] expected = {
385             "32:50: " + getCheckMessage(MSG_KEY, "i"),
386         };
387         verifyWithInlineConfigParser(
388                 getPath("InputVisibilityModifiersNullModifiers.java"), expected);
389     }
390 
391     @Test
392     public void testVisibilityModifiersOfGenericFields() throws Exception {
393         final String[] expected = {
394             "31:56: " + getCheckMessage(MSG_KEY, "perfSeries"),
395             "32:66: " + getCheckMessage(MSG_KEY, "peopleMap"),
396             "33:66: " + getCheckMessage(MSG_KEY, "someMap"),
397             "34:76: " + getCheckMessage(MSG_KEY, "newMap"),
398             "36:45: " + getCheckMessage(MSG_KEY, "optionalOfObject"),
399             "37:35: " + getCheckMessage(MSG_KEY, "obj"),
400             "39:19: " + getCheckMessage(MSG_KEY, "rqUID"),
401             "40:29: " + getCheckMessage(MSG_KEY, "rqTime"),
402             "41:45: " + getCheckMessage(MSG_KEY, "rates"),
403             "42:50: " + getCheckMessage(MSG_KEY, "loans"),
404             "43:60: " + getCheckMessage(MSG_KEY, "cards"),
405             "44:60: " + getCheckMessage(MSG_KEY, "values"),
406             "45:70: " + getCheckMessage(MSG_KEY, "permissions"),
407             "47:38: " + getCheckMessage(MSG_KEY, "mapOfStrings"),
408             "48:48: " + getCheckMessage(MSG_KEY, "names"),
409             "49:48: " + getCheckMessage(MSG_KEY, "links"),
410             "50:38: " + getCheckMessage(MSG_KEY, "presentations"),
411             "51:48: " + getCheckMessage(MSG_KEY, "collection"),
412             "54:73: " + getCheckMessage(MSG_KEY, "exceptions"),
413         };
414         verifyWithInlineConfigParser(
415                 getPath("InputVisibilityModifierGenerics.java"), expected);
416     }
417 
418     /**
419      * We can not cover this mutation because it forces all imports to be non static,
420      * but static imports are ignored, so we will not see any effect on validation.
421      * We could remove this method at all, and it will work correctly as we can not use
422      * class with name "", but in this case internal collection will have short names
423      * as "" that will not make problems, but will be weird in debug.
424      *
425      * @throws Exception when exception occurred during execution.
426      */
427     @Test
428     public void testIsStarImportNullAst() throws Exception {
429         final DetailAST importAst = JavaParser.parseFile(
430             new File(getPath("InputVisibilityModifierIsStarImport.java")),
431             JavaParser.Options.WITHOUT_COMMENTS).getFirstChild().getNextSibling();
432         final VisibilityModifierCheck check = new VisibilityModifierCheck();
433         final boolean actual = TestUtil.invokeMethod(check, "isStarImport", importAst);
434 
435         assertWithMessage("Should return true when star import is passed")
436                 .that(actual)
437                 .isTrue();
438     }
439 
440     @Test
441     public void testPackageClassName() throws Exception {
442         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
443         verifyWithInlineConfigParser(
444                 getNonCompilablePath("InputVisibilityModifierPackageClassName.java"),
445                 expected);
446     }
447 
448 }