View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.puppycrawl.tools.checkstyle.PackageObjectFactory.AMBIGUOUS_MODULE_NAME_EXCEPTION_MESSAGE;
23  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.BASE_PACKAGE;
24  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.CHECK_SUFFIX;
25  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.ModuleLoadOption.TRY_IN_ALL_REGISTERED_PACKAGES;
26  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.NULL_LOADER_MESSAGE;
27  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.NULL_PACKAGE_MESSAGE;
28  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.PACKAGE_SEPARATOR;
29  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.STRING_SEPARATOR;
30  import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE;
31  import static org.junit.Assert.assertEquals;
32  import static org.junit.Assert.assertFalse;
33  import static org.junit.Assert.assertNotNull;
34  import static org.junit.Assert.fail;
35  import static org.mockito.Mockito.mock;
36  import static org.mockito.Mockito.when;
37  
38  import java.io.File;
39  import java.io.IOException;
40  import java.lang.reflect.Field;
41  import java.lang.reflect.Method;
42  import java.net.URLClassLoader;
43  import java.util.Arrays;
44  import java.util.Collection;
45  import java.util.Collections;
46  import java.util.HashSet;
47  import java.util.LinkedHashSet;
48  import java.util.Map;
49  import java.util.Set;
50  
51  import org.junit.Test;
52  
53  import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
54  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
55  import com.puppycrawl.tools.checkstyle.api.FileText;
56  import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
57  import com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationLocationCheck;
58  import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;
59  
60  /**
61   * Enter a description of class PackageObjectFactoryTest.java.
62   *
63   * @author Rick Giles
64   */
65  public class PackageObjectFactoryTest {
66  
67      private final PackageObjectFactory factory = new PackageObjectFactory(
68              BASE_PACKAGE, Thread.currentThread().getContextClassLoader());
69  
70      @Test
71      public void testCtorNullLoaderException1() {
72          try {
73              final Object test = new PackageObjectFactory(new HashSet<>(), null);
74              fail("Exception is expected but got " + test);
75          }
76          catch (IllegalArgumentException ex) {
77              assertEquals("Invalid exception message", NULL_LOADER_MESSAGE, ex.getMessage());
78          }
79      }
80  
81      @Test
82      public void testCtorNullLoaderException2() {
83          try {
84              final Object test = new PackageObjectFactory("test", null);
85              fail("Exception is expected but got " + test);
86          }
87          catch (IllegalArgumentException ex) {
88              assertEquals("Invalid exception message", NULL_LOADER_MESSAGE, ex.getMessage());
89          }
90      }
91  
92      @Test
93      public void testCtorNullPackageException1() {
94          final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
95          try {
96              final Object test = new PackageObjectFactory(Collections.singleton(null), classLoader);
97              fail("Exception is expected but got " + test);
98          }
99          catch (IllegalArgumentException ex) {
100             assertEquals("Invalid exception message", NULL_PACKAGE_MESSAGE, ex.getMessage());
101         }
102     }
103 
104     @Test
105     public void testCtorNullPackageException2() {
106         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
107         try {
108             final Object test = new PackageObjectFactory((String) null, classLoader);
109             fail("Exception is expected but got " + test);
110         }
111         catch (IllegalArgumentException ex) {
112             assertEquals("Invalid exception message", NULL_PACKAGE_MESSAGE, ex.getMessage());
113         }
114     }
115 
116     @Test
117     public void testCtorNullPackageException3() {
118         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
119         try {
120             final Object test = new PackageObjectFactory(Collections.singleton(null), classLoader,
121                     TRY_IN_ALL_REGISTERED_PACKAGES);
122             fail("Exception is expected but got " + test);
123         }
124         catch (IllegalArgumentException ex) {
125             assertEquals("Invalid exception message", NULL_PACKAGE_MESSAGE, ex.getMessage());
126         }
127     }
128 
129     @Test
130     public void testMakeObjectFromName()
131             throws CheckstyleException {
132         final Checker checker =
133             (Checker) factory.createModule(
134                         "com.puppycrawl.tools.checkstyle.Checker");
135         assertNotNull("Checker should not be null when creating module from name", checker);
136     }
137 
138     @Test
139     public void testMakeCheckFromName() {
140         final String name = "com.puppycrawl.tools.checkstyle.checks.naming.ConstantName";
141         try {
142             factory.createModule(name);
143             fail("Exception is expected");
144         }
145         catch (CheckstyleException ex) {
146             final LocalizedMessage exceptionMessage = new LocalizedMessage(0,
147                     Definitions.CHECKSTYLE_BUNDLE, UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE,
148                     new String[] {name, null}, null, factory.getClass(), null);
149             assertEquals("Invalid exception message",
150                     exceptionMessage.getMessage(), ex.getMessage());
151         }
152     }
153 
154     @Test
155     public void testCreateModuleWithNonExistName() {
156         testCreateModuleWithNonExistName("NonExistClassOne");
157         testCreateModuleWithNonExistName("NonExistClassTwo");
158     }
159 
160     private void testCreateModuleWithNonExistName(String name) {
161         try {
162             factory.createModule(name);
163             fail("Exception is expected");
164         }
165         catch (CheckstyleException ex) {
166             final String attemptedNames = BASE_PACKAGE + PACKAGE_SEPARATOR + name + STRING_SEPARATOR
167                     + name + CHECK_SUFFIX + STRING_SEPARATOR
168                     + BASE_PACKAGE + PACKAGE_SEPARATOR + name + CHECK_SUFFIX;
169             final LocalizedMessage exceptionMessage = new LocalizedMessage(0,
170                     Definitions.CHECKSTYLE_BUNDLE, UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE,
171                     new String[] {name, attemptedNames}, null, factory.getClass(), null);
172             assertEquals("Invalid exception message",
173                     exceptionMessage.getMessage(), ex.getMessage());
174         }
175     }
176 
177     @Test
178     public void testCreateObjectFromMap() throws Exception {
179         final String moduleName = "Foo";
180         final String name = moduleName + CHECK_SUFFIX;
181         final String packageName = BASE_PACKAGE + ".packageobjectfactory.bar";
182         final String fullName = packageName + PACKAGE_SEPARATOR + name;
183         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
184         final PackageObjectFactory objectFactory =
185                 new PackageObjectFactory(packageName, classLoader);
186         final Object instance1 = objectFactory.createModule(name);
187         assertEquals("Invalid canonical name", fullName, instance1.getClass().getCanonicalName());
188         final Object instance2 = objectFactory.createModule(moduleName);
189         assertEquals("Invalid canonical name", fullName, instance2.getClass().getCanonicalName());
190     }
191 
192     @Test
193     public void testCreateObjectFromFullModuleNamesWithAmbiguousException() {
194         final String barPackage = BASE_PACKAGE + ".packageobjectfactory.bar";
195         final String fooPackage = BASE_PACKAGE + ".packageobjectfactory.foo";
196         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
197         final PackageObjectFactory objectFactory = new PackageObjectFactory(
198                 new LinkedHashSet<>(Arrays.asList(barPackage, fooPackage)), classLoader);
199         final String name = "FooCheck";
200         try {
201             objectFactory.createModule(name);
202             fail("Exception is expected");
203         }
204         catch (CheckstyleException ex) {
205             final String optionalNames = barPackage + PACKAGE_SEPARATOR + name
206                     + STRING_SEPARATOR + fooPackage + PACKAGE_SEPARATOR + name;
207             final LocalizedMessage exceptionMessage = new LocalizedMessage(0,
208                     Definitions.CHECKSTYLE_BUNDLE, AMBIGUOUS_MODULE_NAME_EXCEPTION_MESSAGE,
209                     new String[] {name, optionalNames}, null, getClass(), null);
210             assertEquals("Invalid exception message",
211                     exceptionMessage.getMessage(), ex.getMessage());
212         }
213     }
214 
215     @Test
216     public void testCreateObjectFromFullModuleNamesWithCantInstantiateException() {
217         final String package1 = BASE_PACKAGE + ".wrong1";
218         final String package2 = BASE_PACKAGE + ".wrong2";
219         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
220         final PackageObjectFactory objectFactory = new PackageObjectFactory(
221                 new LinkedHashSet<>(Arrays.asList(package1, package2)), classLoader);
222         final String name = "FooCheck";
223         final String checkName = name + CHECK_SUFFIX;
224         try {
225             objectFactory.createModule(name);
226             fail("Exception is expected");
227         }
228         catch (CheckstyleException ex) {
229             final String attemptedNames = package1 + PACKAGE_SEPARATOR + name + STRING_SEPARATOR
230                     + package2 + PACKAGE_SEPARATOR + name + STRING_SEPARATOR
231                     + checkName + STRING_SEPARATOR
232                     + package1 + PACKAGE_SEPARATOR + checkName + STRING_SEPARATOR
233                     + package2 + PACKAGE_SEPARATOR + checkName;
234             final LocalizedMessage exceptionMessage = new LocalizedMessage(0,
235                     Definitions.CHECKSTYLE_BUNDLE, UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE,
236                     new String[] {name, attemptedNames}, null, getClass(), null);
237             assertEquals("Invalid exception message",
238                     exceptionMessage.getMessage(), ex.getMessage());
239         }
240     }
241 
242     @Test
243     public void testCreateObjectFromFullModuleNamesWithExceptionByBruteForce() {
244         final String package1 = BASE_PACKAGE + ".wrong1";
245         final String package2 = BASE_PACKAGE + ".wrong2";
246         final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
247         final PackageObjectFactory objectFactory = new PackageObjectFactory(
248                 new LinkedHashSet<>(Arrays.asList(package1, package2)), classLoader,
249                 TRY_IN_ALL_REGISTERED_PACKAGES);
250         final String name = "FooCheck";
251         final String checkName = name + CHECK_SUFFIX;
252         try {
253             objectFactory.createModule(name);
254             fail("Exception is expected");
255         }
256         catch (CheckstyleException ex) {
257             final String attemptedNames = package1 + PACKAGE_SEPARATOR + name + STRING_SEPARATOR
258                     + package2 + PACKAGE_SEPARATOR + name + STRING_SEPARATOR
259                     + checkName + STRING_SEPARATOR
260                     + package1 + PACKAGE_SEPARATOR + checkName + STRING_SEPARATOR
261                     + package2 + PACKAGE_SEPARATOR + checkName;
262             final LocalizedMessage exceptionMessage = new LocalizedMessage(0,
263                     Definitions.CHECKSTYLE_BUNDLE, UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE,
264                     new String[] {name, attemptedNames}, null, getClass(), null);
265             assertEquals("Invalid exception message",
266                     exceptionMessage.getMessage(), ex.getMessage());
267         }
268     }
269 
270     @Test
271     public void testCreateObjectByBruteForce() throws Exception {
272         final String className = "Checker";
273         final Method createModuleByBruteForce = PackageObjectFactory.class.getDeclaredMethod(
274                 "createModuleByTryInEachPackage", String.class);
275         createModuleByBruteForce.setAccessible(true);
276         final Checker checker = (Checker) createModuleByBruteForce.invoke(factory, className);
277         assertNotNull("Checker should not be null when creating module from name", checker);
278     }
279 
280     @Test
281     public void testCreateCheckByBruteForce() throws Exception {
282         final String checkName = "AnnotationLocation";
283         final Method createModuleByBruteForce = PackageObjectFactory.class.getDeclaredMethod(
284                 "createModuleByTryInEachPackage", String.class);
285         final PackageObjectFactory packageObjectFactory = new PackageObjectFactory(
286             new HashSet<>(Arrays.asList(BASE_PACKAGE, BASE_PACKAGE + ".checks.annotation")),
287             Thread.currentThread().getContextClassLoader(), TRY_IN_ALL_REGISTERED_PACKAGES);
288         createModuleByBruteForce.setAccessible(true);
289         final AnnotationLocationCheck check = (AnnotationLocationCheck) createModuleByBruteForce
290                 .invoke(packageObjectFactory, checkName);
291         assertNotNull("Check should not be null when creating module from name", check);
292     }
293 
294     @Test
295     public void testCreateCheckWithPartialPackageNameByBruteForce() throws Exception {
296         final String checkName = "checks.annotation.AnnotationLocation";
297         final PackageObjectFactory packageObjectFactory = new PackageObjectFactory(
298             new HashSet<>(Collections.singletonList(BASE_PACKAGE)),
299             Thread.currentThread().getContextClassLoader(), TRY_IN_ALL_REGISTERED_PACKAGES);
300         final AnnotationLocationCheck check = (AnnotationLocationCheck) packageObjectFactory
301                 .createModule(checkName);
302         assertNotNull("Check should not be null when creating module from name", check);
303     }
304 
305     @Test
306     @SuppressWarnings("unchecked")
307     public void testGenerateThirdPartyNameToFullModuleNameWithException() throws Exception {
308         final URLClassLoader classLoader = mock(URLClassLoader.class);
309         when(classLoader.getURLs()).thenThrow(IOException.class);
310         final Method method = factory.getClass().getDeclaredMethod(
311                 "generateThirdPartyNameToFullModuleName", ClassLoader.class);
312         method.setAccessible(true);
313         final int size = ((Map<String, String>) method.invoke(factory, classLoader)).size();
314         assertEquals("Invalid map size", 0, size);
315     }
316 
317     @Test
318     public void testJoinPackageNamesWithClassName() throws Exception {
319         final Class<PackageObjectFactory> clazz = PackageObjectFactory.class;
320         final Method method =
321             clazz.getDeclaredMethod("joinPackageNamesWithClassName", String.class, Set.class);
322         method.setAccessible(true);
323         final Set<String> packages = Collections.singleton("test");
324         final String className = "SomeClass";
325         final String actual =
326             String.valueOf(method.invoke(PackageObjectFactory.class, className, packages));
327         assertEquals("Invalid class name", "test." + className, actual);
328     }
329 
330     @Test
331     @SuppressWarnings("unchecked")
332     public void testNameToFullModuleNameMap() throws Exception {
333         final Set<Class<?>> classes = CheckUtil.getCheckstyleModules();
334         final Class<PackageObjectFactory> packageObjectFactoryClass = PackageObjectFactory.class;
335         final Field field = packageObjectFactoryClass.getDeclaredField("NAME_TO_FULL_MODULE_NAME");
336         field.setAccessible(true);
337         final Collection<String> canonicalNames = ((Map<String, String>) field.get(null)).values();
338         assertFalse("Invalid canonical name", classes.stream()
339                 .anyMatch(clazz -> !canonicalNames.contains(clazz.getCanonicalName())));
340     }
341 
342     @Test
343     public void testConstructorFailure() {
344         try {
345             factory.createModule(FailConstructorFileSet.class.getName());
346             fail("Exception is expected");
347         }
348         catch (CheckstyleException ex) {
349             assertEquals("Invalid exception message",
350                     "Unable to instantiate com.puppycrawl.tools.checkstyle."
351                     + "PackageObjectFactoryTest$FailConstructorFileSet", ex.getMessage());
352             assertEquals("Invalid exception cause class",
353                     "IllegalArgumentException", ex.getCause().getCause().getClass()
354                     .getSimpleName());
355         }
356     }
357 
358     private static final class FailConstructorFileSet extends AbstractFileSetCheck {
359         private FailConstructorFileSet() {
360             throw new IllegalArgumentException("Test");
361         }
362 
363         @Override
364         protected void processFiltered(File file, FileText fileText) {
365             // not used
366         }
367     }
368 }