• 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    '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