OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """Traces each test cases of a google-test executable individually. |
| 7 |
| 8 Gives detailed information about each test case. The logs can be read afterward |
| 9 with ./trace_inputs.py read -l /path/to/executable.logs |
| 10 """ |
| 11 |
| 12 import logging |
| 13 import multiprocessing |
| 14 import os |
| 15 import sys |
| 16 import time |
| 17 |
| 18 import run_test_cases |
| 19 import trace_inputs |
| 20 |
| 21 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
| 22 ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR)) |
| 23 |
| 24 |
| 25 class Tracer(object): |
| 26 def __init__(self, tracer, cmd, cwd_dir, progress): |
| 27 # Constants |
| 28 self.tracer = tracer |
| 29 self.cmd = cmd[:] |
| 30 self.cwd_dir = cwd_dir |
| 31 self.progress = progress |
| 32 |
| 33 def map(self, test_case): |
| 34 """Traces a single test case and returns its output.""" |
| 35 cmd = self.cmd[:] |
| 36 cmd.append('--gtest_filter=%s' % test_case) |
| 37 tracename = test_case.replace('/', '-') |
| 38 |
| 39 out = [] |
| 40 for retry in range(5): |
| 41 start = time.time() |
| 42 returncode, output = self.tracer.trace( |
| 43 cmd, self.cwd_dir, tracename, True) |
| 44 duration = time.time() - start |
| 45 # TODO(maruel): Define a way to detect if an strace log is valid. |
| 46 valid = True |
| 47 out.append( |
| 48 { |
| 49 'test_case': test_case, |
| 50 'returncode': returncode, |
| 51 'duration': duration, |
| 52 'valid': valid, |
| 53 'output': output, |
| 54 }) |
| 55 logging.debug( |
| 56 'Tracing %s done: %d, %.1fs' % (test_case, returncode, duration)) |
| 57 if retry: |
| 58 self.progress.update_item( |
| 59 '%s - %d' % (test_case, retry), True, not valid) |
| 60 else: |
| 61 self.progress.update_item(test_case, True, not valid) |
| 62 if valid: |
| 63 break |
| 64 return out |
| 65 |
| 66 |
| 67 def trace_test_cases(cmd, cwd_dir, test_cases, jobs, logname): |
| 68 """Traces test cases one by one.""" |
| 69 assert os.path.isabs(cwd_dir) and os.path.isdir(cwd_dir) |
| 70 |
| 71 if not test_cases: |
| 72 return 0 |
| 73 |
| 74 # Resolve any symlink. |
| 75 cwd_dir = os.path.realpath(cwd_dir) |
| 76 assert os.path.isdir(cwd_dir) |
| 77 |
| 78 progress = run_test_cases.Progress(len(test_cases)) |
| 79 with run_test_cases.ThreadPool(jobs or multiprocessing.cpu_count()) as pool: |
| 80 api = trace_inputs.get_api() |
| 81 api.clean_trace(logname) |
| 82 with api.get_tracer(logname) as tracer: |
| 83 function = Tracer(tracer, cmd, cwd_dir, progress).map |
| 84 for test_case in test_cases: |
| 85 pool.add_task(function, test_case) |
| 86 |
| 87 pool.join(progress, 0.1) |
| 88 print('') |
| 89 return 0 |
| 90 |
| 91 |
| 92 def main(): |
| 93 """CLI frontend to validate arguments.""" |
| 94 parser = run_test_cases.OptionParserTestCases( |
| 95 usage='%prog <options> [gtest]', |
| 96 description=sys.modules['__main__'].__doc__) |
| 97 parser.format_description = lambda *_: parser.description |
| 98 parser.add_option( |
| 99 '-o', '--out', |
| 100 help='output file, defaults to <executable>.test_cases') |
| 101 options, args = parser.parse_args() |
| 102 |
| 103 if not args: |
| 104 parser.error( |
| 105 'Please provide the executable line to run, if you need fancy things ' |
| 106 'like xvfb, start this script from *inside* xvfb, it\'ll be much faster' |
| 107 '.') |
| 108 |
| 109 cmd = run_test_cases.fix_python_path(args) |
| 110 |
| 111 if not options.out: |
| 112 options.out = '%s.test_cases' % cmd[-1] |
| 113 |
| 114 test_cases = parser.process_gtest_options(cmd, options) |
| 115 |
| 116 # Then run them. |
| 117 return trace_test_cases( |
| 118 cmd, |
| 119 os.getcwd(), |
| 120 test_cases, |
| 121 options.jobs, |
| 122 # TODO(maruel): options.timeout, |
| 123 options.out) |
| 124 |
| 125 |
| 126 if __name__ == '__main__': |
| 127 sys.exit(main()) |
OLD | NEW |