9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jdk.jpackage.test; 24 25 import java.io.IOException; 26 import java.nio.file.Files; 27 import java.nio.file.Path; 28 import java.util.*; 29 import java.util.stream.Collectors; 30 import java.util.stream.Stream; 31 32 public class WindowsHelper { 33 34 static String getBundleName(JPackageCommand cmd) { 35 cmd.verifyIsOfType(PackageType.WINDOWS); 36 return String.format("%s-%s%s", cmd.name(), cmd.version(), 37 cmd.packageType().getSuffix()); 38 } 39 40 static Path getInstallationDirectory(JPackageCommand cmd) { 41 cmd.verifyIsOfType(PackageType.WINDOWS); 42 Path installDir = Path.of( 43 cmd.getArgumentValue("--install-dir", () -> cmd.name())); 44 if (isUserLocalInstall(cmd)) { 45 return USER_LOCAL.resolve(installDir); 46 } 47 return PROGRAM_FILES.resolve(installDir); 48 } 49 50 private static boolean isUserLocalInstall(JPackageCommand cmd) { 51 return cmd.hasArgument("--win-per-user-install"); 52 } 53 54 static class AppVerifier { 55 56 AppVerifier(JPackageCommand cmd) { 57 cmd.verifyIsOfType(PackageType.WINDOWS); 58 this.cmd = cmd; 59 verifyStartMenuShortcut(); 60 verifyDesktopShortcut(); 61 verifyFileAssociationsRegistry(); 62 } 63 64 private void verifyDesktopShortcut() { 65 boolean appInstalled = cmd.appLauncherPath().toFile().exists(); 66 if (cmd.hasArgument("--win-shortcut")) { 67 if (isUserLocalInstall(cmd)) { 68 verifyUserLocalDesktopShortcut(appInstalled); 69 verifySystemDesktopShortcut(false); 70 } else { 71 verifySystemDesktopShortcut(appInstalled); 72 verifyUserLocalDesktopShortcut(false); 73 } 74 } else { 75 verifySystemDesktopShortcut(false); 76 verifyUserLocalDesktopShortcut(false); 184 "HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\" + contentType, 185 "Extension"); 186 187 if (exists) { 188 TKit.assertEquals(suffix, suffixFromRegistry, 189 "Check suffix in registry is as expected"); 190 TKit.assertEquals(contentType, contentTypeFromRegistry, 191 "Check content type in registry is as expected"); 192 } else { 193 TKit.assertNull(suffixFromRegistry, 194 "Check suffix in registry not found"); 195 TKit.assertNull(contentTypeFromRegistry, 196 "Check content type in registry not found"); 197 } 198 } 199 200 private final JPackageCommand cmd; 201 } 202 203 private static String queryRegistryValue(String keyPath, String valueName) { 204 Executor.Result status = new Executor() 205 .setExecutable("reg") 206 .saveOutput() 207 .addArguments("query", keyPath, "/v", valueName) 208 .execute(); 209 if (status.exitCode == 1) { 210 // Should be the case of no such registry value or key 211 String lookupString = "ERROR: The system was unable to find the specified registry key or value."; 212 status.getOutput().stream().filter(line -> line.equals(lookupString)).findFirst().orElseThrow( 213 () -> new RuntimeException(String.format( 214 "Failed to find [%s] string in the output", 215 lookupString))); 216 TKit.trace(String.format( 217 "Registry value [%s] at [%s] path not found", valueName, 218 keyPath)); 219 return null; 220 } 221 222 String value = status.assertExitCodeIsZero().getOutput().stream().skip(2).findFirst().orElseThrow(); 223 // Extract the last field from the following line: 224 // Common Desktop REG_SZ C:\Users\Public\Desktop 225 value = value.split(" REG_SZ ")[1]; 226 227 TKit.trace(String.format("Registry value [%s] at [%s] path is [%s]", 228 valueName, keyPath, value)); 229 230 return value; 231 } 232 233 private static String queryRegistryValueCache(String keyPath, | 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package jdk.jpackage.test; 24 25 import java.io.IOException; 26 import java.nio.file.Files; 27 import java.nio.file.Path; 28 import java.util.*; 29 import java.util.function.BiConsumer; 30 import java.util.stream.Collectors; 31 import java.util.stream.Stream; 32 import jdk.jpackage.test.Functional.ThrowingRunnable; 33 import jdk.jpackage.test.PackageTest.PackageHandlers; 34 35 public class WindowsHelper { 36 37 static String getBundleName(JPackageCommand cmd) { 38 cmd.verifyIsOfType(PackageType.WINDOWS); 39 return String.format("%s-%s%s", cmd.name(), cmd.version(), 40 cmd.packageType().getSuffix()); 41 } 42 43 static Path getInstallationDirectory(JPackageCommand cmd) { 44 Path installSubDir = getInstallationSubDirectory(cmd); 45 if (isUserLocalInstall(cmd)) { 46 return USER_LOCAL.resolve(installSubDir); 47 } 48 return PROGRAM_FILES.resolve(installSubDir); 49 } 50 51 static Path getInstallationSubDirectory(JPackageCommand cmd) { 52 cmd.verifyIsOfType(PackageType.WINDOWS); 53 return Path.of(cmd.getArgumentValue("--install-dir", () -> cmd.name())); 54 } 55 56 private static void runMsiexecWithRetries(Executor misexec) { 57 Executor.Result result = null; 58 for (int attempt = 0; attempt != 3; ++attempt) { 59 result = misexec.executeWithoutExitCodeCheck(); 60 if (result.exitCode == 1618) { 61 // Another installation is already in progress. 62 // Wait a little and try again. 63 ThrowingRunnable.toRunnable(() -> Thread.sleep(3000)).run(); 64 continue; 65 } 66 break; 67 } 68 69 result.assertExitCodeIsZero(); 70 } 71 72 static PackageHandlers createMsiPackageHandlers() { 73 BiConsumer<JPackageCommand, Boolean> installMsi = (cmd, install) -> { 74 cmd.verifyIsOfType(PackageType.WIN_MSI); 75 runMsiexecWithRetries(Executor.of("msiexec", "/qn", "/norestart", 76 install ? "/i" : "/x").addArgument(cmd.outputBundle())); 77 }; 78 79 PackageHandlers msi = new PackageHandlers(); 80 msi.installHandler = cmd -> installMsi.accept(cmd, true); 81 msi.uninstallHandler = cmd -> installMsi.accept(cmd, false); 82 msi.unpackHandler = (cmd, destinationDir) -> { 83 cmd.verifyIsOfType(PackageType.WIN_MSI); 84 runMsiexecWithRetries(Executor.of("msiexec", "/a") 85 .addArgument(cmd.outputBundle().normalize()) 86 .addArguments("/qn", String.format("TARGETDIR=%s", 87 destinationDir.toAbsolutePath().normalize()))); 88 return destinationDir.resolve(getInstallationSubDirectory(cmd)); 89 }; 90 return msi; 91 } 92 93 static PackageHandlers createExePackageHandlers() { 94 PackageHandlers exe = new PackageHandlers(); 95 exe.installHandler = cmd -> { 96 cmd.verifyIsOfType(PackageType.WIN_EXE); 97 new Executor().setExecutable(cmd.outputBundle()).execute(); 98 }; 99 100 return exe; 101 } 102 103 public static String getMsiProperty(JPackageCommand cmd, String propertyName) { 104 cmd.verifyIsOfType(PackageType.WIN_MSI); 105 return Executor.of("cscript.exe", "//Nologo") 106 .addArgument(TKit.TEST_SRC_ROOT.resolve("resources/query-msi-property.js")) 107 .addArgument(cmd.outputBundle()) 108 .addArgument(propertyName) 109 .dumpOutput() 110 .executeAndGetOutput().stream().collect(Collectors.joining("\n")); 111 } 112 113 private static boolean isUserLocalInstall(JPackageCommand cmd) { 114 return cmd.hasArgument("--win-per-user-install"); 115 } 116 117 static class DesktopIntegrationVerifier { 118 119 DesktopIntegrationVerifier(JPackageCommand cmd) { 120 cmd.verifyIsOfType(PackageType.WINDOWS); 121 this.cmd = cmd; 122 verifyStartMenuShortcut(); 123 verifyDesktopShortcut(); 124 verifyFileAssociationsRegistry(); 125 } 126 127 private void verifyDesktopShortcut() { 128 boolean appInstalled = cmd.appLauncherPath().toFile().exists(); 129 if (cmd.hasArgument("--win-shortcut")) { 130 if (isUserLocalInstall(cmd)) { 131 verifyUserLocalDesktopShortcut(appInstalled); 132 verifySystemDesktopShortcut(false); 133 } else { 134 verifySystemDesktopShortcut(appInstalled); 135 verifyUserLocalDesktopShortcut(false); 136 } 137 } else { 138 verifySystemDesktopShortcut(false); 139 verifyUserLocalDesktopShortcut(false); 247 "HKLM\\Software\\Classes\\MIME\\Database\\Content Type\\" + contentType, 248 "Extension"); 249 250 if (exists) { 251 TKit.assertEquals(suffix, suffixFromRegistry, 252 "Check suffix in registry is as expected"); 253 TKit.assertEquals(contentType, contentTypeFromRegistry, 254 "Check content type in registry is as expected"); 255 } else { 256 TKit.assertNull(suffixFromRegistry, 257 "Check suffix in registry not found"); 258 TKit.assertNull(contentTypeFromRegistry, 259 "Check content type in registry not found"); 260 } 261 } 262 263 private final JPackageCommand cmd; 264 } 265 266 private static String queryRegistryValue(String keyPath, String valueName) { 267 var status = Executor.of("reg", "query", keyPath, "/v", valueName) 268 .saveOutput() 269 .executeWithoutExitCodeCheck(); 270 if (status.exitCode == 1) { 271 // Should be the case of no such registry value or key 272 String lookupString = "ERROR: The system was unable to find the specified registry key or value."; 273 TKit.assertTextStream(lookupString) 274 .predicate(String::equals) 275 .orElseThrow(() -> new RuntimeException(String.format( 276 "Failed to find [%s] string in the output", 277 lookupString))); 278 TKit.trace(String.format( 279 "Registry value [%s] at [%s] path not found", valueName, 280 keyPath)); 281 return null; 282 } 283 284 String value = status.assertExitCodeIsZero().getOutput().stream().skip(2).findFirst().orElseThrow(); 285 // Extract the last field from the following line: 286 // Common Desktop REG_SZ C:\Users\Public\Desktop 287 value = value.split(" REG_SZ ")[1]; 288 289 TKit.trace(String.format("Registry value [%s] at [%s] path is [%s]", 290 valueName, keyPath, value)); 291 292 return value; 293 } 294 295 private static String queryRegistryValueCache(String keyPath, |