Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(770)

Side by Side Diff: run_test_cases.py

Issue 12459014: Implement clustering support in run_test_cases.py. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/swarm_client
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/gtest_fake/expected.xml » ('j') | tests/gtest_fake/gtest_fake_pass.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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:]))
OLDNEW
« no previous file with comments | « no previous file | tests/gtest_fake/expected.xml » ('j') | tests/gtest_fake/gtest_fake_pass.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698