• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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