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