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