Skip to content

8351372: Improve negative tests coverage of jpackage #23936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1404e6d
TestInstance: escape control characters in test description. Fail run…
alexeysemenyukoracle Mar 2, 2025
c50fc5c
More robust msi unpack when wix4 is used
alexeysemenyukoracle Mar 6, 2025
08a2a66
Can run launcher verifier with altered environment variables
alexeysemenyukoracle Mar 5, 2025
ce854a6
Add Executor.setEnvVar()
alexeysemenyukoracle Mar 5, 2025
2ede257
Relax constraints on arguments for string formatting in JPackageStrin…
alexeysemenyukoracle Feb 28, 2025
e74c14c
Make CannedFormattedString a record and add null checks. Add CannedFo…
alexeysemenyukoracle Feb 28, 2025
f4381f0
JPackageCommand can derive app name from jpackage command line withou…
alexeysemenyukoracle Feb 28, 2025
f2cd651
Create fake runtime in unique directory
alexeysemenyukoracle Mar 7, 2025
4fc37b2
Support multiple output validators in JPackageCommand
alexeysemenyukoracle Mar 6, 2025
185e38a
Make PackageType.getName() public and rename it into PackageType.getT…
alexeysemenyukoracle Feb 28, 2025
2838a08
Move token substitution in generic TokenReplace class.
alexeysemenyukoracle Mar 2, 2025
9398463
More negative tests
alexeysemenyukoracle Feb 28, 2025
2706c90
Better testing of DottedVersion: verify exception error messages
alexeysemenyukoracle Feb 28, 2025
5197f61
Better test coverage for bad app image
alexeysemenyukoracle Feb 28, 2025
a4288bb
Test error reported by jpackage when `--temp` arg points to existing …
alexeysemenyukoracle Mar 2, 2025
fefdf4d
Remove MacAppStoreRuntimeTest.java. The positive test duplicates othe…
alexeysemenyukoracle Mar 3, 2025
d85d67f
Remove redundant PredefinedAppImageErrorTest.java. It duplicates Erro…
alexeysemenyukoracle Mar 3, 2025
915a15c
Remove redundant MacAppStoreJlinkOptionsTest.testWithoutStripNativeCo…
alexeysemenyukoracle Mar 3, 2025
2aa3de5
Better negative testing in MainClassTest
alexeysemenyukoracle Mar 6, 2025
0c32b27
NonExistentTest is redundant. It duplicates functionality of ErrorTes…
alexeysemenyukoracle Mar 6, 2025
810aac3
ModulePathTest: use CannedFormattedString to validate error output
alexeysemenyukoracle Mar 6, 2025
30d42dc
FileAssociationsTest: use CannedFormattedString
alexeysemenyukoracle Mar 6, 2025
3810b57
Make JavaOptionsEqualsTest and JavaOptionsTest work when external fak…
alexeysemenyukoracle Mar 6, 2025
e853e56
LinuxResourceTest: use CannedFormattedString
alexeysemenyukoracle Mar 6, 2025
cc03a14
Enhance RuntimePackageTest to test that jpackage can derive package n…
alexeysemenyukoracle Mar 6, 2025
4835cc5
Remove negative tests from AppVersionTest. ErrorTest tests invalid ap…
alexeysemenyukoracle Feb 28, 2025
656a8a3
CommandLine.parse() doesn't throw `java.io.FileNotFoundException` whe…
alexeysemenyukoracle Feb 28, 2025
f810e23
DeployParams: replace {"ERR_MissingArgument", "--input"} with "error.…
alexeysemenyukoracle Feb 28, 2025
8e2db23
Update copyright year
alexeysemenyukoracle Mar 7, 2025
dc5974f
Ran ./bin/blessed-modifier-order.sh
alexeysemenyukoracle Mar 7, 2025
8d72812
Ignore default runtime if jpackage command line has `--jlink-options`…
alexeysemenyukoracle Mar 7, 2025
3fe3eed
Give unique descriptions to signing tests
alexeysemenyukoracle Mar 7, 2025
eda0efe
Update copyright year
alexeysemenyukoracle Mar 7, 2025
e6ad500
Fix Unicode signing certificate index
alexeysemenyukoracle Mar 10, 2025
5458514
Fix typo
alexeysemenyukoracle Mar 10, 2025
3fdf653
Use Executor.trace() instead of TKit.trace()
alexeysemenyukoracle Mar 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -166,8 +166,7 @@ public void validate() throws PackagerException {
}
} else {
if (!hasInput && !hasAppImage) {
throw new PackagerException(
"ERR_MissingArgument", "--input");
throw new PackagerException("error.no-input-parameter");
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -57,9 +57,7 @@ private DottedVersion(String version, boolean greedy) {
if (!greedy) {
return null;
} else {
throw new IllegalArgumentException(MessageFormat.format(I18N.
getString("error.version-string-zero-length-component"),
version));
ds.throwException();
}
}

Expand All @@ -77,8 +75,7 @@ private DottedVersion(String version, boolean greedy) {
}).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();
}
}
}
Expand All @@ -89,7 +86,7 @@ private static class DigitsSupplier {
this.input = input;
}

public String getNextDigits() {
String getNextDigits() {
if (stoped) {
return null;
}
Expand Down Expand Up @@ -130,10 +127,29 @@ public String getNextDigits() {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -185,10 +185,6 @@ private static LauncherData createNonModular(
}

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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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.<String>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<Integer> 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<String, Object> 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<String, Object> 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<String, Object> createCachingTokenValueSupplier(Map<String, Supplier<Object>> tokenValueSuppliers) {
Objects.requireNonNull(tokenValueSuppliers);
final Map<String, Object> 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<Pattern> regexps;
private final static Object NULL_SUPPLIED = new Object();
}
17 changes: 9 additions & 8 deletions src/jdk.jpackage/share/classes/jdk/jpackage/main/Main.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {

Expand Down Expand Up @@ -69,7 +70,7 @@ public int execute(PrintWriter out, PrintWriter err, String... args) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ CountingBundleVerifier createBundleVerifier(int jpackageExitCode) {
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),
Expand Down Expand Up @@ -238,7 +238,7 @@ public String toString() {
private final int expectedJPackageExitCode;
}

private final static class CountingInstaller extends TickCounter implements Function<JPackageCommand, Integer> {
private static final class CountingInstaller extends TickCounter implements Function<JPackageCommand, Integer> {

@Override
public Integer apply(JPackageCommand cmd) {
Expand Down Expand Up @@ -376,7 +376,7 @@ public Executor.Result execute(int expectedExitCode) {
};
}).setExpectedExitCode(expectedJPackageExitCode)
.setExpectedInstallExitCode(handlersSpec.installExitCode)
.isPackageTypeSupported(type -> true)
.isPackageTypeEnabled(type -> true)
.forTypes().packageHandlers(handlers);
}

Expand Down Expand Up @@ -439,7 +439,7 @@ private List<Verifiable> run(Optional<Consumer<PackageTest>> customConfigure) {
}
}

private final static class TestSpecBuilder {
private static final class TestSpecBuilder {

TestSpecBuilder type(PackageType v) {
type = Objects.requireNonNull(v);
Expand Down
Loading