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