AutomaticModulesTest.java revision 13901:b2a69d66dc65
1/* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/** 25 * @test 26 * @library /lib/testlibrary 27 * @build AutomaticModulesTest ModuleUtils 28 * @run testng AutomaticModulesTest 29 * @summary Basic tests for automatic modules 30 */ 31 32import java.io.IOException; 33import java.io.OutputStream; 34import java.lang.module.Configuration; 35import java.lang.module.ModuleDescriptor; 36import java.lang.module.ModuleDescriptor.Exports; 37import java.lang.module.ModuleDescriptor.Requires.Modifier; 38import java.lang.module.ModuleFinder; 39import java.lang.module.ModuleReference; 40import java.lang.module.ResolvedModule; 41import java.lang.reflect.Layer; 42import java.lang.reflect.Module; 43import java.nio.file.Files; 44import java.nio.file.Path; 45import java.nio.file.Paths; 46import java.util.Optional; 47import java.util.Set; 48import java.util.jar.Attributes; 49import java.util.jar.JarEntry; 50import java.util.jar.JarFile; 51import java.util.jar.JarOutputStream; 52import java.util.jar.Manifest; 53import java.util.stream.Collectors; 54 55import org.testng.annotations.DataProvider; 56import org.testng.annotations.Test; 57import static org.testng.Assert.*; 58 59@Test 60public class AutomaticModulesTest { 61 62 private static final Path USER_DIR 63 = Paths.get(System.getProperty("user.dir")); 64 65 66 @DataProvider(name = "names") 67 public Object[][] createNames() { 68 69 return new Object[][] { 70 71 // JAR file name module-name[/version] 72 73 { "foo.jar", "foo" }, 74 75 { "foo-1.jar", "foo/1" }, 76 { "foo-1.2.jar", "foo/1.2" }, 77 { "foo-1.2.3.jar", "foo/1.2.3" }, 78 { "foo-1.2.3.4.jar", "foo/1.2.3.4" }, 79 80 { "foo-10.jar", "foo/10" }, 81 { "foo-10.20.jar", "foo/10.20" }, 82 { "foo-10.20.30.jar", "foo/10.20.30" }, 83 { "foo-10.20.30.40.jar", "foo/10.20.30.40" }, 84 85 { "foo-bar.jar", "foo.bar" }, 86 { "foo-bar-1.jar", "foo.bar/1" }, 87 { "foo-bar-1.2.jar", "foo.bar/1.2"}, 88 { "foo-bar-10.jar", "foo.bar/10" }, 89 { "foo-bar-10.20.jar", "foo.bar/10.20" }, 90 91 { "foo-1.2-SNAPSHOT.jar", "foo/1.2-SNAPSHOT" }, 92 { "foo-bar-1.2-SNAPSHOT.jar", "foo.bar/1.2-SNAPSHOT" }, 93 94 { "foo--bar-1.0.jar", "foo.bar/1.0" }, 95 { "-foo-bar-1.0.jar", "foo.bar/1.0" }, 96 { "foo-bar--1.0.jar", "foo.bar/1.0" }, 97 98 }; 99 } 100 101 /** 102 * Test mapping of JAR file names to module names 103 */ 104 @Test(dataProvider = "names") 105 public void testNames(String fn, String mid) throws IOException { 106 107 String[] s = mid.split("/"); 108 String mn = s[0]; 109 String vs = (s.length == 2) ? s[1] : null; 110 111 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 112 Path jf = dir.resolve(fn); 113 114 // create empty JAR file 115 createJarFile(jf); 116 117 // create a ModuleFinder to find modules in the directory 118 ModuleFinder finder = ModuleFinder.of(dir); 119 120 // a module with the expected name should be found 121 Optional<ModuleReference> mref = finder.find(mn); 122 assertTrue(mref.isPresent(), mn + " not found"); 123 124 ModuleDescriptor descriptor = mref.get().descriptor(); 125 assertEquals(descriptor.name(), mn); 126 if (vs == null) { 127 assertFalse(descriptor.version().isPresent()); 128 } else { 129 assertEquals(descriptor.version().get().toString(), vs); 130 } 131 132 } 133 134 135 /** 136 * Test all packages are exported 137 */ 138 public void testExports() throws IOException { 139 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 140 createJarFile(dir.resolve("m1.jar"), 141 "p/C1.class", "p/C2.class", "q/C1.class"); 142 143 ModuleFinder finder = ModuleFinder.of(dir); 144 145 Configuration parent = Layer.boot().configuration(); 146 Configuration cf = resolve(parent, finder, "m1"); 147 148 ModuleDescriptor m1 = findDescriptor(cf, "m1"); 149 150 Set<String> exports 151 = m1.exports().stream().map(Exports::source).collect(Collectors.toSet()); 152 153 assertTrue(exports.size() == 2); 154 assertTrue(exports.contains("p")); 155 assertTrue(exports.contains("q")); 156 assertTrue(m1.conceals().isEmpty()); 157 } 158 159 160 /** 161 * Test that a JAR file with a Main-Class attribute results 162 * in a module with a main class. 163 */ 164 public void testMainClass() throws IOException { 165 String mainClass = "p.Main"; 166 167 Manifest man = new Manifest(); 168 Attributes attrs = man.getMainAttributes(); 169 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 170 attrs.put(Attributes.Name.MAIN_CLASS, mainClass); 171 172 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 173 createJarFile(dir.resolve("m1.jar"), man); 174 175 ModuleFinder finder = ModuleFinder.of(dir); 176 177 Configuration parent = Layer.boot().configuration(); 178 Configuration cf = resolve(parent, finder, "m1"); 179 180 ModuleDescriptor m1 = findDescriptor(cf, "m1"); 181 182 assertTrue(m1.mainClass().isPresent()); 183 assertEquals(m1.mainClass().get(), mainClass); 184 } 185 186 187 /** 188 * Basic test of a configuration created with automatic modules. 189 * m1 requires m2* 190 * m1 requires m3* 191 * m2* 192 * m3* 193 */ 194 public void testConfiguration1() throws Exception { 195 ModuleDescriptor descriptor1 196 = new ModuleDescriptor.Builder("m1") 197 .requires("m2") 198 .requires("m3") 199 .requires("java.base") 200 .build(); 201 202 // m2 and m3 are automatic modules 203 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 204 createJarFile(dir.resolve("m2.jar"), "p/T.class"); 205 createJarFile(dir.resolve("m3.jar"), "q/T.class"); 206 207 // module finder locates m1 and the modules in the directory 208 ModuleFinder finder 209 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1), 210 ModuleFinder.of(dir)); 211 212 Configuration parent = Layer.boot().configuration(); 213 Configuration cf = resolve(parent, finder, "m1"); 214 215 assertTrue(cf.modules().size() == 3); 216 assertTrue(cf.findModule("m1").isPresent()); 217 assertTrue(cf.findModule("m2").isPresent()); 218 assertTrue(cf.findModule("m3").isPresent()); 219 220 ResolvedModule base = cf.findModule("java.base").get(); 221 assertTrue(base.configuration() == Layer.boot().configuration()); 222 ResolvedModule m1 = cf.findModule("m1").get(); 223 ResolvedModule m2 = cf.findModule("m2").get(); 224 ResolvedModule m3 = cf.findModule("m3").get(); 225 226 // m2 && m3 only require java.base 227 assertTrue(m2.reference().descriptor().requires().size() == 1); 228 assertTrue(m3.reference().descriptor().requires().size() == 1); 229 230 // readability 231 232 assertTrue(m1.reads().size() == 3); 233 assertTrue(m1.reads().contains(base)); 234 assertTrue(m1.reads().contains(m2)); 235 assertTrue(m1.reads().contains(m3)); 236 237 assertTrue(m2.reads().contains(m1)); 238 assertTrue(m2.reads().contains(m3)); 239 testReadAllBootModules(cf, "m2"); // m2 reads all modules in boot layer 240 241 assertTrue(m3.reads().contains(m1)); 242 assertTrue(m3.reads().contains(m2)); 243 testReadAllBootModules(cf, "m3"); // m3 reads all modules in boot layer 244 245 } 246 247 /** 248 * Basic test of a configuration created with automatic modules 249 * m1 requires m2 250 * m2 requires m3* 251 * m3* 252 * m4* 253 */ 254 public void testInConfiguration2() throws IOException { 255 256 ModuleDescriptor descriptor1 257 = new ModuleDescriptor.Builder("m1") 258 .requires("m2") 259 .requires("java.base") 260 .build(); 261 262 ModuleDescriptor descriptor2 263 = new ModuleDescriptor.Builder("m2") 264 .requires("m3") 265 .requires("java.base") 266 .build(); 267 268 // m3 and m4 are automatic modules 269 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 270 createJarFile(dir.resolve("m3.jar"), "p/T.class"); 271 createJarFile(dir.resolve("m4.jar"), "q/T.class"); 272 273 // module finder locates m1 and the modules in the directory 274 ModuleFinder finder 275 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2), 276 ModuleFinder.of(dir)); 277 278 Configuration parent = Layer.boot().configuration(); 279 Configuration cf = resolve(parent, finder, "m1", "m4"); 280 281 assertTrue(cf.modules().size() == 4); 282 assertTrue(cf.findModule("m1").isPresent()); 283 assertTrue(cf.findModule("m2").isPresent()); 284 assertTrue(cf.findModule("m3").isPresent()); 285 assertTrue(cf.findModule("m4").isPresent()); 286 287 // m3 && m4 should only require java.base 288 assertTrue(findDescriptor(cf, "m3").requires().size() == 1); 289 assertTrue(findDescriptor(cf, "m4").requires().size() == 1); 290 291 // readability 292 293 ResolvedModule base = cf.findModule("java.base").get(); 294 assertTrue(base.configuration() == Layer.boot().configuration()); 295 ResolvedModule m1 = cf.findModule("m1").get(); 296 ResolvedModule m2 = cf.findModule("m2").get(); 297 ResolvedModule m3 = cf.findModule("m3").get(); 298 ResolvedModule m4 = cf.findModule("m4").get(); 299 300 assertTrue(m1.reads().size() == 2); 301 assertTrue(m1.reads().contains(m2)); 302 assertTrue(m1.reads().contains(base)); 303 304 assertTrue(m2.reads().size() == 3); 305 assertTrue(m2.reads().contains(m3)); 306 assertTrue(m2.reads().contains(m4)); 307 assertTrue(m2.reads().contains(base)); 308 309 assertTrue(m3.reads().contains(m1)); 310 assertTrue(m3.reads().contains(m2)); 311 assertTrue(m3.reads().contains(m4)); 312 testReadAllBootModules(cf, "m3"); // m3 reads all modules in boot layer 313 314 assertTrue(m4.reads().contains(m1)); 315 assertTrue(m4.reads().contains(m2)); 316 assertTrue(m4.reads().contains(m3)); 317 testReadAllBootModules(cf, "m4"); // m4 reads all modules in boot layer 318 319 } 320 321 322 /** 323 * Basic test of a configuration created with automatic modules 324 * m1 requires m2 325 * m2 requires public m3* 326 * m3* 327 * m4* 328 */ 329 public void testInConfiguration3() throws IOException { 330 331 ModuleDescriptor descriptor1 332 = new ModuleDescriptor.Builder("m1") 333 .requires("m2") 334 .requires("java.base") 335 .build(); 336 337 ModuleDescriptor descriptor2 338 = new ModuleDescriptor.Builder("m2") 339 .requires(Modifier.PUBLIC, "m3") 340 .requires("java.base") 341 .build(); 342 343 // m3 and m4 are automatic modules 344 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 345 createJarFile(dir.resolve("m3.jar"), "p/T.class"); 346 createJarFile(dir.resolve("m4.jar"), "q/T.class"); 347 348 // module finder locates m1 and the modules in the directory 349 ModuleFinder finder 350 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2), 351 ModuleFinder.of(dir)); 352 353 Configuration parent = Layer.boot().configuration(); 354 Configuration cf = resolve(parent, finder, "m1", "m4"); 355 356 assertTrue(cf.modules().size() == 4); 357 assertTrue(cf.findModule("m1").isPresent()); 358 assertTrue(cf.findModule("m2").isPresent()); 359 assertTrue(cf.findModule("m3").isPresent()); 360 assertTrue(cf.findModule("m4").isPresent()); 361 362 ResolvedModule base = cf.findModule("java.base").get(); 363 assertTrue(base.configuration() == Layer.boot().configuration()); 364 ResolvedModule m1 = cf.findModule("m1").get(); 365 ResolvedModule m2 = cf.findModule("m2").get(); 366 ResolvedModule m3 = cf.findModule("m3").get(); 367 ResolvedModule m4 = cf.findModule("m4").get(); 368 369 // m3 && m4 should only require java.base 370 assertTrue(findDescriptor(cf, "m3").requires().size() == 1); 371 assertTrue(findDescriptor(cf, "m4").requires().size() == 1); 372 373 // readability 374 375 assertTrue(m1.reads().size() == 4); 376 assertTrue(m1.reads().contains(m2)); 377 assertTrue(m1.reads().contains(m3)); 378 assertTrue(m1.reads().contains(m4)); 379 assertTrue(m1.reads().contains(base)); 380 381 assertTrue(m2.reads().size() == 3); 382 assertTrue(m2.reads().contains(m3)); 383 assertTrue(m2.reads().contains(m4)); 384 assertTrue(m2.reads().contains(base)); 385 386 assertTrue(reads(cf, "m2", "m3")); 387 assertTrue(reads(cf, "m2", "m4")); 388 assertTrue(reads(cf, "m2", "java.base")); 389 390 assertTrue(m3.reads().contains(m1)); 391 assertTrue(m3.reads().contains(m2)); 392 assertTrue(m3.reads().contains(m4)); 393 testReadAllBootModules(cf, "m3"); // m3 reads all modules in boot layer 394 395 assertTrue(m4.reads().contains(m1)); 396 assertTrue(m4.reads().contains(m2)); 397 assertTrue(m4.reads().contains(m3)); 398 testReadAllBootModules(cf, "m4"); // m4 reads all modules in boot layer 399 400 } 401 402 403 /** 404 * Basic test of Layer containing automatic modules 405 */ 406 public void testInLayer() throws IOException { 407 ModuleDescriptor descriptor 408 = new ModuleDescriptor.Builder("m1") 409 .requires("m2") 410 .requires("m3") 411 .build(); 412 413 // m2 and m3 are simple JAR files 414 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 415 createJarFile(dir.resolve("m2.jar"), "p/T.class"); 416 createJarFile(dir.resolve("m3.jar"), "q/T2.class"); 417 418 // module finder locates m1 and the modules in the directory 419 ModuleFinder finder 420 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), 421 ModuleFinder.of(dir)); 422 423 Configuration parent = Layer.boot().configuration(); 424 Configuration cf = resolve(parent, finder, "m1"); 425 assertTrue(cf.modules().size() == 3); 426 427 // each module gets its own loader 428 Layer layer = Layer.boot().defineModules(cf, mn -> new ClassLoader() { }); 429 430 // an unnamed module 431 Module unnamed = (new ClassLoader() { }).getUnnamedModule(); 432 433 Module m2 = layer.findModule("m2").get(); 434 assertTrue(m2.isNamed()); 435 assertTrue(m2.canRead(unnamed)); 436 testsReadsAll(m2, layer); 437 438 Module m3 = layer.findModule("m3").get(); 439 assertTrue(m3.isNamed()); 440 assertTrue(m2.canRead(unnamed)); 441 testsReadsAll(m3, layer); 442 } 443 444 445 /** 446 * Test miscellaneous methods. 447 */ 448 public void testMisc() throws IOException { 449 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 450 Path m1_jar = createJarFile(dir.resolve("m1.jar"), "p/T.class"); 451 452 ModuleFinder finder = ModuleFinder.of(m1_jar); 453 454 assertTrue(finder.find("m1").isPresent()); 455 ModuleDescriptor m1 = finder.find("m1").get().descriptor(); 456 457 // test miscellaneous methods 458 assertTrue(m1.isAutomatic()); 459 assertFalse(m1.isSynthetic()); 460 assertFalse(m1.osName().isPresent()); 461 assertFalse(m1.osArch().isPresent()); 462 assertFalse(m1.osVersion().isPresent()); 463 } 464 465 466 /** 467 * Invokes parent.resolveRequires to resolve the given root modules. 468 */ 469 static Configuration resolve(Configuration parent, 470 ModuleFinder finder, 471 String... roots) { 472 return parent.resolveRequires(finder, ModuleFinder.empty(), Set.of(roots)); 473 } 474 475 /** 476 * Finds a module in the given configuration or its parents, returning 477 * the module descriptor (or null if not found) 478 */ 479 static ModuleDescriptor findDescriptor(Configuration cf, String name) { 480 Optional<ResolvedModule> om = cf.findModule(name); 481 if (om.isPresent()) { 482 return om.get().reference().descriptor(); 483 } else { 484 return null; 485 } 486 } 487 488 /** 489 * Test that a module in a configuration reads all modules in the boot 490 * configuration. 491 */ 492 static void testReadAllBootModules(Configuration cf, String mn) { 493 494 Set<String> bootModules = Layer.boot().modules().stream() 495 .map(Module::getName) 496 .collect(Collectors.toSet()); 497 498 bootModules.forEach(other -> assertTrue(reads(cf, mn, other))); 499 500 } 501 502 /** 503 * Test that the given Module reads all module in the given Layer 504 * and its parent Layers. 505 */ 506 static void testsReadsAll(Module m, Layer layer) { 507 while (layer != Layer.empty()) { 508 509 // check that m reads all module in the layer 510 layer.configuration().modules().stream() 511 .map(ResolvedModule::name) 512 .map(layer::findModule) 513 .map(Optional::get) 514 .forEach(m2 -> assertTrue(m.canRead(m2))); 515 516 layer = layer.parent().get(); 517 } 518 } 519 520 /** 521 * Returns {@code true} if the configuration contains module mn1 522 * that reads module mn2. 523 */ 524 static boolean reads(Configuration cf, String mn1, String mn2) { 525 Optional<ResolvedModule> om1 = cf.findModule(mn1); 526 if (!om1.isPresent()) 527 return false; 528 529 return om1.get().reads().stream() 530 .map(ResolvedModule::name) 531 .anyMatch(mn2::equals); 532 } 533 534 /** 535 * Creates a JAR file, optionally with a manifest, and with the given 536 * entries. The entries will be empty in the resulting JAR file. 537 */ 538 static Path createJarFile(Path file, Manifest man, String... entries) 539 throws IOException 540 { 541 try (OutputStream out = Files.newOutputStream(file)) { 542 try (JarOutputStream jos = new JarOutputStream(out)) { 543 544 if (man != null) { 545 JarEntry je = new JarEntry(JarFile.MANIFEST_NAME); 546 jos.putNextEntry(je); 547 man.write(jos); 548 jos.closeEntry(); 549 } 550 551 for (String entry : entries) { 552 JarEntry je = new JarEntry(entry); 553 jos.putNextEntry(je); 554 jos.closeEntry(); 555 } 556 557 } 558 } 559 return file; 560 } 561 562 /** 563 * Creates a JAR file and with the given entries. The entries will be empty 564 * in the resulting JAR file. 565 */ 566 static Path createJarFile(Path file, String... entries) 567 throws IOException 568 { 569 return createJarFile(file, null, entries); 570 } 571 572} 573