From 57082bcd9f2d14dfee891fa3c6eb8204ae2fdb89 Mon Sep 17 00:00:00 2001 From: alexander_matveev Date: Thu, 25 Sep 2025 19:26:05 -0700 Subject: [PATCH 1/5] 8362598: [macos] Add tests for custom info plist files --- .../jpackage/internal/util/PListWriter.java | 4 +- .../helpers/jdk/jpackage/test/MacHelper.java | 4 + .../jpackage/macosx/CustomInfoPListTest.java | 114 ++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java index 381faee3802e3..dfb7371a9dd8e 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java @@ -90,11 +90,13 @@ public static void writeArray(XMLStreamWriter xml, XmlConsumer content) public static void writePList(XMLStreamWriter xml, XmlConsumer content) throws XMLStreamException, IOException { - xml.writeDTD("plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"https://www.apple.com/DTDs/PropertyList-1.0.dtd\""); + xml.writeStartDocument(); + xml.writeDTD(""); xml.writeStartElement("plist"); xml.writeAttribute("version", "1.0"); content.accept(xml); xml.writeEndElement(); + xml.writeEndDocument(); } public static void writeKey(XMLStreamWriter xml, String key) 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 f4feb3e2fde1f..9ed1e45b15cf6 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -128,6 +128,10 @@ public static PListReader readPListFromAppImage(Path appImage) { return readPList(appImage.resolve("Contents/Info.plist")); } + public static PListReader readPListFromEmbeddedRuntime(Path appImage) { + return readPList(appImage.resolve("Contents/runtime/Contents/Info.plist")); + } + public static PListReader readPList(Path path) { TKit.assertReadableFileExists(path); return ThrowingSupplier.toSupplier(() -> readPList(Files.readAllLines( diff --git a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java new file mode 100644 index 0000000000000..1d869ea33c840 --- /dev/null +++ b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java @@ -0,0 +1,114 @@ +/* + * 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. + */ + + /** + * Test --resource-dir with custom "Info.plist" for the top-level bundle + * and "Runtime-Info.plist" for the embedded runtime bundle + */ + +/* + * @test + * @summary jpackage with --type image --resource-dir "Info.plist" and "Runtime-Info.plist" + * @library /test/jdk/tools/jpackage/helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @build CustomInfoPListTest + * @requires (os.family == "mac") + * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=CustomInfoPListTest + */ + +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.MacHelper; +import jdk.jpackage.test.JPackageCommand; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import javax.xml.stream.XMLOutputFactory; + +import jdk.jpackage.test.Annotations.Test; + +import static jdk.jpackage.internal.util.PListWriter.writePList; +import static jdk.jpackage.internal.util.PListWriter.writeDict; +import static jdk.jpackage.internal.util.PListWriter.writeString; +import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; +import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; + +public class CustomInfoPListTest { + + private static final String BUNDLE_NAME_APP = "CustomAppName"; + private static final String BUNDLE_NAME_RUNTIME = "CustomRuntimeName"; + + // We do not need full Info.plist for testing + private static String getInfoPListXML(String bundleName) { + return toSupplier(() -> { + var buf = new StringWriter(); + var xml = XMLOutputFactory.newInstance().createXMLStreamWriter(buf); + writePList(xml, toXmlConsumer(() -> { + writeDict(xml, toXmlConsumer(() -> { + writeString(xml, "CFBundleName", bundleName); + writeString(xml, "CFBundleIdentifier", "CustomInfoPListTest"); + writeString(xml, "CFBundleVersion", "1.0"); + })); + })); + xml.flush(); + xml.close(); + return buf.toString(); + }).get(); + } + + private static String getResourceDirWithCustomInfoPList() { + final Path resources = TKit.createTempDirectory("resources"); + try { + Files.writeString(resources.resolve("Info.plist"), + getInfoPListXML(BUNDLE_NAME_APP)); + Files.writeString(resources.resolve("Runtime-Info.plist"), + getInfoPListXML(BUNDLE_NAME_RUNTIME)); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + + return resources.toString(); + } + + @Test + public void test() { + JPackageCommand cmd = JPackageCommand.helloAppImage() + .addArguments("--resource-dir", + getResourceDirWithCustomInfoPList()); + + cmd.executeAndAssertHelloAppImageCreated(); + + var appPList = MacHelper.readPListFromAppImage(cmd.outputBundle()); + TKit.assertEquals(BUNDLE_NAME_APP, appPList.queryValue("CFBundleName"), String.format( + "Check value of %s plist key", "CFBundleName")); + + var runtimePList = MacHelper.readPListFromEmbeddedRuntime(cmd.outputBundle()); + TKit.assertEquals(BUNDLE_NAME_RUNTIME, runtimePList.queryValue("CFBundleName"), String.format( + "Check value of %s plist key", "CFBundleName")); + } +} From 50011dadf11079ca1b7a78d63f5b9d8c49a899d6 Mon Sep 17 00:00:00 2001 From: alexander_matveev Date: Thu, 25 Sep 2025 19:51:37 -0700 Subject: [PATCH 2/5] 8362598: [macos] Add tests for custom info plist files [v2] --- .../helpers/jdk/jpackage/test/MacHelper.java | 23 +++++++++ .../jpackage/macosx/CustomInfoPListTest.java | 50 ++++++++++++++++--- .../SigningRuntimeImagePackageTest.java | 25 +--------- 3 files changed, 67 insertions(+), 31 deletions(-) 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 9ed1e45b15cf6..72100e194477f 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -175,6 +175,29 @@ public static boolean appImageSigned(JPackageCommand cmd) { return (cmd.hasArgument("--mac-signing-key-user-name") || cmd.hasArgument("--mac-app-image-sign-identity")); } + public static Path createInputRuntimeImage() throws IOException { + + final Path runtimeImageDir; + + if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { + runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; + } else { + runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); + + new Executor().setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", runtimeImageDir.toString(), + "--add-modules", "java.desktop", + "--strip-debug", + "--no-header-files", + "--no-man-pages") + .execute(); + } + + return runtimeImageDir; + } + static PackageHandlers createDmgPackageHandlers() { return new PackageHandlers(MacHelper::installDmg, MacHelper::uninstallDmg, MacHelper::unpackDmg); } diff --git a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java index 1d869ea33c840..ac39cece03a32 100644 --- a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java +++ b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java @@ -41,12 +41,14 @@ import jdk.jpackage.test.TKit; import jdk.jpackage.test.MacHelper; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.PackageType; import java.io.IOException; import java.io.StringWriter; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.Executor; import javax.xml.stream.XMLOutputFactory; @@ -61,6 +63,7 @@ public class CustomInfoPListTest { private static final String BUNDLE_NAME_APP = "CustomAppName"; + private static final String BUNDLE_NAME_EMBEDDED_RUNTIME = "CustomEmbeddedRuntimeName"; private static final String BUNDLE_NAME_RUNTIME = "CustomRuntimeName"; // We do not need full Info.plist for testing @@ -81,13 +84,16 @@ private static String getInfoPListXML(String bundleName) { }).get(); } - private static String getResourceDirWithCustomInfoPList() { + private static String getResourceDirWithCustomInfoPList( + String bundleName, boolean includeRuntimePList) { final Path resources = TKit.createTempDirectory("resources"); try { Files.writeString(resources.resolve("Info.plist"), - getInfoPListXML(BUNDLE_NAME_APP)); - Files.writeString(resources.resolve("Runtime-Info.plist"), - getInfoPListXML(BUNDLE_NAME_RUNTIME)); + getInfoPListXML(bundleName)); + if (includeRuntimePList) { + Files.writeString(resources.resolve("Runtime-Info.plist"), + getInfoPListXML(BUNDLE_NAME_EMBEDDED_RUNTIME)); + } } catch (IOException ex) { throw new UncheckedIOException(ex); } @@ -96,10 +102,10 @@ private static String getResourceDirWithCustomInfoPList() { } @Test - public void test() { + public void testApp() { JPackageCommand cmd = JPackageCommand.helloAppImage() .addArguments("--resource-dir", - getResourceDirWithCustomInfoPList()); + getResourceDirWithCustomInfoPList(BUNDLE_NAME_APP, true)); cmd.executeAndAssertHelloAppImageCreated(); @@ -108,7 +114,37 @@ public void test() { "Check value of %s plist key", "CFBundleName")); var runtimePList = MacHelper.readPListFromEmbeddedRuntime(cmd.outputBundle()); - TKit.assertEquals(BUNDLE_NAME_RUNTIME, runtimePList.queryValue("CFBundleName"), String.format( + TKit.assertEquals(BUNDLE_NAME_EMBEDDED_RUNTIME, runtimePList.queryValue("CFBundleName"), String.format( "Check value of %s plist key", "CFBundleName")); } + + @Test + public void testRuntime() throws IOException { + final var runtimeImage = MacHelper.createInputRuntimeImage(); + + final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); + + final var unpackadeRuntimeBundleDir = runtimeBundleWorkDir.resolve("unpacked"); + + var cmd = new JPackageCommand() + .useToolProvider(true) + .ignoreDefaultRuntime(true) + .dumpOutput(true) + .setPackageType(PackageType.MAC_DMG) + .setArgumentValue("--name", "foo") + .addArguments("--runtime-image", runtimeImage) + .addArguments("--resource-dir", + getResourceDirWithCustomInfoPList(BUNDLE_NAME_RUNTIME, false)) + .addArguments("--dest", runtimeBundleWorkDir); + + cmd.execute(); + + MacHelper.withExplodedDmg(cmd, dmgImage -> { + if (dmgImage.endsWith(cmd.name() + ".jdk")) { + var runtimePList = MacHelper.readPListFromAppImage(dmgImage); + TKit.assertEquals(BUNDLE_NAME_RUNTIME, runtimePList.queryValue("CFBundleName"), + String.format("Check value of %s plist key", "CFBundleName")); + } + }); + } } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index 8032a4532e92a..f3a595f13059a 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -94,32 +94,9 @@ private static JPackageCommand addSignOptions(JPackageCommand cmd, int certIndex return cmd; } - private static Path createInputRuntimeImage() throws IOException { - - final Path runtimeImageDir; - - if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { - runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; - } else { - runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); - - new Executor().setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments( - "--output", runtimeImageDir.toString(), - "--add-modules", "java.desktop", - "--strip-debug", - "--no-header-files", - "--no-man-pages") - .execute(); - } - - return runtimeImageDir; - } - private static Path createInputRuntimeBundle(int certIndex) throws IOException { - final var runtimeImage = createInputRuntimeImage(); + final var runtimeImage = MacHelper.createInputRuntimeImage(); final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); From f1e991300211c437646e2920a25a795873937339 Mon Sep 17 00:00:00 2001 From: alexander_matveev Date: Wed, 1 Oct 2025 19:21:55 -0700 Subject: [PATCH 3/5] 8362598: [macos] Add tests for custom info plist files [v3] --- .../jpackage/internal/util/PListWriter.java | 2 - .../jdk/jpackage/test/JPackageCommand.java | 23 +++ .../helpers/jdk/jpackage/test/MacHelper.java | 23 --- .../jpackage/macosx/CustomInfoPListTest.java | 174 +++++++++++++----- .../SigningRuntimeImagePackageTest.java | 6 +- .../jpackage/share/RuntimePackageTest.java | 27 +-- 6 files changed, 160 insertions(+), 95 deletions(-) diff --git a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java index dfb7371a9dd8e..c23c292b8a8de 100644 --- a/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java +++ b/src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/util/PListWriter.java @@ -90,13 +90,11 @@ public static void writeArray(XMLStreamWriter xml, XmlConsumer content) public static void writePList(XMLStreamWriter xml, XmlConsumer content) throws XMLStreamException, IOException { - xml.writeStartDocument(); xml.writeDTD(""); xml.writeStartElement("plist"); xml.writeAttribute("version", "1.0"); content.accept(xml); xml.writeEndElement(); - xml.writeEndDocument(); } public static void writeKey(XMLStreamWriter xml, String key) 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 49f565e27e94f..3fbd5fd9ac0ed 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JPackageCommand.java @@ -363,6 +363,29 @@ public static JPackageCommand helloAppImage(JavaAppDesc javaAppDesc) { return cmd; } + public static Path createInputRuntimeImage() throws IOException { + + final Path runtimeImageDir; + + if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { + runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; + } else { + runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); + + new Executor().setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", runtimeImageDir.toString(), + "--add-modules", "java.desktop", + "--strip-debug", + "--no-header-files", + "--no-man-pages") + .execute(); + } + + return runtimeImageDir; + } + public JPackageCommand setPackageType(PackageType type) { verifyMutable(); type.applyTo(this); 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 72100e194477f..9ed1e45b15cf6 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MacHelper.java @@ -175,29 +175,6 @@ public static boolean appImageSigned(JPackageCommand cmd) { return (cmd.hasArgument("--mac-signing-key-user-name") || cmd.hasArgument("--mac-app-image-sign-identity")); } - public static Path createInputRuntimeImage() throws IOException { - - final Path runtimeImageDir; - - if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { - runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; - } else { - runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); - - new Executor().setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments( - "--output", runtimeImageDir.toString(), - "--add-modules", "java.desktop", - "--strip-debug", - "--no-header-files", - "--no-man-pages") - .execute(); - } - - return runtimeImageDir; - } - static PackageHandlers createDmgPackageHandlers() { return new PackageHandlers(MacHelper::installDmg, MacHelper::uninstallDmg, MacHelper::unpackDmg); } diff --git a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java index ac39cece03a32..8f5ba41485d35 100644 --- a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java +++ b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java @@ -34,6 +34,7 @@ * @build jdk.jpackage.test.* * @build CustomInfoPListTest * @requires (os.family == "mac") + * @requires (jpackage.test.SQETest == null) * @run main/othervm/timeout=1440 -Xmx512m jdk.jpackage.test.Main * --jpt-run=CustomInfoPListTest */ @@ -41,6 +42,7 @@ import jdk.jpackage.test.TKit; import jdk.jpackage.test.MacHelper; import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JPackageStringBundle; import jdk.jpackage.test.PackageType; import java.io.IOException; @@ -48,84 +50,170 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; import java.util.concurrent.Executor; import javax.xml.stream.XMLOutputFactory; import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameter; + +import jdk.jpackage.internal.util.XmlUtils; +import jdk.jpackage.internal.util.PListReader; import static jdk.jpackage.internal.util.PListWriter.writePList; import static jdk.jpackage.internal.util.PListWriter.writeDict; import static jdk.jpackage.internal.util.PListWriter.writeString; import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer; -import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier; public class CustomInfoPListTest { - private static final String BUNDLE_NAME_APP = "CustomAppName"; - private static final String BUNDLE_NAME_EMBEDDED_RUNTIME = "CustomEmbeddedRuntimeName"; - private static final String BUNDLE_NAME_RUNTIME = "CustomRuntimeName"; - - // We do not need full Info.plist for testing - private static String getInfoPListXML(String bundleName) { - return toSupplier(() -> { - var buf = new StringWriter(); - var xml = XMLOutputFactory.newInstance().createXMLStreamWriter(buf); - writePList(xml, toXmlConsumer(() -> { - writeDict(xml, toXmlConsumer(() -> { - writeString(xml, "CFBundleName", bundleName); - writeString(xml, "CFBundleIdentifier", "CustomInfoPListTest"); - writeString(xml, "CFBundleVersion", "1.0"); - })); - })); - xml.flush(); - xml.close(); - return buf.toString(); - }).get(); + private static final String APP_PLIST_KEY = "CustomAppPList"; + private static final String EMBEDDED_RUNTIME_PLIST_KEY = "CustomEmbeddedRuntimePList"; + private static final String RUNTIME_PLIST_KEY = "CustomRuntimePList"; + + private static final Map appKeyValue = new HashMap<>(); + private static final Map embeddedRuntimeKeyValue = new HashMap<>(); + private static final Map runtimeKeyValue = new HashMap<>(); + + static { + appKeyValue.put("CFBundleExecutable", "AppCustomInfoPListTest"); + appKeyValue.put("CFBundleIconFile", "AppCustomInfoPListTest.icns"); + appKeyValue.put("CFBundleIdentifier", "Hello"); + appKeyValue.put("CFBundleName", "AppCustomInfoPListTest"); + appKeyValue.put("CFBundleShortVersionString", "1.0"); + appKeyValue.put("LSApplicationCategoryType", "public.app-category.utilities"); + appKeyValue.put("CFBundleVersion", "1.0"); + appKeyValue.put("NSHumanReadableCopyright", JPackageStringBundle.MAIN.cannedFormattedString( + "param.copyright.default", new Date()).getValue()); + appKeyValue.put("UTTypeIdentifier", "Hello.foo"); + appKeyValue.put("UTTypeDescription", "bar"); + + embeddedRuntimeKeyValue.put("CFBundleIdentifier", "Hello"); + embeddedRuntimeKeyValue.put("CFBundleName", "AppCustomInfoPListTest"); + embeddedRuntimeKeyValue.put("CFBundleShortVersionString", "1.0"); + embeddedRuntimeKeyValue.put("CFBundleVersion", "1.0"); + + runtimeKeyValue.put("CFBundleIdentifier", "foo"); + runtimeKeyValue.put("CFBundleName", "foo"); + runtimeKeyValue.put("CFBundleShortVersionString", "1.0"); + runtimeKeyValue.put("CFBundleVersion", "1.0"); } - private static String getResourceDirWithCustomInfoPList( - String bundleName, boolean includeRuntimePList) { - final Path resources = TKit.createTempDirectory("resources"); + // We do not need full and valid Info.plist for testing + private static void createInfoPListFile(String key, Path plistFile) { try { - Files.writeString(resources.resolve("Info.plist"), - getInfoPListXML(bundleName)); - if (includeRuntimePList) { - Files.writeString(resources.resolve("Runtime-Info.plist"), - getInfoPListXML(BUNDLE_NAME_EMBEDDED_RUNTIME)); - } + XmlUtils.createXml(plistFile, xml -> { + writePList(xml, toXmlConsumer(() -> { + writeDict(xml, toXmlConsumer(() -> { + writeString(xml, "CustomInfoPListTestKey", key); + if (key.equals(APP_PLIST_KEY)) { + // Application + writeString(xml, "CFBundleExecutable", "DEPLOY_LAUNCHER_NAME"); + writeString(xml, "CFBundleIconFile", "DEPLOY_ICON_FILE"); + writeString(xml, "CFBundleIdentifier", "DEPLOY_BUNDLE_IDENTIFIER"); + writeString(xml, "CFBundleName", "DEPLOY_BUNDLE_NAME"); + writeString(xml, "CFBundleShortVersionString", "DEPLOY_BUNDLE_SHORT_VERSION"); + writeString(xml, "LSApplicationCategoryType", "DEPLOY_APP_CATEGORY"); + writeString(xml, "CFBundleVersion", "DEPLOY_BUNDLE_CFBUNDLE_VERSION"); + writeString(xml, "NSHumanReadableCopyright", "DEPLOY_BUNDLE_COPYRIGHT"); + writeString(xml, "CustomInfoPListFA", "DEPLOY_FILE_ASSOCIATIONS"); + } else if (key.equals(EMBEDDED_RUNTIME_PLIST_KEY) || key.equals(RUNTIME_PLIST_KEY)) { + // Embedded runtime and runtime + writeString(xml, "CFBundleIdentifier", "CF_BUNDLE_IDENTIFIER"); + writeString(xml, "CFBundleName", "CF_BUNDLE_NAME"); + writeString(xml, "CFBundleShortVersionString", "CF_BUNDLE_SHORT_VERSION_STRING"); + writeString(xml, "CFBundleVersion", "CF_BUNDLE_VERSION"); + } + })); + })); + }); } catch (IOException ex) { throw new UncheckedIOException(ex); } + } + private static String getResourceDirWithCustomInfoPList( + String key, boolean includeMainPList, boolean includeRuntimePList) { + final Path resources = TKit.createTempDirectory("resources"); + if (includeMainPList) { + createInfoPListFile(key, resources.resolve("Info.plist")); + } + if (includeRuntimePList) { + createInfoPListFile(EMBEDDED_RUNTIME_PLIST_KEY, resources.resolve("Runtime-Info.plist")); + } return resources.toString(); } + private static void validateInfoPListFileKey(PListReader plistFile, Optional key) { + if (key.isPresent()) { + TKit.assertEquals(key.get(), plistFile.queryValue("CustomInfoPListTestKey"), String.format( + "Check value of %s plist key", "CustomInfoPListTestKey")); + } else { + boolean exceptionThrown = false; + try { + plistFile.queryValue("CustomInfoPListTestKey"); + } catch (NoSuchElementException ex) { + exceptionThrown = true; + } + TKit.assertTrue(exceptionThrown, "NoSuchElementException exception not thrown"); + } + } + + private static void validateInfoPList(PListReader plistFile, Map values) { + values.forEach((key, value) -> { + TKit.assertEquals(value, plistFile.queryValue(key), String.format( + "Check value of %s plist key", key)); + }); + } + @Test - public void testApp() { + @Parameter({"TRUE", "FALSE"}) + @Parameter({"FALSE", "TRUE"}) + @Parameter({"TRUE", "TRUE"}) + public void testApp(boolean includeMainPList, boolean includeRuntimePList) { + final Path propFile = TKit.workDir().resolve("fa.properties"); + TKit.createPropertiesFile(propFile, Map.of( + "mime-type", "application/x-jpackage-foo", + "extension", "foo", + "description", "bar" + )); + JPackageCommand cmd = JPackageCommand.helloAppImage() .addArguments("--resource-dir", - getResourceDirWithCustomInfoPList(BUNDLE_NAME_APP, true)); + getResourceDirWithCustomInfoPList(APP_PLIST_KEY, + includeMainPList, includeRuntimePList)) + .addArguments("--file-associations", propFile); cmd.executeAndAssertHelloAppImageCreated(); var appPList = MacHelper.readPListFromAppImage(cmd.outputBundle()); - TKit.assertEquals(BUNDLE_NAME_APP, appPList.queryValue("CFBundleName"), String.format( - "Check value of %s plist key", "CFBundleName")); + if (includeMainPList) { + validateInfoPListFileKey(appPList, Optional.of(APP_PLIST_KEY)); + validateInfoPList(appPList, appKeyValue); + } else { + validateInfoPListFileKey(appPList, Optional.empty()); + } var runtimePList = MacHelper.readPListFromEmbeddedRuntime(cmd.outputBundle()); - TKit.assertEquals(BUNDLE_NAME_EMBEDDED_RUNTIME, runtimePList.queryValue("CFBundleName"), String.format( - "Check value of %s plist key", "CFBundleName")); + if (includeRuntimePList) { + validateInfoPListFileKey(runtimePList, Optional.of(EMBEDDED_RUNTIME_PLIST_KEY)); + validateInfoPList(runtimePList, embeddedRuntimeKeyValue); + } else { + validateInfoPListFileKey(runtimePList, Optional.empty()); + } } @Test public void testRuntime() throws IOException { - final var runtimeImage = MacHelper.createInputRuntimeImage(); + final var runtimeImage = JPackageCommand.createInputRuntimeImage(); final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); - final var unpackadeRuntimeBundleDir = runtimeBundleWorkDir.resolve("unpacked"); - var cmd = new JPackageCommand() .useToolProvider(true) .ignoreDefaultRuntime(true) @@ -134,16 +222,16 @@ public void testRuntime() throws IOException { .setArgumentValue("--name", "foo") .addArguments("--runtime-image", runtimeImage) .addArguments("--resource-dir", - getResourceDirWithCustomInfoPList(BUNDLE_NAME_RUNTIME, false)) + getResourceDirWithCustomInfoPList(RUNTIME_PLIST_KEY, true, false)) .addArguments("--dest", runtimeBundleWorkDir); cmd.execute(); MacHelper.withExplodedDmg(cmd, dmgImage -> { - if (dmgImage.endsWith(cmd.name() + ".jdk")) { + if (dmgImage.endsWith(cmd.appInstallationDirectory().getFileName())) { var runtimePList = MacHelper.readPListFromAppImage(dmgImage); - TKit.assertEquals(BUNDLE_NAME_RUNTIME, runtimePList.queryValue("CFBundleName"), - String.format("Check value of %s plist key", "CFBundleName")); + validateInfoPListFileKey(runtimePList, Optional.of(RUNTIME_PLIST_KEY)); + validateInfoPList(runtimePList, runtimeKeyValue); } }); } diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index f3a595f13059a..c0a289854323b 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -25,6 +25,8 @@ import java.nio.file.Path; import java.util.function.Predicate; import java.util.stream.Stream; + +import base.SigningBase; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; @@ -96,7 +98,7 @@ private static JPackageCommand addSignOptions(JPackageCommand cmd, int certIndex private static Path createInputRuntimeBundle(int certIndex) throws IOException { - final var runtimeImage = MacHelper.createInputRuntimeImage(); + final var runtimeImage = JPackageCommand.createInputRuntimeImage(); final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); @@ -155,7 +157,7 @@ public static void test(boolean useJDKBundle, if (useJDKBundle) { inputRuntime[0] = createInputRuntimeBundle(jdkBundleCert.value()); } else { - inputRuntime[0] = createInputRuntimeImage(); + inputRuntime[0] = JPackageCommand.createInputRuntimeImage(); } }) .addInitializer(cmd -> { diff --git a/test/jdk/tools/jpackage/share/RuntimePackageTest.java b/test/jdk/tools/jpackage/share/RuntimePackageTest.java index f66f774b227ac..c6a266261689c 100644 --- a/test/jdk/tools/jpackage/share/RuntimePackageTest.java +++ b/test/jdk/tools/jpackage/share/RuntimePackageTest.java @@ -114,7 +114,7 @@ public static void testName() { } private static PackageTest init() { - return init(RuntimePackageTest::createInputRuntimeImage); + return init(JPackageCommand::createInputRuntimeImage); } private static PackageTest init(ThrowingSupplier createRuntime) { @@ -173,32 +173,9 @@ private static Path inputRuntimeDir(JPackageCommand cmd) { return path; } - private static Path createInputRuntimeImage() throws IOException { - - final Path runtimeImageDir; - - if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) { - runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE; - } else { - runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data"); - - new Executor().setToolProvider(JavaTool.JLINK) - .dumpOutput() - .addArguments( - "--output", runtimeImageDir.toString(), - "--add-modules", "java.desktop", - "--strip-debug", - "--no-header-files", - "--no-man-pages") - .execute(); - } - - return runtimeImageDir; - } - private static Path createInputRuntimeBundle() throws IOException { - final var runtimeImage = createInputRuntimeImage(); + final var runtimeImage = JPackageCommand.createInputRuntimeImage(); final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle"); From af103a66b9399653ddda4bec06b2872d78226f16 Mon Sep 17 00:00:00 2001 From: alexander_matveev Date: Fri, 3 Oct 2025 13:15:32 -0700 Subject: [PATCH 4/5] 8362598: [macos] Add tests for custom info plist files [v4] --- .../tools/jpackage/macosx/SigningRuntimeImagePackageTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java index c0a289854323b..ec9fc67f7252d 100644 --- a/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java +++ b/test/jdk/tools/jpackage/macosx/SigningRuntimeImagePackageTest.java @@ -26,7 +26,6 @@ import java.util.function.Predicate; import java.util.stream.Stream; -import base.SigningBase; import jdk.jpackage.test.Annotations.Parameter; import jdk.jpackage.test.Annotations.Test; import jdk.jpackage.test.Executor; From 01e41f1fae0168b3530c725e8d899be1830f0b6d Mon Sep 17 00:00:00 2001 From: alexander_matveev Date: Fri, 3 Oct 2025 17:55:30 -0700 Subject: [PATCH 5/5] 8362598: [macos] Add tests for custom info plist files [v5] --- .../jpackage/macosx/CustomInfoPListTest.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java index 8f5ba41485d35..c617b4150dee4 100644 --- a/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java +++ b/test/jdk/tools/jpackage/macosx/CustomInfoPListTest.java @@ -50,8 +50,10 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; @@ -80,6 +82,8 @@ public class CustomInfoPListTest { private static final Map embeddedRuntimeKeyValue = new HashMap<>(); private static final Map runtimeKeyValue = new HashMap<>(); + private static final List faKeysAndValues = new ArrayList<>(); + static { appKeyValue.put("CFBundleExecutable", "AppCustomInfoPListTest"); appKeyValue.put("CFBundleIconFile", "AppCustomInfoPListTest.icns"); @@ -90,8 +94,6 @@ public class CustomInfoPListTest { appKeyValue.put("CFBundleVersion", "1.0"); appKeyValue.put("NSHumanReadableCopyright", JPackageStringBundle.MAIN.cannedFormattedString( "param.copyright.default", new Date()).getValue()); - appKeyValue.put("UTTypeIdentifier", "Hello.foo"); - appKeyValue.put("UTTypeDescription", "bar"); embeddedRuntimeKeyValue.put("CFBundleIdentifier", "Hello"); embeddedRuntimeKeyValue.put("CFBundleName", "AppCustomInfoPListTest"); @@ -102,6 +104,13 @@ public class CustomInfoPListTest { runtimeKeyValue.put("CFBundleName", "foo"); runtimeKeyValue.put("CFBundleShortVersionString", "1.0"); runtimeKeyValue.put("CFBundleVersion", "1.0"); + + faKeysAndValues.add("CFBundleDocumentTypes"); + faKeysAndValues.add("LSItemContentTypes"); + faKeysAndValues.add("Hello.foo"); + faKeysAndValues.add("UTTypeDescription"); + faKeysAndValues.add("CFBundleTypeName"); + faKeysAndValues.add("bar"); } // We do not need full and valid Info.plist for testing @@ -171,6 +180,15 @@ private static void validateInfoPList(PListReader plistFile, Map }); } + // For FA check that main keys and values are present + private static void validateInfoPListFA(PListReader plistFile, List values) { + String faXml = plistFile.queryValue("CustomInfoPListFA"); + values.forEach(value -> { + TKit.assertTrue(faXml.contains(value), String.format( + "Check FA key/value is present [%s]", value)); + }); + } + @Test @Parameter({"TRUE", "FALSE"}) @Parameter({"FALSE", "TRUE"}) @@ -195,6 +213,7 @@ public void testApp(boolean includeMainPList, boolean includeRuntimePList) { if (includeMainPList) { validateInfoPListFileKey(appPList, Optional.of(APP_PLIST_KEY)); validateInfoPList(appPList, appKeyValue); + validateInfoPListFA(appPList, faKeysAndValues); } else { validateInfoPListFileKey(appPList, Optional.empty()); }