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 @Ignore("JDK-8193232") 274 @Test 275 public void quarantinedAppTest() throws IOException, ConfigException, UnsupportedPlatformException { 276 277 AbstractBundler bundler = new MacPkgBundler(); 278 279 assertNotNull(bundler.getName()); 280 assertNotNull(bundler.getID()); 281 assertNotNull(bundler.getDescription()); 282 //assertNotNull(bundler.getBundleParameters()); 283 284 Map<String, Object> bundleParams = new HashMap<>(); 285 286 bundleParams.put(BUILD_ROOT.getID(), tmpBase); 287 288 bundleParams.put(APP_NAME.getID(), "Quarantine App"); 289 bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle"); 290 bundleParams.put(PREFERENCES_ID.getID(), "the/really/long/preferences/id"); 291 bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); 292 bundleParams.put(VERBOSE.getID(), true); 293 294 if (runtimeJdk != null) { 295 bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); 296 } 297 298 if (!signingKeysPresent) { 299 String keychain = createFakeCerts(bundleParams); 300 Assume.assumeNotNull(keychain); 301 bundleParams.put(SIGNING_KEYCHAIN.getID(), keychain); 302 } 303 304 boolean valid = bundler.validate(bundleParams); 305 assertTrue(valid); 306 307 File result = bundler.execute(bundleParams, new File(workDir, "quarantine")); 308 System.err.println("Bundle at - " + result); 309 assertNotNull(result); 310 assertTrue(result.exists()); 311 assertTrue(result.length() > MIN_SIZE); 312 validateSignatures(result); 313 314 // mark it as though it's been downloaded 315 ProcessBuilder pb = new ProcessBuilder( 316 "xattr", "-w", "com.apple.quarantine", 317 "0000;" + Long.toHexString(System.currentTimeMillis() / 1000L) + ";Java Unit Tests;|com.oracle.jvm.8u", 318 result.toString()); 319 IOUtils.exec(pb, true); 320 } 321 322 /** 323 * The bare minimum configuration needed to make it work 324 * <ul> 325 * <li>Where to build it</li> 326 * <li>The jar containing the application (with a main-class attribute)</li> 327 * </ul> 328 * 329 * All other values will be driven off of those two values. 330 */ 331 @Ignore("JDK-8193232") 332 @Test 333 public void minimumConfig() throws IOException, ConfigException, UnsupportedPlatformException { 334 Bundler bundler = new MacPkgBundler(); 335 336 Map<String, Object> bundleParams = new HashMap<>(); 337 338 bundleParams.put(BUILD_ROOT.getID(), tmpBase); 339 340 bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); 341 342 if (runtimeJdk != null) { 343 bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); 344 } 345 346 String keychain = null; 347 if (!signingKeysPresent) { 348 keychain = createFakeCerts(bundleParams); 349 if (keychain != null) { 350 bundleParams.put(SIGNING_KEYCHAIN.getID(), keychain); 351 } 352 } 353 354 boolean valid = bundler.validate(bundleParams); 355 assertTrue(valid); 356 357 File output = bundler.execute(bundleParams, new File(workDir, "BareMinimum")); 358 System.err.println("Bundle at - " + output); 359 assertNotNull(output); 360 assertTrue(output.exists()); 361 assertTrue(output.length() > MIN_SIZE); 362 if (signingKeysPresent || keychain != null) { 363 validateSignatures(output); 364 } 365 } 366 367 /** 368 * Test with unicode in places we expect it to be 369 */ 370 @Ignore("JDK-8163859") 371 @Test 372 public void unicodeConfig() throws IOException, ConfigException, UnsupportedPlatformException { 373 Bundler bundler = new MacPkgBundler(); 374 375 Map<String, Object> bundleParams = new HashMap<>(); 376 377 bundleParams.put(BUILD_ROOT.getID(), tmpBase); 378 379 bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); 380 381 bundleParams.put(APP_NAME.getID(), "хелловорлд"); 382 bundleParams.put(TITLE.getID(), "ХеллоВорлд аппликейшн"); 383 bundleParams.put(VENDOR.getID(), "Оракл девелопмент"); 384 bundleParams.put(DESCRIPTION.getID(), "крайне большое описание со странными символами"); 385 386 if (runtimeJdk != null) { 387 bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); 388 } 389 390 String keychain = null; 391 if (!signingKeysPresent) { 392 keychain = createFakeCerts(bundleParams); 393 if (keychain != null) { 394 bundleParams.put(SIGNING_KEYCHAIN.getID(), keychain); 395 } 396 } 397 398 bundler.validate(bundleParams); 399 400 File output = bundler.execute(bundleParams, new File(workDir, "Unicode")); 401 System.err.println("Bundle at - " + output); 402 assertNotNull(output); 403 assertTrue(output.exists()); 404 if (signingKeysPresent || keychain != null) { 405 validateSignatures(output); 406 } 407 } 408 409 /** 410 * Create a PKG with an external app rather than a self-created one. 411 */ 412 @Test 413 public void externalApp() throws IOException, ConfigException, UnsupportedPlatformException { 414 // only run with full tests 415 Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST"))); 416 417 // first create the external app 418 Bundler appBundler = new MacAppBundler(); 419 420 Map<String, Object> appBundleParams = new HashMap<>(); 421 422 appBundleParams.put(BUILD_ROOT.getID(), tmpBase); 423 424 appBundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); 425 appBundleParams.put(APP_NAME.getID(), "External APP PKG Test"); 426 appBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external"); 427 appBundleParams.put(VERBOSE.getID(), true); 428 429 if (runtimeJdk != null) { 430 appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); 431 } 432 433 String keychain = null; 434 if (!signingKeysPresent) { 435 keychain = createFakeCerts(appBundleParams); 436 if (keychain != null) { 437 appBundleParams.put(SIGNING_KEYCHAIN.getID(), keychain); 438 } 439 } 440 441 boolean valid = appBundler.validate(appBundleParams); 442 assertTrue(valid); 443 444 File appOutput = appBundler.execute(appBundleParams, new File(workDir, "PKGExternalApp1")); 445 System.err.println("App at - " + appOutput); 446 assertNotNull(appOutput); 447 assertTrue(appOutput.exists()); 448 449 // now create the PKG referencing this external app 450 Bundler pkgBundler = new MacPkgBundler(); 451 452 Map<String, Object> pkgBundleParams = new HashMap<>(); 453 454 pkgBundleParams.put(BUILD_ROOT.getID(), tmpBase); 455 456 pkgBundleParams.put(MAC_APP_IMAGE.getID(), appOutput); 457 pkgBundleParams.put(APP_NAME.getID(), "External APP PKG Test"); 458 pkgBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external"); 459 460 pkgBundleParams.put(VERBOSE.getID(), true); 461 462 if (runtimeJdk != null) { 463 pkgBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); 464 } 465 if (keychain != null) { 466 pkgBundleParams.put(SIGNING_KEYCHAIN.getID(), keychain); 467 } 468 469 valid = pkgBundler.validate(pkgBundleParams); 470 assertTrue(valid); 471 472 File pkgOutput = pkgBundler.execute(pkgBundleParams, new File(workDir, "PKGExternalApp2")); 473 System.err.println(".pkg at - " + pkgOutput); 474 assertNotNull(pkgOutput); 475 assertTrue(pkgOutput.exists()); 476 assertTrue(pkgOutput.length() > MIN_SIZE); 477 478 if (signingKeysPresent || keychain != null) { 479 validateSignatures(pkgOutput); 480 } 481 482 } 483 484 @Test(expected = ConfigException.class) 485 public void externanNoAppName() 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(IDENTIFIER.getID(), "net.example.bogus"); 494 pkgBundleParams.put(VERBOSE.getID(), true); 495 496 pkgBundler.validate(pkgBundleParams); 497 } 498 499 @Test(expected = ConfigException.class) 500 public void externanNoID() throws ConfigException, UnsupportedPlatformException { 501 Bundler pkgBundler = new MacPkgBundler(); 502 503 Map<String, Object> pkgBundleParams = new HashMap<>(); 504 505 pkgBundleParams.put(BUILD_ROOT.getID(), tmpBase); 506 507 pkgBundleParams.put(MAC_APP_IMAGE.getID(), "."); 508 pkgBundleParams.put(APP_NAME.getID(), "Bogus App"); 509 pkgBundleParams.put(VERBOSE.getID(), true); 510 511 pkgBundler.validate(pkgBundleParams); 512 } 513 514 @Test(expected = ConfigException.class) 515 public void invalidLicenseFile() throws ConfigException, UnsupportedPlatformException { 516 Bundler bundler = new MacPkgBundler(); 517 518 Map<String, Object> bundleParams = new HashMap<>(); 519 520 bundleParams.put(BUILD_ROOT.getID(), tmpBase); 521 522 bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); 523 bundleParams.put(LICENSE_FILE.getID(), "BOGUS_LICENSE"); 524 525 bundler.validate(bundleParams); 526 } 527 528 /** 529 * Test a misconfiguration where signature is requested but no key is specified. 530 */ 531 @Test 532 public void signButNoCert() throws IOException, ConfigException, UnsupportedPlatformException { 533 // only run with full tests 534 Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST"))); 535 536 try { 537 // first create the external app 538 Bundler appBundler = new MacAppBundler(); 539 540 Map<String, Object> appBundleParams = new HashMap<>(); 541 542 appBundleParams.put(BUILD_ROOT.getID(), tmpBase); 543 544 appBundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); 545 appBundleParams.put(APP_NAME.getID(), "External APP PKG Negative Signature Test"); 546 appBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external"); 547 appBundleParams.put(VERBOSE.getID(), true); 548 549 if (runtimeJdk != null) { 550 appBundleParams.put(MAC_RUNTIME.getID(), runtimeJdk); 551 } 552 553 boolean valid = appBundler.validate(appBundleParams); 554 assertTrue(valid); 555 556 File appOutput = appBundler.execute(appBundleParams, new File(workDir, "PKGExternalAppSignTest")); 557 System.err.println("App at - " + appOutput); 558 assertNotNull(appOutput); 559 assertTrue(appOutput.exists()); 560 561 // now create the PKG referencing this external app 562 Bundler pkgBundler = new MacPkgBundler(); 563 564 Map<String, Object> pkgBundleParams = new HashMap<>(); 565 566 pkgBundleParams.put(BUILD_ROOT.getID(), tmpBase); 567 568 pkgBundleParams.put(MAC_APP_IMAGE.getID(), appOutput); 569 pkgBundleParams.put(APP_NAME.getID(), "Negative Signature Test"); 570 pkgBundleParams.put(IDENTIFIER.getID(), "com.example.pkg.external"); 571 572 pkgBundleParams.put(SIGN_BUNDLE.getID(), true); 573 pkgBundleParams.put(DEVELOPER_ID_INSTALLER_SIGNING_KEY.getID(), null); 574 575 pkgBundler.validate(pkgBundleParams); 576 577 // if we get here we fail 578 assertTrue("ConfigException should have been thrown", false); 579 } catch (ConfigException ignore) { 580 // expected 581 } 582 } 583 584 @Test 585 public void configureEverything() throws Exception { 586 AbstractBundler bundler = new MacPkgBundler(); 587 Collection<BundlerParamInfo<?>> parameters = bundler.getBundleParameters(); 588 589 Map<String, Object> bundleParams = new HashMap<>(); 590 591 bundleParams.put(APP_NAME.getID(), "Everything App Name"); 592 bundleParams.put(APP_RESOURCES.getID(), new RelativeFileSet(appResourcesDir, appResources)); 593 bundleParams.put(ARGUMENTS.getID(), Arrays.asList("He Said", "She Said")); 594 bundleParams.put(BUNDLE_ID_SIGNING_PREFIX.getID(), "everything.signing.prefix."); 595 bundleParams.put(CLASSPATH.getID(), "mainApp.jar"); 596 bundleParams.put(ICON_ICNS.getID(), hdpiIcon); 597 bundleParams.put(INSTALLER_SUFFIX.getID(), "-PKG-TEST"); 598 bundleParams.put(JVM_OPTIONS.getID(), "-Xms128M"); 599 bundleParams.put(JVM_PROPERTIES.getID(), "everything.jvm.property=everything.jvm.property.value"); 600 bundleParams.put(MAC_CATEGORY.getID(), "public.app-category.developer-tools"); 601 bundleParams.put(MAC_CF_BUNDLE_IDENTIFIER.getID(), "com.example.everything.cf-bundle-identifier"); 602 bundleParams.put(MAC_CF_BUNDLE_NAME.getID(), "Everything CF Bundle Name"); 603 bundleParams.put(MAC_RUNTIME.getID(), runtimeJdk == null ? System.getProperty("java.home") : runtimeJdk); 604 bundleParams.put(MAIN_CLASS.getID(), "hello.HelloRectangle"); 605 bundleParams.put(MAIN_JAR.getID(), "mainApp.jar"); 606 bundleParams.put(PREFERENCES_ID.getID(), "everything/preferences/id"); 607 bundleParams.put(PRELOADER_CLASS.getID(), "hello.HelloPreloader"); 608 bundleParams.put(SIGNING_KEYCHAIN.getID(), ""); 609 bundleParams.put(USER_JVM_OPTIONS.getID(), "-Xmx=256M\n"); 610 bundleParams.put(VERSION.getID(), "1.2.3.4"); 611 612 //bundleParams.put(IDENTIFIER.getID(), "com.example.everything.identifier"); 613 bundleParams.put(DEVELOPER_ID_INSTALLER_SIGNING_KEY.getID(), "Developer ID Installer"); 614 bundleParams.put(LICENSE_FILE.getID(), "LICENSE"); 615 //bundleParams.put(SERVICE_HINT.getID(), false); 616 617 // assert they are set 618 for (BundlerParamInfo bi :parameters) { 619 assertNotNull("Bundle args Contains " + bi.getID(), bundleParams.containsKey(bi.getID())); 620 } 621 622 // and only those are set 623 bundleParamLoop: 624 for (String s :bundleParams.keySet()) { 625 for (BundlerParamInfo<?> bpi : parameters) { 626 if (s.equals(bpi.getID())) { 627 continue bundleParamLoop; 628 } 629 } 630 fail("Enumerated parameters does not contain " + s); 631 } 632 633 // assert they resolve 634 for (BundlerParamInfo bi :parameters) { 635 bi.fetchFrom(bundleParams); 636 } 637 638 // add verbose now that we are done scoping out parameters 639 bundleParams.put(BUILD_ROOT.getID(), tmpBase); 640 bundleParams.put(VERBOSE.getID(), true); 641 642 // assert it validates 643 boolean valid = bundler.validate(bundleParams); 644 assertTrue(valid); 645 646 // only run the bundle with full tests 647 Assume.assumeTrue(Boolean.parseBoolean(System.getProperty("FULL_TEST"))); 648 649 // but first remove signing keys, test servers don't have these... 650 bundleParams.remove(DEVELOPER_ID_INSTALLER_SIGNING_KEY.getID()); 651 652 File result = bundler.execute(bundleParams, new File(workDir, "everything")); 653 System.err.println("Bundle at - " + result); 654 assertNotNull(result); 655 assertTrue(result.exists()); 656 assertTrue(result.length() > MIN_SIZE); 657 } 658 659 public void validateSignatures(File appLocation) throws IOException { 660 // Check the signatures with pkgUtil 661 ProcessBuilder pb = new ProcessBuilder( 662 "pkgutil", "--check-signature", 663 appLocation.getCanonicalPath()); 664 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 665 PrintStream ps = new PrintStream(baos); 666 try { 667 IOUtils.exec(pb, true, false, ps); 668 } catch (IOException ioe) { 669 if (signingKeysPresent) { 670 // these were real keys, failures are real 671 throw ioe; 672 } 673 // failure was for bogus key 674 if (ioe.getMessage().contains("Exec failed with code 1 ")) { 675 // this is likely because the key is not signed by apple, lets look 676 // ok, look to see if our key is in the output 677 if (!baos.toString().contains("1. Developer ID Installer: Insecure Test Cert")) { 678 // didn't list our key as #1, must be some other error 679 throw ioe; 680 } 681 // ok, this is expected. Ignore it. 682 } else { 683 // some other failure, throw the error. 684 throw ioe; 685 } 686 } 687 } 688 689 }