Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 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 | |
| 4 # found in the LICENSE file. | |
| 5 # get_swarm_results.py: Retrieves and output swarm test results for a given | |
| 6 # test request name. | |
| 7 | |
| 8 import json # pylint: disable=F0401 | |
|
M-A Ruel
2012/04/17 20:16:00
You shouldn't need to disable the pylint warning,
csharp
2012/04/18 18:20:29
Done.
| |
| 9 import optparse | |
| 10 import sys | |
| 11 import time | |
| 12 import urllib | |
| 13 import urllib2 | |
| 14 | |
| 15 DESCRIPTION = """This script takes in a test name and retrives all the output | |
|
M-A Ruel
2012/04/17 20:16:00
Make that a docstring, e.g. move it around line 7.
csharp
2012/04/18 18:20:29
Done.
| |
| 16 that the swarm server has produced for tests with that name. This is expected to | |
| 17 be called as a build step.""" | |
| 18 | |
|
M-A Ruel
2012/04/17 20:16:00
2 lines between file level symbols
csharp
2012/04/18 18:20:29
Done.
| |
| 19 def _get_first_number(line): | |
| 20 for part in line.split(): | |
| 21 if part.isdigit(): | |
| 22 return int(part) | |
| 23 | |
| 24 print 'No number in :' | |
| 25 print line | |
| 26 return 0 | |
| 27 | |
| 28 class TestSummary(object): | |
| 29 def __init__(self): | |
| 30 self.test_passed_count = 0 | |
| 31 self.failed_tests = [] | |
| 32 self.disabled_test_count = 0 | |
| 33 self.ignored_test_count = 0 | |
| 34 | |
| 35 def AddSummaryData(self, buf): | |
| 36 lines = buf.splitlines() | |
| 37 | |
| 38 for line in lines: | |
| 39 if '[ PASSED ]' in line: | |
| 40 self.test_passed_count += _get_first_number(line) | |
| 41 elif '[ FAILED ]' in line: | |
| 42 if ', listed below' not in line: | |
| 43 self.failed_tests.append(line) | |
| 44 elif 'DISABLED' in line: | |
| 45 self.disabled_test_count += _get_first_number(line) | |
| 46 elif 'failures' in line: | |
| 47 self.ignored_test_count += _get_first_number(line) | |
| 48 | |
| 49 def Output(self): | |
| 50 output = [] | |
| 51 | |
| 52 output.append('[ PASSED ] %i tests.' % self.test_passed_count) | |
| 53 if self.failed_tests: | |
| 54 output.append('[ FAILED ] failed tests listed below:') | |
| 55 output.extend(self.failed_tests) | |
| 56 output.append('%i FAILED TESTS' % len(self.failed_tests)) | |
| 57 | |
| 58 if self.disabled_test_count: | |
| 59 output.append('%i DISABLED TESTS' % self.disabled_test_count) | |
| 60 | |
| 61 if self.ignored_test_count: | |
| 62 output.append('%i tests with ignored failures (FAILS prefix)' % | |
| 63 self.ignored_test_count) | |
| 64 | |
| 65 return output | |
| 66 | |
| 67 | |
| 68 # TODO(csharp) The sharing_supervisor.py also has test parsing code, they should | |
| 69 # be shared. | |
| 70 def TestRunOutput(output): | |
| 71 """Go through the given output and only return the output from the Test Run | |
| 72 Step. | |
| 73 """ | |
| 74 test_run_output = [] | |
| 75 | |
| 76 in_step = False | |
| 77 step_name = '' | |
| 78 for line in output.splitlines(): | |
| 79 if in_step: | |
| 80 if '[ OK ] ' + step_name in line: | |
| 81 break | |
| 82 else: | |
| 83 test_run_output.append(line) | |
| 84 elif '[ RUN ] ' in line and 'Run Test' in line: | |
| 85 in_step = True | |
| 86 i = len('[ RUN ] ') | |
| 87 step_name = line[i:] | |
| 88 | |
| 89 return '\n'.join(test_run_output) | |
|
M-A Ruel
2012/04/17 20:16:00
Technically, it's better to use splitlines(True) a
csharp
2012/04/18 18:20:29
Done.
| |
| 90 | |
| 91 | |
| 92 def main(): | |
| 93 """Retrieve the given swarm test results from the swarm server and print it | |
| 94 to stdout. | |
| 95 | |
| 96 Args: | |
| 97 test_name: The name of the test to retrieve output for. | |
| 98 """ | |
| 99 # Parses arguments | |
| 100 parser = optparse.OptionParser(usage='%prog [options] test_name', | |
| 101 description=DESCRIPTION) | |
|
M-A Ruel
2012/04/17 20:16:00
description=sys.modules[__name__].__doc__
csharp
2012/04/18 18:20:29
Done.
| |
| 102 parser.add_option('-n', '--hostname', default='localhost', | |
| 103 help='Specify the hostname of the Swarm server. ' | |
| 104 'Defaults to %default') | |
| 105 parser.add_option('-p', '--port', type='int', default=8080, | |
| 106 help='Specify the port of the Swarm server. ' | |
| 107 'Defaults to %default') | |
| 108 parser.add_option('-v', '--verbose', action='store_true', | |
| 109 help='Print verbose logging') | |
| 110 (options, args) = parser.parse_args() | |
| 111 if not args: | |
| 112 parser.error('Must specify one test name.') | |
| 113 elif len(args) > 1: | |
| 114 parser.error('Must specify only one test name.') | |
| 115 test_name = args[0] | |
| 116 | |
| 117 swarm_base_url = 'http://%s:%d' % (options.hostname, options.port) | |
| 118 key_data = urllib.urlencode([('name', test_name)]) | |
| 119 test_keys_url = '%s/get_matching_test_cases?%s' % (swarm_base_url, | |
| 120 key_data) | |
| 121 result = urllib2.urlopen(test_keys_url).read() | |
| 122 | |
| 123 if 'No matching' in result: | |
| 124 print('Error: Unable to find any tests with the name, %s, on swarm server', | |
| 125 test_name) | |
| 126 return 1 | |
| 127 | |
| 128 test_keys = result.split() | |
|
M-A Ruel
2012/04/17 20:16:00
Oh, I guess eventually it'd make sense to use a pr
csharp
2012/04/18 18:20:29
TODO added
| |
| 129 | |
| 130 # Get the swarm results. | |
|
M-A Ruel
2012/04/17 20:16:00
Can you move that out of main into it's own functi
csharp
2012/04/18 18:20:29
Done.
| |
| 131 summary_total = TestSummary() | |
| 132 hostnames = ['unknown'] * len(test_keys) | |
| 133 exit_codes = [1] * len(test_keys) | |
| 134 for index in range(len(test_keys)): | |
| 135 result_url = '%s/get_result?r=%s' % (swarm_base_url, test_keys[index]) | |
| 136 while True: | |
| 137 output = None | |
| 138 try: | |
| 139 output = urllib2.urlopen(result_url).read() | |
| 140 except urllib2.HTTPError, e: | |
| 141 print 'Calling %s threw %s' % (result_url, e) | |
| 142 break | |
| 143 | |
| 144 try: | |
| 145 test_outputs = json.loads(output) | |
| 146 except (ValueError, TypeError), e: | |
| 147 print 'Unable to get results for shard %d' % index | |
| 148 print e | |
| 149 break | |
| 150 | |
| 151 if test_outputs['output']: | |
| 152 test_exit_codes = test_outputs['exit_codes'].split(',') | |
| 153 exit_codes[index] = int(max(test_exit_codes)) | |
| 154 hostnames[index] = test_outputs['hostname'] | |
| 155 | |
| 156 print | |
| 157 print '================================================================' | |
| 158 print 'Begin output from shard index %s (%s)' % (index, | |
| 159 hostnames[index]) | |
| 160 print '================================================================' | |
| 161 print | |
| 162 | |
| 163 cleaned_output = TestRunOutput(test_outputs['output']) | |
| 164 summary_index = cleaned_output.rfind('[ PASSED ]') | |
| 165 summary_total.AddSummaryData(cleaned_output[summary_index:]) | |
| 166 sys.stdout.write(cleaned_output[:summary_index - 1]) | |
| 167 | |
| 168 print | |
| 169 print '================================================================' | |
| 170 print 'End output from shard index %s (%s). Return %d' % ( | |
| 171 index, hostnames[index], exit_codes[index]) | |
| 172 print '================================================================' | |
| 173 print | |
| 174 | |
| 175 if exit_codes[index] == 0: | |
| 176 # If the test passed, delete the key since it is no longer needed. | |
| 177 remove_key_url = '%s/cleanup_results' % swarm_base_url | |
| 178 key_encoding = urllib.urlencode([('r', test_keys[index])]) | |
| 179 urllib2.urlopen(remove_key_url, | |
| 180 key_encoding) | |
| 181 break | |
| 182 else: | |
| 183 # Test is not yet done, wait a bit before checking again. | |
| 184 time.sleep(0.5) | |
| 185 | |
| 186 print '\n'.join(summary_total.Output()) | |
| 187 print | |
| 188 | |
| 189 if options.verbose: | |
| 190 print 'All tests completed:' | |
| 191 for i in range(options.num_shards): | |
| 192 print 'Shard index %s (%s): Exit code: %d' % (i, | |
| 193 hostnames[i], exit_codes[i]) | |
| 194 | |
| 195 return max(exit_codes) | |
| 196 | |
| 197 if __name__ == '__main__': | |
| 198 sys.exit(main()) | |
| OLD | NEW |