1 /* 2 * Copyright (c) 2019, 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 package jdk.jpackage.tests; 25 26 import java.io.IOException; 27 import java.nio.file.Files; 28 import java.nio.file.Path; 29 import java.util.List; 30 import java.util.ArrayList; 31 import java.util.function.Function; 32 import java.util.function.Predicate; 33 import java.util.regex.Pattern; 34 import java.util.stream.Collectors; 35 import java.util.stream.Stream; 36 import jdk.jpackage.test.*; 37 import jdk.jpackage.test.Functional.ThrowingConsumer; 38 import jdk.jpackage.test.Annotations.*; 39 40 /* 41 * @test 42 * @summary jpackage basic testing 43 * @library ../../../../helpers 44 * @build jdk.jpackage.test.* 45 * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal 46 * @compile BasicTest.java 47 * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main 48 * --jpt-run=jdk.jpackage.tests.BasicTest 49 */ 50 51 public final class BasicTest { 52 @Test 53 public void testNoArgs() { 54 List<String> output = 55 getJPackageToolProvider().executeAndGetOutput(); 56 TKit.assertStringListEquals(List.of("Usage: jpackage <options>", 57 "Use jpackage --help (or -h) for a list of possible options"), 58 output, "Check jpackage output"); 59 } 60 61 @Test 62 public void testVersion() { 63 List<String> output = 64 getJPackageToolProvider() 65 .addArgument("--version") 66 .executeAndGetOutput(); 67 TKit.assertStringListEquals(List.of(System.getProperty("java.version")), 68 output, "Check jpackage output"); 69 } 70 71 @Test 72 public void testHelp() { 73 List<String> hOutput = getJPackageToolProvider() 74 .addArgument("-h").executeAndGetOutput(); 75 List<String> helpOutput = getJPackageToolProvider() 76 .addArgument("--help").executeAndGetOutput(); 77 78 TKit.assertStringListEquals(hOutput, helpOutput, 79 "Check -h and --help parameters produce the same output"); 80 81 final String windowsPrefix = "--win-"; 82 final String linuxPrefix = "--linux-"; 83 final String osxPrefix = "--mac-"; 84 85 final String expectedPrefix; 86 final List<String> unexpectedPrefixes; 87 88 if (TKit.isWindows()) { 89 expectedPrefix = windowsPrefix; 90 unexpectedPrefixes = List.of(osxPrefix, linuxPrefix); 91 } else if (TKit.isLinux()) { 92 expectedPrefix = linuxPrefix; 93 unexpectedPrefixes = List.of(windowsPrefix, osxPrefix); 94 } else if (TKit.isOSX()) { 95 expectedPrefix = osxPrefix; 96 unexpectedPrefixes = List.of(linuxPrefix, windowsPrefix); 97 } else { 98 throw TKit.throwUnknownPlatformError(); 99 } 100 101 Function<String, Predicate<String>> createPattern = (prefix) -> { 102 return Pattern.compile("^ " + prefix).asPredicate(); 103 }; 104 105 Function<List<String>, Long> countStrings = (prefixes) -> { 106 return hOutput.stream().filter( 107 prefixes.stream().map(createPattern).reduce(x -> false, 108 Predicate::or)).peek(TKit::trace).count(); 109 }; 110 111 TKit.trace("Check parameters in help text"); 112 TKit.assertNotEquals(0, countStrings.apply(List.of(expectedPrefix)), 113 "Check help text contains plaform specific parameters"); 114 TKit.assertEquals(0, countStrings.apply(unexpectedPrefixes), 115 "Check help text doesn't contain unexpected parameters"); 116 } 117 118 @Test 119 @SuppressWarnings("unchecked") 120 public void testVerbose() { 121 JPackageCommand cmd = JPackageCommand.helloAppImage() 122 .setFakeRuntime().executePrerequisiteActions(); 123 124 List<String> expectedVerboseOutputStrings = new ArrayList<>(); 125 expectedVerboseOutputStrings.add("Creating app package:"); 126 if (TKit.isWindows()) { 127 expectedVerboseOutputStrings.add("Result application bundle:"); 128 expectedVerboseOutputStrings.add( 129 "Succeeded in building Windows Application Image package"); 130 } else if (TKit.isLinux()) { 131 expectedVerboseOutputStrings.add( 132 "Succeeded in building Linux Application Image package"); 133 } else if (TKit.isOSX()) { 134 expectedVerboseOutputStrings.add("Preparing Info.plist:"); 135 expectedVerboseOutputStrings.add( 136 "Succeeded in building Mac Application Image package"); 137 } else { 138 TKit.throwUnknownPlatformError(); 139 } 140 141 TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); 142 List<String> nonVerboseOutput = cmd.createExecutor().executeAndGetOutput(); 143 List<String>[] verboseOutput = (List<String>[])new List<?>[1]; 144 145 // Directory clean up is not 100% reliable on Windows because of 146 // antivirus software that can lock .exe files. Setup 147 // diffreent output directory instead of cleaning the default one for 148 // verbose jpackage run. 149 TKit.withTempDirectory("verbose-output", tempDir -> { 150 cmd.setArgumentValue("--dest", tempDir); 151 verboseOutput[0] = cmd.createExecutor().addArgument( 152 "--verbose").executeAndGetOutput(); 153 }); 154 155 TKit.assertTrue(nonVerboseOutput.size() < verboseOutput[0].size(), 156 "Check verbose output is longer than regular"); 157 158 expectedVerboseOutputStrings.forEach(str -> { 159 TKit.assertTextStream(str).label("regular output") 160 .predicate(String::contains).negate() 161 .apply(nonVerboseOutput.stream()); 162 }); 163 164 expectedVerboseOutputStrings.forEach(str -> { 165 TKit.assertTextStream(str).label("verbose output") 166 .apply(verboseOutput[0].stream()); 167 }); 168 } 169 170 @Test 171 public void testNoName() { 172 final String mainClassName = "Greetings"; 173 174 JPackageCommand cmd = JPackageCommand.helloAppImage(mainClassName) 175 .removeArgumentWithValue("--name"); 176 177 Path expectedImageDir = cmd.outputDir().resolve(mainClassName); 178 if (TKit.isOSX()) { 179 expectedImageDir = expectedImageDir.getParent().resolve( 180 expectedImageDir.getFileName().toString() + ".app"); 181 } 182 183 cmd.executeAndAssertHelloAppImageCreated(); 184 TKit.assertEquals(expectedImageDir.toAbsolutePath().normalize().toString(), 185 cmd.outputBundle().toAbsolutePath().normalize().toString(), 186 String.format( 187 "Check [%s] directory is filled with application image data", 188 expectedImageDir)); 189 } 190 191 @Test 192 // Regular app 193 @Parameter("Hello") 194 // Modular app 195 @Parameter("com.other/com.other.Hello") 196 public void testApp(String javaAppDesc) { 197 JPackageCommand.helloAppImage(javaAppDesc) 198 .executeAndAssertHelloAppImageCreated(); 199 } 200 201 @Test 202 public void testWhitespaceInPaths() { 203 JPackageCommand.helloAppImage("a/b c.jar:Hello") 204 .setArgumentValue("--input", TKit.workDir().resolve("The quick brown fox")) 205 .setArgumentValue("--dest", TKit.workDir().resolve("jumps over the lazy dog")) 206 .executeAndAssertHelloAppImageCreated(); 207 } 208 209 @Test 210 @Parameter("ALL-MODULE-PATH") 211 @Parameter("ALL-DEFAULT") 212 @Parameter("java.desktop") 213 @Parameter("java.desktop,jdk.jartool") 214 @Parameter({ "java.desktop", "jdk.jartool" }) 215 public void testAddModules(String... addModulesArg) { 216 JPackageCommand cmd = JPackageCommand 217 .helloAppImage("goodbye.jar:com.other/com.other.Hello"); 218 Stream.of(addModulesArg).map(v -> Stream.of("--add-modules", v)).flatMap( 219 s -> s).forEachOrdered(cmd::addArgument); 220 cmd.executeAndAssertHelloAppImageCreated(); 221 } 222 223 /** 224 * Test --temp option. Doesn't make much sense for app image as temporary 225 * directory is used only on Windows. Test it in packaging mode. 226 * @throws IOException 227 */ 228 @Test 229 public void testTemp() throws IOException { 230 TKit.withTempDirectory("temp-root", tempRoot -> { 231 Function<JPackageCommand, Path> getTempDir = cmd -> { 232 return tempRoot.resolve(cmd.outputBundle().getFileName()); 233 }; 234 235 ThrowingConsumer<JPackageCommand> addTempDir = cmd -> { 236 Path tempDir = getTempDir.apply(cmd); 237 Files.createDirectories(tempDir); 238 cmd.addArguments("--temp", tempDir); 239 }; 240 241 new PackageTest().configureHelloApp().addInitializer(addTempDir) 242 .addBundleVerifier(cmd -> { 243 // Check jpackage actually used the supplied directory. 244 Path tempDir = getTempDir.apply(cmd); 245 TKit.assertNotEquals(0, tempDir.toFile().list().length, 246 String.format( 247 "Check jpackage wrote some data in the supplied temporary directory [%s]", 248 tempDir)); 249 }) 250 .run(); 251 252 new PackageTest().configureHelloApp().addInitializer(addTempDir) 253 .addInitializer(cmd -> { 254 // Clean output from the previus jpackage run. 255 Files.delete(cmd.outputBundle()); 256 }) 257 // Temporary directory should not be empty, 258 // jpackage should exit with error. 259 .setExpectedExitCode(1) 260 .run(); 261 }); 262 } 263 264 @Test 265 public void testAtFile() throws IOException { 266 JPackageCommand cmd = JPackageCommand.helloAppImage(); 267 268 // Init options file with the list of options configured 269 // for JPackageCommand instance. 270 final Path optionsFile = TKit.workDir().resolve("options"); 271 Files.write(optionsFile, 272 List.of(String.join(" ", cmd.getAllArguments()))); 273 274 // Build app jar file. 275 cmd.executePrerequisiteActions(); 276 277 // Make sure output directory is empty. Normally JPackageCommand would 278 // do this automatically. 279 TKit.deleteDirectoryContentsRecursive(cmd.outputDir()); 280 281 // Instead of running jpackage command through configured 282 // JPackageCommand instance, run vanilla jpackage command with @ file. 283 getJPackageToolProvider() 284 .addArgument(String.format("@%s", optionsFile)) 285 .execute().assertExitCodeIsZero(); 286 287 // Verify output of jpackage command. 288 cmd.assertImageCreated(); 289 HelloApp.executeLauncherAndVerifyOutput(cmd); 290 } 291 292 @Parameter("Hello") 293 @Parameter("com.foo/com.foo.main.Aloha") 294 @Test 295 public void testJLinkRuntime(String javaAppDesc) { 296 JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc); 297 298 // If `--module` parameter was set on jpackage command line, get its 299 // value and extract module name. 300 // E.g.: foo.bar2/foo.bar.Buz -> foo.bar2 301 // Note: HelloApp class manages `--module` parameter on jpackage command line 302 final String moduleName = cmd.getArgumentValue("--module", () -> null, 303 (v) -> v.split("/", 2)[0]); 304 305 if (moduleName != null) { 306 // Build module jar. 307 cmd.executePrerequisiteActions(); 308 } 309 310 TKit.withTempDirectory("runtime", tempDir -> { 311 final Path runtimeDir = tempDir.resolve("data"); 312 313 // List of modules required for test app. 314 final var modules = new String[] { 315 "java.base", 316 "java.desktop" 317 }; 318 319 Executor jlink = getToolProvider(JavaTool.JLINK) 320 .saveOutput(false) 321 .addArguments( 322 "--add-modules", String.join(",", modules), 323 "--output", runtimeDir.toString(), 324 "--strip-debug", 325 "--no-header-files", 326 "--no-man-pages"); 327 328 if (moduleName != null) { 329 jlink.addArguments("--add-modules", moduleName, "--module-path", 330 Path.of(cmd.getArgumentValue("--module-path")).resolve( 331 "hello.jar").toString()); 332 } 333 334 jlink.execute().assertExitCodeIsZero(); 335 336 cmd.addArguments("--runtime-image", runtimeDir); 337 cmd.executeAndAssertHelloAppImageCreated(); 338 }); 339 } 340 341 private static Executor getJPackageToolProvider() { 342 return getToolProvider(JavaTool.JPACKAGE); 343 } 344 345 private static Executor getToolProvider(JavaTool tool) { 346 return new Executor().dumpOutput().saveOutput().setToolProvider(tool); 347 } 348 }