| /* | |
| * 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.Files; | |
| import java.nio.file.Path; | |
| import java.util.UUID; | |
| import java.util.function.Supplier; | |
| import javax.xml.transform.Result; | |
| import javax.xml.transform.Source; | |
| import javax.xml.transform.Transformer; | |
| import javax.xml.transform.TransformerFactory; | |
| import javax.xml.transform.dom.DOMSource; | |
| import javax.xml.transform.stream.StreamResult; | |
| import javax.xml.xpath.XPath; | |
| import javax.xml.xpath.XPathConstants; | |
| import javax.xml.xpath.XPathFactory; | |
| import jdk.jpackage.internal.util.XmlUtils; | |
| import jdk.jpackage.test.Annotations.Test; | |
| import jdk.jpackage.test.Executor; | |
| import jdk.jpackage.test.PackageTest; | |
| import jdk.jpackage.test.PackageType; | |
| import jdk.jpackage.test.RunnablePackageTest.Action; | |
| import jdk.jpackage.test.TKit; | |
| import org.w3c.dom.Document; | |
| import org.w3c.dom.NodeList; | |
| /** | |
| * Test --app-version parameter properly supports long version numbers, i.e. | |
| * version numbers with more than three components. | |
| * Output of the test should be WinLongVersionTest-1.0.exe, | |
| * WinLongVersionTest-2.0.0.1.exe, and WinLongVersionTest-2.0.0.2.exe installers. | |
| * The output installers should provide the same functionality as | |
| * the default installer (see description of the default installer in | |
| * SimplePackageTest.java) but have the same product code and different | |
| * versions. | |
| * Test scenario: | |
| * - Run WinLongVersionTest-2.0.0.2.exe; | |
| * - Run WinLongVersionTest-1.0.exe; package installed with | |
| * WinLongVersionTest-2.0.0.2.exe command must remain installed | |
| * - Run WinLongVersionTest-2.0.0.1.exe; packages installed with | |
| * WinLongVersionTest-2.0.0.2.exe and WinLongVersionTest-1.0.exe installers | |
| * must be automatically uninstalled, only WinLongVersionTest-2.0.0.1 package | |
| * must remain installed | |
| * - Uninstall WinLongVersionTest-2.0.0.2; all packages installed in the test | |
| * scenario must be uninstalled | |
| */ | |
| /* | |
| * @test | |
| * @summary jpackage with long version number | |
| * @library /test/jdk/tools/jpackage/helpers | |
| * @key jpackagePlatformPackage | |
| * @requires (jpackage.test.SQETest != null) | |
| * @build jdk.jpackage.test.* | |
| * @requires (os.family == "windows") | |
| * @compile -Xlint:all -Werror WinLongVersionTest.java | |
| * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main | |
| * --jpt-run=WinLongVersionTest.test | |
| */ | |
| /* | |
| * @test | |
| * @summary jpackage with long version number | |
| * @library /test/jdk/tools/jpackage/helpers | |
| * @key jpackagePlatformPackage | |
| * @requires (jpackage.test.SQETest == null) | |
| * @build jdk.jpackage.test.* | |
| * @requires (os.family == "windows") | |
| * @compile -Xlint:all -Werror WinLongVersionTest.java | |
| * @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main | |
| * --jpt-run=WinLongVersionTest | |
| */ | |
| public class WinLongVersionTest { | |
| @Test | |
| public static void test() throws IOException { | |
| Supplier<PackageTest> init = () -> { | |
| final UUID upgradeCode = UUID.fromString( | |
| "65099D7A-D5B1-4E5B-85B1-717F0DE4D5D5"); | |
| return new PackageTest() | |
| .forTypes(PackageType.WINDOWS) | |
| .configureHelloApp() | |
| .addInitializer(cmd -> cmd.addArguments("--win-upgrade-uuid", | |
| upgradeCode.toString())) ; | |
| }; | |
| PackageTest test1 = init.get().addInitializer(cmd -> { | |
| cmd.setArgumentValue("--app-version", "2.0.0.2"); | |
| cmd.setArgumentValue("--arguments", "bar"); | |
| cmd.setArgumentValue("--install-dir", cmd.name() + "-1"); | |
| }); | |
| // Tweak Upgrade table of the second package in a way the default | |
| // FindRelatedProducts MSI action will find 1st package, but custom | |
| // jpackage's FindRelatedProductsEx action will not as it gracefuly | |
| // handles more than 3 components of version strings. | |
| // In MSI log of the 2nd package installartion session it will be something like: | |
| /* | |
| Action start 12:08:38: FindRelatedProducts. | |
| FindRelatedProducts: Found application: {D88EEA02-56CC-34AD-8216-C2CC244FA898} | |
| MSI (c) (0C:14) [12:08:38:040]: PROPERTY CHANGE: Adding JP_DOWNGRADABLE_FOUND property. Its value is '{D88EEA02-56CC-34AD-8216-C2CC244FA898}'. | |
| MSI (c) (0C:14) [12:08:38:040]: PROPERTY CHANGE: Adding MIGRATE property. Its value is '{D88EEA02-56CC-34AD-8216-C2CC244FA898}'. | |
| Action ended 12:08:38: FindRelatedProducts. Return value 1. | |
| ... | |
| Action start 12:08:38: JpFindRelatedProducts. | |
| Java [12:08:38.180 libwixhelper.cpp:120 (FindRelatedProductsEx)] TRACE: Entering FindRelatedProductsEx | |
| Java [12:08:38.185 libwixhelper.cpp:85 (`anonymous-namespace'::findInstalledPackages)] TRACE: Found {D88EEA02-56CC-34AD-8216-C2CC244FA898} product | |
| Java [12:08:38.187 MsiCA.cpp:61 (msi::CAImpl::removeProperty)] TRACE: Removing MSI property 'JP_UPGRADABLE_FOUND' | |
| Java [12:08:38.187 MsiCA.cpp:61 (msi::CAImpl::removeProperty)] TRACE: Removing MSI property 'MIGRATE' | |
| Java [12:08:38.189 MsiCA.cpp:61 (msi::CAImpl::removeProperty)] TRACE: Removing MSI property 'JP_DOWNGRADABLE_FOUND' | |
| Java [12:08:38.190 libwixhelper.cpp:0 (FindRelatedProductsEx)] TRACE: Exiting FindRelatedProductsEx (entered at libwixhelper.cpp:120) | |
| Action ended 12:08:38: JpFindRelatedProducts. Return value 1. | |
| */ | |
| PackageTest test2 = init.get().addInstallVerifier(cmd -> { | |
| if (!cmd.isPackageUnpacked()) { | |
| // Installation of this package must NOT uninstall | |
| // previously installed package. Verify that. | |
| test1.run(Action.VERIFY_INSTALL); | |
| } | |
| }).forTypes(PackageType.WIN_EXE) | |
| .addInitializer(cmd -> { | |
| final Path resourceDir = TKit.createTempDirectory("resources"); | |
| cmd.addArguments("--resource-dir", resourceDir); | |
| Path scriptPath = resourceDir.resolve(String.format( | |
| "%s-post-msi.wsf", cmd.name())); | |
| XmlUtils.createXml(scriptPath, xml -> { | |
| xml.writeStartElement("job"); | |
| xml.writeAttribute("id", "main"); | |
| xml.writeStartElement("script"); | |
| xml.writeAttribute("language", "JScript"); | |
| xml.writeCData(String.join("\n", Files.readAllLines( | |
| TKit.TEST_SRC_ROOT.resolve(String.format( | |
| "resources/%s-edit-msi.js", cmd.name()))))); | |
| xml.writeEndElement(); | |
| xml.writeEndElement(); | |
| }); | |
| }).forTypes(PackageType.WIN_MSI) | |
| .addBundleVerifier(cmd -> { | |
| Executor.of("cscript.exe", "//Nologo") | |
| .addArgument(TKit.TEST_SRC_ROOT.resolve(String.format( | |
| "resources/%s-edit-msi.js", cmd.name()))) | |
| .addArgument(cmd.outputBundle()) | |
| .execute(); | |
| }); | |
| // Replace real uninstall commands for the first packages with nop action. | |
| // They will be uninstalled automatically when the last package will | |
| // be installed. | |
| test1.disablePackageUninstaller(); | |
| test2.disablePackageUninstaller(); | |
| PackageTest test3 = init.get().addInitializer(cmd -> { | |
| cmd.setArgumentValue("--app-version", "2.0.0.1"); | |
| cmd.setArgumentValue("--arguments", "buzz"); | |
| }); | |
| new PackageTest.Group(test1, test2, test3).run(); | |
| } | |
| @Test | |
| public static void testNoUpgradeTable() throws IOException { | |
| new PackageTest() | |
| .forTypes(PackageType.WINDOWS) | |
| .configureHelloApp() | |
| .addInitializer(cmd -> { | |
| final Path resourceDir = TKit.createTempDirectory("resources"); | |
| cmd.addArguments("--resource-dir", resourceDir); | |
| cmd.setFakeRuntime(); | |
| // Create package without Upgrade table | |
| Document doc = XmlUtils.initDocumentBuilder().parse( | |
| Files.newInputStream(TKit.SRC_ROOT.resolve( | |
| "windows/classes/jdk/jpackage/internal/resources/main.wxs"))); | |
| XPath xPath = XPathFactory.newInstance().newXPath(); | |
| NodeList nodes = (NodeList) xPath.evaluate("/Wix/Product/Upgrade", | |
| doc, XPathConstants.NODESET); | |
| nodes.item(0).getParentNode().removeChild(nodes.item(0)); | |
| Source source = new DOMSource(doc); | |
| Result result = new StreamResult(Files.newOutputStream( | |
| resourceDir.resolve("main.wxs"))); | |
| Transformer trans = TransformerFactory.newInstance().newTransformer(); | |
| trans.transform(source, result); | |
| }).run(); | |
| } | |
| } |