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(cmd=cmd, code=e.returncode) 56 print e 57 return None 58 59 60def get_android_root_or_die(): 61 value = os.environ.get('ANDROID_BUILD_TOP') 62 if not value: 63 # Try to find build/soong/soong_ui.bash upwards until root directory 64 current_path = os.path.abspath(os.getcwd()) 65 while current_path and os.path.isdir(current_path): 66 soong_ui_bash_path = os.path.join(current_path, SOONG_UI_BASH) 67 if os.path.isfile(soong_ui_bash_path): 68 # Use value returned from Soong UI instead in case definition to TOP 69 # changes in the future 70 value = get_output_from_command((soong_ui_bash_path, '--dumpvar-mode', '--abs', 'TOP')) 71 break 72 parent_path = os.path.abspath(os.path.join(current_path, os.pardir)) 73 if parent_path == current_path: 74 current_path = None 75 else: 76 current_path = parent_path 77 if not value: 78 print 'Cannot determine ANDROID_BUILD_TOP' 79 sys.exit(1) 80 check_dir_exists(value, '$ANDROID_BUILD_TOP') 81 return value 82 83 84def get_android_host_out_or_die(): 85 value = os.environ.get('ANDROID_HOST_OUT') 86 if not value: 87 ANDROID_BUILD_TOP = get_android_root_or_die() 88 value = get_output_from_command((os.path.join(ANDROID_BUILD_TOP, SOONG_UI_BASH), '--dumpvar-mode', '--abs', 89 'HOST_OUT')) 90 if not value: 91 print 'Cannot determine ANDROID_HOST_OUT' 92 sys.exit(1) 93 check_dir_exists(value, '$ANDROID_HOST_OUT') 94 return value 95 96 97def get_android_dist_dir_or_die(): 98 # Check if $DIST_DIR is predefined as environment variable 99 value = os.environ.get('DIST_DIR') 100 if not value: 101 # If not use the default path 102 ANDROID_BUILD_TOP = get_android_root_or_die() 103 value = os.path.join(os.path.join(ANDROID_BUILD_TOP, 'out'), 'dist') 104 if not os.path.isdir(value): 105 if os.path.exists(value): 106 print '%s is not a directory!' % (value) 107 sys.exit(1) 108 os.makedirs(value) 109 return value 110 111 112def get_native_test_root_or_die(): 113 android_host_out = get_android_host_out_or_die() 114 test_root = os.path.join(android_host_out, 'nativetest64') 115 if not os.path.isdir(test_root): 116 test_root = os.path.join(android_host_out, 'nativetest') 117 if not os.path.isdir(test_root): 118 print 'Neither nativetest64 nor nativetest directory exist,' \ 119 ' please compile first' 120 sys.exit(1) 121 return test_root 122 123 124def get_test_cmd_or_die(test_root, test_name, enable_xml, test_filter): 125 test_path = os.path.join(os.path.join(test_root, test_name), test_name) 126 if not os.path.isfile(test_path): 127 print 'Cannot find: ' + test_path 128 sys.exit(1) 129 cmd = [test_path] 130 if enable_xml: 131 dist_dir = get_android_dist_dir_or_die() 132 log_output_path = os.path.join(dist_dir, 'gtest/{0}_test_details.xml'.format(test_name)) 133 cmd.append('--gtest_output=xml:{0}'.format(log_output_path)) 134 if test_filter: 135 cmd.append('--gtest_filter=%s' % test_filter) 136 return cmd 137 138 139# path is relative to Android build top 140def build_target(target, num_tasks): 141 ANDROID_BUILD_TOP = get_android_root_or_die() 142 build_cmd = [SOONG_UI_BASH, '--make-mode'] 143 if num_tasks > 1: 144 build_cmd.append('-j' + str(num_tasks)) 145 build_cmd.append(target) 146 p = subprocess.Popen(build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy()) 147 return_code = p.wait() 148 if return_code != 0: 149 print 'BUILD FAILED, return code: {0}'.format(str(return_code)) 150 sys.exit(1) 151 return 152 153 154def main(): 155 """ run_host_unit_tests.py - Run registered host based unit tests 156 """ 157 parser = argparse.ArgumentParser(description='Run host based unit tests.') 158 parser.add_argument( 159 '--enable_xml', 160 type=str2bool, 161 dest='enable_xml', 162 nargs='?', 163 const=True, 164 default=False, 165 help='Whether to output structured XML log output in out/dist/gtest directory') 166 parser.add_argument( 167 '-j', 168 type=int, 169 nargs='?', 170 dest='num_tasks', 171 const=-1, 172 default=-1, 173 help='Number of tasks to run at the same time') 174 parser.add_argument( 175 'rest', nargs=argparse.REMAINDER, help='-- args, other gtest arguments for each individual test') 176 args = parser.parse_args() 177 178 build_target('MODULES-IN-system-bt', args.num_tasks) 179 TEST_ROOT = get_native_test_root_or_die() 180 test_results = [] 181 for test in HOST_TESTS: 182 test_cmd = get_test_cmd_or_die(TEST_ROOT, test, args.enable_xml, args.rest) 183 if subprocess.call(test_cmd) != 0: 184 test_results.append(False) 185 else: 186 test_results.append(True) 187 if not all(test_results): 188 failures = [i for i, x in enumerate(test_results) if not x] 189 for index in failures: 190 print 'TEST FAILLED: ' + HOST_TESTS[index] 191 sys.exit(0) 192 print 'TEST PASSED ' + str(len(test_results)) + ' tests were run' 193 194 dist_dir = get_android_dist_dir_or_die() 195 log_output_path = os.path.join(dist_dir, 'gtest/coverage') 196 cmd_path = os.path.join(get_android_root_or_die(), 'system/bt/test') 197 print cmd_path 198 cmd = [ 199 os.path.join(cmd_path, 'gen_coverage.py'), 200 '--skip-html', 201 '--json-file', 202 '-o', 203 log_output_path, 204 ] 205 subprocess.call(cmd) 206 207 sys.exit(0) 208 209 210if __name__ == '__main__': 211 main() 212