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