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;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
24  
25  import java.io.File;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.net.URL;
29  import java.net.URLConnection;
30  import java.net.URLStreamHandler;
31  import java.util.Arrays;
32  import java.util.Collections;
33  import java.util.Enumeration;
34  import java.util.HashSet;
35  import java.util.Set;
36  
37  import org.junit.jupiter.api.Test;
38  import org.xml.sax.SAXException;
39  
40  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41  
42  /**
43   * Tests loading of package names from XML files.
44   *
45   * @noinspection ClassLoaderInstantiation
46   * @noinspectionreason ClassLoaderInstantiation - Custom class loader is needed to pass URLs to
47   *      pretend these are loaded from the classpath though we can't add/change the files for
48   *      testing. The class loader is nested in this class, so the custom class loader we
49   *      are using is safe.
50   */
51  public class PackageNamesLoaderTest extends AbstractPathTestSupport {
52  
53      @Override
54      protected String getPackageLocation() {
55          return "com/puppycrawl/tools/checkstyle/packagenamesloader";
56      }
57  
58      @Test
59      public void testDefault()
60              throws CheckstyleException {
61          final Set<String> packageNames = PackageNamesLoader
62                  .getPackageNames(Thread.currentThread()
63                          .getContextClassLoader());
64          assertWithMessage("pkgNames.length.")
65              .that(packageNames)
66              .isEmpty();
67      }
68  
69      @Test
70      public void testNoPackages() throws Exception {
71          final Set<String> actualPackageNames = PackageNamesLoader
72                  .getPackageNames(new TestUrlsClassLoader(Collections.emptyEnumeration()));
73  
74          assertWithMessage("Invalid package names length.")
75              .that(actualPackageNames)
76              .isEmpty();
77      }
78  
79      @Test
80      public void testPackagesFile() throws Exception {
81          final Enumeration<URL> enumeration = Collections.enumeration(Collections.singleton(
82                  new File(getPath("InputPackageNamesLoaderFile.xml")).toURI().toURL()));
83  
84          final Set<String> actualPackageNames = PackageNamesLoader
85                  .getPackageNames(new TestUrlsClassLoader(enumeration));
86          final String[] expectedPackageNames = {
87              "com.puppycrawl.tools.checkstyle",
88              "com.puppycrawl.tools.checkstyle.checks",
89              "com.puppycrawl.tools.checkstyle.checks.annotation",
90              "com.puppycrawl.tools.checkstyle.checks.blocks",
91              "com.puppycrawl.tools.checkstyle.checks.coding",
92              "com.puppycrawl.tools.checkstyle.checks.design",
93              "com.puppycrawl.tools.checkstyle.checks.header",
94              "com.puppycrawl.tools.checkstyle.checks.imports",
95              "com.puppycrawl.tools.checkstyle.checks.indentation",
96              "com.puppycrawl.tools.checkstyle.checks.javadoc",
97              "com.puppycrawl.tools.checkstyle.checks.metrics",
98              "com.puppycrawl.tools.checkstyle.checks.modifier",
99              "com.puppycrawl.tools.checkstyle.checks.naming",
100             "com.puppycrawl.tools.checkstyle.checks.regexp",
101             "com.puppycrawl.tools.checkstyle.checks.sizes",
102             "com.puppycrawl.tools.checkstyle.checks.whitespace",
103             "com.puppycrawl.tools.checkstyle.filefilters",
104             "com.puppycrawl.tools.checkstyle.filters",
105         };
106 
107         assertWithMessage("Invalid package names length.")
108             .that(actualPackageNames)
109             .hasSize(expectedPackageNames.length);
110         final Set<String> checkstylePackagesSet =
111                 new HashSet<>(Arrays.asList(expectedPackageNames));
112         assertWithMessage("Invalid names set.")
113             .that(actualPackageNames)
114             .isEqualTo(checkstylePackagesSet);
115     }
116 
117     @Test
118     public void testPackagesWithDots() throws Exception {
119         final Enumeration<URL> enumeration = Collections.enumeration(Collections.singleton(
120                 new File(getPath("InputPackageNamesLoaderWithDots.xml")).toURI().toURL()));
121 
122         final Set<String> actualPackageNames = PackageNamesLoader
123                 .getPackageNames(new TestUrlsClassLoader(enumeration));
124         final String[] expectedPackageNames = {
125             "coding.",
126         };
127 
128         assertWithMessage("Invalid package names length.")
129             .that(actualPackageNames)
130             .hasSize(expectedPackageNames.length);
131         final Set<String> checkstylePackagesSet =
132                 new HashSet<>(Arrays.asList(expectedPackageNames));
133         assertWithMessage("Invalid names set.")
134             .that(actualPackageNames)
135             .isEqualTo(checkstylePackagesSet);
136     }
137 
138     @Test
139     public void testPackagesWithDotsEx() throws Exception {
140         final Enumeration<URL> enumeration = Collections.enumeration(Collections.singleton(
141                 new File(getPath("InputPackageNamesLoaderWithDotsEx.xml")).toURI().toURL()));
142 
143         final Set<String> actualPackageNames = PackageNamesLoader
144                 .getPackageNames(new TestUrlsClassLoader(enumeration));
145         final String[] expectedPackageNames = {
146             "coding.specific",
147             "coding.",
148         };
149 
150         assertWithMessage("Invalid package names length.")
151             .that(actualPackageNames)
152             .hasSize(expectedPackageNames.length);
153         final Set<String> checkstylePackagesSet =
154                 new HashSet<>(Arrays.asList(expectedPackageNames));
155         assertWithMessage("Invalid names set.")
156             .that(actualPackageNames)
157             .isEqualTo(checkstylePackagesSet);
158     }
159 
160     @Test
161     public void testPackagesWithSaxException() throws Exception {
162         final Enumeration<URL> enumeration = Collections.enumeration(Collections.singleton(
163                 new File(getPath("InputPackageNamesLoaderNotXml.java")).toURI().toURL()));
164 
165         try {
166             PackageNamesLoader.getPackageNames(new TestUrlsClassLoader(enumeration));
167             assertWithMessage("CheckstyleException is expected").fail();
168         }
169         catch (CheckstyleException ex) {
170             assertWithMessage("Invalid exception cause class")
171                     .that(ex)
172                     .hasCauseThat()
173                     .isInstanceOf(SAXException.class);
174         }
175     }
176 
177     @Test
178     public void testPackagesWithIoException() throws Exception {
179         final URLConnection urlConnection = new URLConnection(null) {
180             @Override
181             public void connect() {
182                 // no code
183             }
184 
185             @Override
186             public InputStream getInputStream() {
187                 return null;
188             }
189         };
190         final URL url = new URL("test", null, 0, "", new URLStreamHandler() {
191             @Override
192             protected URLConnection openConnection(URL u) {
193                 return urlConnection;
194             }
195         });
196 
197         final Enumeration<URL> enumeration = Collections.enumeration(Collections.singleton(url));
198 
199         try {
200             PackageNamesLoader.getPackageNames(new TestUrlsClassLoader(enumeration));
201             assertWithMessage("CheckstyleException is expected").fail();
202         }
203         catch (CheckstyleException ex) {
204             assertWithMessage("Invalid exception cause class")
205                     .that(ex)
206                     .hasCauseThat()
207                     .isInstanceOf(IOException.class);
208             assertWithMessage("Invalid exception message")
209                     .that(ex)
210                     .hasMessageThat()
211                     .isNotEqualTo("unable to get package file resources");
212         }
213     }
214 
215     @Test
216     public void testPackagesWithIoExceptionGetResources() {
217         try {
218             PackageNamesLoader.getPackageNames(new TestIoExceptionClassLoader());
219             assertWithMessage("CheckstyleException is expected").fail();
220         }
221         catch (CheckstyleException ex) {
222             assertWithMessage("Invalid exception cause class")
223                     .that(ex)
224                     .hasCauseThat()
225                     .isInstanceOf(IOException.class);
226             assertWithMessage("Invalid exception message")
227                 .that(ex.getMessage())
228                 .isEqualTo("unable to get package file resources");
229         }
230     }
231 
232     @Test
233     public void testUnmodifiableCollection() throws Exception {
234         final Set<String> actualPackageNames = PackageNamesLoader
235                 .getPackageNames(new TestUrlsClassLoader(Collections.emptyEnumeration()));
236 
237         final Exception ex = getExpectedThrowable(UnsupportedOperationException.class,
238                 () -> actualPackageNames.add("com.puppycrawl.tools.checkstyle.checks.modifier"));
239 
240         assertWithMessage("Exception class is not expected")
241                 .that(ex.getClass())
242                 .isEqualTo(UnsupportedOperationException.class);
243     }
244 
245     @Test
246     public void testMapping() throws Exception {
247         final Enumeration<URL> enumeration = Collections.enumeration(Collections.singleton(
248                 new File(getPath("InputPackageNamesLoader1.xml")).toURI().toURL()));
249 
250         final Set<String> actualPackageNames = PackageNamesLoader
251                 .getPackageNames(new TestUrlsClassLoader(enumeration));
252 
253         assertWithMessage("Invalid package names length.")
254             .that(actualPackageNames)
255             .hasSize(3);
256     }
257 
258     @Test
259     public void testMapping2() throws Exception {
260         final Enumeration<URL> enumeration = Collections.enumeration(Collections.singleton(
261                 new File(getPath("InputPackageNamesLoader2.xml")).toURI().toURL()));
262 
263         final Set<String> actualPackageNames = PackageNamesLoader
264                 .getPackageNames(new TestUrlsClassLoader(enumeration));
265 
266         assertWithMessage("Invalid package names length.")
267             .that(actualPackageNames)
268             .hasSize(3);
269     }
270 
271     /**
272      * Mocked ClassLoader for testing URL loading.
273      *
274      * @noinspection CustomClassloader
275      * @noinspectionreason CustomClassloader - needed to pass URLs to pretend these are loaded
276      *      from the classpath though we can't add/change the files for testing
277      */
278     private static final class TestUrlsClassLoader extends ClassLoader {
279 
280         private final Enumeration<URL> urls;
281 
282         private TestUrlsClassLoader(Enumeration<URL> urls) {
283             this.urls = urls;
284         }
285 
286         @Override
287         public Enumeration<URL> getResources(String name) {
288             return urls;
289         }
290     }
291 
292     /**
293      * Mocked ClassLoader for testing exceptions.
294      *
295      * @noinspection CustomClassloader
296      * @noinspectionreason CustomClassloader - needed to throw an exception to
297      *      test a catch statement
298      */
299     private static final class TestIoExceptionClassLoader extends ClassLoader {
300         @Override
301         public Enumeration<URL> getResources(String name) throws IOException {
302             throw new IOException("test");
303         }
304     }
305 
306 }