diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java index 18645f17a6d..c95edbbad0a 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DeployParams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -166,8 +166,7 @@ public class DeployParams { } } else { if (!hasInput && !hasAppImage) { - throw new PackagerException( - "ERR_MissingArgument", "--input"); + throw new PackagerException("error.no-input-parameter"); } } } else { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DottedVersion.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DottedVersion.java index 91c0a4915fd..b188a9b552c 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DottedVersion.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/DottedVersion.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -57,9 +57,7 @@ final class DottedVersion { if (!greedy) { return null; } else { - throw new IllegalArgumentException(MessageFormat.format(I18N. - getString("error.version-string-zero-length-component"), - version)); + ds.throwException(); } } @@ -77,8 +75,7 @@ final class DottedVersion { }).takeWhile(Objects::nonNull).toArray(BigInteger[]::new); suffix = ds.getUnprocessedString(); if (!suffix.isEmpty() && greedy) { - throw new IllegalArgumentException(MessageFormat.format(I18N.getString( - "error.version-string-invalid-component"), version, suffix)); + ds.throwException(); } } } @@ -89,7 +86,7 @@ final class DottedVersion { this.input = input; } - public String getNextDigits() { + String getNextDigits() { if (stoped) { return null; } @@ -130,10 +127,29 @@ final class DottedVersion { return sb.toString(); } - public String getUnprocessedString() { + String getUnprocessedString() { return input.substring(cursor); } + void throwException() { + final String tail; + if (lastDotPos >= 0) { + tail = input.substring(lastDotPos + 1); + } else { + tail = getUnprocessedString(); + } + + final String errMessage; + if (tail.isEmpty()) { + errMessage = MessageFormat.format(I18N.getString( + "error.version-string-zero-length-component"), input); + } else { + errMessage = MessageFormat.format(I18N.getString( + "error.version-string-invalid-component"), input, tail); + } + throw new IllegalArgumentException(errMessage); + } + private int cursor; private int lastDotPos = -1; private boolean stoped; diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherData.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherData.java index ce6813de28b..c5a74ac09cc 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherData.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/LauncherData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -185,10 +185,6 @@ final class LauncherData { } Path mainJarDir = StandardBundlerParam.SOURCE_DIR.fetchFrom(params); - if (mainJarDir == null && launcherData.qualifiedClassName == null) { - throw new ConfigException(I18N.getString("error.no-input-parameter"), - null); - } final Path mainJarPath; if (launcherData.mainJarName != null && mainJarDir != null) { diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TokenReplace.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TokenReplace.java new file mode 100644 index 00000000000..24c13db1aa7 --- /dev/null +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/util/TokenReplace.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2025, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.internal.util; + +import static java.util.stream.Collectors.joining; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public final class TokenReplace { + + private record TokenCut(String[] main, String[] sub) { + static String[] orderTokens(String... tokens) { + if (tokens.length == 0) { + throw new IllegalArgumentException("Empty token list"); + } + + final var orderedTokens = Stream.of(tokens) + .sorted(Comparator.naturalOrder().thenComparing(Comparator.comparingInt(String::length))) + .distinct() + .toArray(String[]::new); + + if (orderedTokens[0].isEmpty()) { + throw new IllegalArgumentException("Empty token in the list of tokens"); + } + + return orderedTokens; + } + + static TokenCut createFromOrderedTokens(String... tokens) { + final List subTokens = new ArrayList<>(); + + for (var i = 0; i < tokens.length - 1; ++i) { + final var x = tokens[i]; + for (var j = i + 1; j < tokens.length; ++j) { + final var y = tokens[j]; + if (y.contains(x)) { + subTokens.add(i); + } + } + } + + if (subTokens.isEmpty()) { + return new TokenCut(tokens, null); + } else { + final var main = IntStream.range(0, tokens.length) + .mapToObj(Integer::valueOf) + .filter(Predicate.not(subTokens::contains)) + .map(i -> { + return tokens[i]; + }).toArray(String[]::new); + final var sub = subTokens.stream().map(i -> { + return tokens[i]; + }).toArray(String[]::new); + return new TokenCut(main, sub); + } + } + + @Override + public String toString() { + return String.format("TokenCut(main=%s, sub=%s)", Arrays.toString(main), Arrays.toString(sub)); + } + } + + public TokenReplace(String... tokens) { + tokens = TokenCut.orderTokens(tokens); + + this.tokens = tokens; + regexps = new ArrayList<>(); + + for(;;) { + final var tokenCut = TokenCut.createFromOrderedTokens(tokens); + regexps.add(Pattern.compile(Stream.of(tokenCut.main()).map(Pattern::quote).collect(joining("|", "(", ")")))); + + if (tokenCut.sub() == null) { + break; + } + + tokens = tokenCut.sub(); + } + } + + public String applyTo(String str, Function tokenValueSupplier) { + Objects.requireNonNull(str); + Objects.requireNonNull(tokenValueSupplier); + for (final var regexp : regexps) { + str = regexp.matcher(str).replaceAll(mr -> { + final var token = mr.group(); + return Matcher.quoteReplacement(Objects.requireNonNull(tokenValueSupplier.apply(token), () -> { + return String.format("Null value for token [%s]", token); + }).toString()); + }); + } + return str; + } + + public String recursiveApplyTo(String str, Function tokenValueSupplier) { + String newStr; + int counter = tokens.length + 1; + while (!(newStr = applyTo(str, tokenValueSupplier)).equals(str)) { + str = newStr; + if (counter-- == 0) { + throw new IllegalStateException("Infinite recursion"); + } + } + return newStr; + } + + @Override + public int hashCode() { + // Auto generated code + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(tokens); + return result; + } + + @Override + public boolean equals(Object obj) { + // Auto generated code + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TokenReplace other = (TokenReplace) obj; + return Arrays.equals(tokens, other.tokens); + } + + @Override + public String toString() { + return "TokenReplace(" + String.join("|", tokens) + ")"; + } + + public static TokenReplace combine(TokenReplace x, TokenReplace y) { + return new TokenReplace(Stream.of(x.tokens, y.tokens).flatMap(Stream::of).toArray(String[]::new)); + } + + public static Function createCachingTokenValueSupplier(Map> tokenValueSuppliers) { + Objects.requireNonNull(tokenValueSuppliers); + final Map cache = new HashMap<>(); + return token -> { + final var value = cache.computeIfAbsent(token, k -> { + final var tokenValueSupplier = Objects.requireNonNull(tokenValueSuppliers.get(token), () -> { + return String.format("No token value supplier for token [%s]", token); + }); + return Optional.ofNullable(tokenValueSupplier.get()).orElse(NULL_SUPPLIED); + }); + + if (value == NULL_SUPPLIED) { + throw new NullPointerException(String.format("Null value for token [%s]", token)); + } + + return value; + }; + } + + private final String[] tokens; + private final transient List regexps; + private final static Object NULL_SUPPLIED = new Object(); +} diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java b/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java index 97b1faee249..9ead69890cc 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2025, 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 @@ -25,15 +25,16 @@ package jdk.jpackage.main; -import jdk.internal.opt.CommandLine; -import jdk.jpackage.internal.Arguments; -import jdk.jpackage.internal.Log; -import jdk.jpackage.internal.CLIHelp; -import java.io.PrintWriter; -import java.util.ResourceBundle; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.NoSuchFileException; import java.text.MessageFormat; +import java.util.ResourceBundle; +import jdk.internal.opt.CommandLine; +import jdk.jpackage.internal.Arguments; +import jdk.jpackage.internal.CLIHelp; +import jdk.jpackage.internal.Log; public class Main { @@ -69,7 +70,7 @@ public class Main { String[] newArgs; try { newArgs = CommandLine.parse(args); - } catch (FileNotFoundException fnfe) { + } catch (FileNotFoundException|NoSuchFileException fnfe) { Log.fatalError(MessageFormat.format(I18N.getString( "ERR_CannotParseOptions"), fnfe.getMessage())); return 1; diff --git a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java index 433db6fc6f6..3e14022a70e 100644 --- a/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java +++ b/test/jdk/tools/jpackage/helpers-test/jdk/jpackage/test/PackageTestTest.java @@ -97,12 +97,12 @@ public class PackageTestTest extends JUnitAdapter { private final int tickCount; } - private final static int ERROR_EXIT_CODE_JPACKAGE = 35; - private final static int ERROR_EXIT_CODE_INSTALL = 27; + private static final int ERROR_EXIT_CODE_JPACKAGE = 35; + private static final int ERROR_EXIT_CODE_INSTALL = 27; - private final static CallbackFactory NEVER = new CallbackFactory(0); - private final static CallbackFactory ONCE = new CallbackFactory(1); - private final static CallbackFactory TWICE = new CallbackFactory(2); + private static final CallbackFactory NEVER = new CallbackFactory(0); + private static final CallbackFactory ONCE = new CallbackFactory(1); + private static final CallbackFactory TWICE = new CallbackFactory(2); enum BundleVerifier { ONCE_SUCCESS(ONCE), @@ -238,7 +238,7 @@ public class PackageTestTest extends JUnitAdapter { private final int expectedJPackageExitCode; } - private final static class CountingInstaller extends TickCounter implements Function { + private static final class CountingInstaller extends TickCounter implements Function { @Override public Integer apply(JPackageCommand cmd) { @@ -376,7 +376,7 @@ public class PackageTestTest extends JUnitAdapter { }; }).setExpectedExitCode(expectedJPackageExitCode) .setExpectedInstallExitCode(handlersSpec.installExitCode) - .isPackageTypeSupported(type -> true) + .isPackageTypeEnabled(type -> true) .forTypes().packageHandlers(handlers); } @@ -439,7 +439,7 @@ public class PackageTestTest extends JUnitAdapter { } } - private final static class TestSpecBuilder { + private static final class TestSpecBuilder { TestSpecBuilder type(PackageType v) { type = Objects.requireNonNull(v); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java index 8b28049b7b1..940dd47e06d 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/CannedFormattedString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -22,20 +22,60 @@ */ package jdk.jpackage.test; +import java.nio.file.Path; import java.util.List; +import java.util.Objects; import java.util.function.BiFunction; +import java.util.function.Supplier; +import java.util.stream.Stream; -public final class CannedFormattedString { +public record CannedFormattedString(BiFunction formatter, String key, Object[] args) { - CannedFormattedString(BiFunction formatter, - String key, Object[] args) { - this.formatter = formatter; - this.key = key; - this.args = args; + @FunctionalInterface + public interface CannedArgument { + public String value(); + } + + public static Object cannedArgument(Supplier supplier, String label) { + Objects.requireNonNull(supplier); + Objects.requireNonNull(label); + return new CannedArgument() { + + @Override + public String value() { + return supplier.get().toString(); + } + + @Override + public String toString( ) { + return label; + } + }; + } + + public static Object cannedAbsolutePath(Path v) { + return cannedArgument(() -> v.toAbsolutePath(), String.format("AbsolutePath(%s)", v)); + } + + public static Object cannedAbsolutePath(String v) { + return cannedAbsolutePath(Path.of(v)); + } + + public CannedFormattedString { + Objects.requireNonNull(formatter); + Objects.requireNonNull(key); + Objects.requireNonNull(args); + List.of(args).forEach(Objects::requireNonNull); } public String getValue() { - return formatter.apply(key, args); + return formatter.apply(key, Stream.of(args).map(arg -> { + if (arg instanceof CannedArgument cannedArg) { + return cannedArg.value(); + } else { + return arg; + } + }).toArray()); } @Override @@ -46,8 +86,4 @@ public final class CannedFormattedString { return String.format("%s+%s", key, List.of(args)); } } - - private final BiFunction formatter; - private final String key; - private final Object[] args; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index a8a849cb9e7..66cd422203b 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -33,8 +33,10 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -92,6 +94,13 @@ public final class Executor extends CommandArguments { public Executor removeEnvVar(String envVarName) { removeEnvVars.add(Objects.requireNonNull(envVarName)); + setEnvVars.remove(envVarName); + return this; + } + + public Executor setEnvVar(String envVarName, String envVarValue) { + setEnvVars.put(Objects.requireNonNull(envVarName), Objects.requireNonNull(envVarValue)); + removeEnvVars.remove(envVarName); return this; } @@ -370,11 +379,27 @@ public final class Executor extends CommandArguments { builder.directory(directory.toFile()); sb.append(String.format("; in directory [%s]", directory)); } - if (!removeEnvVars.isEmpty()) { - final var envComm = Comm.compare(builder.environment().keySet(), removeEnvVars); - builder.environment().keySet().removeAll(envComm.common()); + if (!setEnvVars.isEmpty()) { + final var defaultEnv = builder.environment(); + final var envComm = Comm.compare(defaultEnv.keySet(), setEnvVars.keySet()); + envComm.unique2().forEach(envVar -> { + trace(String.format("Adding %s=[%s] to environment", envVar, setEnvVars.get(envVar))); + }); envComm.common().forEach(envVar -> { - TKit.trace(String.format("Clearing %s in environment", envVar)); + final var curValue = defaultEnv.get(envVar); + final var newValue = setEnvVars.get(envVar); + if (!curValue.equals(newValue)) { + trace(String.format("Setting %s=[%s] in environment", envVar, setEnvVars.get(envVar))); + } + }); + defaultEnv.putAll(setEnvVars); + } + if (!removeEnvVars.isEmpty()) { + final var defaultEnv = builder.environment().keySet(); + final var envComm = Comm.compare(defaultEnv, removeEnvVars); + defaultEnv.removeAll(envComm.common()); + envComm.common().forEach(envVar -> { + trace(String.format("Clearing %s in environment", envVar)); }); } @@ -515,6 +540,7 @@ public final class Executor extends CommandArguments { private Set saveOutputType; private Path directory; private Set removeEnvVars = new HashSet<>(); + private Map setEnvVars = new HashMap<>(); private boolean winEnglishOutput; private String winTmpDir = null; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java index 224a3f752cf..7ac4159c2a6 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java @@ -31,6 +31,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -350,6 +351,7 @@ public final class HelloApp { this.launcherPath = helloAppLauncher; this.outputFilePath = TKit.workDir().resolve(OUTPUT_FILENAME); this.params = new HashMap<>(); + this.env = new HashMap<>(); this.defaultLauncherArgs = new ArrayList<>(); } @@ -363,6 +365,16 @@ public final class HelloApp { return this; } + public AppOutputVerifier addEnvironment(Map v) { + env.putAll(v); + return this; + } + + public AppOutputVerifier addEnvironmentVar(String name, String value) { + env.put(Objects.requireNonNull(name), Objects.requireNonNull(name)); + return this; + } + public AppOutputVerifier addDefaultArguments(String... v) { return addDefaultArguments(List.of(v)); } @@ -466,6 +478,10 @@ public final class HelloApp { .setExecutable(executablePath) .addArguments(List.of(args)); + env.forEach((envVarName, envVarValue) -> { + executor.setEnvVar(envVarName, envVarValue); + }); + return configureEnvironment(executor); } @@ -476,6 +492,7 @@ public final class HelloApp { private int expectedExitCode; private final List defaultLauncherArgs; private final Map params; + private final Map env; } public static AppOutputVerifier assertApp(Path helloAppLauncher) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java index efcd0041579..1a6fd9ac6ca 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -76,7 +76,7 @@ public class JPackageCommand extends CommandArguments { prerequisiteActions = new Actions(cmd.prerequisiteActions); verifyActions = new Actions(cmd.verifyActions); appLayoutAsserts = cmd.appLayoutAsserts; - outputValidator = cmd.outputValidator; + outputValidators = cmd.outputValidators; executeInDirectory = cmd.executeInDirectory; winMsiLogFile = cmd.winMsiLogFile; } @@ -218,26 +218,27 @@ public class JPackageCommand extends CommandArguments { } public String name() { - String appImage = getArgumentValue("--app-image"); - if (appImage != null) { - String name = AppImageFile.load(Path.of(appImage)).mainLauncherName(); - // can be null if using foreign app-image - return ((name != null) ? name : getArgumentValue("--name")); - } - return getArgumentValue("--name", () -> getArgumentValue("--main-class")); + return nameFromAppImage().or(this::nameFromBasicArgs).or(this::nameFromRuntimeImage).orElseThrow(); } public String installerName() { verifyIsOfType(PackageType.NATIVE); - String installerName = getArgumentValue("--name", - () -> getArgumentValue("--main-class", () -> null)); - if (installerName == null) { - String appImage = getArgumentValue("--app-image"); - if (appImage != null) { - installerName = AppImageFile.load(Path.of(appImage)).mainLauncherName(); - } - } - return installerName; + return nameFromBasicArgs().or(this::nameFromAppImage).or(this::nameFromRuntimeImage).orElseThrow(); + } + + private Optional nameFromAppImage() { + return Optional.ofNullable(getArgumentValue("--app-image")) + .map(Path::of).map(AppImageFile::load).map(AppImageFile::mainLauncherName); + } + + private Optional nameFromRuntimeImage() { + return Optional.ofNullable(getArgumentValue("--runtime-image")) + .map(Path::of).map(Path::getFileName).map(Path::toString); + } + + private Optional nameFromBasicArgs() { + return Optional.ofNullable(getArgumentValue("--name")).or( + () -> Optional.ofNullable(getArgumentValue("--main-class"))); } public boolean isRuntime() { @@ -273,7 +274,7 @@ public class JPackageCommand extends CommandArguments { }; addPrerequisiteAction(cmd -> { - Path fakeRuntimeDir = TKit.workDir().resolve("fake_runtime"); + Path fakeRuntimeDir = TKit.createTempDirectory("fake_runtime"); TKit.trace(String.format("Init fake runtime in [%s] directory", fakeRuntimeDir)); @@ -703,21 +704,53 @@ public class JPackageCommand extends CommandArguments { } public JPackageCommand validateOutput(TKit.TextStreamVerifier validator) { - return JPackageCommand.this.validateOutput(validator::apply); + return validateOutput(validator::apply); } public JPackageCommand validateOutput(Consumer> validator) { - if (validator != null) { - saveConsoleOutput(true); - outputValidator = validator; - } else { - outputValidator = null; - } + Objects.requireNonNull(validator); + saveConsoleOutput(true); + outputValidators.add(validator); return this; } - public JPackageCommand validateOutput(CannedFormattedString str) { - return JPackageCommand.this.validateOutput(TKit.assertTextStream(str.getValue())); + @FunctionalInterface + public interface CannedArgument { + public String value(JPackageCommand cmd); + } + + public static Object cannedArgument(Function supplier, String label) { + Objects.requireNonNull(supplier); + Objects.requireNonNull(label); + return new CannedArgument() { + @Override + public String value(JPackageCommand cmd) { + return supplier.apply(cmd).toString(); + } + + @Override + public String toString( ) { + return label; + } + }; + } + + public String getValue(CannedFormattedString str) { + return new CannedFormattedString(str.formatter(), str.key(), Stream.of(str.args()).map(arg -> { + if (arg instanceof CannedArgument cannedArg) { + return cannedArg.value(this); + } else { + return arg; + } + }).toArray()).getValue(); + } + + public JPackageCommand validateOutput(CannedFormattedString... str) { + // Will look up the given errors in the order they are specified. + return validateOutput(Stream.of(str) + .map(this::getValue) + .map(TKit::assertTextStream) + .reduce(TKit.TextStreamVerifier::andThen).get()); } public boolean isWithToolProvider() { @@ -798,7 +831,7 @@ public class JPackageCommand extends CommandArguments { .createExecutor() .execute(expectedExitCode); - if (outputValidator != null) { + for (final var outputValidator: outputValidators) { outputValidator.accept(result.getOutput().stream()); } @@ -876,6 +909,9 @@ public class JPackageCommand extends CommandArguments { copy.immutable = false; copy.removeArgumentWithValue("--runtime-image"); copy.dmgInstallDir = cmd.appInstallationDirectory(); + if (!copy.hasArgument("--name")) { + copy.addArguments("--name", cmd.nameFromRuntimeImage().orElseThrow()); + } return copy; } @@ -1022,7 +1058,7 @@ public class JPackageCommand extends CommandArguments { // to allow the jlink process to print exception stacktraces on any failure addArgument("-J-Djlink.debug=true"); } - if (!hasArgument("--runtime-image") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) { + if (!hasArgument("--runtime-image") && !hasArgument("--jlink-options") && !hasArgument("--app-image") && DEFAULT_RUNTIME_IMAGE != null && !ignoreDefaultRuntime) { addArguments("--runtime-image", DEFAULT_RUNTIME_IMAGE); } @@ -1195,14 +1231,14 @@ public class JPackageCommand extends CommandArguments { private Path executeInDirectory; private Path winMsiLogFile; private Set appLayoutAsserts = Set.of(AppLayoutAssert.values()); - private Consumer> outputValidator; + private List>> outputValidators = new ArrayList<>(); private static boolean defaultWithToolProvider; private static final Map PACKAGE_TYPES = Functional.identity( () -> { Map reply = new HashMap<>(); for (PackageType type : PackageType.values()) { - reply.put(type.getName(), type); + reply.put(type.getType(), type); } return reply; }).get(); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java index fff08d35410..cdf2855faef 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageStringBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -64,7 +64,7 @@ public enum JPackageStringBundle { } } - public CannedFormattedString cannedFormattedString(String key, String ... args) { + public CannedFormattedString cannedFormattedString(String key, Object ... args) { return new CannedFormattedString(this::getFormattedString, key, args); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java index 439479a666e..e7f06b0d608 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -23,6 +23,10 @@ package jdk.jpackage.test; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toMap; +import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX; + import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayDeque; @@ -32,9 +36,7 @@ import java.util.Deque; import java.util.List; import java.util.function.Function; import java.util.function.Predicate; -import static java.util.stream.Collectors.toCollection; import java.util.stream.Stream; -import static jdk.jpackage.test.TestBuilder.CMDLINE_ARG_PREFIX; public final class Main { @@ -88,9 +90,7 @@ public final class Main { TKit.unbox(throwable); } finally { if (!success) { - TKit.log( - String.format("Error processing parameter=[%s]", - arg)); + TKit.log(String.format("Error processing parameter=[%s]", arg)); } } } @@ -104,6 +104,13 @@ public final class Main { // Just list the tests orderedTests.forEach(test -> System.out.println(String.format( "%s; workDir=[%s]", test.fullName(), test.workDir()))); + } + + orderedTests.stream().collect(toMap(TestInstance::fullName, x -> x, (x, y) -> { + throw new IllegalArgumentException(String.format("Multiple tests with the same description: [%s]", x.fullName())); + })); + + if (listTests) { return; } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java index e7b3f3e3a44..5500b670fbc 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java @@ -71,7 +71,7 @@ import jdk.jpackage.internal.util.function.ThrowingRunnable; public final class PackageTest extends RunnablePackageTest { public PackageTest() { - isPackageTypeSupported = PackageType::isSupported; + isPackageTypeEnabled = PackageType::isEnabled; jpackageFactory = JPackageCommand::new; packageHandlers = new HashMap<>(); disabledInstallers = new HashSet<>(); @@ -102,7 +102,7 @@ public final class PackageTest extends RunnablePackageTest { newTypes = Stream.of(types).collect(Collectors.toSet()); } currentTypes = newTypes.stream() - .filter(isPackageTypeSupported) + .filter(isPackageTypeEnabled) .filter(Predicate.not(excludeTypes::contains)) .collect(Collectors.toUnmodifiableSet()); return this; @@ -394,9 +394,9 @@ public final class PackageTest extends RunnablePackageTest { return this; } - PackageTest isPackageTypeSupported(Predicate v) { + PackageTest isPackageTypeEnabled(Predicate v) { Objects.requireNonNull(v); - isPackageTypeSupported = v; + isPackageTypeEnabled = v; return this; } @@ -505,7 +505,7 @@ public final class PackageTest extends RunnablePackageTest { case UNPACK -> { cmd.setUnpackedPackageLocation(null); final var unpackRootDir = TKit.createTempDirectory( - String.format("unpacked-%s", type.getName())); + String.format("unpacked-%s", type.getType())); final Path unpackDir = packageHandlers.unpack(cmd, unpackRootDir); if (!unpackDir.startsWith(TKit.workDir())) { state.deleteUnpackDirs.add(unpackDir); @@ -618,7 +618,7 @@ public final class PackageTest extends RunnablePackageTest { return processed(Action.UNPACK) && packageHandlers.unpackHandler().isEmpty(); } - private final static class State { + private static final class State { private final Set packageActions = new HashSet<>(); private final List deleteUnpackDirs = new ArrayList<>(); } @@ -918,7 +918,7 @@ public final class PackageTest extends RunnablePackageTest { private final Map packageHandlers; private final Set disabledInstallers; private final Set disabledUninstallers; - private Predicate isPackageTypeSupported; + private Predicate isPackageTypeEnabled; private Supplier jpackageFactory; private boolean ignoreBundleOutputDir; private boolean createMsiLog; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java index 4d85f82df9b..f6c2b84057a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageType.java @@ -26,6 +26,7 @@ import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -48,19 +49,23 @@ public enum PackageType { TKit.isLinux() ? "jdk.jpackage.internal.LinuxRpmBundler" : null), MAC_DMG(".dmg", TKit.isOSX() ? "jdk.jpackage.internal.MacDmgBundler" : null), MAC_PKG(".pkg", TKit.isOSX() ? "jdk.jpackage.internal.MacPkgBundler" : null), - IMAGE("app-image", null, null); + IMAGE; + + PackageType() { + type = "app-image"; + suffix = null; + supported = true; + enabled = true; + } PackageType(String packageName, String bundleSuffix, String bundlerClass) { - name = packageName; - suffix = bundleSuffix; - if (bundlerClass != null && !Inner.DISABLED_PACKAGERS.contains(getName())) { - supported = isBundlerSupported(bundlerClass); - } else { - supported = false; - } + type = Objects.requireNonNull(packageName); + suffix = Objects.requireNonNull(bundleSuffix); + supported = Optional.ofNullable(bundlerClass).map(PackageType::isBundlerSupported).orElse(false); + enabled = supported && !Inner.DISABLED_PACKAGERS.contains(getType()); - if (suffix != null && supported) { - TKit.trace(String.format("Bundler %s supported", getName())); + if (suffix != null && enabled) { + TKit.trace(String.format("Bundler %s enabled", getType())); } } @@ -69,30 +74,23 @@ public enum PackageType { } void applyTo(JPackageCommand cmd) { - cmd.setArgumentValue("--type", getName()); + cmd.setArgumentValue("--type", getType()); } String getSuffix() { - return suffix; + return Optional.ofNullable(suffix).orElseThrow(UnsupportedOperationException::new); } - boolean isSupported() { + public boolean isSupported() { return supported; } - String getName() { - return name; + public boolean isEnabled() { + return supported; } - static PackageType fromSuffix(String packageFilename) { - if (packageFilename != null) { - for (PackageType v : values()) { - if (packageFilename.endsWith(v.getSuffix())) { - return v; - } - } - } - return null; + public String getType() { + return type; } private static boolean isBundlerSupportedImpl(String bundlerClass) { @@ -133,8 +131,9 @@ public enum PackageType { return reply.get(); } - private final String name; + private final String type; private final String suffix; + private final boolean enabled; private final boolean supported; public static final Set LINUX = Set.of(LINUX_DEB, LINUX_RPM); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java index ca9523d5760..159a46c96c2 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/TestInstance.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.HexFormat; import java.util.List; import java.util.Map; import java.util.Objects; @@ -119,12 +120,24 @@ final class TestInstance implements ThrowingRunnable { return String.format("%s(length=%d)", asString, Array.getLength(v)); } return String.format("%s", v); - }).collect(Collectors.joining(", ")); + }).collect(Collectors.joining(", ")).transform(str -> { + final var sb = new StringBuilder(); + for (var chr : str.toCharArray()) { + if (chr != ' ' && (Character.isWhitespace(chr) || Character.isISOControl(chr))) { + sb.append("\\u").append(ARGS_CHAR_FORMATTER.toHexDigits(chr)); + } else { + sb.append(chr); + } + } + return sb.toString(); + }); } private List ctorArgs; private List methodArgs; private Method method; + + private static final HexFormat ARGS_CHAR_FORMATTER = HexFormat.of().withUpperCase(); } static TestDesc create(Method m, Object... args) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java index 91705afd5fe..bc67f6e417d 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -107,7 +107,7 @@ public class WindowsHelper { final Optional msiLogFile; if (createMsiLog) { msiLogFile = Optional.of(TKit.createTempFile(String.format("logs\\%s-msi.log", - cmd.packageType().getName()))); + cmd.packageType().getType()))); } else { msiLogFile = Optional.empty(); } @@ -175,13 +175,19 @@ public class WindowsHelper { Path installationSubDirectory = getInstallationSubDirectory(cmd); Path from = Path.of(extraPathComponent).resolve(installationSubDirectory); Path to = installationSubDirectory; - TKit.trace(String.format("Convert [%s] into [%s] in [%s] directory", from, to, - unpackDir)); + ThrowingRunnable.toRunnable(() -> { Files.createDirectories(unpackDir.resolve(to).getParent()); - Files.move(unpackDir.resolve(from), unpackDir.resolve(to)); - TKit.deleteDirectoryRecursive(unpackDir.resolve(extraPathComponent)); }).run(); + + // Files.move() occasionally results into java.nio.file.AccessDeniedException + Executor.tryRunMultipleTimes(ThrowingRunnable.toRunnable(() -> { + TKit.trace(String.format("Convert [%s] into [%s] in [%s] directory", from, to, unpackDir)); + final var dstDir = unpackDir.resolve(to); + TKit.deleteDirectoryRecursive(dstDir); + Files.move(unpackDir.resolve(from), dstDir); + TKit.deleteDirectoryRecursive(unpackDir.resolve(extraPathComponent)); + }), 3, 5); } } return destinationDir; @@ -656,7 +662,7 @@ public class WindowsHelper { private final RegValuePath reg; private final Optional alt; - private final static Map CACHE = new ConcurrentHashMap<>(); + private static final Map CACHE = new ConcurrentHashMap<>(); } private static final class ShortPathUtils { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DottedVersionTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DottedVersionTest.java index c9c2b018556..82567824189 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DottedVersionTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/DottedVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -22,8 +22,10 @@ */ package jdk.jpackage.internal; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -112,28 +114,57 @@ public class DottedVersionTest { return data; } - @ParameterizedTest - @MethodSource - public void testInvalid(String str) { - assertThrowsExactly(IllegalArgumentException.class, () -> new DottedVersion(str)); + record InvalidVersionTestSpec(String version, String invalidComponent) { + public InvalidVersionTestSpec { + Objects.requireNonNull(version); + Objects.requireNonNull(invalidComponent); + } + + InvalidVersionTestSpec(String version) { + this(version, ""); + } + + void run() { + final String expectedErrorMsg; + if (invalidComponent.isEmpty()) { + expectedErrorMsg = MessageFormat.format(I18N.getString("error.version-string-zero-length-component"), version); + } else { + expectedErrorMsg = MessageFormat.format(I18N.getString("error.version-string-invalid-component"), version, invalidComponent); + } + + final var ex = assertThrowsExactly(IllegalArgumentException.class, () -> new DottedVersion(version)); + + assertEquals(expectedErrorMsg, ex.getMessage()); + } } - private static Stream testInvalid() { + @ParameterizedTest + @MethodSource + public void testInvalid(InvalidVersionTestSpec testSpec) { + testSpec.run(); + } + + private static Stream testInvalid() { return Stream.of( - "1.-1", - "5.", - "4.2.", - "3..2", - "2.a", - "0a", - ".", - " ", - " 1", - "1. 2", - "+1", - "-1", - "-0", - "+0" + new InvalidVersionTestSpec("1.-1", "-1"), + new InvalidVersionTestSpec("5."), + new InvalidVersionTestSpec("4.2."), + new InvalidVersionTestSpec("3..2", ".2"), + new InvalidVersionTestSpec("3...2", "..2"), + new InvalidVersionTestSpec("2.a", "a"), + new InvalidVersionTestSpec("0a", "a"), + new InvalidVersionTestSpec("1.0a", "0a"), + new InvalidVersionTestSpec(".", "."), + new InvalidVersionTestSpec("..", ".."), + new InvalidVersionTestSpec(".a.b", ".a.b"), + new InvalidVersionTestSpec(".1.2", ".1.2"), + new InvalidVersionTestSpec(" ", " "), + new InvalidVersionTestSpec(" 1", " 1"), + new InvalidVersionTestSpec("1. 2", " 2"), + new InvalidVersionTestSpec("+1", "+1"), + new InvalidVersionTestSpec("-1", "-1"), + new InvalidVersionTestSpec("-0", "-0"), + new InvalidVersionTestSpec("+0", "+0") ); } @@ -145,7 +176,8 @@ public class DottedVersionTest { @Test public void testEmptyGreedy() { - assertThrowsExactly(IllegalArgumentException.class, () -> DottedVersion.greedy(""), "Version may not be empty string"); + final var ex = assertThrowsExactly(IllegalArgumentException.class, () -> DottedVersion.greedy("")); + assertEquals(I18N.getString("error.version-string-empty"), ex.getMessage()); } @Test diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/TokenReplaceTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/TokenReplaceTest.java new file mode 100644 index 00000000000..01d1d10ef9d --- /dev/null +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/TokenReplaceTest.java @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2025, 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.internal.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class TokenReplaceTest { + + public record TestSpec(String str, Optional expectedStr, Optional expectedCtorException, + Optional expectedApplyToException, Map tokenWithValues, boolean recursive) { + + public TestSpec { + Objects.requireNonNull(expectedStr); + Objects.requireNonNull(expectedCtorException); + Objects.requireNonNull(expectedApplyToException); + Objects.requireNonNull(tokenWithValues); + tokenWithValues.values().forEach(Objects::requireNonNull); + + if (expectedStr.isPresent()) { + if (!(expectedCtorException.isEmpty() && expectedApplyToException.isEmpty())) { + throw new IllegalArgumentException(); + } + } else if (expectedCtorException.isEmpty() == expectedApplyToException.isEmpty()) { + throw new IllegalArgumentException(); + } + } + + static final class Builder { + + Builder str(String v) { + str = v; + return this; + } + + Builder recursive(boolean v) { + recursive = v; + return this; + } + + Builder recursive() { + return recursive(true); + } + + Builder expect(String v) { + expectedStr = v; + return this; + } + + Builder expectCtorThrow(String v) { + expectedCtorException = new IllegalArgumentException(v); + return this; + } + + Builder expectApplyToNPE() { + expectedApplyToException = new NullPointerException(); + return this; + } + + Builder expectInfiniteRecursion() { + expectedApplyToException = new IllegalStateException("Infinite recursion"); + return this; + } + + Builder token(String token, String value) { + tokenWithValues.put(token, value); + return this; + } + + TestSpec create() { + return new TestSpec(str, expectedStr(), Optional.ofNullable(expectedCtorException), + Optional.ofNullable(expectedApplyToException), tokenWithValues, recursive); + } + + private Optional expectedStr() { + if (expectedCtorException == null && expectedApplyToException == null) { + return Optional.ofNullable(expectedStr).or(() -> Optional.of(str)); + } else { + return Optional.empty(); + } + } + + private boolean recursive; + private String str; + private String expectedStr; + private Exception expectedCtorException; + private Exception expectedApplyToException; + private final Map tokenWithValues = new HashMap<>(); + } + + void test() { + final var tokens = tokenWithValues.keySet().toArray(String[]::new); + expectedStr.ifPresent(expected -> { + final var tokenReplace = new TokenReplace(tokens); + final String actual; + if (recursive) { + actual = tokenReplace.recursiveApplyTo(str, tokenWithValues::get); + } else { + actual = tokenReplace.applyTo(str, tokenWithValues::get); + } + assertEquals(expected, actual); + }); + + expectedCtorException.ifPresent(expected -> { + final var ex = assertThrows(expected.getClass(), () -> { + new TokenReplace(tokens); + }); + assertEquals(expected.getMessage(), ex.getMessage()); + }); + + expectedApplyToException.ifPresent(expected -> { + final var tokenReplace = new TokenReplace(tokens); + final var ex = assertThrows(expected.getClass(), () -> { + if (recursive) { + tokenReplace.recursiveApplyTo(str, tokenWithValues::get); + } else { + tokenReplace.applyTo(str, tokenWithValues::get); + } + }); + assertEquals(expected.getMessage(), ex.getMessage()); + }); + } + } + + @ParameterizedTest + @MethodSource + public void test(TestSpec spec) { + spec.test(); + } + + public static Stream test() { + return Stream.of( + testSpec("foo").token("", "B").expectCtorThrow("Empty token in the list of tokens"), + testSpec("foo").expectCtorThrow("Empty token list"), + testSpec("a").expect("a").token("b", "B"), + testSpec("a").expect("A").token("a", "A"), + testSpec("aaa").expect("AAA").token("a", "A"), + testSpec("aaa").recursive().expect("{B}{B}{B}").token("a", "b").token("b", "{B}"), + testSpec("aaa").token("a", "aa").token("aa", "C").expect("Caa"), + testSpec("aaa").token("a", "aa").token("aa", "C").expect("CC").recursive(), + testSpec("aaa").expect("A2A").token("a", "A").token("aa", "A2"), + testSpec("aaa").token("a", "b").token("b", "c").token("c", "a").expect("bbb"), + testSpec("aaa").token("a", "b").token("b", "").recursive().expect(""), + testSpec("aaa").token("a", "").recursive().expect(""), + testSpec("aaa").token("a", "b").token("b", "c").token("c", "a").expectInfiniteRecursion().recursive(), + testSpec(null).token("a", "b").expectApplyToNPE(), + testSpec("abc").expect("abc").token(".", "A"), + testSpec("abc.").expect("abcD").token(".", "D") + ).map(TestSpec.Builder::create); + } + + private static final class CountingSupplier implements Supplier { + + CountingSupplier(Object value, int expectedCount) { + this.value = value; + this.expectedCount = expectedCount; + } + + @Override + public Object get() { + counter++; + return value; + } + + public Object value() { + return value; + } + + void verifyCount() { + assertEquals(expectedCount, counter); + } + + private final Object value; + private int counter; + private final int expectedCount; + } + + @Test + public void testCombine() { + final var x = new TokenReplace("a"); + final var y = new TokenReplace("aa"); + + final var xy = TokenReplace.combine(x, y); + + assertEquals(xy, new TokenReplace("aa", "a")); + assertEquals(xy, new TokenReplace("a", "aa")); + } + + @Test + public void testCombine2() { + final var x = new TokenReplace("a"); + final var y = new TokenReplace("a"); + + final var xy = TokenReplace.combine(x, y); + + assertEquals(xy, new TokenReplace("a", "a")); + assertEquals(xy, new TokenReplace("a")); + assertEquals(xy, x); + assertEquals(xy, y); + } + + @Test + public void testCombine3() { + final var x = new TokenReplace("a"); + final var y = new TokenReplace("b"); + + final var xy = TokenReplace.combine(x, y); + + assertEquals(xy, new TokenReplace("a", "b")); + assertEquals(xy, new TokenReplace("b", "a")); + } + + @Test + public void testEquals() { + final var x = new TokenReplace("x"); + final var y = new TokenReplace("y"); + final var y2 = new TokenReplace("y"); + + assertNotEquals(x, y); + assertNotEquals(x, null); + assertNotEquals(null, x); + assertNotEquals(x, "x"); + + assertEquals(y, y2); + assertEquals(y, y); + } + + @Test + public void testCreateCachingTokenValueSupplier() { + final var neverCalledSupplier = new CountingSupplier("", 0); + final var calledOnceSupplier = new CountingSupplier("foo", 1); + final var calledOnceNullSupplier = new CountingSupplier(null, 1); + + final var supplier = TokenReplace.createCachingTokenValueSupplier(Map.of( + "never", neverCalledSupplier, + "once", calledOnceSupplier, + "onceNull", calledOnceNullSupplier + )); + + for (int i = 0; i != 2; i++) { + assertEquals(calledOnceSupplier.value(), supplier.apply("once")); + + final var ex = assertThrows(NullPointerException.class, () -> supplier.apply("onceNull")); + assertEquals("Null value for token [onceNull]", ex.getMessage()); + } + + final var ex = assertThrows(NullPointerException.class, () -> supplier.apply("foo")); + assertEquals("No token value supplier for token [foo]", ex.getMessage()); + + neverCalledSupplier.verifyCount(); + calledOnceSupplier.verifyCount(); + calledOnceNullSupplier.verifyCount(); + } + + private static TestSpec.Builder testSpec(String str) { + return new TestSpec.Builder().str(str); + } +} diff --git a/test/jdk/tools/jpackage/linux/LinuxResourceTest.java b/test/jdk/tools/jpackage/linux/LinuxResourceTest.java index 1dc8dee3c97..8371dc01e43 100644 --- a/test/jdk/tools/jpackage/linux/LinuxResourceTest.java +++ b/test/jdk/tools/jpackage/linux/LinuxResourceTest.java @@ -21,14 +21,19 @@ * questions. */ +import static jdk.jpackage.test.JPackageStringBundle.MAIN; + import java.io.IOException; import java.nio.file.Path; -import jdk.jpackage.test.TKit; +import java.util.List; +import java.util.Objects; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.LinuxHelper; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; -import jdk.jpackage.test.LinuxHelper; -import jdk.jpackage.test.Annotations.Test; -import java.util.List; +import jdk.jpackage.test.RunnablePackageTest.Action; +import jdk.jpackage.test.TKit; /* * @test @@ -55,48 +60,47 @@ public class LinuxResourceTest { }) .forTypes(PackageType.LINUX_DEB) .addInitializer(cmd -> { - Path controlFile = Path.of(cmd.getArgumentValue("--resource-dir"), - "control"); + Path controlFile = Path.of(cmd.getArgumentValue("--resource-dir"), "control"); + + final var packageProp = property("Package", "dont-install-me"); + final var verProp = property("Version", "1.2.3-R2"); + final var arhProp = property("Architecture", "bar"); + TKit.createTextFile(controlFile, List.of( - "Package: dont-install-me", - "Version: 1.2.3-R2", + packageProp.format(), + verProp.format(), "Section: APPLICATION_SECTION", "Maintainer: APPLICATION_MAINTAINER", "Priority: optional", - "Architecture: bar", + arhProp.format(), "Provides: dont-install-me", "Description: APPLICATION_DESCRIPTION", "Installed-Size: APPLICATION_INSTALLED_SIZE", "Depends: PACKAGE_DEFAULT_DEPENDENCIES" )); - }) - .addBundleVerifier((cmd, result) -> { - TKit.assertTextStream("Using custom package resource [DEB control file]") - .predicate(String::contains) - .apply(result.getOutput().stream()); - TKit.assertTextStream(String.format( - "Expected value of \"Package\" property is [%s]. Actual value in output package is [dont-install-me]", - LinuxHelper.getPackageName(cmd))) - .predicate(String::contains) - .apply(result.getOutput().stream()); - TKit.assertTextStream( - "Expected value of \"Version\" property is [1.0]. Actual value in output package is [1.2.3-R2]") - .predicate(String::contains) - .apply(result.getOutput().stream()); - TKit.assertTextStream(String.format( - "Expected value of \"Architecture\" property is [%s]. Actual value in output package is [bar]", - LinuxHelper.getDefaultPackageArch(cmd.packageType()))) - .predicate(String::contains) - .apply(result.getOutput().stream()); + + cmd.validateOutput(MAIN.cannedFormattedString( + "message.using-custom-resource", + String.format("[%s]", MAIN.cannedFormattedString("resource.deb-control-file").getValue()), + controlFile.getFileName())); + + packageProp.expectedValue(LinuxHelper.getPackageName(cmd)).token("APPLICATION_PACKAGE").resourceDirFile(controlFile).validateOutput(cmd); + verProp.expectedValue(cmd.version()).token("APPLICATION_VERSION_WITH_RELEASE").resourceDirFile(controlFile).validateOutput(cmd); + arhProp.expectedValue(LinuxHelper.getDefaultPackageArch(cmd.packageType())).token("APPLICATION_ARCH").resourceDirFile(controlFile).validateOutput(cmd); }) .forTypes(PackageType.LINUX_RPM) .addInitializer(cmd -> { Path specFile = Path.of(cmd.getArgumentValue("--resource-dir"), LinuxHelper.getPackageName(cmd) + ".spec"); + + final var packageProp = property("Name", "dont-install-me"); + final var verProp = property("Version", "1.2.3"); + final var releaseProp = property("Release", "R2"); + TKit.createTextFile(specFile, List.of( - "Name: dont-install-me", - "Version: 1.2.3", - "Release: R2", + packageProp.format(), + verProp.format(), + releaseProp.format(), "Summary: APPLICATION_SUMMARY", "License: APPLICATION_LICENSE_TYPE", "Prefix: %{dirname:APPLICATION_DIRECTORY}", @@ -113,25 +117,77 @@ public class LinuxResourceTest { "%files", "APPLICATION_DIRECTORY" )); + + cmd.validateOutput(MAIN.cannedFormattedString( + "message.using-custom-resource", + String.format("[%s]", MAIN.cannedFormattedString("resource.rpm-spec-file").getValue()), + specFile.getFileName())); + + packageProp.expectedValue(LinuxHelper.getPackageName(cmd)).token("APPLICATION_PACKAGE").resourceDirFile(specFile).validateOutput(cmd); + verProp.expectedValue(cmd.version()).token("APPLICATION_VERSION").resourceDirFile(specFile).validateOutput(cmd); + releaseProp.expectedValue("1").token("APPLICATION_RELEASE").resourceDirFile(specFile).validateOutput(cmd); }) - .addBundleVerifier((cmd, result) -> { - TKit.assertTextStream("Using custom package resource [RPM spec file]") - .predicate(String::contains) - .apply(result.getOutput().stream()); - TKit.assertTextStream(String.format( - "Expected value of \"Name\" property is [%s]. Actual value in output package is [dont-install-me]", - LinuxHelper.getPackageName(cmd))) - .predicate(String::contains) - .apply(result.getOutput().stream()); - TKit.assertTextStream( - "Expected value of \"Version\" property is [1.0]. Actual value in output package is [1.2.3]") - .predicate(String::contains) - .apply(result.getOutput().stream()); - TKit.assertTextStream( - "Expected value of \"Release\" property is [1]. Actual value in output package is [R2]") - .predicate(String::contains) - .apply(result.getOutput().stream()); - }) - .run(); + .run(Action.CREATE); + } + + private static final class PropertyValidator { + + PropertyValidator name(String v) { + name = v; + return this; + } + + PropertyValidator customValue(String v) { + customValue = v; + return this; + } + + PropertyValidator expectedValue(String v) { + expectedValue = v; + return this; + } + + PropertyValidator token(String v) { + token = v; + return this; + } + + PropertyValidator resourceDirFile(Path v) { + resourceDirFile = v; + return this; + } + + String format() { + Objects.requireNonNull(name); + Objects.requireNonNull(customValue); + return String.format("%s: %s", name, customValue); + } + + void validateOutput(JPackageCommand cmd) { + Objects.requireNonNull(name); + Objects.requireNonNull(customValue); + Objects.requireNonNull(expectedValue); + Objects.requireNonNull(token); + Objects.requireNonNull(resourceDirFile); + + final var customResourcePath = customResourcePath(); + cmd.validateOutput( + MAIN.cannedFormattedString("error.unexpected-package-property", name, expectedValue, customValue, customResourcePath), + MAIN.cannedFormattedString("error.unexpected-package-property.advice", token, customValue, name, customResourcePath)); + } + + private Path customResourcePath() { + return resourceDirFile.getFileName(); + } + + private String name; + private String customValue; + private String expectedValue; + private String token; + private Path resourceDirFile; + } + + private static PropertyValidator property(String name, String customValue) { + return new PropertyValidator().name(name).customValue(customValue); } } diff --git a/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java b/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java index 1501fd15d8f..0f7534aa88d 100644 --- a/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java +++ b/test/jdk/tools/jpackage/macosx/MacAppStoreJlinkOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -25,9 +25,7 @@ import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.Annotations.Test; /** - * Tests generation of app image with --mac-app-store and --jlink-options. jpackage should able - * to generate app image if "--strip-native-commands" is specified for --jlink-options and should - * fail if it is not specified. + * Tests generation of app image with --mac-app-store and --jlink-options. */ /* @@ -50,13 +48,4 @@ public class MacAppStoreJLinkOptionsTest { cmd.executeAndAssertHelloAppImageCreated(); } - - @Test - public static void testWithoutStripNativeCommands() throws Exception { - JPackageCommand cmd = JPackageCommand.helloAppImage(); - cmd.addArguments("--mac-app-store", "--jlink-options", - "--strip-debug --no-man-pages --no-header-files"); - - cmd.execute(1); - } } diff --git a/test/jdk/tools/jpackage/macosx/MacAppStoreRuntimeTest.java b/test/jdk/tools/jpackage/macosx/MacAppStoreRuntimeTest.java deleted file mode 100644 index ab8b8590b33..00000000000 --- a/test/jdk/tools/jpackage/macosx/MacAppStoreRuntimeTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2022, 2024, 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 java.nio.file.Files; -import java.nio.file.Path; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.Executor; -import jdk.jpackage.test.TKit; -import jdk.jpackage.test.JavaTool; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.Annotations.Parameter; - - -/** - * Tests generation of app image with --mac-app-store and --runtime-image. jpackage should able - * to generate app image if runtime image does not have "bin" folder and fail otherwise. - */ - -/* - * @test - * @summary jpackage with --mac-app-store and --runtime-image - * @library /test/jdk/tools/jpackage/helpers - * @build jdk.jpackage.test.* - * @build MacAppStoreRuntimeTest - * @requires (os.family == "mac") - * @run main/othervm -Xmx512m jdk.jpackage.test.Main - * --jpt-run=MacAppStoreRuntimeTest - */ -public class MacAppStoreRuntimeTest { - - private static String getRuntimeImage(boolean stripNativeCommands) throws IOException { - final Path workDir = TKit.createTempDirectory("runtime").resolve("data"); - final Path jlinkOutputDir = workDir.resolve("temp.runtime"); - Files.createDirectories(jlinkOutputDir.getParent()); - - // List of modules required for test app. - final var modules = new String[] { - "java.base", - "java.desktop" - }; - - List jlinkArgs = new ArrayList<>(); - jlinkArgs.add("--output"); - jlinkArgs.add(jlinkOutputDir.toString()); - jlinkArgs.add("--add-modules"); - jlinkArgs.add(String.join(",", modules)); - jlinkArgs.add("--strip-debug"); - jlinkArgs.add("--no-header-files"); - jlinkArgs.add("--no-man-pages"); - if (stripNativeCommands) { - jlinkArgs.add("--strip-native-commands"); - } - - new Executor() - .setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments(jlinkArgs) - .execute(); - - return jlinkOutputDir.toString(); - } - - @Test - @Parameter("true") - @Parameter("false") - public static void test(boolean stripNativeCommands) throws Exception { - JPackageCommand cmd = JPackageCommand.helloAppImage(); - cmd.addArguments("--mac-app-store", "--runtime-image", getRuntimeImage(stripNativeCommands)); - - if (stripNativeCommands) { - cmd.executeAndAssertHelloAppImageCreated(); - } else { - cmd.execute(1); - } - } -} diff --git a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java index 959f6d710b6..e27f6c8729b 100644 --- a/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningAppImageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -62,17 +62,15 @@ public class SigningAppImageTest { @Test // ({"sign or not", "signing-key or sign-identity", "certificate index"}) // Sign, signing-key and ASCII certificate - @Parameter({"true", "true", SigningBase.ASCII_INDEX}) + @Parameter({"true", "true", "ASCII_INDEX"}) // Sign, signing-key and UNICODE certificate - @Parameter({"true", "true", SigningBase.UNICODE_INDEX}) + @Parameter({"true", "true", "UNICODE_INDEX"}) // Sign, signing-indentity and UNICODE certificate - @Parameter({"true", "false", SigningBase.UNICODE_INDEX}) + @Parameter({"true", "false", "UNICODE_INDEX"}) // Unsigned - @Parameter({"false", "true", "-1"}) - public void test(String... testArgs) throws Exception { - boolean doSign = Boolean.parseBoolean(testArgs[0]); - boolean signingKey = Boolean.parseBoolean(testArgs[1]); - int certIndex = Integer.parseInt(testArgs[2]); + @Parameter({"false", "true", "INVALID_INDEX"}) + public void test(boolean doSign, boolean signingKey, SigningBase.CertIndex certEnum) throws Exception { + final var certIndex = certEnum.value(); SigningCheck.checkCertificates(certIndex); diff --git a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java index 46a3dad2af8..c56f01e192e 100644 --- a/test/jdk/tools/jpackage/macosx/SigningPackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningPackageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -110,27 +110,24 @@ public class SigningPackageTest { return SigningBase.getDevNameIndex(devName); } else { // Signing-indentity - return Integer.valueOf(SigningBase.UNICODE_INDEX); + return SigningBase.CertIndex.UNICODE_INDEX.value(); } } @Test // ("signing-key or sign-identity", "sign app-image", "sign pkg", "certificate index"}) // Signing-key and ASCII certificate - @Parameter({"true", "true", "true", SigningBase.ASCII_INDEX}) + @Parameter({"true", "true", "true", "ASCII_INDEX"}) // Signing-key and UNICODE certificate - @Parameter({"true", "true", "true", SigningBase.UNICODE_INDEX}) + @Parameter({"true", "true", "true", "UNICODE_INDEX"}) // Signing-indentity and UNICODE certificate - @Parameter({"false", "true", "true", SigningBase.UNICODE_INDEX}) + @Parameter({"false", "true", "true", "UNICODE_INDEX"}) // Signing-indentity, but sign app-image only and UNICODE certificate - @Parameter({"false", "true", "false", SigningBase.UNICODE_INDEX}) + @Parameter({"false", "true", "false", "UNICODE_INDEX"}) // Signing-indentity, but sign pkg only and UNICODE certificate - @Parameter({"false", "false", "true", SigningBase.UNICODE_INDEX}) - public static void test(String... testArgs) throws Exception { - boolean signingKey = Boolean.parseBoolean(testArgs[0]); - boolean signAppImage = Boolean.parseBoolean(testArgs[1]); - boolean signPKG = Boolean.parseBoolean(testArgs[2]); - int certIndex = Integer.parseInt(testArgs[3]); + @Parameter({"false", "false", "true", "UNICODE_INDEX"}) + public static void test(boolean signingKey, boolean signAppImage, boolean signPKG, SigningBase.CertIndex certEnum) throws Exception { + final var certIndex = certEnum.value(); SigningCheck.checkCertificates(certIndex); diff --git a/test/jdk/tools/jpackage/macosx/base/SigningBase.java b/test/jdk/tools/jpackage/macosx/base/SigningBase.java index 254aa306b52..021a19bb82f 100644 --- a/test/jdk/tools/jpackage/macosx/base/SigningBase.java +++ b/test/jdk/tools/jpackage/macosx/base/SigningBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -32,9 +32,23 @@ import jdk.jpackage.test.Executor.Result; public class SigningBase { + enum CertIndex { + ASCII_INDEX(0), + UNICODE_INDEX(1), + INVALID_INDEX(-1); + + CertIndex(int value) { + this.value = value; + } + + int value() { + return value; + } + + private final int value; + } + public static int DEFAULT_INDEX = 0; - public static final String ASCII_INDEX = "0"; - public static final String UNICODE_INDEX = "0"; private static String [] DEV_NAMES = { "jpackage.openjdk.java.net", "jpackage.openjdk.java.net (ö)", diff --git a/test/jdk/tools/jpackage/share/AppImagePackageTest.java b/test/jdk/tools/jpackage/share/AppImagePackageTest.java index fc545cc1f55..34a418c6f9e 100644 --- a/test/jdk/tools/jpackage/share/AppImagePackageTest.java +++ b/test/jdk/tools/jpackage/share/AppImagePackageTest.java @@ -25,10 +25,13 @@ import java.nio.file.Path; import java.nio.file.Files; import java.io.IOException; import java.util.List; +import jdk.jpackage.internal.util.XmlUtils; import jdk.jpackage.test.AppImageFile; import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.TKit; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.RunnablePackageTest.Action; import jdk.jpackage.test.Annotations.Test; @@ -106,17 +109,16 @@ public class AppImagePackageTest { public static void testBadAppImage() throws IOException { Path appImageDir = TKit.createTempDirectory("appimage"); Files.createFile(appImageDir.resolve("foo")); - configureAppImageWithoutJPackageXMLFile(appImageDir).addInitializer( - cmd -> { - cmd.removeArgumentWithValue("--name"); - }).run(Action.CREATE); + configureBadAppImage(appImageDir).addInitializer(cmd -> { + cmd.removeArgumentWithValue("--name"); + }).run(Action.CREATE); } @Test public static void testBadAppImage2() throws IOException { Path appImageDir = TKit.createTempDirectory("appimage"); Files.createFile(appImageDir.resolve("foo")); - configureAppImageWithoutJPackageXMLFile(appImageDir).run(Action.CREATE); + configureBadAppImage(appImageDir).run(Action.CREATE); } @Test @@ -126,29 +128,45 @@ public class AppImagePackageTest { JPackageCommand appImageCmd = JPackageCommand.helloAppImage(). setFakeRuntime().setArgumentValue("--dest", appImageDir); - configureAppImageWithoutJPackageXMLFile(appImageCmd.outputBundle()). - addRunOnceInitializer(() -> { - appImageCmd.execute(); - Files.delete(AppImageFile.getPathInAppImage(appImageCmd. - outputBundle())); - }).run(Action.CREATE); + configureBadAppImage(appImageCmd.outputBundle()).addRunOnceInitializer(() -> { + appImageCmd.execute(); + Files.delete(AppImageFile.getPathInAppImage(appImageCmd.outputBundle())); + }).run(Action.CREATE); } - private static PackageTest configureAppImageWithoutJPackageXMLFile( - Path appImageDir) { - return new PackageTest() - .addInitializer(cmd -> { - cmd.saveConsoleOutput(true); - cmd.addArguments("--app-image", appImageDir); - cmd.removeArgumentWithValue("--input"); - cmd.ignoreDefaultVerbose(true); // no "--verbose" option - }) - .addBundleVerifier((cmd, result) -> { - TKit.assertTextStream( - "Error: Missing .jpackage.xml file in app-image dir").apply( - result.getOutput().stream()); - }) - .setExpectedExitCode(1); + @Test + public static void testBadAppImageFile() throws IOException { + final var appImageRoot = TKit.createTempDirectory("appimage"); + + final var appImageCmd = JPackageCommand.helloAppImage(). + setFakeRuntime().setArgumentValue("--dest", appImageRoot); + + final var appImageDir = appImageCmd.outputBundle(); + + final var expectedError = JPackageStringBundle.MAIN.cannedFormattedString( + "error.invalid-app-image", appImageDir, AppImageFile.getPathInAppImage(appImageDir)); + + configureBadAppImage(appImageDir, expectedError).addRunOnceInitializer(() -> { + appImageCmd.execute(); + XmlUtils.createXml(AppImageFile.getPathInAppImage(appImageDir), xml -> { + xml.writeStartElement("jpackage-state"); + xml.writeEndElement(); + }); + }).run(Action.CREATE); + } + + private static PackageTest configureBadAppImage(Path appImageDir) { + return configureBadAppImage(appImageDir, + JPackageStringBundle.MAIN.cannedFormattedString("error.foreign-app-image", appImageDir)); + } + + private static PackageTest configureBadAppImage(Path appImageDir, CannedFormattedString expectedError) { + return new PackageTest().addInitializer(cmd -> { + cmd.addArguments("--app-image", appImageDir); + cmd.removeArgumentWithValue("--input"); + cmd.ignoreDefaultVerbose(true); // no "--verbose" option + cmd.validateOutput(expectedError); + }).setExpectedExitCode(1); } private static Path iconPath(String name) { diff --git a/test/jdk/tools/jpackage/share/AppVersionTest.java b/test/jdk/tools/jpackage/share/AppVersionTest.java index e81b1eae9cf..c9e66cbd7a1 100644 --- a/test/jdk/tools/jpackage/share/AppVersionTest.java +++ b/test/jdk/tools/jpackage/share/AppVersionTest.java @@ -31,7 +31,6 @@ import jdk.jpackage.test.AppImageFile; import jdk.jpackage.test.Annotations.Parameters; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.TKit; /* @@ -70,19 +69,6 @@ public final class AppVersionTest { "--app-version", "7.5.81"}} })); - // These are invalid version strings. - // Don't need to test all invalid input as this is handled in - // PlatformVersionTest unit test - if (TKit.isWindows()) { - data.addAll(List.of(new Object[][]{ - {null, "Hello", new String[]{"--app-version", "256"}} - })); - } else if (TKit.isOSX()) { - data.addAll(List.of(new Object[][]{ - {null, "Hello", new String[]{"--app-version", "0.2"}} - })); - } - return data; } @@ -95,17 +81,6 @@ public final class AppVersionTest { @Test public void test() throws XPathExpressionException, IOException { - if (expectedVersion == null) { - new PackageTest() - .setExpectedExitCode(1) - .configureHelloApp(javaAppDesc) - .addInitializer(cmd -> { - cmd.addArguments(jpackageArgs); - }) - .run(); - return; - } - JPackageCommand cmd = JPackageCommand.helloAppImage(javaAppDesc); if (jpackageArgs != null) { cmd.addArguments(jpackageArgs); diff --git a/test/jdk/tools/jpackage/share/BasicTest.java b/test/jdk/tools/jpackage/share/BasicTest.java index 94e0a335d18..fe988533a28 100644 --- a/test/jdk/tools/jpackage/share/BasicTest.java +++ b/test/jdk/tools/jpackage/share/BasicTest.java @@ -36,6 +36,7 @@ import java.util.regex.Pattern; import java.util.stream.Stream; import jdk.jpackage.test.TKit; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.HelloApp; @@ -381,7 +382,10 @@ public final class BasicTest { ); if (TestTempType.TEMPDIR_NOT_EMPTY.equals(type)) { - pkgTest.setExpectedExitCode(1).addBundleVerifier(cmd -> { + pkgTest.setExpectedExitCode(1).addInitializer(cmd -> { + cmd.validateOutput(JPackageStringBundle.MAIN.cannedFormattedString( + "ERR_BuildRootInvalid", cmd.getArgumentValue("--temp"))); + }).addBundleVerifier(cmd -> { // Check jpackage didn't use the supplied directory. Path tempDir = Path.of(cmd.getArgumentValue("--temp")); TKit.assertDirectoryContent(tempDir).match(Path.of("foo.txt")); diff --git a/test/jdk/tools/jpackage/share/ErrorTest.java b/test/jdk/tools/jpackage/share/ErrorTest.java index f28fe0a4152..5260dceae32 100644 --- a/test/jdk/tools/jpackage/share/ErrorTest.java +++ b/test/jdk/tools/jpackage/share/ErrorTest.java @@ -22,19 +22,32 @@ */ +import static java.util.stream.Collectors.toMap; +import static jdk.internal.util.OperatingSystem.LINUX; +import static jdk.internal.util.OperatingSystem.MACOS; +import static jdk.internal.util.OperatingSystem.WINDOWS; +import static jdk.jpackage.test.CannedFormattedString.cannedAbsolutePath; + +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Pattern; import java.util.stream.Stream; +import jdk.jpackage.internal.util.TokenReplace; +import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.ParameterSupplier; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JPackageStringBundle; -import jdk.jpackage.test.PackageTest; -import jdk.jpackage.test.RunnablePackageTest; +import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; -import static jdk.internal.util.OperatingSystem.WINDOWS; /* * @test @@ -42,7 +55,7 @@ import static jdk.internal.util.OperatingSystem.WINDOWS; * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @compile -Xlint:all -Werror ErrorTest.java - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ErrorTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useExecutableByDefault */ @@ -53,101 +66,566 @@ import static jdk.internal.util.OperatingSystem.WINDOWS; * @library /test/jdk/tools/jpackage/helpers * @build jdk.jpackage.test.* * @compile -Xlint:all -Werror ErrorTest.java - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main + * @run main/othervm/timeout=720 -Xmx512m jdk.jpackage.test.Main * --jpt-run=ErrorTest * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useToolProviderByDefault */ public final class ErrorTest { - public static Collection input() { - return List.of(new Object[][]{ + enum Token { + JAVA_HOME(cmd -> { + return System.getProperty("java.home"); + }), + APP_IMAGE(cmd -> { + final var appImageRoot = TKit.createTempDirectory("appimage"); + + final var appImageCmd = JPackageCommand.helloAppImage() + .setFakeRuntime().setArgumentValue("--dest", appImageRoot); + + appImageCmd.execute(); + + return appImageCmd.outputBundle().toString(); + }), + ADD_LAUNCHER_PROPERTY_FILE; + + private Token() { + this.valueSupplier = Optional.empty(); + } + + private Token(Function valueSupplier) { + this.valueSupplier = Optional.of(valueSupplier); + } + + String token() { + return makeToken(name()); + } + + TokenReplace asTokenReplace() { + return tokenReplace; + } + + Optional expand(JPackageCommand cmd) { + return valueSupplier.map(func -> func.apply(cmd)); + } + + private static String makeToken(String v) { + Objects.requireNonNull(v); + return String.format("@@%s@@", v); + } + + private final Optional> valueSupplier; + private final TokenReplace tokenReplace = new TokenReplace(token()); + } + + public record TestSpec(Optional type, Optional appDesc, List addArgs, + List removeArgs, List expectedErrors) { + + static final class Builder { + + Builder type(PackageType v) { + type = v; + return this; + } + + Builder notype() { + return type(null); + } + + Builder nativeType() { + return type(NATIVE_TYPE); + } + + Builder appDesc(String v) { + appDesc = v; + return this; + } + + Builder noAppDesc() { + return appDesc(null); + } + + Builder setAddArgs(List v) { + addArgs.clear(); + addArgs.addAll(v); + return this; + } + + Builder setAddArgs(String... v) { + return setAddArgs(List.of(v)); + } + + Builder addArgs(List v) { + addArgs.addAll(v); + return this; + } + + Builder addArgs(String... v) { + return addArgs(List.of(v)); + } + + Builder setRemoveArgs(List v) { + removeArgs.clear(); + removeArgs.addAll(v); + return this; + } + + Builder setRemoveArgs(String... v) { + return setRemoveArgs(List.of(v)); + } + + Builder removeArgs(List v) { + removeArgs.addAll(v); + return this; + } + + Builder removeArgs(String... v) { + return removeArgs(List.of(v)); + } + + Builder setErrors(List v) { + expectedErrors = v; + return this; + } + + Builder setErrors(CannedFormattedString... v) { + return setErrors(List.of(v)); + } + + Builder errors(List v) { + expectedErrors.addAll(v); + return this; + } + + Builder errors(CannedFormattedString... v) { + return errors(List.of(v)); + } + + Builder error(String key, Object ... args) { + return errors(JPackageStringBundle.MAIN.cannedFormattedString(key, args)); + } + + Builder invalidTypeArg(String arg, String... otherArgs) { + return addArgs(arg).addArgs(otherArgs).error("ERR_InvalidTypeOption", arg, type.getType()); + } + + Builder unsupportedPlatformOption(String arg, String ... otherArgs) { + return addArgs(arg).addArgs(otherArgs).error("ERR_UnsupportedOption", arg); + } + + TestSpec create() { + return new TestSpec(Optional.ofNullable(type), Optional.ofNullable(appDesc), + List.copyOf(addArgs), List.copyOf(removeArgs), List.copyOf(expectedErrors)); + } + + private PackageType type = PackageType.IMAGE; + private String appDesc = DEFAULT_APP_DESC; + private List addArgs = new ArrayList<>(); + private List removeArgs = new ArrayList<>(); + private List expectedErrors = new ArrayList<>(); + } + + public TestSpec { + Objects.requireNonNull(type); + Objects.requireNonNull(appDesc); + Objects.requireNonNull(addArgs); + addArgs.forEach(Objects::requireNonNull); + Objects.requireNonNull(removeArgs); + removeArgs.forEach(Objects::requireNonNull); + if (expectedErrors.isEmpty()) { + throw new IllegalArgumentException("The list of expected errors must be non-empty"); + } + } + + void test() { + test(Map.of()); + } + + void test(Map> tokenValueSuppliers) { + final var cmd = appDesc.map(JPackageCommand::helloAppImage).orElseGet(JPackageCommand::new); + type.ifPresent(cmd::setPackageType); + + removeArgs.forEach(cmd::removeArgumentWithValue); + cmd.addArguments(addArgs); + + final var tokenValueSupplier = TokenReplace.createCachingTokenValueSupplier(Stream.of(Token.values()).collect(toMap(Token::token, token -> { + return () -> { + return token.expand(cmd).orElseGet(() -> { + final var tvs = Objects.requireNonNull(tokenValueSuppliers.get(token), () -> { + return String.format("No token value supplier for token [%s]", token); + }); + return tvs.apply(cmd); + }); + }; + }))); + + for (final var token : Token.values()) { + final var newArgs = cmd.getAllArguments().stream().map(arg -> { + return token.asTokenReplace().applyTo(arg, tokenValueSupplier); + }).toList(); + cmd.clearArguments().addArguments(newArgs); + } + + defaultInit(cmd, expectedErrors); + cmd.execute(1); + } + + @Override + public final String toString() { + final var sb = new StringBuilder(); + type.ifPresent(v -> { + sb.append(v).append("; "); + }); + appDesc.ifPresent(v -> { + sb.append("app-desc=").append(v).append("; "); + }); + if (!addArgs.isEmpty()) { + sb.append("args-add=").append(addArgs).append("; "); + } + if (!removeArgs.isEmpty()) { + sb.append("args-del=").append(removeArgs).append("; "); + } + sb.append("errors=").append(expectedErrors); + return sb.toString(); + } + + private static final String DEFAULT_APP_DESC = "Hello"; + } + + private static TestSpec.Builder testSpec() { + return new TestSpec.Builder(); + } + + public static Collection basic() { + final List testCases = new ArrayList<>(); + + testCases.addAll(Stream.of( // non-existent arg - {"Hello", - new String[]{"--no-such-argument"}, - null, - JPackageStringBundle.MAIN.cannedFormattedString("ERR_InvalidOption", "--no-such-argument")}, + testSpec().addArgs("--no-such-argument") + .error("ERR_InvalidOption", "--no-such-argument"), // no main jar - {"Hello", - null, - new String[]{"--main-jar"}, - JPackageStringBundle.MAIN.cannedFormattedString("ERR_NoEntryPoint")}, + testSpec().removeArgs("--main-jar").error("ERR_NoEntryPoint"), // no main-class - {"Hello", - null, - new String[]{"--main-class"}, - JPackageStringBundle.MAIN.cannedFormattedString("error.no-main-class-with-main-jar", "hello.jar"), - JPackageStringBundle.MAIN.cannedFormattedString("error.no-main-class-with-main-jar.advice", "hello.jar")}, + testSpec().removeArgs("--main-class") + .error("error.no-main-class-with-main-jar", "hello.jar") + .error("error.no-main-class-with-main-jar.advice", "hello.jar"), // non-existent main jar - {"Hello", - new String[]{"--main-jar", "non-existent.jar"}, - null, - JPackageStringBundle.MAIN.cannedFormattedString("error.main-jar-does-not-exist", "non-existent.jar")}, + testSpec().addArgs("--main-jar", "non-existent.jar") + .error("error.main-jar-does-not-exist", "non-existent.jar"), // non-existent runtime - {"Hello", - new String[]{"--runtime-image", "non-existent.runtime"}, - null, - JPackageStringBundle.MAIN.cannedFormattedString("message.runtime-image-dir-does-not-exist", "runtime-image", "non-existent.runtime")}, + testSpec().addArgs("--runtime-image", "non-existent.runtime") + .error("message.runtime-image-dir-does-not-exist", "runtime-image", "non-existent.runtime"), + // non-existent app image + testSpec().noAppDesc().nativeType().addArgs("--name", "foo", "--app-image", "non-existent.appimage") + .error("ERR_AppImageNotExist", "non-existent.appimage"), // non-existent resource-dir - {"Hello", - new String[]{"--resource-dir", "non-existent.dir"}, - null, - JPackageStringBundle.MAIN.cannedFormattedString("message.resource-dir-does-not-exist", "resource-dir", "non-existent.dir")}, + testSpec().addArgs("--resource-dir", "non-existent.dir") + .error("message.resource-dir-does-not-exist", "resource-dir", "non-existent.dir"), + // non-existent icon + testSpec().addArgs("--icon", "non-existent.icon") + .error("ERR_IconFileNotExit", cannedAbsolutePath("non-existent.icon")), + // non-existent license file + testSpec().nativeType().addArgs("--license-file", "non-existent.license") + .error("ERR_LicenseFileNotExit"), // invalid type - {"Hello", - new String[]{"--type", "invalid-type"}, - null, - JPackageStringBundle.MAIN.cannedFormattedString("ERR_InvalidInstallerType", "invalid-type")}, - // no --input - {"Hello", - null, - new String[]{"--input"}, - JPackageStringBundle.MAIN.cannedFormattedString("ERR_MissingArgument", "--input")}, + testSpec().addArgs("--type", "invalid-type") + .error("ERR_InvalidInstallerType", "invalid-type"), + // no --input for non-mudular app + testSpec().removeArgs("--input").error("error.no-input-parameter"), // no --module-path - {"com.other/com.other.Hello", - null, - new String[]{"--module-path"}, - JPackageStringBundle.MAIN.cannedFormattedString("ERR_MissingArgument", "--runtime-image or --module-path")}, - }); + testSpec().appDesc("com.other/com.other.Hello").removeArgs("--module-path") + .error("ERR_MissingArgument", "--runtime-image or --module-path"), + // no main class in module path + testSpec().noAppDesc().addArgs("--module", "java.base", "--runtime-image", Token.JAVA_HOME.token()) + .error("ERR_NoMainClass"), + // no module in module path + testSpec().noAppDesc().addArgs("--module", "com.foo.bar", "--runtime-image", Token.JAVA_HOME.token()) + .error("error.no-module-in-path", "com.foo.bar"), + // --main-jar and --module-name + testSpec().noAppDesc().addArgs("--main-jar", "foo.jar", "--module", "foo.bar") + .error("ERR_BothMainJarAndModule"), + // non-existing argument file + testSpec().noAppDesc().notype().addArgs("@foo") + .error("ERR_CannotParseOptions", "foo"), + // invalid jlink option + testSpec().addArgs("--jlink-options", "--foo") + .error("error.jlink.failed", "Error: unknown option: --foo") + ).map(TestSpec.Builder::create).toList()); + + // forbidden jlink options + testCases.addAll(Stream.of("--output", "--add-modules", "--module-path").map(opt -> { + return testSpec().addArgs("--jlink-options", opt).error("error.blocked.option", opt); + }).map(TestSpec.Builder::create).toList()); + + // --runtime-image and --app-image are mutually-exclusive + testCases.addAll(createRuntimeMutuallyExclusive("--app-image", "app-image")); + // --runtime-image and --app-modules are mutually-exclusive + testCases.addAll(createRuntimeMutuallyExclusive("--add-modules", "foo.bar", "--module", "foo.bar")); + // --runtime-image and --jlink-options are mutually-exclusive + testCases.addAll(createRuntimeMutuallyExclusive("--jlink-options", "--bind-services", "--module", "foo.bar")); + + return toTestArgs(testCases.stream()); + } + + record ArgumentGroup(String arg, String... otherArgs) { + ArgumentGroup { + Objects.requireNonNull(arg); + List.of(otherArgs).forEach(Objects::requireNonNull); + } + + String[] asArray() { + return Stream.concat(Stream.of(arg), Stream.of(otherArgs)).toArray(String[]::new); + } + } + + private static List createRuntimeMutuallyExclusive(String arg, String... otherArgs) { + return createMutuallyExclusive( + new ArgumentGroup("--runtime-image", Token.JAVA_HOME.token()), + new ArgumentGroup(arg, otherArgs) + ).map(TestSpec.Builder::noAppDesc).map(TestSpec.Builder::nativeType).map(TestSpec.Builder::create).toList(); + } + + private static Stream createMutuallyExclusive(ArgumentGroup firstGroup, ArgumentGroup secondGroup) { + final Supplier createBuilder = () -> { + return testSpec().error("ERR_MutuallyExclusiveOptions", firstGroup.arg(), secondGroup.arg()); + }; + return Stream.of( + createBuilder.get().addArgs(firstGroup.asArray()).addArgs(secondGroup.asArray()), + createBuilder.get().addArgs(secondGroup.asArray()).addArgs(firstGroup.asArray())); + } + + public static Collection invalidAppVersion() { + return fromTestSpecBuilders(Stream.of( + // Invalid app version. Just cover all different error messages. + // Extensive testing of invalid version strings is done in DottedVersionTest unit test. + testSpec().addArgs("--app-version", "").error("error.version-string-empty"), + testSpec().addArgs("--app-version", "1.").error("error.version-string-zero-length-component", "1."), + testSpec().addArgs("--app-version", "1.b.3").error("error.version-string-invalid-component", "1.b.3", "b.3") + )); } @Test - @ParameterSupplier("input") - public static void test(String javaAppDesc, String[] jpackageArgs, - String[] removeArgs, CannedFormattedString... expectedErrors) { - // Init default jpackage test command line. - var cmd = JPackageCommand.helloAppImage(javaAppDesc); - - defaultInit(cmd, expectedErrors); - - // Add arguments if requested. - Optional.ofNullable(jpackageArgs).ifPresent(cmd::addArguments); - - // Remove arguments if requested. - Optional.ofNullable(removeArgs).map(List::of).ifPresent( - args -> args.forEach(cmd::removeArgumentWithValue)); - - cmd.execute(1); + @ParameterSupplier("basic") + @ParameterSupplier(value="testWindows", ifOS = WINDOWS) + @ParameterSupplier(value="testMac", ifOS = MACOS) + @ParameterSupplier(value="winOption", ifNotOS = WINDOWS) + @ParameterSupplier(value="linuxOption", ifNotOS = LINUX) + @ParameterSupplier(value="macOption", ifNotOS = MACOS) + @ParameterSupplier(value="invalidAppVersion", ifOS = {WINDOWS,MACOS}) + public static void test(TestSpec spec) { + spec.test(); } - @Test(ifOS = WINDOWS) - public static void testWinService() { - - CannedFormattedString[] expectedErrors = new CannedFormattedString[] { - JPackageStringBundle.MAIN.cannedFormattedString("error.missing-service-installer"), - JPackageStringBundle.MAIN.cannedFormattedString("error.missing-service-installer.advice") - }; - - new PackageTest().configureHelloApp() - .addInitializer(cmd -> { - defaultInit(cmd, expectedErrors); - cmd.addArgument("--launcher-as-service"); - }) - .setExpectedExitCode(1) - .run(RunnablePackageTest.Action.CREATE); + @Test + @Parameter({"--input", "foo"}) + @Parameter({"--module-path", "dir"}) + @Parameter({"--add-modules", "java.base"}) + @Parameter({"--main-class", "Hello"}) + @Parameter({"--arguments", "foo"}) + @Parameter({"--java-options", "-Dfoo.bar=10"}) + @Parameter({"--add-launcher", "foo=foo.properties"}) + @Parameter({"--app-content", "dir"}) + @Parameter(value="--win-console", ifOS = WINDOWS) + public static void testRuntimeInstallerInvalidOptions(String... args) { + testSpec().noAppDesc().nativeType().addArgs("--runtime-image", Token.JAVA_HOME.token()).addArgs(args) + .error("ERR_NoInstallerEntryPoint", args[0]).create().test(); } - private static void defaultInit(JPackageCommand cmd, CannedFormattedString... expectedErrors) { + @Test + @ParameterSupplier + public static void testAdditionLaunchers(TestSpec spec) { + final Path propsFile = TKit.createTempFile("add-launcher.properties"); + TKit.createPropertiesFile(propsFile, Map.of()); + spec.test(Map.of(Token.ADD_LAUNCHER_PROPERTY_FILE, cmd -> propsFile)); + } + + public static Collection testAdditionLaunchers() { + return fromTestSpecBuilders(Stream.of( + testSpec().addArgs("--add-launcher", Token.ADD_LAUNCHER_PROPERTY_FILE.token()) + .error("ERR_NoAddLauncherName"), + testSpec().removeArgs("--name").addArgs("--name", "foo", "--add-launcher", "foo=" + Token.ADD_LAUNCHER_PROPERTY_FILE.token()) + .error("ERR_NoUniqueName") + )); + } + + @Test + @ParameterSupplier("invalidNames") + public static void testInvalidAppName(String name) { + testSpec().removeArgs("--name").addArgs("--name", name) + .error("ERR_InvalidAppName", adjustTextStreamVerifierArg(name)).create().test(); + } + + @Test + @ParameterSupplier("invalidNames") + public static void testInvalidAddLauncherName(String name) { + testAdditionLaunchers(testSpec() + .addArgs("--add-launcher", name + "=" + Token.ADD_LAUNCHER_PROPERTY_FILE.token()) + .error("ERR_InvalidSLName", adjustTextStreamVerifierArg(name)) + .create()); + } + + public static Collection invalidNames() { + final List data = new ArrayList<>(); + data.addAll(List.of("", "foo/bar", "foo\tbar", "foo\rbar", "foo\nbar")); + if (TKit.isWindows()) { + data.add("foo\\bar"); + } + return toTestArgs(data.stream()); + } + + public static Collection testWindows() { + final List testCases = new ArrayList<>(); + + testCases.addAll(PackageType.WINDOWS.stream().map(type -> { + return Stream.of( + testSpec().type(type).addArgs("--launcher-as-service") + .error("error.missing-service-installer") + .error("error.missing-service-installer.advice"), + // The below version strings are invalid for msi and exe packaging. + // They are valid for app image packaging. + testSpec().type(type).addArgs("--app-version", "1234") + .error("error.msi-product-version-components", "1234") + .error("error.version-string-wrong-format.advice"), + testSpec().type(type).addArgs("--app-version", "1.2.3.4.5") + .error("error.msi-product-version-components", "1.2.3.4.5") + .error("error.version-string-wrong-format.advice"), + testSpec().type(type).addArgs("--app-version", "256.1") + .error("error.msi-product-version-major-out-of-range", "256.1") + .error("error.version-string-wrong-format.advice"), + testSpec().type(type).addArgs("--app-version", "1.256") + .error("error.msi-product-version-minor-out-of-range", "1.256") + .error("error.version-string-wrong-format.advice"), + testSpec().type(type).addArgs("--app-version", "1.2.65536") + .error("error.msi-product-version-build-out-of-range", "1.2.65536") + .error("error.version-string-wrong-format.advice") + ); + }).flatMap(x -> x).map(TestSpec.Builder::create).toList()); + + return toTestArgs(testCases.stream()); + } + + public static Collection testMac() { + final List testCases = new ArrayList<>(); + + testCases.addAll(Stream.of( + testSpec().addArgs("--app-version", "0.2") + .error("message.version-string-first-number-not-zero") + .error("error.invalid-cfbundle-version.advice"), + testSpec().addArgs("--app-version", "1.2.3.4") + .error("message.version-string-too-many-components") + .error("error.invalid-cfbundle-version.advice"), + testSpec().invalidTypeArg("--mac-installer-sign-identity", "foo"), + testSpec().type(PackageType.MAC_DMG).invalidTypeArg("--mac-installer-sign-identity", "foo"), + testSpec().invalidTypeArg("--mac-dmg-content", "foo"), + testSpec().type(PackageType.MAC_PKG).invalidTypeArg("--mac-dmg-content", "foo"), + testSpec().noAppDesc().addArgs("--app-image", Token.APP_IMAGE.token()) + .error("error.app-image.mac-sign.required"), + testSpec().type(PackageType.MAC_PKG).addArgs("--mac-package-identifier", "#1") + .error("message.invalid-identifier", "#1"), + // Bundle for mac app store should not have runtime commands + testSpec().nativeType().addArgs("--mac-app-store", "--jlink-options", "--bind-services") + .error("ERR_MissingJLinkOptMacAppStore", "--strip-native-commands"), + testSpec().nativeType().addArgs("--mac-app-store", "--runtime-image", Token.JAVA_HOME.token()) + .error("ERR_MacAppStoreRuntimeBinExists", JPackageCommand.cannedArgument(cmd -> { + return Path.of(cmd.getArgumentValue("--runtime-image")).toAbsolutePath(); + }, Token.JAVA_HOME.token())) + ).map(TestSpec.Builder::create).toList()); + + // Test a few app-image options that should not be used when signing external app image + testCases.addAll(Stream.of( + new ArgumentGroup("--app-version", "2.0"), + new ArgumentGroup("--name", "foo"), + new ArgumentGroup("--mac-app-store") + ).map(argGroup -> { + return testSpec().noAppDesc().addArgs(argGroup.asArray()).addArgs("--app-image", Token.APP_IMAGE.token()) + .error("ERR_InvalidOptionWithAppImageSigning", argGroup.arg()); + }).mapMulti((builder, acc) -> { + // It should bail out with the same error message regardless of `--mac-sign` option. + acc.accept(builder.create()); + acc.accept(builder.addArgs("--mac-sign").create()); + }).toList()); + + testCases.addAll(createMutuallyExclusive( + new ArgumentGroup("--mac-signing-key-user-name", "foo"), + new ArgumentGroup("--mac-app-image-sign-identity", "bar") + ).map(TestSpec.Builder::create).toList()); + + testCases.addAll(createMutuallyExclusive( + new ArgumentGroup("--mac-signing-key-user-name", "foo"), + new ArgumentGroup("--mac-installer-sign-identity", "bar") + ).map(TestSpec.Builder::nativeType).map(TestSpec.Builder::create).toList()); + + return toTestArgs(testCases.stream()); + } + + private record UnsupportedPlatformOption(String name, Optional value) { + UnsupportedPlatformOption { + Objects.requireNonNull(name); + Objects.requireNonNull(value); + } + + UnsupportedPlatformOption(String name) { + this(name, Optional.empty()); + } + + UnsupportedPlatformOption(String name, String value) { + this(name, Optional.of(value)); + } + + TestSpec toTestSpec() { + return value.map(v -> testSpec().unsupportedPlatformOption(name, v)).orElseGet( + () -> testSpec().unsupportedPlatformOption(name)).create(); + } + + static Collection createTestArgs(UnsupportedPlatformOption... options) { + return toTestArgs(Stream.of(options).map(UnsupportedPlatformOption::toTestSpec)); + } + } + + public static Collection winOption() { + return UnsupportedPlatformOption.createTestArgs( + new UnsupportedPlatformOption("--win-console"), + new UnsupportedPlatformOption("--win-dir-chooser"), + new UnsupportedPlatformOption("--win-help-url", "url"), + new UnsupportedPlatformOption("--win-menu"), + new UnsupportedPlatformOption("--win-menu-group", "name"), + new UnsupportedPlatformOption("--win-per-user-install"), + new UnsupportedPlatformOption("--win-shortcut"), + new UnsupportedPlatformOption("--win-shortcut-prompt"), + new UnsupportedPlatformOption("--win-update-url", "url"), + new UnsupportedPlatformOption("--win-upgrade-uuid", "uuid") + ); + } + + public static Collection linuxOption() { + return UnsupportedPlatformOption.createTestArgs( + new UnsupportedPlatformOption("--linux-package-name", "name"), + new UnsupportedPlatformOption("--linux-deb-maintainer", "email-address"), + new UnsupportedPlatformOption("--linux-menu-group", "menu-group-name"), + new UnsupportedPlatformOption("--linux-package-deps", "deps"), + new UnsupportedPlatformOption("--linux-rpm-license-type", "type"), + new UnsupportedPlatformOption("--linux-app-release", "release"), + new UnsupportedPlatformOption("--linux-app-category", "category-value"), + new UnsupportedPlatformOption("--linux-shortcut") + ); + } + + public static Collection macOption() { + return UnsupportedPlatformOption.createTestArgs( + new UnsupportedPlatformOption("--mac-package-identifier", "identifier"), + new UnsupportedPlatformOption("--mac-package-name", "name"), + new UnsupportedPlatformOption("--mac-package-signing-prefix", "prefix"), + new UnsupportedPlatformOption("--mac-sign"), + new UnsupportedPlatformOption("--mac-signing-keychain", "keychain-name"), + new UnsupportedPlatformOption("--mac-signing-key-user-name", "name"), + new UnsupportedPlatformOption("--mac-app-store"), + new UnsupportedPlatformOption("--mac-entitlements", "path"), + new UnsupportedPlatformOption("--mac-app-category", "category"), + new UnsupportedPlatformOption("--mac-dmg-content", "additional-content") + ); + } + + private static void defaultInit(JPackageCommand cmd, List expectedErrors) { // Disable default logic adding `--verbose` option // to jpackage command line. @@ -158,11 +636,36 @@ public final class ErrorTest { // with jpackage arguments in this test. cmd.ignoreDefaultRuntime(true); - // Configure jpackage output verifier to look up the list of provided - // errors in the order they are specified. - cmd.validateOutput(Stream.of(expectedErrors) - .map(CannedFormattedString::getValue) - .map(TKit::assertTextStream) - .reduce(TKit.TextStreamVerifier::andThen).get()); + cmd.validateOutput(expectedErrors.toArray(CannedFormattedString[]::new)); } + + private static PackageType defaultNativeType() { + if (TKit.isLinux()) { + return PackageType.LINUX.stream().filter(PackageType::isSupported).findFirst().orElseThrow(); + } else if (TKit.isOSX()) { + return PackageType.MAC_DMG; + } else if (TKit.isWindows()) { + return PackageType.WIN_MSI; + } else { + throw new UnsupportedOperationException(); + } + } + + private static Collection toTestArgs(Stream stream) { + return stream.map(v -> { + return new Object[] {v}; + }).toList(); + } + + private static Collection fromTestSpecBuilders(Stream stream) { + return toTestArgs(stream.map(TestSpec.Builder::create)); + } + + private static String adjustTextStreamVerifierArg(String str) { + return LINE_SEP_REGEXP.split(str)[0]; + } + + private static final Pattern LINE_SEP_REGEXP = Pattern.compile("\\R"); + + private static final PackageType NATIVE_TYPE = defaultNativeType(); } diff --git a/test/jdk/tools/jpackage/share/FileAssociationsTest.java b/test/jdk/tools/jpackage/share/FileAssociationsTest.java index 08578ab8027..2257c5dbc65 100644 --- a/test/jdk/tools/jpackage/share/FileAssociationsTest.java +++ b/test/jdk/tools/jpackage/share/FileAssociationsTest.java @@ -21,12 +21,14 @@ * questions. */ +import static jdk.jpackage.test.JPackageStringBundle.MAIN; + import java.nio.file.Path; import java.util.Map; - import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.FileAssociations; +import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.TKit; @@ -111,22 +113,16 @@ public class FileAssociationsTest { public static void testNoMime() { final Path propFile = TKit.workDir().resolve("fa.properties"); - PackageTest packageTest = new PackageTest().excludeTypes(PackageType.MAC); - - packageTest.configureHelloApp().addRunOnceInitializer(() -> { + initPackageTest().addRunOnceInitializer(() -> { TKit.createPropertiesFile(propFile, Map.of( "extension", "foo", "description", "bar" )); }).addInitializer(cmd -> { - cmd.addArguments("--file-associations", propFile).saveConsoleOutput(true); - }).setExpectedExitCode(1).addBundleVerifier((cmd, result) -> { - TKit.assertTextStream( - "No MIME types were specified for File Association number 1") - .apply(result.getOutput().stream()); - TKit.assertTextStream( - "Advice to fix: Specify MIME type for File Association number 1") - .apply(result.getOutput().stream()); + cmd.addArguments("--file-associations", propFile); + cmd.validateOutput( + MAIN.cannedFormattedString("error.no-content-types-for-file-association", 1), + MAIN.cannedFormattedString("error.no-content-types-for-file-association.advice", 1)); }).run(); } @@ -134,23 +130,25 @@ public class FileAssociationsTest { public static void testTooManyMimes() { final Path propFile = TKit.workDir().resolve("fa.properties"); - PackageTest packageTest = new PackageTest().excludeTypes(PackageType.MAC); - - packageTest.configureHelloApp().addRunOnceInitializer(() -> { + initPackageTest().addRunOnceInitializer(() -> { TKit.createPropertiesFile(propFile, Map.of( "mime-type", "application/x-jpackage-foo, application/x-jpackage-bar", "extension", "foo", "description", "bar" )); }).addInitializer(cmd -> { - cmd.addArguments("--file-associations", propFile).saveConsoleOutput(true); - }).setExpectedExitCode(1).addBundleVerifier((cmd, result) -> { - TKit.assertTextStream( - "More than one MIME types was specified for File Association number 1") - .apply(result.getOutput().stream()); - TKit.assertTextStream( - "Advice to fix: Specify only one MIME type for File Association number 1") - .apply(result.getOutput().stream()); + cmd.addArguments("--file-associations", propFile); + cmd.validateOutput( + MAIN.cannedFormattedString("error.too-many-content-types-for-file-association", 1), + MAIN.cannedFormattedString("error.too-many-content-types-for-file-association.advice", 1)); }).run(); } + + private static PackageTest initPackageTest() { + return new PackageTest() + .excludeTypes(PackageType.MAC) + .configureHelloApp() + .addInitializer(JPackageCommand::setFakeRuntime) + .setExpectedExitCode(1); + } } diff --git a/test/jdk/tools/jpackage/share/JLinkOptionsTest.java b/test/jdk/tools/jpackage/share/JLinkOptionsTest.java index 7c110a25014..a882fd15f18 100644 --- a/test/jdk/tools/jpackage/share/JLinkOptionsTest.java +++ b/test/jdk/tools/jpackage/share/JLinkOptionsTest.java @@ -154,7 +154,7 @@ public final class JLinkOptionsTest { } private final JPackageCommand createJPackageCommand(String javaAppDesc) { - return JPackageCommand.helloAppImage(javaAppDesc).ignoreDefaultRuntime(true); + return JPackageCommand.helloAppImage(javaAppDesc); } private final Set getModulesInRuntime(String ... jlinkOptions) { diff --git a/test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java b/test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java index a4c2a26944a..220c55b5e50 100644 --- a/test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java +++ b/test/jdk/tools/jpackage/share/JavaOptionsEqualsTest.java @@ -54,13 +54,13 @@ import jdk.jpackage.test.TKit; public class JavaOptionsEqualsTest { - private final static String OPTION1 = + private static final String OPTION1 = "--add-exports=java.base/sun.util=me.mymodule.foo,ALL-UNNAMED"; - private final static String OPTION2 = + private static final String OPTION2 = "--add-exports=java.base/sun.security.util=other.mod.bar,ALL-UNNAMED"; - private final static String WARNING1 = + private static final String WARNING1 = "WARNING: Unknown module: me.mymodule.foo"; - private final static String WARNING2 = + private static final String WARNING2 = "WARNING: Unknown module: other.mod.bar"; private final JPackageCommand cmd; @@ -75,17 +75,13 @@ public class JavaOptionsEqualsTest { } public JavaOptionsEqualsTest(String javaAppDesc, String[] jpackageArgs) { - cmd = JPackageCommand.helloAppImage(javaAppDesc); - if (jpackageArgs != null) { - cmd.addArguments(jpackageArgs); - } + cmd = JPackageCommand.helloAppImage(javaAppDesc).addArguments(jpackageArgs).ignoreFakeRuntime(); } @Test public void test() { cmd.executeAndAssertHelloAppImageCreated(); List output = HelloApp.executeLauncher(cmd).getOutput(); - TKit.assertNotNull(output, "output is null"); TKit.assertTextStream(WARNING1).apply(output.stream()); TKit.assertTextStream(WARNING2).apply(output.stream()); } diff --git a/test/jdk/tools/jpackage/share/JavaOptionsTest.java b/test/jdk/tools/jpackage/share/JavaOptionsTest.java index e15dc02eac6..014a9434cad 100644 --- a/test/jdk/tools/jpackage/share/JavaOptionsTest.java +++ b/test/jdk/tools/jpackage/share/JavaOptionsTest.java @@ -76,7 +76,7 @@ public class JavaOptionsTest { public JavaOptionsTest(String javaAppDesc, String[] jpackageArgs, String[] expectedParams) { - cmd = JPackageCommand.helloAppImage(javaAppDesc); + cmd = JPackageCommand.helloAppImage(javaAppDesc).ignoreFakeRuntime(); if (jpackageArgs != null) { cmd.addArguments(jpackageArgs); } @@ -90,7 +90,6 @@ public class JavaOptionsTest { // 2.) run the launcher it generated List output = HelloApp.executeLauncher(cmd).getOutput(); - TKit.assertNotNull(output, "output is null"); for (String expect : expected) { TKit.assertTextStream(expect).apply(output.stream()); } diff --git a/test/jdk/tools/jpackage/share/MainClassTest.java b/test/jdk/tools/jpackage/share/MainClassTest.java index 7ac72c2c87b..15d4e0999c5 100644 --- a/test/jdk/tools/jpackage/share/MainClassTest.java +++ b/test/jdk/tools/jpackage/share/MainClassTest.java @@ -39,10 +39,13 @@ import java.util.stream.Stream; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.test.Annotations.Parameters; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.CannedFormattedString; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.CfgFile; import jdk.jpackage.test.Executor; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import static jdk.jpackage.test.JPackageCommand.cannedArgument; import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.TKit; @@ -87,8 +90,8 @@ public final class MainClassTest { return this; } - Script expectedErrorMessage(String v) { - expectedErrorMessage = v; + Script expectedErrorMessage(String key, Object... args) { + expectedErrorMessage = JPackageStringBundle.MAIN.cannedFormattedString(key, args); return this; } @@ -131,7 +134,7 @@ public final class MainClassTest { private boolean withJLink; private MainClassType mainClass; private MainClassType jarMainClass; - private String expectedErrorMessage; + private CannedFormattedString expectedErrorMessage; } public MainClassTest(Script script) { @@ -194,11 +197,12 @@ public final class MainClassTest { if (withMainClass.contains(jarMainClass) || withMainClass.contains(mainClass)) { } else if (modular) { - script.expectedErrorMessage( - "Error: Main application class is missing"); + script.expectedErrorMessage("ERR_NoMainClass"); } else { script.expectedErrorMessage( - "A main class was not specified nor was one found in the jar"); + "error.no-main-class-with-main-jar", cannedArgument(cmd -> { + return cmd.getArgumentValue("--main-jar"); + }, "MAIN-JAR")); } scripts.add(new Script[]{script}); @@ -218,11 +222,7 @@ public final class MainClassTest { if (script.expectedErrorMessage != null) { // This is the case when main class is not found nor in jar // file nor on command line. - List output = cmd - .saveConsoleOutput(true) - .execute(1) - .getOutput(); - TKit.assertTextStream(script.expectedErrorMessage).apply(output.stream()); + cmd.validateOutput(script.expectedErrorMessage).execute(1); return; } diff --git a/test/jdk/tools/jpackage/share/ModulePathTest.java b/test/jdk/tools/jpackage/share/ModulePathTest.java index 9fd07a95c23..06b30ec89e7 100644 --- a/test/jdk/tools/jpackage/share/ModulePathTest.java +++ b/test/jdk/tools/jpackage/share/ModulePathTest.java @@ -30,10 +30,12 @@ import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import jdk.jpackage.test.CannedFormattedString; import jdk.jpackage.test.TKit; import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.PackageType; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Parameters; @@ -121,25 +123,22 @@ public final class ModulePathTest { if (withGoodPath) { cmd.executeAndAssertHelloAppImageCreated(); } else { - final String expectedErrorMessage; + final CannedFormattedString expectedErrorMessage; if (modulePathArgs.isEmpty()) { - expectedErrorMessage = "Error: Missing argument: --runtime-image or --module-path"; + expectedErrorMessage = JPackageStringBundle.MAIN.cannedFormattedString( + "ERR_MissingArgument", "--runtime-image or --module-path"); } else { - expectedErrorMessage = String.format( - "Failed to find %s module in module path", appDesc.moduleName()); + expectedErrorMessage = JPackageStringBundle.MAIN.cannedFormattedString( + "error.no-module-in-path", appDesc.moduleName()); } - List output = cmd - .saveConsoleOutput(true) - .execute(1) - .getOutput(); - TKit.assertTextStream(expectedErrorMessage).apply(output.stream()); + cmd.validateOutput(expectedErrorMessage).execute(1); } } private final List modulePathArgs; - private final static String GOOD_PATH = "@GoodPath@"; - private final static String EMPTY_DIR = "@EmptyDir@"; - private final static String NON_EXISTING_DIR = "@NonExistingDir@"; + private static final String GOOD_PATH = "@GoodPath@"; + private static final String EMPTY_DIR = "@EmptyDir@"; + private static final String NON_EXISTING_DIR = "@NonExistingDir@"; } diff --git a/test/jdk/tools/jpackage/share/NonExistentTest.java b/test/jdk/tools/jpackage/share/NonExistentTest.java deleted file mode 100644 index 3c87d3c6411..00000000000 --- a/test/jdk/tools/jpackage/share/NonExistentTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2020, 2025, 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 java.util.Collection; -import java.util.List; -import jdk.jpackage.test.Annotations.Parameters; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.TKit; - -/* - * @test - * @summary jpackage application version testing - * @library /test/jdk/tools/jpackage/helpers - * @build jdk.jpackage.test.* - * @compile -Xlint:all -Werror NonExistentTest.java - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=NonExistentTest - */ - -public final class NonExistentTest { - - private final String expectedError; - private final JPackageCommand cmd; - - @Parameters - public static Collection input() { - return List.of(new Object[][]{ - // non-existent icon - {"Hello", - new String[]{"--icon", "non-existent"}, - "Error:"}, - {"com.other/com.other.Hello", - new String[]{"--icon", "non-existent"}, - "Error:"}, - // non-existent input - {"Hello", - new String[]{"--input", "non-existent"}, - "Exception:"}, - {"com.other/com.other.Hello", - new String[]{"--input", "non-existent"}, - "Exception:"}, - // non-existent resource-dir - {"Hello", - new String[]{"--resource-dir", "non-existent"}, - "Specified resource directory"}, - {"com.other/com.other.Hello", - new String[]{"--resource-dir", "non-existent"}, - "Specified resource directory"}, - }); - } - - public NonExistentTest(String javaAppDesc, String[] jpackageArgs, - String expectedError) { - this.expectedError = expectedError; - - cmd = JPackageCommand.helloAppImage(javaAppDesc) - .saveConsoleOutput(true).dumpOutput(true); - if (jpackageArgs != null) { - cmd.addArguments(jpackageArgs); - } - } - - @Test - public void test() { - List output = cmd.execute(1).getOutput(); - TKit.assertNotNull(output, "output is null"); - TKit.assertTextStream(expectedError).apply(output.stream()); - } -} diff --git a/test/jdk/tools/jpackage/share/PredefinedAppImageErrorTest.java b/test/jdk/tools/jpackage/share/PredefinedAppImageErrorTest.java deleted file mode 100644 index a415f0370cc..00000000000 --- a/test/jdk/tools/jpackage/share/PredefinedAppImageErrorTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2022, 2025, 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 java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Files; -import java.util.Collection; -import java.util.List; - -import jdk.jpackage.test.AppImageFile; -import jdk.jpackage.test.Annotations.Parameters; -import jdk.jpackage.test.Annotations.Test; -import jdk.jpackage.test.JPackageCommand; -import jdk.jpackage.test.TKit; - -/* - * @test - * @summary Test jpackage output for erroneous input with --type "app-image" and --app-image - * @library /test/jdk/tools/jpackage/helpers - * @build jdk.jpackage.test.* - * @compile -Xlint:all -Werror PredefinedAppImageErrorTest.java - * - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=PredefinedAppImageErrorTest - * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useExecutableByDefault - * - * @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main - * --jpt-run=PredefinedAppImageErrorTest - * --jpt-before-run=jdk.jpackage.test.JPackageCommand.useToolProviderByDefault - */ - -public final class PredefinedAppImageErrorTest { - - private final String expectedError; - private final JPackageCommand cmd; - - @Parameters - public static Collection input() throws IOException { - return List.of(new Object[][]{ - // --mac-sign is required - {"Hello", - null, - new String[]{"--input", "--dest", "--name", "--main-jar", "--main-class"}, - TKit.isOSX() ? - "--mac-sign option is required" : - "Option [--app-image] is not valid with type [app-image]" - }, - // --mac-app-store is required - {"Hello", - new String[]{"--mac-sign", "--mac-app-store", "--mac-app-image-sign-identity", "test"}, - new String[]{"--input", "--dest", "--name", "--main-jar", "--main-class"}, - TKit.isOSX() ? - "Option [--mac-app-store] is not valid" : - "Option [--mac-sign] is not valid on this platform" - }, - }); - } - - public PredefinedAppImageErrorTest(String javaAppDesc, String[] jpackageArgs, - String[] removeArgs, - String expectedError) { - this.expectedError = expectedError; - - cmd = JPackageCommand.helloAppImage(javaAppDesc) - .saveConsoleOutput(true).dumpOutput(true); - if (jpackageArgs != null) { - cmd.addArguments(jpackageArgs); - } - if (removeArgs != null) { - for (String arg : removeArgs) { - cmd.removeArgumentWithValue(arg); - } - } - } - - @Test - public void test() throws IOException { - getDummyAppImage(cmd); - - List output = cmd.execute(1).getOutput(); - TKit.assertNotNull(output, "output is null"); - TKit.assertTextStream(expectedError).apply(output.stream()); - } - - private void getDummyAppImage(JPackageCommand cmd) throws IOException { - Path dummyAppFolder - = TKit.createTempDirectory("DummyAppImage").toAbsolutePath(); - - Path dummyAppFile - = dummyAppFolder.resolve("DummyAppFile").toAbsolutePath(); - Files.createFile(dummyAppFile); - - cmd.addArguments("--app-image", dummyAppFolder.toString()); - new AppImageFile("PredefinedAppImageErrorTest", "Hello").save(dummyAppFolder); - } - -} diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index 7b0e88a5ae7..9eb4f8d231e 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -29,10 +29,12 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.RunnablePackageTest.Action; import jdk.jpackage.test.PackageTest; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.TKit; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Executor; import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.LinuxHelper; @@ -83,17 +85,24 @@ public class RuntimePackageTest { } @Test - public static void testUsrInstallDir() { + @Parameter("/usr") + @Parameter("/usr/lib/Java") + public static void testUsrInstallDir(String installDir) { init(PackageType.LINUX) .addInitializer(cmd -> cmd.addArguments("--install-dir", "/usr")) .run(); } @Test - public static void testUsrInstallDir2() { - init(PackageType.LINUX) - .addInitializer(cmd -> cmd.addArguments("--install-dir", "/usr/lib/Java")) - .run(); + public static void testName() { + // Test that jpackage can derive package name from the path to runtime image. + init(PackageType.NATIVE) + .addInitializer(cmd -> cmd.removeArgumentWithValue("--name")) + // Don't attempt to install this package as it may have an odd name derived from + // the runtime image path. Say, on Linux for `--runtime-image foo/bar/sed` + // command line jpackage will create a package named 'sed' that will conflict + // with the default 'sed' package. + .run(Action.CREATE_AND_UNPACK); } private static PackageTest init(Set types) { @@ -157,7 +166,8 @@ public class RuntimePackageTest { "Check the package doesn't deliver [%s] copyright file", copyright)); } - }); + }) + .forTypes(types); } private static Set listFiles(Path root) throws IOException {