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.utils;
21  
22  import static com.google.common.truth.Truth.assertThat;
23  import static com.google.common.truth.Truth.assertWithMessage;
24  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
25  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.isUtilsClassHasPrivateConstructor;
26  import static org.mockito.Mockito.CALLS_REAL_METHODS;
27  import static org.mockito.Mockito.mock;
28  import static org.mockito.Mockito.mockStatic;
29  import static org.mockito.Mockito.when;
30  
31  import java.io.Closeable;
32  import java.io.File;
33  import java.io.IOException;
34  import java.lang.reflect.Constructor;
35  import java.net.URI;
36  import java.net.URISyntaxException;
37  import java.net.URL;
38  import java.nio.charset.StandardCharsets;
39  import java.util.Dictionary;
40  import java.util.Properties;
41  import java.util.regex.Pattern;
42  
43  import org.apache.commons.io.IOUtils;
44  import org.junit.jupiter.api.Test;
45  import org.mockito.MockedStatic;
46  
47  import com.puppycrawl.tools.checkstyle.AbstractPathTestSupport;
48  import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
49  import com.puppycrawl.tools.checkstyle.PropertiesExpander;
50  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
51  import com.puppycrawl.tools.checkstyle.api.Configuration;
52  
53  public class CommonUtilTest extends AbstractPathTestSupport {
54  
55      /** After appending to path produces equivalent, but denormalized path. */
56      private static final String PATH_DENORMALIZER = "/levelDown/.././";
57  
58      @Override
59      protected String getPackageLocation() {
60          return "com/puppycrawl/tools/checkstyle/utils/commonutil";
61      }
62  
63      @Test
64      public void testIsProperUtilsClass() throws ReflectiveOperationException {
65          assertWithMessage("Constructor is not private")
66                  .that(isUtilsClassHasPrivateConstructor(CommonUtil.class))
67                  .isTrue();
68      }
69  
70      /**
71       * Test CommonUtil.countCharInString.
72       */
73      @Test
74      public void testLengthExpandedTabs() {
75          final String s1 = "\t";
76          assertWithMessage("Invalid expanded tabs length")
77              .that(CommonUtil.lengthExpandedTabs(s1, s1.length(), 8))
78              .isEqualTo(8);
79  
80          final String s2 = "  \t";
81          assertWithMessage("Invalid expanded tabs length")
82              .that(CommonUtil.lengthExpandedTabs(s2, s2.length(), 8))
83              .isEqualTo(8);
84  
85          final String s3 = "\t\t";
86          assertWithMessage("Invalid expanded tabs length")
87              .that(CommonUtil.lengthExpandedTabs(s3, s3.length(), 8))
88              .isEqualTo(16);
89  
90          final String s4 = " \t ";
91          assertWithMessage("Invalid expanded tabs length")
92              .that(CommonUtil.lengthExpandedTabs(s4, s4.length(), 8))
93              .isEqualTo(9);
94  
95          assertWithMessage("Invalid expanded tabs length")
96              .that(CommonUtil.lengthMinusTrailingWhitespace(""))
97              .isEqualTo(0);
98          assertWithMessage("Invalid expanded tabs length")
99              .that(CommonUtil.lengthMinusTrailingWhitespace(" \t "))
100             .isEqualTo(0);
101         assertWithMessage("Invalid expanded tabs length")
102             .that(CommonUtil.lengthMinusTrailingWhitespace(" 23"))
103             .isEqualTo(3);
104         assertWithMessage("Invalid expanded tabs length")
105             .that(CommonUtil.lengthMinusTrailingWhitespace(" 23 \t "))
106             .isEqualTo(3);
107     }
108 
109     @Test
110     public void testCreatePattern() {
111         assertWithMessage("invalid pattern")
112             .that(CommonUtil.createPattern("Test").pattern())
113             .isEqualTo("Test");
114         assertWithMessage("invalid pattern")
115             .that(CommonUtil.createPattern(".*Pattern.*")
116                 .pattern())
117             .isEqualTo(".*Pattern.*");
118     }
119 
120     @Test
121     public void testBadRegex() {
122         final IllegalArgumentException ex =
123                 getExpectedThrowable(IllegalArgumentException.class, () -> {
124                     CommonUtil.createPattern("[");
125                 });
126         assertWithMessage("Invalid exception message")
127                 .that(ex)
128                 .hasMessageThat()
129                         .isEqualTo("Failed to initialise regular expression [");
130     }
131 
132     @Test
133     public void testBadRegex2() {
134         final IllegalArgumentException ex =
135                 getExpectedThrowable(IllegalArgumentException.class, () -> {
136                     CommonUtil.createPattern("[", Pattern.MULTILINE);
137                 });
138         assertWithMessage("Invalid exception message")
139                 .that(ex)
140                 .hasMessageThat()
141                         .isEqualTo("Failed to initialise regular expression [");
142     }
143 
144     @Test
145     public void testFileExtensions() {
146         final String[] fileExtensions = {"java"};
147         final File pdfFile = new File("file.pdf");
148         assertWithMessage("Invalid file extension")
149                 .that(CommonUtil.matchesFileExtension(pdfFile, fileExtensions))
150                 .isFalse();
151         assertWithMessage("Invalid file extension")
152                 .that(CommonUtil.matchesFileExtension(pdfFile))
153                 .isTrue();
154         assertWithMessage("Invalid file extension")
155                 .that(CommonUtil.matchesFileExtension(pdfFile, (String[]) null))
156                 .isTrue();
157         final File javaFile = new File("file.java");
158         assertWithMessage("Invalid file extension")
159                 .that(CommonUtil.matchesFileExtension(javaFile, fileExtensions))
160                 .isTrue();
161         final File invalidJavaFile = new File("file,java");
162         assertWithMessage("Invalid file extension")
163                 .that(CommonUtil.matchesFileExtension(invalidJavaFile, fileExtensions))
164                 .isFalse();
165         final File emptyExtensionFile = new File("file.");
166         assertWithMessage("Invalid file extension")
167                 .that(CommonUtil.matchesFileExtension(emptyExtensionFile, ""))
168                 .isTrue();
169         assertWithMessage("Invalid file extension")
170                 .that(CommonUtil.matchesFileExtension(pdfFile, ".noMatch"))
171                 .isFalse();
172         assertWithMessage("Invalid file extension")
173                 .that(CommonUtil.matchesFileExtension(pdfFile, ".pdf"))
174                 .isTrue();
175     }
176 
177     @Test
178     public void testHasWhitespaceBefore() {
179         assertWithMessage("Invalid result")
180                 .that(CommonUtil.hasWhitespaceBefore(0, "a"))
181                 .isTrue();
182         assertWithMessage("Invalid result")
183                 .that(CommonUtil.hasWhitespaceBefore(4, "    a"))
184                 .isTrue();
185         assertWithMessage("Invalid result")
186                 .that(CommonUtil.hasWhitespaceBefore(5, "    a"))
187                 .isFalse();
188     }
189 
190     @Test
191     public void testBaseClassNameForCanonicalName() {
192         assertWithMessage("Invalid base class name")
193             .that(CommonUtil.baseClassName("java.util.List"))
194             .isEqualTo("List");
195     }
196 
197     @Test
198     public void testBaseClassNameForSimpleName() {
199         assertWithMessage("Invalid base class name")
200             .that(CommonUtil.baseClassName("Set"))
201             .isEqualTo("Set");
202     }
203 
204     @Test
205     public void testRelativeNormalizedPath() {
206         final String relativePath = CommonUtil.relativizePath("/home", "/home/test");
207 
208         assertWithMessage("Invalid relative path")
209             .that(relativePath)
210             .isEqualTo("test");
211     }
212 
213     @Test
214     public void testRelativeNormalizedPathWithNullBaseDirectory() {
215         final String relativePath = CommonUtil.relativizePath(null, "/tmp");
216 
217         assertWithMessage("Invalid relative path")
218             .that(relativePath)
219             .isEqualTo("/tmp");
220     }
221 
222     @Test
223     public void testRelativeNormalizedPathWithDenormalizedBaseDirectory() throws IOException {
224         final String sampleAbsolutePath = new File("src/main/java").getCanonicalPath();
225         final String absoluteFilePath = sampleAbsolutePath + "/SampleFile.java";
226         final String basePath = sampleAbsolutePath + PATH_DENORMALIZER;
227 
228         final String relativePath = CommonUtil.relativizePath(basePath,
229             absoluteFilePath);
230 
231         assertWithMessage("Invalid relative path")
232             .that(relativePath)
233             .isEqualTo("SampleFile.java");
234     }
235 
236     @Test
237     public void testPattern() {
238         final boolean result = CommonUtil.isPatternValid("someValidPattern");
239         assertWithMessage("Should return true when pattern is valid")
240                 .that(result)
241                 .isTrue();
242     }
243 
244     @Test
245     public void testInvalidPattern() {
246         final boolean result = CommonUtil.isPatternValid("some[invalidPattern");
247         assertWithMessage("Should return false when pattern is invalid")
248                 .that(result)
249                 .isFalse();
250     }
251 
252     @Test
253     public void testGetExistingConstructor() throws NoSuchMethodException {
254         final Constructor<?> constructor = CommonUtil.getConstructor(String.class, String.class);
255 
256         assertWithMessage("Invalid constructor")
257             .that(constructor)
258             .isEqualTo(String.class.getConstructor(String.class));
259     }
260 
261     @Test
262     public void testGetNonExistentConstructor() {
263         final IllegalStateException ex = getExpectedThrowable(IllegalStateException.class, () -> {
264             CommonUtil.getConstructor(Math.class);
265         });
266         assertWithMessage("Invalid exception cause")
267                 .that(ex)
268                 .hasCauseThat()
269                         .isInstanceOf(NoSuchMethodException.class);
270     }
271 
272     @Test
273     public void testInvokeConstructor() throws NoSuchMethodException {
274         final Constructor<String> constructor = String.class.getConstructor(String.class);
275 
276         final String constructedString = CommonUtil.invokeConstructor(constructor, "string");
277 
278         assertWithMessage("Invalid construction result")
279             .that(constructedString)
280             .isEqualTo("string");
281     }
282 
283     @SuppressWarnings("rawtypes")
284     @Test
285     public void testInvokeConstructorThatFails() throws NoSuchMethodException {
286         final Constructor<Dictionary> constructor = Dictionary.class.getConstructor();
287         final IllegalStateException ex = getExpectedThrowable(IllegalStateException.class, () -> {
288             CommonUtil.invokeConstructor(constructor);
289         });
290         assertWithMessage("Invalid exception cause")
291                 .that(ex)
292                 .hasCauseThat()
293                         .isInstanceOf(InstantiationException.class);
294     }
295 
296     @Test
297     public void testClose() {
298         final TestCloseable closeable = new TestCloseable();
299 
300         CommonUtil.close(null);
301         CommonUtil.close(closeable);
302 
303         assertWithMessage("Should be closed")
304                 .that(closeable.closed)
305                 .isTrue();
306     }
307 
308     @Test
309     public void testCloseWithException() {
310         final IllegalStateException ex = getExpectedThrowable(IllegalStateException.class, () -> {
311             CommonUtil.close(() -> {
312                 throw new IOException("Test IOException");
313             });
314         });
315         assertWithMessage("Invalid exception message")
316                 .that(ex)
317                 .hasMessageThat()
318                         .isEqualTo("Cannot close the stream");
319     }
320 
321     @Test
322     public void testFillTemplateWithStringsByRegexp() {
323         assertWithMessage("invalid result")
324             .that(CommonUtil.fillTemplateWithStringsByRegexp("template",
325                 "lineToPlaceInTemplate", Pattern.compile("NO MATCH")))
326             .isEqualTo("template");
327         assertWithMessage("invalid result")
328             .that(CommonUtil.fillTemplateWithStringsByRegexp("before $0 after", "word",
329                         Pattern.compile("\\w+")))
330             .isEqualTo("before word after");
331         assertWithMessage("invalid result")
332             .that(CommonUtil.fillTemplateWithStringsByRegexp("before $0 after1 $1 after2 $2 after3",
333                         "word 123", Pattern.compile("(\\w+) (\\d+)")))
334             .isEqualTo("before word 123 after1 word after2 123 after3");
335     }
336 
337     @Test
338     public void testGetFileNameWithoutExtension() {
339         assertWithMessage("invalid result")
340             .that(CommonUtil.getFileNameWithoutExtension("filename"))
341             .isEqualTo("filename");
342         assertWithMessage("invalid result")
343             .that(CommonUtil.getFileNameWithoutExtension("filename.extension"))
344             .isEqualTo("filename");
345         assertWithMessage("invalid result")
346             .that(CommonUtil.getFileNameWithoutExtension("filename.subext.extension"))
347             .isEqualTo("filename.subext");
348     }
349 
350     @Test
351     public void testGetFileExtension() {
352         assertWithMessage("Invalid extension")
353             .that(CommonUtil.getFileExtension("filename"))
354             .isEqualTo("");
355         assertWithMessage("Invalid extension")
356             .that(CommonUtil.getFileExtension("filename.extension"))
357             .isEqualTo("extension");
358         assertWithMessage("Invalid extension")
359             .that(CommonUtil.getFileExtension("filename.subext.extension"))
360             .isEqualTo("extension");
361     }
362 
363     @Test
364     public void testIsIdentifier() {
365         assertWithMessage("Should return true when valid identifier is passed")
366                 .that(CommonUtil.isIdentifier("aValidIdentifier"))
367                 .isTrue();
368     }
369 
370     @Test
371     public void testIsIdentifierEmptyString() {
372         assertWithMessage("Should return false when empty string is passed")
373                 .that(CommonUtil.isIdentifier(""))
374                 .isFalse();
375     }
376 
377     @Test
378     public void testIsIdentifierInvalidFirstSymbol() {
379         assertWithMessage("Should return false when invalid identifier is passed")
380                 .that(CommonUtil.isIdentifier("1InvalidIdentifier"))
381                 .isFalse();
382     }
383 
384     @Test
385     public void testIsIdentifierInvalidSymbols() {
386         assertWithMessage("Should return false when invalid identifier is passed")
387                 .that(CommonUtil.isIdentifier("invalid#Identifier"))
388                 .isFalse();
389     }
390 
391     @Test
392     public void testIsName() {
393         assertWithMessage("Should return true when valid name is passed")
394                 .that(CommonUtil.isName("a.valid.Nam3"))
395                 .isTrue();
396     }
397 
398     @Test
399     public void testIsNameEmptyString() {
400         assertWithMessage("Should return false when empty string is passed")
401                 .that(CommonUtil.isName(""))
402                 .isFalse();
403     }
404 
405     @Test
406     public void testIsNameInvalidFirstSymbol() {
407         assertWithMessage("Should return false when invalid name is passed")
408                 .that(CommonUtil.isName("1.invalid.name"))
409                 .isFalse();
410     }
411 
412     @Test
413     public void testIsNameEmptyPart() {
414         assertWithMessage("Should return false when name has empty part")
415                 .that(CommonUtil.isName("invalid..name"))
416                 .isFalse();
417     }
418 
419     @Test
420     public void testIsNameEmptyLastPart() {
421         assertWithMessage("Should return false when name has empty part")
422                 .that(CommonUtil.isName("invalid.name."))
423                 .isFalse();
424     }
425 
426     @Test
427     public void testIsNameInvalidSymbol() {
428         assertWithMessage("Should return false when invalid name is passed")
429                 .that(CommonUtil.isName("invalid.name#42"))
430                 .isFalse();
431     }
432 
433     @Test
434     public void testIsBlank() {
435         assertWithMessage("Should return false when string is not empty")
436                 .that(CommonUtil.isBlank("string"))
437                 .isFalse();
438     }
439 
440     @Test
441     public void testIsBlankAheadWhitespace() {
442         assertWithMessage("Should return false when string is not empty")
443                 .that(CommonUtil.isBlank("  string"))
444                 .isFalse();
445     }
446 
447     @Test
448     public void testIsBlankBehindWhitespace() {
449         assertWithMessage("Should return false when string is not empty")
450                 .that(CommonUtil.isBlank("string    "))
451                 .isFalse();
452     }
453 
454     @Test
455     public void testIsBlankWithWhitespacesAround() {
456         assertWithMessage("Should return false when string is not empty")
457                 .that(CommonUtil.isBlank("    string    "))
458                 .isFalse();
459     }
460 
461     @Test
462     public void testIsBlankWhitespaceInside() {
463         assertWithMessage("Should return false when string is not empty")
464                 .that(CommonUtil.isBlank("str    ing"))
465                 .isFalse();
466     }
467 
468     @Test
469     public void testIsBlankNullString() {
470         assertWithMessage("Should return true when string is null")
471                 .that(CommonUtil.isBlank(null))
472                 .isTrue();
473     }
474 
475     @Test
476     public void testIsBlankWithEmptyString() {
477         assertWithMessage("Should return true when string is empty")
478                 .that(CommonUtil.isBlank(""))
479                 .isTrue();
480     }
481 
482     @Test
483     public void testIsBlankWithWhitespacesOnly() {
484         assertWithMessage("Should return true when string contains only spaces")
485                 .that(CommonUtil.isBlank("   "))
486                 .isTrue();
487     }
488 
489     @Test
490     public void testGetUriByFilenameFindsAbsoluteResourceOnClasspath() throws Exception {
491         final String filename =
492             "/" + getPackageLocation() + "/InputCommonUtilTest_empty_checks.xml";
493         final URI uri = CommonUtil.getUriByFilename(filename);
494 
495         final Properties properties = System.getProperties();
496         final Configuration config = ConfigurationLoader.loadConfiguration(uri.toASCIIString(),
497             new PropertiesExpander(properties));
498         assertWithMessage("Unexpected config name!")
499             .that(config.getName())
500             .isEqualTo("Checker");
501     }
502 
503     @Test
504     public void testGetUriByFilenameFindsRelativeResourceOnClasspath() throws Exception {
505         final String filename =
506             getPackageLocation() + "/InputCommonUtilTest_empty_checks.xml";
507         final URI uri = CommonUtil.getUriByFilename(filename);
508 
509         final Properties properties = System.getProperties();
510         final Configuration config = ConfigurationLoader.loadConfiguration(uri.toASCIIString(),
511             new PropertiesExpander(properties));
512         assertWithMessage("Unexpected config name!")
513             .that(config.getName())
514             .isEqualTo("Checker");
515     }
516 
517     /**
518      * This test illustrates #6232.
519      * Without fix, the assertion will fail because the URL under test
520      * "com/puppycrawl/tools/checkstyle/utils/commonutil/InputCommonUtilTest_resource.txt"
521      * will be interpreted relative to the current package
522      * "com/puppycrawl/tools/checkstyle/utils/"
523      */
524     @Test
525     public void testGetUriByFilenameFindsResourceRelativeToRootClasspath() throws Exception {
526         final String filename =
527                 getPackageLocation() + "/InputCommonUtilTest_resource.txt";
528         final URI uri = CommonUtil.getUriByFilename(filename);
529         assertWithMessage("URI is null for: " + filename)
530             .that(uri)
531             .isNotNull();
532         final String uriRelativeToPackage =
533                 "com/puppycrawl/tools/checkstyle/utils/"
534                         + getPackageLocation() + "/InputCommonUtilTest_resource.txt";
535         assertWithMessage("URI is relative to package " + uriRelativeToPackage)
536             .that(uri.toASCIIString())
537             .doesNotContain(uriRelativeToPackage);
538         final String content = IOUtils.toString(uri.toURL(), StandardCharsets.UTF_8);
539         assertWithMessage("Content mismatches for: " + uri.toASCIIString())
540             .that(content)
541             .startsWith("good");
542     }
543 
544     @Test
545     public void testGetUriByFilenameClasspathPrefixLoadConfig() throws Exception {
546         final String filename = CommonUtil.CLASSPATH_URL_PROTOCOL
547             + getPackageLocation() + "/InputCommonUtilTestWithChecks.xml";
548         final URI uri = CommonUtil.getUriByFilename(filename);
549 
550         final Properties properties = System.getProperties();
551         final Configuration config = ConfigurationLoader.loadConfiguration(uri.toASCIIString(),
552             new PropertiesExpander(properties));
553         assertWithMessage("Unexpected config name!")
554             .that(config.getName())
555             .isEqualTo("Checker");
556     }
557 
558     @Test
559     public void testGetUriByFilenameFindsRelativeResourceOnClasspathPrefix() throws Exception {
560         final String filename = CommonUtil.CLASSPATH_URL_PROTOCOL
561             + getPackageLocation() + "/InputCommonUtilTest_empty_checks.xml";
562         final URI uri = CommonUtil.getUriByFilename(filename);
563 
564         final Properties properties = System.getProperties();
565         final Configuration config = ConfigurationLoader.loadConfiguration(uri.toASCIIString(),
566             new PropertiesExpander(properties));
567         assertWithMessage("Unexpected config name!")
568             .that(config.getName())
569             .isEqualTo("Checker");
570     }
571 
572     @Test
573     public void testIsCodePointWhitespace() {
574         final int[] codePoints = " 123".codePoints().toArray();
575         assertThat(CommonUtil.isCodePointWhitespace(codePoints, 0))
576                 .isTrue();
577         assertThat(CommonUtil.isCodePointWhitespace(codePoints, 1))
578                 .isFalse();
579     }
580 
581     @Test
582     public void testLoadSuppressionsUriSyntaxException() throws Exception {
583         final URL configUrl = mock();
584         when(configUrl.toURI()).thenThrow(URISyntaxException.class);
585         try (MockedStatic<CommonUtil> utilities =
586                      mockStatic(CommonUtil.class, CALLS_REAL_METHODS)) {
587             final String fileName = "/suppressions_none.xml";
588             utilities.when(() -> CommonUtil.getCheckstyleResource(fileName))
589                     .thenReturn(configUrl);
590 
591             final CheckstyleException ex = getExpectedThrowable(CheckstyleException.class, () -> {
592                 CommonUtil.getUriByFilename(fileName);
593             });
594             assertWithMessage("Invalid exception cause")
595                     .that(ex)
596                     .hasCauseThat()
597                             .isInstanceOf(URISyntaxException.class);
598             assertWithMessage("Invalid exception message")
599                     .that(ex)
600                     .hasMessageThat()
601                             .isEqualTo("Unable to find: " + fileName);
602         }
603     }
604 
605     private static final class TestCloseable implements Closeable {
606 
607         private boolean closed;
608 
609         @Override
610         public void close() {
611             closed = true;
612         }
613 
614     }
615 
616 }