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 }