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