1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.reflect;
18
19 import static com.google.common.truth.Truth.assertThat;
20
21 import com.google.common.base.Charsets;
22 import com.google.common.collect.ImmutableMap;
23 import com.google.common.collect.Maps;
24 import com.google.common.collect.Sets;
25 import com.google.common.io.Closer;
26 import com.google.common.io.Resources;
27 import com.google.common.reflect.ClassPath.ClassInfo;
28 import com.google.common.reflect.ClassPath.ResourceInfo;
29 import com.google.common.reflect.subpackage.ClassInSubPackage;
30 import com.google.common.testing.EqualsTester;
31 import com.google.common.testing.NullPointerTester;
32
33 import junit.framework.TestCase;
34
35 import org.junit.Test;
36
37 import java.io.ByteArrayInputStream;
38 import java.io.File;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.net.URI;
43 import java.net.URISyntaxException;
44 import java.net.URL;
45 import java.net.URLClassLoader;
46 import java.util.Map;
47 import java.util.Set;
48 import java.util.jar.Attributes;
49 import java.util.jar.JarFile;
50 import java.util.jar.JarOutputStream;
51 import java.util.jar.Manifest;
52 import java.util.zip.ZipEntry;
53
54
55
56
57 public class ClassPathTest extends TestCase {
58
59 public void testGetResources() throws Exception {
60 Map<String, ResourceInfo> byName = Maps.newHashMap();
61 Map<String, ResourceInfo> byToString = Maps.newHashMap();
62 ClassPath classpath = ClassPath.from(getClass().getClassLoader());
63 for (ResourceInfo resource : classpath.getResources()) {
64 assertThat(resource.getResourceName()).isNotEqualTo(JarFile.MANIFEST_NAME);
65 assertThat(resource.toString()).isNotEqualTo(JarFile.MANIFEST_NAME);
66 byName.put(resource.getResourceName(), resource);
67 byToString.put(resource.toString(), resource);
68 assertNotNull(resource.url());
69 }
70 String testResourceName = "com/google/common/reflect/test.txt";
71 assertThat(byName.keySet()).has().allOf(
72 "com/google/common/reflect/ClassPath.class",
73 "com/google/common/reflect/ClassPathTest.class",
74 "com/google/common/reflect/ClassPathTest$Nested.class",
75 testResourceName);
76 assertThat(byToString.keySet()).has().allOf(
77 "com.google.common.reflect.ClassPath",
78 "com.google.common.reflect.ClassPathTest",
79 "com.google.common.reflect.ClassPathTest$Nested",
80 testResourceName);
81 assertEquals(getClass().getClassLoader().getResource(testResourceName),
82 byName.get("com/google/common/reflect/test.txt").url());
83 }
84
85 public void testGetAllClasses() throws Exception {
86 Set<String> names = Sets.newHashSet();
87 Set<String> strings = Sets.newHashSet();
88 Set<Class<?>> classes = Sets.newHashSet();
89 Set<String> packageNames = Sets.newHashSet();
90 Set<String> simpleNames = Sets.newHashSet();
91 ClassPath classpath = ClassPath.from(getClass().getClassLoader());
92 for (ClassInfo classInfo : classpath.getAllClasses()) {
93 if (!classInfo.getPackageName().equals(ClassPathTest.class.getPackage().getName())) {
94 continue;
95 }
96 names.add(classInfo.getName());
97 strings.add(classInfo.toString());
98 classes.add(classInfo.load());
99 packageNames.add(classInfo.getPackageName());
100 simpleNames.add(classInfo.getSimpleName());
101 }
102 class LocalClass {}
103 Class<?> anonymousClass = new Object() {}.getClass();
104 assertThat(names).has().allOf(anonymousClass.getName(), LocalClass.class.getName(),
105 ClassPath.class.getName(), ClassPathTest.class.getName());
106 assertThat(strings).has().allOf(anonymousClass.getName(), LocalClass.class.getName(),
107 ClassPath.class.getName(), ClassPathTest.class.getName());
108 assertThat(classes).has().allOf(anonymousClass, LocalClass.class, ClassPath.class,
109 ClassPathTest.class);
110 assertThat(packageNames).has().exactly(ClassPath.class.getPackage().getName());
111 assertThat(simpleNames).has().allOf("", "Local", "ClassPath", "ClassPathTest");
112 }
113
114 public void testGetTopLevelClasses() throws Exception {
115 Set<String> names = Sets.newHashSet();
116 Set<String> strings = Sets.newHashSet();
117 Set<Class<?>> classes = Sets.newHashSet();
118 Set<String> packageNames = Sets.newHashSet();
119 Set<String> simpleNames = Sets.newHashSet();
120 ClassPath classpath = ClassPath.from(getClass().getClassLoader());
121 for (ClassInfo classInfo
122 : classpath.getTopLevelClasses(ClassPathTest.class.getPackage().getName())) {
123 names.add(classInfo.getName());
124 strings.add(classInfo.toString());
125 classes.add(classInfo.load());
126 packageNames.add(classInfo.getPackageName());
127 simpleNames.add(classInfo.getSimpleName());
128 }
129 assertThat(names).has().allOf(ClassPath.class.getName(), ClassPathTest.class.getName());
130 assertThat(strings).has().allOf(ClassPath.class.getName(), ClassPathTest.class.getName());
131 assertThat(classes).has().allOf(ClassPath.class, ClassPathTest.class);
132 assertThat(packageNames).has().item(ClassPath.class.getPackage().getName());
133 assertThat(simpleNames).has().allOf("ClassPath", "ClassPathTest");
134 assertFalse(classes.contains(ClassInSubPackage.class));
135 }
136
137 public void testGetTopLevelClassesRecursive() throws Exception {
138 Set<Class<?>> classes = Sets.newHashSet();
139 ClassPath classpath = ClassPath.from(ClassPathTest.class.getClassLoader());
140 for (ClassInfo classInfo
141 : classpath.getTopLevelClassesRecursive(ClassPathTest.class.getPackage().getName())) {
142 if (classInfo.getName().contains("ClassPathTest")) {
143 System.err.println("");
144 }
145 classes.add(classInfo.load());
146 }
147 assertThat(classes).has().allOf(ClassPathTest.class, ClassInSubPackage.class);
148 }
149
150 public void testGetTopLevelClasses_diamond() throws Exception {
151 ClassLoader parent = ClassPathTest.class.getClassLoader();
152 ClassLoader sub1 = new ClassLoader(parent) {};
153 ClassLoader sub2 = new ClassLoader(parent) {};
154 assertEquals(findClass(ClassPath.from(sub1).getTopLevelClasses(), ClassPathTest.class),
155 findClass(ClassPath.from(sub2).getTopLevelClasses(), ClassPathTest.class));
156 }
157
158 public void testEquals() {
159 new EqualsTester()
160 .addEqualityGroup(classInfo(ClassPathTest.class), classInfo(ClassPathTest.class))
161 .addEqualityGroup(classInfo(Test.class), classInfo(Test.class, getClass().getClassLoader()))
162 .addEqualityGroup(
163 new ResourceInfo("a/b/c.txt", getClass().getClassLoader()),
164 new ResourceInfo("a/b/c.txt", getClass().getClassLoader()))
165 .addEqualityGroup(
166 new ResourceInfo("x.txt", getClass().getClassLoader()))
167 .testEquals();
168 }
169
170 public void testClassPathEntries_emptyURLClassLoader_noParent() {
171 assertThat(ClassPath.getClassPathEntries(new URLClassLoader(new URL[0], null)).keySet())
172 .isEmpty();
173 }
174
175 public void testClassPathEntries_URLClassLoader_noParent() throws Exception {
176 URL url1 = new URL("file:/a");
177 URL url2 = new URL("file:/b");
178 URLClassLoader classloader = new URLClassLoader(new URL[] {url1, url2}, null);
179 assertEquals(
180 ImmutableMap.of(url1.toURI(), classloader, url2.toURI(), classloader),
181 ClassPath.getClassPathEntries(classloader));
182 }
183
184 public void testClassPathEntries_URLClassLoader_withParent() throws Exception {
185 URL url1 = new URL("file:/a");
186 URL url2 = new URL("file:/b");
187 URLClassLoader parent = new URLClassLoader(new URL[] {url1}, null);
188 URLClassLoader child = new URLClassLoader(new URL[] {url2}, parent) {};
189 ImmutableMap<URI, ClassLoader> classPathEntries = ClassPath.getClassPathEntries(child);
190 assertEquals(ImmutableMap.of(url1.toURI(), parent, url2.toURI(), child), classPathEntries);
191 assertThat(classPathEntries.keySet()).has().exactly(url1.toURI(), url2.toURI()).inOrder();
192 }
193
194 public void testClassPathEntries_duplicateUri_parentWins() throws Exception {
195 URL url = new URL("file:/a");
196 URLClassLoader parent = new URLClassLoader(new URL[] {url}, null);
197 URLClassLoader child = new URLClassLoader(new URL[] {url}, parent) {};
198 assertEquals(ImmutableMap.of(url.toURI(), parent), ClassPath.getClassPathEntries(child));
199 }
200
201 public void testClassPathEntries_notURLClassLoader_noParent() {
202 assertThat(ClassPath.getClassPathEntries(new ClassLoader(null) {}).keySet()).isEmpty();
203 }
204
205 public void testClassPathEntries_notURLClassLoader_withParent() throws Exception {
206 URL url = new URL("file:/a");
207 URLClassLoader parent = new URLClassLoader(new URL[] {url}, null);
208 assertEquals(
209 ImmutableMap.of(url.toURI(), parent),
210 ClassPath.getClassPathEntries(new ClassLoader(parent) {}));
211 }
212
213 public void testClassPathEntries_notURLClassLoader_withParentAndGrandParent() throws Exception {
214 URL url1 = new URL("file:/a");
215 URL url2 = new URL("file:/b");
216 URLClassLoader grandParent = new URLClassLoader(new URL[] {url1}, null);
217 URLClassLoader parent = new URLClassLoader(new URL[] {url2}, grandParent);
218 assertEquals(
219 ImmutableMap.of(url1.toURI(), grandParent, url2.toURI(), parent),
220 ClassPath.getClassPathEntries(new ClassLoader(parent) {}));
221 }
222
223 public void testClassPathEntries_notURLClassLoader_withGrandParent() throws Exception {
224 URL url = new URL("file:/a");
225 URLClassLoader grandParent = new URLClassLoader(new URL[] {url}, null);
226 ClassLoader parent = new ClassLoader(grandParent) {};
227 assertEquals(
228 ImmutableMap.of(url.toURI(), grandParent),
229 ClassPath.getClassPathEntries(new ClassLoader(parent) {}));
230 }
231
232 public void testScan_classPathCycle() throws IOException {
233 File jarFile = File.createTempFile("with_circular_class_path", ".jar");
234 try {
235 writeSelfReferencingJarFile(jarFile, "test.txt");
236 ClassPath.Scanner scanner = new ClassPath.Scanner();
237 scanner.scan(jarFile.toURI(), ClassPathTest.class.getClassLoader());
238 assertEquals(1, scanner.getResources().size());
239 } finally {
240 jarFile.delete();
241 }
242 }
243
244 public void testScanFromFile_fileNotExists() throws IOException {
245 ClassLoader classLoader = ClassPathTest.class.getClassLoader();
246 ClassPath.Scanner scanner = new ClassPath.Scanner();
247 scanner.scanFrom(new File("no/such/file/anywhere"), classLoader);
248 assertThat(scanner.getResources()).isEmpty();
249 }
250
251 public void testScanFromFile_notJarFile() throws IOException {
252 ClassLoader classLoader = ClassPathTest.class.getClassLoader();
253 File notJar = File.createTempFile("not_a_jar", "txt");
254 ClassPath.Scanner scanner = new ClassPath.Scanner();
255 try {
256 scanner.scanFrom(notJar, classLoader);
257 } finally {
258 notJar.delete();
259 }
260 assertThat(scanner.getResources()).isEmpty();
261 }
262
263 public void testGetClassPathEntry() throws URISyntaxException {
264 assertEquals(URI.create("file:/usr/test/dep.jar"),
265 ClassPath.Scanner.getClassPathEntry(
266 new File("/home/build/outer.jar"), "file:/usr/test/dep.jar"));
267 assertEquals(URI.create("file:/home/build/a.jar"),
268 ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "a.jar"));
269 assertEquals(URI.create("file:/home/build/x/y/z"),
270 ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z"));
271 assertEquals(URI.create("file:/home/build/x/y/z.jar"),
272 ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z.jar"));
273 }
274
275 public void testGetClassPathFromManifest_nullManifest() {
276 assertThat(ClassPath.Scanner.getClassPathFromManifest(new File("some.jar"), null)).isEmpty();
277 }
278
279 public void testGetClassPathFromManifest_noClassPath() throws IOException {
280 File jarFile = new File("base.jar");
281 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest("")))
282 .isEmpty();
283 }
284
285 public void testGetClassPathFromManifest_emptyClassPath() throws IOException {
286 File jarFile = new File("base.jar");
287 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifestClasspath("")))
288 .isEmpty();
289 }
290
291 public void testGetClassPathFromManifest_badClassPath() throws IOException {
292 File jarFile = new File("base.jar");
293 Manifest manifest = manifestClasspath("an_invalid^path");
294 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
295 .isEmpty();
296 }
297
298 public void testGetClassPathFromManifest_relativeDirectory() throws IOException {
299 File jarFile = new File("base/some.jar");
300
301 Manifest manifest = manifestClasspath("with/relative/dir");
302 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
303 .has().exactly(new File("base/with/relative/dir").toURI()).inOrder();
304 }
305
306 public void testGetClassPathFromManifest_relativeJar() throws IOException {
307 File jarFile = new File("base/some.jar");
308
309 Manifest manifest = manifestClasspath("with/relative.jar");
310 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
311 .has().exactly(new File("base/with/relative.jar").toURI()).inOrder();
312 }
313
314 public void testGetClassPathFromManifest_jarInCurrentDirectory() throws IOException {
315 File jarFile = new File("base/some.jar");
316
317 Manifest manifest = manifestClasspath("current.jar");
318 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
319 .has().exactly(new File("base/current.jar").toURI()).inOrder();
320 }
321
322 public void testGetClassPathFromManifest_absoluteDirectory() throws IOException {
323 File jarFile = new File("base/some.jar");
324 Manifest manifest = manifestClasspath("file:/with/absolute/dir");
325 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
326 .has().exactly(new File("/with/absolute/dir").toURI()).inOrder();
327 }
328
329 public void testGetClassPathFromManifest_absoluteJar() throws IOException {
330 File jarFile = new File("base/some.jar");
331 Manifest manifest = manifestClasspath("file:/with/absolute.jar");
332 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
333 .has().exactly(new File("/with/absolute.jar").toURI()).inOrder();
334 }
335
336 public void testGetClassPathFromManifest_multiplePaths() throws IOException {
337 File jarFile = new File("base/some.jar");
338 Manifest manifest = manifestClasspath("file:/with/absolute.jar relative.jar relative/dir");
339 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
340 .has().exactly(
341 new File("/with/absolute.jar").toURI(),
342 new File("base/relative.jar").toURI(),
343 new File("base/relative/dir").toURI())
344 .inOrder();
345 }
346
347 public void testGetClassPathFromManifest_leadingBlanks() throws IOException {
348 File jarFile = new File("base/some.jar");
349 Manifest manifest = manifestClasspath(" relative.jar");
350 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
351 .has().exactly(new File("base/relative.jar").toURI()).inOrder();
352 }
353
354 public void testGetClassPathFromManifest_trailingBlanks() throws IOException {
355 File jarFile = new File("base/some.jar");
356 Manifest manifest = manifestClasspath("relative.jar ");
357 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
358 .has().exactly(new File("base/relative.jar").toURI()).inOrder();
359 }
360
361 public void testGetClassName() {
362 assertEquals("abc.d.Abc", ClassPath.getClassName("abc/d/Abc.class"));
363 }
364
365 public void testResourceInfo_of() {
366 assertEquals(ClassInfo.class, resourceInfo(ClassPathTest.class).getClass());
367 assertEquals(ClassInfo.class, resourceInfo(ClassPath.class).getClass());
368 assertEquals(ClassInfo.class, resourceInfo(Nested.class).getClass());
369 }
370
371 public void testGetSimpleName() {
372 assertEquals("Foo",
373 new ClassInfo("Foo.class", getClass().getClassLoader()).getSimpleName());
374 assertEquals("Foo",
375 new ClassInfo("a/b/Foo.class", getClass().getClassLoader()).getSimpleName());
376 assertEquals("Foo",
377 new ClassInfo("a/b/Bar$Foo.class", getClass().getClassLoader()).getSimpleName());
378 assertEquals("",
379 new ClassInfo("a/b/Bar$1.class", getClass().getClassLoader()).getSimpleName());
380 assertEquals("Foo",
381 new ClassInfo("a/b/Bar$Foo.class", getClass().getClassLoader()).getSimpleName());
382 assertEquals("",
383 new ClassInfo("a/b/Bar$1.class", getClass().getClassLoader()).getSimpleName());
384 assertEquals("Local",
385 new ClassInfo("a/b/Bar$1Local.class", getClass().getClassLoader()).getSimpleName());
386
387 }
388
389 public void testGetPackageName() {
390 assertEquals("",
391 new ClassInfo("Foo.class", getClass().getClassLoader()).getPackageName());
392 assertEquals("a.b",
393 new ClassInfo("a/b/Foo.class", getClass().getClassLoader()).getPackageName());
394 }
395
396 private static class Nested {}
397
398 public void testNulls() throws IOException {
399 new NullPointerTester().testAllPublicStaticMethods(ClassPath.class);
400 new NullPointerTester()
401 .testAllPublicInstanceMethods(ClassPath.from(getClass().getClassLoader()));
402 }
403
404 private static ClassPath.ClassInfo findClass(
405 Iterable<ClassPath.ClassInfo> classes, Class<?> cls) {
406 for (ClassPath.ClassInfo classInfo : classes) {
407 if (classInfo.getName().equals(cls.getName())) {
408 return classInfo;
409 }
410 }
411 throw new AssertionError("failed to find " + cls);
412 }
413
414 private static ResourceInfo resourceInfo(Class<?> cls) {
415 return ResourceInfo.of(cls.getName().replace('.', '/') + ".class", cls.getClassLoader());
416 }
417
418 private static ClassInfo classInfo(Class<?> cls) {
419 return classInfo(cls, cls.getClassLoader());
420 }
421
422 private static ClassInfo classInfo(Class<?> cls, ClassLoader classLoader) {
423 return new ClassInfo(cls.getName().replace('.', '/') + ".class", classLoader);
424 }
425
426 private static Manifest manifestClasspath(String classpath) throws IOException {
427 return manifest("Class-Path: " + classpath + "\n");
428 }
429
430 private static void writeSelfReferencingJarFile(File jarFile, String... entries)
431 throws IOException {
432 Manifest manifest = new Manifest();
433
434 manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
435 manifest.getMainAttributes().put(Attributes.Name.CLASS_PATH, jarFile.getName());
436
437 Closer closer = Closer.create();
438 try {
439 FileOutputStream fileOut = closer.register(new FileOutputStream(jarFile));
440 JarOutputStream jarOut = closer.register(new JarOutputStream(fileOut));
441 for (String entry : entries) {
442 jarOut.putNextEntry(new ZipEntry(entry));
443 Resources.copy(ClassPathTest.class.getResource(entry), jarOut);
444 jarOut.closeEntry();
445 }
446 } catch (Throwable e) {
447 throw closer.rethrow(e);
448 } finally {
449 closer.close();
450 }
451 }
452
453 private static Manifest manifest(String content) throws IOException {
454 InputStream in = new ByteArrayInputStream(content.getBytes(Charsets.US_ASCII));
455 Manifest manifest = new Manifest();
456 manifest.read(in);
457 return manifest;
458 }
459 }