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