1 /*
   2  * Copyright (c) 2016, 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 8170859
  27  * @summary Basic test for incubator modules in jmods and images
  28  * @library /lib/testlibrary /test/lib
  29  * @key intermittent
  30  * @modules jdk.compiler jdk.jartool jdk.jlink
  31  * @build jdk.test.lib.Utils
  32  *        jdk.test.lib.Asserts
  33  *        jdk.test.lib.JDKToolFinder
  34  *        jdk.test.lib.JDKToolLauncher
  35  *        jdk.test.lib.process.*
  36  *        jdk.test.lib.util.FileUtils
  37  *        jdk.test.lib.Platform
  38  *        jdk.test.lib.compiler.CompilerUtils
  39  * @run testng/othervm ImageModules
  40  */
  41 
  42 import java.io.ByteArrayOutputStream;
  43 import java.io.File;
  44 import java.io.IOException;
  45 import java.io.PrintStream;
  46 import java.nio.file.Files;
  47 import java.nio.file.Path;
  48 import java.nio.file.Paths;
  49 import java.util.List;
  50 import java.util.function.Consumer;
  51 import java.util.spi.ToolProvider;
  52 import java.util.stream.Collectors;
  53 import java.util.stream.Stream;
  54 
  55 import jdk.test.lib.compiler.CompilerUtils;
  56 import jdk.test.lib.util.FileUtils;
  57 import org.testng.annotations.BeforeTest;
  58 import org.testng.annotations.DataProvider;
  59 import org.testng.annotations.Test;
  60 
  61 import static java.nio.charset.StandardCharsets.UTF_8;
  62 import static jdk.testlibrary.ProcessTools.executeCommand;
  63 import static org.testng.Assert.*;
  64 
  65 public class ImageModules {
  66     private static final String JAVA_HOME = System.getProperty("java.home");
  67     private static final Path JDK_JMODS = Paths.get(JAVA_HOME, "jmods");
  68 
  69     private static final Path TEST_SRC = Paths.get(System.getProperty("test.src"));
  70     private static final Path MODS_DIR = Paths.get("mods");
  71     private static final Path CP_DIR = Paths.get("cp");
  72     private static final Path JARS_DIR = Paths.get("jars");
  73     private static final Path JMODS_DIR = Paths.get("jmods");
  74     private static final Path IMAGE = Paths.get("image");
  75 
  76     private static final String JAVA_BASE = "java.base";
  77     private final String[] modules = new String[] { "message.writer",
  78                                                     "message.converter" };
  79 
  80     @BeforeTest
  81     private void setup() throws Throwable {
  82         Path src = TEST_SRC.resolve("src");
  83         for (String name : modules) {
  84             assertTrue(CompilerUtils.compile(src.resolve(name),
  85                                              MODS_DIR,
  86                                              "--module-source-path", src.toString()));
  87         }
  88 
  89         assertTrue(CompilerUtils.compile(src.resolve("cp"),
  90                                          CP_DIR,
  91                                          "--module-path", MODS_DIR.toString(),
  92                                          "--add-modules", "message.writer"));
  93     }
  94 
  95     @DataProvider(name = "singleModule")
  96     public Object[][] singleModuleValues() throws IOException {
  97         Object[][] values = new Object[][]{
  98          // { Extra args to the build the message.converter jmod
  99          //   Tokens to pass to the run time --add-modules option
 100          //   SUCCESS or FAILURE expected
 101          //   Messages expected in the run time output
 102          //   Messages that must not appear in the run time output },
 103             { "",
 104               List.of("ALL-DEFAULT", "ALL-SYSTEM"),
 105               ToolResult.ASSERT_SUCCESS,
 106               List.of("hello world", "message.converter", "java.base"),
 107               List.of("WARNING") },
 108             { "--do-not-resolve-by-default",
 109               List.of("ALL-DEFAULT"),
 110               ToolResult.ASSERT_FAILURE,
 111               List.of("java.base", "java.lang.ClassNotFoundException: converter.MessageConverter"),
 112               List.of("WARNING", "message.converter") },
 113             { "--warn-if-resolved=incubating",
 114               List.of("ALL-DEFAULT", "ALL-SYSTEM"),
 115               ToolResult.ASSERT_SUCCESS,
 116               List.of("hello world", "message.converter", "java.base",
 117                       "WARNING: Using incubator modules: message.converter"),
 118               List.of() },
 119             { "--do-not-resolve-by-default --warn-if-resolved=incubating",
 120               List.of("ALL-DEFAULT"),
 121               ToolResult.ASSERT_FAILURE,
 122               List.of("java.base", "java.lang.ClassNotFoundException: converter.MessageConverter"),
 123               List.of("WARNING", "message.converter") },
 124             { "--do-not-resolve-by-default --warn-if-resolved=incubating",
 125               List.of("message.converter"),
 126               ToolResult.ASSERT_SUCCESS,
 127               List.of("hello world", "message.converter", "java.base", "WARNING"),
 128               List.of() }
 129         };
 130         return values;
 131     }
 132 
 133     @Test(dataProvider = "singleModule")
 134     public void singleModule(String extraJmodArg,
 135                              List<String> addModsTokens,
 136                              Consumer<ToolResult> assertExitCode,
 137                              List<String> expectedOutput,
 138                              List<String> unexpectedOutput)
 139         throws Throwable
 140     {
 141         if (Files.notExists(JDK_JMODS)) {
 142             System.out.println("JDK jmods not found test cannot run.");
 143             return;
 144         }
 145 
 146         FileUtils.deleteFileTreeUnchecked(JMODS_DIR);
 147         FileUtils.deleteFileTreeUnchecked(IMAGE);
 148         Files.createDirectories(JMODS_DIR);
 149         Path converterJmod = JMODS_DIR.resolve("converter.jmod");
 150 
 151         jmod("create",
 152              "--class-path", MODS_DIR.resolve("message.converter").toString(),
 153              extraJmodArg,
 154              converterJmod.toString())
 155             .assertSuccess();
 156 
 157         String mpath = JDK_JMODS.toString() + File.pathSeparator + JMODS_DIR.toString();
 158         jlink("--module-path", mpath,
 159               "--add-modules", JAVA_BASE + ",message.converter",
 160               "--output", IMAGE.toString())
 161              .assertSuccess();
 162 
 163         for (String addModsToken : addModsTokens) {
 164             String[] props = new String[] {"", "-Djdk.system.module.finder.disabledFastPath"};
 165             for (String systemProp : props)
 166                 java(IMAGE,
 167                      systemProp,
 168                      "--add-modules", addModsToken,
 169                      "-cp", CP_DIR.toString(),
 170                      "test.ConvertToLowerCase", "HEllo WoRlD")
 171                     .resultChecker(assertExitCode)
 172                     .resultChecker(r -> {
 173                         expectedOutput.forEach(e -> r.assertContains(e));
 174                         unexpectedOutput.forEach(e -> r.assertDoesNotContains(e));
 175                     });
 176         }
 177     }
 178 
 179     @Test
 180     public void singleModularJar() throws Throwable {
 181         FileUtils.deleteFileTreeUnchecked(JARS_DIR);
 182         Files.createDirectories(JARS_DIR);
 183         Path converterJar = JARS_DIR.resolve("converter.jar");
 184 
 185         jar("--create",
 186             "--file", converterJar.toString(),
 187             "--warn-if-resolved=incubating",
 188             "-C", MODS_DIR.resolve("message.converter").toString() , ".")
 189             .assertSuccess();
 190 
 191 
 192         java(Paths.get(JAVA_HOME),
 193              "--module-path", JARS_DIR.toString(),
 194              "--add-modules", "message.converter",
 195              "-cp", CP_DIR.toString(),
 196              "test.ConvertToLowerCase", "HEllo WoRlD")
 197             .assertSuccess()
 198             .resultChecker(r -> {
 199                 r.assertContains("WARNING: Using incubator modules: message.converter");
 200             });
 201     }
 202 
 203     @DataProvider(name = "twoModules")
 204     public Object[][] twoModulesValues() throws IOException {
 205         Object[][] values = new Object[][]{
 206          // { Extra args to the build the message.writer jmod
 207          //   Extra args to the build the message.converter jmod
 208          //   Tokens to pass to the run time --add-modules option
 209          //   SUCCESS or FAILURE expected
 210          //   Messages expected in the run time output
 211          //   Messages that must not appear in the run time output },
 212             { "",
 213               "",
 214               List.of("ALL-DEFAULT", "ALL-SYSTEM"),
 215               ToolResult.ASSERT_SUCCESS,
 216               List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base"),
 217               List.of() },
 218             { "",
 219               "--do-not-resolve-by-default",
 220               List.of("ALL-DEFAULT", "ALL-SYSTEM"),
 221               ToolResult.ASSERT_SUCCESS,
 222               List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base"),
 223               List.of() },
 224             { "--do-not-resolve-by-default",
 225               "",
 226               List.of("ALL-DEFAULT"),
 227               ToolResult.ASSERT_FAILURE,
 228               List.of("java.lang.ClassNotFoundException: writer.MessageWriter", "java.base"),
 229               List.of("message.writer") },
 230             { "--do-not-resolve-by-default",
 231               "--do-not-resolve-by-default",
 232               List.of("ALL-DEFAULT"),
 233               ToolResult.ASSERT_FAILURE,
 234               List.of("java.lang.ClassNotFoundException: writer.MessageWriter", "java.base"),
 235               List.of("message.converter", "message.writer") },
 236         // now add in warnings
 237             { "--do-not-resolve-by-default --warn-if-resolved=incubating",
 238               "",
 239               List.of("message.writer"),
 240               ToolResult.ASSERT_SUCCESS,
 241               List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base",
 242                       "WARNING: Using incubator modules: message.writer"),
 243               List.of() },
 244             { "",
 245               "--do-not-resolve-by-default --warn-if-resolved=incubating",
 246               List.of("message.writer"),
 247               ToolResult.ASSERT_SUCCESS,
 248               List.of("HELLO CHEGAR !!!", "message.writer", "message.converter", "java.base",
 249                       "WARNING: Using incubator modules: message.converter"),
 250               List.of() }
 251             };
 252         return values;
 253     }
 254 
 255     @Test(dataProvider = "twoModules")
 256     public void doNotResolveByDefaultTwoModules(String extraFirstJmodArg,
 257                                                 String extraSecondJmodArg,
 258                                                 List<String> addModsTokens,
 259                                                 Consumer<ToolResult> assertExitCode,
 260                                                 List<String> expectedOutput,
 261                                                 List<String> unexpectedOutput)
 262         throws Throwable
 263     {
 264         if (Files.notExists(JDK_JMODS)) {
 265             System.out.println("JDK jmods not found test cannot run.");
 266             return;
 267         }
 268 
 269         FileUtils.deleteFileTreeUnchecked(JMODS_DIR);
 270         FileUtils.deleteFileTreeUnchecked(IMAGE);
 271         Files.createDirectories(JMODS_DIR);
 272         Path writerJmod = JMODS_DIR.resolve("writer.jmod");
 273         Path converterJmod = JMODS_DIR.resolve("converter.jmod");
 274 
 275         jmod("create",
 276              extraFirstJmodArg,
 277              "--class-path", MODS_DIR.resolve("message.writer").toString(),
 278              writerJmod.toString());
 279 
 280         jmod("create",
 281              "--class-path", MODS_DIR.resolve("message.converter").toString(),
 282              extraSecondJmodArg,
 283              converterJmod.toString())
 284             .assertSuccess();
 285 
 286         String mpath = JDK_JMODS.toString() + File.pathSeparator + JMODS_DIR.toString();
 287         jlink("--module-path", mpath,
 288               "--add-modules", JAVA_BASE + ",message.writer,message.converter",
 289               "--output", IMAGE.toString())
 290              .assertSuccess();
 291 
 292         for (String addModsToken : addModsTokens) {
 293             String[] props = new String[] {"", "-Djdk.system.module.finder.disabledFastPath"};
 294             for (String systemProp : props)
 295                 java(IMAGE,
 296                      systemProp,
 297                      "--add-modules", addModsToken,
 298                      "-cp", CP_DIR.toString(),
 299                      "test.WriteUpperCase", "hello chegar !!!")
 300                     .resultChecker(assertExitCode)
 301                     .resultChecker(r -> {
 302                         expectedOutput.forEach(e -> r.assertContains(e));
 303                         unexpectedOutput.forEach(e -> r.assertDoesNotContains(e));
 304                     });
 305         }
 306     }
 307 
 308     static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
 309             .orElseThrow(() -> new RuntimeException("jmod tool not found"));
 310     static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
 311             .orElseThrow(() -> new RuntimeException("jar tool not found"));
 312     static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
 313             .orElseThrow(() -> new RuntimeException("jlink tool not found"));
 314 
 315     static ToolResult jmod(String... args) { return execTool(JMOD_TOOL, args); }
 316 
 317     static ToolResult jar(String... args) { return execTool(JAR_TOOL, args); }
 318 
 319     static ToolResult jlink(String... args) { return execTool(JLINK_TOOL, args); }
 320 
 321     static ToolResult java(Path image, String... opts) throws Throwable {
 322         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 323         PrintStream ps = new PrintStream(baos);
 324         String[] options = Stream.concat(Stream.of(getJava(image)),
 325                                          Stream.of(opts).filter(s -> !s.equals("")))
 326                                  .toArray(String[]::new);
 327 
 328         ProcessBuilder pb = new ProcessBuilder(options);
 329         int exitValue = executeCommand(pb).outputTo(ps)
 330                                           .errorTo(ps)
 331                                           .getExitValue();
 332 
 333         return new ToolResult(exitValue, new String(baos.toByteArray(), UTF_8));
 334     }
 335 
 336     static ToolResult execTool(ToolProvider tool, String... args) {
 337         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 338         PrintStream ps = new PrintStream(baos);
 339         List<String> filteredArgs = Stream.of(args)
 340                                           .map(s -> s.split(" ")).flatMap(Stream::of)
 341                                           .filter(s -> !s.equals(""))
 342                                           .collect(Collectors.toList());
 343         System.out.println(tool + " " + filteredArgs);
 344         int ec = tool.run(ps, ps, filteredArgs.toArray(new String[] {}));
 345         return new ToolResult(ec, new String(baos.toByteArray(), UTF_8));
 346     }
 347 
 348     static class ToolResult {
 349         final int exitCode;
 350         final String output;
 351 
 352         ToolResult(int exitValue, String output) {
 353             this.exitCode = exitValue;
 354             this.output = output;
 355         }
 356 
 357         static Consumer<ToolResult> ASSERT_SUCCESS = r ->
 358             assertEquals(r.exitCode, 0,
 359                         "Expected exit code 0, got " + r.exitCode
 360                                 + ", with output[" + r.output + "]");
 361         static Consumer<ToolResult> ASSERT_FAILURE = r ->
 362             assertNotEquals(r.exitCode, 0,
 363                            "Expected exit code != 0, got " + r.exitCode
 364                                    + ", with output[" + r.output + "]");
 365 
 366         ToolResult assertSuccess() { ASSERT_SUCCESS.accept(this); return this; }
 367         ToolResult assertFailure() { ASSERT_FAILURE.accept(this); return this; }
 368         ToolResult resultChecker(Consumer<ToolResult> r) { r.accept(this); return this; }
 369 
 370         ToolResult assertContains(String subString) {
 371             assertTrue(output.contains(subString),
 372                        "Expected to find [" + subString + "], in output ["
 373                             + output + "]" + "\n");
 374             return this;
 375         }
 376         ToolResult assertDoesNotContains(String subString) {
 377             assertFalse(output.contains(subString),
 378                        "Expected to NOT find [" + subString + "], in output ["
 379                            + output + "]" + "\n");
 380             return this;
 381         }
 382     }
 383 
 384     static String getJava(Path image) {
 385         boolean isWindows = System.getProperty("os.name").startsWith("Windows");
 386         Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
 387         if (Files.notExists(java))
 388             throw new RuntimeException(java + " not found");
 389         return java.toAbsolutePath().toString();
 390     }
 391 }