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