< prev index next >

test/jdk/tools/jpackage/helpers/jdk/jpackage/test/PackageTest.java

Print this page




  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.awt.GraphicsEnvironment;
  27 import java.io.File;
  28 import java.nio.file.Files;
  29 import java.nio.file.Path;
  30 import java.util.*;
  31 import java.util.function.BiConsumer;
  32 import java.util.function.Consumer;
  33 import java.util.function.Predicate;
  34 import java.util.function.Supplier;
  35 import java.util.stream.Collectors;
  36 import java.util.stream.Stream;
  37 import jdk.jpackage.test.Functional.ThrowingConsumer;
  38 import jdk.incubator.jpackage.internal.AppImageFile;


  39 import static jdk.jpackage.test.PackageType.*;
  40 
  41 /**
  42  * Instance of PackageTest is for configuring and running a single jpackage
  43  * command to produce platform specific package bundle.
  44  *
  45  * Provides methods to hook up custom configuration of jpackage command and
  46  * verification of the output bundle.
  47  */
  48 public final class PackageTest {
  49 
  50     /**
  51      * Default test configuration for jpackage command. Default jpackage command
  52      * initialization includes:
  53      * <li>Set --input and --dest parameters.
  54      * <li>Set --name parameter. Value of the parameter is the name of the first
  55      * class with main function found in the callers stack. Defaults can be
  56      * overridden with custom initializers set with subsequent addInitializer()
  57      * function calls.
  58      */
  59     public PackageTest() {
  60         action = DEFAULT_ACTION;
  61         excludeTypes = new HashSet<>();
  62         forTypes();
  63         setExpectedExitCode(0);
  64         handlers = new HashMap<>();
  65         namedInitializers = new HashSet<>();
  66         currentTypes.forEach(v -> handlers.put(v, new Handler(v)));


  67     }
  68 
  69     public PackageTest excludeTypes(PackageType... types) {
  70         excludeTypes.addAll(Stream.of(types).collect(Collectors.toSet()));
  71         return forTypes(currentTypes);
  72     }
  73 
  74     public PackageTest excludeTypes(Collection<PackageType> types) {
  75         return excludeTypes(types.toArray(PackageType[]::new));
  76     }
  77 
  78     public PackageTest forTypes(PackageType... types) {
  79         Collection<PackageType> newTypes;
  80         if (types == null || types.length == 0) {
  81             newTypes = PackageType.NATIVE;
  82         } else {
  83             newTypes = Stream.of(types).collect(Collectors.toSet());
  84         }
  85         currentTypes = newTypes.stream()
  86                 .filter(PackageType::isSupported)


 100     public PackageTest notForTypes(Collection<PackageType> types) {
 101         Set<PackageType> workset = new HashSet<>(currentTypes);
 102         workset.removeAll(types);
 103         return forTypes(workset);
 104     }
 105 
 106     public PackageTest setExpectedExitCode(int v) {
 107         expectedJPackageExitCode = v;
 108         return this;
 109     }
 110 
 111     private PackageTest addInitializer(ThrowingConsumer<JPackageCommand> v,
 112             String id) {
 113         if (id != null) {
 114             if (namedInitializers.contains(id)) {
 115                 return this;
 116             }
 117 
 118             namedInitializers.add(id);
 119         }
 120         currentTypes.stream().forEach(type -> handlers.get(type).addInitializer(
 121                 ThrowingConsumer.toConsumer(v)));
 122         return this;
 123     }
 124 














 125     public PackageTest addInitializer(ThrowingConsumer<JPackageCommand> v) {
 126         return addInitializer(v, null);
 127     }
 128 




 129     public PackageTest addBundleVerifier(
 130             BiConsumer<JPackageCommand, Executor.Result> v) {
 131         currentTypes.stream().forEach(
 132                 type -> handlers.get(type).addBundleVerifier(v));
 133         return this;
 134     }
 135 
 136     public PackageTest addBundleVerifier(ThrowingConsumer<JPackageCommand> v) {
 137         return addBundleVerifier(
 138                 (cmd, unused) -> ThrowingConsumer.toConsumer(v).accept(cmd));
 139     }
 140 
 141     public PackageTest addBundlePropertyVerifier(String propertyName,
 142             BiConsumer<String, String> pred) {
 143         return addBundleVerifier(cmd -> {
 144             pred.accept(propertyName,
 145                     LinuxHelper.getBundleProperty(cmd, propertyName));









 146         });
 147     }
 148 
 149     public PackageTest addBundlePropertyVerifier(String propertyName,
 150             String expectedPropertyValue) {
 151         return addBundlePropertyVerifier(propertyName, (unused, v) -> {
 152             TKit.assertEquals(expectedPropertyValue, v, String.format(
 153                     "Check value of %s property is [%s]", propertyName, v));
 154         });
 155     }
 156 
 157     public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) {
 158         forTypes(LINUX, () -> {
 159             LinuxHelper.addBundleDesktopIntegrationVerifier(this, integrated);
 160         });
 161         return this;
 162     }
 163 
 164     public PackageTest addInstallVerifier(ThrowingConsumer<JPackageCommand> v) {
 165         currentTypes.stream().forEach(
 166                 type -> handlers.get(type).addInstallVerifier(
 167                         ThrowingConsumer.toConsumer(v)));
 168         return this;
 169     }
 170 
 171     public PackageTest addUninstallVerifier(ThrowingConsumer<JPackageCommand> v) {
 172         currentTypes.stream().forEach(
 173                 type -> handlers.get(type).addUninstallVerifier(
 174                         ThrowingConsumer.toConsumer(v)));
 175         return this;
 176     }
 177 


















 178     static void withTestFileAssociationsFile(FileAssociations fa,
 179             ThrowingConsumer<Path> consumer) {
 180         final String testFileDefaultName = String.join(".", "test",
 181                 fa.getSuffix());
 182         TKit.withTempFile(testFileDefaultName, fa.getSuffix(), testFile -> {
 183             if (TKit.isLinux()) {
 184                 LinuxHelper.initFileAssociationsTestFile(testFile);
 185             }
 186             consumer.accept(testFile);
 187         });
 188     }
 189 
 190     PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa,
 191             String... faLauncherDefaultArgs) {
 192 
 193         // Setup test app to have valid jpackage command line before
 194         // running check of type of environment.
 195         addInitializer(cmd -> new HelloApp(null).addTo(cmd), "HelloApp");
 196 
 197         String noActionMsg = "Not running file associations test";
 198         if (GraphicsEnvironment.isHeadless()) {
 199             TKit.trace(String.format(
 200                     "%s because running in headless environment", noActionMsg));
 201             return this;
 202         }
 203 
 204         addInstallVerifier(cmd -> {
 205             if (cmd.isFakeRuntime(noActionMsg)) {
 206                 return;
 207             }
 208 
 209             withTestFileAssociationsFile(fa, testFile -> {
 210                 testFile = testFile.toAbsolutePath().normalize();
 211 
 212                 final Path appOutput = testFile.getParent()
 213                         .resolve(HelloApp.OUTPUT_FILENAME);
 214                 Files.deleteIfExists(appOutput);
 215 
 216                 TKit.trace(String.format("Use desktop to open [%s] file",
 217                         testFile));
 218                 Desktop.getDesktop().open(testFile.toFile());
 219                 TKit.waitForFileCreated(appOutput, 7);
 220 
 221                 List<String> expectedArgs = new ArrayList<>(List.of(
 222                         faLauncherDefaultArgs));
 223                 expectedArgs.add(testFile.toString());
 224 
 225                 // Wait a little bit after file has been created to
 226                 // make sure there are no pending writes into it.
 227                 Thread.sleep(3000);
 228                 HelloApp.verifyOutputFile(appOutput, expectedArgs);

 229             });
 230         });
 231 
 232         forTypes(PackageType.LINUX, () -> {
 233             LinuxHelper.addFileAssociationsVerifier(this, fa);
 234         });
 235 
 236         return this;
 237     }
 238 
 239     PackageTest forTypes(Collection<PackageType> types, Runnable action) {
 240         Set<PackageType> oldTypes = Set.of(currentTypes.toArray(
 241                 PackageType[]::new));
 242         try {
 243             forTypes(types);
 244             action.run();
 245         } finally {
 246             forTypes(oldTypes);
 247         }
 248         return this;
 249     }
 250 
 251     PackageTest forTypes(PackageType type, Runnable action) {
 252         return forTypes(List.of(type), action);
 253     }
 254 
 255     PackageTest notForTypes(Collection<PackageType> types, Runnable action) {
 256         Set<PackageType> workset = new HashSet<>(currentTypes);
 257         workset.removeAll(types);
 258         return forTypes(workset, action);
 259     }
 260 
 261     PackageTest notForTypes(PackageType type, Runnable action) {
 262         return notForTypes(List.of(type), action);
 263     }
 264 
 265     public PackageTest configureHelloApp() {
 266         return configureHelloApp(null);
 267     }
 268 
 269     public PackageTest configureHelloApp(String encodedName) {
 270         addInitializer(
 271                 cmd -> new HelloApp(JavaAppDesc.parse(encodedName)).addTo(cmd));
 272         addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput);
 273         return this;
 274     }
 275 
 276     public void run() {
 277         List<Handler> supportedHandlers = handlers.values().stream()
 278                 .filter(entry -> !entry.isVoid())































































 279                 .collect(Collectors.toList());













 280 
 281         if (supportedHandlers.isEmpty()) {
 282             // No handlers with initializers found. Nothing to do.
 283             return;
 284         }
 285 
 286         Supplier<JPackageCommand> initializer = new Supplier<>() {
 287             @Override
 288             public JPackageCommand get() {
 289                 JPackageCommand cmd = new JPackageCommand().setDefaultInputOutput();
 290                 if (bundleOutputDir != null) {
 291                     cmd.setArgumentValue("--dest", bundleOutputDir.toString());
 292                 }
 293                 cmd.setDefaultAppName();
 294                 return cmd;










 295             }
 296         };
 297 
 298         supportedHandlers.forEach(handler -> handler.accept(initializer.get()));





 299     }
 300 
 301     public PackageTest setAction(Action value) {
 302         action = value;
 303         return this;



 304     }
 305 
 306     public Action getAction() {
 307         return action;






 308     }
 309 
 310     private class Handler implements Consumer<JPackageCommand> {





 311 
 312         Handler(PackageType type) {
 313             if (!PackageType.NATIVE.contains(type)) {
 314                 throw new IllegalArgumentException(
 315                         "Attempt to configure a test for image packaging");








 316             }
 317             this.type = type;



 318             initializers = new ArrayList<>();
 319             bundleVerifiers = new ArrayList<>();
 320             installVerifiers = new ArrayList<>();
 321             uninstallVerifiers = new ArrayList<>();
 322         }
 323 
 324         boolean isVoid() {
 325             return initializers.isEmpty();
 326         }
 327 
 328         void addInitializer(Consumer<JPackageCommand> v) {
 329             initializers.add(v);
 330         }
 331 
 332         void addBundleVerifier(BiConsumer<JPackageCommand, Executor.Result> v) {
 333             bundleVerifiers.add(v);
 334         }
 335 
 336         void addInstallVerifier(Consumer<JPackageCommand> v) {
 337             installVerifiers.add(v);
 338         }
 339 
 340         void addUninstallVerifier(Consumer<JPackageCommand> v) {
 341             uninstallVerifiers.add(v);
 342         }
 343 
 344         @Override
 345         public void accept(JPackageCommand cmd) {
 346             type.applyTo(cmd);
 347 
 348             initializers.stream().forEach(v -> v.accept(cmd));



 349             cmd.executePrerequisiteActions();

 350 
 351             switch (action) {
 352                 case CREATE:
 353                     Executor.Result result = cmd.execute();
 354                     result.assertExitCodeIs(expectedJPackageExitCode);
 355                     if (expectedJPackageExitCode == 0) {
 356                         TKit.assertFileExists(cmd.outputBundle());
 357                     } else {
 358                         TKit.assertPathExists(cmd.outputBundle(), false);
 359                     }
 360                     verifyPackageBundle(cmd.createImmutableCopy(), result);
 361                     break;
 362 
 363                 case VERIFY_INSTALL:
 364                     if (expectedJPackageExitCode == 0) {
 365                         verifyPackageInstalled(cmd.createImmutableCopy());
 366                     }
 367                     break;
 368 
 369                 case VERIFY_UNINSTALL:
 370                     if (expectedJPackageExitCode == 0) {
 371                         verifyPackageUninstalled(cmd.createImmutableCopy());
 372                     }
 373                     break;
 374             }
 375         }
 376 
 377         private void verifyPackageBundle(JPackageCommand cmd,
 378                 Executor.Result result) {
 379             if (expectedJPackageExitCode == 0) {
 380                 if (PackageType.LINUX.contains(cmd.packageType())) {
 381                     LinuxHelper.verifyPackageBundleEssential(cmd);
 382                 }
 383             }
 384             bundleVerifiers.stream().forEach(v -> v.accept(cmd, result));
 385         }
 386 
 387         private void verifyPackageInstalled(JPackageCommand cmd) {
 388             TKit.trace(String.format("Verify installed: %s",
 389                     cmd.getPrintableCommandLine()));






 390             TKit.assertDirectoryExists(cmd.appRuntimeDirectory());
 391             if (!cmd.isRuntime()) {
 392                 TKit.assertExecutableFileExists(cmd.appLauncherPath());
 393 
 394                 if (PackageType.WINDOWS.contains(cmd.packageType())) {
 395                     new WindowsHelper.AppVerifier(cmd);


 396                 }
 397             }
 398 
 399             TKit.assertPathExists(AppImageFile.getPathInAppImage(
 400                     cmd.appInstallationDirectory()), false);
 401 
 402             installVerifiers.stream().forEach(v -> v.accept(cmd));
 403         }
 404 
 405         private void verifyPackageUninstalled(JPackageCommand cmd) {
 406             TKit.trace(String.format("Verify uninstalled: %s",
 407                     cmd.getPrintableCommandLine()));
 408             if (!cmd.isRuntime()) {
 409                 TKit.assertPathExists(cmd.appLauncherPath(), false);
 410 
 411                 if (PackageType.WINDOWS.contains(cmd.packageType())) {
 412                     new WindowsHelper.AppVerifier(cmd);
 413                 }
 414             }
 415 
 416             TKit.assertPathExists(cmd.appInstallationDirectory(), false);
 417 
 418             uninstallVerifiers.stream().forEach(v -> v.accept(cmd));
 419         }
 420 
 421         private final PackageType type;
 422         private final List<Consumer<JPackageCommand>> initializers;
 423         private final List<BiConsumer<JPackageCommand, Executor.Result>> bundleVerifiers;
 424         private final List<Consumer<JPackageCommand>> installVerifiers;
 425         private final List<Consumer<JPackageCommand>> uninstallVerifiers;
 426     }
 427 




















 428     private Collection<PackageType> currentTypes;
 429     private Set<PackageType> excludeTypes;
 430     private int expectedJPackageExitCode;
 431     private Map<PackageType, Handler> handlers;
 432     private Set<String> namedInitializers;
 433     private Action action;
 434 
 435     /**
 436      * Test action.
 437      */
 438     static public enum Action {
 439         /**
 440          * Create bundle.
 441          */
 442         CREATE,
 443         /**
 444          * Verify bundle installed.
 445          */
 446         VERIFY_INSTALL,
 447         /**
 448          * Verify bundle uninstalled.
 449          */
 450         VERIFY_UNINSTALL;
 451 
 452         @Override
 453         public String toString() {
 454             return name().toLowerCase().replace('_', '-');
 455         }
 456     };
 457     private final static Action DEFAULT_ACTION;
 458     private final static File bundleOutputDir;
 459 
 460     static {
 461         final String propertyName = "output";
 462         String val = TKit.getConfigProperty(propertyName);
 463         if (val == null) {
 464             bundleOutputDir = null;
 465         } else {
 466             bundleOutputDir = new File(val).getAbsoluteFile();
 467 
 468             if (!bundleOutputDir.isDirectory()) {
 469                 throw new IllegalArgumentException(String.format(
 470                         "Invalid value of %s sytem property: [%s]. Should be existing directory",
 471                         TKit.getConfigPropertyName(propertyName),
 472                         bundleOutputDir));
 473             }
 474         }
 475     }
 476 
 477     static {
 478         final String propertyName = "action";
 479         String action = Optional.ofNullable(TKit.getConfigProperty(propertyName)).orElse(
 480                 Action.CREATE.toString()).toLowerCase();
 481         DEFAULT_ACTION = Stream.of(Action.values()).filter(
 482                 a -> a.toString().equals(action)).findFirst().orElseThrow(
 483                         () -> new IllegalArgumentException(String.format(
 484                                 "Unrecognized value of %s property: [%s]",
 485                                 TKit.getConfigPropertyName(propertyName), action)));
 486     }
 487 }


  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.awt.GraphicsEnvironment;
  27 import java.io.File;
  28 import java.nio.file.Files;
  29 import java.nio.file.Path;
  30 import java.util.*;
  31 import java.util.function.*;



  32 import java.util.stream.Collectors;
  33 import java.util.stream.Stream;
  34 import jdk.jpackage.test.Functional.ThrowingConsumer;
  35 import jdk.incubator.jpackage.internal.AppImageFile;
  36 import jdk.jpackage.test.Functional.ThrowingBiConsumer;
  37 import jdk.jpackage.test.Functional.ThrowingRunnable;
  38 import static jdk.jpackage.test.PackageType.*;
  39 
  40 /**
  41  * Instance of PackageTest is for configuring and running a single jpackage
  42  * command to produce platform specific package bundle.
  43  *
  44  * Provides methods to hook up custom configuration of jpackage command and
  45  * verification of the output bundle.
  46  */
  47 public final class PackageTest extends RunnablePackageTest {
  48 









  49     public PackageTest() {

  50         excludeTypes = new HashSet<>();
  51         forTypes();
  52         setExpectedExitCode(0);

  53         namedInitializers = new HashSet<>();
  54         handlers = currentTypes.stream()
  55                 .collect(Collectors.toMap(v -> v, v -> new Handler()));
  56         packageHandlers = createDefaultPackageHandlers();
  57     }
  58 
  59     public PackageTest excludeTypes(PackageType... types) {
  60         excludeTypes.addAll(Stream.of(types).collect(Collectors.toSet()));
  61         return forTypes(currentTypes);
  62     }
  63 
  64     public PackageTest excludeTypes(Collection<PackageType> types) {
  65         return excludeTypes(types.toArray(PackageType[]::new));
  66     }
  67 
  68     public PackageTest forTypes(PackageType... types) {
  69         Collection<PackageType> newTypes;
  70         if (types == null || types.length == 0) {
  71             newTypes = PackageType.NATIVE;
  72         } else {
  73             newTypes = Stream.of(types).collect(Collectors.toSet());
  74         }
  75         currentTypes = newTypes.stream()
  76                 .filter(PackageType::isSupported)


  90     public PackageTest notForTypes(Collection<PackageType> types) {
  91         Set<PackageType> workset = new HashSet<>(currentTypes);
  92         workset.removeAll(types);
  93         return forTypes(workset);
  94     }
  95 
  96     public PackageTest setExpectedExitCode(int v) {
  97         expectedJPackageExitCode = v;
  98         return this;
  99     }
 100 
 101     private PackageTest addInitializer(ThrowingConsumer<JPackageCommand> v,
 102             String id) {
 103         if (id != null) {
 104             if (namedInitializers.contains(id)) {
 105                 return this;
 106             }
 107 
 108             namedInitializers.add(id);
 109         }
 110         currentTypes.forEach(type -> handlers.get(type).addInitializer(
 111                 ThrowingConsumer.toConsumer(v)));
 112         return this;
 113     }
 114 
 115     private PackageTest addRunOnceInitializer(ThrowingRunnable v, String id) {
 116         return addInitializer(new ThrowingConsumer<JPackageCommand>() {
 117             @Override
 118             public void accept(JPackageCommand unused) throws Throwable {
 119                 if (!executed) {
 120                     executed = true;
 121                     v.run();
 122                 }
 123             }
 124 
 125             private boolean executed;
 126         }, id);
 127     }
 128 
 129     public PackageTest addInitializer(ThrowingConsumer<JPackageCommand> v) {
 130         return addInitializer(v, null);
 131     }
 132 
 133     public PackageTest addRunOnceInitializer(ThrowingRunnable v) {
 134         return addRunOnceInitializer(v, null);
 135     }
 136 
 137     public PackageTest addBundleVerifier(
 138             ThrowingBiConsumer<JPackageCommand, Executor.Result> v) {
 139         currentTypes.forEach(type -> handlers.get(type).addBundleVerifier(
 140                 ThrowingBiConsumer.toBiConsumer(v)));
 141         return this;
 142     }
 143 
 144     public PackageTest addBundleVerifier(ThrowingConsumer<JPackageCommand> v) {
 145         return addBundleVerifier(
 146                 (cmd, unused) -> ThrowingConsumer.toConsumer(v).accept(cmd));
 147     }
 148 
 149     public PackageTest addBundlePropertyVerifier(String propertyName,
 150             Predicate<String> pred, String predLabel) {
 151         return addBundleVerifier(cmd -> {
 152             final String value;
 153             if (TKit.isLinux()) {
 154                 value = LinuxHelper.getBundleProperty(cmd, propertyName);
 155             } else if (TKit.isWindows()) {
 156                 value = WindowsHelper.getMsiProperty(cmd, propertyName);
 157             } else {
 158                 throw new IllegalStateException();
 159             }
 160             TKit.assertTrue(pred.test(value), String.format(
 161                     "Check value of %s property %s [%s]", propertyName,
 162                     predLabel, value));
 163         });
 164     }
 165 
 166     public PackageTest addBundlePropertyVerifier(String propertyName,
 167             String expectedPropertyValue) {
 168         return addBundlePropertyVerifier(propertyName,
 169                 expectedPropertyValue::equals, "is");


 170     }
 171 
 172     public PackageTest addBundleDesktopIntegrationVerifier(boolean integrated) {
 173         forTypes(LINUX, () -> {
 174             LinuxHelper.addBundleDesktopIntegrationVerifier(this, integrated);
 175         });
 176         return this;
 177     }
 178 
 179     public PackageTest addInstallVerifier(ThrowingConsumer<JPackageCommand> v) {
 180         currentTypes.forEach(type -> handlers.get(type).addInstallVerifier(

 181                 ThrowingConsumer.toConsumer(v)));
 182         return this;
 183     }
 184 
 185     public PackageTest addUninstallVerifier(ThrowingConsumer<JPackageCommand> v) {
 186         currentTypes.forEach(type -> handlers.get(type).addUninstallVerifier(

 187                 ThrowingConsumer.toConsumer(v)));
 188         return this;
 189     }
 190 
 191     public PackageTest setPackageInstaller(Consumer<JPackageCommand> v) {
 192         currentTypes.forEach(
 193                 type -> packageHandlers.get(type).installHandler = v);
 194         return this;
 195     }
 196 
 197     public PackageTest setPackageUnpacker(
 198             BiFunction<JPackageCommand, Path, Path> v) {
 199         currentTypes.forEach(type -> packageHandlers.get(type).unpackHandler = v);
 200         return this;
 201     }
 202 
 203     public PackageTest setPackageUninstaller(Consumer<JPackageCommand> v) {
 204         currentTypes.forEach(
 205                 type -> packageHandlers.get(type).uninstallHandler = v);
 206         return this;
 207     }
 208 
 209     static void withTestFileAssociationsFile(FileAssociations fa,
 210             ThrowingConsumer<Path> consumer) {
 211         final Path testFileDefaultName = Path.of("test" + fa.getSuffix());
 212         TKit.withTempFile(testFileDefaultName, testFile -> {

 213             if (TKit.isLinux()) {
 214                 LinuxHelper.initFileAssociationsTestFile(testFile);
 215             }
 216             consumer.accept(testFile);
 217         });
 218     }
 219 
 220     PackageTest addHelloAppFileAssociationsVerifier(FileAssociations fa,
 221             String... faLauncherDefaultArgs) {
 222 
 223         // Setup test app to have valid jpackage command line before
 224         // running check of type of environment.
 225         addHelloAppInitializer(null);
 226 
 227         String noActionMsg = "Not running file associations test";
 228         if (GraphicsEnvironment.isHeadless()) {
 229             TKit.trace(String.format(
 230                     "%s because running in headless environment", noActionMsg));
 231             return this;
 232         }
 233 
 234         addInstallVerifier(cmd -> {
 235             if (cmd.isFakeRuntime(noActionMsg) || cmd.isPackageUnpacked(noActionMsg)) {
 236                 return;
 237             }
 238 
 239             withTestFileAssociationsFile(fa, testFile -> {
 240                 testFile = testFile.toAbsolutePath().normalize();
 241 
 242                 final Path appOutput = testFile.getParent()
 243                         .resolve(HelloApp.OUTPUT_FILENAME);
 244                 Files.deleteIfExists(appOutput);
 245 
 246                 TKit.trace(String.format("Use desktop to open [%s] file",
 247                         testFile));
 248                 Desktop.getDesktop().open(testFile.toFile());
 249                 TKit.waitForFileCreated(appOutput, 7);
 250 
 251                 List<String> expectedArgs = new ArrayList<>(List.of(
 252                         faLauncherDefaultArgs));
 253                 expectedArgs.add(testFile.toString());
 254 
 255                 // Wait a little bit after file has been created to
 256                 // make sure there are no pending writes into it.
 257                 Thread.sleep(3000);
 258                 HelloApp.verifyOutputFile(appOutput, expectedArgs,
 259                         Collections.emptyMap());
 260             });
 261         });
 262 
 263         forTypes(PackageType.LINUX, () -> {
 264             LinuxHelper.addFileAssociationsVerifier(this, fa);
 265         });
 266 
 267         return this;
 268     }
 269 
 270     public PackageTest forTypes(Collection<PackageType> types, Runnable action) {
 271         Set<PackageType> oldTypes = Set.of(currentTypes.toArray(
 272                 PackageType[]::new));
 273         try {
 274             forTypes(types);
 275             action.run();
 276         } finally {
 277             forTypes(oldTypes);
 278         }
 279         return this;
 280     }
 281 
 282     public PackageTest forTypes(PackageType type, Runnable action) {
 283         return forTypes(List.of(type), action);
 284     }
 285 
 286     public PackageTest notForTypes(Collection<PackageType> types, Runnable action) {
 287         Set<PackageType> workset = new HashSet<>(currentTypes);
 288         workset.removeAll(types);
 289         return forTypes(workset, action);
 290     }
 291 
 292     public PackageTest notForTypes(PackageType type, Runnable action) {
 293         return notForTypes(List.of(type), action);
 294     }
 295 
 296     public PackageTest configureHelloApp() {
 297         return configureHelloApp(null);
 298     }
 299 
 300     public PackageTest configureHelloApp(String javaAppDesc) {
 301         addHelloAppInitializer(javaAppDesc);

 302         addInstallVerifier(HelloApp::executeLauncherAndVerifyOutput);
 303         return this;
 304     }
 305 
 306     public final static class Group extends RunnablePackageTest {
 307         public Group(PackageTest... tests) {
 308             handlers = Stream.of(tests)
 309                     .map(PackageTest::createPackageTypeHandlers)
 310                     .flatMap(List<Consumer<Action>>::stream)
 311                     .collect(Collectors.toUnmodifiableList());
 312         }
 313 
 314         @Override
 315         protected void runAction(Action... action) {
 316             if (Set.of(action).contains(Action.UNINSTALL)) {
 317                 ListIterator<Consumer<Action>> listIterator = handlers.listIterator(
 318                         handlers.size());
 319                 while (listIterator.hasPrevious()) {
 320                     var handler = listIterator.previous();
 321                     List.of(action).forEach(handler::accept);
 322                 }
 323             } else {
 324                 handlers.forEach(handler -> List.of(action).forEach(handler::accept));
 325             }
 326         }
 327 
 328         private final List<Consumer<Action>> handlers;
 329     }
 330 
 331     final static class PackageHandlers {
 332         Consumer<JPackageCommand> installHandler;
 333         Consumer<JPackageCommand> uninstallHandler;
 334         BiFunction<JPackageCommand, Path, Path> unpackHandler;
 335     }
 336 
 337     @Override
 338     protected void runActions(List<Action[]> actions) {
 339         createPackageTypeHandlers().forEach(
 340                 handler -> actions.forEach(
 341                         action -> List.of(action).forEach(handler::accept)));
 342     }
 343 
 344     @Override
 345     protected void runAction(Action... action) {
 346         throw new UnsupportedOperationException();
 347     }
 348 
 349     private void addHelloAppInitializer(String javaAppDesc) {
 350         addInitializer(
 351                 cmd -> new HelloApp(JavaAppDesc.parse(javaAppDesc)).addTo(cmd),
 352                 "HelloApp");
 353     }
 354 
 355     private List<Consumer<Action>> createPackageTypeHandlers() {
 356         return PackageType.NATIVE.stream()
 357                 .map(type -> {
 358                     Handler handler = handlers.entrySet().stream()
 359                         .filter(entry -> !entry.getValue().isVoid())
 360                         .filter(entry -> entry.getKey() == type)
 361                         .map(entry -> entry.getValue())
 362                         .findAny().orElse(null);
 363                     Map.Entry<PackageType, Handler> result = null;
 364                     if (handler != null) {
 365                         result = Map.entry(type, handler);
 366                     }
 367                     return result;
 368                 })
 369                 .filter(Objects::nonNull)
 370                 .map(entry -> createPackageTypeHandler(
 371                         entry.getKey(), entry.getValue()))
 372                 .collect(Collectors.toList());
 373     }
 374 
 375     private Consumer<Action> createPackageTypeHandler(
 376             PackageType type, Handler handler) {
 377         return ThrowingConsumer.toConsumer(new ThrowingConsumer<Action>() {
 378             @Override
 379             public void accept(Action action) throws Throwable {
 380                 if (action == Action.FINALIZE) {
 381                     if (unpackDir != null && Files.isDirectory(unpackDir)
 382                             && !unpackDir.startsWith(TKit.workDir())) {
 383                         TKit.deleteDirectoryRecursive(unpackDir);
 384                     }
 385                 }
 386 
 387                 if (aborted) {

 388                     return;
 389                 }
 390 
 391                 final JPackageCommand curCmd;
 392                 if (Set.of(Action.INITIALIZE, Action.CREATE).contains(action)) {
 393                     curCmd = cmd;
 394                 } else {
 395                     curCmd = cmd.createImmutableCopy();

 396                 }
 397 
 398                 switch (action) {
 399                     case UNPACK: {
 400                         var handler = packageHandlers.get(type).unpackHandler;
 401                         if (!(aborted = (handler == null))) {
 402                             unpackDir = TKit.createTempDirectory(
 403                                             String.format("unpacked-%s",
 404                                                     type.getName()));
 405                             unpackDir = handler.apply(cmd, unpackDir);
 406                             cmd.setUnpackedPackageLocation(unpackDir);
 407                         }
 408                         break;
 409                     }

 410 
 411                     case INSTALL: {
 412                         var handler = packageHandlers.get(type).installHandler;
 413                         if (!(aborted = (handler == null))) {
 414                             handler.accept(curCmd);
 415                         }
 416                         break;
 417                     }
 418 
 419                     case UNINSTALL: {
 420                         var handler = packageHandlers.get(type).uninstallHandler;
 421                         if (!(aborted = (handler == null))) {
 422                             handler.accept(curCmd);
 423                         }
 424                         break;
 425                     }
 426 
 427                     case CREATE:
 428                         handler.accept(action, curCmd);
 429                         aborted = (expectedJPackageExitCode != 0);
 430                         return;
 431 
 432                     default:
 433                         handler.accept(action, curCmd);
 434                         break;
 435                 }
 436 
 437                 if (aborted) {
 438                     TKit.trace(
 439                             String.format("Aborted [%s] action of %s command",
 440                                     action, cmd.getPrintableCommandLine()));
 441                 }
 442             }
 443 
 444             private Path unpackDir;
 445             private boolean aborted;
 446             private final JPackageCommand cmd = Functional.identity(() -> {
 447                 JPackageCommand result = new JPackageCommand();
 448                 result.setDefaultInputOutput().setDefaultAppName();
 449                 if (BUNDLE_OUTPUT_DIR != null) {
 450                     result.setArgumentValue("--dest", BUNDLE_OUTPUT_DIR.toString());
 451                 }
 452                 type.applyTo(result);
 453                 return result;
 454             }).get();
 455         });
 456     }
 457 
 458     private class Handler implements BiConsumer<Action, JPackageCommand> {
 459 
 460         Handler() {
 461             initializers = new ArrayList<>();
 462             bundleVerifiers = new ArrayList<>();
 463             installVerifiers = new ArrayList<>();
 464             uninstallVerifiers = new ArrayList<>();
 465         }
 466 
 467         boolean isVoid() {
 468             return initializers.isEmpty();
 469         }
 470 
 471         void addInitializer(Consumer<JPackageCommand> v) {
 472             initializers.add(v);
 473         }
 474 
 475         void addBundleVerifier(BiConsumer<JPackageCommand, Executor.Result> v) {
 476             bundleVerifiers.add(v);
 477         }
 478 
 479         void addInstallVerifier(Consumer<JPackageCommand> v) {
 480             installVerifiers.add(v);
 481         }
 482 
 483         void addUninstallVerifier(Consumer<JPackageCommand> v) {
 484             uninstallVerifiers.add(v);
 485         }
 486 
 487         @Override
 488         public void accept(Action action, JPackageCommand cmd) {
 489             switch (action) {
 490                 case INITIALIZE:
 491                     initializers.forEach(v -> v.accept(cmd));
 492                     if (cmd.isImagePackageType()) {
 493                         throw new UnsupportedOperationException();
 494                     }
 495                     cmd.executePrerequisiteActions();
 496                     break;
 497 

 498                 case CREATE:
 499                     Executor.Result result = cmd.execute(expectedJPackageExitCode);

 500                     if (expectedJPackageExitCode == 0) {
 501                         TKit.assertFileExists(cmd.outputBundle());
 502                     } else {
 503                         TKit.assertPathExists(cmd.outputBundle(), false);
 504                     }
 505                     verifyPackageBundle(cmd, result);
 506                     break;
 507 
 508                 case VERIFY_INSTALL:
 509                     if (expectedJPackageExitCode == 0) {
 510                         verifyPackageInstalled(cmd);
 511                     }
 512                     break;
 513 
 514                 case VERIFY_UNINSTALL:
 515                     if (expectedJPackageExitCode == 0) {
 516                         verifyPackageUninstalled(cmd);
 517                     }
 518                     break;
 519             }
 520         }
 521 
 522         private void verifyPackageBundle(JPackageCommand cmd,
 523                 Executor.Result result) {
 524             if (expectedJPackageExitCode == 0) {
 525                 if (PackageType.LINUX.contains(cmd.packageType())) {
 526                     LinuxHelper.verifyPackageBundleEssential(cmd);
 527                 }
 528             }
 529             bundleVerifiers.forEach(v -> v.accept(cmd, result));
 530         }
 531 
 532         private void verifyPackageInstalled(JPackageCommand cmd) {
 533             final String formatString;
 534             if (cmd.isPackageUnpacked()) {
 535                 formatString = "Verify unpacked: %s";
 536             } else {
 537                 formatString = "Verify installed: %s";
 538             }
 539             TKit.trace(String.format(formatString, cmd.getPrintableCommandLine()));
 540 
 541             TKit.assertDirectoryExists(cmd.appRuntimeDirectory());
 542             if (!cmd.isRuntime()) {
 543                 TKit.assertExecutableFileExists(cmd.appLauncherPath());
 544 
 545                 if (PackageType.WINDOWS.contains(cmd.packageType())
 546                         && !cmd.isPackageUnpacked(
 547                                 "Not verifying desktop integration")) {
 548                     new WindowsHelper.DesktopIntegrationVerifier(cmd);
 549                 }
 550             }
 551 
 552             TKit.assertPathExists(AppImageFile.getPathInAppImage(
 553                     cmd.appInstallationDirectory()), false);
 554 
 555             installVerifiers.forEach(v -> v.accept(cmd));
 556         }
 557 
 558         private void verifyPackageUninstalled(JPackageCommand cmd) {
 559             TKit.trace(String.format("Verify uninstalled: %s",
 560                     cmd.getPrintableCommandLine()));
 561             if (!cmd.isRuntime()) {
 562                 TKit.assertPathExists(cmd.appLauncherPath(), false);
 563 
 564                 if (PackageType.WINDOWS.contains(cmd.packageType())) {
 565                     new WindowsHelper.DesktopIntegrationVerifier(cmd);
 566                 }
 567             }
 568 
 569             TKit.assertPathExists(cmd.appInstallationDirectory(), false);
 570 
 571             uninstallVerifiers.forEach(v -> v.accept(cmd));
 572         }
 573 

 574         private final List<Consumer<JPackageCommand>> initializers;
 575         private final List<BiConsumer<JPackageCommand, Executor.Result>> bundleVerifiers;
 576         private final List<Consumer<JPackageCommand>> installVerifiers;
 577         private final List<Consumer<JPackageCommand>> uninstallVerifiers;
 578     }
 579 
 580     private static Map<PackageType, PackageHandlers> createDefaultPackageHandlers() {
 581         HashMap<PackageType, PackageHandlers> handlers = new HashMap<>();
 582         if (TKit.isLinux()) {
 583             handlers.put(PackageType.LINUX_DEB, LinuxHelper.createDebPackageHandlers());
 584             handlers.put(PackageType.LINUX_RPM, LinuxHelper.createRpmPackageHandlers());
 585         }
 586 
 587         if (TKit.isWindows()) {
 588             handlers.put(PackageType.WIN_MSI, WindowsHelper.createMsiPackageHandlers());
 589             handlers.put(PackageType.WIN_EXE, WindowsHelper.createExePackageHandlers());
 590         }
 591 
 592         if (TKit.isOSX()) {
 593             handlers.put(PackageType.MAC_DMG,  MacHelper.createDmgPackageHandlers());
 594             handlers.put(PackageType.MAC_PKG,  MacHelper.createPkgPackageHandlers());
 595         }
 596 
 597         return handlers;
 598     }
 599 
 600     private Collection<PackageType> currentTypes;
 601     private Set<PackageType> excludeTypes;
 602     private int expectedJPackageExitCode;
 603     private Map<PackageType, Handler> handlers;
 604     private Set<String> namedInitializers;
 605     private Map<PackageType, PackageHandlers> packageHandlers;

















 606 
 607     private final static File BUNDLE_OUTPUT_DIR;






 608 
 609     static {
 610         final String propertyName = "output";
 611         String val = TKit.getConfigProperty(propertyName);
 612         if (val == null) {
 613             BUNDLE_OUTPUT_DIR = null;
 614         } else {
 615             BUNDLE_OUTPUT_DIR = new File(val).getAbsoluteFile();
 616 
 617             if (!BUNDLE_OUTPUT_DIR.isDirectory()) {
 618                 throw new IllegalArgumentException(String.format("Invalid value of %s sytem property: [%s]. Should be existing directory",

 619                         TKit.getConfigPropertyName(propertyName),
 620                         BUNDLE_OUTPUT_DIR));
 621             }
 622         }











 623     }
 624 }
< prev index next >