Index: tools/find_flakies.py |
diff --git a/tools/find_flakies.py b/tools/find_flakies.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..a4162693446f643e9cd0bcef49501207fd69ea5d |
--- /dev/null |
+++ b/tools/find_flakies.py |
@@ -0,0 +1,110 @@ |
+#!/usr/bin/env python |
+# Copyright (c) 2011 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. |
+ |
+"""Finds flaky test cases by sharding and running a test for the specified |
+number of times. The data file is read at the beginning of each run to find |
+the last known counts and is overwritten at the end of each run with the new |
+counts. There is an optional sleep interval between each run so the script can |
+be killed without losing the data, useful for overnight (or weekend!) runs. |
+""" |
+ |
+ |
+import optparse |
+import os |
+import random |
+import subprocess |
+import sys |
+import time |
+ |
+ |
+FF_USAGE = 'python %prog [options] path/to/test [sharding_supervisor_args]' |
+FF_DEFAULT_DATA_SUFFIX = '_flakies' |
+FF_DEFAULT_SLEEP_INTERVAL = 10.0 |
+FF_DEFAULT_NUM_ITERATIONS = 100 |
+FF_DEFAULT_SUPERVISOR_ARGS = ['-r3', '--random-seed'] |
+ |
+ |
+def FindFlakies(test_path, data_path, supervisor_args): |
+ failed_tests = {} |
+ if os.path.exists(data_path): |
+ data_file = open(data_path, 'r') |
+ num_runs = int(data_file.readline().split(' ')[0]) |
+ num_passes = int(data_file.readline().split(' ')[0]) |
+ for line in data_file: |
+ if line: |
+ split_line = line.split(' -> ') |
+ failed_tests[split_line[0]] = int(split_line[1]) |
+ data_file.close() |
+ else: |
+ num_runs = 0 |
+ num_passes = 0 |
+ log_lines = False |
+ args = ['python', 'sharding_supervisor/sharding_supervisor.py'] |
+ args.extend(supervisor_args + [test_path]) |
+ proc = subprocess.Popen(args, stderr=subprocess.PIPE) |
+ while True: |
+ line = proc.stderr.readline() |
+ if not line: |
+ if proc.poll() is not None: |
+ break |
+ continue |
+ sys.stderr.write(line) |
+ if log_lines: |
+ line = line.rstrip() |
+ if line in failed_tests: |
+ failed_tests[line] += 1 |
+ else: |
+ failed_tests[line] = 1 |
+ elif line.find('FAILED TESTS:') >= 0: |
+ log_lines = True |
+ num_runs += 1 |
+ if proc.returncode == 0: |
+ num_passes += 1 |
+ data_file = open(data_path, 'w') |
+ print '%i runs' % num_runs |
+ data_file.write('%i runs\n' % num_runs) |
+ print '%i passes' % num_passes |
+ data_file.write('%i passes\n' % num_passes) |
+ for (test, count) in failed_tests.iteritems(): |
+ print '%s -> %i' % (test, count) |
+ data_file.write('%s -> %i\n' % (test, count)) |
+ data_file.close() |
+ |
+ |
+def main(): |
+ parser = optparse.OptionParser(usage=FF_USAGE) |
+ parser.add_option( |
+ '--data-path', |
+ help='path to i/o file (default is to append "%s" to the end of the' |
+ ' test name in the current directory)' % FF_DEFAULT_DATA_SUFFIX) |
+ parser.add_option( |
+ '--sleep', type='float', default=FF_DEFAULT_SLEEP_INTERVAL, |
+ help='sleep interval between each run, useful for overnight runs' |
+ ' (default = %i)' % FF_DEFAULT_SLEEP_INTERVAL) |
+ parser.add_option( |
+ '--iterations', type='int', default=FF_DEFAULT_NUM_ITERATIONS, |
+ help='number of times to run the sharded test' |
+ ' (default = %i)' % FF_DEFAULT_NUM_ITERATIONS) |
+ parser.disable_interspersed_args() |
+ (options, args) = parser.parse_args() |
+ |
+ if not args: |
+ parser.error('You must specify a path to test!') |
+ if not os.path.exists(args[0]): |
+ parser.error('%s does not exist!' % args[0]) |
+ |
+ if not options.data_path: |
+ options.data_path = os.path.basename(args[0]) + FF_DEFAULT_DATA_SUFFIX |
+ |
+ supervisor_args = FF_DEFAULT_SUPERVISOR_ARGS + args[1:] |
+ |
+ for i in range(options.iterations): |
+ FindFlakies(args[0], options.data_path, supervisor_args) |
+ print 'That was just iteration %i of %i' % (i + 1, options.iterations) |
+ time.sleep(options.sleep) |
+ |
+ |
+if __name__ == '__main__': |
+ main() |