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_btm_iso', 30 'net_test_btpackets', 31] 32 33SOONG_UI_BASH = 'build/soong/soong_ui.bash' 34 35 36def str2bool(argument, default=False): 37 """ Convert a string to a booleen value. """ 38 argument = str(argument) 39 if argument.lower() in ['0', 'f', 'false', 'off', 'no', 'n']: 40 return False 41 elif argument.lower() in ['1', 't', 'true', 'on', 'yes', 'y']: 42 return True 43 return default 44 45 46def check_dir_exists(dir, dirname): 47 if not os.path.isdir(dir): 48 print "Couldn't find %s (%s)!" % (dirname, dir) 49 sys.exit(0) 50 51 52def get_output_from_command(cmd): 53 try: 54 return subprocess.check_output(cmd).strip() 55 except subprocess.CalledProcessError as e: 56 print 'Failed to call {cmd}, return code {code}'.format(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((soong_ui_bash_path, '--dumpvar-mode', '--abs', 'TOP')) 72 break 73 parent_path = os.path.abspath(os.path.join(current_path, os.pardir)) 74 if parent_path == current_path: 75 current_path = None 76 else: 77 current_path = parent_path 78 if not value: 79 print 'Cannot determine ANDROID_BUILD_TOP' 80 sys.exit(1) 81 check_dir_exists(value, '$ANDROID_BUILD_TOP') 82 return value 83 84 85def get_android_host_out_or_die(): 86 value = os.environ.get('ANDROID_HOST_OUT') 87 if not value: 88 ANDROID_BUILD_TOP = get_android_root_or_die() 89 value = get_output_from_command((os.path.join(ANDROID_BUILD_TOP, SOONG_UI_BASH), '--dumpvar-mode', '--abs', 90 'HOST_OUT')) 91 if not value: 92 print 'Cannot determine ANDROID_HOST_OUT' 93 sys.exit(1) 94 check_dir_exists(value, '$ANDROID_HOST_OUT') 95 return value 96 97 98def get_android_dist_dir_or_die(): 99 # Check if $DIST_DIR is predefined as environment variable 100 value = os.environ.get('DIST_DIR') 101 if not value: 102 # If not use the default path 103 ANDROID_BUILD_TOP = get_android_root_or_die() 104 value = os.path.join(os.path.join(ANDROID_BUILD_TOP, 'out'), 'dist') 105 if not os.path.isdir(value): 106 if os.path.exists(value): 107 print '%s is not a directory!' % (value) 108 sys.exit(1) 109 os.makedirs(value) 110 return value 111 112 113def get_native_test_root_or_die(): 114 android_host_out = get_android_host_out_or_die() 115 test_root = os.path.join(android_host_out, 'nativetest64') 116 if not os.path.isdir(test_root): 117 test_root = os.path.join(android_host_out, 'nativetest') 118 if not os.path.isdir(test_root): 119 print 'Neither nativetest64 nor nativetest directory exist,' \ 120 ' please compile first' 121 sys.exit(1) 122 return test_root 123 124 125def get_test_cmd_or_die(test_root, test_name, enable_xml, test_filter): 126 test_path = os.path.join(os.path.join(test_root, test_name), test_name) 127 if not os.path.isfile(test_path): 128 print 'Cannot find: ' + test_path 129 sys.exit(1) 130 cmd = [test_path] 131 if enable_xml: 132 dist_dir = get_android_dist_dir_or_die() 133 log_output_path = os.path.join(dist_dir, 'gtest/{0}_test_details.xml'.format(test_name)) 134 cmd.append('--gtest_output=xml:{0}'.format(log_output_path)) 135 if test_filter: 136 cmd.append('--gtest_filter=%s' % test_filter) 137 return cmd 138 139 140# path is relative to Android build top 141def build_target(target, num_tasks): 142 ANDROID_BUILD_TOP = get_android_root_or_die() 143 build_cmd = [SOONG_UI_BASH, '--make-mode'] 144 if num_tasks > 1: 145 build_cmd.append('-j' + str(num_tasks)) 146 build_cmd.append(target) 147 p = subprocess.Popen(build_cmd, cwd=ANDROID_BUILD_TOP, env=os.environ.copy()) 148 return_code = p.wait() 149 if return_code != 0: 150 print 'BUILD FAILED, return code: {0}'.format(str(return_code)) 151 sys.exit(1) 152 return 153 154 155def main(): 156 """ run_host_unit_tests.py - Run registered host based unit tests 157 """ 158 parser = argparse.ArgumentParser(description='Run host based unit tests.') 159 parser.add_argument( 160 '--enable_xml', 161 type=str2bool, 162 dest='enable_xml', 163 nargs='?', 164 const=True, 165 default=False, 166 help='Whether to output structured XML log output in out/dist/gtest directory') 167 parser.add_argument( 168 '-j', 169 type=int, 170 nargs='?', 171 dest='num_tasks', 172 const=-1, 173 default=-1, 174 help='Number of tasks to run at the same time') 175 parser.add_argument( 176 'rest', nargs=argparse.REMAINDER, help='-- args, other gtest arguments for each individual test') 177 args = parser.parse_args() 178 179 build_target('MODULES-IN-system-bt', args.num_tasks) 180 TEST_ROOT = get_native_test_root_or_die() 181 test_results = [] 182 for test in HOST_TESTS: 183 test_cmd = get_test_cmd_or_die(TEST_ROOT, test, args.enable_xml, args.rest) 184 if subprocess.call(test_cmd) != 0: 185 test_results.append(False) 186 else: 187 test_results.append(True) 188 if not all(test_results): 189 failures = [i for i, x in enumerate(test_results) if not x] 190 for index in failures: 191 print 'TEST FAILLED: ' + HOST_TESTS[index] 192 sys.exit(0) 193 print 'TEST PASSED ' + str(len(test_results)) + ' tests were run' 194 195 dist_dir = get_android_dist_dir_or_die() 196 log_output_path = os.path.join(dist_dir, 'gtest/coverage') 197 cmd_path = os.path.join(get_android_root_or_die(), 'packages/modules/Bluetooth/system/test') 198 print cmd_path 199 cmd = [ 200 os.path.join(cmd_path, 'gen_coverage.py'), 201 '--skip-html', 202 '--json-file', 203 '-o', 204 log_output_path, 205 ] 206 subprocess.call(cmd) 207 208 sys.exit(0) 209 210 211if __name__ == '__main__': 212 main() 213