• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.common.reflect;
17 
18 import static com.google.common.base.Charsets.US_ASCII;
19 import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH;
20 import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR;
21 import static com.google.common.io.MoreFiles.deleteRecursively;
22 import static com.google.common.truth.Truth.assertThat;
23 import static java.nio.file.Files.createDirectory;
24 import static java.nio.file.Files.createFile;
25 import static java.nio.file.Files.createSymbolicLink;
26 import static java.nio.file.Files.createTempDirectory;
27 import static java.util.logging.Level.WARNING;
28 
29 import com.google.common.base.Joiner;
30 import com.google.common.collect.ImmutableList;
31 import com.google.common.collect.ImmutableSet;
32 import com.google.common.io.Closer;
33 import com.google.common.io.Files;
34 import com.google.common.io.Resources;
35 import com.google.common.reflect.ClassPath.ClassInfo;
36 import com.google.common.reflect.ClassPath.ResourceInfo;
37 import com.google.common.testing.EqualsTester;
38 import com.google.common.testing.NullPointerTester;
39 import java.io.ByteArrayInputStream;
40 import java.io.File;
41 import java.io.FileOutputStream;
42 import java.io.FilePermission;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.net.MalformedURLException;
46 import java.net.URISyntaxException;
47 import java.net.URL;
48 import java.net.URLClassLoader;
49 import java.security.Permission;
50 import java.security.PermissionCollection;
51 import java.util.jar.Attributes;
52 import java.util.jar.JarOutputStream;
53 import java.util.jar.Manifest;
54 import java.util.logging.Logger;
55 import java.util.zip.ZipEntry;
56 import junit.framework.TestCase;
57 import org.junit.Test;
58 
59 /** Functional tests of {@link ClassPath}. */
60 public class ClassPathTest extends TestCase {
61   private static final Logger log = Logger.getLogger(ClassPathTest.class.getName());
62   private static final File FILE = new File(".");
63 
testEquals()64   public void testEquals() {
65     new EqualsTester()
66         .addEqualityGroup(classInfo(ClassPathTest.class), classInfo(ClassPathTest.class))
67         .addEqualityGroup(classInfo(Test.class), classInfo(Test.class, getClass().getClassLoader()))
68         .addEqualityGroup(
69             new ResourceInfo(FILE, "a/b/c.txt", getClass().getClassLoader()),
70             new ResourceInfo(FILE, "a/b/c.txt", getClass().getClassLoader()))
71         .addEqualityGroup(new ResourceInfo(FILE, "x.txt", getClass().getClassLoader()))
72         .testEquals();
73   }
74 
75   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_emptyURLClassLoader_noParent()76   public void testClassPathEntries_emptyURLClassLoader_noParent() {
77     assertThat(ClassPath.getClassPathEntries(new URLClassLoader(new URL[0], null)).keySet())
78         .isEmpty();
79   }
80 
81   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_URLClassLoader_noParent()82   public void testClassPathEntries_URLClassLoader_noParent() throws Exception {
83     URL url1 = new URL("file:/a");
84     URL url2 = new URL("file:/b");
85     URLClassLoader classloader = new URLClassLoader(new URL[] {url1, url2}, null);
86     assertThat(ClassPath.getClassPathEntries(classloader))
87         .containsExactly(new File("/a"), classloader, new File("/b"), classloader);
88   }
89 
90   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_URLClassLoader_withParent()91   public void testClassPathEntries_URLClassLoader_withParent() throws Exception {
92     URL url1 = new URL("file:/a");
93     URL url2 = new URL("file:/b");
94     URLClassLoader parent = new URLClassLoader(new URL[] {url1}, null);
95     URLClassLoader child = new URLClassLoader(new URL[] {url2}, parent) {};
96     assertThat(ClassPath.getClassPathEntries(child))
97         .containsExactly(new File("/a"), parent, new File("/b"), child)
98         .inOrder();
99   }
100 
101   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_duplicateUri_parentWins()102   public void testClassPathEntries_duplicateUri_parentWins() throws Exception {
103     URL url = new URL("file:/a");
104     URLClassLoader parent = new URLClassLoader(new URL[] {url}, null);
105     URLClassLoader child = new URLClassLoader(new URL[] {url}, parent) {};
106     assertThat(ClassPath.getClassPathEntries(child)).containsExactly(new File("/a"), parent);
107   }
108 
109   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_notURLClassLoader_noParent()110   public void testClassPathEntries_notURLClassLoader_noParent() {
111     assertThat(ClassPath.getClassPathEntries(new ClassLoader(null) {})).isEmpty();
112   }
113 
114   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_notURLClassLoader_withParent()115   public void testClassPathEntries_notURLClassLoader_withParent() throws Exception {
116     URL url = new URL("file:/a");
117     URLClassLoader parent = new URLClassLoader(new URL[] {url}, null);
118     assertThat(ClassPath.getClassPathEntries(new ClassLoader(parent) {}))
119         .containsExactly(new File("/a"), parent);
120   }
121 
122   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_notURLClassLoader_withParentAndGrandParent()123   public void testClassPathEntries_notURLClassLoader_withParentAndGrandParent() throws Exception {
124     URL url1 = new URL("file:/a");
125     URL url2 = new URL("file:/b");
126     URLClassLoader grandParent = new URLClassLoader(new URL[] {url1}, null);
127     URLClassLoader parent = new URLClassLoader(new URL[] {url2}, grandParent);
128     assertThat(ClassPath.getClassPathEntries(new ClassLoader(parent) {}))
129         .containsExactly(new File("/a"), grandParent, new File("/b"), parent);
130   }
131 
132   @AndroidIncompatible // Android forbids null parent ClassLoader
testClassPathEntries_notURLClassLoader_withGrandParent()133   public void testClassPathEntries_notURLClassLoader_withGrandParent() throws Exception {
134     URL url = new URL("file:/a");
135     URLClassLoader grandParent = new URLClassLoader(new URL[] {url}, null);
136     ClassLoader parent = new ClassLoader(grandParent) {};
137     assertThat(ClassPath.getClassPathEntries(new ClassLoader(parent) {}))
138         .containsExactly(new File("/a"), grandParent);
139   }
140 
141   @AndroidIncompatible // Android forbids null parent ClassLoader
142   // https://github.com/google/guava/issues/2152
testClassPathEntries_URLClassLoader_pathWithSpace()143   public void testClassPathEntries_URLClassLoader_pathWithSpace() throws Exception {
144     URL url = new URL("file:///c:/Documents and Settings/");
145     URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null);
146     assertThat(ClassPath.getClassPathEntries(classloader))
147         .containsExactly(new File("/c:/Documents and Settings/"), classloader);
148   }
149 
150   @AndroidIncompatible // Android forbids null parent ClassLoader
151   // https://github.com/google/guava/issues/2152
testClassPathEntries_URLClassLoader_pathWithEscapedSpace()152   public void testClassPathEntries_URLClassLoader_pathWithEscapedSpace() throws Exception {
153     URL url = new URL("file:///c:/Documents%20and%20Settings/");
154     URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null);
155     assertThat(ClassPath.getClassPathEntries(classloader))
156         .containsExactly(new File("/c:/Documents and Settings/"), classloader);
157   }
158 
159   // https://github.com/google/guava/issues/2152
testToFile()160   public void testToFile() throws Exception {
161     assertThat(ClassPath.toFile(new URL("file:///c:/Documents%20and%20Settings/")))
162         .isEqualTo(new File("/c:/Documents and Settings/"));
163     assertThat(ClassPath.toFile(new URL("file:///c:/Documents ~ Settings, or not/11-12 12:05")))
164         .isEqualTo(new File("/c:/Documents ~ Settings, or not/11-12 12:05"));
165   }
166 
167   // https://github.com/google/guava/issues/2152
168   @AndroidIncompatible // works in newer Android versions but fails at the version we test with
testToFile_AndroidIncompatible()169   public void testToFile_AndroidIncompatible() throws Exception {
170     assertThat(ClassPath.toFile(new URL("file:///c:\\Documents ~ Settings, or not\\11-12 12:05")))
171         .isEqualTo(new File("/c:\\Documents ~ Settings, or not\\11-12 12:05"));
172     assertThat(ClassPath.toFile(new URL("file:///C:\\Program Files\\Apache Software Foundation")))
173         .isEqualTo(new File("/C:\\Program Files\\Apache Software Foundation/"));
174     assertThat(ClassPath.toFile(new URL("file:///C:\\\u20320 \u22909"))) // Chinese Ni Hao
175         .isEqualTo(new File("/C:\\\u20320 \u22909"));
176   }
177 
178 
179   @AndroidIncompatible // Android forbids null parent ClassLoader
180   // https://github.com/google/guava/issues/2152
testJarFileWithSpaces()181   public void testJarFileWithSpaces() throws Exception {
182     URL url = makeJarUrlWithName("To test unescaped spaces in jar file name.jar");
183     URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null);
184     assertThat(ClassPath.from(classloader).getTopLevelClasses()).isNotEmpty();
185   }
186 
187 
testScan_classPathCycle()188   public void testScan_classPathCycle() throws IOException {
189     File jarFile = File.createTempFile("with_circular_class_path", ".jar");
190     try {
191       writeSelfReferencingJarFile(jarFile, "test.txt");
192       assertThat(
193               new ClassPath.LocationInfo(jarFile, ClassPathTest.class.getClassLoader())
194                   .scanResources())
195           .hasSize(1);
196     } finally {
197       jarFile.delete();
198     }
199   }
200 
201   @AndroidIncompatible // Path (for symlink creation)
202 
testScanDirectory_symlinkCycle()203   public void testScanDirectory_symlinkCycle() throws IOException {
204     ClassLoader loader = ClassPathTest.class.getClassLoader();
205     // directory with a cycle,
206     // /root
207     //    /left
208     //       /[sibling -> right]
209     //    /right
210     //       /[sibling -> left]
211     java.nio.file.Path root = createTempDirectory("ClassPathTest");
212     try {
213       java.nio.file.Path left = createDirectory(root.resolve("left"));
214       createFile(left.resolve("some.txt"));
215 
216       java.nio.file.Path right = createDirectory(root.resolve("right"));
217       createFile(right.resolve("another.txt"));
218 
219       createSymbolicLink(left.resolve("sibling"), right);
220       createSymbolicLink(right.resolve("sibling"), left);
221       assertEquals(
222           ImmutableSet.of(
223               new ResourceInfo(FILE, "left/some.txt", loader),
224               new ResourceInfo(FILE, "left/sibling/another.txt", loader),
225               new ResourceInfo(FILE, "right/another.txt", loader),
226               new ResourceInfo(FILE, "right/sibling/some.txt", loader)),
227           new ClassPath.LocationInfo(root.toFile(), loader).scanResources());
228     } finally {
229       deleteRecursivelyOrLog(root);
230     }
231   }
232 
233   @AndroidIncompatible // Path (for symlink creation)
234 
testScanDirectory_symlinkToRootCycle()235   public void testScanDirectory_symlinkToRootCycle() throws IOException {
236     ClassLoader loader = ClassPathTest.class.getClassLoader();
237     // directory with a cycle,
238     // /root
239     //    /child
240     //       /[grandchild -> root]
241     java.nio.file.Path root = createTempDirectory("ClassPathTest");
242     try {
243       createFile(root.resolve("some.txt"));
244       java.nio.file.Path child = createDirectory(root.resolve("child"));
245       createSymbolicLink(child.resolve("grandchild"), root);
246       assertEquals(
247           ImmutableSet.of(new ResourceInfo(FILE, "some.txt", loader)),
248           new ClassPath.LocationInfo(root.toFile(), loader).scanResources());
249     } finally {
250       deleteRecursivelyOrLog(root);
251     }
252   }
253 
254 
testScanFromFile_fileNotExists()255   public void testScanFromFile_fileNotExists() throws IOException {
256     ClassLoader classLoader = ClassPathTest.class.getClassLoader();
257     assertThat(
258             new ClassPath.LocationInfo(new File("no/such/file/anywhere"), classLoader)
259                 .scanResources())
260         .isEmpty();
261   }
262 
263 
testScanFromFile_notJarFile()264   public void testScanFromFile_notJarFile() throws IOException {
265     ClassLoader classLoader = ClassPathTest.class.getClassLoader();
266     File notJar = File.createTempFile("not_a_jar", "txt");
267     try {
268       assertThat(new ClassPath.LocationInfo(notJar, classLoader).scanResources()).isEmpty();
269     } finally {
270       notJar.delete();
271     }
272   }
273 
testGetClassPathEntry()274   public void testGetClassPathEntry() throws MalformedURLException, URISyntaxException {
275     assertEquals(
276         new File("/usr/test/dep.jar").toURI(),
277         ClassPath.getClassPathEntry(new File("/home/build/outer.jar"), "file:/usr/test/dep.jar")
278             .toURI());
279     assertEquals(
280         new File("/home/build/a.jar").toURI(),
281         ClassPath.getClassPathEntry(new File("/home/build/outer.jar"), "a.jar").toURI());
282     assertEquals(
283         new File("/home/build/x/y/z").toURI(),
284         ClassPath.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z").toURI());
285     assertEquals(
286         new File("/home/build/x/y/z.jar").toURI(),
287         ClassPath.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z.jar").toURI());
288     assertEquals(
289         "/home/build/x y.jar",
290         ClassPath.getClassPathEntry(new File("/home/build/outer.jar"), "x y.jar").getFile());
291   }
292 
testGetClassPathFromManifest_nullManifest()293   public void testGetClassPathFromManifest_nullManifest() {
294     assertThat(ClassPath.getClassPathFromManifest(new File("some.jar"), null)).isEmpty();
295   }
296 
testGetClassPathFromManifest_noClassPath()297   public void testGetClassPathFromManifest_noClassPath() throws IOException {
298     File jarFile = new File("base.jar");
299     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest(""))).isEmpty();
300   }
301 
testGetClassPathFromManifest_emptyClassPath()302   public void testGetClassPathFromManifest_emptyClassPath() throws IOException {
303     File jarFile = new File("base.jar");
304     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifestClasspath(""))).isEmpty();
305   }
306 
testGetClassPathFromManifest_badClassPath()307   public void testGetClassPathFromManifest_badClassPath() throws IOException {
308     File jarFile = new File("base.jar");
309     Manifest manifest = manifestClasspath("nosuchscheme:an_invalid^path");
310     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest)).isEmpty();
311   }
312 
testGetClassPathFromManifest_pathWithStrangeCharacter()313   public void testGetClassPathFromManifest_pathWithStrangeCharacter() throws IOException {
314     File jarFile = new File("base/some.jar");
315     Manifest manifest = manifestClasspath("file:the^file.jar");
316     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
317         .containsExactly(fullpath("base/the^file.jar"));
318   }
319 
testGetClassPathFromManifest_relativeDirectory()320   public void testGetClassPathFromManifest_relativeDirectory() throws IOException {
321     File jarFile = new File("base/some.jar");
322     // with/relative/directory is the Class-Path value in the mf file.
323     Manifest manifest = manifestClasspath("with/relative/dir");
324     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
325         .containsExactly(fullpath("base/with/relative/dir"));
326   }
327 
testGetClassPathFromManifest_relativeJar()328   public void testGetClassPathFromManifest_relativeJar() throws IOException {
329     File jarFile = new File("base/some.jar");
330     // with/relative/directory is the Class-Path value in the mf file.
331     Manifest manifest = manifestClasspath("with/relative.jar");
332     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
333         .containsExactly(fullpath("base/with/relative.jar"));
334   }
335 
testGetClassPathFromManifest_jarInCurrentDirectory()336   public void testGetClassPathFromManifest_jarInCurrentDirectory() throws IOException {
337     File jarFile = new File("base/some.jar");
338     // with/relative/directory is the Class-Path value in the mf file.
339     Manifest manifest = manifestClasspath("current.jar");
340     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
341         .containsExactly(fullpath("base/current.jar"));
342   }
343 
testGetClassPathFromManifest_absoluteDirectory()344   public void testGetClassPathFromManifest_absoluteDirectory() throws IOException {
345     File jarFile = new File("base/some.jar");
346     Manifest manifest = manifestClasspath("file:/with/absolute/dir");
347     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
348         .containsExactly(fullpath("/with/absolute/dir"));
349   }
350 
testGetClassPathFromManifest_absoluteJar()351   public void testGetClassPathFromManifest_absoluteJar() throws IOException {
352     File jarFile = new File("base/some.jar");
353     Manifest manifest = manifestClasspath("file:/with/absolute.jar");
354     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
355         .containsExactly(fullpath("/with/absolute.jar"));
356   }
357 
testGetClassPathFromManifest_multiplePaths()358   public void testGetClassPathFromManifest_multiplePaths() throws IOException {
359     File jarFile = new File("base/some.jar");
360     Manifest manifest = manifestClasspath("file:/with/absolute.jar relative.jar  relative/dir");
361     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
362         .containsExactly(
363             fullpath("/with/absolute.jar"),
364             fullpath("base/relative.jar"),
365             fullpath("base/relative/dir"))
366         .inOrder();
367   }
368 
testGetClassPathFromManifest_leadingBlanks()369   public void testGetClassPathFromManifest_leadingBlanks() throws IOException {
370     File jarFile = new File("base/some.jar");
371     Manifest manifest = manifestClasspath(" relative.jar");
372     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
373         .containsExactly(fullpath("base/relative.jar"));
374   }
375 
testGetClassPathFromManifest_trailingBlanks()376   public void testGetClassPathFromManifest_trailingBlanks() throws IOException {
377     File jarFile = new File("base/some.jar");
378     Manifest manifest = manifestClasspath("relative.jar ");
379     assertThat(ClassPath.getClassPathFromManifest(jarFile, manifest))
380         .containsExactly(fullpath("base/relative.jar"));
381   }
382 
testGetClassName()383   public void testGetClassName() {
384     assertEquals("abc.d.Abc", ClassPath.getClassName("abc/d/Abc.class"));
385   }
386 
testResourceInfo_of()387   public void testResourceInfo_of() {
388     assertEquals(ClassInfo.class, resourceInfo(ClassPathTest.class).getClass());
389     assertEquals(ClassInfo.class, resourceInfo(ClassPath.class).getClass());
390     assertEquals(ClassInfo.class, resourceInfo(Nested.class).getClass());
391   }
392 
testGetSimpleName()393   public void testGetSimpleName() {
394     ClassLoader classLoader = getClass().getClassLoader();
395     assertEquals("Foo", new ClassInfo(FILE, "Foo.class", classLoader).getSimpleName());
396     assertEquals("Foo", new ClassInfo(FILE, "a/b/Foo.class", classLoader).getSimpleName());
397     assertEquals("Foo", new ClassInfo(FILE, "a/b/Bar$Foo.class", classLoader).getSimpleName());
398     assertEquals("", new ClassInfo(FILE, "a/b/Bar$1.class", classLoader).getSimpleName());
399     assertEquals("Foo", new ClassInfo(FILE, "a/b/Bar$Foo.class", classLoader).getSimpleName());
400     assertEquals("", new ClassInfo(FILE, "a/b/Bar$1.class", classLoader).getSimpleName());
401     assertEquals("Local", new ClassInfo(FILE, "a/b/Bar$1Local.class", classLoader).getSimpleName());
402   }
403 
testGetPackageName()404   public void testGetPackageName() {
405     assertEquals(
406         "", new ClassInfo(FILE, "Foo.class", getClass().getClassLoader()).getPackageName());
407     assertEquals(
408         "a.b", new ClassInfo(FILE, "a/b/Foo.class", getClass().getClassLoader()).getPackageName());
409   }
410 
411   // Test that ResourceInfo.urls() returns identical content to ClassLoader.getResources()
412 
413 
testGetClassPathUrls()414   public void testGetClassPathUrls() throws Exception {
415     String oldPathSeparator = PATH_SEPARATOR.value();
416     String oldClassPath = JAVA_CLASS_PATH.value();
417     System.setProperty(PATH_SEPARATOR.key(), ":");
418     System.setProperty(
419         JAVA_CLASS_PATH.key(),
420         Joiner.on(":")
421             .join(
422                 "relative/path/to/some.jar",
423                 "/absolute/path/to/some.jar",
424                 "relative/path/to/class/root",
425                 "/absolute/path/to/class/root"));
426     try {
427       ImmutableList<URL> urls = ClassPath.parseJavaClassPath();
428 
429       assertThat(urls.get(0).getProtocol()).isEqualTo("file");
430       assertThat(urls.get(0).getAuthority()).isNull();
431       assertThat(urls.get(0).getPath()).endsWith("/relative/path/to/some.jar");
432 
433       assertThat(urls.get(1)).isEqualTo(new URL("file:///absolute/path/to/some.jar"));
434 
435       assertThat(urls.get(2).getProtocol()).isEqualTo("file");
436       assertThat(urls.get(2).getAuthority()).isNull();
437       assertThat(urls.get(2).getPath()).endsWith("/relative/path/to/class/root");
438 
439       assertThat(urls.get(3)).isEqualTo(new URL("file:///absolute/path/to/class/root"));
440 
441       assertThat(urls).hasSize(4);
442     } finally {
443       System.setProperty(PATH_SEPARATOR.key(), oldPathSeparator);
444       System.setProperty(JAVA_CLASS_PATH.key(), oldClassPath);
445     }
446   }
447 
contentEquals(URL left, URL right)448   private static boolean contentEquals(URL left, URL right) throws IOException {
449     return Resources.asByteSource(left).contentEquals(Resources.asByteSource(right));
450   }
451 
452   private static class Nested {}
453 
454 
testNulls()455   public void testNulls() throws IOException {
456     new NullPointerTester().testAllPublicStaticMethods(ClassPath.class);
457     new NullPointerTester()
458         .testAllPublicInstanceMethods(ClassPath.from(getClass().getClassLoader()));
459   }
460 
461 
testLocationsFrom_idempotentScan()462   public void testLocationsFrom_idempotentScan() throws IOException {
463     ImmutableSet<ClassPath.LocationInfo> locations =
464         ClassPath.locationsFrom(getClass().getClassLoader());
465     assertThat(locations).isNotEmpty();
466     for (ClassPath.LocationInfo location : locations) {
467       ImmutableSet<ResourceInfo> resources = location.scanResources();
468       assertThat(location.scanResources()).containsExactlyElementsIn(resources);
469     }
470   }
471 
testLocationsFrom_idempotentLocations()472   public void testLocationsFrom_idempotentLocations() {
473     ImmutableSet<ClassPath.LocationInfo> locations =
474         ClassPath.locationsFrom(getClass().getClassLoader());
475     assertThat(ClassPath.locationsFrom(getClass().getClassLoader()))
476         .containsExactlyElementsIn(locations);
477   }
478 
testLocationEquals()479   public void testLocationEquals() {
480     ClassLoader child = getClass().getClassLoader();
481     ClassLoader parent = child.getParent();
482     new EqualsTester()
483         .addEqualityGroup(
484             new ClassPath.LocationInfo(new File("foo.jar"), child),
485             new ClassPath.LocationInfo(new File("foo.jar"), child))
486         .addEqualityGroup(new ClassPath.LocationInfo(new File("foo.jar"), parent))
487         .addEqualityGroup(new ClassPath.LocationInfo(new File("foo"), child))
488         .testEquals();
489   }
490 
491 
testScanAllResources()492   public void testScanAllResources() throws IOException {
493     assertThat(scanResourceNames(ClassLoader.getSystemClassLoader()))
494         .contains("com/google/common/reflect/ClassPathTest.class");
495   }
496 
497 
testExistsThrowsSecurityException()498   public void testExistsThrowsSecurityException() throws IOException, URISyntaxException {
499     SecurityManager oldSecurityManager = System.getSecurityManager();
500     try {
501       doTestExistsThrowsSecurityException();
502     } finally {
503       System.setSecurityManager(oldSecurityManager);
504     }
505   }
506 
doTestExistsThrowsSecurityException()507   private void doTestExistsThrowsSecurityException() throws IOException, URISyntaxException {
508     File file = null;
509     // In Java 9, Logger may read the TZ database. Only disallow reading the class path URLs.
510     final PermissionCollection readClassPathFiles =
511         new FilePermission("", "read").newPermissionCollection();
512     for (URL url : ClassPath.parseJavaClassPath()) {
513       if (url.getProtocol().equalsIgnoreCase("file")) {
514         file = new File(url.toURI());
515         readClassPathFiles.add(new FilePermission(file.getAbsolutePath(), "read"));
516       }
517     }
518     assertThat(file).isNotNull();
519     SecurityManager disallowFilesSecurityManager =
520         new SecurityManager() {
521           @Override
522           public void checkPermission(Permission p) {
523             if (readClassPathFiles.implies(p)) {
524               throw new SecurityException("Disallowed: " + p);
525             }
526           }
527         };
528     System.setSecurityManager(disallowFilesSecurityManager);
529     try {
530       file.exists();
531       fail("Did not get expected SecurityException");
532     } catch (SecurityException expected) {
533     }
534     ClassPath classPath = ClassPath.from(getClass().getClassLoader());
535     // ClassPath may contain resources from the boot class loader; just not from the class path.
536     for (ResourceInfo resource : classPath.getResources()) {
537       assertThat(resource.getResourceName()).doesNotContain("com/google/common/reflect/");
538     }
539   }
540 
findClass( Iterable<ClassPath.ClassInfo> classes, Class<?> cls)541   private static ClassPath.ClassInfo findClass(
542       Iterable<ClassPath.ClassInfo> classes, Class<?> cls) {
543     for (ClassPath.ClassInfo classInfo : classes) {
544       if (classInfo.getName().equals(cls.getName())) {
545         return classInfo;
546       }
547     }
548     throw new AssertionError("failed to find " + cls);
549   }
550 
resourceInfo(Class<?> cls)551   private static ResourceInfo resourceInfo(Class<?> cls) {
552     String resource = cls.getName().replace('.', '/') + ".class";
553     ClassLoader loader = cls.getClassLoader();
554     return ResourceInfo.of(FILE, resource, loader);
555   }
556 
classInfo(Class<?> cls)557   private static ClassInfo classInfo(Class<?> cls) {
558     return classInfo(cls, cls.getClassLoader());
559   }
560 
classInfo(Class<?> cls, ClassLoader classLoader)561   private static ClassInfo classInfo(Class<?> cls, ClassLoader classLoader) {
562     String resource = cls.getName().replace('.', '/') + ".class";
563     return new ClassInfo(FILE, resource, classLoader);
564   }
565 
manifestClasspath(String classpath)566   private static Manifest manifestClasspath(String classpath) throws IOException {
567     return manifest("Class-Path: " + classpath + "\n");
568   }
569 
writeSelfReferencingJarFile(File jarFile, String... entries)570   private static void writeSelfReferencingJarFile(File jarFile, String... entries)
571       throws IOException {
572     Manifest manifest = new Manifest();
573     // Without version, the manifest is silently ignored. Ugh!
574     manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
575     manifest.getMainAttributes().put(Attributes.Name.CLASS_PATH, jarFile.getName());
576 
577     Closer closer = Closer.create();
578     try {
579       FileOutputStream fileOut = closer.register(new FileOutputStream(jarFile));
580       JarOutputStream jarOut = closer.register(new JarOutputStream(fileOut));
581       for (String entry : entries) {
582         jarOut.putNextEntry(new ZipEntry(entry));
583         Resources.copy(ClassPathTest.class.getResource(entry), jarOut);
584         jarOut.closeEntry();
585       }
586     } catch (Throwable e) {
587       throw closer.rethrow(e);
588     } finally {
589       closer.close();
590     }
591   }
592 
manifest(String content)593   private static Manifest manifest(String content) throws IOException {
594     InputStream in = new ByteArrayInputStream(content.getBytes(US_ASCII));
595     Manifest manifest = new Manifest();
596     manifest.read(in);
597     return manifest;
598   }
599 
fullpath(String path)600   private static File fullpath(String path) {
601     return new File(new File(path).toURI());
602   }
603 
makeJarUrlWithName(String name)604   private static URL makeJarUrlWithName(String name) throws IOException {
605     File fullPath = new File(Files.createTempDir(), name);
606     File jarFile = pickAnyJarFile();
607     Files.copy(jarFile, fullPath);
608     return fullPath.toURI().toURL();
609   }
610 
pickAnyJarFile()611   private static File pickAnyJarFile() throws IOException {
612     for (ClassPath.LocationInfo location :
613         ClassPath.locationsFrom(ClassPathTest.class.getClassLoader())) {
614       if (!location.file().isDirectory() && location.file().exists()) {
615         return location.file();
616       }
617     }
618     throw new AssertionError("Failed to find a jar file");
619   }
620 
621   @AndroidIncompatible // Path (for symlink creation)
deleteRecursivelyOrLog(java.nio.file.Path path)622   private static void deleteRecursivelyOrLog(java.nio.file.Path path) {
623     try {
624       deleteRecursively(path);
625     } catch (IOException e) {
626       log.log(WARNING, "Failure cleaning up test directory", e);
627     }
628   }
629 
scanResourceNames(ClassLoader loader)630   private static ImmutableSet<String> scanResourceNames(ClassLoader loader) throws IOException {
631     ImmutableSet.Builder<String> builder = ImmutableSet.builder();
632     for (ClassPath.LocationInfo location : ClassPath.locationsFrom(loader)) {
633       for (ResourceInfo resource : location.scanResources()) {
634         builder.add(resource.getResourceName());
635       }
636     }
637     return builder.build();
638   }
639 }
640