--- old/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java 2019-12-13 13:35:36.289184500 -0500 +++ new/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java 2019-12-13 13:35:35.293820300 -0500 @@ -28,14 +28,13 @@ 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.function.*; import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.incubator.jpackage.internal.AppImageFile; +import jdk.jpackage.test.Functional.ThrowingBiConsumer; +import jdk.jpackage.test.Functional.ThrowingRunnable; import static jdk.jpackage.test.PackageType.*; /** @@ -45,25 +44,16 @@ * Provides methods to hook up custom configuration of jpackage command and * verification of the output bundle. */ -public final class PackageTest { +public final class PackageTest extends RunnablePackageTest { - /** - * 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))); + handlers = currentTypes.stream() + .collect(Collectors.toMap(v -> v, v -> new Handler())); + packageHandlers = createDefaultPackageHandlers(); } public PackageTest excludeTypes(PackageType... types) { @@ -117,19 +107,37 @@ namedInitializers.add(id); } - currentTypes.stream().forEach(type -> handlers.get(type).addInitializer( + currentTypes.forEach(type -> handlers.get(type).addInitializer( ThrowingConsumer.toConsumer(v))); return this; } + private PackageTest addRunOnceInitializer(ThrowingRunnable v, String id) { + return addInitializer(new ThrowingConsumer() { + @Override + public void accept(JPackageCommand unused) throws Throwable { + if (!executed) { + executed = true; + v.run(); + } + } + + private boolean executed; + }, id); + } + public PackageTest addInitializer(ThrowingConsumer v) { return addInitializer(v, null); } + public PackageTest addRunOnceInitializer(ThrowingRunnable v) { + return addRunOnceInitializer(v, null); + } + public PackageTest addBundleVerifier( - BiConsumer v) { - currentTypes.stream().forEach( - type -> handlers.get(type).addBundleVerifier(v)); + ThrowingBiConsumer v) { + currentTypes.forEach(type -> handlers.get(type).addBundleVerifier( + ThrowingBiConsumer.toBiConsumer(v))); return this; } @@ -139,19 +147,26 @@ } public PackageTest addBundlePropertyVerifier(String propertyName, - BiConsumer pred) { + Predicate pred, String predLabel) { return addBundleVerifier(cmd -> { - pred.accept(propertyName, - LinuxHelper.getBundleProperty(cmd, propertyName)); + final String value; + if (TKit.isLinux()) { + value = LinuxHelper.getBundleProperty(cmd, propertyName); + } else if (TKit.isWindows()) { + value = WindowsHelper.getMsiProperty(cmd, propertyName); + } else { + throw new IllegalStateException(); + } + TKit.assertTrue(pred.test(value), String.format( + "Check value of %s property %s [%s]", propertyName, + predLabel, value)); }); } 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)); - }); + return addBundlePropertyVerifier(propertyName, + expectedPropertyValue::equals, "is"); } public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) { @@ -162,24 +177,39 @@ } public PackageTest addInstallVerifier(ThrowingConsumer v) { - currentTypes.stream().forEach( - type -> handlers.get(type).addInstallVerifier( - ThrowingConsumer.toConsumer(v))); + currentTypes.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))); + currentTypes.forEach(type -> handlers.get(type).addUninstallVerifier( + ThrowingConsumer.toConsumer(v))); + return this; + } + + public PackageTest setPackageInstaller(Consumer v) { + currentTypes.forEach( + type -> packageHandlers.get(type).installHandler = v); + return this; + } + + public PackageTest setPackageUnpacker( + BiFunction v) { + currentTypes.forEach(type -> packageHandlers.get(type).unpackHandler = v); + return this; + } + + public PackageTest setPackageUninstaller(Consumer v) { + currentTypes.forEach( + type -> packageHandlers.get(type).uninstallHandler = v); return this; } static void withTestFileAssociationsFile(FileAssociations fa, ThrowingConsumer consumer) { - final String testFileDefaultName = String.join(".", "test", - fa.getSuffix()); - TKit.withTempFile(testFileDefaultName, fa.getSuffix(), testFile -> { + final Path testFileDefaultName = Path.of("test" + fa.getSuffix()); + TKit.withTempFile(testFileDefaultName, testFile -> { if (TKit.isLinux()) { LinuxHelper.initFileAssociationsTestFile(testFile); } @@ -192,7 +222,7 @@ // Setup test app to have valid jpackage command line before // running check of type of environment. - addInitializer(cmd -> new HelloApp(null).addTo(cmd), "HelloApp"); + addHelloAppInitializer(null); String noActionMsg = "Not running file associations test"; if (GraphicsEnvironment.isHeadless()) { @@ -202,7 +232,7 @@ } addInstallVerifier(cmd -> { - if (cmd.isFakeRuntime(noActionMsg)) { + if (cmd.isFakeRuntime(noActionMsg) || cmd.isPackageUnpacked(noActionMsg)) { return; } @@ -225,7 +255,8 @@ // 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); + HelloApp.verifyOutputFile(appOutput, expectedArgs, + Collections.emptyMap()); }); }); @@ -236,7 +267,7 @@ return this; } - PackageTest forTypes(Collection types, Runnable action) { + public PackageTest forTypes(Collection types, Runnable action) { Set oldTypes = Set.of(currentTypes.toArray( PackageType[]::new)); try { @@ -248,17 +279,17 @@ return this; } - PackageTest forTypes(PackageType type, Runnable action) { + public PackageTest forTypes(PackageType type, Runnable action) { return forTypes(List.of(type), action); } - PackageTest notForTypes(Collection types, Runnable action) { + public PackageTest notForTypes(Collection types, Runnable action) { Set workset = new HashSet<>(currentTypes); workset.removeAll(types); return forTypes(workset, action); } - PackageTest notForTypes(PackageType type, Runnable action) { + public PackageTest notForTypes(PackageType type, Runnable action) { return notForTypes(List.of(type), action); } @@ -266,55 +297,167 @@ return configureHelloApp(null); } - public PackageTest configureHelloApp(String encodedName) { - addInitializer( - cmd -> new HelloApp(JavaAppDesc.parse(encodedName)).addTo(cmd)); + public PackageTest configureHelloApp(String javaAppDesc) { + addHelloAppInitializer(javaAppDesc); 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; + public final static class Group extends RunnablePackageTest { + public Group(PackageTest... tests) { + handlers = Stream.of(tests) + .map(PackageTest::createPackageTypeHandlers) + .flatMap(List>::stream) + .collect(Collectors.toUnmodifiableList()); } - Supplier initializer = new Supplier<>() { - @Override - public JPackageCommand get() { - JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput(); - if (bundleOutputDir != null) { - cmd.setArgumentValue("--dest", bundleOutputDir.toString()); + @Override + protected void runAction(Action... action) { + if (Set.of(action).contains(Action.UNINSTALL)) { + ListIterator> listIterator = handlers.listIterator( + handlers.size()); + while (listIterator.hasPrevious()) { + var handler = listIterator.previous(); + List.of(action).forEach(handler::accept); } - cmd.setDefaultAppName(); - return cmd; + } else { + handlers.forEach(handler -> List.of(action).forEach(handler::accept)); } - }; + } - supportedHandlers.forEach(handler -> handler.accept(initializer.get())); + private final List> handlers; } - public PackageTest setAction(Action value) { - action = value; - return this; + final static class PackageHandlers { + Consumer installHandler; + Consumer uninstallHandler; + BiFunction unpackHandler; } - public Action getAction() { - return action; + @Override + protected void runActions(List actions) { + createPackageTypeHandlers().forEach( + handler -> actions.forEach( + action -> List.of(action).forEach(handler::accept))); } - private class Handler implements Consumer { + @Override + protected void runAction(Action... action) { + throw new UnsupportedOperationException(); + } - Handler(PackageType type) { - if (!PackageType.NATIVE.contains(type)) { - throw new IllegalArgumentException( - "Attempt to configure a test for image packaging"); + private void addHelloAppInitializer(String javaAppDesc) { + addInitializer( + cmd -> new HelloApp(JavaAppDesc.parse(javaAppDesc)).addTo(cmd), + "HelloApp"); + } + + private List> createPackageTypeHandlers() { + return PackageType.NATIVE.stream() + .map(type -> { + Handler handler = handlers.entrySet().stream() + .filter(entry -> !entry.getValue().isVoid()) + .filter(entry -> entry.getKey() == type) + .map(entry -> entry.getValue()) + .findAny().orElse(null); + Map.Entry result = null; + if (handler != null) { + result = Map.entry(type, handler); + } + return result; + }) + .filter(Objects::nonNull) + .map(entry -> createPackageTypeHandler( + entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + private Consumer createPackageTypeHandler( + PackageType type, Handler handler) { + return ThrowingConsumer.toConsumer(new ThrowingConsumer() { + @Override + public void accept(Action action) throws Throwable { + if (action == Action.FINALIZE) { + if (unpackDir != null && Files.isDirectory(unpackDir) + && !unpackDir.startsWith(TKit.workDir())) { + TKit.deleteDirectoryRecursive(unpackDir); + } + } + + if (aborted) { + return; + } + + final JPackageCommand curCmd; + if (Set.of(Action.INITIALIZE, Action.CREATE).contains(action)) { + curCmd = cmd; + } else { + curCmd = cmd.createImmutableCopy(); + } + + switch (action) { + case UNPACK: { + var handler = packageHandlers.get(type).unpackHandler; + if (!(aborted = (handler == null))) { + unpackDir = TKit.createTempDirectory( + String.format("unpacked-%s", + type.getName())); + unpackDir = handler.apply(cmd, unpackDir); + cmd.setUnpackedPackageLocation(unpackDir); + } + break; + } + + case INSTALL: { + var handler = packageHandlers.get(type).installHandler; + if (!(aborted = (handler == null))) { + handler.accept(curCmd); + } + break; + } + + case UNINSTALL: { + var handler = packageHandlers.get(type).uninstallHandler; + if (!(aborted = (handler == null))) { + handler.accept(curCmd); + } + break; + } + + case CREATE: + handler.accept(action, curCmd); + aborted = (expectedJPackageExitCode != 0); + return; + + default: + handler.accept(action, curCmd); + break; + } + + if (aborted) { + TKit.trace( + String.format("Aborted [%s] action of %s command", + action, cmd.getPrintableCommandLine())); + } } - this.type = type; + + private Path unpackDir; + private boolean aborted; + private final JPackageCommand cmd = Functional.identity(() -> { + JPackageCommand result = new JPackageCommand(); + result.setDefaultInputOutput().setDefaultAppName(); + if (BUNDLE_OUTPUT_DIR != null) { + result.setArgumentValue("--dest", BUNDLE_OUTPUT_DIR.toString()); + } + type.applyTo(result); + return result; + }).get(); + }); + } + + private class Handler implements BiConsumer { + + Handler() { initializers = new ArrayList<>(); bundleVerifiers = new ArrayList<>(); installVerifiers = new ArrayList<>(); @@ -342,33 +485,35 @@ } @Override - public void accept(JPackageCommand cmd) { - type.applyTo(cmd); - - initializers.stream().forEach(v -> v.accept(cmd)); - cmd.executePrerequisiteActions(); - + public void accept(Action action, JPackageCommand cmd) { switch (action) { + case INITIALIZE: + initializers.forEach(v -> v.accept(cmd)); + if (cmd.isImagePackageType()) { + throw new UnsupportedOperationException(); + } + cmd.executePrerequisiteActions(); + break; + case CREATE: - Executor.Result result = cmd.execute(); - result.assertExitCodeIs(expectedJPackageExitCode); + Executor.Result result = cmd.execute(expectedJPackageExitCode); if (expectedJPackageExitCode == 0) { TKit.assertFileExists(cmd.outputBundle()); } else { TKit.assertPathExists(cmd.outputBundle(), false); } - verifyPackageBundle(cmd.createImmutableCopy(), result); + verifyPackageBundle(cmd, result); break; case VERIFY_INSTALL: if (expectedJPackageExitCode == 0) { - verifyPackageInstalled(cmd.createImmutableCopy()); + verifyPackageInstalled(cmd); } break; case VERIFY_UNINSTALL: if (expectedJPackageExitCode == 0) { - verifyPackageUninstalled(cmd.createImmutableCopy()); + verifyPackageUninstalled(cmd); } break; } @@ -381,25 +526,33 @@ LinuxHelper.verifyPackageBundleEssential(cmd); } } - bundleVerifiers.stream().forEach(v -> v.accept(cmd, result)); + bundleVerifiers.forEach(v -> v.accept(cmd, result)); } private void verifyPackageInstalled(JPackageCommand cmd) { - TKit.trace(String.format("Verify installed: %s", - cmd.getPrintableCommandLine())); + final String formatString; + if (cmd.isPackageUnpacked()) { + formatString = "Verify unpacked: %s"; + } else { + formatString = "Verify installed: %s"; + } + TKit.trace(String.format(formatString, cmd.getPrintableCommandLine())); + TKit.assertDirectoryExists(cmd.appRuntimeDirectory()); if (!cmd.isRuntime()) { TKit.assertExecutableFileExists(cmd.appLauncherPath()); - if (PackageType.WINDOWS.contains(cmd.packageType())) { - new WindowsHelper.AppVerifier(cmd); + if (PackageType.WINDOWS.contains(cmd.packageType()) + && !cmd.isPackageUnpacked( + "Not verifying desktop integration")) { + new WindowsHelper.DesktopIntegrationVerifier(cmd); } } TKit.assertPathExists(AppImageFile.getPathInAppImage( cmd.appInstallationDirectory()), false); - installVerifiers.stream().forEach(v -> v.accept(cmd)); + installVerifiers.forEach(v -> v.accept(cmd)); } private void verifyPackageUninstalled(JPackageCommand cmd) { @@ -409,79 +562,63 @@ TKit.assertPathExists(cmd.appLauncherPath(), false); if (PackageType.WINDOWS.contains(cmd.packageType())) { - new WindowsHelper.AppVerifier(cmd); + new WindowsHelper.DesktopIntegrationVerifier(cmd); } } TKit.assertPathExists(cmd.appInstallationDirectory(), false); - uninstallVerifiers.stream().forEach(v -> v.accept(cmd)); + uninstallVerifiers.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 static Map createDefaultPackageHandlers() { + HashMap handlers = new HashMap<>(); + if (TKit.isLinux()) { + handlers.put(PackageType.LINUX_DEB, LinuxHelper.createDebPackageHandlers()); + handlers.put(PackageType.LINUX_RPM, LinuxHelper.createRpmPackageHandlers()); + } + + if (TKit.isWindows()) { + handlers.put(PackageType.WIN_MSI, WindowsHelper.createMsiPackageHandlers()); + handlers.put(PackageType.WIN_EXE, WindowsHelper.createExePackageHandlers()); + } + + if (TKit.isOSX()) { + handlers.put(PackageType.MAC_DMG, MacHelper.createDmgPackageHandlers()); + handlers.put(PackageType.MAC_PKG, MacHelper.createPkgPackageHandlers()); + } + + return handlers; + } + 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; + private Map packageHandlers; - @Override - public String toString() { - return name().toLowerCase().replace('_', '-'); - } - }; - private final static Action DEFAULT_ACTION; - private final static File bundleOutputDir; + private final static File BUNDLE_OUTPUT_DIR; static { final String propertyName = "output"; String val = TKit.getConfigProperty(propertyName); if (val == null) { - bundleOutputDir = null; + BUNDLE_OUTPUT_DIR = null; } else { - bundleOutputDir = new File(val).getAbsoluteFile(); + BUNDLE_OUTPUT_DIR = new File(val).getAbsoluteFile(); - if (!bundleOutputDir.isDirectory()) { - throw new IllegalArgumentException(String.format( - "Invalid value of %s sytem property: [%s]. Should be existing directory", + if (!BUNDLE_OUTPUT_DIR.isDirectory()) { + throw new IllegalArgumentException(String.format("Invalid value of %s sytem property: [%s]. Should be existing directory", TKit.getConfigPropertyName(propertyName), - bundleOutputDir)); + BUNDLE_OUTPUT_DIR)); } } } - - 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))); - } }