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

Side by Side Diff: run_test_cases.py

Issue 12995009: Parse the test executable output on the fly. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/swarm_client
Patch Set: oops 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/run_test_cases_test.py » ('j') | no next file with comments »
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 725 matching lines...) Expand 10 before | Expand all | Expand 10 after
736 test_case_data['duration'] = float(match.group(1)) / 1000. 736 test_case_data['duration'] = float(match.group(1)) / 1000.
737 test_case_data['returncode'] = int(line.startswith(FAILED_PREFIX)) 737 test_case_data['returncode'] = int(line.startswith(FAILED_PREFIX))
738 yield test_case_data 738 yield test_case_data
739 test_case = None 739 test_case = None
740 test_case_data = None 740 test_case_data = None
741 741
742 if test_case_data: 742 if test_case_data:
743 assert not terminated_early 743 assert not terminated_early
744 # This means the last one likely crashed. 744 # This means the last one likely crashed.
745 test_case_data['crashed'] = True 745 test_case_data['crashed'] = True
746 test_case_data['duration'] = 0
746 test_case_data['returncode'] = 1 747 test_case_data['returncode'] = 1
747 yield test_case_data 748 yield test_case_data
748 elif terminated_early: 749 elif terminated_early:
749 for _ in lines: 750 for _ in lines:
750 # Exhaust the generator. 751 # Exhaust the generator.
751 pass 752 pass
752 753
753 # If test_cases is not empty, these test cases were not run. 754 # If test_cases is not empty, these test cases were not run.
754 for t in test_cases: 755 for t in test_cases:
755 yield { 756 yield {
(...skipping 27 matching lines...) Expand all
783 # that ran if the startup cost was above 10ms. 784 # that ran if the startup cost was above 10ms.
784 if startup_duration > 0.01 and data_ran: 785 if startup_duration > 0.01 and data_ran:
785 distributed_duration = startup_duration / len(data_ran) 786 distributed_duration = startup_duration / len(data_ran)
786 for i in data_ran: 787 for i in data_ran:
787 i['duration'] += distributed_duration 788 i['duration'] += distributed_duration
788 i['returncode'] = returncode or i['returncode'] 789 i['returncode'] = returncode or i['returncode']
789 break 790 break
790 return data 791 return data
791 792
792 793
794 def convert_to_lines(generator):
795 """Turn input coming from a generator into lines.
796
797 It is Windows-friendly.
798 """
799 accumulator = ''
800 for data in generator:
801 items = (accumulator + data).splitlines(True)
802 for item in items[:-1]:
803 yield item
804 if items[-1].endswith(('\r', '\n')):
805 yield items[-1]
806 accumulator = ''
807 else:
808 accumulator = items[-1]
809 if accumulator:
810 yield accumulator
811
812
793 def chromium_filter_tests(data): 813 def chromium_filter_tests(data):
794 """Removes funky PRE_ chromium-specific tests.""" 814 """Returns a generator that removes funky PRE_ chromium-specific tests."""
795 return [d for d in data if chromium_valid(d['test_case'], False, True)] 815 return (d for d in data if chromium_valid(d['test_case'], False, True))
796 816
797 817
798 class Runner(object): 818 class Runner(object):
799 """Immutable settings to run many test cases in a loop.""" 819 """Immutable settings to run many test cases in a loop."""
800 def __init__( 820 def __init__(
801 self, cmd, cwd_dir, timeout, progress, retries, decider, verbose, 821 self, cmd, cwd_dir, timeout, progress, retries, decider, verbose,
802 add_task, add_serial_task): 822 add_task, add_serial_task):
803 self.cmd = cmd[:] 823 self.cmd = cmd[:]
804 self.cwd_dir = cwd_dir 824 self.cwd_dir = cwd_dir
805 self.timeout = timeout 825 self.timeout = timeout
(...skipping 21 matching lines...) Expand all
827 if '--gtest_print_time' not in cmd: 847 if '--gtest_print_time' not in cmd:
828 cmd.append('--gtest_print_time') 848 cmd.append('--gtest_print_time')
829 849
830 # TODO(maruel): Use a distribution model. 850 # TODO(maruel): Use a distribution model.
831 timeout = self.timeout * len(test_cases) 851 timeout = self.timeout * len(test_cases)
832 852
833 if self.verbose > 1: 853 if self.verbose > 1:
834 self.progress.update_item('Starting command %s with a timeout of %ss' % 854 self.progress.update_item('Starting command %s with a timeout of %ss' %
835 (cmd, timeout), False, False) 855 (cmd, timeout), False, False)
836 856
837 output, _, returncode, duration = call_with_timeout( 857 # TODO(maruel): Differentiate between soft and hard timeouts.
858 proc = Popen(
838 cmd, 859 cmd,
839 timeout,
840 cwd=self.cwd_dir, 860 cwd=self.cwd_dir,
861 stdout=subprocess.PIPE,
841 stderr=subprocess.STDOUT, 862 stderr=subprocess.STDOUT,
842 env=self.env) 863 env=self.env)
843 864 # Create a pipeline of generators.
844 if self.verbose > 1: 865 gen_lines = convert_to_lines(data for _, data in proc.yield_any(timeout))
845 self.progress.update_item('Command %s finished after %ss' % (cmd,
846 duration),
847 False, False)
848
849 # It needs to be valid utf-8 otherwise it can't be stored. 866 # It needs to be valid utf-8 otherwise it can't be stored.
850 # TODO(maruel): Be more intelligent than decoding to ascii. 867 # TODO(maruel): Be more intelligent than decoding to ascii.
851 utf8_output = output.decode('ascii', 'ignore').encode('utf-8') 868 gen_lines_utf8 = (
869 line.decode('ascii', 'ignore').encode('utf-8') for line in gen_lines)
870 gen_test_cases = process_output(gen_lines_utf8, test_cases)
871 gen_test_cases_filtered = chromium_filter_tests(gen_test_cases)
852 872
853 data = process_output(utf8_output.splitlines(True), test_cases) 873 data = []
854 data = normalize_testing_time(data, duration, returncode) 874 for i in gen_test_cases_filtered:
855 data = chromium_filter_tests(data) 875 if i['duration'] is None:
856 876 continue
857 if sys.platform == 'win32': 877 # A new test_case completed.
858 output = output.replace('\r\n', '\n') 878 data.append(i)
859
860 for i in data:
861 self.decider.got_result(i['returncode'] == 0) 879 self.decider.got_result(i['returncode'] == 0)
862 need_to_retry = i['returncode'] != 0 and try_count < self.retries 880 need_to_retry = i['returncode'] != 0 and try_count < self.retries
863 if try_count: 881 if try_count:
864 line = '%s (%.2fs) - retry #%d' % ( 882 line = '%s (%.2fs) - retry #%d' % (
865 i['test_case'], i['duration'] or 0, try_count) 883 i['test_case'], i['duration'] or 0, try_count)
866 else: 884 else:
867 line = '%s (%.2fs)' % (i['test_case'], i['duration'] or 0) 885 line = '%s (%.2fs)' % (i['test_case'], i['duration'] or 0)
868 if self.verbose or i['returncode'] != 0 or try_count > 0: 886 if self.verbose or i['returncode'] != 0 or try_count > 0:
869 # Print output in one of three cases: 887 # Print output in one of three cases:
870 # --verbose was specified. 888 # --verbose was specified.
871 # The test failed. 889 # The test failed.
872 # The wasn't the first attempt (this is needed so the test parser can 890 # The wasn't the first attempt (this is needed so the test parser can
873 # detect that a test has been successfully retried). 891 # detect that a test has been successfully retried).
874 if i['output']: 892 if i['output']:
875 line += '\n' + i['output'] 893 line += '\n' + i['output']
876 self.progress.update_item(line, True, need_to_retry) 894 self.progress.update_item(line, True, need_to_retry)
877 895
878 if need_to_retry: 896 if need_to_retry:
879 if try_count + 1 < self.retries: 897 if try_count + 1 < self.retries:
880 # The test failed and needs to be retried normally. 898 # The test failed and needs to be retried normally.
881 # Leave a buffer of ~40 test cases before retrying. 899 # Leave a buffer of ~40 test cases before retrying.
882 priority += 40 900 priority += 40
883 self.add_task( 901 self.add_task(
884 priority, self.map, priority, [i['test_case']], try_count + 1) 902 priority, self.map, priority, [i['test_case']], try_count + 1)
885 else: 903 else:
886 # This test only has one retry left, so the final retry should be 904 # This test only has one retry left, so the final retry should be
887 # done serially. 905 # done serially.
888 self.add_serial_task( 906 self.add_serial_task(
889 priority, self.map, priority, [i['test_case']], try_count + 1) 907 priority, self.map, priority, [i['test_case']], try_count + 1)
890 return data 908
909 if self.verbose > 1:
910 self.progress.update_item(
911 'Command %s finished after %ss' % (cmd, proc.duration()),
912 False, False)
913
914 return normalize_testing_time(data, proc.duration(), proc.returncode)
891 915
892 916
893 def get_test_cases( 917 def get_test_cases(
894 cmd, cwd, whitelist, blacklist, index, shards, seed, disabled, fails, flaky, 918 cmd, cwd, whitelist, blacklist, index, shards, seed, disabled, fails, flaky,
895 manual): 919 manual):
896 """Returns the filtered list of test cases. 920 """Returns the filtered list of test cases.
897 921
898 This is done synchronously. 922 This is done synchronously.
899 """ 923 """
900 try: 924 try:
(...skipping 670 matching lines...) Expand 10 before | Expand all | Expand 10 after
1571 options.gtest_output, 1595 options.gtest_output,
1572 result_file, 1596 result_file,
1573 options.verbose) 1597 options.verbose)
1574 except Failure as e: 1598 except Failure as e:
1575 print >> sys.stderr, e.args[0] 1599 print >> sys.stderr, e.args[0]
1576 return 1 1600 return 1
1577 1601
1578 1602
1579 if __name__ == '__main__': 1603 if __name__ == '__main__':
1580 sys.exit(main(sys.argv[1:])) 1604 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | tests/run_test_cases_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698