1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.oracle.tools.packager.windows;
  27 
  28 import com.oracle.tools.packager.AbstractBundler;
  29 import com.oracle.tools.packager.Bundler;
  30 import com.oracle.tools.packager.BundlerParamInfo;
  31 import com.oracle.tools.packager.ConfigException;
  32 import com.oracle.tools.packager.Log;
  33 import com.oracle.tools.packager.RelativeFileSet;
  34 import com.oracle.tools.packager.UnsupportedPlatformException;
  35 import org.junit.After;
  36 import org.junit.Assume;
  37 import org.junit.Before;
  38 import org.junit.BeforeClass;
  39 import org.junit.Test;
  40 
  41 import java.io.File;
  42 import java.io.IOException;
  43 import java.util.ArrayList;
  44 import java.util.Arrays;
  45 import java.util.Collection;
  46 import java.util.HashMap;
  47 import java.util.HashSet;
  48 import java.util.List;
  49 import java.util.Map;
  50 import java.util.Set;
  51 import java.util.TreeMap;
  52 
  53 import static com.oracle.tools.packager.StandardBundlerParam.*;
  54 import static com.oracle.tools.packager.windows.WinAppBundler.ICON_ICO;
  55 import static com.oracle.tools.packager.windows.WindowsBundlerParam.WIN_RUNTIME;
  56 import static com.oracle.tools.packager.windows.WindowsBundlerParam.MENU_GROUP;
  57 import static org.junit.Assert.*;
  58 
  59 public class WinExeBundlerTest {
  60 
  61     static File tmpBase;
  62     static File workDir;
  63     static File appResourcesDir;
  64     static File fakeMainJar;
  65     static Set<File> appResources;
  66     static boolean retain = false;
  67 
  68     @BeforeClass
  69     public static void prepareApp() {
  70         // only run on windows
  71         Assume.assumeTrue(System.getProperty("os.name").toLowerCase().startsWith("win"));
  72 
  73         // only run if we have InnoSetup installed
  74         Assume.assumeNotNull(WinExeBundler.TOOL_INNO_SETUP_COMPILER_EXECUTABLE.fetchFrom(new HashMap<>()));
  75 
  76         Log.setLogger(new Log.Logger(true));
  77 
  78         retain = Boolean.parseBoolean(System.getProperty("RETAIN_PACKAGER_TESTS"));
  79 
  80         workDir = new File("build/tmp/tests", "winexe");
  81         appResourcesDir = new File("build/tmp/tests", "appResources");
  82         fakeMainJar = new File(appResourcesDir, "mainApp.jar");
  83 
  84         appResources = new HashSet<>(Arrays.asList(fakeMainJar,
  85                 new File(appResourcesDir, "LICENSE"),
  86                 new File(appResourcesDir, "LICENSE2")
  87         ));
  88     }
  89 
  90     @Before
  91     public void createTmpDir() throws IOException {
  92         if (retain) {
  93             tmpBase = new File("build/tmp/tests/winexe");
  94         } else {
  95             tmpBase = BUILD_ROOT.fetchFrom(new TreeMap<>());
  96         }
  97         tmpBase.mkdir();
  98     }
  99 
 100     @After
 101     public void maybeCleanupTmpDir() {
 102         if (!retain) {
 103             attemptDelete(tmpBase);
 104         }
 105     }
 106 
 107     private void attemptDelete(File tmpBase) {
 108         if (tmpBase.isDirectory()) {
 109             File[] children = tmpBase.listFiles();
 110             if (children != null) {
 111                 for (File f : children) {
 112                     attemptDelete(f);
 113                 }
 114             }
 115         }
 116         boolean success;
 117         try {
 118             success = !tmpBase.exists() || tmpBase.delete();
 119         } catch (SecurityException se) {
 120             success = false;
 121         }
 122         if (!success) {
 123             System.err.println("Could not clean up " + tmpBase.toString());
 124         }
 125     }
 126 
 127     /**
 128      * See if smoke comes out
 129      */
 130     @Test
 131     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
 132         Bundler bundler = new WinExeBundler();
 133 
 134         assertNotNull(bundler.getName());
 135         assertNotNull(bundler.getID());
 136         assertNotNull(bundler.getDescription());
 137         //assertNotNull(bundler.getBundleParameters());
 138 
 139         Map<String, Object> bundleParams = new HashMap<>();
 140 
 141         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 142 
 143         bundleParams.put(APP_NAME.getID(), "Smoke Test");
 144         bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle");
 145         bundleParams.put(PREFERENCES_ID.getID(), "the/really/long/preferences/id");
 146         bundleParams.put(MAIN_JAR.getID(),
 147                 new RelativeFileSet(fakeMainJar.getParentFile(),
 148                         new HashSet<>(Arrays.asList(fakeMainJar)))
 149         );
 150         bundleParams.put(CLASSPATH.getID(), "mainApp.jar");
 151         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 152         bundleParams.put(LICENSE_FILE.getID(), Arrays.asList("LICENSE", "LICENSE2"));
 153         bundleParams.put(COPYRIGHT.getID(), "Copyright(c) 2014 the testers \"who like to break stuff\"");
 154         bundleParams.put(VERBOSE.getID(), true);
 155 
 156         boolean valid = bundler.validate(bundleParams);
 157         assertTrue(valid);
 158 
 159         File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
 160         System.err.println("Bundle at - " + result);
 161         assertNotNull(result);
 162         assertTrue(result.exists());
 163     }
 164 
 165     /**
 166      * The bare minimum configuration needed to make it work
 167      * <ul>
 168      *     <li>Where to build it</li>
 169      *     <li>The jar containing the application (with a main-class attribute)</li>
 170      * </ul>
 171      * 
 172      * All other values will be driven off of those two values.
 173      */
 174     @Test
 175     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
 176         Bundler bundler = new WinExeBundler();
 177 
 178         Map<String, Object> bundleParams = new HashMap<>();
 179 
 180         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 181 
 182         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 183 
 184         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
 185         System.err.println("Bundle at - " + output);
 186         assertNotNull(output);
 187         assertTrue(output.exists());
 188     }
 189 
 190     /**
 191      * Test a misconfiguration where the iscc tools are misconfigured.
 192      */
 193     @Test
 194     public void configISSCBad() throws IOException, ConfigException, UnsupportedPlatformException {
 195         Bundler bundler = new WinExeBundler();
 196 
 197         Map<String, Object> bundleParams = new HashMap<>();
 198 
 199         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 200         bundleParams.put(VERBOSE.getID(), true);
 201 
 202         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 203 
 204         bundleParams.put(WinExeBundler.TOOL_INNO_SETUP_COMPILER_EXECUTABLE.getID(), "c:\\MissingTool.exe");
 205 
 206         File output = bundler.execute(bundleParams, new File(workDir, "BadISSC"));
 207         assertNull(output);
 208     }
 209 
 210     /**
 211      * Test a misconfiguration where the iscc tools are misconfigured.
 212      */
 213     @Test
 214     public void configISSCNull() throws IOException, ConfigException, UnsupportedPlatformException {
 215         Bundler bundler = new WinExeBundler();
 216 
 217         Map<String, Object> bundleParams = new HashMap<>();
 218 
 219         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 220         bundleParams.put(VERBOSE.getID(), true);
 221 
 222         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 223 
 224         bundleParams.put(WinExeBundler.TOOL_INNO_SETUP_COMPILER_EXECUTABLE.getID(), null);
 225 
 226         File output = bundler.execute(bundleParams, new File(workDir, "NullISSC"));
 227         assertNull(output);
 228     }
 229 
 230     @Test(expected = ConfigException.class)
 231     public void invalidLicenseFile() throws ConfigException, UnsupportedPlatformException {
 232         Bundler bundler = new WinExeBundler();
 233 
 234         Map<String, Object> bundleParams = new HashMap<>();
 235 
 236         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 237 
 238         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 239         bundleParams.put(LICENSE_FILE.getID(), "BOGUS_LICENSE");
 240 
 241         bundler.validate(bundleParams);
 242     }
 243 
 244     @Test(expected = ConfigException.class)
 245     public void invalidCopyright() throws ConfigException, UnsupportedPlatformException {
 246         Bundler bundler = new WinExeBundler();
 247 
 248         Map<String, Object> bundleParams = new HashMap<>();
 249 
 250         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 251 
 252         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 253         bundleParams.put(COPYRIGHT.getID(), "Copyright (c) 2014 The Testers\nWho like to break stuff");
 254 
 255         bundler.validate(bundleParams);
 256     }
 257 
 258     @Test(expected = ConfigException.class)
 259     public void longCopyright() throws ConfigException, UnsupportedPlatformException {
 260         Bundler bundler = new WinExeBundler();
 261 
 262         Map<String, Object> bundleParams = new HashMap<>();
 263 
 264         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 265 
 266         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 267         bundleParams.put(COPYRIGHT.getID(), "Copyright (c) 2014 Way to many people to name because there are just so may of them I don't know where to start but I will just start with Alice, Bob, Charlie, Dave, Eve, well, there are just too many to say.");
 268 
 269         bundler.validate(bundleParams);
 270     }
 271 
 272     @Test
 273     public void configureEverything() throws Exception {
 274         AbstractBundler bundler = new WinExeBundler();
 275         Collection<BundlerParamInfo<?>> parameters = bundler.getBundleParameters();
 276 
 277         Map<String, Object> bundleParams = new HashMap<>();
 278 
 279         bundleParams.put(APP_NAME.getID(), "Everything App Name");
 280         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 281         bundleParams.put(ARGUMENTS.getID(), Arrays.asList("He Said", "She Said"));
 282         bundleParams.put(CLASSPATH.getID(), "mainApp.jar");
 283         bundleParams.put(ICON_ICO.getID(), new File(appResourcesDir, "javalogo_white_48.ico"));
 284         bundleParams.put(JVM_OPTIONS.getID(), "-Xms128M");
 285         bundleParams.put(JVM_PROPERTIES.getID(), "everything.jvm.property=everything.jvm.property.value");
 286         bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle");
 287         bundleParams.put(MAIN_JAR.getID(), "mainApp.jar");
 288         bundleParams.put(PREFERENCES_ID.getID(), "everything/preferences/id");
 289         bundleParams.put(PRELOADER_CLASS.getID(), "hello.HelloPreloader");
 290         bundleParams.put(USER_JVM_OPTIONS.getID(), "-Xmx=256M\n");
 291         bundleParams.put(VERSION.getID(), "1.2.3.4");
 292         bundleParams.put(WIN_RUNTIME.getID(), System.getProperty("java.home"));
 293 
 294         bundleParams.put(COPYRIGHT.getID(), "(C) 2014 Everything Copyright");
 295         bundleParams.put(DESCRIPTION.getID(), "Everything Description");
 296         bundleParams.put(LICENSE_FILE.getID(), "LICENSE");
 297         bundleParams.put(MENU_GROUP.getID(), "EverythingMenuGroup");
 298         bundleParams.put(MENU_HINT.getID(), true);
 299 //                RUN_AT_STARTUP,
 300         bundleParams.put(SHORTCUT_HINT.getID(), true);
 301 //                SERVICE_HINT,
 302 //                START_ON_INSTALL,
 303 //                STOP_ON_UNINSTALL,
 304         bundleParams.put(SYSTEM_WIDE.getID(), false);
 305         bundleParams.put(TITLE.getID(), "Everything Title");
 306         bundleParams.put(VENDOR.getID(), "Packager Tests");
 307 
 308         // assert they are set
 309         for (BundlerParamInfo bi :parameters) {
 310             assertTrue("Bundle args should contain " + bi.getID(), bundleParams.containsKey(bi.getID()));
 311         }
 312 
 313         // and only those are set
 314         bundleParamLoop:
 315         for (String s :bundleParams.keySet()) {
 316             for (BundlerParamInfo<?> bpi : parameters) {
 317                 if (s.equals(bpi.getID())) {
 318                     continue bundleParamLoop;
 319                 }
 320             }
 321             fail("Enumerated parameters does not contain " + s);
 322         }
 323 
 324         // assert they resolve
 325         for (BundlerParamInfo bi :parameters) {
 326             bi.fetchFrom(bundleParams);
 327         }
 328 
 329         // add verbose now that we are done scoping out parameters
 330         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 331         bundleParams.put(VERBOSE.getID(), true);
 332 
 333         // assert it validates
 334         boolean valid = bundler.validate(bundleParams);
 335         assertTrue(valid);
 336 
 337         // only run the bundle with full tests
 338         Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST")));
 339 
 340         File result = bundler.execute(bundleParams, new File(workDir, "everything"));
 341         System.err.println("Bundle at - " + result);
 342         assertNotNull(result);
 343         assertTrue(result.exists());
 344     }
 345 
 346 
 347     /**
 348      * multiple launchers
 349      */
 350     @Test
 351     public void twoLaunchersTest() throws IOException, ConfigException, UnsupportedPlatformException {
 352         Bundler bundler = new WinExeBundler();
 353 
 354         assertNotNull(bundler.getName());
 355         assertNotNull(bundler.getID());
 356         assertNotNull(bundler.getDescription());
 357         //assertNotNull(bundler.getBundleParameters());
 358 
 359         Map<String, Object> bundleParams = new HashMap<>();
 360 
 361         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 362 
 363         bundleParams.put(APP_NAME.getID(), "Two Launchers Test");
 364         bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle");
 365         bundleParams.put(PREFERENCES_ID.getID(), "the/really/long/preferences/id");
 366         bundleParams.put(MAIN_JAR.getID(),
 367                 new RelativeFileSet(fakeMainJar.getParentFile(),
 368                         new HashSet<>(Arrays.asList(fakeMainJar)))
 369         );
 370         bundleParams.put(CLASSPATH.getID(), "mainApp.jar");
 371         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 372         bundleParams.put(VERBOSE.getID(), true);
 373 
 374         List<Map<String, ? super Object>> secondaryLaunchers = new ArrayList<>();
 375         for (String name : new String[] {"Fire", "More Fire"}) {
 376             Map<String, ? super Object> launcher = new HashMap<>();
 377             launcher.put(APP_NAME.getID(), name);
 378             launcher.put(PREFERENCES_ID.getID(), "secondary/launcher/" + name);
 379             secondaryLaunchers.add(launcher);
 380         }
 381         bundleParams.put(SECONDARY_LAUNCHERS.getID(), secondaryLaunchers);
 382 
 383         boolean valid = bundler.validate(bundleParams);
 384         assertTrue(valid);
 385 
 386         File output = bundler.execute(bundleParams, new File(workDir, "launchers"));
 387         assertNotNull(output);
 388         assertTrue(output.exists());
 389     }
 390 
 391     /**
 392      * Set File Association
 393      */
 394     @Test
 395     public void testFileAssociation()
 396         throws IOException, ConfigException, UnsupportedPlatformException
 397     {
 398         // only run the bundle with full tests
 399         Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST")));
 400 
 401         testFileAssociation("FASmoke 1", "Bogus File", "bogus", "application/x-vnd.test-bogus",
 402                             new File(appResourcesDir, "small.ico"), false);
 403     }
 404 
 405     @Test
 406     public void testFileAssociationWithNullExtension()
 407         throws IOException, ConfigException, UnsupportedPlatformException
 408     {
 409         // association with no extension is still valid case (see RT-38625)
 410         testFileAssociation("FASmoke null", "Bogus File", null, "application/x-vnd.test-bogus",
 411                             new File(appResourcesDir, "small.ico"), true);
 412     }
 413 
 414     @Test
 415     public void testFileAssociationWithMultipleExtension()
 416             throws IOException, ConfigException, UnsupportedPlatformException
 417     {
 418         // only run the bundle with full tests
 419         Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST")));
 420 
 421         testFileAssociation("FASmoke ME", "Bogus File", "bogus fake", "application/x-vnd.test-bogus",
 422                 new File(appResourcesDir, "small.ico"), false);
 423     }
 424 
 425     @Test
 426     public void testMultipleFileAssociation()
 427             throws IOException, ConfigException, UnsupportedPlatformException
 428     {
 429         // only run the bundle with full tests
 430         Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST")));
 431 
 432         testFileAssociationMultiples("FASmoke MA",
 433                 new String[]{"Bogus File", "Fake file"},
 434                 new String[]{"bogus", "fake"},
 435                 new String[]{"application/x-vnd.test-bogus", "application/x-vnd.test-fake"},
 436                 new File[]{new File(appResourcesDir, "small.ico"), new File(appResourcesDir, "small.ico")}, true);
 437     }
 438 
 439     @Test
 440     public void testMultipleFileAssociationWithMultipleExtension()
 441             throws IOException, ConfigException, UnsupportedPlatformException
 442     {
 443         // association with no extension is still valid case (see RT-38625)
 444         testFileAssociationMultiples("FASmoke MAME",
 445                 new String[]{"Bogus File", "Fake file"},
 446                 new String[]{"bogus boguser", "fake faker"},
 447                 new String[]{"application/x-vnd.test-bogus", "application/x-vnd.test-fake"},
 448                 new File[]{new File(appResourcesDir, "small.ico"), new File(appResourcesDir, "small.ico")}, false);
 449     }
 450 
 451     private void testFileAssociation(String appName, String description, String extensions,
 452                                      String contentType, File icon, boolean systemWide)
 453             throws IOException, ConfigException, UnsupportedPlatformException
 454     {
 455         testFileAssociationMultiples(appName, new String[] {description}, new String[] {extensions},
 456                 new String[] {contentType}, new File[] {icon}, systemWide);
 457     }
 458 
 459     private void testFileAssociationMultiples(String appName, String[] description, String[] extensions,
 460                                               String[] contentType, File[] icon, boolean systemWide)
 461             throws IOException, ConfigException, UnsupportedPlatformException
 462     {
 463         assertEquals("Sanity: description same length as extensions", description.length, extensions.length);
 464         assertEquals("Sanity: extensions same length as contentType", extensions.length, contentType.length);
 465         assertEquals("Sanity: contentType same length as icon", contentType.length, icon.length);
 466 
 467         AbstractBundler bundler = new WinExeBundler();
 468 
 469         assertNotNull(bundler.getName());
 470         assertNotNull(bundler.getID());
 471         assertNotNull(bundler.getDescription());
 472         //assertNotNull(bundler.getBundleParameters());
 473 
 474         Map<String, Object> bundleParams = new HashMap<>();
 475 
 476         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 477 
 478         bundleParams.put(APP_NAME.getID(), appName);
 479         bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle");
 480         bundleParams.put(MAIN_JAR.getID(),
 481                 new RelativeFileSet(fakeMainJar.getParentFile(),
 482                         new HashSet<>(Arrays.asList(fakeMainJar)))
 483         );
 484         bundleParams.put(CLASSPATH.getID(), "mainApp.jar");
 485         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 486         bundleParams.put(VERBOSE.getID(), true);
 487         bundleParams.put(SYSTEM_WIDE.getID(), systemWide);
 488         bundleParams.put(VENDOR.getID(), "Packager Tests");
 489 
 490         List<Map<String, Object>> associations = new ArrayList<>();
 491 
 492         for (int i = 0; i < description.length; i++) {
 493             Map<String, Object> fileAssociation = new HashMap<>();
 494             fileAssociation.put(FA_DESCRIPTION.getID(), description[i]);
 495             fileAssociation.put(FA_EXTENSIONS.getID(), extensions[i]);
 496             fileAssociation.put(FA_CONTENT_TYPE.getID(), contentType[i]);
 497             fileAssociation.put(FA_ICON.getID(), icon[i]);
 498 
 499             associations.add(fileAssociation);
 500         }
 501 
 502         bundleParams.put(FILE_ASSOCIATIONS.getID(), associations);
 503 
 504         boolean valid = bundler.validate(bundleParams);
 505         assertTrue(valid);
 506 
 507         File result = bundler.execute(bundleParams, new File(workDir, APP_FS_NAME.fetchFrom(bundleParams)));
 508         System.err.println("Bundle at - " + result);
 509         assertNotNull(result);
 510         assertTrue(result.exists());
 511     }
 512 }