8229840 : Add jtreg test for --linux-app-category option JDK-8200758-branch
authorherrick
Fri, 06 Sep 2019 17:31:56 -0400
branchJDK-8200758-branch
changeset 58036 f7f10023f7c0
parent 57951 37e70d4679df
child 58037 c127c766fe8e
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
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CommandArguments.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JarBuilder.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaTool.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java
test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Test.java
test/jdk/tools/jpackage/linux/AppCategoryTest.java
test/jdk/tools/jpackage/linux/ReleaseTest.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<T> {
+
+    CommandArguments() {
+        args = new ArrayList<>();
+    }
+    List<String> args;
+
+    final public T addArgument(String v) {
+        args.add(v);
+        return (T) this;
+    }
+
+    final public T addArguments(List<String> 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<Path> 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;
+    }
+}
--- /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<Executor> {
+
+    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<String> 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<String> 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<String> 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
+    };
+}
--- /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<JPackageCommand> {
+
+    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<String> 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> T getArgumentValue(String argName,
+            Supplier<T> defaultValueSupplier,
+            Function<String, T> 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<String> 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<String> 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<Runnable> actions;
+    private boolean immutable;
+
+    private final static Map<String, PackageType> PACKAGE_TYPES
+            = new Supplier<Map<String, PackageType>>() {
+                @Override
+                public Map<String, PackageType> get() {
+                    Map<String, PackageType> reply = new HashMap<>();
+                    for (PackageType type : PackageType.values()) {
+                        reply.put(type.getName(), type);
+                    }
+                    return reply;
+                }
+            }.get();
+}
--- /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<Path> sourceFiles;
+    private File outputJar;
+    private String mainClass;
+}
--- /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;
+    }
+}
--- /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<PackageType, String> archs;
+}
--- /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:
+     * <li>Set --input and --output parameters.
+     * <li>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<JPackageCommand> v,
+            PackageType... types) {
+        normailize(types).forEach(
+                type -> handlers.get(type).addInitializer(v));
+        return this;
+    }
+
+    public PackageTest addBundleVerifier(
+            BiConsumer<JPackageCommand, Executor.Result> v, PackageType... types) {
+        normailize(types).forEach(
+                type -> handlers.get(type).addBundleVerifier(v));
+        return this;
+    }
+
+    public PackageTest addBundleVerifier(
+            Consumer<JPackageCommand> v, PackageType... types) {
+        return addBundleVerifier((cmd, unused) -> v.accept(cmd), types);
+    }
+
+    public PackageTest addInstallVerifier(Consumer<JPackageCommand> v,
+            PackageType... types) {
+        normailize(types).forEach(
+                type -> handlers.get(type).addInstallVerifier(v));
+        return this;
+    }
+
+    public PackageTest addUninstallVerifier(Consumer<JPackageCommand> 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<Handler> supportedHandlers = handlers.values().stream()
+                .filter(entry -> !entry.isVoid())
+                .collect(Collectors.toList());
+
+        if (supportedHandlers.isEmpty()) {
+            return;
+        }
+
+        Supplier<JPackageCommand> 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<JPackageCommand> {
+
+        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<JPackageCommand> v) {
+            if (isSupported()) {
+                initializers.add(v);
+            }
+        }
+
+        void addBundleVerifier(BiConsumer<JPackageCommand, Executor.Result> v) {
+            if (isSupported()) {
+                bundleVerifiers.add(v);
+            }
+        }
+
+        void addInstallVerifier(Consumer<JPackageCommand> v) {
+            if (isSupported()) {
+                installVerifiers.add(v);
+            }
+        }
+
+        void addUninstallVerifier(Consumer<JPackageCommand> 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<Consumer<JPackageCommand>> initializers;
+        private final List<BiConsumer<JPackageCommand, Executor.Result>> bundleVerifiers;
+        private final List<Consumer<JPackageCommand>> installVerifiers;
+        private final List<Consumer<JPackageCommand>> 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<PackageType> 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<PackageType, Handler> 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;
+        }
+    }
+}
--- /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;
+}
--- /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<Path>() {
+        @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<String> logOptions = Set.of(val.toLowerCase().split(","));
+            TRACE = !(logOptions.contains("trace") || logOptions.contains("t"));
+            TRACE_ASSERTS = !(logOptions.contains("assert") || logOptions.contains(
+                    "a"));
+        }
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}