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