| 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(swarm_base_url, test_name): |
| 95 key_data = urllib.urlencode([('name', test_name)]) |
| 96 test_keys_url = '%s/get_matching_test_cases?%s' % (swarm_base_url.rstrip('/'), |
| 97 key_data) |
| 98 result = urllib2.urlopen(test_keys_url).read() |
| 99 if 'No matching' in result: |
| 100 print ('Error: Unable to find any tests with the name, %s, on swarm server' |
| 101 % test_name) |
| 102 return [] |
| 103 |
| 104 # TODO(csharp): return in a proper format (like json) |
| 105 return result.split() |
| 106 |
| 107 |
| 108 def GetSwarmResults(swarm_base_url, test_keys): |
| 109 summary_total = TestRunSummary() |
| 110 hostnames = ['unknown'] * len(test_keys) |
| 111 exit_codes = [1] * len(test_keys) |
| 112 for index in range(len(test_keys)): |
| 113 result_url = '%s/get_result?r=%s' % (swarm_base_url.rstrip('/'), |
| 114 test_keys[index]) |
| 115 while True: |
| 116 output = None |
| 117 try: |
| 118 output = urllib2.urlopen(result_url).read() |
| 119 except urllib2.HTTPError, e: |
| 120 print 'Calling %s threw %s' % (result_url, e) |
| 121 break |
| 122 |
| 123 try: |
| 124 test_outputs = json.loads(output) |
| 125 except (ValueError, TypeError), e: |
| 126 print 'Unable to get results for shard %d' % index |
| 127 print e |
| 128 break |
| 129 |
| 130 if test_outputs['output']: |
| 131 test_exit_codes = test_outputs['exit_codes'].split(',') |
| 132 exit_codes[index] = int(max(test_exit_codes)) |
| 133 hostnames[index] = test_outputs['hostname'] |
| 134 |
| 135 print |
| 136 print '================================================================' |
| 137 print 'Begin output from shard index %s (%s)' % (index, |
| 138 hostnames[index]) |
| 139 print '================================================================' |
| 140 print |
| 141 |
| 142 cleaned_output = TestRunOutput(test_outputs['output']) |
| 143 summary_index = cleaned_output.rfind('[ PASSED ]') |
| 144 summary_total.AddSummaryData(cleaned_output[summary_index:]) |
| 145 sys.stdout.write(cleaned_output[:summary_index - 1]) |
| 146 |
| 147 print |
| 148 print '================================================================' |
| 149 print 'End output from shard index %s (%s). Return %d' % ( |
| 150 index, hostnames[index], exit_codes[index]) |
| 151 print '================================================================' |
| 152 print |
| 153 |
| 154 if exit_codes[index] == 0: |
| 155 # If the test passed, delete the key since it is no longer needed. |
| 156 remove_key_url = '%s/cleanup_results' % (swarm_base_url.rstrip('/')) |
| 157 key_encoding = urllib.urlencode([('r', test_keys[index])]) |
| 158 urllib2.urlopen(remove_key_url, |
| 159 key_encoding) |
| 160 break |
| 161 else: |
| 162 # Test is not yet done, wait a bit before checking again. |
| 163 time.sleep(0.5) |
| 164 |
| 165 print '\n'.join(summary_total.Output()) |
| 166 print |
| 167 |
| 168 return max(exit_codes) |
| 169 |
| 170 |
| 171 def main(): |
| 172 """Retrieve the given swarm test results from the swarm server and print it |
| 173 to stdout. |
| 174 |
| 175 Args: |
| 176 test_name: The name of the test to retrieve output for. |
| 177 """ |
| 178 # Parses arguments |
| 179 parser = optparse.OptionParser(usage='%prog [options] test_name', |
| 180 description=sys.modules[__name__].__doc__) |
| 181 parser.add_option('-u', '--url', default='http://localhost:8080', |
| 182 help='Specify the url of the Swarm server. ' |
| 183 'Defaults to %default') |
| 184 parser.add_option('-v', '--verbose', action='store_true', |
| 185 help='Print verbose logging') |
| 186 (options, args) = parser.parse_args() |
| 187 if not args: |
| 188 parser.error('Must specify one test name.') |
| 189 elif len(args) > 1: |
| 190 parser.error('Must specify only one test name.') |
| 191 test_name = args[0] |
| 192 |
| 193 test_keys = GetTestKeys(options.url, test_name) |
| 194 |
| 195 return GetSwarmResults(options.url, test_keys) |
| 196 |
| 197 |
| 198 if __name__ == '__main__': |
| 199 sys.exit(main()) |
| OLD | NEW |