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.mac;
  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.IOUtils;
  33 import com.oracle.tools.packager.Log;
  34 import com.oracle.tools.packager.RelativeFileSet;
  35 import com.oracle.tools.packager.UnsupportedPlatformException;
  36 import org.junit.After;
  37 import org.junit.Assume;
  38 import org.junit.Before;
  39 import org.junit.BeforeClass;
  40 import org.junit.Test;
  41 
  42 import java.io.File;
  43 import java.io.IOException;
  44 import java.nio.file.Files;
  45 import java.util.Arrays;
  46 import java.util.Collection;
  47 import java.util.HashMap;
  48 import java.util.HashSet;
  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.mac.MacAppBundler.*;
  55 import static com.oracle.tools.packager.mac.MacBaseInstallerBundler.MAC_APP_IMAGE;
  56 import static com.oracle.tools.packager.mac.MacBaseInstallerBundler.SIGNING_KEYCHAIN;
  57 import static com.oracle.tools.packager.mac.MacPkgBundler.DEVELOPER_ID_INSTALLER_SIGNING_KEY;
  58 import static com.oracle.tools.packager.mac.MacPkgBundler.INSTALLER_SUFFIX;
  59 import static org.junit.Assert.*;
  60 
  61 public class MacPkgBundlerTest {
  62 
  63     static final int MIN_SIZE=0x100000; // 1MiB
  64 
  65     static File tmpBase;
  66     static File workDir;
  67     static File appResourcesDir;
  68     static File fakeMainJar;
  69     static File hdpiIcon;
  70     static String runtimeJdk;
  71     static Set<File> appResources;
  72     static boolean retain = false;
  73     static boolean signingKeysPresent = false;
  74     
  75     static final File FAKE_CERT_ROOT = new File("build/tmp/tests/cert/");
  76 
  77     @BeforeClass
  78     public static void prepareApp() {
  79         // only run on mac
  80         Assume.assumeTrue(System.getProperty("os.name").toLowerCase().contains("os x"));
  81 
  82         runtimeJdk = System.getenv("PACKAGER_JDK_ROOT");
  83 
  84         // and only if we have the correct JRE settings
  85         String jre = System.getProperty("java.home").toLowerCase();
  86         Assume.assumeTrue(runtimeJdk != null || jre.endsWith("/contents/home/jre") || jre.endsWith("/contents/home/jre"));
  87 
  88         Log.setLogger(new Log.Logger(true));
  89         Log.setDebug(true);
  90 
  91         retain = Boolean.parseBoolean(System.getProperty("RETAIN_PACKAGER_TESTS"));
  92 
  93         workDir = new File("build/tmp/tests", "macpkg");
  94         hdpiIcon = new File("build/tmp/tests", "GenericAppHiDPI.icns");
  95         appResourcesDir = new File("build/tmp/tests", "appResources");
  96         fakeMainJar = new File(appResourcesDir, "mainApp.jar");
  97 
  98         appResources = new HashSet<>(Arrays.asList(fakeMainJar,
  99                 new File(appResourcesDir, "LICENSE"),
 100                 new File(appResourcesDir, "LICENSE2")
 101         ));
 102 
 103         signingKeysPresent = DEVELOPER_ID_INSTALLER_SIGNING_KEY.fetchFrom(new TreeMap<>()) != null;
 104     }
 105 
 106     @Before
 107     public void createTmpDir() throws IOException {
 108         if (retain) {
 109             tmpBase = new File("build/tmp/tests/macpkg");
 110         } else {
 111             tmpBase = BUILD_ROOT.fetchFrom(new TreeMap<>());
 112         }
 113         tmpBase.mkdir();
 114     }
 115     
 116     public String createFakeCerts(Map<String, ? super Object> p) {
 117         File config = new File(FAKE_CERT_ROOT, "pkg-cert.cfg");
 118         config.getParentFile().mkdirs();
 119         try {
 120             // create the config file holding the key config
 121             Files.write(config.toPath(), Arrays.<String>asList("[ codesign ]",
 122                     "keyUsage=critical,digitalSignature",
 123                     "basicConstraints=critical,CA:false",
 124                     "extendedKeyUsage=critical,codeSigning",
 125                     "[ productbuild ]",
 126                     "basicConstraints=critical,CA:false",
 127                     "keyUsage=critical,digitalSignature",
 128                     "extendedKeyUsage=critical,1.2.840.113635.100.4.13",
 129                     "1.2.840.113635.100.6.1.14=critical,DER:0500"));
 130 
 131             // create the SSL keys
 132             ProcessBuilder pb = new ProcessBuilder("openssl", "req",
 133                     "-newkey", "rsa:2048",
 134                     "-nodes",
 135                     "-out", FAKE_CERT_ROOT + "/pkg.csr",
 136                     "-keyout", FAKE_CERT_ROOT + "/pkg.key",
 137                     "-subj", "/CN=Developer ID Application: Insecure Test Cert/OU=JavaFX Dev/O=Oracle/C=US");
 138             IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 139 
 140             // first, for the app
 141             // create the cert
 142             pb = new ProcessBuilder("openssl", "x509",
 143                     "-req",
 144                     "-days", "1",
 145                     "-in", FAKE_CERT_ROOT + "/pkg.csr",
 146                     "-signkey", FAKE_CERT_ROOT + "/pkg.key",
 147                     "-out", FAKE_CERT_ROOT + "/pkg-app.crt",
 148                     "-extfile", FAKE_CERT_ROOT + "/pkg.cnf",
 149                     "-extensions", "codesign");
 150             IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 151 
 152             // create and add it to the keychain
 153             pb = new ProcessBuilder("certtool",
 154                     "i", FAKE_CERT_ROOT + "/pkg-app.crt",
 155                     "k=" + FAKE_CERT_ROOT + "/pkg.keychain",
 156                     "r=" + FAKE_CERT_ROOT + "/pkg.key",
 157                     "c",
 158                     "p=");
 159             IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 160             
 161             // now for the pkg cert
 162             pb = new ProcessBuilder("openssl", "x509",
 163                         "-req",
 164                         "-days", "10",
 165                         "-in", FAKE_CERT_ROOT + "/pkg.csr",
 166                         "-signkey", FAKE_CERT_ROOT + "/pkg.key",
 167                         "-out", FAKE_CERT_ROOT + "/pkg-pkg.crt",
 168                         "-extfile",FAKE_CERT_ROOT + "/png.cnf",
 169                         "-extensions", "productbuild");
 170             IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 171 
 172             // create and add it to the keychain
 173             pb = new ProcessBuilder("certtool",
 174                     "i", FAKE_CERT_ROOT + "/pkg-pkg.crt",
 175                     "k=" + FAKE_CERT_ROOT + "/pkg.keychain",
 176                     "r=" + FAKE_CERT_ROOT + "/pkg.key");
 177             IOUtils.exec(pb, VERBOSE.fetchFrom(p));
 178             
 179             return FAKE_CERT_ROOT + "/pkg.keychain";
 180         } catch (IOException e) {
 181             e.printStackTrace();
 182         }
 183         
 184         return null;
 185     }
 186 
 187     @After
 188     public void maybeCleanupTmpDir() {
 189         if (!retain) {
 190             attemptDelete(tmpBase);
 191         }
 192         attemptDelete(FAKE_CERT_ROOT);
 193     }
 194 
 195     private void attemptDelete(File tmpBase) {
 196         if (tmpBase.isDirectory()) {
 197             File[] children = tmpBase.listFiles();
 198             if (children != null) {
 199                 for (File f : children) {
 200                     attemptDelete(f);
 201                 }
 202             }
 203         }
 204         boolean success;
 205         try {
 206             success = !tmpBase.exists() || tmpBase.delete();
 207         } catch (SecurityException se) {
 208             success = false;
 209         }
 210         if (!success) {
 211             System.err.println("Could not clean up " + tmpBase.toString());
 212         }
 213     }
 214 
 215     /**
 216      * See if smoke comes out
 217      */
 218     @Test
 219     public void smokeTest() throws IOException, ConfigException, UnsupportedPlatformException {
 220         AbstractBundler bundler = new MacPkgBundler();
 221 
 222         assertNotNull(bundler.getName());
 223         assertNotNull(bundler.getID());
 224         assertNotNull(bundler.getDescription());
 225         //assertNotNull(bundler.getBundleParameters());
 226 
 227         Map<String, Object> bundleParams = new HashMap<>();
 228 
 229         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 230 
 231         bundleParams.put(APP_NAME.getID(), "Smoke Test");
 232         bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle");
 233         bundleParams.put(PREFERENCES_ID.getID(), "the/really/long/preferences/id");
 234         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 235         bundleParams.put(MAIN_JAR.getID(),
 236                 new RelativeFileSet(fakeMainJar.getParentFile(),
 237                         new HashSet<>(Arrays.asList(fakeMainJar)))
 238         );
 239         bundleParams.put(CLASSPATH.getID(), "mainApp.jar");
 240         bundleParams.put(VERBOSE.getID(), true);
 241         bundleParams.put(LICENSE_FILE.getID(), Arrays.asList("LICENSE", "LICENSE2"));
 242         bundleParams.put(SIGN_BUNDLE.getID(), false); // force no signing
 243 
 244         if (runtimeJdk != null) {
 245             bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk);
 246         }
 247 
 248         boolean valid = bundler.validate(bundleParams);
 249         assertTrue(valid);
 250 
 251         File result = bundler.execute(bundleParams, new File(workDir, "smoke"));
 252         System.err.println("Bundle at - " + result);
 253         assertNotNull(result);
 254         assertTrue(result.exists());
 255         assertTrue(result.length() > MIN_SIZE);
 256     }
 257 
 258     /**
 259      * Build smoke test and mark it as quarantined, possibly signed
 260      */
 261     @Test
 262     public void quarantinedAppTest() throws IOException, ConfigException, UnsupportedPlatformException {
 263 
 264         AbstractBundler bundler = new MacPkgBundler();
 265 
 266         assertNotNull(bundler.getName());
 267         assertNotNull(bundler.getID());
 268         assertNotNull(bundler.getDescription());
 269         //assertNotNull(bundler.getBundleParameters());
 270 
 271         Map<String, Object> bundleParams = new HashMap<>();
 272 
 273         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 274 
 275         bundleParams.put(APP_NAME.getID(), "Quarantine App");
 276         bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle");
 277         bundleParams.put(PREFERENCES_ID.getID(), "the/really/long/preferences/id");
 278         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 279         bundleParams.put(VERBOSE.getID(), true);
 280 
 281         if (runtimeJdk != null) {
 282             bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk);
 283         }
 284 
 285         if (!signingKeysPresent) {
 286             String keychain = createFakeCerts(bundleParams);
 287             Assume.assumeNotNull(keychain);
 288             bundleParams.put(SIGNING_KEYCHAIN.getID(), keychain);
 289         }
 290 
 291         boolean valid = bundler.validate(bundleParams);
 292         assertTrue(valid);
 293 
 294         File result = bundler.execute(bundleParams, new File(workDir, "quarantine"));
 295         System.err.println("Bundle at - " + result);
 296         assertNotNull(result);
 297         assertTrue(result.exists());
 298         assertTrue(result.length() > MIN_SIZE);
 299         validateSignatures(result);
 300 
 301         // mark it as though it's been downloaded
 302         ProcessBuilder pb = new ProcessBuilder(
 303                 "xattr", "-w", "com.apple.quarantine",
 304                 "0000;" + Long.toHexString(System.currentTimeMillis() / 1000L) + ";Java Unit Tests;|com.oracle.jvm.8u",
 305                 result.toString());
 306         IOUtils.exec(pb, true);
 307     }
 308 
 309     /**
 310      * The bare minimum configuration needed to make it work
 311      * <ul>
 312      *     <li>Where to build it</li>
 313      *     <li>The jar containing the application (with a main-class attribute)</li>
 314      * </ul>
 315      *
 316      * All other values will be driven off of those two values.
 317      */
 318     @Test
 319     public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException {
 320         Bundler bundler = new MacPkgBundler();
 321 
 322         Map<String, Object> bundleParams = new HashMap<>();
 323 
 324         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 325 
 326         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 327 
 328         if (runtimeJdk != null) {
 329             bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk);
 330         }
 331 
 332         String keychain = null;
 333         if (!signingKeysPresent) {
 334             keychain = createFakeCerts(bundleParams);
 335             if (keychain != null) {
 336                 bundleParams.put(SIGNING_KEYCHAIN.getID(), keychain);
 337             }
 338         }
 339 
 340         boolean valid = bundler.validate(bundleParams);
 341         assertTrue(valid);
 342 
 343         File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum"));
 344         System.err.println("Bundle at - " + output);
 345         assertNotNull(output);
 346         assertTrue(output.exists());
 347         assertTrue(output.length() > MIN_SIZE);
 348         if (signingKeysPresent || keychain != null) {
 349             validateSignatures(output);
 350         }
 351     }
 352 
 353     /**
 354      * Test with unicode in places we expect it to be
 355      */
 356     @Test
 357     public void unicodeConfig() throws IOException, ConfigException, UnsupportedPlatformException {
 358         Bundler bundler = new MacPkgBundler();
 359 
 360         Map<String, Object> bundleParams = new HashMap<>();
 361 
 362         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 363 
 364         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 365 
 366         bundleParams.put(APP_NAME.getID(), "хелловорлд");
 367         bundleParams.put(TITLE.getID(), "ХеллоВорлд аппликейшн");
 368         bundleParams.put(VENDOR.getID(), "Оракл девелопмент");
 369         bundleParams.put(DESCRIPTION.getID(), "крайне большое описание со странными символами");
 370 
 371         if (runtimeJdk != null) {
 372             bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk);
 373         }
 374 
 375         String keychain = null;
 376         if (!signingKeysPresent) {
 377             keychain = createFakeCerts(bundleParams);
 378             if (keychain != null) {
 379                 bundleParams.put(SIGNING_KEYCHAIN.getID(), keychain);
 380             }
 381         }
 382 
 383         bundler.validate(bundleParams);
 384 
 385         File output = bundler.execute(bundleParams, new File(workDir, "Unicode"));
 386         System.err.println("Bundle at - " + output);
 387         assertNotNull(output);
 388         assertTrue(output.exists());
 389         if (signingKeysPresent || keychain != null) {
 390             validateSignatures(output);
 391         }
 392     }
 393 
 394     /**
 395      * Create a PKG with an external app rather than a self-created one.
 396      */
 397     @Test
 398     public void externalApp() throws IOException, ConfigException, UnsupportedPlatformException {
 399         // only run with full tests
 400         Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST")));
 401 
 402         // first create the external app
 403         Bundler appBundler = new MacAppBundler();
 404 
 405         Map<String, Object> appBundleParams = new HashMap<>();
 406 
 407         appBundleParams.put(BUILD_ROOT.getID(), tmpBase);
 408 
 409         appBundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 410         appBundleParams.put(APP_NAME.getID(), "External APP PKG Test");
 411         appBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external");
 412         appBundleParams.put(VERBOSE.getID(), true);
 413 
 414         if (runtimeJdk != null) {
 415             appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk);
 416         }
 417 
 418         String keychain = null;
 419         if (!signingKeysPresent) {
 420             keychain = createFakeCerts(appBundleParams);
 421             if (keychain != null) {
 422                 appBundleParams.put(SIGNING_KEYCHAIN.getID(), keychain);
 423             }
 424         }
 425         
 426         boolean valid = appBundler.validate(appBundleParams);
 427         assertTrue(valid);
 428 
 429         File appOutput = appBundler.execute(appBundleParams, new File(workDir, "PKGExternalApp1"));
 430         System.err.println("App at - " + appOutput);
 431         assertNotNull(appOutput);
 432         assertTrue(appOutput.exists());
 433 
 434         // now create the PKG referencing this external app
 435         Bundler pkgBundler = new MacPkgBundler();
 436 
 437         Map<String, Object> pkgBundleParams = new HashMap<>();
 438 
 439         pkgBundleParams.put(BUILD_ROOT.getID(), tmpBase);
 440 
 441         pkgBundleParams.put(MAC_APP_IMAGE.getID(), appOutput);
 442         pkgBundleParams.put(APP_NAME.getID(), "External APP PKG Test");
 443         pkgBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external");
 444 
 445         pkgBundleParams.put(VERBOSE.getID(), true);
 446 
 447         if (runtimeJdk != null) {
 448             pkgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk);
 449         }
 450         if (keychain != null) {
 451             pkgBundleParams.put(SIGNING_KEYCHAIN.getID(), keychain);
 452         }
 453 
 454         valid = pkgBundler.validate(pkgBundleParams);
 455         assertTrue(valid);
 456 
 457         File pkgOutput = pkgBundler.execute(pkgBundleParams, new File(workDir, "PKGExternalApp2"));
 458         System.err.println(".pkg at - " + pkgOutput);
 459         assertNotNull(pkgOutput);
 460         assertTrue(pkgOutput.exists());
 461         assertTrue(pkgOutput.length() > MIN_SIZE);
 462 
 463         if (signingKeysPresent || keychain != null) {
 464             validateSignatures(pkgOutput);
 465         }
 466         
 467     }
 468 
 469     @Test(expected = ConfigException.class)
 470     public void externanNoAppName() throws ConfigException, UnsupportedPlatformException {
 471         Bundler pkgBundler = new MacPkgBundler();
 472 
 473         Map<String, Object> pkgBundleParams = new HashMap<>();
 474 
 475         pkgBundleParams.put(BUILD_ROOT.getID(), tmpBase);
 476 
 477         pkgBundleParams.put(MAC_APP_IMAGE.getID(), ".");
 478         pkgBundleParams.put(IDENTIFIER.getID(), "net.example.bogus");
 479         pkgBundleParams.put(VERBOSE.getID(), true);
 480 
 481         pkgBundler.validate(pkgBundleParams);
 482     }
 483 
 484     @Test(expected = ConfigException.class)
 485     public void externanNoID() throws ConfigException, UnsupportedPlatformException {
 486         Bundler pkgBundler = new MacPkgBundler();
 487 
 488         Map<String, Object> pkgBundleParams = new HashMap<>();
 489 
 490         pkgBundleParams.put(BUILD_ROOT.getID(), tmpBase);
 491 
 492         pkgBundleParams.put(MAC_APP_IMAGE.getID(), ".");
 493         pkgBundleParams.put(APP_NAME.getID(), "Bogus App");
 494         pkgBundleParams.put(VERBOSE.getID(), true);
 495 
 496         pkgBundler.validate(pkgBundleParams);
 497     }
 498 
 499     @Test(expected = ConfigException.class)
 500     public void invalidLicenseFile() throws ConfigException, UnsupportedPlatformException {
 501         Bundler bundler = new MacPkgBundler();
 502 
 503         Map<String, Object> bundleParams = new HashMap<>();
 504 
 505         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 506 
 507         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 508         bundleParams.put(LICENSE_FILE.getID(), "BOGUS_LICENSE");
 509 
 510         bundler.validate(bundleParams);
 511     }
 512 
 513     /**
 514      * Test a misconfiguration where signature is requested but no key is specified.
 515      */
 516     @Test
 517     public void signButNoCert() throws IOException, ConfigException, UnsupportedPlatformException {
 518         // only run with full tests
 519         Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST")));
 520 
 521         try {
 522             // first create the external app
 523             Bundler appBundler = new MacAppBundler();
 524     
 525             Map<String, Object> appBundleParams = new HashMap<>();
 526     
 527             appBundleParams.put(BUILD_ROOT.getID(), tmpBase);
 528     
 529             appBundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 530             appBundleParams.put(APP_NAME.getID(), "External APP PKG Negative Signature Test");
 531             appBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external");
 532             appBundleParams.put(VERBOSE.getID(), true);
 533     
 534             if (runtimeJdk != null) {
 535                 appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk);
 536             }
 537     
 538             boolean valid = appBundler.validate(appBundleParams);
 539             assertTrue(valid);
 540     
 541             File appOutput = appBundler.execute(appBundleParams, new File(workDir, "PKGExternalAppSignTest"));
 542             System.err.println("App at - " + appOutput);
 543             assertNotNull(appOutput);
 544             assertTrue(appOutput.exists());
 545     
 546             // now create the PKG referencing this external app
 547             Bundler pkgBundler = new MacPkgBundler();
 548     
 549             Map<String, Object> pkgBundleParams = new HashMap<>();
 550     
 551             pkgBundleParams.put(BUILD_ROOT.getID(), tmpBase);
 552     
 553             pkgBundleParams.put(MAC_APP_IMAGE.getID(), appOutput);
 554             pkgBundleParams.put(APP_NAME.getID(), "Negative Signature Test");
 555             pkgBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external");
 556     
 557             pkgBundleParams.put(SIGN_BUNDLE.getID(), true);
 558             pkgBundleParams.put(DEVELOPER_ID_INSTALLER_SIGNING_KEY.getID(), null);
 559     
 560             pkgBundler.validate(pkgBundleParams);
 561 
 562             // if we get here we fail
 563             assertTrue("ConfigException should have been thrown", false);
 564         } catch (ConfigException ignore) {
 565             // expected
 566         }            
 567     }
 568 
 569     @Test
 570     public void configureEverything() throws Exception {
 571         AbstractBundler bundler = new MacPkgBundler();
 572         Collection<BundlerParamInfo<?>> parameters = bundler.getBundleParameters();
 573 
 574         Map<String, Object> bundleParams = new HashMap<>();
 575 
 576         bundleParams.put(APP_NAME.getID(), "Everything App Name");
 577         bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources));
 578         bundleParams.put(ARGUMENTS.getID(), Arrays.asList("He Said", "She Said"));
 579         bundleParams.put(BUNDLE_ID_SIGNING_PREFIX.getID(), "everything.signing.prefix.");
 580         bundleParams.put(CLASSPATH.getID(), "mainApp.jar");
 581         bundleParams.put(ICON_ICNS.getID(), hdpiIcon);
 582         bundleParams.put(INSTALLER_SUFFIX.getID(), "-PKG-TEST");
 583         bundleParams.put(JVM_OPTIONS.getID(), "-Xms128M");
 584         bundleParams.put(JVM_PROPERTIES.getID(), "everything.jvm.property=everything.jvm.property.value");
 585         bundleParams.put(MAC_CATEGORY.getID(), "public.app-category.developer-tools");
 586         bundleParams.put(MAC_CF_BUNDLE_IDENTIFIER.getID(), "com.example.everything.cf-bundle-identifier");
 587         bundleParams.put(MAC_CF_BUNDLE_NAME.getID(), "Everything CF Bundle Name");
 588         bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk);
 589         bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle");
 590         bundleParams.put(MAIN_JAR.getID(), "mainApp.jar");
 591         bundleParams.put(PREFERENCES_ID.getID(), "everything/preferences/id");
 592         bundleParams.put(PRELOADER_CLASS.getID(), "hello.HelloPreloader");
 593         bundleParams.put(SIGNING_KEYCHAIN.getID(), "");
 594         bundleParams.put(USER_JVM_OPTIONS.getID(), "-Xmx=256M\n");
 595         bundleParams.put(VERSION.getID(), "1.2.3.4");
 596 
 597         //bundleParams.put(IDENTIFIER.getID(), "com.example.everything.identifier");
 598         bundleParams.put(DEVELOPER_ID_INSTALLER_SIGNING_KEY.getID(), "Developer ID Installer");
 599         bundleParams.put(LICENSE_FILE.getID(), "LICENSE");
 600         //bundleParams.put(SERVICE_HINT.getID(), false);
 601 
 602         // assert they are set
 603         for (BundlerParamInfo bi :parameters) {
 604             assertNotNull("Bundle args Contains " + bi.getID(), bundleParams.containsKey(bi.getID()));
 605         }
 606 
 607         // and only those are set
 608         bundleParamLoop:
 609         for (String s :bundleParams.keySet()) {
 610             for (BundlerParamInfo<?> bpi : parameters) {
 611                 if (s.equals(bpi.getID())) {
 612                     continue bundleParamLoop;
 613                 }
 614             }
 615             fail("Enumerated parameters does not contain " + s);
 616         }
 617 
 618         // assert they resolve
 619         for (BundlerParamInfo bi :parameters) {
 620             bi.fetchFrom(bundleParams);
 621         }
 622 
 623         // add verbose now that we are done scoping out parameters
 624         bundleParams.put(BUILD_ROOT.getID(), tmpBase);
 625         bundleParams.put(VERBOSE.getID(), true);
 626 
 627         // assert it validates
 628         boolean valid = bundler.validate(bundleParams);
 629         assertTrue(valid);
 630 
 631         // only run the bundle with full tests
 632         Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST")));
 633 
 634         // but first remove signing keys, test servers don't have these...
 635         bundleParams.remove(DEVELOPER_ID_INSTALLER_SIGNING_KEY.getID());
 636 
 637         File result = bundler.execute(bundleParams, new File(workDir, "everything"));
 638         System.err.println("Bundle at - " + result);
 639         assertNotNull(result);
 640         assertTrue(result.exists());
 641         assertTrue(result.length() > MIN_SIZE);
 642     }
 643 
 644     public void validateSignatures(File appLocation) throws IOException {
 645         // Check the signatures with pkgUtil
 646         ProcessBuilder pb = new ProcessBuilder(
 647                 "pkgutil", "--check-signature",
 648                 appLocation.getCanonicalPath());
 649         IOUtils.exec(pb, true);
 650     }
 651 
 652 }