# HG changeset patch # User herrick # Date 1567805516 14400 # Node ID f7f10023f7c0de1e2afde07fc9199110e17b5447 # Parent 37e70d4679df67d82af16661451c2a1b656b8b45 8229840 : Add jtreg test for --linux-app-category option 8229841 : Add jtreg test for --linux-app-release option Submitted-by: asemenyuk Reviewed-by: herrick, almatvee diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +class CommandArguments { + + CommandArguments() { + args = new ArrayList<>(); + } + List args; + + final public T addArgument(String v) { + args.add(v); + return (T) this; + } + + final public T addArguments(List v) { + args.addAll(v); + return (T) this; + } + + final public T addArgument(Path v) { + return addArgument(v.toString()); + } + + final public T addArguments(String... v) { + return addArguments(Arrays.asList(v)); + } + + final public T addPathArguments(List v) { + return addArguments(v.stream().map((p) -> p.toString()).collect( + Collectors.toList())); + } + + protected void verifyMutable() { + if (!isMutable()) { + throw new UnsupportedOperationException( + "Attempt to modify immutable object"); + } + } + + protected boolean isMutable() { + return true; + } +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public final class Executor extends CommandArguments { + + public Executor() { + saveOutputType = SaveOutputType.NONE; + } + + public Executor setExecutable(String v) { + executable = v; + return this; + } + + public Executor setDirectory(Path v) { + directory = v; + return this; + } + + public Executor setExecutable(JavaTool v) { + return setExecutable(v.getPath().getAbsolutePath()); + } + + public Executor saveOutput() { + saveOutputType = SaveOutputType.FULL; + return this; + } + + public Executor saveFirstLineOfOutput() { + saveOutputType = SaveOutputType.FIRST_LINE; + return this; + } + + public class Result { + + Result(int exitCode) { + this.exitCode = exitCode; + } + + public String getFirstLineOfOutput() { + return output.get(0).trim(); + } + + public List getOutput() { + return output; + } + + public String getPrintableCommandLine() { + return Executor.this.getPrintableCommandLine(); + } + + public Result assertExitCodeIs(int expectedExitCode) { + Test.assertEquals(expectedExitCode, exitCode, String.format( + "Check command %s exited with %d code", + getPrintableCommandLine(), expectedExitCode)); + return this; + } + + public Result assertExitCodeIsZero() { + return assertExitCodeIs(0); + } + + int exitCode; + List output; + } + + public Result execute() { + try { + return executeImpl(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private Result executeImpl() throws Exception { + List command = new ArrayList<>(); + command.add(executable); + command.addAll(args); + Path outputFile = null; + ProcessBuilder builder = new ProcessBuilder(command); + StringBuilder sb = new StringBuilder(getPrintableCommandLine()); + if (saveOutputType != SaveOutputType.NONE) { + outputFile = Test.createTempFile(".out"); + builder.redirectErrorStream(true); + builder.redirectOutput(outputFile.toFile()); + sb.append(String.format("; redirect output to [%s]", outputFile)); + } + if (directory != null) { + builder.directory(directory.toFile()); + sb.append(String.format("; in directory [%s]", directory)); + } + + try { + Test.trace("Execute " + sb.toString() + "..."); + Process process = builder.start(); + Result reply = new Result(process.waitFor()); + Test.trace("Done. Exit code: " + reply.exitCode); + if (saveOutputType == SaveOutputType.FIRST_LINE) { + reply.output = Arrays.asList( + Files.readAllLines(outputFile).stream().findFirst().get()); + } else if (saveOutputType == SaveOutputType.FULL) { + reply.output = Collections.unmodifiableList(Files.readAllLines( + outputFile)); + } + return reply; + } finally { + if (outputFile != null) { + Files.deleteIfExists(outputFile); + } + } + } + + public String getPrintableCommandLine() { + return "[" + executable + "]; args(" + args.size() + ")=" + Arrays.toString( + args.toArray()); + } + + private String executable; + private SaveOutputType saveOutputType; + private Path directory; + + private static enum SaveOutputType { + NONE, FULL, FIRST_LINE + }; +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * jpackage command line with prerequisite actions. Prerequisite actions can be + * anything. The simplest is to compile test application and pack in a jar for + * use on jpackage command line. + */ +public final class JPackageCommand extends CommandArguments { + + public JPackageCommand() { + actions = new ArrayList<>(); + } + + static JPackageCommand createImmutable(JPackageCommand v) { + JPackageCommand reply = new JPackageCommand(); + reply.immutable = true; + reply.args.addAll(v.args); + return reply; + } + + public void setArgumentValue(String argName, String newValue) { + String prevArg = null; + ListIterator it = args.listIterator(); + while (it.hasNext()) { + String value = it.next(); + if (prevArg != null && prevArg.equals(argName)) { + if (newValue != null) { + it.set(newValue); + } else { + it.remove(); + it.previous(); + it.remove(); + } + return; + } + prevArg = value; + } + + if (newValue != null) { + addArguments(argName, newValue); + } + } + + public T getArgumentValue(String argName, + Supplier defaultValueSupplier, + Function stringConverter) { + String prevArg = null; + for (String arg : args) { + if (prevArg != null && prevArg.equals(argName)) { + return stringConverter.apply(arg); + } + prevArg = arg; + } + if (defaultValueSupplier != null) { + return defaultValueSupplier.get(); + } + return null; + } + + public String getArgumentValue(String argName, + Supplier defaultValueSupplier) { + return getArgumentValue(argName, defaultValueSupplier, + (v) -> v); + } + + public PackageType packageType() { + return getArgumentValue("--package-type", + () -> PackageType.IMAGE, + (v) -> PACKAGE_TYPES.get(v)); + } + + public Path outputDir() { + return getArgumentValue("--output", + () -> Test.defaultOutputDir(), + (v) -> Path.of(v)); + } + + public Path inputDir() { + return getArgumentValue("--input", + () -> Test.defaultInputDir(), + (v) -> Path.of(v)); + } + + public String version() { + return getArgumentValue("--version", () -> "1.0"); + } + + public String name() { + return getArgumentValue("--name", + () -> getArgumentValue("--main-class", null)); + } + + public JPackageCommand setDefaultInputOutput() { + verifyMutable(); + addArguments("--input", Test.defaultInputDir().toString()); + addArguments("--output", Test.defaultOutputDir().toString()); + return this; + } + + public JPackageCommand setHelloApp() { + verifyMutable(); + actions.add(new Runnable() { + @Override + public void run() { + String mainClass = "Hello"; + Path jar = inputDir().resolve("hello.jar"); + new JarBuilder() + .setOutputJar(jar.toFile()) + .setMainClass(mainClass) + .addSourceFile(Test.TEST_SRC_ROOT.resolve( + Path.of("apps", "image", mainClass + ".java"))) + .create(); + addArguments("--main-jar", jar.getFileName().toString()); + addArguments("--main-class", mainClass); + } + }); + return this; + } + + public JPackageCommand setPackageType(PackageType type) { + verifyMutable(); + type.applyTo(this); + return this; + } + + public Path outputBundle() { + switch (packageType()) { + case LINUX_RPM: + case LINUX_DEB: + return outputDir().resolve(LinuxHelper.getBundleName(this)); + } + return null; + } + + public Path launcherInstallationPath() { + switch (packageType()) { + case LINUX_RPM: + case LINUX_DEB: + return LinuxHelper.getLauncherPath(this); + } + return null; + } + + public Executor.Result execute() { + verifyMutable(); + if (actions != null) { + actions.stream().forEach(r -> r.run()); + } + return new Executor() + .setExecutable(JavaTool.JPACKAGE) + .addArguments(args) + .execute(); + } + + static void verifyHelloApp(Path helloAppLauncher) { + File outputFile = Test.workDir().resolve("appOutput.txt").toFile(); + new Executor() + .setDirectory(outputFile.getParentFile().toPath()) + .setExecutable(helloAppLauncher.toString()) + .execute() + .assertExitCodeIsZero(); + + Test.assertTrue(outputFile.exists(), String.format( + "Check file [%s] exists", outputFile)); + + List output = null; + try { + output = Files.readAllLines(outputFile.toPath()); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + Test.assertEquals(2, output.size(), String.format( + "Check file [%s] contains %d text lines", outputFile, 2)); + + Test.assertEquals("jpackage test application", output.get(0), + String.format( + "Check contents of the first text line in [%s] file", + outputFile)); + + Test.assertEquals("args.length: 0", output.get(1), String.format( + "Check contents of the second text line in [%s] file", + outputFile)); + } + + @Override + protected boolean isMutable() { + return !immutable; + } + + private final List actions; + private boolean immutable; + + private final static Map PACKAGE_TYPES + = new Supplier>() { + @Override + public Map get() { + Map reply = new HashMap<>(); + for (PackageType type : PackageType.values()) { + reply.put(type.getName(), type); + } + return reply; + } + }.get(); +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import java.util.ArrayList; +import java.util.List; +import jdk.jpackage.internal.IOUtils; + + +/** + * Tool to compile Java sources and pack in a jar file. + */ +public final class JarBuilder { + + public JarBuilder() { + sourceFiles = new ArrayList<>(); + } + + public JarBuilder setOutputJar(File v) { + outputJar = v; + return this; + } + + public JarBuilder setMainClass(String v) { + mainClass = v; + return this; + } + + public JarBuilder addSourceFile(Path v) { + sourceFiles.add(v); + return this; + } + + public void create() { + try { + createImpl(); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void createImpl() throws Exception { + Path workDir = Test.createTempDirectory(); + try { + Executor.Result javacReply = new Executor() + .setExecutable(JavaTool.JAVAC) + .addArguments("-d", workDir.toString()) + .addPathArguments(sourceFiles).execute(); + javacReply.assertExitCodeIsZero(); + Path tmpJar = workDir.resolve("foo.jar"); + Executor jarExe = new Executor(); + jarExe.setExecutable(JavaTool.JAR).addArguments("-c", "-v", "-f", + tmpJar.toString()); + if (mainClass != null) { + jarExe.addArguments("-e", mainClass); + } + jarExe.addArguments("-C", workDir.toString(), "."); + javacReply = jarExe.execute(); + javacReply.assertExitCodeIsZero(); + outputJar.getParentFile().mkdirs(); + Files.copy(tmpJar, outputJar.toPath(), REPLACE_EXISTING); + } finally { + IOUtils.deleteRecursive(workDir.toFile()); + } + } + private List sourceFiles; + private File outputJar; + private String mainClass; +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package jdk.jpackage.test; + + +import java.io.File; +import java.nio.file.Path; + +public enum JavaTool { + JAVAC("javac"), JPACKAGE("jpackage"), JAR("jar"); + private File path; + + JavaTool(String name) { + path = Path.of(System.getProperty("java.home"), "bin", name).toFile(); + if (!path.exists()) { + path = new File(path.toString() + ".exe"); + } + if (!path.exists()) { + throw new RuntimeException("Unable to find tool [" + + name + "] at path=[" + path.getAbsolutePath() + "]"); + } + } + + File getPath() { + return path; + } +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +public class LinuxHelper { + static String getRelease(JPackageCommand cmd) { + return cmd.getArgumentValue("--linux-app-release", () -> "1"); + } + + static String getPackageName(JPackageCommand cmd) { + return cmd.name().toLowerCase(); + } + + static String getBundleName(JPackageCommand cmd) { + final String release = getRelease(cmd); + final String version = cmd.version(); + + final PackageType packageType = cmd.packageType(); + String format = null; + switch (packageType) { + case LINUX_DEB: + format = "%s_%s-%s_%s"; + break; + + case LINUX_RPM: + format = "%s-%s-%s.%s"; + break; + } + return String.format(format, + getPackageName(cmd), version, release, getPackageArch(packageType)) + + packageType.getSuffix(); + } + + static Path getLauncherPath(JPackageCommand cmd) { + final String launcherName = cmd.name(); + final Path packageFile = cmd.outputBundle(); + + Executor exec = new Executor(); + exec.saveOutput(); + final PackageType packageType = PackageType.fromSuffix( + packageFile.toString()); + switch (packageType) { + case LINUX_DEB: + exec.setExecutable("dpkg") + .addArgument("--contents") + .addArgument(packageFile); + break; + + case LINUX_RPM: + exec.setExecutable("rpm") + .addArgument("-qpl") + .addArgument(packageFile); + break; + } + + final String launcherRelativePath = Path.of("/", "bin", launcherName).toString(); + for (String line : exec.execute().assertExitCodeIsZero().getOutput()) { + if (line.endsWith(launcherRelativePath)) { + if (packageType == PackageType.LINUX_DEB) { + // Typical text lines produced by dpkg look like: + // drwxr-xr-x root/root 0 2019-08-30 05:30 ./opt/appcategorytest/runtime/lib/ + // -rw-r--r-- root/root 574912 2019-08-30 05:30 ./opt/appcategorytest/runtime/lib/libmlib_image.so + // Need to skip all fields but absolute path to file. + line = line.substring(line.indexOf(" ./") + 2); + } + return Path.of(line); + } + } + + Test.assertUnexpected(String.format("Failed to find %s in %s package", + launcherName)); + return null; + } + + public static String getDebBundleProperty(Path bundle, String fieldName) { + return new Executor() + .saveFirstLineOfOutput() + .setExecutable("dpkg-deb") + .addArguments("-f", bundle.toString(), fieldName) + .execute() + .assertExitCodeIsZero().getFirstLineOfOutput(); + } + + public static String geRpmBundleProperty(Path bundle, String fieldName) { + return new Executor() + .saveFirstLineOfOutput() + .setExecutable("rpm") + .addArguments( + "-qp", + "--queryformat", + String.format("%%{%s}", fieldName), + bundle.toString()) + .execute() + .assertExitCodeIsZero().getFirstLineOfOutput(); + } + + private static String getPackageArch(PackageType type) { + if (archs == null) { + archs = new HashMap<>(); + } + + String arch = archs.get(type); + if (arch == null) { + Executor exec = new Executor(); + exec.saveFirstLineOfOutput(); + switch (type) { + case LINUX_DEB: + exec.setExecutable("dpkg").addArgument( + "--print-architecture"); + break; + + case LINUX_RPM: + exec.setExecutable("rpmbuild").addArgument( + "--eval=%{_target_cpu}"); + break; + } + arch = exec.execute().assertExitCodeIsZero().getFirstLineOfOutput(); + archs.put(type, arch); + } + return arch; + } + + static private Map archs; +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Instance of PackageTest is for configuring and running a single jpackage + * command to produce platform specific package bundle. + * + * Provides methods hook up custom configuration of jpackage command and + * verification of the output bundle. + */ +public final class PackageTest { + + /** + * Default test configuration for jpackage command. Default jpackage command + * initialization includes: + *
  • Set --input and --output parameters. + *
  • Set --name parameter. Value of the parameter is the name of the first + * class with main function found in the callers stack. + * Defaults can be + * overridden with custom initializers set with subsequent addInitializer() + * function calls. + */ + public PackageTest() { + setJPackageExitCode(0); + handlers = new HashMap<>(); + Arrays.asList(PackageType.values()).stream().forEach( + v -> handlers.put(v, new Handler(v))); + } + + public PackageTest setJPackageExitCode(int v) { + expectedJPackageExitCode = 0; + return this; + } + + public PackageTest addInitializer(Consumer v, + PackageType... types) { + normailize(types).forEach( + type -> handlers.get(type).addInitializer(v)); + return this; + } + + public PackageTest addBundleVerifier( + BiConsumer v, PackageType... types) { + normailize(types).forEach( + type -> handlers.get(type).addBundleVerifier(v)); + return this; + } + + public PackageTest addBundleVerifier( + Consumer v, PackageType... types) { + return addBundleVerifier((cmd, unused) -> v.accept(cmd), types); + } + + public PackageTest addInstallVerifier(Consumer v, + PackageType... types) { + normailize(types).forEach( + type -> handlers.get(type).addInstallVerifier(v)); + return this; + } + + public PackageTest addUninstallVerifier(Consumer v, + PackageType... types) { + normailize(types).forEach( + type -> handlers.get(type).addUninstallVerifier(v)); + return this; + } + + public PackageTest configureHelloApp(PackageType... types) { + addInitializer(cmd -> cmd.setHelloApp(), types); + addInstallVerifier(cmd -> JPackageCommand.verifyHelloApp( + cmd.launcherInstallationPath()), types); + return this; + } + + public void run() { + List supportedHandlers = handlers.values().stream() + .filter(entry -> !entry.isVoid()) + .collect(Collectors.toList()); + + if (supportedHandlers.isEmpty()) { + return; + } + + Supplier initializer = new Supplier<>() { + @Override + public JPackageCommand get() { + JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput(); + if (bundleOutputDir != null) { + cmd.setArgumentValue("--output", bundleOutputDir.toString()); + } + setDefaultAppName(cmd); + return cmd; + } + }; + + supportedHandlers.forEach(handler -> handler.accept(initializer.get())); + } + + private class Handler implements Consumer { + + Handler(PackageType type) { + this.type = type; + initializers = new ArrayList<>(); + bundleVerifiers = new ArrayList<>(); + installVerifiers = new ArrayList<>(); + uninstallVerifiers = new ArrayList<>(); + } + + boolean isVoid() { + return initializers.isEmpty(); + } + + void addInitializer(Consumer v) { + if (isSupported()) { + initializers.add(v); + } + } + + void addBundleVerifier(BiConsumer v) { + if (isSupported()) { + bundleVerifiers.add(v); + } + } + + void addInstallVerifier(Consumer v) { + if (isSupported()) { + installVerifiers.add(v); + } + } + + void addUninstallVerifier(Consumer v) { + if (isSupported()) { + uninstallVerifiers.add(v); + } + } + + @Override + public void accept(JPackageCommand cmd) { + type.applyTo(cmd); + + initializers.stream().forEach(v -> v.accept(cmd)); + switch (action) { + case CREATE: + Executor.Result result = cmd.execute(); + result.assertExitCodeIs(expectedJPackageExitCode); + final File bundle = cmd.outputBundle().toFile(); + if (expectedJPackageExitCode == 0) { + Test.assertTrue(bundle.exists(), String.format( + "Check file [%s] exists", bundle)); + } else { + Test.assertFalse(bundle.exists(), String.format( + "Check file [%s] doesn't exist", bundle)); + } + + verifyPackageBundle(JPackageCommand.createImmutable(cmd), result); + break; + + case VERIFY_INSTALLED: + verifyPackageInstalled(JPackageCommand.createImmutable(cmd)); + break; + + case VERIFY_UNINSTALLED: + verifyPackageUninstalled(JPackageCommand.createImmutable(cmd)); + break; + } + } + + private void verifyPackageBundle(JPackageCommand cmd, Executor.Result result) { + bundleVerifiers.stream().forEach(v -> v.accept(cmd, result)); + } + + private void verifyPackageInstalled(JPackageCommand cmd) { + verifyInstalledLauncher(cmd.launcherInstallationPath().toFile()); + installVerifiers.stream().forEach(v -> v.accept(cmd)); + } + + private void verifyPackageUninstalled(JPackageCommand cmd) { + verifyUninstalledLauncher(cmd.launcherInstallationPath().toFile()); + uninstallVerifiers.stream().forEach(v -> v.accept(cmd)); + } + + private boolean isSupported() { + return type.getName() != null && type.isSupported(); + } + + private final PackageType type; + private final List> initializers; + private final List> bundleVerifiers; + private final List> installVerifiers; + private final List> uninstallVerifiers; + } + + private void setDefaultAppName(JPackageCommand cmd) { + StackTraceElement st[] = Thread.currentThread().getStackTrace(); + for (StackTraceElement ste : st) { + if ("main".equals(ste.getMethodName())) { + String name = ste.getClassName(); + name = name.substring(name.lastIndexOf('.') + 1); + cmd.addArguments("--name", name); + break; + } + } + } + + private Stream normailize(PackageType[] types) { + if (types == null || types.length == 0) { + return Arrays.stream(PackageType.values()); + } + return Arrays.stream(types).distinct(); + } + + private void verifyInstalledLauncher(File launcher) { + Test.assertTrue(launcher.isFile(), String.format( + "Check application launcher [%s] is a file", launcher)); + Test.assertTrue(launcher.canExecute(), String.format( + "Check application launcher [%s] can be executed", launcher)); + } + + private void verifyUninstalledLauncher(File launcher) { + Test.assertFalse(launcher.exists(), String.format( + "Check application launcher [%s] is not installed", launcher)); + File installDir = launcher.getParentFile().getParentFile(); + Test.assertFalse(installDir.exists(), String.format( + "Check application installation directory [%s] is not available", + installDir)); + } + + private int expectedJPackageExitCode; + private Map handlers; + + /** + * Test action. + */ + static private enum Action { + /** + * Create bundle. + */ + CREATE, + + /** + * Verify bundle installed. + */ + VERIFY_INSTALLED, + + /** + * Verify bundle uninstalled. + */ + VERIFY_UNINSTALLED + }; + private final static Action action; + private final static File bundleOutputDir; + + static { + final String JPACKAGE_TEST_OUTPUT = "jpackage.test.output"; + + String val = System.getProperty(JPACKAGE_TEST_OUTPUT); + if (val == null) { + bundleOutputDir = null; + } else { + bundleOutputDir = new File(val).getAbsoluteFile(); + + Test.assertTrue(bundleOutputDir.isDirectory(), String.format( + "Check value of %s property [%s] references a directory", + JPACKAGE_TEST_OUTPUT, bundleOutputDir)); + Test.assertTrue(bundleOutputDir.canWrite(), String.format( + "Check value of %s property [%s] references writable directory", + JPACKAGE_TEST_OUTPUT, bundleOutputDir)); + } + } + + static { + if (System.getProperty("jpackage.verify.install") != null) { + action = Action.VERIFY_INSTALLED; + } else if (System.getProperty("jpackage.verify.uninstall") != null) { + action = Action.VERIFY_UNINSTALLED; + } else { + action = Action.CREATE; + } + } +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jpackage.test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +/** + * jpackage package type traits. + */ +public enum PackageType { + WIN_MSI(".msi", "jdk.jpackage.internal.WinMsiBundler"), + WIN_EXE(".exe", "jdk.jpackage.internal.WinMsiBundler"), + LINUX_DEB(".deb", "jdk.jpackage.internal.LinuxDebBundler"), + LINUX_RPM(".rpm", "jdk.jpackage.internal.LinuxRpmBundler"), + OSX_DMG(".dmg", "jdk.jpackage.internal.MacDmgBundler"), + IMAGE(null, null); + + PackageType(String bundleSuffix, String bundlerClass) { + suffix = bundleSuffix; + if (bundlerClass != null) { + supported = isBundlerSupported(bundlerClass); + } else { + supported = false; + } + + if (suffix != null && supported) { + Test.trace(String.format("Bundler %s supported", getName())); + } + } + + void applyTo(JPackageCommand cmd) { + cmd.addArguments("--package-type", getName()); + } + + String getSuffix() { + return suffix; + } + + boolean isSupported() { + return supported; + } + + String getName() { + if (suffix == null) { + return null; + } + return suffix.substring(1); + } + + static PackageType fromSuffix(String packageFilename) { + if (packageFilename != null) { + for (PackageType v: values()) { + if (packageFilename.endsWith(v.getSuffix())) { + return v; + } + } + } + return null; + } + + private static boolean isBundlerSupported(String bundlerClass) { + try { + Class clazz = Class.forName(bundlerClass); + Method isSupported = clazz.getDeclaredMethod("isSupported"); + return ((Boolean)isSupported.invoke(clazz)); + } catch (ClassNotFoundException ex) { + return false; + } catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } + + private final String suffix; + private final boolean supported; +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Test.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jpackage.test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class Test { + + static final Path TEST_SRC_ROOT = new Supplier() { + @Override + public Path get() { + Path root = Path.of(System.getProperty("test.src")); + + for (int i = 0; i != 10; ++i) { + if (root.resolve("apps").toFile().isDirectory()) { + return root.toAbsolutePath(); + } + root = root.resolve(".."); + } + + throw new RuntimeException("Failed to locate apps directory"); + } + }.get(); + + static Path workDir() { + return Path.of("."); + } + + static Path defaultInputDir() { + return workDir().resolve("input"); + } + + static Path defaultOutputDir() { + return workDir().resolve("output"); + } + + static private void log(String v) { + System.err.println(v); + } + + public static void trace(String v) { + if (TRACE) { + log("TRACE: " + v); + } + } + + private static void traceAssert(String v) { + if (TRACE_ASSERTS) { + log("TRACE: " + v); + } + } + + public static void error(String v) { + log("ERROR: " + v); + throw new AssertionError(v); + } + + static Path createTempDirectory() throws IOException { + return Files.createTempDirectory("jpackage_"); + } + + static Path createTempFile(String suffix) throws IOException { + return File.createTempFile("jpackage_", suffix).toPath(); + } + + private static String concatMessages(String msg, String msg2) { + if (msg2 != null && !msg2.isBlank()) { + return msg + ": " + msg2; + } + return msg; + } + + public static void assertEquals(int expected, int actual, String msg) { + if (expected != actual) { + error(concatMessages(String.format( + "Expected [%d]. Actual [%d]", expected, actual), + msg)); + } + + traceAssert(String.format("assertEquals(%d): %s", expected, msg)); + } + + public static void assertEquals(String expected, String actual, String msg) { + if (expected == null && actual == null) { + return; + } + + if (actual == null || !expected.equals(actual)) { + error(concatMessages(String.format( + "Expected [%s]. Actual [%s]", expected, actual), + msg)); + } + + traceAssert(String.format("assertEquals(%s): %s", expected, msg)); + } + + public static void assertNotEquals(int expected, int actual, String msg) { + if (expected == actual) { + error(concatMessages(String.format("Unexpected [%d] value", actual), + msg)); + } + + traceAssert(String.format("assertNotEquals(%d, %d): %s", expected, + actual, msg)); + } + + public static void assertTrue(boolean actual, String msg) { + if (!actual) { + error(concatMessages("Unexpected FALSE", msg)); + } + + traceAssert(String.format("assertTrue(): %s", msg)); + } + + public static void assertFalse(boolean actual, String msg) { + if (actual) { + error(concatMessages("Unexpected TRUE", msg)); + } + + traceAssert(String.format("assertFalse(): %s", msg)); + } + + public static void assertUnexpected(String msg) { + error(concatMessages("Unexpected", msg)); + } + + private static final boolean TRACE; + private static final boolean TRACE_ASSERTS; + + static { + String val = System.getProperty("jpackage.test.suppress-logging"); + if (val == null) { + TRACE = true; + TRACE_ASSERTS = true; + } else { + Set logOptions = Set.of(val.toLowerCase().split(",")); + TRACE = !(logOptions.contains("trace") || logOptions.contains("t")); + TRACE_ASSERTS = !(logOptions.contains("assert") || logOptions.contains( + "a")); + } + } +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/linux/AppCategoryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/linux/AppCategoryTest.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.LinuxHelper; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Test; + + +/** + * Test --linux-app-category parameter. Output of the test should be + * appcategorytest_1.0-1_amd64.deb or appcategorytest-1.0-1.amd64.rpm package + * bundle. The output package should provide the same functionality as the + * default package. + * + * deb: + * Section property of the package should be set to Foo value. + * + * rpm: + * Group property of the package should be set to Foo value. + */ + + +/* + * @test + * @summary jpackage with --linux-app-category + * @library ../helpers + * @requires (os.family == "linux") + * @modules jdk.jpackage/jdk.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m AppCategoryTest + */ +public class AppCategoryTest { + + public static void main(String[] args) throws Exception { + final String CATEGORY = "Foo"; + + new PackageTest().configureHelloApp().addInitializer(cmd -> { + cmd.addArguments("--linux-app-category", CATEGORY); + }).addBundleVerifier(cmd -> { + final String field = "Section"; + String value = LinuxHelper.getDebBundleProperty( + cmd.outputBundle(), field); + Test.assertEquals(CATEGORY, value, + String.format("Check value of %s field is [%s]", + field, CATEGORY)); + }, PackageType.LINUX_DEB).addBundleVerifier(cmd -> { + final String field = "Group"; + String value = LinuxHelper.geRpmBundleProperty( + cmd.outputBundle(), field); + Test.assertEquals(CATEGORY, value, + String.format("Check value of %s field is [%s]", + field, CATEGORY)); + }, PackageType.LINUX_RPM).run(); + } +} diff -r 37e70d4679df -r f7f10023f7c0 test/jdk/tools/jpackage/linux/ReleaseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/tools/jpackage/linux/ReleaseTest.java Fri Sep 06 17:31:56 2019 -0400 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.jpackage.test.LinuxHelper; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Test; + + +/** + * Test --linux-app-release parameter. Output of the test should be + * releasetest_1.0-Rc3_amd64.deb or releasetest-1.0-Rc3.amd64.rpm package + * bundle. The output package should provide the same functionality as the + * default package. + * + * deb: + * Version property of the package should end with -Rc3 substring. + * + * rpm: + * Release property of the package should be set to Rc3 value. + */ + +/* + * @test + * @summary jpackage with --linux-app-release + * @library ../helpers + * @requires (os.family == "linux") + * @modules jdk.jpackage/jdk.jpackage.internal + * @run main/othervm/timeout=360 -Xmx512m ReleaseTest + */ +public class ReleaseTest { + + public static void main(String[] args) throws Exception { + final String RELEASE = "Rc3"; + + new PackageTest().configureHelloApp().addInitializer(cmd -> { + cmd.addArguments("--linux-app-release", RELEASE); + }).addBundleVerifier(cmd -> { + final String field = "Version"; + String version = LinuxHelper.getDebBundleProperty( + cmd.outputBundle(), field); + Test.assertTrue(version.endsWith("-" + RELEASE), + String.format("Check value of %s field [%s] ends with %s", + field, version, RELEASE)); + }, PackageType.LINUX_DEB).addBundleVerifier(cmd -> { + final String field = "Release"; + String value = LinuxHelper.geRpmBundleProperty( + cmd.outputBundle(), "Release"); + Test.assertEquals(RELEASE, value, + String.format("Check value of %s field is [%s]", + field, RELEASE)); + }, PackageType.LINUX_RPM).run(); + } +}