diff --git a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java index 0791c79c662..7c71d375761 100644 --- a/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java +++ b/src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromOptions.java @@ -27,6 +27,7 @@ package jdk.jpackage.internal; import static jdk.jpackage.internal.FromOptions.buildApplicationBuilder; import static jdk.jpackage.internal.FromOptions.createPackageBuilder; import static jdk.jpackage.internal.LinuxPackagingPipeline.APPLICATION_LAYOUT; +import static jdk.jpackage.internal.cli.StandardOption.APP_VERSION; import static jdk.jpackage.internal.cli.StandardOption.LINUX_APP_CATEGORY; import static jdk.jpackage.internal.cli.StandardOption.LINUX_DEB_MAINTAINER_EMAIL; import static jdk.jpackage.internal.cli.StandardOption.LINUX_MENU_GROUP; @@ -37,8 +38,9 @@ import static jdk.jpackage.internal.cli.StandardOption.LINUX_RPM_LICENSE_TYPE; import static jdk.jpackage.internal.cli.StandardOption.LINUX_SHORTCUT_HINT; import static jdk.jpackage.internal.model.StandardPackageType.LINUX_DEB; import static jdk.jpackage.internal.model.StandardPackageType.LINUX_RPM; - +import java.util.Optional; import jdk.jpackage.internal.cli.Options; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.model.Launcher; import jdk.jpackage.internal.model.LinuxApplication; import jdk.jpackage.internal.model.LinuxDebPackage; @@ -50,6 +52,10 @@ import jdk.jpackage.internal.model.StandardPackageType; final class LinuxFromOptions { static LinuxApplication createLinuxApplication(Options options) { + return createLinuxApplication(options, Optional.empty()); + } + + static LinuxApplication createLinuxApplication(Options options, Optional type) { final var launcherFromOptions = new LauncherFromOptions().faWithDefaultDescription(); @@ -67,7 +73,20 @@ final class LinuxFromOptions { appBuilder.launchers().map(LinuxPackagingPipeline::normalizeShortcuts).ifPresent(appBuilder::launchers); - return LinuxApplication.create(appBuilder.create()); + var app = appBuilder.create(); + + if (!APP_VERSION.containsIn(options)) { + // User didn't explicitly specify the version on the command line. jpackage derived it from the input. + // In this case it should ensure the derived value is valid RPM version. + if (type.isPresent()) { + if (type.get().equals(LINUX_RPM)) { + app = ApplicationBuilder.normalizeVersion(app, app.version(), + LinuxFromOptions::normalizeRpmVersion); + } + } + } + + return LinuxApplication.create(app); } static LinuxRpmPackage createLinuxRpmPackage(Options options, LinuxRpmSystemEnvironment sysEnv) { @@ -101,7 +120,7 @@ final class LinuxFromOptions { private static LinuxPackageBuilder createLinuxPackageBuilder(Options options, LinuxSystemEnvironment sysEnv, StandardPackageType type) { - final var app = createLinuxApplication(options); + final var app = createLinuxApplication(options, Optional.of(type)); final var superPkgBuilder = createPackageBuilder(options, app, type); @@ -118,4 +137,15 @@ final class LinuxFromOptions { return pkgBuilder; } + static String normalizeRpmVersion(String version) { + // RPM does not support "-" symbol in version. In some case + // we might have "-" from "release" file version. + // Normalize version if it has "-" symbols. All other supproted version + // formats by "release" file should be supported by RPM. + if (version.contains("-")) { + return DottedVersion.lazy(version).toComponentsString(); + } + + return version; + } } diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java index 6c9069e76eb..95f3668ce09 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromOptions.java @@ -231,10 +231,7 @@ final class MacFromOptions { if (!APP_VERSION.containsIn(options)) { // User didn't explicitly specify the version on the command line. jpackage derived it from the input. // In this case it should ensure the derived value is valid MacOS version. - UnaryOperator versionNormalizer = version -> { - return normalizeVersion(version); - }; - app = ApplicationBuilder.normalizeVersion(app, app.version(), versionNormalizer); + app = ApplicationBuilder.normalizeVersion(app, app.version(), MacFromOptions::normalizeVersion); } final var appBuilder = new MacApplicationBuilder(app); @@ -358,11 +355,6 @@ final class MacFromOptions { // When reading from release file it can be 1 or 3 or maybe more. // We will always normalize to 3 components if needed. DottedVersion ver = DottedVersion.lazy(version); - if (ver.getComponentsCount() > 3) { - return ver.trim(3).pad(3).toComponentsString(); - } else { - // We should drop any characters. For example: "-ea". - return ver.toComponentsString(); - } + return ver.trim(3).toComponentsString(); } } diff --git a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DottedVersion.java b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DottedVersion.java index ea010a91482..0774fb7f1c0 100644 --- a/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DottedVersion.java +++ b/src/jdk.jpackage/share/classes/jdk/jpackage/internal/model/DottedVersion.java @@ -37,12 +37,11 @@ import java.util.stream.Stream; public final class DottedVersion { private DottedVersion(String version, boolean greedy) { - this.value = version; if (version.isEmpty()) { if (greedy) { throw new IllegalArgumentException(I18N.getString("error.version-string-empty")); } else { - this.components = new BigInteger[0]; + this.components = new Component[0]; this.suffix = ""; } } else { @@ -58,7 +57,7 @@ public final class DottedVersion { } try { - return new BigInteger(digits); + return new Component(digits); } catch (NumberFormatException ex) { if (!greedy) { return null; @@ -68,7 +67,7 @@ public final class DottedVersion { digits)); } } - }).takeWhile(Objects::nonNull).toArray(BigInteger[]::new); + }).takeWhile(Objects::nonNull).toArray(Component[]::new); suffix = ds.getUnprocessedString(); if (!suffix.isEmpty() && greedy) { ds.throwException(); @@ -76,6 +75,11 @@ public final class DottedVersion { } } + private DottedVersion(Component[] components, String suffix) { + this.components = components; + this.suffix = suffix; + } + private static class DigitsSupplier { DigitsSupplier(String input) { @@ -211,9 +215,31 @@ public final class DottedVersion { return Arrays.deepEquals(this.components, other.components); } + public DottedVersion trim(int limit) { + if (limit < 0) { + throw new IllegalArgumentException(); + } else if (limit >= components.length) { + return this; + } else { + return new DottedVersion(Arrays.copyOf(components, limit), suffix); + } + } + + public DottedVersion pad(int limit) { + if (limit < 0) { + throw new IllegalArgumentException(); + } else if (limit <= components.length) { + return this; + } else { + var newComponents = Arrays.copyOf(components, limit); + Arrays.fill(newComponents, components.length, newComponents.length, Component.ZERO); + return new DottedVersion(newComponents, suffix); + } + } + @Override public String toString() { - return value; + return Stream.of(components).map(Component::toString).collect(Collectors.joining(".")) + suffix; } public String getUnprocessedSuffix() { @@ -221,35 +247,7 @@ public final class DottedVersion { } public String toComponentsString() { - return Stream.of(components).map(BigInteger::toString).collect(Collectors.joining(".")); - } - - public DottedVersion trim(int componentLimit) { - if (componentLimit < 0) { - throw new IllegalArgumentException(); - } - - if (components.length > componentLimit) { - components = Arrays.stream(components).limit(componentLimit) - .toArray(BigInteger[]::new); - } - - return this; - } - - public DottedVersion pad(int componentLimit) { - if (componentLimit <= 0) { - throw new IllegalArgumentException(); - } - - if (components.length < componentLimit) { - final int origLength = components.length; - components = Arrays.copyOf(components, componentLimit); - Arrays.fill(components, origLength, components.length, - new BigInteger("0")); - } - - return this; + return Stream.of(components).map(Component::parsedValue).map(BigInteger::toString).collect(Collectors.joining(".")); } public int getComponentsCount() { @@ -257,10 +255,27 @@ public final class DottedVersion { } public BigInteger[] getComponents() { - return components; + return Stream.of(components).map(Component::parsedValue).toArray(BigInteger[]::new); } - private BigInteger[] components; - private final String value; + private record Component(BigInteger parsedValue, String strValue) { + Component { + Objects.requireNonNull(parsedValue); + Objects.requireNonNull(strValue); + } + + Component(String strValue) { + this(new BigInteger(strValue), strValue); + } + + @Override + public String toString() { + return strValue; + } + + static final Component ZERO = new Component(BigInteger.ZERO, "0"); + } + + private final Component[] components; private final String suffix; } diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java index 8399c7d4dd7..35f66310045 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/WinFromOptions.java @@ -86,10 +86,7 @@ final class WinFromOptions { if (!APP_VERSION.containsIn(options)) { // User didn't explicitly specify the version on the command line. jpackage derived it from the input. // In this case it should ensure the derived value is valid Windows version. - UnaryOperator versionNormalizer = version -> { - return normalizeVersion(version); - }; - app = ApplicationBuilder.normalizeVersion(app, app.version(), versionNormalizer); + app = ApplicationBuilder.normalizeVersion(app, app.version(), WinFromOptions::normalizeVersion); } return WinApplication.create(app); @@ -137,7 +134,7 @@ final class WinFromOptions { // When reading from release file it can be 1 or 3 or maybe more. // We will always normalize to 4 components if needed. DottedVersion ver = DottedVersion.lazy(version); - if (ver.getComponentsCount() != 2 || ver.getComponentsCount() != 4) { + if (ver.getComponentsCount() != 2 && ver.getComponentsCount() != 4) { return ver.trim(4).pad(4).toComponentsString(); } else { // We should drop any characters. For example: "-ea". 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 6f6fe603cad..f06bf2d9506 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -248,6 +248,11 @@ public class JPackageCommand extends CommandArguments { return getArgumentValue("--input", () -> null, Path::of); } + private boolean isLinuxRpmPackageType() { + return TKit.isLinux() && (PackageType.LINUX_RPM == getArgumentValue("--type", + () -> null, PACKAGE_TYPES::get)); + } + public String version() { return Optional.ofNullable(getArgumentValue("--app-version")).or(() -> { if (isRuntime()) { @@ -259,6 +264,8 @@ public class JPackageCommand extends CommandArguments { return WindowsHelper.getNormalizedVersion(releaseVersion.toString()); } else if (TKit.isOSX()) { return MacHelper.getNormalizedVersion(releaseVersion.toString()); + } else if (isLinuxRpmPackageType()) { + return LinuxHelper.getNormalizedRpmVersion(releaseVersion.toString()); } else { return releaseVersion.toString(); } diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java index 78562b2ed26..66038181bac 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/LinuxHelper.java @@ -49,6 +49,7 @@ import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; +import jdk.jpackage.internal.model.DottedVersion; import jdk.jpackage.internal.util.PathUtils; import jdk.jpackage.internal.util.function.ThrowingConsumer; import jdk.jpackage.test.LauncherShortcut.InvokeShortcutSpec; @@ -127,6 +128,18 @@ public final class LinuxHelper { getDefaultPackageArch(packageType)) + packageType.getSuffix(); } + static String getNormalizedRpmVersion(String version) { + // RPM does not support "-" symbol in version. In some case + // we might have "-" from "release" file version. + // Normalize version if it has "-" symbols. All other supproted version + // formats by "release" file should be supported by RPM. + if (version.contains("-")) { + return DottedVersion.lazy(version).toComponentsString(); + } + + return version; + } + public static Stream getPackageFiles(JPackageCommand cmd) { cmd.verifyIsOfType(PackageType.LINUX); diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java index d2898af59be..c758f7f0106 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -1114,11 +1114,7 @@ public final class MacHelper { // macOS requires 1, 2 or 3 components version string. // We will always normalize to 3 components if needed. DottedVersion ver = DottedVersion.lazy(version); - if (ver.getComponentsCount() > 3) { - return ver.trim(3).pad(3).toComponentsString(); - } else { - return ver.toComponentsString(); - } + return ver.trim(3).toComponentsString(); } static Path getInstallationDirectory(JPackageCommand cmd) { 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 a1b85614eca..c504f23b3f1 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/WindowsHelper.java @@ -61,7 +61,7 @@ public class WindowsHelper { // Windows requires 2 or 4 components version string. // We will always normalize to 4 components if needed. DottedVersion ver = DottedVersion.lazy(version); - if (ver.getComponentsCount() != 2 || ver.getComponentsCount() != 4) { + if (ver.getComponentsCount() != 2 && ver.getComponentsCount() != 4) { return ver.trim(4).pad(4).toComponentsString(); } else { return ver.toComponentsString(); diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java index f4749a6130b..0ddbd9cd181 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/model/DottedVersionTest.java @@ -32,8 +32,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertNotSame; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; @@ -41,34 +44,26 @@ public class DottedVersionTest { public record TestConfig(String input, Function createVersion, String expectedSuffix, - int expectedComponentCount, String expectedToComponent, int numberOfComponents) { + int expectedComponentCount, String expectedToComponent) { TestConfig(String input, Type type, int expectedComponentCount, String expectedToComponent) { - this(input, type.createVersion, "", expectedComponentCount, expectedToComponent, -1); + this(input, type.createVersion, "", expectedComponentCount, expectedToComponent); } TestConfig(String input, Type type, int expectedComponentCount) { - this(input, type.createVersion, "", expectedComponentCount, input, -1); - } - - TestConfig(String input, Type type, String expectedToComponent, int numberOfComponents) { - this(input, type.createVersion, "", -1, expectedToComponent, numberOfComponents); + this(input, type.createVersion, "", expectedComponentCount, input); } static TestConfig greedy(String input, int expectedComponentCount, String expectedToComponent) { - return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, expectedToComponent, -1); + return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, expectedToComponent); } static TestConfig greedy(String input, int expectedComponentCount) { - return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, input, -1); + return new TestConfig(input, Type.GREEDY.createVersion, "", expectedComponentCount, input); } static TestConfig lazy(String input, String expectedSuffix, int expectedComponentCount, String expectedToComponent) { - return new TestConfig(input, Type.LAZY.createVersion, expectedSuffix, expectedComponentCount, expectedToComponent, -1); - } - - static TestConfig lazy(String input, String expectedToComponent, int numberOfComponents) { - return new TestConfig(input, Type.LAZY.createVersion, "", -1, expectedToComponent, numberOfComponents); + return new TestConfig(input, Type.LAZY.createVersion, expectedSuffix, expectedComponentCount, expectedToComponent); } } @@ -124,42 +119,99 @@ public class DottedVersionTest { @ParameterizedTest @MethodSource - public void testTrim(TestConfig cfg) { - var dv = cfg.createVersion.apply(cfg.input()); - assertEquals(cfg.expectedToComponent(), - dv.trim(cfg.numberOfComponents()).toComponentsString()); - } - - private static List testTrim() { - List data = new ArrayList<>(); - data.addAll(List.of( - TestConfig.lazy("", "", 0), - TestConfig.lazy("1", "", 0), - TestConfig.lazy("1.2.3", "1", 1), - TestConfig.lazy("1.2.3", "1.2", 2), - TestConfig.lazy("1.2.3", "1.2.3", 3), - TestConfig.lazy("1.2.3", "1.2.3", 4) - )); - return data; + public void testTrim(DottedVersion ver, String expectedStr, int limit) { + var expected = DottedVersion.lazy(expectedStr); + var actual = ver.trim(limit); + assertEquals(expected, actual); + if (limit >= ver.getComponents().length) { + assertSame(ver, actual); + } else { + assertNotSame(ver, actual); + } + assertEquals(expectedStr, actual.toString()); } @ParameterizedTest @MethodSource - public void testPad(TestConfig cfg) { - var dv = cfg.createVersion.apply(cfg.input()); - assertEquals(cfg.expectedToComponent(), - dv.pad(cfg.numberOfComponents()).toComponentsString()); + public void testTrimNegative(DottedVersion ver, int limit) { + assertThrowsExactly(IllegalArgumentException.class, () -> { + ver.trim(limit); + }); } - private static List testPad() { - List data = new ArrayList<>(); - data.addAll(List.of( - TestConfig.lazy("", "0", 1), - TestConfig.lazy("1", "1", 1), - TestConfig.lazy("1", "1.0", 2), - TestConfig.lazy("1.2.3", "1.2.3.0.0", 5) - )); - return data; + private static Stream testTrim() { + + var testCases = new ArrayList(); + + for (var suffix : List.of("", ".foo")) { + testCases.addAll(List.of( + Arguments.of("1.02.3" + suffix, "" + suffix, 0), + Arguments.of("1.02.3" + suffix, "1" + suffix, 1), + Arguments.of("1.02.3" + suffix, "1.02" + suffix, 2), + Arguments.of("1.02.3" + suffix, "1.02.3" + suffix, 3), + Arguments.of("1.02.3" + suffix, "1.02.3" + suffix, 4) + )); + } + + return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion); + } + + private static Stream testTrimNegative() { + return Stream.of( + Arguments.of("10.5.foo", -1) + ).map(DottedVersionTest::mapFirstStringToDottedVersion); + } + + @ParameterizedTest + @MethodSource + public void testPad(DottedVersion ver, String expectedStr, int limit) { + var expected = DottedVersion.lazy(expectedStr); + var actual = ver.pad(limit); + assertEquals(expected, actual); + if (limit <= ver.getComponents().length) { + assertSame(ver, actual); + } else { + assertNotSame(ver, actual); + } + assertEquals(expectedStr, actual.toString()); + } + + @ParameterizedTest + @MethodSource + public void testPadNegative(DottedVersion ver, int limit) { + assertThrowsExactly(IllegalArgumentException.class, () -> { + ver.pad(limit); + }); + } + + private static Stream testPad() { + + var testCases = new ArrayList(); + + for (var suffix : List.of("", ".foo")) { + testCases.addAll(List.of( + Arguments.of("" + suffix, "" + suffix, 0), + Arguments.of("1.02.3" + suffix, "1.02.3" + suffix, 0), + Arguments.of("" + suffix, "0" + suffix, 1), + Arguments.of("1" + suffix, "1" + suffix, 1), + Arguments.of("1" + suffix, "1.0" + suffix, 2), + Arguments.of("1.02.3" + suffix, "1.02.3.0.0" + suffix, 5) + )); + } + + return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion); + } + + private static Stream testPadNegative() { + return Stream.of( + Arguments.of("10.5.foo", -1) + ).map(DottedVersionTest::mapFirstStringToDottedVersion); + } + + private static Arguments mapFirstStringToDottedVersion(Arguments v) { + var objs = v.get(); + objs[0] = DottedVersion.lazy((String)objs[0]); + return Arguments.of(objs); } record InvalidVersionTestSpec(String version, String invalidComponent) { diff --git a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeVersionReaderTest.java b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeVersionReaderTest.java index bc9e2393309..8e4a20ab25a 100644 --- a/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeVersionReaderTest.java +++ b/test/jdk/tools/jpackage/junit/share/jdk.jpackage/jdk/jpackage/internal/util/RuntimeVersionReaderTest.java @@ -44,41 +44,43 @@ public class RuntimeVersionReaderTest { @ParameterizedTest @CsvSource({ - "27.1.2, true", - "27.1.2, false", - "27.1.2-ea, true", - "27.1.2-ea, false" + "\"27.1.2\"", + "27.1.2", + "\"27.1.2-ea\"", + "27.1.2-ea", + "\"27.1.2+15\"", + "27.1.2+15" }) - public void test_release_file_with_version(String version, - boolean quoteVersion, @TempDir Path workdir) { + public void test_release_file_with_version(String version, @TempDir Path workdir) { final var value = RuntimeVersionReader.readVersion( - createPropFileWithValue(workdir, "JAVA_VERSION", version, quoteVersion)); + createPropFileWithValue(workdir, "JAVA_VERSION", version)); assertTrue(value.isPresent()); value.ifPresent(val -> { - assertEquals(version, value.get().toString()); + // Version return by readVersion is always unquoted + String expectedVersion = version.replaceAll("^\"|\"$", ""); + assertEquals(expectedVersion, value.get().toString()); }); } @ParameterizedTest @CsvSource({ - "7.1.2+foo, true", - "foo, true", - "'', true", - "7.1.2+foo, false", - "foo, false", - "'', false" + "\"7.1.2+foo\"", + "\"foo\"", + "\"\"", + "7.1.2+foo", + "foo", + "''" }) - public void test_release_file_with_invalid_version(String version, - boolean quoteVersion, @TempDir Path workdir) { + public void test_release_file_with_invalid_version(String version, @TempDir Path workdir) { final var value = RuntimeVersionReader.readVersion( - createPropFileWithValue(workdir, "JAVA_VERSION", version, quoteVersion)); + createPropFileWithValue(workdir, "JAVA_VERSION", version)); assertFalse(value.isPresent()); } @Test public void test_release_file_without_version(@TempDir Path workdir) { final var value = RuntimeVersionReader.readVersion( - createPropFileWithValue(workdir, "JDK_VERSION", "27.1.2", true)); + createPropFileWithValue(workdir, "JDK_VERSION", "\"27.1.2\"")); assertFalse(value.isPresent()); } @@ -88,15 +90,10 @@ public class RuntimeVersionReaderTest { assertFalse(value.isPresent()); } - private Path createPropFileWithValue(Path workdir, String name, String value, - boolean quoteValue) { + private Path createPropFileWithValue(Path workdir, String name, String value) { Path releaseFile = workdir.resolve("release"); Properties props = new Properties(); - if (quoteValue) { - props.setProperty(name, "\"" + value + "\""); - } else { - props.setProperty(name, value); - } + props.setProperty(name, value); try (Writer writer = Files.newBufferedWriter(releaseFile)) { props.store(writer, null); diff --git a/test/jdk/tools/jpackage/share/AppVersionTest.java b/test/jdk/tools/jpackage/share/AppVersionTest.java index ad96a9b9e0c..f7e09c03df6 100644 --- a/test/jdk/tools/jpackage/share/AppVersionTest.java +++ b/test/jdk/tools/jpackage/share/AppVersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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,13 +62,25 @@ public final class AppVersionTest { "3.3"}}, {"7.8", "com.other/com.other.Hello", new String[]{"--app-version", "4", "--app-version", "7.8"}}, - // Pick version from jar - {"3.10.17", "com.other/com.other.Hello@3.10.17", null}, // Ignore version in jar if --app-version given {"7.5.81", "com.other/com.other.Hello@3.10.17", new String[]{ "--app-version", "7.5.81"}} })); + if (TKit.isWindows()) { + // Windows will normalize unsupproted number of components + // for jar, module or release file versions. + data.addAll(List.of(new Object[][]{ + // Pick version from jar + {"3.10.17.0", "com.other/com.other.Hello@3.10.17", null}, + })); + } else { + data.addAll(List.of(new Object[][]{ + // Pick version from jar + {"3.10.17", "com.other/com.other.Hello@3.10.17", null}, + })); + } + return data; } diff --git a/test/jdk/tools/jpackage/share/ModulePathTest3.java b/test/jdk/tools/jpackage/share/ModulePathTest3.java index 192fefa4e29..2c6251f6a66 100644 --- a/test/jdk/tools/jpackage/share/ModulePathTest3.java +++ b/test/jdk/tools/jpackage/share/ModulePathTest3.java @@ -28,7 +28,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import javax.xml.xpath.XPathExpressionException; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.test.AppImageFile; import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JavaAppDesc; @@ -53,9 +55,8 @@ import jdk.jpackage.test.TKit; public final class ModulePathTest3 { - public ModulePathTest3(String jlinkOutputSubdir, String runtimeSubdir) { - this.jlinkOutputSubdir = Path.of(jlinkOutputSubdir); - this.runtimeSubdir = Path.of(runtimeSubdir); + public ModulePathTest3(RuntimeType runtimeType) { + this.runtimeType = Objects.requireNonNull(runtimeType); } /** @@ -74,8 +75,24 @@ public final class ModulePathTest3 { HelloApp.createBundle(appDesc, moduleOutputDir); final Path workDir = TKit.createTempDirectory("runtime").resolve("data"); - final Path jlinkOutputDir = workDir.resolve(jlinkOutputSubdir); - Files.createDirectories(jlinkOutputDir.getParent()); + final Path jlinkOutputDir; + switch (runtimeType) { + case IMAGE -> { + jlinkOutputDir = workDir; + } + case MAC_BUNDLE -> { + var macBundle = new MacBundle(workDir); + + // Create macOS bundle structure sufficient to pass jpackage validation. + Files.createDirectories(macBundle.homeDir().getParent()); + Files.createDirectories(macBundle.macOsDir()); + Files.createFile(macBundle.infoPlistFile()); + jlinkOutputDir = macBundle.homeDir(); + } + default -> { + throw new AssertionError(); + } + } new Executor() .setToolProvider(JavaTool.JLINK) @@ -90,21 +107,13 @@ public final class ModulePathTest3 { "--strip-native-commands") .execute(); - if (TKit.isOSX()) { - // MacBundle requires valid bundle - if (Files.exists(workDir.resolve("Contents/Home"))) { - Files.createFile(workDir.resolve("Contents/Info.plist")); - Files.createDirectories(workDir.resolve("Contents/MacOS")); - } - } - JPackageCommand cmd = new JPackageCommand() .setDefaultAppName() .setPackageType(PackageType.IMAGE) .setDefaultInputOutput() .removeArgumentWithValue("--input") .addArguments("--module", appDesc.moduleName() + "/" + appDesc.className()) - .setArgumentValue("--runtime-image", workDir.resolve(runtimeSubdir)); + .setArgumentValue("--runtime-image", workDir); cmd.executeAndAssertHelloAppImageCreated(); @@ -116,23 +125,25 @@ public final class ModulePathTest3 { } @Parameters - public static Collection data() { - final List paths = new ArrayList<>(); - paths.add(new String[] { "", "" }); + public static Collection data() { + final List testCases = new ArrayList<>(); + testCases.add(RuntimeType.IMAGE); if (TKit.isOSX()) { // On OSX jpackage should accept both runtime root and runtime home // directories. - paths.add(new String[] { "Contents/Home", "" }); + testCases.add(RuntimeType.MAC_BUNDLE); } - List data = new ArrayList<>(); - for (var pathCfg : paths) { - data.add(new Object[] { pathCfg[0], pathCfg[1] }); - } - - return data; + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); } - private final Path jlinkOutputSubdir; - private final Path runtimeSubdir; + public enum RuntimeType { + IMAGE, + MAC_BUNDLE, + ; + } + + private final RuntimeType runtimeType; } diff --git a/test/jdk/tools/jpackage/share/NoMPathRuntimeTest.java b/test/jdk/tools/jpackage/share/NoMPathRuntimeTest.java index e35217e3d7e..4464e75c277 100644 --- a/test/jdk/tools/jpackage/share/NoMPathRuntimeTest.java +++ b/test/jdk/tools/jpackage/share/NoMPathRuntimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, 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 @@ -24,18 +24,20 @@ import java.io.IOException; import java.nio.file.Files; -import java.util.Collection; -import java.util.ArrayList; -import java.util.List; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import jdk.jpackage.internal.util.MacBundle; import jdk.jpackage.test.Annotations.Parameters; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; +import jdk.jpackage.test.HelloApp; import jdk.jpackage.test.JPackageCommand; import jdk.jpackage.test.JavaAppDesc; import jdk.jpackage.test.JavaTool; import jdk.jpackage.test.TKit; -import jdk.jpackage.test.HelloApp; /* @@ -50,9 +52,8 @@ import jdk.jpackage.test.HelloApp; public final class NoMPathRuntimeTest { - public NoMPathRuntimeTest(String jlinkOutputSubdir, String runtimeSubdir) { - this.jlinkOutputSubdir = Path.of(jlinkOutputSubdir); - this.runtimeSubdir = Path.of(runtimeSubdir); + public NoMPathRuntimeTest(RuntimeType runtimeType) { + this.runtimeType = Objects.requireNonNull(runtimeType); } @Test @@ -65,8 +66,24 @@ public final class NoMPathRuntimeTest { cmd.executePrerequisiteActions(); final Path workDir = TKit.createTempDirectory("runtime").resolve("data"); - final Path jlinkOutputDir = workDir.resolve(jlinkOutputSubdir); - Files.createDirectories(jlinkOutputDir.getParent()); + final Path jlinkOutputDir; + switch (runtimeType) { + case IMAGE -> { + jlinkOutputDir = workDir; + } + case MAC_BUNDLE -> { + var macBundle = new MacBundle(workDir); + + // Create macOS bundle structure sufficient to pass jpackage validation. + Files.createDirectories(macBundle.homeDir().getParent()); + Files.createDirectories(macBundle.macOsDir()); + Files.createFile(macBundle.infoPlistFile()); + jlinkOutputDir = macBundle.homeDir(); + } + default -> { + throw new AssertionError(); + } + } // List of modules required for test app. final var modules = new String[] { @@ -74,7 +91,7 @@ public final class NoMPathRuntimeTest { "java.desktop" }; - Executor jlink = new Executor() + new Executor() .setToolProvider(JavaTool.JLINK) .dumpOutput() .addArguments( @@ -82,26 +99,17 @@ public final class NoMPathRuntimeTest { "--output", jlinkOutputDir.toString(), "--strip-debug", "--no-header-files", - "--no-man-pages"); - - jlink.addArguments("--add-modules", appDesc.moduleName(), - "--module-path", Path.of(cmd.getArgumentValue("--module-path")) - .resolve("hello.jar").toString()); - - jlink.execute(); - - if (TKit.isOSX()) { - // MacBundle requires valid bundle - if (Files.exists(workDir.resolve("Contents/Home"))) { - Files.createFile(workDir.resolve("Contents/Info.plist")); - Files.createDirectories(workDir.resolve("Contents/MacOS")); - } - } + "--no-man-pages" + ) + .addArguments( + "--add-modules", appDesc.moduleName(), + "--module-path", Path.of(cmd.getArgumentValue("--module-path")).resolve("hello.jar").toString() + ).execute(); // non-modular jar in current dir caused error whe no module-path given cmd.removeArgumentWithValue("--module-path"); - cmd.setArgumentValue("--runtime-image", workDir.resolve(runtimeSubdir)); + cmd.setArgumentValue("--runtime-image", workDir); Path junkJar = null; try { // create a non-modular jar in the current directory @@ -118,24 +126,26 @@ public final class NoMPathRuntimeTest { } @Parameters - public static Collection data() { + public static Collection data() { - final List paths = new ArrayList<>(); - paths.add(new String[] { "", "" }); + final List testCases = new ArrayList<>(); + testCases.add(RuntimeType.IMAGE); if (TKit.isOSX()) { // On OSX jpackage should accept both runtime root and runtime home // directories. - paths.add(new String[] { "Contents/Home", "" }); + testCases.add(RuntimeType.MAC_BUNDLE); } - List data = new ArrayList<>(); - for (var pathCfg : paths) { - data.add(new Object[] { pathCfg[0], pathCfg[1] }); - } - - return data; + return testCases.stream().map(v -> { + return new Object[] {v}; + }).toList(); } - private final Path jlinkOutputSubdir; - private final Path runtimeSubdir; + public enum RuntimeType { + IMAGE, + MAC_BUNDLE, + ; + } + + private final RuntimeType runtimeType; } diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index 48fad14bc83..c4d2176632e 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -115,6 +115,26 @@ public class RuntimePackageTest { .run(Action.CREATE_AND_UNPACK); } + @Test + @Parameter(value = {"foo"}) + @Parameter(value = {""}) + @Parameter(value = {"17.21.3+foo"}) + public static void testInvalidReleaseFileVersion(String version) { + testReleaseFileVersion(version, false, Optional.empty()); + } + + @Test + @Parameter(value = {"17.21.3-ea", "LINUX_DEB"}, ifOS = LINUX) + public static void testValidReleaseFileVersion(String version, PackageType... packageTypes) { + testReleaseFileVersion(version, true, Optional.empty(), packageTypes); + } + + @Test + @Parameter(value = {"17.21.3-ea", "17.21.3", "LINUX_RPM"}, ifOS = LINUX) + public static void testValidReleaseFileVersion(String version, String normalizedVersion, PackageType... packageTypes) { + testReleaseFileVersion(version, true, Optional.of(normalizedVersion), packageTypes); + } + @Test // 27 @Parameter(value = {"27"}, ifOS = {LINUX, MACOS}) @@ -126,10 +146,8 @@ public class RuntimePackageTest { @Parameter(value = {"27.1.2.3"}, ifOS = {LINUX, WINDOWS}) // 27.1.2.3.4 @Parameter(value = {"27.1.2.3.4"}, ifOS = LINUX) - // 17.21.3-ea - @Parameter(value = {"17.21.3-ea"}, ifOS = LINUX) - public static void testReleaseFileVersion(String version) { - testReleaseFileVersion(version, version); + public static void testValidReleaseFileVersion(String version) { + testReleaseFileVersion(version, true, Optional.empty()); } @Test @@ -145,9 +163,18 @@ public class RuntimePackageTest { // 17.21.3-ea @Parameter(value = {"17.21.3-ea", "17.21.3"}, ifOS = MACOS) @Parameter(value = {"17.21.3-ea", "17.21.3.0"}, ifOS = WINDOWS) - public static void testReleaseFileVersion(String version, String normalizedVersion) { - new PackageTest() - .addInitializer(cmd -> { + public static void testValidReleaseFileVersion(String version, String normalizedVersion) { + testReleaseFileVersion(version, true, Optional.of(normalizedVersion)); + } + + private static void testReleaseFileVersion(String version, + boolean validReleaseFileVersion, Optional normalizedVersion, + PackageType... packageTypes) { + var test = new PackageTest(); + if (packageTypes.length != 0) { + test.forTypes(packageTypes); + } + test.addInitializer(cmd -> { // Remove --input parameter from jpackage command line as we don't // create input directory in the test and jpackage fails // if --input references non existant directory. @@ -167,15 +194,17 @@ public class RuntimePackageTest { props.store(writer, null); } - // Validate output - cmd.validateOutput(JPackageStringBundle.MAIN - .cannedFormattedString("message.release-version", - version, runtimeImage.toString())); - // Normalization message is only printed if we did normalization. - if (!version.equals(normalizedVersion)) { + // Validate output only if release version is valid for release file + if (validReleaseFileVersion) { cmd.validateOutput(JPackageStringBundle.MAIN - .cannedFormattedString("message.version-normalized", - normalizedVersion, version)); + .cannedFormattedString("message.release-version", + version, runtimeImage.toString())); + // Normalization message is only printed if we did normalization. + normalizedVersion.ifPresent(nv -> { + cmd.validateOutput(JPackageStringBundle.MAIN + .cannedFormattedString("message.version-normalized", + nv, version)); + }); } }) // Just create package. It is enough to verify version in bundle name.