Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Unified Diff: tools/isolate/trace_test_cases.py

Issue 10377105: Add scripts to list or trace all test cases in a gtest executable. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/isolate/trace_inputs.py ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/isolate/trace_test_cases.py
diff --git a/tools/isolate/trace_test_cases.py b/tools/isolate/trace_test_cases.py
new file mode 100755
index 0000000000000000000000000000000000000000..41abb9b188768269dfba0cd5277204f485b2804d
--- /dev/null
+++ b/tools/isolate/trace_test_cases.py
@@ -0,0 +1,209 @@
+#!/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.
+
+"""A manual version of trace_inputs.py that is specialized in tracing each
+google-test test case individually.
+
+This is mainly written to work around bugs in strace w.r.t. browser_tests.
+"""
+
+import fnmatch
+import json
+import multiprocessing
+import optparse
+import os
+import sys
+import tempfile
+import time
+
+import list_test_cases
+import trace_inputs
+
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR))
+
+
+def trace_test_case(
+ test_case, executable, root_dir, cwd_dir, product_dir, leak):
+ """Traces a single test case and returns the .isolate compatible variable
+ dict.
+ """
+ # Resolve any symlink
+ root_dir = os.path.realpath(root_dir)
+
+ api = trace_inputs.get_api()
+ cmd = [executable, '--gtest_filter=%s' % test_case]
+
+ if not leak:
+ f, logname = tempfile.mkstemp(prefix='trace')
+ os.close(f)
+ else:
+ logname = '%s.%s.log' % (executable, test_case.replace('/', '-'))
+ f = None
+
+ try:
+ for i in range(3):
+ start = time.time()
+ returncode, output = trace_inputs.trace(
+ logname, cmd, os.path.join(root_dir, cwd_dir), api, True)
+ if returncode and i != 2:
+ print '\nFailed while running: %s' % ' '.join(cmd)
+ print 'Trying again!'
+ continue
+ duration = time.time() - start
+ try:
+ _, _, _, _, simplified = trace_inputs.load_trace(logname, root_dir, api)
+ break
+ except Exception:
+ print '\nFailed loading the trace for: %s' % ' '.join(cmd)
+ print 'Trying again!'
+
+ variables = trace_inputs.generate_dict(simplified, cwd_dir, product_dir)
+ return {
+ 'case': test_case,
+ 'variables': variables,
+ 'result': returncode,
+ 'duration': duration,
+ 'output': output,
+ }
+ finally:
+ if f:
+ os.remove(logname)
+
+
+def task(args):
+ """Adaptor for multiprocessing.Pool().imap_unordered().
+
+ It is executed asynchronously.
+ """
+ return trace_test_case(*args)
+
+
+def get_test_cases(executable, skip):
+ """Returns the filtered list of test cases.
+
+ This is done synchronously.
+ """
+ try:
+ out = list_test_cases.gtest_list_tests(executable)
+ except list_test_cases.Failure, e:
+ print e.args[0]
+ return None
+
+ tests = list_test_cases.parse_gtest_cases(out)
+ tests = [t for t in tests if not any(fnmatch.fnmatch(t, s) for s in skip)]
+ print 'Found %d test cases in %s' % (len(tests), os.path.basename(executable))
+ return tests
+
+
+def trace_test_cases(
+ executable, root_dir, cwd_dir, product_dir, leak, skip, jobs, timeout):
+ """Traces test cases one by one."""
+ tests = get_test_cases(executable, skip)
+ if not tests:
+ return
+
+ last_line = ''
+ out = {}
+ index = 0
+ pool = multiprocessing.Pool(processes=jobs)
+ start = time.time()
+ try:
+ g = ((t, executable, root_dir, cwd_dir, product_dir, leak) for t in tests)
+ it = pool.imap_unordered(task, g)
+ while True:
+ try:
+ result = it.next(timeout=timeout)
+ except StopIteration:
+ break
+ case = result.pop('case')
+ index += 1
+ line = '%d of %d (%.1f%%), %.1fs: %s' % (
+ index,
+ len(tests),
+ index * 100. / len(tests),
+ time.time() - start,
+ case)
+ sys.stdout.write(
+ '\r%s%s' % (line, ' ' * max(0, len(last_line) - len(line))))
+ sys.stdout.flush()
+ last_line = line
+ # TODO(maruel): Retry failed tests.
+ out[case] = result
+ return 0
+ except multiprocessing.TimeoutError, e:
+ print 'Got a timeout while processing a task item %s' % e
+ # Be sure to stop the pool on exception.
+ pool.terminate()
+ return 1
+ except Exception, e:
+ # Be sure to stop the pool on exception.
+ pool.terminate()
+ raise
+ finally:
+ with open('%s.test_cases' % executable, 'w') as f:
+ json.dump(out, f, indent=2, sort_keys=True)
+ pool.close()
+ pool.join()
+
+
+def main():
+ """CLI frontend to validate arguments."""
+ parser = optparse.OptionParser(
+ usage='%prog <options> [gtest]')
+ parser.allow_interspersed_args = False
+ parser.add_option(
+ '-c', '--cwd',
+ default='chrome',
+ help='Signal to start the process from this relative directory. When '
+ 'specified, outputs the inputs files in a way compatible for '
+ 'gyp processing. Should be set to the relative path containing the '
+ 'gyp file, e.g. \'chrome\' or \'net\'')
+ parser.add_option(
+ '-p', '--product-dir',
+ default='out/Release',
+ help='Directory for PRODUCT_DIR. Default: %default')
+ parser.add_option(
+ '--root-dir',
+ default=ROOT_DIR,
+ help='Root directory to base everything off. Default: %default')
+ parser.add_option(
+ '-l', '--leak',
+ action='store_true',
+ help='Leak trace files')
+ parser.add_option(
+ '-s', '--skip',
+ default=[],
+ action='append',
+ help='filter to apply to test cases to skip, wildcard-style')
+ parser.add_option(
+ '-j', '--jobs',
+ type='int',
+ help='number of parallel jobs')
+ parser.add_option(
+ '-t', '--timeout',
+ default=120,
+ type='int',
+ help='number of parallel jobs')
+ options, args = parser.parse_args()
+
+ if len(args) != 1:
+ 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 faster.')
+ executable = os.path.join(options.root_dir, options.product_dir, args[0])
+ return trace_test_cases(
+ executable,
+ options.root_dir,
+ options.cwd,
+ options.product_dir,
+ options.leak,
+ options.skip,
+ options.jobs,
+ options.timeout)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
« no previous file with comments | « tools/isolate/trace_inputs.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698