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

Side by Side Diff: generate_test_report.py

Issue 6526031: Add crash detection to test report generation. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/crosutils.git@master
Patch Set: Lines. Created 9 years, 10 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 | no next file » | 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/python 1 #!/usr/bin/python
2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium OS 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 6
7 """Parses and displays the contents of one or more autoserv result directories. 7 """Parses and displays the contents of one or more autoserv result directories.
8 8
9 This script parses the contents of one or more autoserv results folders and 9 This script parses the contents of one or more autoserv results folders and
10 generates test reports. 10 generates test reports.
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
106 status = 'FAIL' 106 status = 'FAIL'
107 if (re.search(r'GOOD.+completed successfully', status_raw) and 107 if (re.search(r'GOOD.+completed successfully', status_raw) and
108 not re.search(r'ABORT|ERROR|FAIL|TEST_NA', status_raw)): 108 not re.search(r'ABORT|ERROR|FAIL|TEST_NA', status_raw)):
109 status = 'PASS' 109 status = 'PASS'
110 110
111 perf = self._CollectPerf(testdir) 111 perf = self._CollectPerf(testdir)
112 112
113 if testdir.startswith(self._options.strip): 113 if testdir.startswith(self._options.strip):
114 testdir = testdir.replace(self._options.strip, '', 1) 114 testdir = testdir.replace(self._options.strip, '', 1)
115 115
116 self._results[testdir] = {'status': status, 116 crashes = []
117 regex = re.compile('Received crash notification for (\w+).+ (sig \d+)')
petkov 2011/02/16 08:17:40 btw, for the process name, you probably want to co
DaleCurtis 2011/02/16 19:59:16 \S+ would grab the process id and such too. Switch
118 for match in regex.finditer(status_raw):
119 crashes.append('%s %s' % match.groups())
120
121 self._results[testdir] = {'crashes': crashes,
122 'status': status,
117 'perf': perf} 123 'perf': perf}
118 124
119 def _CollectResultsRec(self, resdir): 125 def _CollectResultsRec(self, resdir):
120 """Recursively collect results into the self._results dictionary. 126 """Recursively collect results into the self._results dictionary.
121 127
122 Args: 128 Args:
123 resdir: results/test directory to parse results from and recurse into. 129 resdir: results/test directory to parse results from and recurse into.
124 """ 130 """
125 131
126 self._CollectResult(resdir) 132 self._CollectResult(resdir)
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
169 results. 175 results.
170 """ 176 """
171 tests = self._results.keys() 177 tests = self._results.keys()
172 tests.sort() 178 tests.sort()
173 179
174 tests_with_errors = [] 180 tests_with_errors = []
175 181
176 width = self.GetTestColumnWidth() 182 width = self.GetTestColumnWidth()
177 line = ''.ljust(width + 5, '-') 183 line = ''.ljust(width + 5, '-')
178 184
185 crashes = {}
179 tests_pass = 0 186 tests_pass = 0
180 print line 187 print line
181 for test in tests: 188 for test in tests:
182 # Emit the test/status entry first 189 # Emit the test/status entry first
183 test_entry = test.ljust(width) 190 test_entry = test.ljust(width)
184 result = self._results[test] 191 result = self._results[test]
185 status_entry = result['status'] 192 status_entry = result['status']
186 if status_entry == 'PASS': 193 if status_entry == 'PASS':
187 color = Color.GREEN 194 color = Color.GREEN
188 tests_pass += 1 195 tests_pass += 1
189 else: 196 else:
190 color = Color.RED 197 color = Color.RED
191 tests_with_errors.append(test) 198 tests_with_errors.append(test)
192 199
193 status_entry = self._color.Color(color, status_entry) 200 status_entry = self._color.Color(color, status_entry)
194 print test_entry + status_entry 201 print test_entry + status_entry
195 202
196 # Emit the perf keyvals entries. There will be no entries if the 203 # Emit the perf keyvals entries. There will be no entries if the
197 # --no-perf option is specified. 204 # --no-perf option is specified.
198 perf = result['perf'] 205 perf = result['perf']
199 perf_keys = perf.keys() 206 perf_keys = perf.keys()
200 perf_keys.sort() 207 perf_keys.sort()
201 208
202 for perf_key in perf_keys: 209 for perf_key in perf_keys:
203 perf_key_entry = perf_key.ljust(width - self._KEYVAL_INDENT) 210 perf_key_entry = perf_key.ljust(width - self._KEYVAL_INDENT)
204 perf_key_entry = perf_key_entry.rjust(width) 211 perf_key_entry = perf_key_entry.rjust(width)
205 perf_value_entry = self._color.Color(Color.BOLD, perf[perf_key]) 212 perf_value_entry = self._color.Color(Color.BOLD, perf[perf_key])
206 print perf_key_entry + perf_value_entry 213 print perf_key_entry + perf_value_entry
207 214
215 # Ignore top-level entry, since it's just a combination of all the
216 # individual results.
217 if result['crashes'] and not test == tests[0]:
petkov 2011/02/16 03:46:54 isn't this test != tests[0]? this filter is a bit
DaleCurtis 2011/02/16 19:59:16 Yup, changed. Python has me losing sense of basic
218 for crash in result['crashes']:
219 if not crash in crashes:
220 crashes[crash] = set([])
221 crashes[crash].add(test)
222
208 print line 223 print line
209 224
210 total_tests = len(tests) 225 total_tests = len(tests)
211 percent_pass = 100 * tests_pass / total_tests 226 percent_pass = 100 * tests_pass / total_tests
212 pass_str = '%d/%d (%d%%)' % (tests_pass, total_tests, percent_pass) 227 pass_str = '%d/%d (%d%%)' % (tests_pass, total_tests, percent_pass)
213 print 'Total PASS: ' + self._color.Color(Color.BOLD, pass_str) 228 print 'Total PASS: ' + self._color.Color(Color.BOLD, pass_str)
214 229
230 if self._options.crash_detection:
231 print ''
232 if crashes:
233 print self._color.Color(Color.RED, 'Crashes detected during testing:')
234 print line
235
236 for crash_name, crashed_tests in crashes.iteritems():
237 print self._color.Color(Color.RED, crash_name)
238 for crashed_test in crashed_tests:
239 print ' '*self._KEYVAL_INDENT + crashed_test
240
241 print line
242 print 'Total unique crashes: ' + self._color.Color(Color.BOLD,
243 str(len(crashes)))
244 else:
245 print self._color.Color(Color.GREEN,
246 'No crashes detected during testing.')
247
215 # Print out the client debug information for failed tests. 248 # Print out the client debug information for failed tests.
216 if self._options.print_debug: 249 if self._options.print_debug:
217 for test in tests_with_errors: 250 for test in tests_with_errors:
218 debug_file_regex = os.path.join(self._options.strip, test, 'debug', 251 debug_file_regex = os.path.join(self._options.strip, test, 'debug',
219 '%s*.DEBUG' % os.path.basename(test)) 252 '%s*.DEBUG' % os.path.basename(test))
220 for path in glob.glob(debug_file_regex): 253 for path in glob.glob(debug_file_regex):
221 try: 254 try:
222 fh = open(path) 255 fh = open(path)
223 print >> sys.stderr, ( 256 print >> sys.stderr, (
224 '\n========== DEBUG FILE %s FOR TEST %s ==============\n' % ( 257 '\n========== DEBUG FILE %s FOR TEST %s ==============\n' % (
(...skipping 11 matching lines...) Expand all
236 269
237 # Sometimes the builders exit before these buffers are flushed. 270 # Sometimes the builders exit before these buffers are flushed.
238 sys.stderr.flush() 271 sys.stderr.flush()
239 sys.stdout.flush() 272 sys.stdout.flush()
240 273
241 def Run(self): 274 def Run(self):
242 """Runs report generation.""" 275 """Runs report generation."""
243 self._CollectResults() 276 self._CollectResults()
244 self._GenerateReportText() 277 self._GenerateReportText()
245 for v in self._results.itervalues(): 278 for v in self._results.itervalues():
246 if v['status'] != 'PASS': 279 if v['status'] != 'PASS' or (self._options.crash_detection
280 and v['crashes']):
247 sys.exit(1) 281 sys.exit(1)
248 282
249 283
250 def main(): 284 def main():
251 usage = 'Usage: %prog [options] result-directories...' 285 usage = 'Usage: %prog [options] result-directories...'
252 parser = optparse.OptionParser(usage=usage) 286 parser = optparse.OptionParser(usage=usage)
253 parser.add_option('--color', dest='color', action='store_true', 287 parser.add_option('--color', dest='color', action='store_true',
254 default=_STDOUT_IS_TTY, 288 default=_STDOUT_IS_TTY,
255 help='Use color for text reports [default if TTY stdout]') 289 help='Use color for text reports [default if TTY stdout]')
256 parser.add_option('--no-color', dest='color', action='store_false', 290 parser.add_option('--no-color', dest='color', action='store_false',
257 help='Don\'t use color for text reports') 291 help='Don\'t use color for text reports')
292 parser.add_option('--no-crash-detection', dest='crash_detection',
293 action='store_false', default=True,
294 help='Don\'t report crashes or error out when detected')
258 parser.add_option('--perf', dest='perf', action='store_true', 295 parser.add_option('--perf', dest='perf', action='store_true',
259 default=True, 296 default=True,
260 help='Include perf keyvals in the report [default]') 297 help='Include perf keyvals in the report [default]')
261 parser.add_option('--no-perf', dest='perf', action='store_false', 298 parser.add_option('--no-perf', dest='perf', action='store_false',
262 help='Don\'t include perf keyvals in the report') 299 help='Don\'t include perf keyvals in the report')
263 parser.add_option('--strip', dest='strip', type='string', action='store', 300 parser.add_option('--strip', dest='strip', type='string', action='store',
264 default='results.', 301 default='results.',
265 help='Strip a prefix from test directory names' 302 help='Strip a prefix from test directory names'
266 ' [default: \'%default\']') 303 ' [default: \'%default\']')
267 parser.add_option('--no-strip', dest='strip', const='', action='store_const', 304 parser.add_option('--no-strip', dest='strip', const='', action='store_const',
268 help='Don\'t strip a prefix from test directory names') 305 help='Don\'t strip a prefix from test directory names')
269 parser.add_option('--no-debug', dest='print_debug', action='store_false', 306 parser.add_option('--no-debug', dest='print_debug', action='store_false',
270 default=True, 307 default=True,
271 help='Do not print out the debug log when a test fails.') 308 help='Don\'t print out the debug log when a test fails.')
272 (options, args) = parser.parse_args() 309 (options, args) = parser.parse_args()
273 310
274 if not args: 311 if not args:
275 parser.print_help() 312 parser.print_help()
276 Die('no result directories provided') 313 Die('no result directories provided')
277 314
278 generator = ReportGenerator(options, args) 315 generator = ReportGenerator(options, args)
279 generator.Run() 316 generator.Run()
280 317
281 318
282 if __name__ == '__main__': 319 if __name__ == '__main__':
283 main() 320 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698