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