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 * @bug 8142968 8166568 8166286 8170618 8168149 27 * @summary Basic test for jmod 28 * @library /test/lib 29 * @modules jdk.compiler 30 * jdk.jlink 31 * @build jdk.test.lib.compiler.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.Stream; 44 import jdk.test.lib.compiler.CompilerUtils; 45 import jdk.test.lib.util.FileUtils; 46 import org.testng.annotations.BeforeTest; 47 import org.testng.annotations.Test; 48 49 import static java.io.File.pathSeparator; 50 import static java.lang.module.ModuleDescriptor.Version; 51 import static java.nio.charset.StandardCharsets.UTF_8; 52 import static java.util.stream.Collectors.toSet; 53 import static org.testng.Assert.*; 54 55 public class JmodTest { 56 57 static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod") 58 .orElseThrow(() -> 59 new RuntimeException("jmod tool not found") 60 ); 61 62 static final String TEST_SRC = System.getProperty("test.src", "."); 63 static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); 64 static final Path EXPLODED_DIR = Paths.get("build"); 65 static final Path MODS_DIR = Paths.get("jmods"); 66 67 static final String CLASSES_PREFIX = "classes/"; 68 static final String CMDS_PREFIX = "bin/"; 69 static final String LIBS_PREFIX = "lib/"; 70 static final String CONFIGS_PREFIX = "conf/"; 71 72 @BeforeTest 73 public void buildExplodedModules() throws IOException { 74 if (Files.exists(EXPLODED_DIR)) 75 FileUtils.deleteFileTreeWithRetry(EXPLODED_DIR); 76 77 for (String name : new String[] { "foo"/*, "bar", "baz"*/ } ) { 78 Path dir = EXPLODED_DIR.resolve(name); 79 assertTrue(compileModule(name, dir.resolve("classes"))); 80 copyResource(SRC_DIR.resolve("foo"), 81 dir.resolve("classes"), 82 "jdk/test/foo/resources/foo.properties"); 83 createCmds(dir.resolve("bin")); 84 createLibs(dir.resolve("lib")); 85 createConfigs(dir.resolve("conf")); 86 } 87 88 if (Files.exists(MODS_DIR)) 89 FileUtils.deleteFileTreeWithRetry(MODS_DIR); 90 Files.createDirectories(MODS_DIR); 91 } 92 93 // JDK-8166286 - jmod fails on symlink to directory 94 @Test 95 public void testSymlinks() throws IOException { 96 Path apaDir = EXPLODED_DIR.resolve("apa"); 97 Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes"); 98 assertTrue(compileModule("apa", classesDir)); 99 Path libDir = apaDir.resolve("lib"); 100 createFiles(libDir, List.of("foo/bar/libfoo.so")); 101 try { 102 Path link = Files.createSymbolicLink( 103 libDir.resolve("baz"), libDir.resolve("foo").toAbsolutePath()); 104 assertTrue(Files.exists(link)); 105 } catch (UnsupportedOperationException uoe) { 106 // OS does not support symlinks. Nothing to test! 107 return; 108 } 109 110 Path jmod = MODS_DIR.resolve("apa.jmod"); 111 jmod("create", 112 "--libs=", libDir.toString(), 113 "--class-path", classesDir.toString(), 114 jmod.toString()) 115 .assertSuccess(); 116 } 117 118 // JDK-8170618 - jmod should validate if any exported or open package is missing 119 @Test 120 public void testMissingPackages() throws IOException { 121 Path apaDir = EXPLODED_DIR.resolve("apa"); 122 Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes"); 123 if (Files.exists(classesDir)) 124 FileUtils.deleteFileTreeWithRetry(classesDir); 125 assertTrue(compileModule("apa", classesDir)); 126 FileUtils.deleteFileTreeWithRetry(classesDir.resolve("jdk")); 127 Path jmod = MODS_DIR.resolve("apa.jmod"); 128 jmod("create", 129 "--class-path", classesDir.toString(), 130 jmod.toString()) 131 .assertFailure() 132 .resultChecker(r -> { 133 assertContains(r.output, "Packages that are exported or open in apa are not present: [jdk.test.apa]"); 134 }); 135 if (Files.exists(classesDir)) 136 FileUtils.deleteFileTreeWithRetry(classesDir); 137 } 138 139 @Test 140 public void testList() throws IOException { 141 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 142 jmod("create", 143 "--class-path", cp, 144 MODS_DIR.resolve("foo.jmod").toString()) 145 .assertSuccess(); 146 147 jmod("list", 148 MODS_DIR.resolve("foo.jmod").toString()) 149 .assertSuccess() 150 .resultChecker(r -> { 151 // asserts dependent on the exact contents of foo 152 assertContains(r.output, CLASSES_PREFIX + "module-info.class"); 153 assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class"); 154 assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); 155 assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); 156 }); 157 } 158 159 @Test 160 public void testExtractCWD() throws IOException { 161 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 162 jmod("create", 163 "--class-path", cp.toString(), 164 MODS_DIR.resolve("fooExtractCWD.jmod").toString()) 165 .assertSuccess(); 166 167 jmod("extract", 168 MODS_DIR.resolve("fooExtractCWD.jmod").toString()) 169 .assertSuccess() 170 .resultChecker(r -> { 171 // module-info should exist, but jmod will have added its Packages attr. 172 assertTrue(Files.exists(Paths.get("classes/module-info.class"))); 173 assertSameContent(cp.resolve("jdk/test/foo/Foo.class"), 174 Paths.get("classes/jdk/test/foo/Foo.class")); 175 assertSameContent(cp.resolve("jdk/test/foo/internal/Message.class"), 176 Paths.get("classes/jdk/test/foo/internal/Message.class")); 177 assertSameContent(cp.resolve("jdk/test/foo/resources/foo.properties"), 178 Paths.get("classes/jdk/test/foo/resources/foo.properties")); 179 }); 180 } 181 182 @Test 183 public void testExtractDir() throws IOException { 184 if (Files.exists(Paths.get("extractTestDir"))) 185 FileUtils.deleteFileTreeWithRetry(Paths.get("extractTestDir")); 186 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 187 Path bp = EXPLODED_DIR.resolve("foo").resolve("bin"); 188 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 189 Path cf = EXPLODED_DIR.resolve("foo").resolve("conf"); 190 191 jmod("create", 192 "--conf", cf.toString(), 193 "--cmds", bp.toString(), 194 "--libs", lp.toString(), 195 "--class-path", cp.toString(), 196 MODS_DIR.resolve("fooExtractDir.jmod").toString()) 197 .assertSuccess(); 198 199 jmod("extract", 200 "--dir", "extractTestDir", 201 MODS_DIR.resolve("fooExtractDir.jmod").toString()) 202 .assertSuccess(); 203 204 jmod("extract", 205 "--dir", "extractTestDir", 206 MODS_DIR.resolve("fooExtractDir.jmod").toString()) 207 .assertSuccess() 208 .resultChecker(r -> { 209 // check a sample of the extracted files 210 Path p = Paths.get("extractTestDir"); 211 assertTrue(Files.exists(p.resolve("classes/module-info.class"))); 212 assertSameContent(cp.resolve("jdk/test/foo/Foo.class"), 213 p.resolve("classes/jdk/test/foo/Foo.class")); 214 assertSameContent(bp.resolve("first"), 215 p.resolve(CMDS_PREFIX).resolve("first")); 216 assertSameContent(lp.resolve("first.so"), 217 p.resolve(LIBS_PREFIX).resolve("second.so")); 218 assertSameContent(cf.resolve("second.cfg"), 219 p.resolve(CONFIGS_PREFIX).resolve("second.cfg")); 220 }); 221 } 222 223 @Test 224 public void testMainClass() throws IOException { 225 Path jmod = MODS_DIR.resolve("fooMainClass.jmod"); 226 FileUtils.deleteFileIfExistsWithRetry(jmod); 227 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 228 229 jmod("create", 230 "--class-path", cp, 231 "--main-class", "jdk.test.foo.Foo", 232 jmod.toString()) 233 .assertSuccess() 234 .resultChecker(r -> { 235 Optional<String> omc = getModuleDescriptor(jmod).mainClass(); 236 assertTrue(omc.isPresent()); 237 assertEquals(omc.get(), "jdk.test.foo.Foo"); 238 }); 239 } 240 241 @Test 242 public void testModuleVersion() throws IOException { 243 Path jmod = MODS_DIR.resolve("fooVersion.jmod"); 244 FileUtils.deleteFileIfExistsWithRetry(jmod); 245 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 246 247 jmod("create", 248 "--class-path", cp, 249 "--module-version", "5.4.3", 250 jmod.toString()) 251 .assertSuccess() 252 .resultChecker(r -> { 253 Optional<Version> ov = getModuleDescriptor(jmod).version(); 254 assertTrue(ov.isPresent()); 255 assertEquals(ov.get().toString(), "5.4.3"); 256 }); 257 } 258 259 @Test 260 public void testConfig() throws IOException { 261 Path jmod = MODS_DIR.resolve("fooConfig.jmod"); 262 FileUtils.deleteFileIfExistsWithRetry(jmod); 263 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 264 Path cf = EXPLODED_DIR.resolve("foo").resolve("conf"); 265 266 jmod("create", 267 "--class-path", cp.toString(), 268 "--config", cf.toString(), 269 jmod.toString()) 270 .assertSuccess() 271 .resultChecker(r -> { 272 try (Stream<String> s1 = findFiles(cf).map(p -> CONFIGS_PREFIX + p); 273 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { 274 Set<String> expectedFilenames = Stream.concat(s1, s2) 275 .collect(toSet()); 276 assertJmodContent(jmod, expectedFilenames); 277 } 278 }); 279 } 280 281 @Test 282 public void testCmds() throws IOException { 283 Path jmod = MODS_DIR.resolve("fooCmds.jmod"); 284 FileUtils.deleteFileIfExistsWithRetry(jmod); 285 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 286 Path bp = EXPLODED_DIR.resolve("foo").resolve("bin"); 287 288 jmod("create", 289 "--cmds", bp.toString(), 290 "--class-path", cp.toString(), 291 jmod.toString()) 292 .assertSuccess() 293 .resultChecker(r -> { 294 try (Stream<String> s1 = findFiles(bp).map(p -> CMDS_PREFIX + p); 295 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { 296 Set<String> expectedFilenames = Stream.concat(s1,s2) 297 .collect(toSet()); 298 assertJmodContent(jmod, expectedFilenames); 299 } 300 }); 301 } 302 303 @Test 304 public void testLibs() throws IOException { 305 Path jmod = MODS_DIR.resolve("fooLibs.jmod"); 306 FileUtils.deleteFileIfExistsWithRetry(jmod); 307 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 308 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 309 310 jmod("create", 311 "--libs=", lp.toString(), 312 "--class-path", cp.toString(), 313 jmod.toString()) 314 .assertSuccess() 315 .resultChecker(r -> { 316 try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); 317 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) { 318 Set<String> expectedFilenames = Stream.concat(s1,s2) 319 .collect(toSet()); 320 assertJmodContent(jmod, expectedFilenames); 321 } 322 }); 323 } 324 325 @Test 326 public void testAll() throws IOException { 327 Path jmod = MODS_DIR.resolve("fooAll.jmod"); 328 FileUtils.deleteFileIfExistsWithRetry(jmod); 329 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 330 Path bp = EXPLODED_DIR.resolve("foo").resolve("bin"); 331 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 332 Path cf = EXPLODED_DIR.resolve("foo").resolve("conf"); 333 334 jmod("create", 335 "--conf", cf.toString(), 336 "--cmds=", bp.toString(), 337 "--libs=", lp.toString(), 338 "--class-path", cp.toString(), 339 jmod.toString()) 340 .assertSuccess() 341 .resultChecker(r -> { 342 try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p); 343 Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p); 344 Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p); 345 Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) { 346 Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2), 347 Stream.concat(s3, s4)) 348 .collect(toSet()); 349 assertJmodContent(jmod, expectedFilenames); 350 } 351 }); 352 } 353 354 @Test 355 public void testExcludes() throws IOException { 356 Path jmod = MODS_DIR.resolve("fooLibs.jmod"); 357 FileUtils.deleteFileIfExistsWithRetry(jmod); 358 Path cp = EXPLODED_DIR.resolve("foo").resolve("classes"); 359 Path lp = EXPLODED_DIR.resolve("foo").resolve("lib"); 360 361 jmod("create", 362 "--libs=", lp.toString(), 363 "--class-path", cp.toString(), 364 "--exclude", "**internal**", 365 "--exclude", "first.so", 366 jmod.toString()) 367 .assertSuccess() 368 .resultChecker(r -> { 369 Set<String> expectedFilenames = new HashSet<>(); 370 expectedFilenames.add(CLASSES_PREFIX + "module-info.class"); 371 expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/Foo.class"); 372 expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); 373 expectedFilenames.add(LIBS_PREFIX + "second.so"); 374 expectedFilenames.add(LIBS_PREFIX + "third/third.so"); 375 assertJmodContent(jmod, expectedFilenames); 376 377 Set<String> unexpectedFilenames = new HashSet<>(); 378 unexpectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); 379 unexpectedFilenames.add(LIBS_PREFIX + "first.so"); 380 assertJmodDoesNotContain(jmod, unexpectedFilenames); 381 }); 382 } 383 384 @Test 385 public void describe() throws IOException { 386 String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); 387 jmod("create", 388 "--class-path", cp, 389 MODS_DIR.resolve("describeFoo.jmod").toString()) 390 .assertSuccess(); 391 392 jmod("describe", 393 MODS_DIR.resolve("describeFoo.jmod").toString()) 394 .assertSuccess() 395 .resultChecker(r -> { 396 // Expect similar output: "foo... exports jdk.test.foo ... 397 // ... requires java.base mandated... contains jdk.test.foo.internal" 398 Pattern p = Pattern.compile("foo\\s+exports\\s+jdk.test.foo"); 399 assertTrue(p.matcher(r.output).find(), 400 "Expecting to find \"foo... exports jdk.test.foo\"" + 401 "in output, but did not: [" + r.output + "]"); 402 p = Pattern.compile( 403 "requires\\s+java.base\\s+mandated\\s+contains\\s+jdk.test.foo.internal"); 404 assertTrue(p.matcher(r.output).find(), 405 "Expecting to find \"requires java.base mandated..., " + 406 "contains jdk.test.foo.internal ...\"" + 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 Path jmod = MODS_DIR.resolve("testTmpFileRemoved.jmod"); 591 Path tmp = MODS_DIR.resolve(".testTmpFileRemoved.jmod.tmp"); 592 FileUtils.deleteFileIfExistsWithRetry(jmod); 593 FileUtils.deleteFileIfExistsWithRetry(tmp); 594 String cp = EXPLODED_DIR.resolve("foo").resolve("classes") + File.pathSeparator + 595 EXPLODED_DIR.resolve("foo").resolve("classes") 596 .resolve("jdk").resolve("test").resolve("foo").toString(); 597 598 jmod("create", 599 "--class-path", cp, 600 jmod.toString()) 601 .assertFailure() 602 .resultChecker(r -> { 603 assertContains(r.output, "unnamed package"); 604 assertTrue(Files.notExists(tmp), "Unexpected tmp file:" + tmp); 605 }); 606 } 607 608 // --- 609 610 static boolean compileModule(String name, Path dest) throws IOException { 611 return CompilerUtils.compile(SRC_DIR.resolve(name), dest); 612 } 613 614 static void assertContains(String output, String subString) { 615 if (output.contains(subString)) 616 assertTrue(true); 617 else 618 assertTrue(false,"Expected to find [" + subString + "], in output [" 619 + output + "]" + "\n"); 620 } 621 622 static ModuleDescriptor getModuleDescriptor(Path jmod) { 623 ClassLoader cl = ClassLoader.getSystemClassLoader(); 624 try (FileSystem fs = FileSystems.newFileSystem(jmod, cl)) { 625 String p = "/classes/module-info.class"; 626 try (InputStream is = Files.newInputStream(fs.getPath(p))) { 627 return ModuleDescriptor.read(is); 628 } 629 } catch (IOException ioe) { 630 throw new UncheckedIOException(ioe); 631 } 632 } 633 634 static Stream<String> findFiles(Path dir) { 635 try { 636 return Files.find(dir, Integer.MAX_VALUE, (p, a) -> a.isRegularFile()) 637 .map(dir::relativize) 638 .map(Path::toString) 639 .map(p -> p.replace(File.separator, "/")); 640 } catch (IOException x) { 641 throw new UncheckedIOException(x); 642 } 643 } 644 645 static Set<String> getJmodContent(Path jmod) { 646 JmodResult r = jmod("list", jmod.toString()).assertSuccess(); 647 return Stream.of(r.output.split("\r?\n")).collect(toSet()); 648 } 649 650 static void assertJmodContent(Path jmod, Set<String> expected) { 651 Set<String> actual = getJmodContent(jmod); 652 if (!Objects.equals(actual, expected)) { 653 Set<String> unexpected = new HashSet<>(actual); 654 unexpected.removeAll(expected); 655 Set<String> notFound = new HashSet<>(expected); 656 notFound.removeAll(actual); 657 StringBuilder sb = new StringBuilder(); 658 sb.append("Unexpected but found:\n"); 659 unexpected.forEach(s -> sb.append("\t" + s + "\n")); 660 sb.append("Expected but not found:\n"); 661 notFound.forEach(s -> sb.append("\t" + s + "\n")); 662 assertTrue(false, "Jmod content check failed.\n" + sb.toString()); 663 } 664 } 665 666 static void assertJmodDoesNotContain(Path jmod, Set<String> unexpectedNames) { 667 Set<String> actual = getJmodContent(jmod); 668 Set<String> unexpected = new HashSet<>(); 669 for (String name : unexpectedNames) { 670 if (actual.contains(name)) 671 unexpected.add(name); 672 } 673 if (!unexpected.isEmpty()) { 674 StringBuilder sb = new StringBuilder(); 675 for (String s : unexpected) 676 sb.append("Unexpected but found: " + s + "\n"); 677 sb.append("In :"); 678 for (String s : actual) 679 sb.append("\t" + s + "\n"); 680 assertTrue(false, "Jmod content check failed.\n" + sb.toString()); 681 } 682 } 683 684 static void assertSameContent(Path p1, Path p2) { 685 try { 686 byte[] ba1 = Files.readAllBytes(p1); 687 byte[] ba2 = Files.readAllBytes(p2); 688 assertEquals(ba1, ba2); 689 } catch (IOException x) { 690 throw new UncheckedIOException(x); 691 } 692 } 693 694 static JmodResult jmod(String... args) { 695 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 696 PrintStream ps = new PrintStream(baos); 697 System.out.println("jmod " + Arrays.asList(args)); 698 int ec = JMOD_TOOL.run(ps, ps, args); 699 return new JmodResult(ec, new String(baos.toByteArray(), UTF_8)); 700 } 701 702 static class JmodResult { 703 final int exitCode; 704 final String output; 705 706 JmodResult(int exitValue, String output) { 707 this.exitCode = exitValue; 708 this.output = output; 709 } 710 JmodResult assertSuccess() { assertTrue(exitCode == 0, output); return this; } 711 JmodResult assertFailure() { assertTrue(exitCode != 0, output); return this; } 712 JmodResult resultChecker(Consumer<JmodResult> r) { r.accept(this); return this; } 713 } 714 715 static void createCmds(Path dir) throws IOException { 716 List<String> files = Arrays.asList( 717 "first", "second", "third" + File.separator + "third"); 718 createFiles(dir, files); 719 } 720 721 static void createLibs(Path dir) throws IOException { 722 List<String> files = Arrays.asList( 723 "first.so", "second.so", "third" + File.separator + "third.so"); 724 createFiles(dir, files); 725 } 726 727 static void createConfigs(Path dir) throws IOException { 728 List<String> files = Arrays.asList( 729 "first.cfg", "second.cfg", "third" + File.separator + "third.cfg"); 730 createFiles(dir, files); 731 } 732 733 static void createFiles(Path dir, List<String> filenames) throws IOException { 734 for (String name : filenames) { 735 Path file = dir.resolve(name); 736 Files.createDirectories(file.getParent()); 737 Files.createFile(file); 738 try (OutputStream os = Files.newOutputStream(file)) { 739 os.write("blahblahblah".getBytes(UTF_8)); 740 } 741 } 742 } 743 744 static void copyResource(Path srcDir, Path dir, String resource) throws IOException { 745 Path dest = dir.resolve(resource); 746 Files.deleteIfExists(dest); 747 748 Files.createDirectories(dest.getParent()); 749 Files.copy(srcDir.resolve(resource), dest); 750 } 751 752 // Standalone entry point. 753 public static void main(String[] args) throws Throwable { 754 JmodTest test = new JmodTest(); 755 test.buildExplodedModules(); 756 for (Method m : JmodTest.class.getDeclaredMethods()) { 757 if (m.getAnnotation(Test.class) != null) { 758 System.out.println("Invoking " + m.getName()); 759 m.invoke(test); 760 } 761 } 762 } 763 }