Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 2 # Copyright 2014 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 a test repeatedly to measure its flakiness. The return code is non-zero | |
| 7 if the failure rate is higher than the specified threshold, but is not 100%.""" | |
| 8 | |
| 9 import argparse | |
| 10 import subprocess | |
| 11 import sys | |
| 12 import time | |
| 13 | |
| 14 def load_options(): | |
| 15 parser = argparse.ArgumentParser(description=__doc__) | |
| 16 parser.add_argument('--retries', default=1000, type=int, | |
| 17 help='Number of test retries to measure flakiness.') | |
| 18 parser.add_argument('--threshold', default=0.05, type=float, | |
| 19 help='Minimum flakiness level at which test is ' | |
| 20 'considered flaky.') | |
| 21 parser.add_argument('--jobs', '-j', type=int, default=1, | |
| 22 help='Number of parallel jobs to run tests.') | |
| 23 parser.add_argument('command', nargs='+', help='Command to run test.') | |
| 24 return parser.parse_args() | |
| 25 | |
| 26 | |
| 27 def process_finished(running, num_passed, num_failed): | |
| 28 finished = [p for p in running if p.poll() is not None] | |
| 29 running[:] = [p for p in running if p.poll() is None] | |
|
qyearsley
2014/09/15 16:02:28
What's the difference between using slice assignme
Sergiy Byelozyorov
2014/09/15 16:27:29
This is to modify the value outside of the functio
| |
| 30 num_passed += len([p for p in finished if p.returncode == 0]) | |
| 31 num_failed += len([p for p in finished if p.returncode != 0]) | |
| 32 print '%d processed finished. Total passed: %d. Total failed: %d' % ( | |
| 33 len(finished), num_passed, num_failed) | |
| 34 return num_passed, num_failed | |
| 35 | |
| 36 | |
| 37 def main(): | |
| 38 options = load_options() | |
| 39 num_passed = num_failed = 0 | |
| 40 running = [] | |
| 41 | |
| 42 # Start all retries, while limiting total number of running processes. | |
| 43 for attempt in range(options.retries): | |
| 44 print 'Starting retry %d out of %d\n' % (attempt + 1, options.retries) | |
| 45 running.append(subprocess.Popen(options.command, stdout=subprocess.PIPE, | |
| 46 stderr=subprocess.STDOUT)) | |
| 47 while len(running) >= options.jobs: | |
| 48 print 'Waiting for previous retries to finish before starting new ones...' | |
| 49 time.sleep(0.1) | |
| 50 num_passed, num_failed = process_finished(running, num_passed, num_failed) | |
| 51 | |
| 52 | |
| 53 # Wait for the remaining retries to finish. | |
| 54 print 'Waiting for the remaining retries to finish...' | |
| 55 for process in running: | |
| 56 process.wait() | |
| 57 | |
| 58 num_passed, num_failed = process_finished(running, num_passed, num_failed) | |
| 59 if num_passed == 0 or num_failed == 0: | |
| 60 flakiness = 0 | |
| 61 else: | |
| 62 flakiness = num_failed / (options.retries * 1.0) | |
|
qyearsley
2014/09/15 16:02:28
Equivalently, float(options.retries) would do the
Sergiy Byelozyorov
2014/09/15 16:27:29
Done.
| |
| 63 | |
| 64 print 'Flakiness is %.2f' % flakiness | |
| 65 if flakiness > options.threshold: | |
| 66 return 1 | |
| 67 else: | |
| 68 return 0 | |
| 69 | |
| 70 | |
| 71 if __name__ == '__main__': | |
| 72 sys.exit(main()) | |
| OLD | NEW |