1#!/usr/bin/env python 2# Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. 3# 4# Use of this source code is governed by a BSD-style license 5# that can be found in the LICENSE file in the root of the source 6# tree. An additional intellectual property rights grant can be found 7# in the file PATENTS. All contributing project authors may 8# be found in the AUTHORS file in the root of the source tree. 9 10"""Generates command-line instructions to produce one-time iOS coverage using 11coverage.py. 12 13This script is usable for both real devices and simulator. 14But for real devices actual execution should be done manually from Xcode 15and coverage.profraw files should be also copied manually from the device. 16 17Additional prerequisites: 18 191. Xcode 10+ with iPhone Simulator SDK. Can be installed by command: 20 $ mac_toolchain install -kind ios -xcode-version 10l232m \ 21 -output-dir build/mac_files/Xcode.app 22 232. For computing coverage on real device you probably also need to apply 24following patch to code_coverage/coverage.py script: 25 26========== BEGINNING OF PATCH ========== 27--- a/code_coverage/coverage.py 28+++ b/code_coverage/coverage.py 29@@ -693,8 +693,7 @@ def _AddArchArgumentForIOSIfNeeded(cmd_list, num_archs): 30 to use, and one architecture needs to be specified for each binary. 31 "" " 32if _IsIOS(): 33- cmd_list.extend(['-arch=x86_64'] * num_archs) 34+ cmd_list.extend(['-arch=arm64'] * num_archs) 35 36 37def _GetBinaryPath(command): 38@@ -836,8 +835,8 @@ def _GetBinaryPathsFromTargets(targets, build_dir): 39 binary_path = os.path.join(build_dir, target) 40 if coverage_utils.GetHostPlatform() == 'win': 41 binary_path += '.exe' 42+ elif coverage_utils.GetHostPlatform() == 'mac': 43+ binary_path += '.app/%s' % target 44 45if os.path.exists(binary_path): 46 binary_paths.append(binary_path) 47========== ENDING OF PATCH ========== 48 49""" 50 51import sys 52 53DIRECTORY = 'out/coverage' 54 55TESTS = [ 56 'audio_decoder_unittests', 57 'common_audio_unittests', 58 'common_video_unittests', 59 'modules_tests', 60 'modules_unittests', 61 'rtc_media_unittests', 62 'rtc_pc_unittests', 63 'rtc_stats_unittests', 64 'rtc_unittests', 65 'slow_tests', 66 'system_wrappers_unittests', 67 'test_support_unittests', 68 'tools_unittests', 69 'video_capture_tests', 70 'video_engine_tests', 71 'webrtc_nonparallel_tests', 72] 73 74XC_TESTS = [ 75 'apprtcmobile_tests', 76 'sdk_framework_unittests', 77 'sdk_unittests', 78] 79 80 81def FormatIossimTest(test_name, is_xctest=False): 82 args = ['%s/%s.app' % (DIRECTORY, test_name)] 83 if is_xctest: 84 args += ['%s/%s_module.xctest' % (DIRECTORY, test_name)] 85 86 return '-c \'%s/iossim %s\'' % (DIRECTORY, ' '.join(args)) 87 88 89def GetGNArgs(is_simulator): 90 target_cpu = 'x64' if is_simulator else 'arm64' 91 return ([] + 92 ['target_os="ios"'] + 93 ['target_cpu="%s"' % target_cpu] + 94 ['use_clang_coverage=true'] + 95 ['is_component_build=false'] + 96 ['dcheck_always_on=true']) 97 98 99def GenerateIOSSimulatorCommand(): 100 gn_args_string = ' '.join(GetGNArgs(is_simulator=True)) 101 gn_cmd = ['gn', 'gen', DIRECTORY, '--args=\'%s\'' % gn_args_string] 102 103 coverage_cmd = ( 104 [sys.executable, 'tools/code_coverage/coverage.py'] + 105 ["%s.app" % t for t in XC_TESTS + TESTS] + 106 ['-b %s' % DIRECTORY, '-o out/report'] + 107 ['-i=\'.*/out/.*|.*/third_party/.*|.*test.*\''] + 108 [FormatIossimTest(t, is_xctest=True) for t in XC_TESTS] + 109 [FormatIossimTest(t, is_xctest=False) for t in TESTS] 110 ) 111 112 print 'To get code coverage using iOS simulator just run following commands:' 113 print '' 114 print ' '.join(gn_cmd) 115 print '' 116 print ' '.join(coverage_cmd) 117 return 0 118 119 120def GenerateIOSDeviceCommand(): 121 gn_args_string = ' '.join(GetGNArgs(is_simulator=False)) 122 123 coverage_report_cmd = ( 124 [sys.executable, 'tools/code_coverage/coverage.py'] + 125 ['%s.app' % t for t in TESTS] + 126 ['-b %s' % DIRECTORY] + 127 ['-o out/report'] + 128 ['-p %s/merged.profdata' % DIRECTORY] + 129 ['-i=\'.*/out/.*|.*/third_party/.*|.*test.*\''] 130 ) 131 132 print 'Computing code coverage for real iOS device is a little bit tedious.' 133 print '' 134 print 'You will need:' 135 print '' 136 print '1. Generate xcode project and open it with Xcode 10+:' 137 print ' gn gen %s --ide=xcode --args=\'%s\'' % (DIRECTORY, gn_args_string) 138 print ' open %s/all.xcworkspace' % DIRECTORY 139 print '' 140 print '2. Execute these Run targets manually with Xcode Run button and ' 141 print 'manually save generated coverage.profraw file to %s:' % DIRECTORY 142 print '\n'.join('- %s' % t for t in TESTS) 143 print '' 144 print '3. Execute these Test targets manually with Xcode Test button and ' 145 print 'manually save generated coverage.profraw file to %s:' % DIRECTORY 146 print '\n'.join('- %s' % t for t in XC_TESTS) 147 print '' 148 print '4. Merge *.profraw files to *.profdata using llvm-profdata tool:' 149 print (' build/mac_files/Xcode.app/Contents/Developer/Toolchains/' + 150 'XcodeDefault.xctoolchain/usr/bin/llvm-profdata merge ' + 151 '-o %s/merged.profdata ' % DIRECTORY + 152 '-sparse=true %s/*.profraw' % DIRECTORY) 153 print '' 154 print '5. Generate coverage report:' 155 print ' ' + ' '.join(coverage_report_cmd) 156 return 0 157 158 159def Main(): 160 if len(sys.argv) < 2: 161 print 'Please specify type of coverage:' 162 print ' %s simulator' % sys.argv[0] 163 print ' %s device' % sys.argv[0] 164 elif sys.argv[1] == 'simulator': 165 GenerateIOSSimulatorCommand() 166 elif sys.argv[1] == 'device': 167 GenerateIOSDeviceCommand() 168 else: 169 print 'Unsupported type of coverage' 170 171 return 0 172 173if __name__ == '__main__': 174 sys.exit(Main()) 175