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