--- /dev/null 2019-12-03 13:56:28.000000000 -0500
+++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java 2019-12-03 13:56:26.569282400 -0500
@@ -0,0 +1,487 @@
+/*
+ * 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.awt.Desktop;
+import java.awt.GraphicsEnvironment;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import jdk.jpackage.test.Functional.ThrowingConsumer;
+import jdk.incubator.jpackage.internal.AppImageFile;
+import static jdk.jpackage.test.PackageType.*;
+
+/**
+ * Instance of PackageTest is for configuring and running a single jpackage
+ * command to produce platform specific package bundle.
+ *
+ * Provides methods to 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 --dest 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() {
+ action = DEFAULT_ACTION;
+ excludeTypes = new HashSet<>();
+ forTypes();
+ setExpectedExitCode(0);
+ handlers = new HashMap<>();
+ namedInitializers = new HashSet<>();
+ currentTypes.forEach(v -> handlers.put(v, new Handler(v)));
+ }
+
+ public PackageTest excludeTypes(PackageType... types) {
+ excludeTypes.addAll(Stream.of(types).collect(Collectors.toSet()));
+ return forTypes(currentTypes);
+ }
+
+ public PackageTest excludeTypes(Collection types) {
+ return excludeTypes(types.toArray(PackageType[]::new));
+ }
+
+ public PackageTest forTypes(PackageType... types) {
+ Collection newTypes;
+ if (types == null || types.length == 0) {
+ newTypes = PackageType.NATIVE;
+ } else {
+ newTypes = Stream.of(types).collect(Collectors.toSet());
+ }
+ currentTypes = newTypes.stream()
+ .filter(PackageType::isSupported)
+ .filter(Predicate.not(excludeTypes::contains))
+ .collect(Collectors.toUnmodifiableSet());
+ return this;
+ }
+
+ public PackageTest forTypes(Collection types) {
+ return forTypes(types.toArray(PackageType[]::new));
+ }
+
+ public PackageTest notForTypes(PackageType... types) {
+ return notForTypes(List.of(types));
+ }
+
+ public PackageTest notForTypes(Collection types) {
+ Set workset = new HashSet<>(currentTypes);
+ workset.removeAll(types);
+ return forTypes(workset);
+ }
+
+ public PackageTest setExpectedExitCode(int v) {
+ expectedJPackageExitCode = v;
+ return this;
+ }
+
+ private PackageTest addInitializer(ThrowingConsumer v,
+ String id) {
+ if (id != null) {
+ if (namedInitializers.contains(id)) {
+ return this;
+ }
+
+ namedInitializers.add(id);
+ }
+ currentTypes.stream().forEach(type -> handlers.get(type).addInitializer(
+ ThrowingConsumer.toConsumer(v)));
+ return this;
+ }
+
+ public PackageTest addInitializer(ThrowingConsumer v) {
+ return addInitializer(v, null);
+ }
+
+ public PackageTest addBundleVerifier(
+ BiConsumer v) {
+ currentTypes.stream().forEach(
+ type -> handlers.get(type).addBundleVerifier(v));
+ return this;
+ }
+
+ public PackageTest addBundleVerifier(ThrowingConsumer v) {
+ return addBundleVerifier(
+ (cmd, unused) -> ThrowingConsumer.toConsumer(v).accept(cmd));
+ }
+
+ public PackageTest addBundlePropertyVerifier(String propertyName,
+ BiConsumer pred) {
+ return addBundleVerifier(cmd -> {
+ pred.accept(propertyName,
+ LinuxHelper.getBundleProperty(cmd, propertyName));
+ });
+ }
+
+ public PackageTest addBundlePropertyVerifier(String propertyName,
+ String expectedPropertyValue) {
+ return addBundlePropertyVerifier(propertyName, (unused, v) -> {
+ TKit.assertEquals(expectedPropertyValue, v, String.format(
+ "Check value of %s property is [%s]", propertyName, v));
+ });
+ }
+
+ public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) {
+ forTypes(LINUX, () -> {
+ LinuxHelper.addBundleDesktopIntegrationVerifier(this, integrated);
+ });
+ return this;
+ }
+
+ public PackageTest addInstallVerifier(ThrowingConsumer v) {
+ currentTypes.stream().forEach(
+ type -> handlers.get(type).addInstallVerifier(
+ ThrowingConsumer.toConsumer(v)));
+ return this;
+ }
+
+ public PackageTest addUninstallVerifier(ThrowingConsumer v) {
+ currentTypes.stream().forEach(
+ type -> handlers.get(type).addUninstallVerifier(
+ ThrowingConsumer.toConsumer(v)));
+ return this;
+ }
+
+ static void withTestFileAssociationsFile(FileAssociations fa,
+ ThrowingConsumer consumer) {
+ final String testFileDefaultName = String.join(".", "test",
+ fa.getSuffix());
+ TKit.withTempFile(testFileDefaultName, fa.getSuffix(), testFile -> {
+ if (TKit.isLinux()) {
+ LinuxHelper.initFileAssociationsTestFile(testFile);
+ }
+ consumer.accept(testFile);
+ });
+ }
+
+ PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa,
+ String... faLauncherDefaultArgs) {
+
+ // Setup test app to have valid jpackage command line before
+ // running check of type of environment.
+ addInitializer(cmd -> new HelloApp(null).addTo(cmd), "HelloApp");
+
+ String noActionMsg = "Not running file associations test";
+ if (GraphicsEnvironment.isHeadless()) {
+ TKit.trace(String.format(
+ "%s because running in headless environment", noActionMsg));
+ return this;
+ }
+
+ addInstallVerifier(cmd -> {
+ if (cmd.isFakeRuntime(noActionMsg)) {
+ return;
+ }
+
+ withTestFileAssociationsFile(fa, testFile -> {
+ testFile = testFile.toAbsolutePath().normalize();
+
+ final Path appOutput = testFile.getParent()
+ .resolve(HelloApp.OUTPUT_FILENAME);
+ Files.deleteIfExists(appOutput);
+
+ TKit.trace(String.format("Use desktop to open [%s] file",
+ testFile));
+ Desktop.getDesktop().open(testFile.toFile());
+ TKit.waitForFileCreated(appOutput, 7);
+
+ List expectedArgs = new ArrayList<>(List.of(
+ faLauncherDefaultArgs));
+ expectedArgs.add(testFile.toString());
+
+ // Wait a little bit after file has been created to
+ // make sure there are no pending writes into it.
+ Thread.sleep(3000);
+ HelloApp.verifyOutputFile(appOutput, expectedArgs);
+ });
+ });
+
+ forTypes(PackageType.LINUX, () -> {
+ LinuxHelper.addFileAssociationsVerifier(this, fa);
+ });
+
+ return this;
+ }
+
+ PackageTest forTypes(Collection types, Runnable action) {
+ Set oldTypes = Set.of(currentTypes.toArray(
+ PackageType[]::new));
+ try {
+ forTypes(types);
+ action.run();
+ } finally {
+ forTypes(oldTypes);
+ }
+ return this;
+ }
+
+ PackageTest forTypes(PackageType type, Runnable action) {
+ return forTypes(List.of(type), action);
+ }
+
+ PackageTest notForTypes(Collection types, Runnable action) {
+ Set workset = new HashSet<>(currentTypes);
+ workset.removeAll(types);
+ return forTypes(workset, action);
+ }
+
+ PackageTest notForTypes(PackageType type, Runnable action) {
+ return notForTypes(List.of(type), action);
+ }
+
+ public PackageTest configureHelloApp() {
+ return configureHelloApp(null);
+ }
+
+ public PackageTest configureHelloApp(String encodedName) {
+ addInitializer(
+ cmd -> new HelloApp(JavaAppDesc.parse(encodedName)).addTo(cmd));
+ addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput);
+ return this;
+ }
+
+ public void run() {
+ List supportedHandlers = handlers.values().stream()
+ .filter(entry -> !entry.isVoid())
+ .collect(Collectors.toList());
+
+ if (supportedHandlers.isEmpty()) {
+ // No handlers with initializers found. Nothing to do.
+ return;
+ }
+
+ Supplier initializer = new Supplier<>() {
+ @Override
+ public JPackageCommand get() {
+ JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput();
+ if (bundleOutputDir != null) {
+ cmd.setArgumentValue("--dest", bundleOutputDir.toString());
+ }
+ cmd.setDefaultAppName();
+ return cmd;
+ }
+ };
+
+ supportedHandlers.forEach(handler -> handler.accept(initializer.get()));
+ }
+
+ public PackageTest setAction(Action value) {
+ action = value;
+ return this;
+ }
+
+ public Action getAction() {
+ return action;
+ }
+
+ private class Handler implements Consumer {
+
+ Handler(PackageType type) {
+ if (!PackageType.NATIVE.contains(type)) {
+ throw new IllegalArgumentException(
+ "Attempt to configure a test for image packaging");
+ }
+ this.type = type;
+ initializers = new ArrayList<>();
+ bundleVerifiers = new ArrayList<>();
+ installVerifiers = new ArrayList<>();
+ uninstallVerifiers = new ArrayList<>();
+ }
+
+ boolean isVoid() {
+ return initializers.isEmpty();
+ }
+
+ void addInitializer(Consumer v) {
+ initializers.add(v);
+ }
+
+ void addBundleVerifier(BiConsumer v) {
+ bundleVerifiers.add(v);
+ }
+
+ void addInstallVerifier(Consumer v) {
+ installVerifiers.add(v);
+ }
+
+ void addUninstallVerifier(Consumer v) {
+ uninstallVerifiers.add(v);
+ }
+
+ @Override
+ public void accept(JPackageCommand cmd) {
+ type.applyTo(cmd);
+
+ initializers.stream().forEach(v -> v.accept(cmd));
+ cmd.executePrerequisiteActions();
+
+ switch (action) {
+ case CREATE:
+ Executor.Result result = cmd.execute();
+ result.assertExitCodeIs(expectedJPackageExitCode);
+ if (expectedJPackageExitCode == 0) {
+ TKit.assertFileExists(cmd.outputBundle());
+ } else {
+ TKit.assertPathExists(cmd.outputBundle(), false);
+ }
+ verifyPackageBundle(cmd.createImmutableCopy(), result);
+ break;
+
+ case VERIFY_INSTALL:
+ if (expectedJPackageExitCode == 0) {
+ verifyPackageInstalled(cmd.createImmutableCopy());
+ }
+ break;
+
+ case VERIFY_UNINSTALL:
+ if (expectedJPackageExitCode == 0) {
+ verifyPackageUninstalled(cmd.createImmutableCopy());
+ }
+ break;
+ }
+ }
+
+ private void verifyPackageBundle(JPackageCommand cmd,
+ Executor.Result result) {
+ if (expectedJPackageExitCode == 0) {
+ if (PackageType.LINUX.contains(cmd.packageType())) {
+ LinuxHelper.verifyPackageBundleEssential(cmd);
+ }
+ }
+ bundleVerifiers.stream().forEach(v -> v.accept(cmd, result));
+ }
+
+ private void verifyPackageInstalled(JPackageCommand cmd) {
+ TKit.trace(String.format("Verify installed: %s",
+ cmd.getPrintableCommandLine()));
+ TKit.assertDirectoryExists(cmd.appRuntimeDirectory());
+ if (!cmd.isRuntime()) {
+ TKit.assertExecutableFileExists(cmd.appLauncherPath());
+
+ if (PackageType.WINDOWS.contains(cmd.packageType())) {
+ new WindowsHelper.AppVerifier(cmd);
+ }
+ }
+
+ TKit.assertPathExists(AppImageFile.getPathInAppImage(
+ cmd.appInstallationDirectory()), false);
+
+ installVerifiers.stream().forEach(v -> v.accept(cmd));
+ }
+
+ private void verifyPackageUninstalled(JPackageCommand cmd) {
+ TKit.trace(String.format("Verify uninstalled: %s",
+ cmd.getPrintableCommandLine()));
+ if (!cmd.isRuntime()) {
+ TKit.assertPathExists(cmd.appLauncherPath(), false);
+
+ if (PackageType.WINDOWS.contains(cmd.packageType())) {
+ new WindowsHelper.AppVerifier(cmd);
+ }
+ }
+
+ TKit.assertPathExists(cmd.appInstallationDirectory(), false);
+
+ uninstallVerifiers.stream().forEach(v -> v.accept(cmd));
+ }
+
+ private final PackageType type;
+ private final List> initializers;
+ private final List> bundleVerifiers;
+ private final List> installVerifiers;
+ private final List> uninstallVerifiers;
+ }
+
+ private Collection currentTypes;
+ private Set excludeTypes;
+ private int expectedJPackageExitCode;
+ private Map handlers;
+ private Set namedInitializers;
+ private Action action;
+
+ /**
+ * Test action.
+ */
+ static public enum Action {
+ /**
+ * Create bundle.
+ */
+ CREATE,
+ /**
+ * Verify bundle installed.
+ */
+ VERIFY_INSTALL,
+ /**
+ * Verify bundle uninstalled.
+ */
+ VERIFY_UNINSTALL;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase().replace('_', '-');
+ }
+ };
+ private final static Action DEFAULT_ACTION;
+ private final static File bundleOutputDir;
+
+ static {
+ final String propertyName = "output";
+ String val = TKit.getConfigProperty(propertyName);
+ if (val == null) {
+ bundleOutputDir = null;
+ } else {
+ bundleOutputDir = new File(val).getAbsoluteFile();
+
+ if (!bundleOutputDir.isDirectory()) {
+ throw new IllegalArgumentException(String.format(
+ "Invalid value of %s sytem property: [%s]. Should be existing directory",
+ TKit.getConfigPropertyName(propertyName),
+ bundleOutputDir));
+ }
+ }
+ }
+
+ static {
+ final String propertyName = "action";
+ String action = Optional.ofNullable(TKit.getConfigProperty(propertyName)).orElse(
+ Action.CREATE.toString()).toLowerCase();
+ DEFAULT_ACTION = Stream.of(Action.values()).filter(
+ a -> a.toString().equals(action)).findFirst().orElseThrow(
+ () -> new IllegalArgumentException(String.format(
+ "Unrecognized value of %s property: [%s]",
+ TKit.getConfigPropertyName(propertyName), action)));
+ }
+}