1#!/usr/bin/env python 2 3# Copyright (c) 2012 Google Inc. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7""" 8Verifies that device and simulator bundles are built correctly. 9""" 10 11import plistlib 12import TestGyp 13import os 14import struct 15import subprocess 16import sys 17import tempfile 18 19 20def CheckFileType(file, expected): 21 proc = subprocess.Popen(['lipo', '-info', file], stdout=subprocess.PIPE) 22 o = proc.communicate()[0].strip() 23 assert not proc.returncode 24 if not expected in o: 25 print 'File: Expected %s, got %s' % (expected, o) 26 test.fail_test() 27 28def HasCerts(): 29 # Because the bots do not have certs, don't check them if there are no 30 # certs available. 31 proc = subprocess.Popen(['security','find-identity','-p', 'codesigning', 32 '-v'], stdout=subprocess.PIPE) 33 return "0 valid identities found" not in proc.communicate()[0].strip() 34 35def CheckSignature(file): 36 proc = subprocess.Popen(['codesign', '-v', file], stdout=subprocess.PIPE) 37 o = proc.communicate()[0].strip() 38 assert not proc.returncode 39 if "code object is not signed at all" in o: 40 print 'File %s not properly signed.' % (file) 41 test.fail_test() 42 43def CheckEntitlements(file, expected_entitlements): 44 with tempfile.NamedTemporaryFile() as temp: 45 proc = subprocess.Popen(['codesign', '--display', '--entitlements', 46 temp.name, file], stdout=subprocess.PIPE) 47 o = proc.communicate()[0].strip() 48 assert not proc.returncode 49 data = temp.read() 50 entitlements = ParseEntitlements(data) 51 if not entitlements: 52 print 'No valid entitlements found in %s.' % (file) 53 test.fail_test() 54 if entitlements != expected_entitlements: 55 print 'Unexpected entitlements found in %s.' % (file) 56 test.fail_test() 57 58def ParseEntitlements(data): 59 if len(data) < 8: 60 return None 61 magic, length = struct.unpack('>II', data[:8]) 62 if magic != 0xfade7171 or length != len(data): 63 return None 64 return data[8:] 65 66def GetProductVersion(): 67 args = ['xcodebuild','-version','-sdk','iphoneos','ProductVersion'] 68 job = subprocess.Popen(args, stdout=subprocess.PIPE) 69 return job.communicate()[0].strip() 70 71def CheckPlistvalue(plist, key, expected): 72 if key not in plist: 73 print '%s not set in plist' % key 74 test.fail_test() 75 return 76 actual = plist[key] 77 if actual != expected: 78 print 'File: Expected %s, got %s for %s' % (expected, actual, key) 79 test.fail_test() 80 81def CheckPlistNotSet(plist, key): 82 if key in plist: 83 print '%s should not be set in plist' % key 84 test.fail_test() 85 return 86 87def ConvertBinaryPlistToXML(path): 88 proc = subprocess.call(['plutil', '-convert', 'xml1', path], 89 stdout=subprocess.PIPE) 90 91if sys.platform == 'darwin': 92 test = TestGyp.TestGyp(formats=['ninja', 'xcode']) 93 94 test.run_gyp('test-device.gyp', chdir='app-bundle') 95 96 test_configs = ['Default-iphoneos', 'Default'] 97 # TODO(justincohen): Disabling 'Default-iphoneos' for xcode until bots are 98 # configured with signing certs. 99 if test.format == 'xcode': 100 test_configs.remove('Default-iphoneos') 101 102 for configuration in test_configs: 103 test.set_configuration(configuration) 104 test.build('test-device.gyp', 'test_app', chdir='app-bundle') 105 result_file = test.built_file_path('Test App Gyp.bundle/Test App Gyp', 106 chdir='app-bundle') 107 test.must_exist(result_file) 108 109 info_plist = test.built_file_path('Test App Gyp.bundle/Info.plist', 110 chdir='app-bundle') 111 112 # plistlib doesn't support binary plists, but that's what Xcode creates. 113 if test.format == 'xcode': 114 ConvertBinaryPlistToXML(info_plist) 115 plist = plistlib.readPlist(info_plist) 116 117 CheckPlistvalue(plist, 'UIDeviceFamily', [1, 2]) 118 119 if configuration == 'Default-iphoneos': 120 CheckFileType(result_file, 'armv7') 121 CheckPlistvalue(plist, 'DTPlatformVersion', GetProductVersion()) 122 CheckPlistvalue(plist, 'CFBundleSupportedPlatforms', ['iPhoneOS']) 123 CheckPlistvalue(plist, 'DTPlatformName', 'iphoneos') 124 else: 125 CheckFileType(result_file, 'i386') 126 CheckPlistNotSet(plist, 'DTPlatformVersion') 127 CheckPlistvalue(plist, 'CFBundleSupportedPlatforms', ['iPhoneSimulator']) 128 CheckPlistvalue(plist, 'DTPlatformName', 'iphonesimulator') 129 130 if HasCerts() and configuration == 'Default-iphoneos': 131 test.build('test-device.gyp', 'sig_test', chdir='app-bundle') 132 result_file = test.built_file_path('sig_test.bundle/sig_test', 133 chdir='app-bundle') 134 CheckSignature(result_file) 135 info_plist = test.built_file_path('sig_test.bundle/Info.plist', 136 chdir='app-bundle') 137 138 plist = plistlib.readPlist(info_plist) 139 CheckPlistvalue(plist, 'UIDeviceFamily', [1]) 140 141 entitlements_file = test.built_file_path('sig_test.xcent', 142 chdir='app-bundle') 143 if os.path.isfile(entitlements_file): 144 expected_entitlements = open(entitlements_file).read() 145 CheckEntitlements(result_file, expected_entitlements) 146 147 test.pass_test() 148