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 * @bug 8142968 8166568 8166286 8170618 8168149 27 * @summary Basic test for jmod 28 * @library /lib/testlibrary 29 * @modules jdk.compiler 30 * jdk.jlink 31 * @build jdk.testlibrary.FileUtils CompilerUtils 32 * @run testng JmodTest 33 */ 34 35 import java.io.*; 36 import java.lang.module.ModuleDescriptor; 37 import java.lang.reflect.Method; 38 import java.nio.file.*; 39 import java.util.*; 40 import java.util.function.Consumer; 41 import java.util.regex.Pattern; 42 import java.util.spi.ToolProvider; 43 import java.util.stream.Stream; 44 import jdk.testlibrary.FileUtils; 45 import org.testng.annotations.BeforeTest; 46 import org.testng.annotations.Test; 47 48 import static java.io.File.pathSeparator; 49 import static java.lang.module.ModuleDescriptor.Version; 50 import static java.nio.charset.StandardCharsets.UTF_8; 51 import static java.util.stream.Collectors.toSet; 52 import static org.testng.Assert.*; 53 54 public class JmodTest { 55 56 static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod") 57 .orElseThrow(() -> 58 new RuntimeException("jmod tool not found") 59 ); 60 61 static final String TEST_SRC = System.getProperty("test.src", "."); 62 static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); 63 static final Path EXPLODED_DIR = Paths.get("build"); 64 static final Path MODS_DIR = Paths.get("jmods"); 65 66 static final String CLASSES_PREFIX = "classes/"; 67 static final String CMDS_PREFIX = "bin/"; 68 static final String LIBS_PREFIX = "native/"; 69 static final String CONFIGS_PREFIX = "conf/"; 70 71 @BeforeTest 72 public void buildExplodedModules() throws IOException { 73 if (Files.exists(EXPLODED_DIR)) 74 FileUtils.deleteFileTreeWithRetry(EXPLODED_DIR); 75 76 for (String name : new String[] { "foo"/*, "bar", "baz"*/ } ) { 77 Path dir = EXPLODED_DIR.resolve(name); 78 assertTrue(compileModule(name, dir.resolve("classes"))); 79 copyResource(SRC_DIR.resolve("foo"), 80 dir.resolve("classes"), 81 "jdk/test/foo/resources/foo.properties"); 82 createCmds(dir.resolve("bin")); 83 createLibs(dir.resolve("lib")); 84 createConfigs(dir.resolve("conf")); 85 } 86 87 if (Files.exists(MODS_DIR)) 88 FileUtils.deleteFileTreeWithRetry(MODS_DIR); 89 Files.createDirectories(MODS_DIR); 90 } 91 92 // JDK-8166286 - jmod fails on symlink to directory 93 @Test 94 public void testSymlinks() throws IOException { 95 Path apaDir = EXPLODED_DIR.resolve("apa"); 96 Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes"); 97 assertTrue(compileModule("apa", classesDir)); 98 Path libDir = apaDir.resolve("lib"); 99 createFiles(libDir, List.of("foo/bar/libfoo.so")); 100 try { 101 Path link = Files.createSymbolicLink( 102 libDir.resolve("baz"), libDir.resolve("foo").toAbsolutePath()); 103 assertTrue(Files.exists(link)); 104 } catch (UnsupportedOperationException uoe) { 105 // OS does not support symlinks. Nothing to test! 106 return; 107 } 108 109 Path jmod = MODS_DIR.resolve("apa.jmod"); 110 jmod("create", 111 "--libs=", libDir.toString(), 112 "--class-path", classesDir.toString(), 113 jmod.toString()) 114 .assertSuccess(); 115 } 116 117 // JDK-8170618 - jmod should validate if any exported or open package is missing 118 @Test 119 public void testMissingPackages() throws IOException { 120 Path apaDir = EXPLODED_DIR.resolve("apa"); 121 Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes"); 122 if (Files.exists(classesDir)) 123 FileUtils.deleteFileTreeWithRetry(classesDir); 124 assertTrue(compileModule("apa", classesDir)); 125 FileUtils.deleteFileTreeWithRetry(classesDir.resolve("jdk")); 126 Path jmod = MODS_DIR.resolve("apa.jmod"); 127 jmod("create", 128 "--class-path", classesDir.toString(), 129 jmod.toString()) 130 .assertFailure() 131 .resultChecker(r -> { 132 assertContains(r.output, "Packages that are exported or open in apa are not present: [jdk.test.apa]"); 133 }); 134 if (Files.exists(classesDir)) 135 FileUtils.deleteFileTreeWithRetry(classesDir); 136 } 137 138 @Test 139 public void testList() throws IOException { 140 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 141 jmod("create", 142 "--class-path", cp, 143 MODS_DIR.resolve("foo.jmod").toString()) 144 .assertSuccess(); 145 146 jmod("list", 147 MODS_DIR.resolve("foo.jmod").toString()) 148 .assertSuccess() 149 .resultChecker(r -> { 150 // asserts dependent on the exact contents of foo 151 assertContains(r.output, CLASSES_PREFIX + "module-info.class"); 152 assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class"); 153 assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); 154 assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); 155 }); 156 } 157 158 @Test 159 public void testExtractCWD() throws IOException { 160 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 161 jmod("create", 162 "--class-path", cp.toString(), 163 MODS_DIR.resolve("fooExtractCWD.jmod").toString()) 164 .assertSuccess(); 165 166 jmod("extract", 167 MODS_DIR.resolve("fooExtractCWD.jmod").toString()) 168 .assertSuccess() 169 .resultChecker(r -> { 170 // module-info should exist, but jmod will have added its Packages attr. 171 assertTrue(Files.exists(Paths.get("classes/module-info.class"))); 172 assertSameContent(cp.resolve("jdk/test/foo/Foo.class"), 173 Paths.get("classes/jdk/test/foo/Foo.class")); 174 assertSameContent(cp.resolve("jdk/test/foo/internal/Message.class"), 175 Paths.get("classes/jdk/test/foo/internal/Message.class")); 176 assertSameContent(cp.resolve("jdk/test/foo/resources/foo.properties"), 177 Paths.get("classes/jdk/test/foo/resources/foo.properties")); 178 }); 179 } 180 181 @Test 182 public void testExtractDir() throws IOException { 183 if (Files.exists(Paths.get("extractTestDir"))) 184 FileUtils.deleteFileTreeWithRetry(Paths.get("extractTestDir")); 185 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 186 Path bp = EXPLODED_DIR.resolve("foo").resolve("bin"); 187 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 188 Path cf = EXPLODED_DIR.resolve("foo").resolve("conf"); 189 190 jmod("create", 191 "--conf", cf.toString(), 192 "--cmds", bp.toString(), 193 "--libs", lp.toString(), 194 "--class-path", cp.toString(), 195 MODS_DIR.resolve("fooExtractDir.jmod").toString()) 196 .assertSuccess(); 197 198 jmod("extract", 199 "--dir", "extractTestDir", 200 MODS_DIR.resolve("fooExtractDir.jmod").toString()) 201 .assertSuccess(); 202 203 jmod("extract", 204 "--dir", "extractTestDir", 205 MODS_DIR.resolve("fooExtractDir.jmod").toString()) 206 .assertSuccess() 207 .resultChecker(r -> { 208 // check a sample of the extracted files 209 Path p = Paths.get("extractTestDir"); 210 assertTrue(Files.exists(p.resolve("classes/module-info.class"))); 211 assertSameContent(cp.resolve("jdk/test/foo/Foo.class"), 212 p.resolve("classes/jdk/test/foo/Foo.class")); 213 assertSameContent(bp.resolve("first"), 214 p.resolve(CMDS_PREFIX).resolve("first")); 215 assertSameContent(lp.resolve("first.so"), 216 p.resolve(LIBS_PREFIX).resolve("second.so")); 217 assertSameContent(cf.resolve("second.cfg"), 218 p.resolve(CONFIGS_PREFIX).resolve("second.cfg")); 219 }); 220 } 221 222 @Test 223 public void testMainClass() throws IOException { 224 Path jmod = MODS_DIR.resolve("fooMainClass.jmod"); 225 FileUtils.deleteFileIfExistsWithRetry(jmod); 226 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 227 228 jmod("create", 229 "--class-path", cp, 230 "--main-class", "jdk.test.foo.Foo", 231 jmod.toString()) 232 .assertSuccess() 233 .resultChecker(r -> { 234 Optional<String> omc = getModuleDescriptor(jmod).mainClass(); 235 assertTrue(omc.isPresent()); 236 assertEquals(omc.get(), "jdk.test.foo.Foo"); 237 }); 238 } 239 240 @Test 241 public void testModuleVersion() throws IOException { 242 Path jmod = MODS_DIR.resolve("fooVersion.jmod"); 243 FileUtils.deleteFileIfExistsWithRetry(jmod); 244 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 245 246 jmod("create", 247 "--class-path", cp, 248 "--module-version", "5.4.3", 249 jmod.toString()) 250 .assertSuccess() 251 .resultChecker(r -> { 252 Optional<Version> ov = getModuleDescriptor(jmod).version(); 253 assertTrue(ov.isPresent()); 254 assertEquals(ov.get().toString(), "5.4.3"); 255 }); 256 } 257 258 @Test 259 public void testConfig() throws IOException { 260 Path jmod = MODS_DIR.resolve("fooConfig.jmod"); 261 FileUtils.deleteFileIfExistsWithRetry(jmod); 262 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 263 Path cf = EXPLODED_DIR.resolve("foo").resolve("conf"); 264 265 jmod("create", 266 "--class-path", cp.toString(), 267 "--config", cf.toString(), 268 jmod.toString()) 269 .assertSuccess() 270 .resultChecker(r -> { 271 try (Stream<String> s1 = findFiles(cf).map(p -> CONFIGS_PREFIX + p); 272 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { 273 Set<String> expectedFilenames = Stream.concat(s1, s2) 274 .collect(toSet()); 275 assertJmodContent(jmod, expectedFilenames); 276 } 277 }); 278 } 279 280 @Test 281 public void testCmds() throws IOException { 282 Path jmod = MODS_DIR.resolve("fooCmds.jmod"); 283 FileUtils.deleteFileIfExistsWithRetry(jmod); 284 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 285 Path bp = EXPLODED_DIR.resolve("foo").resolve("bin"); 286 287 jmod("create", 288 "--cmds", bp.toString(), 289 "--class-path", cp.toString(), 290 jmod.toString()) 291 .assertSuccess() 292 .resultChecker(r -> { 293 try (Stream<String> s1 = findFiles(bp).map(p -> CMDS_PREFIX + p); 294 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { 295 Set<String> expectedFilenames = Stream.concat(s1,s2) 296 .collect(toSet()); 297 assertJmodContent(jmod, expectedFilenames); 298 } 299 }); 300 } 301 302 @Test 303 public void testLibs() throws IOException { 304 Path jmod = MODS_DIR.resolve("fooLibs.jmod"); 305 FileUtils.deleteFileIfExistsWithRetry(jmod); 306 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 307 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 308 309 jmod("create", 310 "--libs=", lp.toString(), 311 "--class-path", cp.toString(), 312 jmod.toString()) 313 .assertSuccess() 314 .resultChecker(r -> { 315 try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); 316 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { 317 Set<String> expectedFilenames = Stream.concat(s1,s2) 318 .collect(toSet()); 319 assertJmodContent(jmod, expectedFilenames); 320 } 321 }); 322 } 323 324 @Test 325 public void testAll() throws IOException { 326 Path jmod = MODS_DIR.resolve("fooAll.jmod"); 327 FileUtils.deleteFileIfExistsWithRetry(jmod); 328 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 329 Path bp = EXPLODED_DIR.resolve("foo").resolve("bin"); 330 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 331 Path cf = EXPLODED_DIR.resolve("foo").resolve("conf"); 332 333 jmod("create", 334 "--conf", cf.toString(), 335 "--cmds=", bp.toString(), 336 "--libs=", lp.toString(), 337 "--class-path", cp.toString(), 338 jmod.toString()) 339 .assertSuccess() 340 .resultChecker(r -> { 341 try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); 342 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p); 343 Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p); 344 Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) { 345 Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2), 346 Stream.concat(s3, s4)) 347 .collect(toSet()); 348 assertJmodContent(jmod, expectedFilenames); 349 } 350 }); 351 } 352 353 @Test 354 public void testExcludes() throws IOException { 355 Path jmod = MODS_DIR.resolve("fooLibs.jmod"); 356 FileUtils.deleteFileIfExistsWithRetry(jmod); 357 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 358 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 359 360 jmod("create", 361 "--libs=", lp.toString(), 362 "--class-path", cp.toString(), 363 "--exclude", "**internal**", 364 "--exclude", "first.so", 365 jmod.toString()) 366 .assertSuccess() 367 .resultChecker(r -> { 368 Set<String> expectedFilenames = new HashSet<>(); 369 expectedFilenames.add(CLASSES_PREFIX + "module-info.class"); 370 expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/Foo.class"); 371 expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); 372 expectedFilenames.add(LIBS_PREFIX + "second.so"); 373 expectedFilenames.add(LIBS_PREFIX + "third/third.so"); 374 assertJmodContent(jmod, expectedFilenames); 375 376 Set<String> unexpectedFilenames = new HashSet<>(); 377 unexpectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); 378 unexpectedFilenames.add(LIBS_PREFIX + "first.so"); 379 assertJmodDoesNotContain(jmod, unexpectedFilenames); 380 }); 381 } 382 383 @Test 384 public void describe() throws IOException { 385 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 386 jmod("create", 387 "--class-path", cp, 388 MODS_DIR.resolve("describeFoo.jmod").toString()) 389 .assertSuccess(); 390 391 jmod("describe", 392 MODS_DIR.resolve("describeFoo.jmod").toString()) 393 .assertSuccess() 394 .resultChecker(r -> { 395 // Expect similar output: "foo, requires mandated java.base 396 // exports jdk.test.foo, contains jdk.test.foo.internal" 397 Pattern p = Pattern.compile("\\s+foo\\s+requires\\s+mandated\\s+java.base"); 398 assertTrue(p.matcher(r.output).find(), 399 "Expecting to find \"foo, requires java.base\"" + 400 "in output, but did not: [" + r.output + "]"); 401 p = Pattern.compile( 402 "exports\\s+jdk.test.foo\\s+contains\\s+jdk.test.foo.internal"); 403 assertTrue(p.matcher(r.output).find(), 404 "Expecting to find \"exports ..., contains ...\"" + 405 "in output, but did not: [" + r.output + "]"); 406 }); 407 } 408 409 @Test 410 public void testDuplicateEntries() throws IOException { 411 Path jmod = MODS_DIR.resolve("testDuplicates.jmod"); 412 FileUtils.deleteFileIfExistsWithRetry(jmod); 413 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 414 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 415 416 jmod("create", 417 "--class-path", cp + pathSeparator + cp, 418 jmod.toString()) 419 .assertSuccess() 420 .resultChecker(r -> 421 assertContains(r.output, "Warning: ignoring duplicate entry") 422 ); 423 424 FileUtils.deleteFileIfExistsWithRetry(jmod); 425 jmod("create", 426 "--class-path", cp, 427 "--libs", lp.toString() + pathSeparator + lp.toString(), 428 jmod.toString()) 429 .assertSuccess() 430 .resultChecker(r -> 431 assertContains(r.output, "Warning: ignoring duplicate entry") 432 ); 433 } 434 435 @Test 436 public void testIgnoreModuleInfoInOtherSections() throws IOException { 437 Path jmod = MODS_DIR.resolve("testIgnoreModuleInfoInOtherSections.jmod"); 438 FileUtils.deleteFileIfExistsWithRetry(jmod); 439 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 440 441 jmod("create", 442 "--class-path", cp, 443 "--libs", cp, 444 jmod.toString()) 445 .assertSuccess() 446 .resultChecker(r -> 447 assertContains(r.output, "Warning: ignoring entry") 448 ); 449 450 FileUtils.deleteFileIfExistsWithRetry(jmod); 451 jmod("create", 452 "--class-path", cp, 453 "--cmds", cp, 454 jmod.toString()) 455 .assertSuccess() 456 .resultChecker(r -> 457 assertContains(r.output, "Warning: ignoring entry") 458 ); 459 } 460 461 @Test 462 public void testLastOneWins() throws IOException { 463 Path workDir = Paths.get("lastOneWins"); 464 if (Files.exists(workDir)) 465 FileUtils.deleteFileTreeWithRetry(workDir); 466 Files.createDirectory(workDir); 467 Path jmod = MODS_DIR.resolve("lastOneWins.jmod"); 468 FileUtils.deleteFileIfExistsWithRetry(jmod); 469 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 470 Path bp = EXPLODED_DIR.resolve("foo").resolve("bin"); 471 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 472 Path cf = EXPLODED_DIR.resolve("foo").resolve("conf"); 473 474 Path shouldNotBeAdded = workDir.resolve("shouldNotBeAdded"); 475 Files.createDirectory(shouldNotBeAdded); 476 Files.write(shouldNotBeAdded.resolve("aFile"), "hello".getBytes(UTF_8)); 477 478 // Pairs of options. For options with required arguments the last one 479 // should win ( first should be effectively ignored, but may still be 480 // validated ). 481 jmod("create", 482 "--conf", shouldNotBeAdded.toString(), 483 "--conf", cf.toString(), 484 "--cmds", shouldNotBeAdded.toString(), 485 "--cmds", bp.toString(), 486 "--libs", shouldNotBeAdded.toString(), 487 "--libs", lp.toString(), 488 "--class-path", shouldNotBeAdded.toString(), 489 "--class-path", cp.toString(), 490 "--main-class", "does.NotExist", 491 "--main-class", "jdk.test.foo.Foo", 492 "--module-version", "00001", 493 "--module-version", "5.4.3", 494 "--do-not-resolve-by-default", 495 "--do-not-resolve-by-default", 496 "--warn-if-resolved=incubating", 497 "--warn-if-resolved=deprecated", 498 MODS_DIR.resolve("lastOneWins.jmod").toString()) 499 .assertSuccess() 500 .resultChecker(r -> { 501 ModuleDescriptor md = getModuleDescriptor(jmod); 502 Optional<String> omc = md.mainClass(); 503 assertTrue(omc.isPresent()); 504 assertEquals(omc.get(), "jdk.test.foo.Foo"); 505 Optional<Version> ov = md.version(); 506 assertTrue(ov.isPresent()); 507 assertEquals(ov.get().toString(), "5.4.3"); 508 509 try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); 510 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p); 511 Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p); 512 Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) { 513 Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2), 514 Stream.concat(s3, s4)) 515 .collect(toSet()); 516 assertJmodContent(jmod, expectedFilenames); 517 } 518 }); 519 520 jmod("extract", 521 "--dir", "blah", 522 "--dir", "lastOneWinsExtractDir", 523 jmod.toString()) 524 .assertSuccess() 525 .resultChecker(r -> { 526 assertTrue(Files.exists(Paths.get("lastOneWinsExtractDir"))); 527 assertTrue(Files.notExists(Paths.get("blah"))); 528 }); 529 } 530 531 @Test 532 public void testPackagesAttribute() throws IOException { 533 Path jmod = MODS_DIR.resolve("foo.jmod"); 534 FileUtils.deleteFileIfExistsWithRetry(jmod); 535 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 536 537 Set<String> expectedPackages = Set.of("jdk.test.foo", 538 "jdk.test.foo.internal", 539 "jdk.test.foo.resources"); 540 541 jmod("create", 542 "--class-path", cp, 543 jmod.toString()) 544 .assertSuccess() 545 .resultChecker(r -> { 546 Set<String> pkgs = getModuleDescriptor(jmod).packages(); 547 assertEquals(pkgs, expectedPackages); 548 }); 549 } 550 551 @Test 552 public void testVersion() { 553 jmod("--version") 554 .assertSuccess() 555 .resultChecker(r -> { 556 assertContains(r.output, System.getProperty("java.version")); 557 }); 558 } 559 560 @Test 561 public void testHelp() { 562 jmod("--help") 563 .assertSuccess() 564 .resultChecker(r -> { 565 assertTrue(r.output.startsWith("Usage: jmod"), "Help not printed"); 566 assertFalse(r.output.contains("--do-not-resolve-by-default")); 567 assertFalse(r.output.contains("--warn-if-resolved")); 568 }); 569 } 570 571 @Test 572 public void testHelpExtra() { 573 jmod("--help-extra") 574 .assertSuccess() 575 .resultChecker(r -> { 576 assertTrue(r.output.startsWith("Usage: jmod"), "Extra help not printed"); 577 assertContains(r.output, "--do-not-resolve-by-default"); 578 assertContains(r.output, "--warn-if-resolved"); 579 }); 580 } 581 582 @Test 583 public void testTmpFileRemoved() throws IOException { 584 // Implementation detail: jmod tool creates <jmod-file>.tmp 585 // Ensure that it is removed in the event of a failure. 586 // The failure in this case is a class in the unnamed package. 587 588 String filename = "testTmpFileRemoved.jmod"; 589 Path jmod = MODS_DIR.resolve(filename); 590 591 // clean up files 592 FileUtils.deleteFileIfExistsWithRetry(jmod); 593 findTmpFiles(filename).forEach(tmp -> { 594 try { 595 FileUtils.deleteFileIfExistsWithRetry(tmp); 596 } catch (IOException e) { 597 throw new UncheckedIOException(e); 598 } 599 }); 600 601 String cp = EXPLODED_DIR.resolve("foo").resolve("classes") + File.pathSeparator + 602 EXPLODED_DIR.resolve("foo").resolve("classes") 603 .resolve("jdk").resolve("test").resolve("foo").toString(); 604 605 jmod("create", 606 "--class-path", cp, 607 jmod.toString()) 608 .assertFailure() 609 .resultChecker(r -> { 610 assertContains(r.output, "unnamed package"); 611 Set<Path> tmpfiles = findTmpFiles(filename).collect(toSet()); 612 assertTrue(tmpfiles.isEmpty(), "Unexpected tmp file:" + tmpfiles); 613 }); 614 } 615 616 private Stream<Path> findTmpFiles(String prefix) { 617 try { 618 Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir")); 619 return Files.find(tmpdir, 1, (p, attrs) -> 620 p.getFileName().toString().startsWith(prefix) 621 && p.getFileName().toString().endsWith(".tmp")); 622 } catch (IOException e) { 623 throw new UncheckedIOException(e); 624 } 625 } 626 627 // --- 628 629 static boolean compileModule(String name, Path dest) throws IOException { 630 return CompilerUtils.compile(SRC_DIR.resolve(name), dest); 631 } 632 633 static void assertContains(String output, String subString) { 634 if (output.contains(subString)) 635 assertTrue(true); 636 else 637 assertTrue(false,"Expected to find [" + subString + "], in output [" 638 + output + "]" + "\n"); 639 } 640 641 static ModuleDescriptor getModuleDescriptor(Path jmod) { 642 ClassLoader cl = ClassLoader.getSystemClassLoader(); 643 try (FileSystem fs = FileSystems.newFileSystem(jmod, cl)) { 644 String p = "/classes/module-info.class"; 645 try (InputStream is = Files.newInputStream(fs.getPath(p))) { 646 return ModuleDescriptor.read(is); 647 } 648 } catch (IOException ioe) { 649 throw new UncheckedIOException(ioe); 650 } 651 } 652 653 static Stream<String> findFiles(Path dir) { 654 try { 655 return Files.find(dir, Integer.MAX_VALUE, (p, a) -> a.isRegularFile()) 656 .map(dir::relativize) 657 .map(Path::toString) 658 .map(p -> p.replace(File.separator, "/")); 659 } catch (IOException x) { 660 throw new UncheckedIOException(x); 661 } 662 } 663 664 static Set<String> getJmodContent(Path jmod) { 665 JmodResult r = jmod("list", jmod.toString()).assertSuccess(); 666 return Stream.of(r.output.split("\r?\n")).collect(toSet()); 667 } 668 669 static void assertJmodContent(Path jmod, Set<String> expected) { 670 Set<String> actual = getJmodContent(jmod); 671 if (!Objects.equals(actual, expected)) { 672 Set<String> unexpected = new HashSet<>(actual); 673 unexpected.removeAll(expected); 674 Set<String> notFound = new HashSet<>(expected); 675 notFound.removeAll(actual); 676 StringBuilder sb = new StringBuilder(); 677 sb.append("Unexpected but found:\n"); 678 unexpected.forEach(s -> sb.append("\t" + s + "\n")); 679 sb.append("Expected but not found:\n"); 680 notFound.forEach(s -> sb.append("\t" + s + "\n")); 681 assertTrue(false, "Jmod content check failed.\n" + sb.toString()); 682 } 683 } 684 685 static void assertJmodDoesNotContain(Path jmod, Set<String> unexpectedNames) { 686 Set<String> actual = getJmodContent(jmod); 687 Set<String> unexpected = new HashSet<>(); 688 for (String name : unexpectedNames) { 689 if (actual.contains(name)) 690 unexpected.add(name); 691 } 692 if (!unexpected.isEmpty()) { 693 StringBuilder sb = new StringBuilder(); 694 for (String s : unexpected) 695 sb.append("Unexpected but found: " + s + "\n"); 696 sb.append("In :"); 697 for (String s : actual) 698 sb.append("\t" + s + "\n"); 699 assertTrue(false, "Jmod content check failed.\n" + sb.toString()); 700 } 701 } 702 703 static void assertSameContent(Path p1, Path p2) { 704 try { 705 byte[] ba1 = Files.readAllBytes(p1); 706 byte[] ba2 = Files.readAllBytes(p2); 707 assertEquals(ba1, ba2); 708 } catch (IOException x) { 709 throw new UncheckedIOException(x); 710 } 711 } 712 713 static JmodResult jmod(String... args) { 714 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 715 PrintStream ps = new PrintStream(baos); 716 System.out.println("jmod " + Arrays.asList(args)); 717 int ec = JMOD_TOOL.run(ps, ps, args); 718 return new JmodResult(ec, new String(baos.toByteArray(), UTF_8)); 719 } 720 721 static class JmodResult { 722 final int exitCode; 723 final String output; 724 725 JmodResult(int exitValue, String output) { 726 this.exitCode = exitValue; 727 this.output = output; 728 } 729 JmodResult assertSuccess() { assertTrue(exitCode == 0, output); return this; } 730 JmodResult assertFailure() { assertTrue(exitCode != 0, output); return this; } 731 JmodResult resultChecker(Consumer<JmodResult> r) { r.accept(this); return this; } 732 } 733 734 static void createCmds(Path dir) throws IOException { 735 List<String> files = Arrays.asList( 736 "first", "second", "third" + File.separator + "third"); 737 createFiles(dir, files); 738 } 739 740 static void createLibs(Path dir) throws IOException { 741 List<String> files = Arrays.asList( 742 "first.so", "second.so", "third" + File.separator + "third.so"); 743 createFiles(dir, files); 744 } 745 746 static void createConfigs(Path dir) throws IOException { 747 List<String> files = Arrays.asList( 748 "first.cfg", "second.cfg", "third" + File.separator + "third.cfg"); 749 createFiles(dir, files); 750 } 751 752 static void createFiles(Path dir, List<String> filenames) throws IOException { 753 for (String name : filenames) { 754 Path file = dir.resolve(name); 755 Files.createDirectories(file.getParent()); 756 Files.createFile(file); 757 try (OutputStream os = Files.newOutputStream(file)) { 758 os.write("blahblahblah".getBytes(UTF_8)); 759 } 760 } 761 } 762 763 static void copyResource(Path srcDir, Path dir, String resource) throws IOException { 764 Path dest = dir.resolve(resource); 765 Files.deleteIfExists(dest); 766 767 Files.createDirectories(dest.getParent()); 768 Files.copy(srcDir.resolve(resource), dest); 769 } 770 771 // Standalone entry point. 772 public static void main(String[] args) throws Throwable { 773 JmodTest test = new JmodTest(); 774 test.buildExplodedModules(); 775 for (Method m : JmodTest.class.getDeclaredMethods()) { 776 if (m.getAnnotation(Test.class) != null) { 777 System.out.println("Invoking " + m.getName()); 778 m.invoke(test); 779 } 780 } 781 } 782 }