1#!/usr/bin/env python 2# 3# Copyright 2017, The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16import sys 17import subprocess 18import os 19import argparse 20 21# Registered host based unit tests 22# Must have 'host_supported: true' 23HOST_TESTS = [ 24 'bluetooth_test_common', 25 'bluetoothtbd_test', 26 'net_test_avrcp', 27 'net_test_btcore', 28 'net_test_types', 29 'net_test_btpackets', 30] 31 32SOONG_UI_BASH = 'build/soong/soong_ui.bash' 33 34 35def str2bool(argument, default=False): 36 """ Convert a string to a booleen value. """ 37 argument = str(argument) 38 if argument.lower() in ['0', 'f', 'false', 'off', 'no', 'n']: 39 return False 40 elif argument.lower() in ['1', 't', 'true', 'on', 'yes', 'y']: 41 return True 42 return default 43 44 45def check_dir_exists(dir, dirname): 46 if not os.path.isdir(dir): 47 print "Couldn't find %s (%s)!" % (dirname, dir) 48 sys.exit(0) 49 50 51def get_output_from_command(cmd): 52 try: 53 return subprocess.check_output(cmd).strip() 54 except subprocess.CalledProcessError as e: 55 print 'Failed to call {cmd}, return code {code}'.format( 56 cmd=cmd, code=e.returncode) 57 print e 58 return None 59 60 61def get_android_root_or_die(): 62 value = os.environ.get('ANDROID_BUILD_TOP') 63 if not value: 64 # Try to find build/soong/soong_ui.bash upwards until root directory 65 current_path = os.path.abspath(os.getcwd()) 66 while current_path and os.path.isdir(current_path): 67 soong_ui_bash_path = os.path.join(current_path, SOONG_UI_BASH) 68 if os.path.isfile(soong_ui_bash_path): 69 # Use value returned from Soong UI instead in case definition to TOP 70 # changes in the future 71 value = get_output_from_command( 72 (soong_ui_bash_path, '--dumpvar-mode', '--abs', 'TOP')) 73 break 74 parent_path = os.path.abspath(os.path.join(current_path, os.pardir)) 75 if parent_path == current_path: 76 current_path = None 77 else: 78 current_path = parent_path 79 if not value: 80 print 'Cannot determine ANDROID_BUILD_TOP' 81 sys.exit(1) 82 check_dir_exists(value, '$ANDROID_BUILD_TOP') 83 return value 84 85 86def get_android_host_out_or_die(): 87 value = os.environ.get('ANDROID_HOST_OUT') 88 if not value: 89 ANDROID_BUILD_TOP = get_android_root_or_die() 90 value = get_output_from_command( 91 (os.path.join(ANDROID_BUILD_TOP, SOONG_UI_BASH), '--dumpvar-mode', 92 '--abs', 'HOST_OUT')) 93 if not value: 94 print 'Cannot determine ANDROID_HOST_OUT' 95 sys.exit(1) 96 check_dir_exists(value, '$ANDROID_HOST_OUT') 97 return value 98 99 100def get_android_dist_dir_or_die(): 101 # Check if $DIST_DIR is predefined as environment variable 102 value = os.environ.get('DIST_DIR') 103 if not value: 104 # If not use the default path 105 ANDROID_BUILD_TOP = get_android_root_or_die() 106 value = os.path.join(os.path.join(ANDROID_BUILD_TOP, 'out'), 'dist') 107 if not os.path.isdir(value): 108 if os.path.exists(value): 109 print '%s is not a directory!' % (value) 110 sys.exit(1) 111 os.makedirs(value) 112 return value 113 114 115def get_native_test_root_or_die(): 116 android_host_out = get_android_host_out_or_die() 117 test_root = os.path.join(android_host_out, 'nativetest64') 118 if not os.path.isdir(test_root): 119 test_root = os.path.join(android_host_out, 'nativetest') 120 if not os.path.isdir(test_root): 121 print 'Neither nativetest64 nor nativetest directory exist,' \ 122 ' please compile first' 123 sys.exit(1) 124 return test_root 125 126 127def get_test_cmd_or_die(test_root, test_name, enable_xml, test_filter): 128 test_path = os.path.join(os.path.join(test_root, test_name), test_name) 129 if not os.path.isfile(test_path): 130 print 'Cannot find: ' + test_path 131 sys.exit(1) 132 cmd = [test_path] 133 if enable_xml: 134 dist_dir = get_android_dist_dir_or_die() 135 log_output_path = os.path.join( 136 dist_dir, 'gtest/{0}_test_details.xml'.format(test_name)) 137 cmd.append('--gtest_output=xml:{0}'.format(log_output_path)) 138 if test_filter: 139 cmd.append('--gtest_filter=%s' % test_filter) 140 return cmd 141 142 143# path is relative to Android build top 144def build_target(target, num_tasks): 145 ANDROID_BUILD_TOP = get_android_root_or_die() 146 build_cmd = [SOONG_UI_BASH, '--make-mode'] 147 if num_tasks > 1: 148 build_cmd.append('-j' + str(num_tasks)) 149 build_cmd.append(target) 150 p = subprocess.Popen( 151 build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy()) 152 return_code = p.wait() 153 if return_code != 0: 154 print 'BUILD FAILED, return code: {0}'.format(str(return_code)) 155 sys.exit(1) 156 return 157 158 159def main(): 160 """ run_host_unit_tests.py - Run registered host based unit tests 161 """ 162 parser = argparse.ArgumentParser(description='Run host based unit tests.') 163 parser.add_argument( 164 '--enable_xml', 165 type=str2bool, 166 dest='enable_xml', 167 nargs='?', 168 const=True, 169 default=False, 170 help= 171 'Whether to output structured XML log output in out/dist/gtest directory' 172 ) 173 parser.add_argument( 174 '-j', 175 type=int, 176 nargs='?', 177 dest='num_tasks', 178 const=-1, 179 default=-1, 180 help='Number of tasks to run at the same time') 181 parser.add_argument( 182 'rest', 183 nargs=argparse.REMAINDER, 184 help='-- args, other gtest arguments for each individual test') 185 args = parser.parse_args() 186 187 build_target('MODULES-IN-system-bt', args.num_tasks) 188 TEST_ROOT = get_native_test_root_or_die() 189 test_results = [] 190 for test in HOST_TESTS: 191 test_cmd = get_test_cmd_or_die(TEST_ROOT, test, args.enable_xml, 192 args.rest) 193 if subprocess.call(test_cmd) != 0: 194 test_results.append(False) 195 else: 196 test_results.append(True) 197 if not all(test_results): 198 failures = [i for i, x in enumerate(test_results) if not x] 199 for index in failures: 200 print 'TEST FAILLED: ' + HOST_TESTS[index] 201 sys.exit(0) 202 print 'TEST PASSED ' + str(len(test_results)) + ' tests were run' 203 204 dist_dir = get_android_dist_dir_or_die() 205 log_output_path = os.path.join(dist_dir, 'gtest/coverage') 206 cmd_path = os.path.join(get_android_root_or_die(), 'system/bt/test') 207 print cmd_path 208 cmd = [ 209 os.path.join(cmd_path, 'gen_coverage.py'), 210 '--skip-html', 211 '--json-file', 212 '-o', 213 log_output_path, 214 ] 215 subprocess.call(cmd) 216 217 sys.exit(0) 218 219 220if __name__ == '__main__': 221 main() 222