1 /* 2 * Copyright (c) 2015, 2017, 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 JarUtils 28 * @run testng AutomaticModulesTest 29 * @summary Basic tests for automatic modules 30 */ 31 32 import java.io.IOException; 33 import java.lang.module.Configuration; 34 import java.lang.module.FindException; 35 import java.lang.module.ModuleDescriptor; 36 import java.lang.module.ModuleDescriptor.Requires.Modifier; 37 import java.lang.module.ModuleFinder; 38 import java.lang.module.ModuleReference; 39 import java.lang.module.ResolutionException; 40 import java.lang.module.ResolvedModule; 41 import java.lang.reflect.Layer; 42 import java.lang.reflect.Module; 43 import java.nio.file.Files; 44 import java.nio.file.Path; 45 import java.nio.file.Paths; 46 import java.util.Optional; 47 import java.util.Set; 48 import java.util.jar.Attributes; 49 import java.util.jar.Manifest; 50 import java.util.stream.Collectors; 51 import java.util.stream.Stream; 52 53 import org.testng.annotations.DataProvider; 54 import org.testng.annotations.Test; 55 import static org.testng.Assert.*; 56 57 @Test 58 public class AutomaticModulesTest { 59 60 private static final Path USER_DIR 61 = Paths.get(System.getProperty("user.dir")); 62 63 @DataProvider(name = "names") 64 public Object[][] createNames() { 65 return new Object[][] { 66 67 // JAR file name module-name[/version] 68 69 { "foo.jar", "foo" }, 70 { "foo4j.jar", "foo4j", }, 71 72 { "foo1.jar", "foo" }, 73 { "foo1.2.jar", "foo" }, 74 { "foo1.2.3.jar", "foo" }, 75 76 { "foo10.jar", "foo" }, 77 { "foo10.20.jar", "foo" }, 78 { "foo10.20.30.jar", "foo" }, 79 80 { "foo-1.jar", "foo/1" }, 81 { "foo-1.2.jar", "foo/1.2" }, 82 { "foo-1.2.3.jar", "foo/1.2.3" }, 83 { "foo-1.2.3.4.jar", "foo/1.2.3.4" }, 84 85 { "foo-10.jar", "foo/10" }, 86 { "foo-10.20.jar", "foo/10.20" }, 87 { "foo-10.20.30.jar", "foo/10.20.30" }, 88 { "foo-10.20.30.40.jar", "foo/10.20.30.40" }, 89 90 { "foo-bar.jar", "foo.bar" }, 91 { "foo-bar-1.jar", "foo.bar/1" }, 92 { "foo-bar-1.2.jar", "foo.bar/1.2"}, 93 { "foo-bar-10.jar", "foo.bar/10" }, 94 { "foo-bar-10.20.jar", "foo.bar/10.20" }, 95 96 { "foo-1.2-SNAPSHOT.jar", "foo/1.2-SNAPSHOT" }, 97 { "foo-bar-1.2-SNAPSHOT.jar", "foo.bar/1.2-SNAPSHOT" }, 98 99 { "foo--bar-1.0.jar", "foo.bar/1.0" }, 100 { "-foo-bar-1.0.jar", "foo.bar/1.0" }, 101 { "foo-bar--1.0.jar", "foo.bar/1.0" }, 102 103 }; 104 } 105 106 // JAR file names that do not map to a legal module name 107 @DataProvider(name = "badnames") 108 public Object[][] createBadNames() { 109 return new Object[][]{ 110 111 { ".jar", null }, 112 { "_.jar", null } 113 114 }; 115 } 116 117 /** 118 * Test mapping of JAR file names to module names 119 */ 120 @Test(dataProvider = "names") 121 public void testNames(String fn, String mid) throws IOException { 122 String[] s = mid.split("/"); 123 String mn = s[0]; 124 String vs = (s.length == 2) ? s[1] : null; 125 126 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 127 Path jf = dir.resolve(fn); 128 129 // create empty JAR file 130 createDummyJarFile(jf); 131 132 // create a ModuleFinder to find modules in the directory 133 ModuleFinder finder = ModuleFinder.of(dir); 134 135 // a module with the expected name should be found 136 Optional<ModuleReference> mref = finder.find(mn); 137 assertTrue(mref.isPresent(), mn + " not found"); 138 139 ModuleDescriptor descriptor = mref.get().descriptor(); 140 assertTrue(descriptor.isAutomatic()); 141 assertEquals(descriptor.name(), mn); 142 if (vs == null) { 143 assertFalse(descriptor.version().isPresent()); 144 } else { 145 assertEquals(descriptor.version().get().toString(), vs); 146 } 147 } 148 149 150 /** 151 * Test impossible mapping of JAR files to modules names 152 */ 153 @Test(dataProvider = "badnames", expectedExceptions = FindException.class) 154 public void testBadNames(String fn, String ignore) throws IOException { 155 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 156 Path jf = dir.resolve(fn); 157 158 // create empty JAR file 159 createDummyJarFile(jf); 160 161 // should throw FindException 162 ModuleFinder.of(dir).findAll(); 163 } 164 165 166 /** 167 * Test all packages are exported 168 */ 169 public void testPackages() throws IOException { 170 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 171 createDummyJarFile(dir.resolve("m.jar"), 172 "p/C1.class", "p/C2.class", "q/C1.class"); 173 174 ModuleFinder finder = ModuleFinder.of(dir); 175 Optional<ModuleReference> mref = finder.find("m"); 176 assertTrue(mref.isPresent(), "m not found"); 177 178 ModuleDescriptor descriptor = mref.get().descriptor(); 179 assertTrue(descriptor.isAutomatic()); 180 181 assertTrue(descriptor.packages().size() == 2); 182 assertTrue(descriptor.packages().contains("p")); 183 assertTrue(descriptor.packages().contains("q")); 184 185 assertTrue(descriptor.exports().isEmpty()); 186 assertTrue(descriptor.opens().isEmpty()); 187 } 188 189 /** 190 * Test class files in JAR file where the entry does not correspond to a 191 * legal package name. 192 */ 193 public void testBadPackage() throws IOException { 194 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 195 createDummyJarFile(dir.resolve("m.jar"), "p/C1.class", "p-/C2.class"); 196 197 ModuleFinder finder = ModuleFinder.of(dir); 198 Optional<ModuleReference> mref = finder.find("m"); 199 assertTrue(mref.isPresent(), "m not found"); 200 201 ModuleDescriptor descriptor = mref.get().descriptor(); 202 assertTrue(descriptor.isAutomatic()); 203 204 assertTrue(descriptor.packages().size() == 1); 205 assertTrue(descriptor.packages().contains("p")); 206 207 assertTrue(descriptor.exports().isEmpty()); 208 assertTrue(descriptor.opens().isEmpty()); 209 } 210 211 /** 212 * Test non-class resources in a JAR file. 213 */ 214 public void testNonClassResources() throws IOException { 215 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 216 createDummyJarFile(dir.resolve("m.jar"), 217 "LICENSE", 218 "README", 219 "WEB-INF/tags", 220 "p/Type.class", 221 "p/resources/m.properties"); 222 223 ModuleFinder finder = ModuleFinder.of(dir); 224 Optional<ModuleReference> mref = finder.find("m"); 225 assertTrue(mref.isPresent(), "m not found"); 226 227 ModuleDescriptor descriptor = mref.get().descriptor(); 228 assertTrue(descriptor.isAutomatic()); 229 230 assertTrue(descriptor.packages().size() == 1); 231 assertTrue(descriptor.packages().contains("p")); 232 } 233 234 /** 235 * Test .class file in unnamed package (top-level directory) 236 */ 237 @Test(expectedExceptions = FindException.class) 238 public void testClassInUnnamedPackage() throws IOException { 239 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 240 createDummyJarFile(dir.resolve("m.jar"), "Mojo.class"); 241 ModuleFinder finder = ModuleFinder.of(dir); 242 finder.findAll(); 243 } 244 245 /** 246 * Test JAR file with META-INF/services configuration file 247 */ 248 public void testServicesConfiguration() throws IOException { 249 String service = "p.S"; 250 String provider = "p.S1"; 251 252 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 253 254 // provider class 255 Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class"); 256 Files.createDirectories(providerClass.getParent()); 257 Files.createFile(providerClass); 258 259 // services configuration file 260 Path services = tmpdir.resolve("META-INF").resolve("services"); 261 Files.createDirectories(services); 262 Files.write(services.resolve(service), Set.of(provider)); 263 264 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 265 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 266 267 ModuleFinder finder = ModuleFinder.of(dir); 268 269 Optional<ModuleReference> mref = finder.find("m"); 270 assertTrue(mref.isPresent(), "m not found"); 271 272 ModuleDescriptor descriptor = mref.get().descriptor(); 273 assertTrue(descriptor.provides().size() == 1); 274 ModuleDescriptor.Provides provides = descriptor.provides().iterator().next(); 275 assertEquals(provides.service(), service); 276 assertTrue(provides.providers().size() == 1); 277 assertTrue(provides.providers().contains((provider))); 278 } 279 280 281 // META-INF/services files that don't map to legal service names 282 @DataProvider(name = "badservices") 283 public Object[][] createBadServices() { 284 return new Object[][] { 285 286 // service type provider type 287 { "-", "p.S1" }, 288 { ".S", "p.S1" }, 289 }; 290 } 291 292 /** 293 * Test JAR file with META-INF/services configuration file with bad 294 * values or names. 295 */ 296 @Test(dataProvider = "badservices") 297 public void testBadServicesNames(String service, String provider) 298 throws IOException 299 { 300 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 301 Path services = tmpdir.resolve("META-INF").resolve("services"); 302 Files.createDirectories(services); 303 Files.write(services.resolve(service), Set.of(provider)); 304 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 305 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 306 307 Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m"); 308 assertTrue(omref.isPresent()); 309 ModuleDescriptor descriptor = omref.get().descriptor(); 310 assertTrue(descriptor.provides().isEmpty()); 311 } 312 313 314 // META-INF/services configuration file entries that are not legal 315 @DataProvider(name = "badproviders") 316 public Object[][] createBadProviders() { 317 return new Object[][] { 318 319 // service type provider type 320 { "p.S", "-" }, 321 { "p.S", "p..S1" }, 322 { "p.S", "S1." }, 323 }; 324 } 325 326 /** 327 * Test JAR file with META-INF/services configuration file with bad 328 * values or names. 329 */ 330 @Test(dataProvider = "badproviders", expectedExceptions = FindException.class) 331 public void testBadProviderNames(String service, String provider) 332 throws IOException 333 { 334 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 335 336 // provider class 337 Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class"); 338 Files.createDirectories(providerClass.getParent()); 339 Files.createFile(providerClass); 340 341 // services configuration file 342 Path services = tmpdir.resolve("META-INF").resolve("services"); 343 Files.createDirectories(services); 344 Files.write(services.resolve(service), Set.of(provider)); 345 346 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 347 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 348 349 // should throw FindException 350 ModuleFinder.of(dir).findAll(); 351 } 352 353 /** 354 * Test JAR file with META-INF/services configuration file listing a 355 * provider that is not in the module. 356 */ 357 @Test(expectedExceptions = FindException.class) 358 public void testMissingProviderPackage() throws IOException { 359 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 360 361 // services configuration file 362 Path services = tmpdir.resolve("META-INF").resolve("services"); 363 Files.createDirectories(services); 364 Files.write(services.resolve("p.S"), Set.of("q.P")); 365 366 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 367 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 368 369 // should throw FindException 370 ModuleFinder.of(dir).findAll(); 371 } 372 373 374 /** 375 * Test that a JAR file with a Main-Class attribute results 376 * in a module with a main class. 377 */ 378 public void testMainClass() throws IOException { 379 String mainClass = "p.Main"; 380 381 Manifest man = new Manifest(); 382 Attributes attrs = man.getMainAttributes(); 383 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 384 attrs.put(Attributes.Name.MAIN_CLASS, mainClass); 385 386 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 387 String entry = mainClass.replace('.', '/') + ".class"; 388 createDummyJarFile(dir.resolve("m.jar"), man, entry); 389 390 ModuleFinder finder = ModuleFinder.of(dir); 391 392 Configuration parent = Layer.boot().configuration(); 393 Configuration cf = resolve(parent, finder, "m"); 394 395 ModuleDescriptor descriptor = findDescriptor(cf, "m"); 396 397 assertTrue(descriptor.mainClass().isPresent()); 398 assertEquals(descriptor.mainClass().get(), mainClass); 399 } 400 401 402 // Main-Class files that do not map to a legal qualified type name 403 @DataProvider(name = "badmainclass") 404 public Object[][] createBadMainClass() { 405 return new Object[][]{ 406 407 { "Main", null }, 408 { "p..Main", null }, 409 { "p-.Main", null }, 410 411 }; 412 } 413 414 /** 415 * Test that a JAR file with a Main-Class attribute that is not a qualified 416 * type name. 417 */ 418 @Test(dataProvider = "badmainclass", expectedExceptions = FindException.class) 419 public void testBadMainClass(String mainClass, String ignore) throws IOException { 420 Manifest man = new Manifest(); 421 Attributes attrs = man.getMainAttributes(); 422 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 423 attrs.put(Attributes.Name.MAIN_CLASS, mainClass); 424 425 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 426 String entry = mainClass.replace('.', '/') + ".class"; 427 createDummyJarFile(dir.resolve("m.jar"), man, entry); 428 429 // should throw FindException 430 ModuleFinder.of(dir).findAll(); 431 } 432 433 /** 434 * Test that a JAR file with a Main-Class attribute that is not in the module 435 */ 436 @Test(expectedExceptions = FindException.class) 437 public void testMissingMainClassPackage() throws IOException { 438 Manifest man = new Manifest(); 439 Attributes attrs = man.getMainAttributes(); 440 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 441 attrs.put(Attributes.Name.MAIN_CLASS, "p.Main"); 442 443 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 444 createDummyJarFile(dir.resolve("m.jar"), man); 445 446 // should throw FindException 447 ModuleFinder.of(dir).findAll(); 448 } 449 450 451 /** 452 * Basic test of a configuration created with automatic modules. 453 * a requires b* 454 * a requires c* 455 * b* 456 * c* 457 */ 458 public void testConfiguration1() throws Exception { 459 ModuleDescriptor descriptor1 460 = ModuleDescriptor.newModule("a") 461 .requires("b") 462 .requires("c") 463 .requires("java.base") 464 .build(); 465 466 // b and c are automatic modules 467 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 468 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 469 createDummyJarFile(dir.resolve("c.jar"), "q/T.class"); 470 471 // module finder locates a and the modules in the directory 472 ModuleFinder finder 473 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1), 474 ModuleFinder.of(dir)); 475 476 Configuration parent = Layer.boot().configuration(); 477 Configuration cf = resolve(parent, finder, "a"); 478 479 assertTrue(cf.modules().size() == 3); 480 assertTrue(cf.findModule("a").isPresent()); 481 assertTrue(cf.findModule("b").isPresent()); 482 assertTrue(cf.findModule("c").isPresent()); 483 484 ResolvedModule base = cf.findModule("java.base").get(); 485 assertTrue(base.configuration() == Layer.boot().configuration()); 486 ResolvedModule a = cf.findModule("a").get(); 487 ResolvedModule b = cf.findModule("b").get(); 488 ResolvedModule c = cf.findModule("c").get(); 489 490 // b && c only require java.base 491 assertTrue(b.reference().descriptor().requires().size() == 1); 492 assertTrue(c.reference().descriptor().requires().size() == 1); 493 494 // readability 495 496 assertTrue(a.reads().size() == 3); 497 assertTrue(a.reads().contains(base)); 498 assertTrue(a.reads().contains(b)); 499 assertTrue(a.reads().contains(c)); 500 501 assertTrue(b.reads().contains(a)); 502 assertTrue(b.reads().contains(c)); 503 testReadAllBootModules(cf, "b"); // b reads all modules in boot layer 504 505 assertTrue(c.reads().contains(a)); 506 assertTrue(c.reads().contains(b)); 507 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer 508 509 } 510 511 /** 512 * Basic test of a configuration created with automatic modules 513 * a requires b 514 * b requires c* 515 * c* 516 * d* 517 */ 518 public void testInConfiguration2() throws IOException { 519 ModuleDescriptor descriptor1 520 = ModuleDescriptor.newModule("a") 521 .requires("b") 522 .requires("java.base") 523 .build(); 524 525 ModuleDescriptor descriptor2 526 = ModuleDescriptor.newModule("b") 527 .requires("c") 528 .requires("java.base") 529 .build(); 530 531 // c and d are automatic modules 532 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 533 createDummyJarFile(dir.resolve("c.jar"), "p/T.class"); 534 createDummyJarFile(dir.resolve("d.jar"), "q/T.class"); 535 536 // module finder locates a and the modules in the directory 537 ModuleFinder finder 538 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2), 539 ModuleFinder.of(dir)); 540 541 Configuration parent = Layer.boot().configuration(); 542 Configuration cf = resolve(parent, finder, "a", "d"); 543 544 assertTrue(cf.modules().size() == 4); 545 assertTrue(cf.findModule("a").isPresent()); 546 assertTrue(cf.findModule("b").isPresent()); 547 assertTrue(cf.findModule("c").isPresent()); 548 assertTrue(cf.findModule("d").isPresent()); 549 550 // c && d should only require java.base 551 assertTrue(findDescriptor(cf, "c").requires().size() == 1); 552 assertTrue(findDescriptor(cf, "d").requires().size() == 1); 553 554 // readability 555 556 ResolvedModule base = cf.findModule("java.base").get(); 557 assertTrue(base.configuration() == Layer.boot().configuration()); 558 ResolvedModule a = cf.findModule("a").get(); 559 ResolvedModule b = cf.findModule("b").get(); 560 ResolvedModule c = cf.findModule("c").get(); 561 ResolvedModule d = cf.findModule("d").get(); 562 563 assertTrue(a.reads().size() == 2); 564 assertTrue(a.reads().contains(b)); 565 assertTrue(a.reads().contains(base)); 566 567 assertTrue(b.reads().size() == 3); 568 assertTrue(b.reads().contains(c)); 569 assertTrue(b.reads().contains(d)); 570 assertTrue(b.reads().contains(base)); 571 572 assertTrue(c.reads().contains(a)); 573 assertTrue(c.reads().contains(b)); 574 assertTrue(c.reads().contains(d)); 575 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer 576 577 assertTrue(d.reads().contains(a)); 578 assertTrue(d.reads().contains(b)); 579 assertTrue(d.reads().contains(c)); 580 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer 581 } 582 583 584 /** 585 * Basic test of a configuration created with automatic modules 586 * a requires b 587 * b requires transitive c* 588 * c* 589 * d* 590 */ 591 public void testInConfiguration3() throws IOException { 592 ModuleDescriptor descriptor1 593 = ModuleDescriptor.newModule("a") 594 .requires("b") 595 .requires("java.base") 596 .build(); 597 598 ModuleDescriptor descriptor2 599 = ModuleDescriptor.newModule("b") 600 .requires(Set.of(Modifier.TRANSITIVE), "c") 601 .requires("java.base") 602 .build(); 603 604 // c and d are automatic modules 605 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 606 createDummyJarFile(dir.resolve("c.jar"), "p/T.class"); 607 createDummyJarFile(dir.resolve("d.jar"), "q/T.class"); 608 609 // module finder locates a and the modules in the directory 610 ModuleFinder finder 611 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor1, descriptor2), 612 ModuleFinder.of(dir)); 613 614 Configuration parent = Layer.boot().configuration(); 615 Configuration cf = resolve(parent, finder, "a", "d"); 616 617 assertTrue(cf.modules().size() == 4); 618 assertTrue(cf.findModule("a").isPresent()); 619 assertTrue(cf.findModule("b").isPresent()); 620 assertTrue(cf.findModule("c").isPresent()); 621 assertTrue(cf.findModule("d").isPresent()); 622 623 ResolvedModule base = cf.findModule("java.base").get(); 624 assertTrue(base.configuration() == Layer.boot().configuration()); 625 ResolvedModule a = cf.findModule("a").get(); 626 ResolvedModule b = cf.findModule("b").get(); 627 ResolvedModule c = cf.findModule("c").get(); 628 ResolvedModule d = cf.findModule("d").get(); 629 630 // c && d should only require java.base 631 assertTrue(findDescriptor(cf, "c").requires().size() == 1); 632 assertTrue(findDescriptor(cf, "d").requires().size() == 1); 633 634 // readability 635 636 assertTrue(a.reads().size() == 4); 637 assertTrue(a.reads().contains(b)); 638 assertTrue(a.reads().contains(c)); 639 assertTrue(a.reads().contains(d)); 640 assertTrue(a.reads().contains(base)); 641 642 assertTrue(b.reads().size() == 3); 643 assertTrue(b.reads().contains(c)); 644 assertTrue(b.reads().contains(d)); 645 assertTrue(b.reads().contains(base)); 646 647 assertTrue(reads(cf, "b", "c")); 648 assertTrue(reads(cf, "b", "d")); 649 assertTrue(reads(cf, "b", "java.base")); 650 651 assertTrue(c.reads().contains(a)); 652 assertTrue(c.reads().contains(b)); 653 assertTrue(c.reads().contains(d)); 654 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer 655 656 assertTrue(d.reads().contains(a)); 657 assertTrue(d.reads().contains(b)); 658 assertTrue(d.reads().contains(c)); 659 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer 660 } 661 662 663 /** 664 * Basic test of a configuration created with automatic modules 665 * a requires b* and c* 666 * b* contains p 667 * c* contains p 668 */ 669 @Test(expectedExceptions = { ResolutionException.class }) 670 public void testDuplicateSuppliers1() throws IOException { 671 ModuleDescriptor descriptor 672 = ModuleDescriptor.newModule("a") 673 .requires("b") 674 .requires("c") 675 .build(); 676 677 // c and d are automatic modules with the same package 678 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 679 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 680 createDummyJarFile(dir.resolve("c.jar"), "p/T.class"); 681 682 // module finder locates 'a' and the modules in the directory 683 ModuleFinder finder 684 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), 685 ModuleFinder.of(dir)); 686 687 Configuration parent = Layer.boot().configuration(); 688 resolve(parent, finder, "a"); 689 } 690 691 692 /** 693 * Basic test of a configuration created with automatic modules 694 * a contains p, requires b* 695 * b* contains p 696 */ 697 @Test(expectedExceptions = { ResolutionException.class }) 698 public void testDuplicateSuppliers2() throws IOException { 699 ModuleDescriptor descriptor 700 = ModuleDescriptor.newModule("a") 701 .packages(Set.of("p")) 702 .requires("b") 703 .build(); 704 705 // c and d are automatic modules with the same package 706 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 707 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 708 709 // module finder locates 'a' and the modules in the directory 710 ModuleFinder finder 711 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), 712 ModuleFinder.of(dir)); 713 714 Configuration parent = Layer.boot().configuration(); 715 resolve(parent, finder, "a"); 716 } 717 718 719 /** 720 * Basic test of Layer containing automatic modules 721 */ 722 public void testInLayer() throws IOException { 723 ModuleDescriptor descriptor 724 = ModuleDescriptor.newModule("a") 725 .requires("b") 726 .requires("c") 727 .build(); 728 729 // b and c are simple JAR files 730 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 731 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 732 createDummyJarFile(dir.resolve("c.jar"), "q/T2.class"); 733 734 // module finder locates a and the modules in the directory 735 ModuleFinder finder 736 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), 737 ModuleFinder.of(dir)); 738 739 Configuration parent = Layer.boot().configuration(); 740 Configuration cf = resolve(parent, finder, "a"); 741 assertTrue(cf.modules().size() == 3); 742 743 // each module gets its own loader 744 Layer layer = Layer.boot().defineModules(cf, mn -> new ClassLoader() { }); 745 746 // an unnamed module 747 Module unnamed = (new ClassLoader() { }).getUnnamedModule(); 748 749 Module b = layer.findModule("b").get(); 750 assertTrue(b.isNamed()); 751 assertTrue(b.canRead(unnamed)); 752 testsReadsAll(b, layer); 753 754 Module c = layer.findModule("c").get(); 755 assertTrue(c.isNamed()); 756 assertTrue(b.canRead(unnamed)); 757 testsReadsAll(c, layer); 758 } 759 760 761 /** 762 * Test miscellaneous methods. 763 */ 764 public void testMisc() throws IOException { 765 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 766 Path m_jar = createDummyJarFile(dir.resolve("m.jar"), "p/T.class"); 767 768 ModuleFinder finder = ModuleFinder.of(m_jar); 769 770 assertTrue(finder.find("m").isPresent()); 771 ModuleDescriptor m = finder.find("m").get().descriptor(); 772 773 // test miscellaneous methods 774 assertTrue(m.isAutomatic()); 775 assertFalse(m.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)); 776 } 777 778 779 /** 780 * Invokes parent.resolve to resolve the given root modules. 781 */ 782 static Configuration resolve(Configuration parent, 783 ModuleFinder finder, 784 String... roots) { 785 return parent.resolve(finder, ModuleFinder.of(), Set.of(roots)); 786 } 787 788 /** 789 * Finds a module in the given configuration or its parents, returning 790 * the module descriptor (or null if not found) 791 */ 792 static ModuleDescriptor findDescriptor(Configuration cf, String name) { 793 Optional<ResolvedModule> om = cf.findModule(name); 794 if (om.isPresent()) { 795 return om.get().reference().descriptor(); 796 } else { 797 return null; 798 } 799 } 800 801 /** 802 * Test that a module in a configuration reads all modules in the boot 803 * configuration. 804 */ 805 static void testReadAllBootModules(Configuration cf, String mn) { 806 807 Set<String> bootModules = Layer.boot().modules().stream() 808 .map(Module::getName) 809 .collect(Collectors.toSet()); 810 811 bootModules.forEach(other -> assertTrue(reads(cf, mn, other))); 812 813 } 814 815 /** 816 * Test that the given Module reads all module in the given Layer 817 * and its parent Layers. 818 */ 819 static void testsReadsAll(Module m, Layer layer) { 820 // check that m reads all modules in the layer 821 layer.configuration().modules().stream() 822 .map(ResolvedModule::name) 823 .map(layer::findModule) 824 .map(Optional::get) 825 .forEach(other -> assertTrue(m.canRead(other))); 826 827 // also check parent layers 828 layer.parents().forEach(l -> testsReadsAll(m, l)); 829 } 830 831 /** 832 * Returns {@code true} if the configuration contains module mn1 833 * that reads module mn2. 834 */ 835 static boolean reads(Configuration cf, String mn1, String mn2) { 836 Optional<ResolvedModule> om = cf.findModule(mn1); 837 if (!om.isPresent()) 838 return false; 839 840 return om.get().reads().stream() 841 .map(ResolvedModule::name) 842 .anyMatch(mn2::equals); 843 } 844 845 /** 846 * Creates a JAR file, optionally with a manifest, and with the given 847 * entries. The entries will be empty in the resulting JAR file. 848 */ 849 static Path createDummyJarFile(Path jarfile, Manifest man, String... entries) 850 throws IOException 851 { 852 Path dir = Files.createTempDirectory(USER_DIR, "tmp"); 853 854 for (String entry : entries) { 855 Path file = dir.resolve(entry); 856 Path parent = file.getParent(); 857 if (parent != null) 858 Files.createDirectories(parent); 859 Files.createFile(file); 860 } 861 862 Path[] paths = Stream.of(entries).map(Paths::get).toArray(Path[]::new); 863 JarUtils.createJarFile(jarfile, man, dir, paths); 864 return jarfile; 865 } 866 867 /** 868 * Creates a JAR file and with the given entries. The entries will be empty 869 * in the resulting JAR file. 870 */ 871 static Path createDummyJarFile(Path jarfile, String... entries) 872 throws IOException 873 { 874 return createDummyJarFile(jarfile, null, entries); 875 } 876 877 }