Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """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
| |
| 7 those that hold on to shared resources. The idea is that if a test uses a | |
| 8 unary resource, then running many instances of this test will purge out some | |
| 9 of them as failures or timeouts. | |
| 10 """ | |
| 11 | |
| 12 | |
| 13 import optparse | |
| 14 import os | |
| 15 import re | |
| 16 import subprocess | |
| 17 import time | |
| 18 | |
| 19 | |
| 20 PF_USAGE = 'python %prog [options] path/to/test' | |
| 21 PF_DEFAULT_OUTPUT_SUFFIX = '_purges' | |
| 22 PF_DEFAULT_NUM_PROCS = 20 | |
| 23 PF_DEFAULT_NUM_REPEATS = 10 | |
| 24 PF_DEFAULT_TIMEOUT = 600 | |
| 25 | |
| 26 | |
| 27 def PurgeFlakies(test_path, output_path, num_procs, num_repeats, timeout): | |
| 28 test_name_regex = r'((\w+/)?\w+\.\w+(/\d+)?)' | |
| 29 test_start = re.compile('\[\s+RUN\s+\] ' + test_name_regex) | |
| 30 test_list = [] | |
| 31 | |
| 32 # run the test to discover all the test cases | |
| 33 proc = subprocess.Popen([test_path], stdout=subprocess.PIPE) | |
| 34 while True: | |
| 35 line = proc.stdout.readline() | |
| 36 if not line: | |
| 37 if proc.poll() is not None: | |
| 38 break | |
| 39 continue | |
| 40 print line.rstrip() | |
| 41 results = test_start.search(line) | |
| 42 if results: | |
| 43 test_list.append(results.group(1)) | |
| 44 | |
| 45 failures = [] | |
| 46 index = 0 | |
| 47 total = len(test_list) | |
| 48 | |
| 49 # run each test case in parallel with itself | |
| 50 for test_name in test_list: | |
| 51 num_fails = 0 | |
| 52 num_terminated = 0 | |
| 53 procs = [] | |
| 54 args = [test_path, '--gtest_filter=' + test_name, | |
| 55 '--gtest_repeat=%i' % num_repeats] | |
| 56 while len(procs) < num_procs: | |
| 57 procs.append(subprocess.Popen(args)) | |
| 58 seconds = 0 | |
| 59 while procs: | |
| 60 for proc in procs: | |
| 61 if proc.poll() is not None: | |
| 62 if proc.returncode != 0: | |
| 63 ++num_fails | |
| 64 procs.remove(proc) | |
| 65 # timeout exceeded, kill the remaining processes and make a note | |
| 66 if seconds > timeout: | |
| 67 num_fails += len(procs) | |
| 68 num_terminated = len(procs) | |
| 69 while procs: | |
| 70 procs.pop().terminate() | |
| 71 time.sleep(1.0) | |
| 72 seconds += 1 | |
| 73 if num_fails: | |
| 74 line = '%s: %i failed' % (test_name, num_fails) | |
| 75 if num_terminated: | |
| 76 line += ' (%i terminated)' % num_terminated | |
| 77 failures.append(line) | |
| 78 print '%s (%i / %i): %i failed' % (test_name, index, total, num_fails) | |
| 79 index += 1 | |
| 80 time.sleep(1.0) | |
| 81 | |
| 82 # print the results and write the data file | |
| 83 print failures | |
| 84 data_file = open(output_path, 'w') | |
| 85 for line in failures: | |
| 86 data_file.write(line + '\n') | |
| 87 data_file.close() | |
| 88 | |
| 89 | |
| 90 def main(): | |
| 91 parser = optparse.OptionParser(usage=PF_USAGE) | |
| 92 parser.add_option( | |
| 93 '--output-path', | |
| 94 help='path to output file (default is to append "%s" to the end of the' | |
| 95 ' test name in the current directory)' % PF_DEFAULT_OUTPUT_SUFFIX) | |
| 96 parser.add_option( | |
| 97 '--procs', type='int', default=PF_DEFAULT_NUM_PROCS, | |
| 98 help='number of parallel test processes to start up' | |
| 99 ' (default = %s)' % PF_DEFAULT_NUM_PROCS) | |
| 100 parser.add_option( | |
| 101 '--repeats', type='int', default=PF_DEFAULT_NUM_REPEATS, | |
| 102 help='number of times to repeat each test in each process' | |
| 103 ' (default = %s)' % PF_DEFAULT_NUM_REPEATS) | |
| 104 parser.add_option( | |
| 105 '--timeout', type='int', default=PF_DEFAULT_TIMEOUT, | |
| 106 help='test processes are killed if their runtimes exceed the timeout,' | |
| 107 ' useful for some tests (default = %s)' % PF_DEFAULT_TIMEOUT) | |
| 108 (options, args) = parser.parse_args() | |
| 109 | |
| 110 if not args: | |
| 111 parser.error('You must specify a path to test!') | |
| 112 if not os.path.exists(args[0]): | |
| 113 parser.error('%s does not exist!' % args[0]) | |
| 114 | |
| 115 if not options.output_path: | |
| 116 options.output_path = os.path.basename(args[0]) + PF_DEFAULT_OUTPUT_SUFFIX | |
| 117 | |
| 118 PurgeFlakies(args[0], options.output_path, options.procs, options.repeats, | |
| 119 options.timeout) | |
| 120 | |
| 121 | |
| 122 if __name__ == '__main__': | |
| 123 main() | |
| OLD | NEW |