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