5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 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.File; 26 import java.nio.file.Path; 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.HashMap; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Set; 33 import java.util.function.BiConsumer; 34 import java.util.function.Consumer; 35 import java.util.function.Supplier; 36 import java.util.stream.Collectors; 37 import java.util.stream.Stream; 38 39 /** 40 * Instance of PackageTest is for configuring and running a single jpackage 41 * command to produce platform specific package bundle. 42 * 43 * Provides methods hook up custom configuration of jpackage command and 44 * verification of the output bundle. 45 */ 46 public final class PackageTest { 47 48 /** 49 * Default test configuration for jpackage command. Default jpackage command 50 * initialization includes: 51 * <li>Set --input and --dest parameters. 52 * <li>Set --name parameter. Value of the parameter is the name of the first 53 * class with main function found in the callers stack. Defaults can be 54 * overridden with custom initializers set with subsequent addInitializer() 55 * function calls. 56 */ 57 public PackageTest() { 58 action = DEFAULT_ACTION; 59 forTypes(); 60 setJPackageExitCode(0); 61 handlers = new HashMap<>(); 62 currentTypes.forEach(v -> handlers.put(v, new Handler(v))); 63 } 64 65 public PackageTest forTypes(PackageType... types) { 66 Collection<PackageType> newTypes; 67 if (types == null || types.length == 0) { 68 newTypes = PackageType.NATIVE; 69 } else { 70 newTypes = Set.of(types); 71 } 72 currentTypes = newTypes.stream().filter(type -> type.isSupported()).collect( 73 Collectors.toUnmodifiableSet()); 74 return this; 75 } 76 77 public PackageTest forTypes(Collection<PackageType> types) { 78 return forTypes(types.toArray(PackageType[]::new)); 79 } 80 81 public PackageTest setJPackageExitCode(int v) { 82 expectedJPackageExitCode = 0; 83 return this; 84 } 85 86 public PackageTest addInitializer(Consumer<JPackageCommand> v) { 87 currentTypes.stream().forEach(type -> handlers.get(type).addInitializer( 88 v)); 89 return this; 90 } 91 92 public PackageTest addBundleVerifier( 93 BiConsumer<JPackageCommand, Executor.Result> v) { 94 currentTypes.stream().forEach( 95 type -> handlers.get(type).addBundleVerifier(v)); 96 return this; 97 } 98 99 public PackageTest addBundleVerifier(Consumer<JPackageCommand> v) { 100 return addBundleVerifier((cmd, unused) -> v.accept(cmd)); 101 } 102 103 public PackageTest addBundlePropertyVerifier(String propertyName, 104 BiConsumer<String, String> pred) { 105 return addBundleVerifier(cmd -> { 106 String propertyValue = null; 107 switch (cmd.packageType()) { 108 case LINUX_DEB: 109 propertyValue = LinuxHelper.getDebBundleProperty( 110 cmd.outputBundle(), propertyName); 111 break; 112 113 case LINUX_RPM: 114 propertyValue = LinuxHelper.geRpmBundleProperty( 115 cmd.outputBundle(), propertyName); 116 break; 117 118 default: 119 throw new UnsupportedOperationException(); 120 } 121 122 pred.accept(propertyName, propertyValue); 123 }); 124 } 125 126 public PackageTest addBundlePropertyVerifier(String propertyName, 127 String expectedPropertyValue) { 128 return addBundlePropertyVerifier(propertyName, (unused, v) -> { 129 Test.assertEquals(expectedPropertyValue, v, String.format( 130 "Check value of %s property is [%s]", propertyName, v)); 131 }); 132 } 133 134 public PackageTest addInstallVerifier(Consumer<JPackageCommand> v) { 135 currentTypes.stream().forEach( 136 type -> handlers.get(type).addInstallVerifier(v)); 137 return this; 138 } 139 140 public PackageTest addUninstallVerifier(Consumer<JPackageCommand> v) { 141 currentTypes.stream().forEach( 142 type -> handlers.get(type).addUninstallVerifier(v)); 143 return this; 144 } 145 146 public PackageTest configureHelloApp() { 147 addInitializer(cmd -> HelloApp.addTo(cmd)); 148 addInstallVerifier(cmd -> HelloApp.executeAndVerifyOutput( 149 cmd.launcherInstallationPath(), cmd.getAllArgumentValues( 150 "--arguments"))); 151 return this; 152 } 153 154 public void run() { 155 List<Handler> supportedHandlers = handlers.values().stream() 156 .filter(entry -> !entry.isVoid()) 157 .collect(Collectors.toList()); 158 159 if (supportedHandlers.isEmpty()) { 160 // No handlers with initializers found. Nothing to do. 161 return; 162 } 163 164 Supplier<JPackageCommand> initializer = new Supplier<>() { 165 @Override 166 public JPackageCommand get() { 167 JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput(); 168 if (bundleOutputDir != null) { 169 cmd.setArgumentValue("--dest", bundleOutputDir.toString()); 170 } 213 214 void addInstallVerifier(Consumer<JPackageCommand> v) { 215 installVerifiers.add(v); 216 } 217 218 void addUninstallVerifier(Consumer<JPackageCommand> v) { 219 uninstallVerifiers.add(v); 220 } 221 222 @Override 223 public void accept(JPackageCommand cmd) { 224 type.applyTo(cmd); 225 226 initializers.stream().forEach(v -> v.accept(cmd)); 227 switch (action) { 228 case CREATE: 229 Executor.Result result = cmd.execute(); 230 result.assertExitCodeIs(expectedJPackageExitCode); 231 Test.assertFileExists(cmd.outputBundle(), 232 expectedJPackageExitCode == 0); 233 verifyPackageBundle(JPackageCommand.createImmutable(cmd), 234 result); 235 break; 236 237 case VERIFY_INSTALLED: 238 verifyPackageInstalled(JPackageCommand.createImmutable(cmd)); 239 break; 240 241 case VERIFY_UNINSTALLED: 242 verifyPackageUninstalled( 243 JPackageCommand.createImmutable(cmd)); 244 break; 245 } 246 } 247 248 private void verifyPackageBundle(JPackageCommand cmd, 249 Executor.Result result) { 250 bundleVerifiers.stream().forEach(v -> v.accept(cmd, result)); 251 } 252 253 private void verifyPackageInstalled(JPackageCommand cmd) { 254 Test.trace(String.format("Verify installed: %s", 255 cmd.getPrintableCommandLine())); 256 if (cmd.isRuntime()) { 257 Test.assertDirectoryExists( 258 cmd.appInstallationDirectory().resolve("runtime"), false); 259 Test.assertDirectoryExists( 260 cmd.appInstallationDirectory().resolve("app"), false); 261 } 262 263 Test.assertExecutableFileExists(cmd.launcherInstallationPath(), 264 !cmd.isRuntime()); 265 266 if (PackageType.WINDOWS.contains(cmd.packageType())) { 267 new WindowsHelper.AppVerifier(cmd); 268 } 269 270 installVerifiers.stream().forEach(v -> v.accept(cmd)); 271 } 272 273 private void verifyPackageUninstalled(JPackageCommand cmd) { 274 Test.trace(String.format("Verify uninstalled: %s", 275 cmd.getPrintableCommandLine())); 276 if (!cmd.isRuntime()) { 277 Test.assertFileExists(cmd.launcherInstallationPath(), false); 278 Test.assertDirectoryExists(cmd.appInstallationDirectory(), false); 279 } 280 281 if (PackageType.WINDOWS.contains(cmd.packageType())) { 282 new WindowsHelper.AppVerifier(cmd); 283 } 284 285 uninstallVerifiers.stream().forEach(v -> v.accept(cmd)); 286 } 287 288 private final PackageType type; 289 private final List<Consumer<JPackageCommand>> initializers; 290 private final List<BiConsumer<JPackageCommand, Executor.Result>> bundleVerifiers; 291 private final List<Consumer<JPackageCommand>> installVerifiers; 292 private final List<Consumer<JPackageCommand>> uninstallVerifiers; 293 } 294 295 private Collection<PackageType> currentTypes; 296 private int expectedJPackageExitCode; 297 private Map<PackageType, Handler> handlers; 298 private Action action; 299 300 /** 301 * Test action. 302 */ 303 static public enum Action { 304 /** 305 * Create bundle. 306 */ 307 CREATE, 308 /** 309 * Verify bundle installed. 310 */ 311 VERIFY_INSTALLED, 312 /** 313 * Verify bundle uninstalled. 314 */ 315 VERIFY_UNINSTALLED 316 }; 317 private final static Action DEFAULT_ACTION; 318 private final static File bundleOutputDir; 319 320 static { 321 final String JPACKAGE_TEST_OUTPUT = "jpackage.test.output"; 322 323 String val = System.getProperty(JPACKAGE_TEST_OUTPUT); 324 if (val == null) { 325 bundleOutputDir = null; 326 } else { 327 bundleOutputDir = new File(val).getAbsoluteFile(); 328 329 Test.assertTrue(bundleOutputDir.isDirectory(), String.format( 330 "Check value of %s property [%s] references a directory", 331 JPACKAGE_TEST_OUTPUT, bundleOutputDir)); 332 Test.assertTrue(bundleOutputDir.canWrite(), String.format( 333 "Check value of %s property [%s] references writable directory", 334 JPACKAGE_TEST_OUTPUT, bundleOutputDir)); 335 } 336 } 337 338 static { 339 if (System.getProperty("jpackage.verify.install") != null) { 340 DEFAULT_ACTION = Action.VERIFY_INSTALLED; 341 } else if (System.getProperty("jpackage.verify.uninstall") != null) { 342 DEFAULT_ACTION = Action.VERIFY_UNINSTALLED; 343 } else { 344 DEFAULT_ACTION = Action.CREATE; 345 } 346 } 347 } | 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 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.awt.Desktop; 26 import java.io.File; 27 import java.io.IOException; 28 import java.nio.file.Files; 29 import java.nio.file.Path; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Optional; 37 import java.util.Set; 38 import java.util.function.BiConsumer; 39 import java.util.function.Consumer; 40 import java.util.function.Supplier; 41 import java.util.stream.Collectors; 42 import java.util.stream.Stream; 43 import static jdk.jpackage.test.PackageType.LINUX_DEB; 44 import static jdk.jpackage.test.PackageType.LINUX_RPM; 45 46 /** 47 * Instance of PackageTest is for configuring and running a single jpackage 48 * command to produce platform specific package bundle. 49 * 50 * Provides methods hook up custom configuration of jpackage command and 51 * verification of the output bundle. 52 */ 53 public final class PackageTest { 54 55 /** 56 * Default test configuration for jpackage command. Default jpackage command 57 * initialization includes: 58 * <li>Set --input and --dest parameters. 59 * <li>Set --name parameter. Value of the parameter is the name of the first 60 * class with main function found in the callers stack. Defaults can be 61 * overridden with custom initializers set with subsequent addInitializer() 62 * function calls. 63 */ 64 public PackageTest() { 65 action = DEFAULT_ACTION; 66 forTypes(); 67 setJPackageExitCode(0); 68 handlers = new HashMap<>(); 69 namedInitializers = new HashSet<>(); 70 currentTypes.forEach(v -> handlers.put(v, new Handler(v))); 71 } 72 73 public PackageTest forTypes(PackageType... types) { 74 Collection<PackageType> newTypes; 75 if (types == null || types.length == 0) { 76 newTypes = PackageType.NATIVE; 77 } else { 78 newTypes = Set.of(types); 79 } 80 currentTypes = newTypes.stream().filter(type -> type.isSupported()).collect( 81 Collectors.toUnmodifiableSet()); 82 return this; 83 } 84 85 public PackageTest forTypes(Collection<PackageType> types) { 86 return forTypes(types.toArray(PackageType[]::new)); 87 } 88 89 public PackageTest setJPackageExitCode(int v) { 90 expectedJPackageExitCode = v; 91 return this; 92 } 93 94 private PackageTest addInitializer(Consumer<JPackageCommand> v, String id) { 95 if (id != null) { 96 if (namedInitializers.contains(id)) { 97 return this; 98 } 99 100 namedInitializers.add(id); 101 } 102 currentTypes.stream().forEach(type -> handlers.get(type).addInitializer( 103 v)); 104 return this; 105 } 106 107 public PackageTest addInitializer(Consumer<JPackageCommand> v) { 108 return addInitializer(v, null); 109 } 110 111 public PackageTest addBundleVerifier( 112 BiConsumer<JPackageCommand, Executor.Result> v) { 113 currentTypes.stream().forEach( 114 type -> handlers.get(type).addBundleVerifier(v)); 115 return this; 116 } 117 118 public PackageTest addBundleVerifier(Consumer<JPackageCommand> v) { 119 return addBundleVerifier((cmd, unused) -> v.accept(cmd)); 120 } 121 122 public PackageTest addBundlePropertyVerifier(String propertyName, 123 BiConsumer<String, String> pred) { 124 return addBundleVerifier(cmd -> { 125 String propertyValue = null; 126 switch (cmd.packageType()) { 127 case LINUX_DEB: 128 propertyValue = LinuxHelper.getDebBundleProperty( 129 cmd.outputBundle(), propertyName); 130 break; 131 132 case LINUX_RPM: 133 propertyValue = LinuxHelper.getRpmBundleProperty( 134 cmd.outputBundle(), propertyName); 135 break; 136 137 default: 138 throw new UnsupportedOperationException(); 139 } 140 141 pred.accept(propertyName, propertyValue); 142 }); 143 } 144 145 public PackageTest addBundlePropertyVerifier(String propertyName, 146 String expectedPropertyValue) { 147 return addBundlePropertyVerifier(propertyName, (unused, v) -> { 148 Test.assertEquals(expectedPropertyValue, v, String.format( 149 "Check value of %s property is [%s]", propertyName, v)); 150 }); 151 } 152 153 public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) { 154 forTypes(LINUX_DEB, () -> { 155 LinuxHelper.addDebBundleDesktopIntegrationVerifier(this, integrated); 156 }); 157 return this; 158 } 159 160 public PackageTest addInstallVerifier(Consumer<JPackageCommand> v) { 161 currentTypes.stream().forEach( 162 type -> handlers.get(type).addInstallVerifier(v)); 163 return this; 164 } 165 166 public PackageTest addUninstallVerifier(Consumer<JPackageCommand> v) { 167 currentTypes.stream().forEach( 168 type -> handlers.get(type).addUninstallVerifier(v)); 169 return this; 170 } 171 172 public PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa, 173 String... faLauncherDefaultArgs) { 174 175 addInitializer(cmd -> HelloApp.addTo(cmd), "HelloApp"); 176 addInstallVerifier(cmd -> { 177 if (cmd.isFakeRuntimeInstalled( 178 "Not running file associations test")) { 179 return; 180 } 181 182 Test.withTempFile(fa.getSuffix(), testFile -> { 183 if (PackageType.LINUX.contains(cmd.packageType())) { 184 LinuxHelper.initFileAssociationsTestFile(testFile); 185 } 186 187 try { 188 final Path appOutput = Path.of(HelloApp.OUTPUT_FILENAME); 189 Files.deleteIfExists(appOutput); 190 191 Test.trace(String.format("Use desktop to open [%s] file", 192 testFile)); 193 Desktop.getDesktop().open(testFile.toFile()); 194 Test.waitForFileCreated(appOutput, 7); 195 196 List<String> expectedArgs = new ArrayList<>(List.of( 197 faLauncherDefaultArgs)); 198 expectedArgs.add(testFile.toString()); 199 200 // Wait a little bit after file has been created to 201 // make sure there are no pending writes into it. 202 Thread.sleep(3000); 203 HelloApp.verifyOutputFile(appOutput, expectedArgs.toArray( 204 String[]::new)); 205 } catch (IOException | InterruptedException ex) { 206 throw new RuntimeException(ex); 207 } 208 }); 209 }); 210 211 forTypes(PackageType.LINUX, () -> { 212 LinuxHelper.addFileAssociationsVerifier(this, fa); 213 }); 214 215 return this; 216 } 217 218 private void forTypes(Collection<PackageType> types, Runnable action) { 219 Set<PackageType> oldTypes = Set.of(currentTypes.toArray( 220 PackageType[]::new)); 221 try { 222 forTypes(types); 223 action.run(); 224 } finally { 225 forTypes(oldTypes); 226 } 227 } 228 229 private void forTypes(PackageType type, Runnable action) { 230 forTypes(List.of(type), action); 231 } 232 233 public PackageTest configureHelloApp() { 234 addInitializer(cmd -> HelloApp.addTo(cmd), "HelloApp"); 235 addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput); 236 return this; 237 } 238 239 public void run() { 240 List<Handler> supportedHandlers = handlers.values().stream() 241 .filter(entry -> !entry.isVoid()) 242 .collect(Collectors.toList()); 243 244 if (supportedHandlers.isEmpty()) { 245 // No handlers with initializers found. Nothing to do. 246 return; 247 } 248 249 Supplier<JPackageCommand> initializer = new Supplier<>() { 250 @Override 251 public JPackageCommand get() { 252 JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput(); 253 if (bundleOutputDir != null) { 254 cmd.setArgumentValue("--dest", bundleOutputDir.toString()); 255 } 298 299 void addInstallVerifier(Consumer<JPackageCommand> v) { 300 installVerifiers.add(v); 301 } 302 303 void addUninstallVerifier(Consumer<JPackageCommand> v) { 304 uninstallVerifiers.add(v); 305 } 306 307 @Override 308 public void accept(JPackageCommand cmd) { 309 type.applyTo(cmd); 310 311 initializers.stream().forEach(v -> v.accept(cmd)); 312 switch (action) { 313 case CREATE: 314 Executor.Result result = cmd.execute(); 315 result.assertExitCodeIs(expectedJPackageExitCode); 316 Test.assertFileExists(cmd.outputBundle(), 317 expectedJPackageExitCode == 0); 318 verifyPackageBundle(cmd.createImmutableCopy(), result); 319 break; 320 321 case VERIFY_INSTALL: 322 verifyPackageInstalled(cmd.createImmutableCopy()); 323 break; 324 325 case VERIFY_UNINSTALL: 326 verifyPackageUninstalled(cmd.createImmutableCopy()); 327 break; 328 } 329 } 330 331 private void verifyPackageBundle(JPackageCommand cmd, 332 Executor.Result result) { 333 if (PackageType.LINUX.contains(cmd.packageType())) { 334 Test.assertNotEquals(0L, LinuxHelper.getInstalledPackageSizeKB( 335 cmd), String.format( 336 "Check installed size of [%s] package in KB is not zero", 337 LinuxHelper.getPackageName(cmd))); 338 } 339 bundleVerifiers.stream().forEach(v -> v.accept(cmd, result)); 340 } 341 342 private void verifyPackageInstalled(JPackageCommand cmd) { 343 Test.trace(String.format("Verify installed: %s", 344 cmd.getPrintableCommandLine())); 345 if (cmd.isRuntime()) { 346 Test.assertDirectoryExists( 347 cmd.appRuntimeInstallationDirectory(), false); 348 Test.assertDirectoryExists( 349 cmd.appInstallationDirectory().resolve("app"), false); 350 } else { 351 Test.assertExecutableFileExists(cmd.launcherInstallationPath(), 352 true); 353 } 354 355 if (PackageType.WINDOWS.contains(cmd.packageType())) { 356 new WindowsHelper.AppVerifier(cmd); 357 } 358 359 installVerifiers.stream().forEach(v -> v.accept(cmd)); 360 } 361 362 private void verifyPackageUninstalled(JPackageCommand cmd) { 363 Test.trace(String.format("Verify uninstalled: %s", 364 cmd.getPrintableCommandLine())); 365 if (!cmd.isRuntime()) { 366 Test.assertFileExists(cmd.launcherInstallationPath(), false); 367 Test.assertDirectoryExists(cmd.appInstallationDirectory(), false); 368 } 369 370 if (PackageType.WINDOWS.contains(cmd.packageType())) { 371 new WindowsHelper.AppVerifier(cmd); 372 } 373 374 uninstallVerifiers.stream().forEach(v -> v.accept(cmd)); 375 } 376 377 private final PackageType type; 378 private final List<Consumer<JPackageCommand>> initializers; 379 private final List<BiConsumer<JPackageCommand, Executor.Result>> bundleVerifiers; 380 private final List<Consumer<JPackageCommand>> installVerifiers; 381 private final List<Consumer<JPackageCommand>> uninstallVerifiers; 382 } 383 384 private Collection<PackageType> currentTypes; 385 private int expectedJPackageExitCode; 386 private Map<PackageType, Handler> handlers; 387 private Set<String> namedInitializers; 388 private Action action; 389 390 /** 391 * Test action. 392 */ 393 static public enum Action { 394 /** 395 * Create bundle. 396 */ 397 CREATE, 398 /** 399 * Verify bundle installed. 400 */ 401 VERIFY_INSTALL, 402 /** 403 * Verify bundle uninstalled. 404 */ 405 VERIFY_UNINSTALL; 406 407 @Override 408 public String toString() { 409 return name().toLowerCase().replace('_', '-'); 410 } 411 }; 412 private final static Action DEFAULT_ACTION; 413 private final static File bundleOutputDir; 414 415 static { 416 final String propertyName = "output"; 417 String val = Test.getConfigProperty(propertyName); 418 if (val == null) { 419 bundleOutputDir = null; 420 } else { 421 bundleOutputDir = new File(val).getAbsoluteFile(); 422 423 if (!bundleOutputDir.isDirectory()) { 424 throw new IllegalArgumentException(String.format( 425 "Invalid value of %s sytem property: [%s]. Should be existing directory", 426 Test.getConfigPropertyName(propertyName), 427 bundleOutputDir)); 428 } 429 } 430 } 431 432 static { 433 final String propertyName = "action"; 434 String action = Optional.ofNullable(Test.getConfigProperty(propertyName)).orElse( 435 Action.CREATE.toString()).toLowerCase(); 436 DEFAULT_ACTION = Stream.of(Action.values()).filter( 437 a -> a.toString().equals(action)).findFirst().orElseThrow( 438 () -> new IllegalArgumentException(String.format( 439 "Unrecognized value of %s property: [%s]", 440 Test.getConfigPropertyName(propertyName), action))); 441 } 442 } |