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