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