Chromium Code Reviews| Index: tools/purge_flakies.py |
| diff --git a/tools/purge_flakies.py b/tools/purge_flakies.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..551490ab43a5e3378fd3e1cd02a6009568f4e00b |
| --- /dev/null |
| +++ b/tools/purge_flakies.py |
| @@ -0,0 +1,123 @@ |
| +#!/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. |
| + |
| +"""Runs all the test cases in a given test in parallel with itself, to get at |
|
Paweł Hajdan Jr.
2011/08/19 18:35:50
Would it make sense to combine this in a single fi
clee
2011/08/19 21:06:35
Ideally yes, but the command line options to the t
|
| +those that hold on to shared resources. The idea is that if a test uses a |
| +unary resource, then running many instances of this test will purge out some |
| +of them as failures or timeouts. |
| +""" |
| + |
| + |
| +import optparse |
| +import os |
| +import re |
| +import subprocess |
| +import time |
| + |
| + |
| +PF_USAGE = 'python %prog [options] path/to/test' |
| +PF_DEFAULT_OUTPUT_SUFFIX = '_purges' |
| +PF_DEFAULT_NUM_PROCS = 20 |
| +PF_DEFAULT_NUM_REPEATS = 10 |
| +PF_DEFAULT_TIMEOUT = 600 |
| + |
| + |
| +def PurgeFlakies(test_path, output_path, num_procs, num_repeats, timeout): |
| + test_name_regex = r'((\w+/)?\w+\.\w+(/\d+)?)' |
| + test_start = re.compile('\[\s+RUN\s+\] ' + test_name_regex) |
| + test_list = [] |
| + |
| + # run the test to discover all the test cases |
| + proc = subprocess.Popen([test_path], stdout=subprocess.PIPE) |
| + while True: |
| + line = proc.stdout.readline() |
| + if not line: |
| + if proc.poll() is not None: |
| + break |
| + continue |
| + print line.rstrip() |
| + results = test_start.search(line) |
| + if results: |
| + test_list.append(results.group(1)) |
| + |
| + failures = [] |
| + index = 0 |
| + total = len(test_list) |
| + |
| + # run each test case in parallel with itself |
| + for test_name in test_list: |
| + num_fails = 0 |
| + num_terminated = 0 |
| + procs = [] |
| + args = [test_path, '--gtest_filter=' + test_name, |
| + '--gtest_repeat=%i' % num_repeats] |
| + while len(procs) < num_procs: |
| + procs.append(subprocess.Popen(args)) |
| + seconds = 0 |
| + while procs: |
| + for proc in procs: |
| + if proc.poll() is not None: |
| + if proc.returncode != 0: |
| + ++num_fails |
| + procs.remove(proc) |
| + # timeout exceeded, kill the remaining processes and make a note |
| + if seconds > timeout: |
| + num_fails += len(procs) |
| + num_terminated = len(procs) |
| + while procs: |
| + procs.pop().terminate() |
| + time.sleep(1.0) |
| + seconds += 1 |
| + if num_fails: |
| + line = '%s: %i failed' % (test_name, num_fails) |
| + if num_terminated: |
| + line += ' (%i terminated)' % num_terminated |
| + failures.append(line) |
| + print '%s (%i / %i): %i failed' % (test_name, index, total, num_fails) |
| + index += 1 |
| + time.sleep(1.0) |
| + |
| + # print the results and write the data file |
| + print failures |
| + data_file = open(output_path, 'w') |
| + for line in failures: |
| + data_file.write(line + '\n') |
| + data_file.close() |
| + |
| + |
| +def main(): |
| + parser = optparse.OptionParser(usage=PF_USAGE) |
| + parser.add_option( |
| + '--output-path', |
| + help='path to output file (default is to append "%s" to the end of the' |
| + ' test name in the current directory)' % PF_DEFAULT_OUTPUT_SUFFIX) |
| + parser.add_option( |
| + '--procs', type='int', default=PF_DEFAULT_NUM_PROCS, |
| + help='number of parallel test processes to start up' |
| + ' (default = %s)' % PF_DEFAULT_NUM_PROCS) |
| + parser.add_option( |
| + '--repeats', type='int', default=PF_DEFAULT_NUM_REPEATS, |
| + help='number of times to repeat each test in each process' |
| + ' (default = %s)' % PF_DEFAULT_NUM_REPEATS) |
| + parser.add_option( |
| + '--timeout', type='int', default=PF_DEFAULT_TIMEOUT, |
| + help='test processes are killed if their runtimes exceed the timeout,' |
| + ' useful for some tests (default = %s)' % PF_DEFAULT_TIMEOUT) |
| + (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.output_path: |
| + options.output_path = os.path.basename(args[0]) + PF_DEFAULT_OUTPUT_SUFFIX |
| + |
| + PurgeFlakies(args[0], options.output_path, options.procs, options.repeats, |
| + options.timeout) |
| + |
| + |
| +if __name__ == '__main__': |
| + main() |