Index: trace_test_cases.py |
diff --git a/trace_test_cases.py b/trace_test_cases.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..1cd7b3865fb4c9b5eed63fa63374e6fdb618df4f |
--- /dev/null |
+++ b/trace_test_cases.py |
@@ -0,0 +1,127 @@ |
+#!/usr/bin/env python |
+# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""Traces each test cases of a google-test executable individually. |
+ |
+Gives detailed information about each test case. The logs can be read afterward |
+with ./trace_inputs.py read -l /path/to/executable.logs |
+""" |
+ |
+import logging |
+import multiprocessing |
+import os |
+import sys |
+import time |
+ |
+import run_test_cases |
+import trace_inputs |
+ |
+BASE_DIR = os.path.dirname(os.path.abspath(__file__)) |
+ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR)) |
+ |
+ |
+class Tracer(object): |
+ def __init__(self, tracer, cmd, cwd_dir, progress): |
+ # Constants |
+ self.tracer = tracer |
+ self.cmd = cmd[:] |
+ self.cwd_dir = cwd_dir |
+ self.progress = progress |
+ |
+ def map(self, test_case): |
+ """Traces a single test case and returns its output.""" |
+ cmd = self.cmd[:] |
+ cmd.append('--gtest_filter=%s' % test_case) |
+ tracename = test_case.replace('/', '-') |
+ |
+ out = [] |
+ for retry in range(5): |
+ start = time.time() |
+ returncode, output = self.tracer.trace( |
+ cmd, self.cwd_dir, tracename, True) |
+ duration = time.time() - start |
+ # TODO(maruel): Define a way to detect if an strace log is valid. |
+ valid = True |
+ out.append( |
+ { |
+ 'test_case': test_case, |
+ 'returncode': returncode, |
+ 'duration': duration, |
+ 'valid': valid, |
+ 'output': output, |
+ }) |
+ logging.debug( |
+ 'Tracing %s done: %d, %.1fs' % (test_case, returncode, duration)) |
+ if retry: |
+ self.progress.update_item( |
+ '%s - %d' % (test_case, retry), True, not valid) |
+ else: |
+ self.progress.update_item(test_case, True, not valid) |
+ if valid: |
+ break |
+ return out |
+ |
+ |
+def trace_test_cases(cmd, cwd_dir, test_cases, jobs, logname): |
+ """Traces test cases one by one.""" |
+ assert os.path.isabs(cwd_dir) and os.path.isdir(cwd_dir) |
+ |
+ if not test_cases: |
+ return 0 |
+ |
+ # Resolve any symlink. |
+ cwd_dir = os.path.realpath(cwd_dir) |
+ assert os.path.isdir(cwd_dir) |
+ |
+ progress = run_test_cases.Progress(len(test_cases)) |
+ with run_test_cases.ThreadPool(jobs or multiprocessing.cpu_count()) as pool: |
+ api = trace_inputs.get_api() |
+ api.clean_trace(logname) |
+ with api.get_tracer(logname) as tracer: |
+ function = Tracer(tracer, cmd, cwd_dir, progress).map |
+ for test_case in test_cases: |
+ pool.add_task(function, test_case) |
+ |
+ pool.join(progress, 0.1) |
+ print('') |
+ return 0 |
+ |
+ |
+def main(): |
+ """CLI frontend to validate arguments.""" |
+ parser = run_test_cases.OptionParserTestCases( |
+ usage='%prog <options> [gtest]', |
+ description=sys.modules['__main__'].__doc__) |
+ parser.format_description = lambda *_: parser.description |
+ parser.add_option( |
+ '-o', '--out', |
+ help='output file, defaults to <executable>.test_cases') |
+ options, args = parser.parse_args() |
+ |
+ if not args: |
+ parser.error( |
+ 'Please provide the executable line to run, if you need fancy things ' |
+ 'like xvfb, start this script from *inside* xvfb, it\'ll be much faster' |
+ '.') |
+ |
+ cmd = run_test_cases.fix_python_path(args) |
+ |
+ if not options.out: |
+ options.out = '%s.test_cases' % cmd[-1] |
+ |
+ test_cases = parser.process_gtest_options(cmd, options) |
+ |
+ # Then run them. |
+ return trace_test_cases( |
+ cmd, |
+ os.getcwd(), |
+ test_cases, |
+ options.jobs, |
+ # TODO(maruel): options.timeout, |
+ options.out) |
+ |
+ |
+if __name__ == '__main__': |
+ sys.exit(main()) |