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.coding;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.coding.IllegalSymbolCheck.MSG_KEY;
24  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
25  import static org.junit.Assert.assertEquals;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import org.junit.jupiter.api.Test;
30  
31  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
32  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
33  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
34  
35  public class IllegalSymbolCheckTest extends AbstractModuleTestSupport {
36  
37      @Override
38      public String getPackageLocation() {
39          return "com/puppycrawl/tools/checkstyle/checks/coding/illegalsymbol";
40      }
41  
42      @Test
43      public void testDefault() throws Exception {
44          final String[] expected = {
45              "12:7: " + getCheckMessage(MSG_KEY, "✅"),
46          };
47          verifyWithInlineConfigParser(
48                  getPath("InputIllegalSymbolDefault.java"), expected);
49      }
50  
51      @Test
52      public void testEmojiInComment() throws Exception {
53          final String[] expected = {
54              "12:18: " + getCheckMessage(MSG_KEY, "✅"),
55              "13:18: " + getCheckMessage(MSG_KEY, "❌"),
56          };
57          verifyWithInlineConfigParser(
58                  getPath("InputIllegalSymbolEmoji.java"), expected);
59      }
60  
61      @Test
62      public void testAsciiOnly() throws Exception {
63          final String[] expected = {
64              "12:18: " + getCheckMessage(MSG_KEY, "é"),
65              "13:18: " + getCheckMessage(MSG_KEY, "•"),
66          };
67  
68          verifyWithInlineConfigParser(
69                  getPath("InputIllegalSymbolAsciiOnly.java"),
70                  expected);
71      }
72  
73      @Test
74      public void testMultipleRanges() throws Exception {
75          final String[] expected = {
76              "12:19: " + getCheckMessage(MSG_KEY, "✅"),
77              "13:19: " + getCheckMessage(MSG_KEY, "😀"),
78              "14:19: " + getCheckMessage(MSG_KEY, "©"),
79          };
80          verifyWithInlineConfigParser(
81                  getPath("InputIllegalSymbolMultipleRanges.java"), expected);
82      }
83  
84      @Test
85      public void testMultipleViolationsInSameToken() throws Exception {
86          final String[] expected = {
87              "12:18: " + getCheckMessage(MSG_KEY, "😀"),
88          };
89  
90          verifyWithInlineConfigParser(
91                  getPath("InputIllegalSymbolMultiple.java"), expected);
92      }
93  
94      @Test
95      public void testNoViolationWhenNotConfigured() throws Exception {
96          final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
97  
98          verifyWithInlineConfigParser(
99                  getPath("InputIllegalSymbolNoConfig.java"), expected);
100     }
101 
102     @Test
103     public void testSymbolStringLiteral() throws Exception {
104         final String[] expected = {
105             "12:16: " + getCheckMessage(MSG_KEY, "😀"),
106         };
107 
108         verifyWithInlineConfigParser(
109                 getPath("InputIllegalSymbolStringLiteral.java"), expected);
110     }
111 
112     @Test
113     public void testCharLiteral() throws Exception {
114         final String[] expected = {
115             "12:14: " + getCheckMessage(MSG_KEY, "é"),
116         };
117 
118         verifyWithInlineConfigParser(
119                 getPath("InputIllegalSymbolChar.java"), expected);
120     }
121 
122     @Test
123     public void testTextBlock() throws Exception {
124         final String[] expected = {
125             "13:19: " + getCheckMessage(MSG_KEY, "😀"),
126         };
127 
128         verifyWithInlineConfigParser(
129                 getPath("InputIllegalSymbolTextBlock.java"), expected);
130     }
131 
132     @Test
133     public void testLowercasePlusU() throws Exception {
134         final String[] expected = {
135             "12:18: " + getCheckMessage(MSG_KEY, "✅"),
136         };
137 
138         verifyWithInlineConfigParser(
139                 getPath("InputIllegalSymbolLowercaseU.java"), expected);
140     }
141 
142     @Test
143     public void testUppercase0X() throws Exception {
144         final String[] expected = {
145             "12:18: " + getCheckMessage(MSG_KEY, "✅"),
146         };
147 
148         verifyWithInlineConfigParser(
149                 getPath("InputIllegalSymbolUppercase0X.java"), expected);
150     }
151 
152     @Test
153     public void testHexWithoutPrefix() throws Exception {
154         final String[] expected = {
155             "12:18: " + getCheckMessage(MSG_KEY, "✅"),
156         };
157 
158         verifyWithInlineConfigParser(
159                 getPath("InputIllegalSymbolHexNoPrefix.java"), expected);
160     }
161 
162     @Test
163     public void testBackslashFormatU() throws Exception {
164         final String[] expected = {
165             "12:18: " + getCheckMessage(MSG_KEY, "✅"),
166         };
167 
168         verifyWithInlineConfigParser(
169                 getPath("InputIllegalSymbolBackslashU.java"), expected);
170     }
171 
172     @Test
173     public void testUnicodePlusFormat() throws Exception {
174         final String[] expected = {
175             "14:7: " + getCheckMessage(MSG_KEY, "✅"),
176         };
177         verifyWithInlineConfigParser(
178                 getPath("InputIllegalSymbolUnicodePlus.java"), expected);
179     }
180 
181     @Test
182     public void testInvalidRangeMultipleDashes() throws Exception {
183         try {
184             verifyWithInlineConfigParser(
185                     getPath("InputIllegalSymbolMultipleDashes.java"),
186                     CommonUtil.EMPTY_STRING_ARRAY);
187             fail("CheckstyleException expected");
188         }
189         catch (CheckstyleException exception) {
190             assertTrue("Exception should indicate initialization failure",
191                     exception.getMessage().contains("cannot initialize module"));
192         }
193     }
194 
195     @Test
196     public void testSetSymbolCodesNull() throws Exception {
197         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
198         verifyWithInlineConfigParser(
199                 getPath("InputIllegalSymbolNullConfig.java"),
200                 expected);
201     }
202 
203     @Test
204     public void testSetSymbolCodesEmptyViaConfig() throws Exception {
205         verifyWithInlineConfigParser(
206                 getPath("InputIllegalSymbolEmptyConfig.java"),
207                 CommonUtil.EMPTY_STRING_ARRAY);
208     }
209 
210     @Test
211     public void testInitParseSymbolCodesIsCalledAndPopulatesSets() throws Exception {
212         final String[] expected = {
213             "12:7: " + getCheckMessage(MSG_KEY, "😀"),
214             "13:7: " + getCheckMessage(MSG_KEY, "🚀"),
215         };
216         verifyWithInlineConfigParser(
217                 getPath("InputIllegalSymbolDefaultStrong.java"), expected);
218     }
219 
220     @Test
221     public void testRangeStartEqualsEnd() throws Exception {
222         final String[] expected = {
223             "12:18: " + getCheckMessage(MSG_KEY, "✅"),
224         };
225         verifyWithInlineConfigParser(
226                 getPath("InputIllegalSymbolRangeStartEqualsEnd.java"), expected);
227     }
228 
229     @Test
230     public void testRangePartsAreTrimmed() throws Exception {
231         final String[] expected = {
232             "12:18: " + getCheckMessage(MSG_KEY, "✈"),
233         };
234         verifyWithInlineConfigParser(
235                 getPath("InputIllegalSymbolRangeTrimmed.java"), expected);
236     }
237 
238     @Test
239     public void testSymbolCodesWithSpacesAreTrimmed() throws Exception {
240         final String[] expected = {
241             "12:18: " + getCheckMessage(MSG_KEY, "✅"),
242         };
243         verifyWithInlineConfigParser(
244                 getPath("InputIllegalSymbolSpacedEntries.java"), expected);
245     }
246 
247     @Test
248     public void testInvalidSingleSymbolCode() throws Exception {
249         try {
250             verifyWithInlineConfigParser(
251                     getPath("InputIllegalSymbolInvalidCode.java"),
252                     CommonUtil.EMPTY_STRING_ARRAY);
253             fail("CheckstyleException expected");
254         }
255         catch (CheckstyleException exception) {
256             final String message = exception.getMessage();
257             assertTrue("Exception should indicate initialization failure, but got: " + message,
258                     message.contains("cannot initialize module")
259                             || message.contains("Invalid symbol code format")
260                             || message.contains("NumberFormatException")
261                             || message.contains("Cannot set property"));
262         }
263     }
264 
265     @Test
266     public void testInvalidRangeFormat() throws Exception {
267         try {
268             verifyWithInlineConfigParser(
269                     getPath("InputIllegalSymbolInvalidRange.java"),
270                     CommonUtil.EMPTY_STRING_ARRAY);
271             fail("CheckstyleException expected");
272         }
273         catch (CheckstyleException exception) {
274             final String message = exception.getMessage();
275             assertTrue("Exception should indicate initialization failure, but got: " + message,
276                     message.contains("cannot initialize module")
277                             || message.contains("Invalid range format")
278                             || message.contains("Cannot set property"));
279         }
280     }
281 
282     @Test
283     public void testRangeStartGreaterThanEnd() throws Exception {
284         try {
285             verifyWithInlineConfigParser(
286                     getPath("InputIllegalSymbolRangeReversed.java"),
287                     CommonUtil.EMPTY_STRING_ARRAY);
288             fail("CheckstyleException expected");
289         }
290         catch (CheckstyleException exception) {
291             final String message = exception.getMessage();
292             assertTrue("Exception should indicate initialization failure, but got: " + message,
293                     message.contains("cannot initialize module")
294                             || message.contains("Range start must be <= end")
295                             || message.contains("Cannot set property"));
296         }
297     }
298 
299     @Test
300     public void testInvalidShortCodePoint() throws Exception {
301         try {
302             verifyWithInlineConfigParser(
303                     getPath("InputIllegalSymbolShortCode.java"),
304                     CommonUtil.EMPTY_STRING_ARRAY);
305             fail("CheckstyleException expected");
306         }
307         catch (CheckstyleException exception) {
308             final String message = exception.getMessage();
309             assertTrue("Exception should indicate initialization failure, but got: " + message,
310                     message.contains("cannot initialize module")
311                             || message.contains("Invalid code point format")
312                             || message.contains("NumberFormatException")
313                             || message.contains("Cannot set property"));
314         }
315     }
316 
317     @Test
318     public void testSetSymbolCodesIgnoresEmptyEntries() throws Exception {
319         final String[] expected = {
320             "12:7: " + getCheckMessage(MSG_KEY, "✅"),
321         };
322         verifyWithInlineConfigParser(
323                 getPath("InputIllegalSymbolIgnoreEmpty.java"),
324                 expected);
325     }
326 
327     @Test
328     public void testSetSymbolCodesEmptyList() throws Exception {
329         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
330 
331         verifyWithInlineConfigParser(
332                 getPath("InputIllegalSymbolNoConfig.java"),
333                 expected);
334 
335         assertEquals("Expected should have 0 violations", 0, expected.length);
336     }
337 
338     @Test
339     public void testSetSymbolCodesBlankRangeStart() throws Exception {
340         try {
341             verifyWithInlineConfigParser(
342                     getPath("InputIllegalSymbolBlankStart.java"),
343                     CommonUtil.EMPTY_STRING_ARRAY);
344             fail("CheckstyleException expected");
345         }
346         catch (CheckstyleException exception) {
347             final String message = exception.getMessage();
348             assertTrue("Exception should indicate initialization failure, but got: " + message,
349                     message.contains("cannot initialize module")
350                             || message.contains("Invalid range format")
351                             || message.contains("Cannot set property"));
352         }
353     }
354 
355     @Test
356     public void testContainsReturnsFalse() throws Exception {
357         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
358 
359         verifyWithInlineConfigParser(
360                 getPath("InputIllegalSymbolOutsideRange.java"),
361                 expected);
362     }
363 
364     @Test
365     public void testSetSymbolCodesEmptyString() throws Exception {
366         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
367 
368         verifyWithInlineConfigParser(
369                 getPath("InputIllegalSymbolEmptyConfig.java"),
370                 expected);
371 
372         assertEquals("Expected should have 0 violations", 0, expected.length);
373     }
374 
375     @Test
376     public void testRangeStartBoundary() throws Exception {
377         final String[] expected = {
378             "13:18: " + getCheckMessage(MSG_KEY, "😀"),
379         };
380         verifyWithInlineConfigParser(
381                 getPath("InputIllegalSymbolRangeStart.java"), expected);
382     }
383 
384     @Test
385     public void testRangeEndBoundary() throws Exception {
386         final String[] expected = {
387             "13:18: " + getCheckMessage(MSG_KEY, "🙏"),
388         };
389         verifyWithInlineConfigParser(
390                 getPath("InputIllegalSymbolRangeEnd.java"), expected);
391     }
392 
393     @Test
394     public void testSetSymbolCodesTwice() throws Exception {
395         final String[] expected = {
396             "12:18: " + getCheckMessage(MSG_KEY, "😀"),
397         };
398         verifyWithInlineConfigParser(
399                 getPath("InputIllegalSymbolSetTwice.java"), expected);
400     }
401 
402     @Test
403     public void testSymbolNotInRange() throws Exception {
404         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
405         verifyWithInlineConfigParser(
406                 getPath("InputIllegalSymbolNotInRange.java"), expected);
407     }
408 
409     @Test
410     public void testEmptyConfigDoesNothing() throws Exception {
411         verifyWithInlineConfigParser(
412                 getPath("InputIllegalSymbolEmpty.java"),
413                 CommonUtil.EMPTY_STRING_ARRAY);
414     }
415 
416     @Test
417     public void testJustOutsideRange() throws Exception {
418         verifyWithInlineConfigParser(
419                 getPath("InputIllegalSymbolJustOutsideRange.java"),
420                 CommonUtil.EMPTY_STRING_ARRAY);
421     }
422 
423     @Test
424     public void testSetThenClearConfig() throws Exception {
425         verifyWithInlineConfigParser(
426                 getPath("InputIllegalSymbolSetThenClear.java"),
427                 CommonUtil.EMPTY_STRING_ARRAY);
428     }
429 
430     @Test
431     public void testRangeBoundaries() throws Exception {
432         final String[] expected = {
433             "13:18: " + getCheckMessage(MSG_KEY, "😀"),
434             "14:18: " + getCheckMessage(MSG_KEY, "😁"),
435             "15:18: " + getCheckMessage(MSG_KEY, "🙏"),
436         };
437 
438         verifyWithInlineConfigParser(
439                 getPath("InputIllegalSymbolRangeBoundaries.java"),
440                 expected);
441     }
442 
443     @Test
444     public void testMultipleIllegalSymbolsOnlyOneViolation() throws Exception {
445         final String[] expected = {
446             "12:18: " + getCheckMessage(MSG_KEY, "😀"),
447         };
448 
449         verifyWithInlineConfigParser(
450                 getPath("InputIllegalSymbolMultipleInComment.java"),
451                 expected);
452     }
453 
454     @Test
455     public void testTrimmedCommaSeparatedValues() throws Exception {
456         final String[] expected = {
457             "12:18: " + getCheckMessage(MSG_KEY, "😀"),
458         };
459 
460         verifyWithInlineConfigParser(
461                 getPath("InputIllegalSymbolTrimComma.java"),
462                 expected);
463     }
464 
465     @Test
466     public void testInvalidShortPrefix() {
467         final IllegalSymbolCheck check = new IllegalSymbolCheck();
468 
469         final IllegalArgumentException ex0x = getExpectedThrowable(
470                 IllegalArgumentException.class,
471                 () -> check.setSymbolCodes("0x"));
472         assertWithMessage("Wrong exception message for '0x' prefix")
473                 .that(ex0x.getMessage())
474                 .contains("Invalid");
475 
476         final IllegalArgumentException ex = getExpectedThrowable(
477                 IllegalArgumentException.class,
478                 () -> check.setSymbolCodes("U+"));
479         assertWithMessage("Wrong exception message for 'U+' prefix")
480                 .that(ex.getMessage())
481                 .contains("Invalid");
482     }
483 
484     @Test
485     public void testInvalidRangeFormats() {
486         final IllegalSymbolCheck check = new IllegalSymbolCheck();
487 
488         final IllegalArgumentException exMissingStart = getExpectedThrowable(
489                 IllegalArgumentException.class,
490                 () -> check.setSymbolCodes("-0x1234"));
491         assertWithMessage("Exception message mismatch for missing range start")
492                 .that(exMissingStart.getMessage())
493                 .contains("Invalid range");
494 
495         final IllegalArgumentException exMissingEnd = getExpectedThrowable(
496                 IllegalArgumentException.class,
497                 () -> check.setSymbolCodes("0x1234-"));
498         assertWithMessage("Exception message mismatch for missing range end")
499                 .that(exMissingEnd.getMessage())
500                 .contains("Invalid range");
501     }
502 
503     @Test
504     public void testSingleAndRangeOverlap() throws Exception {
505         final String[] expected = {
506             "12:18: " + getCheckMessage(MSG_KEY, "😀"),
507         };
508 
509         verifyWithInlineConfigParser(
510             getPath("InputIllegalSymbolSingleAndRange.java"),
511             expected);
512     }
513 
514     @Test
515     public void testInvalidRangeOrder() {
516         final IllegalSymbolCheck check = new IllegalSymbolCheck();
517 
518         final IllegalArgumentException exception = getExpectedThrowable(
519                 IllegalArgumentException.class,
520                 () -> check.setSymbolCodes("0x1F601-0x1F600"));
521         assertWithMessage("Wrong message")
522                 .that(exception.getMessage())
523                 .contains("Range start must be <=");
524     }
525 
526     @Test
527     public void testSingleAndRangeOverlapBehaviour() throws Exception {
528         final String[] expected = {
529             "12:18: " + getCheckMessage(MSG_KEY, "😀"),
530         };
531 
532         verifyWithInlineConfigParser(
533             getPath("InputIllegalSymbolOverlap.java"),
534             expected);
535     }
536 }