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 import java.io.*; 25 import java.lang.reflect.Method; 26 import java.nio.file.FileVisitResult; 27 import java.nio.file.Files; 28 import java.nio.file.Path; 29 import java.nio.file.Paths; 30 import java.nio.file.SimpleFileVisitor; 31 import java.nio.file.attribute.BasicFileAttributes; 32 import java.util.*; 33 import java.util.function.Consumer; 34 import java.util.jar.JarEntry; 35 import java.util.jar.JarInputStream; 36 import java.util.regex.Pattern; 37 import java.util.stream.Stream; 38 import javax.tools.JavaCompiler; 39 import javax.tools.JavaFileObject; 40 import javax.tools.StandardJavaFileManager; 41 import javax.tools.StandardLocation; 42 import javax.tools.ToolProvider; 43 44 import jdk.testlibrary.FileUtils; 45 import jdk.testlibrary.JDKToolFinder; 46 import org.testng.annotations.BeforeTest; 47 import org.testng.annotations.Test; 48 49 import static java.lang.String.format; 50 import static java.lang.System.out; 51 52 /* 53 * @test 54 * @library /lib/testlibrary 55 * @build jdk.testlibrary.FileUtils jdk.testlibrary.JDKToolFinder 56 * @compile Basic.java 57 * @run testng Basic 58 * @summary Basic test for Modular jars 59 */ 60 61 public class Basic { 62 static final Path TEST_SRC = Paths.get(System.getProperty("test.src", ".")); 63 static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", ".")); 64 static final Path MODULE_CLASSES = TEST_CLASSES.resolve("build"); 65 66 // Details based on the checked in module source 67 static TestModuleData FOO = new TestModuleData("foo", 68 "1.123", 69 "jdk.test.foo.Foo", 70 "Hello World!!!", null, 71 "jdk.test.foo.internal"); 72 static TestModuleData BAR = new TestModuleData("bar", 73 "4.5.6.7", 74 "jdk.test.bar.Bar", 75 "Hello from Bar!", null, 76 "jdk.test.bar", 77 "jdk.test.bar.internal"); 78 79 static class TestModuleData { 80 final String moduleName; 81 final String mainClass; 82 final String version; 83 final String message; 84 final String hashes; 85 final Set<String> conceals; 86 TestModuleData(String mn, String v, String mc, String m, String h, String... pkgs) { 87 moduleName = mn; mainClass = mc; version = v; message = m; hashes = h; 88 conceals = new HashSet<>(); 89 Stream.of(pkgs).forEach(conceals::add); 90 } 91 TestModuleData(String mn, String v, String mc, String m, String h, Set<String> pkgs) { 92 moduleName = mn; mainClass = mc; version = v; message = m; hashes = h; 93 conceals = pkgs; 94 } 95 static TestModuleData from(String s) { 96 try { 97 BufferedReader reader = new BufferedReader(new StringReader(s)); 98 String line; 99 String message = null; 100 String name = null, version = null, mainClass = null; 101 String hashes = null; 102 Set<String> conceals = null; 103 while ((line = reader.readLine()) != null) { 104 if (line.startsWith("message:")) { 105 message = line.substring("message:".length()); 106 } else if (line.startsWith("nameAndVersion:")) { 107 line = line.substring("nameAndVersion:".length()); 108 int i = line.indexOf('@'); 109 if (i != -1) { 110 name = line.substring(0, i); 111 version = line.substring(i + 1, line.length()); 112 } else { 113 name = line; 114 } 115 } else if (line.startsWith("mainClass:")) { 116 mainClass = line.substring("mainClass:".length()); 117 } else if (line.startsWith("hashes:")) { 118 hashes = line.substring("hashes:".length()); 119 } else if (line.startsWith("conceals:")) { 120 line = line.substring("conceals:".length()); 121 conceals = new HashSet<>(); 122 int i = line.indexOf(','); 123 if (i != -1) { 124 String[] p = line.split(","); 125 Stream.of(p).forEach(conceals::add); 126 } else { 127 conceals.add(line); 128 } 129 } else { 130 throw new AssertionError("Unknown value " + line); 131 } 132 } 133 134 return new TestModuleData(name, version, mainClass, message, hashes, conceals); 135 } catch (IOException x) { 136 throw new UncheckedIOException(x); 137 } 138 } 139 } 140 141 static void assertModuleData(Result r, TestModuleData expected) { 142 //out.printf("%s%n", r.output); 143 TestModuleData received = TestModuleData.from(r.output); 144 if (expected.message != null) 145 assertTrue(expected.message.equals(received.message), 146 "Expected message:", expected.message, ", got:", received.message); 147 assertTrue(expected.moduleName.equals(received.moduleName), 148 "Expected moduleName: ", expected.moduleName, ", got:", received.moduleName); 149 assertTrue(expected.version.equals(received.version), 150 "Expected version: ", expected.version, ", got:", received.version); 151 assertTrue(expected.mainClass.equals(received.mainClass), 152 "Expected mainClass: ", expected.mainClass, ", got:", received.mainClass); 153 expected.conceals.forEach(p -> assertTrue(received.conceals.contains(p), 154 "Expected ", p, ", in ", received.conceals)); 155 received.conceals.forEach(p -> assertTrue(expected.conceals.contains(p), 156 "Expected ", p, ", in ", expected.conceals)); 157 } 158 159 @BeforeTest 160 public void compileModules() throws Exception { 161 compileModule(FOO.moduleName); 162 compileModule(BAR.moduleName, MODULE_CLASSES); 163 compileModule("baz"); // for service provider consistency checking 164 } 165 166 @Test 167 public void createFoo() throws IOException { 168 Path mp = Paths.get("createFoo"); 169 createTestDir(mp); 170 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 171 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 172 173 jar("--create", 174 "--file=" + modularJar.toString(), 175 "--main-class=" + FOO.mainClass, 176 "--module-version=" + FOO.version, 177 "--no-manifest", 178 "-C", modClasses.toString(), ".") 179 .assertSuccess(); 180 java(mp, FOO.moduleName + "/" + FOO.mainClass) 181 .assertSuccess() 182 .resultChecker(r -> assertModuleData(r, FOO)); 183 184 try (InputStream fis = Files.newInputStream(modularJar); 185 JarInputStream jis = new JarInputStream(fis)) { 186 assertTrue(!jarContains(jis, "./"), 187 "Unexpected ./ found in ", modularJar.toString()); 188 } 189 } 190 191 @Test 192 public void updateFoo() throws IOException { 193 Path mp = Paths.get("updateFoo"); 194 createTestDir(mp); 195 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 196 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 197 198 jar("--create", 199 "--file=" + modularJar.toString(), 200 "--no-manifest", 201 "-C", modClasses.toString(), "jdk") 202 .assertSuccess(); 203 jar("--update", 204 "--file=" + modularJar.toString(), 205 "--main-class=" + FOO.mainClass, 206 "--module-version=" + FOO.version, 207 "--no-manifest", 208 "-C", modClasses.toString(), "module-info.class") 209 .assertSuccess(); 210 java(mp, FOO.moduleName + "/" + FOO.mainClass) 211 .assertSuccess() 212 .resultChecker(r -> assertModuleData(r, FOO)); 213 } 214 215 @Test 216 public void partialUpdateFooMainClass() throws IOException { 217 Path mp = Paths.get("partialUpdateFooMainClass"); 218 createTestDir(mp); 219 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 220 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 221 222 // A "bad" main class in first create ( and no version ) 223 jar("--create", 224 "--file=" + modularJar.toString(), 225 "--main-class=" + "IAmNotTheEntryPoint", 226 "--no-manifest", 227 "-C", modClasses.toString(), ".") // includes module-info.class 228 .assertSuccess(); 229 jar("--update", 230 "--file=" + modularJar.toString(), 231 "--main-class=" + FOO.mainClass, 232 "--module-version=" + FOO.version, 233 "--no-manifest") 234 .assertSuccess(); 235 java(mp, FOO.moduleName + "/" + FOO.mainClass) 236 .assertSuccess() 237 .resultChecker(r -> assertModuleData(r, FOO)); 238 } 239 240 @Test 241 public void partialUpdateFooVersion() throws IOException { 242 Path mp = Paths.get("partialUpdateFooVersion"); 243 createTestDir(mp); 244 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 245 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 246 247 // A "bad" version in first create ( and no main class ) 248 jar("--create", 249 "--file=" + modularJar.toString(), 250 "--module-version=" + "100000000", 251 "--no-manifest", 252 "-C", modClasses.toString(), ".") // includes module-info.class 253 .assertSuccess(); 254 jar("--update", 255 "--file=" + modularJar.toString(), 256 "--main-class=" + FOO.mainClass, 257 "--module-version=" + FOO.version, 258 "--no-manifest") 259 .assertSuccess(); 260 java(mp, FOO.moduleName + "/" + FOO.mainClass) 261 .assertSuccess() 262 .resultChecker(r -> assertModuleData(r, FOO)); 263 } 264 265 @Test 266 public void partialUpdateFooNotAllFiles() throws IOException { 267 Path mp = Paths.get("partialUpdateFooNotAllFiles"); 268 createTestDir(mp); 269 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 270 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 271 272 // Not all files, and none from non-exported packages, 273 // i.e. no concealed list in first create 274 jar("--create", 275 "--file=" + modularJar.toString(), 276 "--no-manifest", 277 "-C", modClasses.toString(), "module-info.class", 278 "-C", modClasses.toString(), "jdk/test/foo/Foo.class") 279 .assertSuccess(); 280 jar("--update", 281 "--file=" + modularJar.toString(), 282 "--main-class=" + FOO.mainClass, 283 "--module-version=" + FOO.version, 284 "--no-manifest", 285 "-C", modClasses.toString(), "jdk/test/foo/internal/Message.class") 286 .assertSuccess(); 287 java(mp, FOO.moduleName + "/" + FOO.mainClass) 288 .assertSuccess() 289 .resultChecker(r -> assertModuleData(r, FOO)); 290 } 291 292 @Test 293 public void partialUpdateFooAllFilesAndAttributes() throws IOException { 294 Path mp = Paths.get("partialUpdateFooAllFilesAndAttributes"); 295 createTestDir(mp); 296 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 297 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 298 299 // all attributes and files 300 jar("--create", 301 "--file=" + modularJar.toString(), 302 "--main-class=" + FOO.mainClass, 303 "--module-version=" + FOO.version, 304 "--no-manifest", 305 "-C", modClasses.toString(), ".") 306 .assertSuccess(); 307 jar("--update", 308 "--file=" + modularJar.toString(), 309 "--main-class=" + FOO.mainClass, 310 "--module-version=" + FOO.version, 311 "--no-manifest", 312 "-C", modClasses.toString(), ".") 313 .assertSuccess(); 314 java(mp, FOO.moduleName + "/" + FOO.mainClass) 315 .assertSuccess() 316 .resultChecker(r -> assertModuleData(r, FOO)); 317 } 318 319 @Test 320 public void partialUpdateFooModuleInfo() throws IOException { 321 Path mp = Paths.get("partialUpdateFooModuleInfo"); 322 createTestDir(mp); 323 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 324 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 325 Path barModInfo = MODULE_CLASSES.resolve(BAR.moduleName); 326 327 jar("--create", 328 "--file=" + modularJar.toString(), 329 "--main-class=" + FOO.mainClass, 330 "--module-version=" + FOO.version, 331 "--no-manifest", 332 "-C", modClasses.toString(), ".") 333 .assertSuccess(); 334 jar("--update", 335 "--file=" + modularJar.toString(), 336 "--no-manifest", 337 "-C", barModInfo.toString(), "module-info.class") // stuff in bar's info 338 .assertSuccess(); 339 jar("-p", 340 "--file=" + modularJar.toString()) 341 .assertSuccess() 342 .resultChecker(r -> { 343 // Expect similar output: "bar, requires mandated foo, ... 344 // conceals jdk.test.foo, conceals jdk.test.foo.internal" 345 Pattern p = Pattern.compile("\\s+bar\\s+requires\\s++foo"); 346 assertTrue(p.matcher(r.output).find(), 347 "Expecting to find \"bar, requires foo,...\"", 348 "in output, but did not: [" + r.output + "]"); 349 p = Pattern.compile( 350 "conceals\\s+jdk.test.foo\\s+conceals\\s+jdk.test.foo.internal"); 351 assertTrue(p.matcher(r.output).find(), 352 "Expecting to find \"conceals jdk.test.foo,...\"", 353 "in output, but did not: [" + r.output + "]"); 354 }); 355 } 356 357 @Test 358 public void hashBarInFooModule() throws IOException { 359 Path mp = Paths.get("dependencesFooBar"); 360 createTestDir(mp); 361 362 Path modClasses = MODULE_CLASSES.resolve(BAR.moduleName); 363 Path modularJar = mp.resolve(BAR.moduleName + ".jar"); 364 jar("--create", 365 "--file=" + modularJar.toString(), 366 "--main-class=" + BAR.mainClass, 367 "--module-version=" + BAR.version, 368 "--no-manifest", 369 "-C", modClasses.toString(), ".") 370 .assertSuccess(); 371 372 modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 373 modularJar = mp.resolve(FOO.moduleName + ".jar"); 374 jar("--create", 375 "--file=" + modularJar.toString(), 376 "--main-class=" + FOO.mainClass, 377 "--module-version=" + FOO.version, 378 "--modulepath=" + mp.toString(), 379 "--hash-modules=" + "bar", 380 "--no-manifest", 381 "-C", modClasses.toString(), ".") 382 .assertSuccess(); 383 384 java(mp, BAR.moduleName + "/" + BAR.mainClass, 385 "-XaddExports:java.base/jdk.internal.module=bar") 386 .assertSuccess() 387 .resultChecker(r -> { 388 assertModuleData(r, BAR); 389 TestModuleData received = TestModuleData.from(r.output); 390 assertTrue(received.hashes != null, "Expected non-null hashes value."); 391 }); 392 } 393 394 @Test 395 public void invalidHashInFooModule() throws IOException { 396 Path mp = Paths.get("badDependencyFooBar"); 397 createTestDir(mp); 398 399 Path barClasses = MODULE_CLASSES.resolve(BAR.moduleName); 400 Path barJar = mp.resolve(BAR.moduleName + ".jar"); 401 jar("--create", 402 "--file=" + barJar.toString(), 403 "--main-class=" + BAR.mainClass, 404 "--module-version=" + BAR.version, 405 "--no-manifest", 406 "-C", barClasses.toString(), ".").assertSuccess(); 407 408 Path fooClasses = MODULE_CLASSES.resolve(FOO.moduleName); 409 Path fooJar = mp.resolve(FOO.moduleName + ".jar"); 410 jar("--create", 411 "--file=" + fooJar.toString(), 412 "--main-class=" + FOO.mainClass, 413 "--module-version=" + FOO.version, 414 "--modulepath=" + mp.toString(), 415 "--hash-modules=" + "bar", 416 "--no-manifest", 417 "-C", fooClasses.toString(), ".").assertSuccess(); 418 419 // Rebuild bar.jar with a change that will cause its hash to be different 420 FileUtils.deleteFileWithRetry(barJar); 421 jar("--create", 422 "--file=" + barJar.toString(), 423 "--main-class=" + BAR.mainClass, 424 "--module-version=" + BAR.version + ".1", // a newer version 425 "--no-manifest", 426 "-C", barClasses.toString(), ".").assertSuccess(); 427 428 java(mp, BAR.moduleName + "/" + BAR.mainClass, 429 "-XaddExports:java.base/jdk.internal.module=bar") 430 .assertFailure() 431 .resultChecker(r -> { 432 // Expect similar output: "java.lang.module.ResolutionException: Hash 433 // of bar (WdktSIQSkd4+CEacpOZoeDrCosMATNrIuNub9b5yBeo=) differs to 434 // expected hash (iepvdv8xTeVrFgMtUhcFnmetSub6qQHCHc92lSaSEg0=)" 435 Pattern p = Pattern.compile(".*Hash of bar.*differs to expected hash.*"); 436 assertTrue(p.matcher(r.output).find(), 437 "Expecting error message containing \"Hash of bar ... differs to" 438 + " expected hash...\" but got: [", r.output + "]"); 439 }); 440 } 441 442 @Test 443 public void badOptionsFoo() throws IOException { 444 Path mp = Paths.get("badOptionsFoo"); 445 createTestDir(mp); 446 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 447 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 448 449 jar("--create", 450 "--file=" + modularJar.toString(), 451 "--module-version=" + 1.1, // no module-info.class 452 "-C", modClasses.toString(), "jdk") 453 .assertFailure(); // TODO: expected failure message 454 455 jar("--create", 456 "--file=" + modularJar.toString(), 457 "--hash-modules=" + ".*", // no module-info.class 458 "-C", modClasses.toString(), "jdk") 459 .assertFailure(); // TODO: expected failure message 460 } 461 462 @Test 463 public void servicesCreateWithoutFailure() throws IOException { 464 Path mp = Paths.get("servicesCreateWithoutFailure"); 465 createTestDir(mp); 466 Path modClasses = MODULE_CLASSES.resolve("baz"); 467 Path modularJar = mp.resolve("baz" + ".jar"); 468 469 // Positive test, create 470 jar("--create", 471 "--file=" + modularJar.toString(), 472 "-C", modClasses.toString(), "module-info.class", 473 "-C", modClasses.toString(), "jdk/test/baz/BazService.class", 474 "-C", modClasses.toString(), "jdk/test/baz/internal/BazServiceImpl.class") 475 .assertSuccess(); 476 } 477 478 @Test 479 public void servicesCreateWithoutServiceImpl() throws IOException { 480 Path mp = Paths.get("servicesWithoutServiceImpl"); 481 createTestDir(mp); 482 Path modClasses = MODULE_CLASSES.resolve("baz"); 483 Path modularJar = mp.resolve("baz" + ".jar"); 484 485 // Omit service impl 486 jar("--create", 487 "--file=" + modularJar.toString(), 488 "-C", modClasses.toString(), "module-info.class", 489 "-C", modClasses.toString(), "jdk/test/baz/BazService.class") 490 .assertFailure(); 491 } 492 493 @Test 494 public void servicesUpdateWithoutFailure() throws IOException { 495 Path mp = Paths.get("servicesUpdateWithoutFailure"); 496 createTestDir(mp); 497 Path modClasses = MODULE_CLASSES.resolve("baz"); 498 Path modularJar = mp.resolve("baz" + ".jar"); 499 500 // Positive test, update 501 jar("--create", 502 "--file=" + modularJar.toString(), 503 "-C", modClasses.toString(), "jdk/test/baz/BazService.class", 504 "-C", modClasses.toString(), "jdk/test/baz/internal/BazServiceImpl.class") 505 .assertSuccess(); 506 jar("--update", 507 "--file=" + modularJar.toString(), 508 "-C", modClasses.toString(), "module-info.class") 509 .assertSuccess(); 510 } 511 512 @Test 513 public void servicesUpdateWithoutServiceImpl() throws IOException { 514 Path mp = Paths.get("servicesUpdateWithoutServiceImpl"); 515 createTestDir(mp); 516 Path modClasses = MODULE_CLASSES.resolve("baz"); 517 Path modularJar = mp.resolve("baz" + ".jar"); 518 519 // Omit service impl 520 jar("--create", 521 "--file=" + modularJar.toString(), 522 "-C", modClasses.toString(), "jdk/test/baz/BazService.class") 523 .assertSuccess(); 524 jar("--update", 525 "--file=" + modularJar.toString(), 526 "-C", modClasses.toString(), "module-info.class") 527 .assertFailure(); 528 } 529 530 @Test 531 public void printModuleDescriptorFoo() throws IOException { 532 Path mp = Paths.get("printModuleDescriptorFoo"); 533 createTestDir(mp); 534 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 535 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 536 537 jar("--create", 538 "--file=" + modularJar.toString(), 539 "--main-class=" + FOO.mainClass, 540 "--module-version=" + FOO.version, 541 "--no-manifest", 542 "-C", modClasses.toString(), ".") 543 .assertSuccess(); 544 545 for (String option : new String[] {"--print-module-descriptor", "-p" }) { 546 jar(option, 547 "--file=" + modularJar.toString()) 548 .assertSuccess() 549 .resultChecker(r -> 550 assertTrue(r.output.contains(FOO.moduleName + "@" + FOO.version), 551 "Expected to find ", FOO.moduleName + "@" + FOO.version, 552 " in [", r.output, "]") 553 ); 554 } 555 } 556 557 @Test 558 public void printModuleDescriptorFooFromStdin() throws IOException { 559 Path mp = Paths.get("printModuleDescriptorFooFromStdin"); 560 createTestDir(mp); 561 Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName); 562 Path modularJar = mp.resolve(FOO.moduleName + ".jar"); 563 564 jar("--create", 565 "--file=" + modularJar.toString(), 566 "--main-class=" + FOO.mainClass, 567 "--module-version=" + FOO.version, 568 "--no-manifest", 569 "-C", modClasses.toString(), ".") 570 .assertSuccess(); 571 572 for (String option : new String[] {"--print-module-descriptor", "-p" }) { 573 jarWithStdin(modularJar.toFile(), 574 option) 575 .assertSuccess() 576 .resultChecker(r -> 577 assertTrue(r.output.contains(FOO.moduleName + "@" + FOO.version), 578 "Expected to find ", FOO.moduleName + "@" + FOO.version, 579 " in [", r.output, "]") 580 ); 581 } 582 } 583 584 // -- Infrastructure 585 586 static Result jarWithStdin(File stdinSource, String... args) { 587 String jar = getJDKTool("jar"); 588 List<String> commands = new ArrayList<>(); 589 commands.add(jar); 590 Stream.of(args).forEach(x -> commands.add(x)); 591 ProcessBuilder p = new ProcessBuilder(commands); 592 if (stdinSource != null) 593 p.redirectInput(stdinSource); 594 return run(p); 595 } 596 597 static Result jar(String... args) { 598 return jarWithStdin(null, args); 599 } 600 601 static Path compileModule(String mn) throws IOException { 602 return compileModule(mn, null); 603 } 604 605 static Path compileModule(String mn, Path mp) 606 throws IOException 607 { 608 Path fooSourcePath = TEST_SRC.resolve("src").resolve(mn); 609 Path build = Files.createDirectories(MODULE_CLASSES.resolve(mn)); 610 javac(build, mp, fileList(fooSourcePath)); 611 return build; 612 } 613 614 // Re-enable when there is support in javax.tools for module path 615 // static void javac(Path dest, Path... sourceFiles) throws IOException { 616 // out.printf("Compiling %d source files %s%n", sourceFiles.length, 617 // Arrays.asList(sourceFiles)); 618 // JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 619 // try (StandardJavaFileManager fileManager = 620 // compiler.getStandardFileManager(null, null, null)) { 621 // 622 // List<File> files = Stream.of(sourceFiles) 623 // .map(p -> p.toFile()) 624 // .collect(Collectors.toList()); 625 // List<File> dests = Stream.of(dest) 626 // .map(p -> p.toFile()) 627 // .collect(Collectors.toList()); 628 // Iterable<? extends JavaFileObject> compilationUnits = 629 // fileManager.getJavaFileObjectsFromFiles(files); 630 // fileManager.setLocation(StandardLocation.CLASS_OUTPUT, dests); 631 // JavaCompiler.CompilationTask task = 632 // compiler.getTask(null, fileManager, null, null, null, compilationUnits); 633 // boolean passed = task.call(); 634 // if (!passed) 635 // throw new RuntimeException("Error compiling " + files); 636 // } 637 // } 638 639 static void javac(Path dest, Path... sourceFiles) throws IOException { 640 javac(dest, null, sourceFiles); 641 } 642 643 static void javac(Path dest, Path modulePath, Path... sourceFiles) 644 throws IOException 645 { 646 String javac = getJDKTool("javac"); 647 648 List<String> commands = new ArrayList<>(); 649 commands.add(javac); 650 commands.add("-d"); 651 commands.add(dest.toString()); 652 if (dest.toString().contains("bar")) 653 commands.add("-XaddExports:java.base/jdk.internal.module=bar"); 654 if (modulePath != null) { 655 commands.add("-mp"); 656 commands.add(modulePath.toString()); 657 } 658 Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x)); 659 660 quickFail(run(new ProcessBuilder(commands))); 661 } 662 663 static Result java(Path modulePath, String entryPoint, String... args) { 664 String java = getJDKTool("java"); 665 666 List<String> commands = new ArrayList<>(); 667 commands.add(java); 668 Stream.of(args).forEach(x -> commands.add(x)); 669 commands.add("-mp"); 670 commands.add(modulePath.toString()); 671 commands.add("-m"); 672 commands.add(entryPoint); 673 674 return run(new ProcessBuilder(commands)); 675 } 676 677 static Path[] fileList(Path directory) throws IOException { 678 final List<Path> filePaths = new ArrayList<>(); 679 Files.walkFileTree(directory, new SimpleFileVisitor<Path>() { 680 @Override 681 public FileVisitResult visitFile(Path file, 682 BasicFileAttributes attrs) { 683 filePaths.add(file); 684 return FileVisitResult.CONTINUE; 685 } 686 }); 687 return filePaths.toArray(new Path[filePaths.size()]); 688 } 689 690 static void createTestDir(Path p) throws IOException{ 691 if (Files.exists(p)) 692 FileUtils.deleteFileTreeWithRetry(p); 693 Files.createDirectory(p); 694 } 695 696 static boolean jarContains(JarInputStream jis, String entryName) 697 throws IOException 698 { 699 JarEntry e; 700 while((e = jis.getNextJarEntry()) != null) { 701 if (e.getName().equals(entryName)) 702 return true; 703 } 704 return false; 705 } 706 707 static void quickFail(Result r) { 708 if (r.ec != 0) 709 throw new RuntimeException(r.output); 710 } 711 712 static Result run(ProcessBuilder pb) { 713 Process p; 714 out.printf("Running: %s%n", pb.command()); 715 try { 716 p = pb.start(); 717 } catch (IOException e) { 718 throw new RuntimeException( 719 format("Couldn't start process '%s'", pb.command()), e); 720 } 721 722 String output; 723 try { 724 output = toString(p.getInputStream(), p.getErrorStream()); 725 } catch (IOException e) { 726 throw new RuntimeException( 727 format("Couldn't read process output '%s'", pb.command()), e); 728 } 729 730 try { 731 p.waitFor(); 732 } catch (InterruptedException e) { 733 throw new RuntimeException( 734 format("Process hasn't finished '%s'", pb.command()), e); 735 } 736 return new Result(p.exitValue(), output); 737 } 738 739 static final String DEFAULT_IMAGE_BIN = System.getProperty("java.home") 740 + File.separator + "bin" + File.separator; 741 742 static String getJDKTool(String name) { 743 try { 744 return JDKToolFinder.getJDKTool(name); 745 } catch (Exception x) { 746 return DEFAULT_IMAGE_BIN + name; 747 } 748 } 749 750 static String toString(InputStream in1, InputStream in2) throws IOException { 751 try (ByteArrayOutputStream dst = new ByteArrayOutputStream(); 752 InputStream concatenated = new SequenceInputStream(in1, in2)) { 753 concatenated.transferTo(dst); 754 return new String(dst.toByteArray(), "UTF-8"); 755 } 756 } 757 758 static class Result { 759 final int ec; 760 final String output; 761 762 private Result(int ec, String output) { 763 this.ec = ec; 764 this.output = output; 765 } 766 Result assertSuccess() { 767 assertTrue(ec == 0, "Expected ec 0, got: ", ec, " , output [", output, "]"); 768 return this; 769 } 770 Result assertFailure() { 771 assertTrue(ec != 0, "Expected ec != 0, got:", ec, " , output [", output, "]"); 772 return this; 773 } 774 Result resultChecker(Consumer<Result> r) { r.accept(this); return this; } 775 } 776 777 static void assertTrue(boolean cond, Object ... failedArgs) { 778 if (cond) 779 return; 780 StringBuilder sb = new StringBuilder(); 781 for (Object o : failedArgs) 782 sb.append(o); 783 org.testng.Assert.assertTrue(false, sb.toString()); 784 } 785 786 // Standalone entry point. 787 public static void main(String[] args) throws Throwable { 788 Basic test = new Basic(); 789 test.compileModules(); 790 for (Method m : Basic.class.getDeclaredMethods()) { 791 if (m.getAnnotation(Test.class) != null) { 792 System.out.println("Invoking " + m.getName()); 793 m.invoke(test); 794 } 795 } 796 } 797 }