View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 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.whitespace;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.whitespace.GenericWhitespaceCheck.MSG_WS_FOLLOWED;
24  import static com.puppycrawl.tools.checkstyle.checks.whitespace.GenericWhitespaceCheck.MSG_WS_ILLEGAL_FOLLOW;
25  import static com.puppycrawl.tools.checkstyle.checks.whitespace.GenericWhitespaceCheck.MSG_WS_NOT_PRECEDED;
26  import static com.puppycrawl.tools.checkstyle.checks.whitespace.GenericWhitespaceCheck.MSG_WS_PRECEDED;
27  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
28  
29  import java.io.File;
30  import java.nio.charset.StandardCharsets;
31  import java.util.Optional;
32  
33  import org.antlr.v4.runtime.CommonToken;
34  import org.junit.jupiter.api.Test;
35  
36  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
37  import com.puppycrawl.tools.checkstyle.DetailAstImpl;
38  import com.puppycrawl.tools.checkstyle.JavaParser;
39  import com.puppycrawl.tools.checkstyle.api.DetailAST;
40  import com.puppycrawl.tools.checkstyle.api.FileContents;
41  import com.puppycrawl.tools.checkstyle.api.FileText;
42  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
43  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
44  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
45  
46  public class GenericWhitespaceCheckTest
47      extends AbstractModuleTestSupport {
48  
49      @Override
50      public String getPackageLocation() {
51          return "com/puppycrawl/tools/checkstyle/checks/whitespace/genericwhitespace";
52      }
53  
54      @Test
55      public void testGetRequiredTokens() {
56          final GenericWhitespaceCheck checkObj = new GenericWhitespaceCheck();
57          final int[] expected = {
58              TokenTypes.GENERIC_START,
59              TokenTypes.GENERIC_END,
60          };
61          assertWithMessage("Default required tokens are invalid")
62              .that(checkObj.getRequiredTokens())
63              .isEqualTo(expected);
64      }
65  
66      @Test
67      public void testDefault() throws Exception {
68          final String[] expected = {
69              "22:14: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
70              "22:14: " + getCheckMessage(MSG_WS_FOLLOWED, "<"),
71              "22:24: " + getCheckMessage(MSG_WS_PRECEDED, ">"),
72              "22:44: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
73              "22:44: " + getCheckMessage(MSG_WS_FOLLOWED, "<"),
74              "22:54: " + getCheckMessage(MSG_WS_PRECEDED, ">"),
75              "22:54: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
76              "23:14: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
77              "23:14: " + getCheckMessage(MSG_WS_FOLLOWED, "<"),
78              "23:21: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
79              "23:21: " + getCheckMessage(MSG_WS_FOLLOWED, "<"),
80              "23:31: " + getCheckMessage(MSG_WS_PRECEDED, ">"),
81              "23:31: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
82              "23:33: " + getCheckMessage(MSG_WS_PRECEDED, ">"),
83              "23:53: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
84              "23:53: " + getCheckMessage(MSG_WS_FOLLOWED, "<"),
85              "23:60: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
86              "23:60: " + getCheckMessage(MSG_WS_FOLLOWED, "<"),
87              "23:70: " + getCheckMessage(MSG_WS_PRECEDED, ">"),
88              "23:70: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
89              "23:72: " + getCheckMessage(MSG_WS_PRECEDED, ">"),
90              "23:72: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
91              "36:18: " + getCheckMessage(MSG_WS_NOT_PRECEDED, "<"),
92              "36:20: " + getCheckMessage(MSG_WS_ILLEGAL_FOLLOW, ">"),
93              "48:22: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
94              "48:29: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
95              "66:35: " + getCheckMessage(MSG_WS_NOT_PRECEDED, "&"),
96              "69:35: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
97              "87:28: " + getCheckMessage(MSG_WS_NOT_PRECEDED, "<"),
98              "88:34: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
99              "89:34: " + getCheckMessage(MSG_WS_NOT_PRECEDED, "<"),
100             "89:41: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
101             "92:26: " + getCheckMessage(MSG_WS_NOT_PRECEDED, "<"),
102             "93:35: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
103             "94:35: " + getCheckMessage(MSG_WS_NOT_PRECEDED, "<"),
104             "94:42: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
105         };
106         verifyWithInlineConfigParser(
107                 getPath("InputGenericWhitespaceDefault.java"), expected);
108     }
109 
110     @Test
111     public void testAtTheStartOfTheLine() throws Exception {
112         final String[] expected = {
113             "16:2: " + getCheckMessage(MSG_WS_PRECEDED, ">"),
114             "18:2: " + getCheckMessage(MSG_WS_PRECEDED, "<"),
115         };
116         verifyWithInlineConfigParser(
117                 getPath("InputGenericWhitespaceAtStartOfTheLine.java"), expected);
118     }
119 
120     @Test
121     public void testNestedGeneric() throws Exception {
122         final String[] expected = {
123             "17:1: " + getCheckMessage(MSG_WS_NOT_PRECEDED, "&"),
124         };
125         verifyWithInlineConfigParser(
126                 getPath("InputGenericWhitespaceNested.java"), expected);
127     }
128 
129     @Test
130     public void testList() throws Exception {
131         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
132         verifyWithInlineConfigParser(
133                 getPath("InputGenericWhitespaceList.java"), expected);
134     }
135 
136     @Test
137     public void testInnerClass() throws Exception {
138         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
139         verifyWithInlineConfigParser(
140                 getPath("InputGenericWhitespaceInnerClass.java"), expected);
141     }
142 
143     @Test
144     public void testMethodReferences() throws Exception {
145         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
146         verifyWithInlineConfigParser(
147                 getPath("InputGenericWhitespaceMethodRef1.java"), expected);
148     }
149 
150     @Test
151     public void testMethodReferences2() throws Exception {
152         final String[] expected = {
153             "16:37: " + getCheckMessage(MSG_WS_FOLLOWED, ">"),
154         };
155         verifyWithInlineConfigParser(
156                 getPath("InputGenericWhitespaceMethodRef2.java"), expected);
157     }
158 
159     @Test
160     public void testGenericEndsTheLine() throws Exception {
161         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
162         verifyWithInlineConfigParser(
163                 getPath("InputGenericWhitespaceEndsTheLine.java"), expected);
164     }
165 
166     @Test
167     public void testGenericWhitespaceWithEmoji() throws Exception {
168         final String[] expected = {
169             "35:2: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
170             "40:35: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
171             "40:42: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
172             "44:28: " + getCheckMessage(MSG_WS_NOT_PRECEDED, '<'),
173             "45:53: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
174         };
175         verifyWithInlineConfigParser(
176                 getPath("InputGenericWhitespaceWithEmoji.java"), expected);
177     }
178 
179     @Test
180     public void testBeforeCtorInvocation() throws Exception {
181         final String[] expected = {
182             "17:31: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
183             "19:56: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
184             "19:56: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
185             "24:25: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
186             "27:36: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
187             "31:35: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
188             "31:35: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
189             "31:47: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
190             "31:47: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
191             "38:34: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
192             "39:47: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
193             "40:28: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
194             "40:48: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
195             "47:41: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
196             "50:47: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
197             "52:44: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
198         };
199         verifyWithInlineConfigParser(
200                 getPath("InputGenericWhitespaceBeforeCtorInvocation.java"), expected);
201     }
202 
203     @Test
204     public void testAfterNew() throws Exception {
205         final String[] expected = {
206             "17:30: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
207             "21:12: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
208             "21:12: " + getCheckMessage(MSG_WS_NOT_PRECEDED, '<'),
209             "21:23: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
210             "21:23: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
211             "28:22: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
212             "33:31: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
213             "33:31: " + getCheckMessage(MSG_WS_NOT_PRECEDED, '<'),
214             "33:40: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
215             "33:40: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
216             "41:28: " + getCheckMessage(MSG_WS_NOT_PRECEDED, '<'),
217             "41:36: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
218             "41:56: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
219             "41:61: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
220             "41:63: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
221             "41:65: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
222             "41:66: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
223             "41:85: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
224             "41:92: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
225         };
226         verifyWithInlineConfigParser(
227                 getPath("InputGenericWhitespaceAfterNew.java"), expected);
228     }
229 
230     @Test
231     public void testBeforeRecordHeader() throws Exception {
232         final String[] expected = {
233             "17:20: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
234             "18:20: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
235             "18:20: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
236             "18:24: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
237             "18:24: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
238             "30:27: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
239             "30:38: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
240             "30:80: " + getCheckMessage(MSG_WS_ILLEGAL_FOLLOW, '>'),
241             "36:38: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
242             "43:44: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
243             "43:69: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
244             "49:21: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
245             "49:64: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
246             "49:66: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
247             "56:63: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
248             "56:80: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
249             "62:36: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
250             "62:61: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
251             "67:49: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
252             "73:26: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
253             "73:51: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
254             "73:64: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
255             "80:26: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
256             "80:34: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
257             "80:55: " + getCheckMessage(MSG_WS_ILLEGAL_FOLLOW, '>'),
258             "91:25: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
259             "91:44: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
260             "91:47: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
261             "91:61: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
262             "91:71: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
263             "91:73: " + getCheckMessage(MSG_WS_PRECEDED, '>'),
264             "101:25: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
265             "101:58: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
266             "108:25: " + getCheckMessage(MSG_WS_PRECEDED, '<'),
267             "108:32: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
268             "108:46: " + getCheckMessage(MSG_WS_FOLLOWED, '<'),
269             "108:63: " + getCheckMessage(MSG_WS_FOLLOWED, '>'),
270         };
271         verifyWithInlineConfigParser(
272                 getPath("InputGenericWhitespaceBeforeRecordHeader.java"),
273                 expected);
274     }
275 
276     /**
277      * Checks if the private field {@code depth} is properly cleared during the
278      * start of processing the next file in the check.
279      *
280      * @throws Exception if there is an error.
281      */
282     @Test
283     public void testClearState() throws Exception {
284         final GenericWhitespaceCheck check = new GenericWhitespaceCheck();
285         final FileText fileText = new FileText(
286                 new File(getPath("InputGenericWhitespaceDefault.java")),
287                 StandardCharsets.UTF_8.name());
288         check.setFileContents(new FileContents(fileText));
289         final DetailAST root = JavaParser.parseFileText(fileText,
290                 JavaParser.Options.WITHOUT_COMMENTS);
291         final Optional<DetailAST> genericStart = TestUtil.findTokenInAstByPredicate(root,
292             ast -> ast.getType() == TokenTypes.GENERIC_START);
293 
294         assertWithMessage("Ast should contain GENERIC_START")
295                 .that(genericStart.isPresent())
296                 .isTrue();
297         assertWithMessage("State is not cleared on beginTree")
298                 .that(
299                     TestUtil.isStatefulFieldClearedDuringBeginTree(check,
300                             genericStart.orElseThrow(), "depth",
301                             depth -> ((Number) depth).intValue() == 0))
302                 .isTrue();
303     }
304 
305     @Test
306     public void testGetAcceptableTokens() {
307         final GenericWhitespaceCheck genericWhitespaceCheckObj = new GenericWhitespaceCheck();
308         final int[] actual = genericWhitespaceCheckObj.getAcceptableTokens();
309         final int[] expected = {
310             TokenTypes.GENERIC_START,
311             TokenTypes.GENERIC_END,
312         };
313         assertWithMessage("Default acceptable tokens are invalid")
314             .that(actual)
315             .isEqualTo(expected);
316     }
317 
318     @Test
319     public void testWrongTokenType() {
320         final GenericWhitespaceCheck genericWhitespaceCheckObj = new GenericWhitespaceCheck();
321         final DetailAstImpl ast = new DetailAstImpl();
322         ast.initialize(new CommonToken(TokenTypes.INTERFACE_DEF, "interface"));
323         final IllegalArgumentException exc =
324             getExpectedThrowable(IllegalArgumentException.class, () -> {
325                 genericWhitespaceCheckObj.visitToken(ast);
326             });
327         assertWithMessage("Invalid exception message")
328             .that(exc.getMessage())
329             .isEqualTo("Unknown type interface[0x-1]");
330     }
331 
332     @Test
333     public void testTabAndMissingSpace() throws Exception {
334         final String[] expected = {
335             "14:16: " + getCheckMessage(MSG_WS_ILLEGAL_FOLLOW, '>'),
336         };
337         verifyWithInlineConfigParser(
338                 getPath("InputGenericWhitespace.java"),
339                 expected);
340     }
341 
342 }