Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Runs each test cases as a single shard, single process execution. | 6 """Runs each test cases as a single shard, single process execution. |
| 7 | 7 |
| 8 Similar to sharding_supervisor.py but finer grained. It runs each test case | 8 Similar to sharding_supervisor.py but finer grained. It runs each test case |
| 9 individually instead of running per shard. Runs multiple instances in parallel. | 9 individually instead of running per shard. Runs multiple instances in parallel. |
| 10 """ | 10 """ |
| (...skipping 645 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 656 # times in total. | 656 # times in total. |
| 657 self.retries = retries | 657 self.retries = retries |
| 658 self.decider = decider | 658 self.decider = decider |
| 659 self.verbose = verbose | 659 self.verbose = verbose |
| 660 self.add_task = add_task | 660 self.add_task = add_task |
| 661 self.add_serial_task = add_serial_task | 661 self.add_serial_task = add_serial_task |
| 662 # It is important to remove the shard environment variables since it could | 662 # It is important to remove the shard environment variables since it could |
| 663 # conflict with --gtest_filter. | 663 # conflict with --gtest_filter. |
| 664 self.env = setup_gtest_env() | 664 self.env = setup_gtest_env() |
| 665 | 665 |
| 666 def map(self, priority, test_case, try_count): | 666 def map(self, priority, test_cases, try_count): |
| 667 """Traces a single test case and returns its output. | 667 """Traces a single test case and returns its output. |
| 668 | 668 |
| 669 try_count is 0 based, the original try is 0. | 669 try_count is 0 based, the original try is 0. |
| 670 """ | 670 """ |
| 671 if self.decider.should_stop(): | 671 if self.decider.should_stop(): |
| 672 return [] | 672 return [] |
| 673 | 673 |
| 674 cmd = self.cmd + ['--gtest_filter=%s' % ':'.join(test_cases)] | |
| 675 if '--gtest_print_time' not in cmd: | |
| 676 cmd.append('--gtest_print_time') | |
| 674 start = time.time() | 677 start = time.time() |
| 675 output, returncode = call_with_timeout( | 678 output, returncode = call_with_timeout( |
| 676 self.cmd + ['--gtest_filter=%s' % test_case], | 679 cmd, |
| 677 self.timeout, | 680 self.timeout, |
| 678 cwd=self.cwd_dir, | 681 cwd=self.cwd_dir, |
| 679 stderr=subprocess.STDOUT, | 682 stderr=subprocess.STDOUT, |
| 680 env=self.env) | 683 env=self.env) |
| 681 duration = time.time() - start | 684 duration = time.time() - start |
| 682 data = { | 685 |
| 683 'test_case': test_case, | 686 # It needs to be valid utf-8 otherwise it can't be store. |
|
csharp
2013/03/11 13:33:22
nit: store -> stored
M-A Ruel
2013/03/11 17:30:53
Done.
| |
| 684 'returncode': returncode, | 687 # TODO(maruel): Be more intelligent than decoding to ascii. |
| 685 'duration': duration, | 688 utf8_output = output.decode('ascii', 'ignore').encode('utf-8') |
| 686 # It needs to be valid utf-8 otherwise it can't be store. | 689 |
| 687 'output': output.decode('ascii', 'ignore').encode('utf-8'), | 690 if len(test_cases) > 1: |
| 688 } | 691 data = process_output(utf8_output, test_cases, duration, returncode) |
| 689 if '[ RUN ]' not in output: | 692 else: |
| 690 # Can't find gtest marker, mark it as invalid. | 693 data = [ |
| 691 returncode = returncode or 1 | 694 { |
| 695 'test_case': test_cases[0], | |
| 696 'returncode': returncode, | |
| 697 'duration': duration, | |
| 698 'output': utf8_output, | |
| 699 } | |
| 700 ] | |
| 701 if '[ RUN ]' not in output: | |
| 702 # Can't find gtest marker, mark it as invalid. | |
| 703 returncode = returncode or 1 | |
| 704 | |
| 692 self.decider.got_result(not bool(returncode)) | 705 self.decider.got_result(not bool(returncode)) |
| 693 if sys.platform == 'win32': | 706 if sys.platform == 'win32': |
| 694 output = output.replace('\r\n', '\n') | 707 output = output.replace('\r\n', '\n') |
| 695 need_to_retry = returncode and try_count < self.retries | 708 need_to_retry = returncode and try_count < self.retries |
| 696 | 709 |
| 697 if try_count: | 710 for i in data: |
| 698 line = '%s (%.2fs) - retry #%d' % (test_case, duration, try_count) | 711 if try_count: |
| 699 else: | 712 line = '%s (%.2fs) - retry #%d' % ( |
| 700 line = '%s (%.2fs)' % (test_case, duration) | 713 i['test_case'], i['duration'] or 0, try_count) |
| 701 if self.verbose or returncode or try_count > 0: | 714 else: |
| 702 # Print output in one of three cases: | 715 line = '%s (%.2fs)' % (i['test_case'], i['duration'] or 0) |
| 703 # --verbose was specified. | 716 if self.verbose or i['returncode'] or try_count > 0: |
| 704 # The test failed. | 717 # Print output in one of three cases: |
| 705 # The wasn't the first attempt (this is needed so the test parser can | 718 # --verbose was specified. |
| 706 # detect that a test has been successfully retried). | 719 # The test failed. |
| 707 line += '\n' + output | 720 # The wasn't the first attempt (this is needed so the test parser can |
| 708 self.progress.update_item(line, True, need_to_retry) | 721 # detect that a test has been successfully retried). |
| 722 line += '\n' + output | |
| 723 self.progress.update_item(line, True, need_to_retry) | |
| 709 | 724 |
| 710 if need_to_retry: | 725 if need_to_retry: |
| 711 if try_count + 1 < self.retries: | 726 if try_count + 1 < self.retries: |
| 712 # The test failed and needs to be retried normally. | 727 # The test failed and needs to be retried normally. |
| 713 # Leave a buffer of ~40 test cases before retrying. | 728 # Leave a buffer of ~40 test cases before retrying. |
| 714 priority += 40 | 729 priority += 40 |
| 715 self.add_task(priority, self.map, priority, test_case, try_count + 1) | 730 self.add_task( |
| 716 else: | 731 priority, self.map, priority, [i['test_case']], try_count + 1) |
| 717 # This test only has one retry left, so the final retry should be | 732 else: |
| 718 # done serially. | 733 # This test only has one retry left, so the final retry should be |
| 719 self.add_serial_task(priority, self.map, priority, test_case, | 734 # done serially. |
| 720 try_count + 1) | 735 self.add_serial_task( |
| 721 | 736 priority, self.map, priority, [i['test_case']], try_count + 1) |
| 722 return [data] | 737 return data |
| 723 | 738 |
| 724 | 739 |
| 725 def get_test_cases(cmd, cwd, whitelist, blacklist, index, shards, seed): | 740 def get_test_cases(cmd, cwd, whitelist, blacklist, index, shards, seed): |
| 726 """Returns the filtered list of test cases. | 741 """Returns the filtered list of test cases. |
| 727 | 742 |
| 728 This is done synchronously. | 743 This is done synchronously. |
| 729 """ | 744 """ |
| 730 try: | 745 try: |
| 731 tests = list_test_cases( | 746 tests = list_test_cases( |
| 732 cmd, | 747 cmd, |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 935 def run_test_cases( | 950 def run_test_cases( |
| 936 cmd, cwd, test_cases, jobs, timeout, clusters, retries, run_all, | 951 cmd, cwd, test_cases, jobs, timeout, clusters, retries, run_all, |
| 937 max_failures, no_cr, gtest_output, result_file, verbose): | 952 max_failures, no_cr, gtest_output, result_file, verbose): |
| 938 """Runs test cases in parallel. | 953 """Runs test cases in parallel. |
| 939 | 954 |
| 940 Arguments: | 955 Arguments: |
| 941 - cmd: command to run. | 956 - cmd: command to run. |
| 942 - cwd: working directory. | 957 - cwd: working directory. |
| 943 - test_cases: list of preprocessed test cases to run. | 958 - test_cases: list of preprocessed test cases to run. |
| 944 - jobs: number of parallel execution threads to do. | 959 - jobs: number of parallel execution threads to do. |
| 945 - timeout: individual test case timeout. | 960 - timeout: individual test case timeout. Modulated when used with |
| 961 clustering. | |
| 946 - clusters: number of test cases to lump together in a single execution. 0 | 962 - clusters: number of test cases to lump together in a single execution. 0 |
| 947 means the default automatic value which depends on len(test_cases) and | 963 means the default automatic value which depends on len(test_cases) and |
| 948 jobs. Capped to len(test_cases) / jobs. | 964 jobs. Capped to len(test_cases) / jobs. |
| 949 - retries: number of times a test case can be retried. | 965 - retries: number of times a test case can be retried. |
| 950 - run_all: If true, do not early return even if all test cases fail. | 966 - run_all: If true, do not early return even if all test cases fail. |
| 951 - max_failures is the absolute maximum number of tolerated failures or None. | 967 - max_failures is the absolute maximum number of tolerated failures or None. |
| 952 - no_cr: makes output friendly to piped logs. | 968 - no_cr: makes output friendly to piped logs. |
| 953 - gtest_output: saves results as xml. | 969 - gtest_output: saves results as xml. |
| 954 - result_file: saves results as json. | 970 - result_file: saves results as json. |
| 955 - verbose: print more details. | 971 - verbose: print more details. |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 984 assert callable(func) | 1000 assert callable(func) |
| 985 serial_tasks.put((priority, func, args, kwargs)) | 1001 serial_tasks.put((priority, func, args, kwargs)) |
| 986 | 1002 |
| 987 with ThreadPool(progress, jobs, jobs, len(test_cases)) as pool: | 1003 with ThreadPool(progress, jobs, jobs, len(test_cases)) as pool: |
| 988 runner = Runner( | 1004 runner = Runner( |
| 989 cmd, cwd, timeout, progress, retries, decider, verbose, | 1005 cmd, cwd, timeout, progress, retries, decider, verbose, |
| 990 pool.add_task, add_serial_task) | 1006 pool.add_task, add_serial_task) |
| 991 function = runner.map | 1007 function = runner.map |
| 992 logging.debug('Adding tests to ThreadPool') | 1008 logging.debug('Adding tests to ThreadPool') |
| 993 progress.use_cr_only = not no_cr | 1009 progress.use_cr_only = not no_cr |
| 994 for i, test_case in enumerate(test_cases): | 1010 # Cluster the test cases right away. |
| 995 pool.add_task(i, function, i, test_case, 0) | 1011 for i in xrange((len(test_cases) + clusters - 1) / clusters): |
| 1012 cluster = test_cases[i*clusters : (i+1)*clusters] | |
| 1013 pool.add_task(i, function, i, cluster, 0) | |
| 996 logging.debug('All tests added to the ThreadPool') | 1014 logging.debug('All tests added to the ThreadPool') |
| 997 results = pool.join() | 1015 results = pool.join() |
| 998 | 1016 |
| 999 # Retry any failed tests serially. | 1017 # Retry any failed tests serially. |
| 1000 if not serial_tasks.empty(): | 1018 if not serial_tasks.empty(): |
| 1001 progress.update_item('\n'.join(running_serial_warning()), index=False, | 1019 progress.update_item('\n'.join(running_serial_warning()), index=False, |
| 1002 size=False) | 1020 size=False) |
| 1003 | 1021 |
| 1004 while not serial_tasks.empty(): | 1022 while not serial_tasks.empty(): |
| 1005 _priority, func, args, kwargs = serial_tasks.get() | 1023 _priority, func, args, kwargs = serial_tasks.get() |
| 1006 results.append(func(*args, **kwargs)) | 1024 results.append(func(*args, **kwargs)) |
| 1007 serial_tasks.task_done() | 1025 serial_tasks.task_done() |
| 1008 | 1026 |
| 1009 # Call join since that is a standard call once a queue has been emptied. | 1027 # Call join since that is a standard call once a queue has been emptied. |
| 1010 serial_tasks.join() | 1028 serial_tasks.join() |
| 1011 | 1029 |
| 1012 duration = time.time() - pool.tasks.progress.start | 1030 duration = time.time() - pool.tasks.progress.start |
| 1013 | 1031 |
| 1014 cleaned = {} | 1032 cleaned = {} |
| 1015 for item in results: | 1033 for i in results: |
|
csharp
2013/03/11 13:33:22
nit: i -> test_case?
M-A Ruel
2013/03/11 17:30:53
Clarified.
| |
| 1016 if item: | 1034 for item in i: |
| 1017 cleaned.setdefault(item[0]['test_case'], []).extend(item) | 1035 cleaned.setdefault(item['test_case'], []).append(item) |
| 1018 results = cleaned | 1036 results = cleaned |
| 1019 | 1037 |
| 1020 # Total time taken to run each test case. | 1038 # Total time taken to run each test case. |
| 1021 test_case_duration = dict( | 1039 test_case_duration = dict( |
| 1022 (test_case, sum(i.get('duration', 0) for i in item)) | 1040 (test_case, sum((i.get('duration') or 0) for i in item)) |
| 1023 for test_case, item in results.iteritems()) | 1041 for test_case, item in results.iteritems()) |
| 1024 | 1042 |
| 1025 # Classify the results | 1043 # Classify the results |
| 1026 success = [] | 1044 success = [] |
| 1027 flaky = [] | 1045 flaky = [] |
| 1028 fail = [] | 1046 fail = [] |
| 1029 nb_runs = 0 | 1047 nb_runs = 0 |
| 1030 for test_case in sorted(results): | 1048 for test_case in sorted(results): |
| 1031 items = results[test_case] | 1049 items = results[test_case] |
| 1032 nb_runs += len(items) | 1050 nb_runs += len(items) |
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1353 options.gtest_output, | 1371 options.gtest_output, |
| 1354 result_file, | 1372 result_file, |
| 1355 options.verbose) | 1373 options.verbose) |
| 1356 except Failure as e: | 1374 except Failure as e: |
| 1357 print >> sys.stderr, e.args[0] | 1375 print >> sys.stderr, e.args[0] |
| 1358 return 1 | 1376 return 1 |
| 1359 | 1377 |
| 1360 | 1378 |
| 1361 if __name__ == '__main__': | 1379 if __name__ == '__main__': |
| 1362 sys.exit(main(sys.argv[1:])) | 1380 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |