| # Copyright (C) 2014 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| import getopt |
| import multiprocessing |
| import os |
| import re |
| import subprocess |
| import sys |
| |
| |
| class ProgressBarWrapper(object): |
| def __init__(self, maxval): |
| try: |
| import progressbar |
| self.pb = progressbar.ProgressBar(maxval=maxval) |
| except ImportError: |
| self.pb = None |
| |
| def start(self): |
| if self.pb: |
| self.pb.start() |
| |
| def update(self, value): |
| if self.pb: |
| self.pb.update(value) |
| |
| def finish(self): |
| if self.pb: |
| self.pb.finish() |
| |
| |
| class HostTest(object): |
| def __init__(self, path): |
| self.src_path = re.sub(r'\.pass\.cpp', '', path) |
| self.name = '{0}'.format(self.src_path) |
| self.path = '{0}/bin/libc++tests/{1}'.format( |
| os.getenv('ANDROID_HOST_OUT'), self.name) |
| |
| def run(self): |
| return subprocess.call(['timeout', '30', self.path], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| |
| |
| class DeviceTest(object): |
| def __init__(self, path): |
| self.src_path = re.sub(r'\.pass\.cpp', '', path) |
| self.name = '{0}'.format(self.src_path) |
| self.path = '/system/bin/libc++tests/{0}'.format(self.name) |
| |
| def run(self): |
| return adb_shell(self.path) |
| |
| |
| def adb_shell(command): |
| proc = subprocess.Popen(['timeout', '30', |
| 'adb', 'shell', '{0}; echo $? 2>&1'.format(command)], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| out, err = proc.communicate() |
| proc.wait() |
| if proc.returncode: |
| return proc.returncode |
| out = [x for x in out.split('\r\n') if x] |
| return int(out[-1]) |
| |
| |
| def get_all_tests(subdir): |
| tests = {'host': [], 'device': []} |
| for path, _dirs, files in os.walk(subdir): |
| path = os.path.normpath(path) |
| if path == '.': |
| path = '' |
| for test in [t for t in files if t.endswith('.pass.cpp')]: |
| tests['host'].append(HostTest(os.path.join(path, test))) |
| tests['device'].append(DeviceTest(os.path.join(path, test))) |
| return tests |
| |
| |
| def get_tests_in_subdirs(subdirs): |
| tests = {'host': [], 'device': []} |
| for subdir in subdirs: |
| subdir_tests = get_all_tests(subdir=subdir) |
| tests['host'].extend(subdir_tests['host']) |
| tests['device'].extend(subdir_tests['device']) |
| return tests |
| |
| |
| def run_tests(tests, num_threads): |
| pb = ProgressBarWrapper(maxval=len(tests)) |
| pool = multiprocessing.Pool(num_threads) |
| |
| pb.start() |
| results = pool.imap(pool_task, tests) |
| num_run = {'host': 0, 'device': 0} |
| failures = {'host': [], 'device': []} |
| for name, status, target in results: |
| num_run[target] += 1 |
| if status: |
| failures[target].append(name) |
| pb.update(sum(num_run.values())) |
| pb.finish() |
| return {'num_run': num_run, 'failures': failures} |
| |
| |
| def report_results(results): |
| num_run = results['num_run'] |
| failures = results['failures'] |
| failed_both = sorted(filter( |
| lambda x: x in failures['host'], |
| failures['device'])) |
| |
| for target, failed in failures.iteritems(): |
| failed = [x for x in failed if x not in failed_both] |
| print '{0} tests run: {1}'.format(target, num_run[target]) |
| print '{0} tests failed: {1}'.format(target, len(failed)) |
| for failure in sorted(failed): |
| print '\t{0}'.format(failure) |
| print |
| |
| if len(failed_both): |
| print '{0} tests failed in both environments'.format(len(failed_both)) |
| for failure in failed_both: |
| print '\t{0}'.format(failure) |
| |
| |
| def pool_task(test): |
| target = 'host' if isinstance(test, HostTest) else 'device' |
| #print '{0} run {1}'.format(target, test.name) |
| return (test.name, test.run(), target) |
| |
| |
| def main(): |
| try: |
| opts, args = getopt.getopt( |
| sys.argv[1:], 'n:t:', ['threads=', 'target=']) |
| except getopt.GetoptError as err: |
| sys.exit(str(err)) |
| |
| subdirs = ['.'] |
| target = 'both' |
| num_threads = multiprocessing.cpu_count() * 2 |
| for opt, arg in opts: |
| if opt in ('-n', '--threads'): |
| num_threads = int(arg) |
| elif opt in ('-t', '--target'): |
| target = arg |
| else: |
| sys.exit('Unknown option {0}'.format(opt)) |
| |
| if len(args): |
| subdirs = args |
| |
| tests = get_tests_in_subdirs(subdirs) |
| if target == 'both': |
| tests = tests['host'] + tests['device'] |
| else: |
| tests = tests[target] |
| |
| results = run_tests(tests, num_threads) |
| report_results(results) |
| |
| |
| if __name__ == '__main__': |
| main() |