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

Unified Diff: fix_test_cases.py

Issue 11048019: Add everything from src/tools/isolate r159537. (Closed) Base URL: https://git.chromium.org/chromium/tools/swarm_client.git@master
Patch Set: Ensure --similarity is sticky Created 8 years, 2 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 | « README.py ('k') | isolate.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: fix_test_cases.py
diff --git a/fix_test_cases.py b/fix_test_cases.py
new file mode 100755
index 0000000000000000000000000000000000000000..98fcdbc905c41df018da2bec6ec18920a4486b55
--- /dev/null
+++ b/fix_test_cases.py
@@ -0,0 +1,224 @@
+#!/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.
+
+"""Runs a test, grab the failures and trace them."""
+
+import json
+import os
+import subprocess
+import sys
+import tempfile
+
+import run_test_cases
+
+
+XVFB_PATH = os.path.join('..', '..', 'testing', 'xvfb.py')
+
+
+if sys.platform == 'win32':
+ import msvcrt # pylint: disable=F0401
+
+ def get_keyboard():
+ """Returns a letter from the keyboard if any.
+
+ This function returns immediately.
+ """
+ if msvcrt.kbhit():
+ return ord(msvcrt.getch())
+
+else:
+ import select
+
+ def get_keyboard():
+ """Returns a letter from the keyboard if any, as soon as he pressed enter.
+
+ This function returns (almost) immediately.
+
+ The library doesn't give a way to just get the initial letter.
+ """
+ if select.select([sys.stdin], [], [], 0.00001)[0]:
+ return sys.stdin.read(1)
+
+
+def trace_and_merge(result, test):
+ """Traces a single test case and merges the result back into .isolate."""
+ env = os.environ.copy()
+ env['RUN_TEST_CASES_RUN_ALL'] = '1'
+
+ print 'Starting trace of %s' % test
+ subprocess.call(
+ [
+ sys.executable, 'isolate.py', 'trace', '-r', result,
+ '--', '--gtest_filter=' + test,
+ ],
+ env=env)
+
+ print 'Starting merge of %s' % test
+ return not subprocess.call(
+ [sys.executable, 'isolate.py', 'merge', '-r', result])
+
+
+def run_all(result, shard_index, shard_count):
+ """Runs all the tests. Returns the tests that failed or None on failure.
+
+ Assumes run_test_cases.py is implicitly called.
+ """
+ handle, result_file = tempfile.mkstemp(prefix='run_test_cases')
+ os.close(handle)
+ env = os.environ.copy()
+ env['RUN_TEST_CASES_RESULT_FILE'] = result_file
+ env['RUN_TEST_CASES_RUN_ALL'] = '1'
+ env['GTEST_SHARD_INDEX'] = str(shard_index)
+ env['GTEST_TOTAL_SHARDS'] = str(shard_count)
+ cmd = [sys.executable, 'isolate.py', 'run', '-r', result]
+ subprocess.call(cmd, env=env)
+ if not os.path.isfile(result_file):
+ print >> sys.stderr, 'Failed to find %s' % result_file
+ return None
+ with open(result_file) as f:
+ try:
+ data = json.load(f)
+ except ValueError as e:
+ print >> sys.stderr, ('Unable to load json file, %s: %s' %
+ (result_file, str(e)))
+ return None
+ os.remove(result_file)
+ return [
+ test for test, runs in data.iteritems()
+ if not any(not run['returncode'] for run in runs)
+ ]
+
+
+def run(result, test):
+ """Runs a single test case in an isolated environment.
+
+ Returns True if the test passed.
+ """
+ return not subprocess.call([
+ sys.executable, 'isolate.py', 'run', '-r', result,
+ '--', '--gtest_filter=' + test,
+ ])
+
+
+def run_normally(executable, test):
+ return not subprocess.call([
+ sys.executable, XVFB_PATH, os.path.dirname(executable), executable,
+ '--gtest_filter=' + test])
+
+
+def diff_and_commit(test):
+ """Prints the diff and commit."""
+ subprocess.call(['git', 'diff'])
+ subprocess.call(['git', 'commit', '-a', '-m', test])
+
+
+def trace_and_verify(result, test):
+ """Traces a test case, updates .isolate and makes sure it passes afterward.
+
+ Return None if the test was already passing, True on success.
+ """
+ trace_and_merge(result, test)
+ diff_and_commit(test)
+ print 'Verifying trace...'
+ return run(result, test)
+
+
+def fix_all(result, shard_index, shard_count, executable):
+ """Runs all the test cases in a gtest executable and trace the failing tests.
+
+ Returns True on success.
+
+ Makes sure the test passes afterward.
+ """
+ # These could have adverse side-effects.
+ # TODO(maruel): Be more intelligent about it, for now be safe.
+ run_test_cases_env = ['RUN_TEST_CASES_RESULT_FILE', 'RUN_TEST_CASES_RUN_ALL']
+ for i in run_test_cases.KNOWN_GTEST_ENV_VARS + run_test_cases_env:
+ if i in os.environ:
+ print >> 'Please unset %s' % i
+ return False
+
+ test_cases = run_all(result, shard_index, shard_count)
+ if test_cases is None:
+ return False
+
+ print '\nFound %d broken test cases.' % len(test_cases)
+ if not test_cases:
+ return True
+
+ failed_alone = []
+ failures = []
+ fixed_tests = []
+ try:
+ for index, test_case in enumerate(test_cases):
+ if get_keyboard():
+ # Return early.
+ return True
+
+ try:
+ # Check if the test passes normally, because otherwise there is no
+ # reason to trace its failure.
+ if not run_normally(executable, test_case):
+ print '%s is broken when run alone, please fix the test.' % test_case
+ failed_alone.append(test_case)
+ continue
+
+ if not trace_and_verify(result, test_case):
+ failures.append(test_case)
+ print 'Failed to fix %s' % test_case
+ else:
+ fixed_tests.append(test_case)
+ except: # pylint: disable=W0702
+ failures.append(test_case)
+ print 'Failed to fix %s' % test_case
+ print '%d/%d' % (index+1, len(test_cases))
+ finally:
+ print 'Test cases fixed (%d):' % len(fixed_tests)
+ for fixed_test in fixed_tests:
+ print ' %s' % fixed_test
+ print ''
+
+ print 'Test cases still failing (%d):' % len(failures)
+ for failure in failures:
+ print ' %s' % failure
+
+ if failed_alone:
+ print ('Test cases that failed normally when run alone (%d):' %
+ len(failed_alone))
+ for failed in failed_alone:
+ print failed
+ return not failures
+
+
+def main():
+ parser = run_test_cases.OptionParserWithTestSharding(
+ usage='%prog <option> [test]')
+ parser.add_option('-d', '--dir', default='../../out/Release',
+ help='The directory containing the the test executable and '
+ 'result file. Defaults to %default')
+ options, args = parser.parse_args()
+
+ if len(args) != 1:
+ parser.error('Use with the name of the test only, e.g. unit_tests')
+
+ basename = args[0]
+ executable = os.path.join(options.dir, basename)
+ result = '%s.results' % executable
+ if sys.platform in('win32', 'cygwin'):
+ executable += '.exe'
+ if not os.path.isfile(executable):
+ print >> sys.stderr, (
+ '%s doesn\'t exist, please build %s_run' % (executable, basename))
+ return 1
+ if not os.path.isfile(result):
+ print >> sys.stderr, (
+ '%s doesn\'t exist, please build %s_run' % (result, basename))
+ return 1
+
+ return not fix_all(result, options.index, options.shards, executable)
+
+
+if __name__ == '__main__':
+ sys.exit(main())
« no previous file with comments | « README.py ('k') | isolate.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698