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