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 200 @Parameter("com.other/com.other.Hello") 201 public void testApp(String javaAppDesc) { 202 JPackageCommand.helloAppImage(javaAppDesc) 203 .executeAndAssertHelloAppImageCreated(); 204 } 205 206 @Test 207 public void testWhitespaceInPaths() { 208 JPackageCommand.helloAppImage("a/b c.jar:Hello") 209 .setArgumentValue("--input", TKit.workDir().resolve("The quick brown fox")) 210 .setArgumentValue("--dest", TKit.workDir().resolve("jumps over the lazy dog")) 211 .executeAndAssertHelloAppImageCreated(); 212 } 213 214 @Test 215 @Parameter("ALL-MODULE-PATH") 216 @Parameter("ALL-DEFAULT") 217 @Parameter("java.desktop") 218 @Parameter("java.desktop,jdk.jartool") 219 @Parameter({ "java.desktop", "jdk.jartool" }) 220 public void testAddModules(String... addModulesArg) { 221 JPackageCommand cmd = JPackageCommand 222 .helloAppImage("goodbye.jar:com.other/com.other.Hello"); 223 Stream.of(addModulesArg).map(v -> Stream.of("--add-modules", v)).flatMap( 224 s -> s).forEachOrdered(cmd::addArgument); 225 cmd.executeAndAssertHelloAppImageCreated(); 226 } 227 228 /** 229 * Test --temp option. Doesn't make much sense for app image as temporary 230 * directory is used only on Windows. Test it in packaging mode. 231 * @throws IOException 232 */ 233 @Test 234 public void testTemp() throws IOException { 235 final Path tempRoot = TKit.createTempDirectory("temp-root"); 236 237 Function<JPackageCommand, Path> getTempDir = cmd -> { 238 return tempRoot.resolve(cmd.outputBundle().getFileName()); 239 }; 240 241 Supplier<PackageTest> createTest = () -> { 242 return new PackageTest() 243 .configureHelloApp() 244 // Force save of package bundle in test work directory. 245 .addInitializer(JPackageCommand::setDefaultInputOutput) 246 .addInitializer(cmd -> { 247 Path tempDir = getTempDir.apply(cmd); 248 Files.createDirectories(tempDir); 249 cmd.addArguments("--temp", tempDir); 250 }); 251 }; 252 253 createTest.get() 254 .addBundleVerifier(cmd -> { 255 // Check jpackage actually used the supplied directory. 256 Path tempDir = getTempDir.apply(cmd); 257 TKit.assertNotEquals(0, tempDir.toFile().list().length, 258 String.format( 259 "Check jpackage wrote some data in the supplied temporary directory [%s]", 260 tempDir)); 261 }) 262 .run(PackageTest.Action.CREATE); 263 264 createTest.get() 265 .addInitializer(cmd -> { 266 // Clean output from the previus jpackage run. 267 Files.delete(cmd.outputBundle()); 268 }) 269 // Temporary directory should not be empty, 270 // jpackage should exit with error. 271 .setExpectedExitCode(1) 272 .run(PackageTest.Action.CREATE); 273 } 274 275 @Test 276 public void testAtFile() throws IOException { 277 JPackageCommand cmd = JPackageCommand 278 .helloAppImage() 279 .setArgumentValue("--dest", TKit.createTempDirectory("output")); 280 281 // Init options file with the list of options configured 282 // for JPackageCommand instance. 283 final Path optionsFile = TKit.createTempFile(Path.of("options")); 284 Files.write(optionsFile, 285 List.of(String.join(" ", cmd.getAllArguments()))); 286 287 // Build app jar file. 288 cmd.executePrerequisiteActions(); 289 290 // Instead of running jpackage command through configured 291 // JPackageCommand instance, run vanilla jpackage command with @ file. 292 getJPackageToolProvider() 293 .addArgument(String.format("@%s", optionsFile)) 294 .execute(); 295 296 // Verify output of jpackage command. 297 cmd.assertImageCreated(); 298 HelloApp.executeLauncherAndVerifyOutput(cmd); 299 } 300 301 @Parameter("Hello") 302 @Parameter("com.foo/com.foo.main.Aloha") 303 @Test 304 public void testJLinkRuntime(String javaAppDesc) throws IOException { 305 JavaAppDesc appDesc = JavaAppDesc.parse(javaAppDesc); 306 307 JPackageCommand cmd = JPackageCommand.helloAppImage(appDesc); 308 309 final String moduleName = appDesc.moduleName(); 310 311 if (moduleName != null) { 312 // Build module jar. 313 cmd.executePrerequisiteActions(); 314 } 315 316 final Path runtimeDir = TKit.createTempDirectory("runtime").resolve("data"); 317 318 // List of modules required for test app. 319 final var modules = new String[] { 320 "java.base", 321 "java.desktop" 322 }; 323 324 Executor jlink = getToolProvider(JavaTool.JLINK) 325 .saveOutput(false) 326 .addArguments( 327 "--add-modules", String.join(",", modules), 328 "--output", runtimeDir.toString(), 329 "--strip-debug", 330 "--no-header-files", 331 "--no-man-pages"); 332 333 if (moduleName != null) { 334 jlink.addArguments("--add-modules", moduleName, "--module-path", 335 Path.of(cmd.getArgumentValue("--module-path")).resolve( 336 "hello.jar").toString()); 337 } 338 339 jlink.execute(); 340 341 cmd.addArguments("--runtime-image", runtimeDir); 342 cmd.executeAndAssertHelloAppImageCreated(); 343 } 344 345 private static Executor getJPackageToolProvider() { 346 return getToolProvider(JavaTool.JPACKAGE); 347 } 348 349 private static Executor getToolProvider(JavaTool tool) { 350 return new Executor().dumpOutput().saveOutput().setToolProvider(tool); 351 } 352 }