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

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: Fix variable overlap. 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 .+)')
118 for match in regex.finditer(status_raw):
119 crashes.append(match.group(1).split('\t')[0])
petkov 2011/02/16 00:53:56 what does this do? group(1) is the whole string, r
DaleCurtis 2011/02/16 00:59:00 Autotest attaches the timetamp and a couple other
petkov 2011/02/16 01:01:17 Sorry, I still don't get what this does... Can you
DaleCurtis 2011/02/16 01:03:54 Line in status.log is this: "INFO desktopui_Chr
petkov 2011/02/16 01:06:03 Thanks. It seems "Received crash notification for
DaleCurtis 2011/02/16 01:18:49 Done.
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_detected = False
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 if result['crashes']:
216 crashes_detected = True
217
208 print line 218 print line
209 219
210 total_tests = len(tests) 220 total_tests = len(tests)
211 percent_pass = 100 * tests_pass / total_tests 221 percent_pass = 100 * tests_pass / total_tests
212 pass_str = '%d/%d (%d%%)' % (tests_pass, total_tests, percent_pass) 222 pass_str = '%d/%d (%d%%)' % (tests_pass, total_tests, percent_pass)
213 print 'Total PASS: ' + self._color.Color(Color.BOLD, pass_str) 223 print 'Total PASS: ' + self._color.Color(Color.BOLD, pass_str)
214 224
225 if self._options.crash_detection:
226 print ''
petkov 2011/02/16 00:53:56 Isn't it better to interleave the crash results in
DaleCurtis 2011/02/16 00:59:00 I thought about this, but cmasone indicated he'd l
227 if crashes_detected:
228 num_crashes = 0
229 print self._color.Color(Color.RED, 'Crashes detected during testing:')
230 print line
231
232 # Ignore top-level entry, since it's just a combination of all the
233 # individual results.
234 for test in tests[1:]:
235 crashes = self._results[test]['crashes']
236 if not crashes:
237 continue
238 print test
239 for crash in crashes:
240 num_crashes += 1
241 print ' '*self._KEYVAL_INDENT + self._color.Color(Color.RED, crash)
242
243 print line
244 print 'Total crashes: ' + self._color.Color(Color.BOLD,
245 str(num_crashes))
246 else:
247 print self._color.Color(Color.GREEN,
248 'No crashes detected during testing.')
249
215 # Print out the client debug information for failed tests. 250 # Print out the client debug information for failed tests.
216 if self._options.print_debug: 251 if self._options.print_debug:
217 for test in tests_with_errors: 252 for test in tests_with_errors:
218 debug_file_regex = os.path.join(self._options.strip, test, 'debug', 253 debug_file_regex = os.path.join(self._options.strip, test, 'debug',
219 '%s*.DEBUG' % os.path.basename(test)) 254 '%s*.DEBUG' % os.path.basename(test))
220 for path in glob.glob(debug_file_regex): 255 for path in glob.glob(debug_file_regex):
221 try: 256 try:
222 fh = open(path) 257 fh = open(path)
223 print >> sys.stderr, ( 258 print >> sys.stderr, (
224 '\n========== DEBUG FILE %s FOR TEST %s ==============\n' % ( 259 '\n========== DEBUG FILE %s FOR TEST %s ==============\n' % (
(...skipping 11 matching lines...) Expand all
236 271
237 # Sometimes the builders exit before these buffers are flushed. 272 # Sometimes the builders exit before these buffers are flushed.
238 sys.stderr.flush() 273 sys.stderr.flush()
239 sys.stdout.flush() 274 sys.stdout.flush()
240 275
241 def Run(self): 276 def Run(self):
242 """Runs report generation.""" 277 """Runs report generation."""
243 self._CollectResults() 278 self._CollectResults()
244 self._GenerateReportText() 279 self._GenerateReportText()
245 for v in self._results.itervalues(): 280 for v in self._results.itervalues():
246 if v['status'] != 'PASS': 281 if v['status'] != 'PASS' or (self._options.crash_detection
282 and v['crashes']):
247 sys.exit(1) 283 sys.exit(1)
248 284
249 285
250 def main(): 286 def main():
251 usage = 'Usage: %prog [options] result-directories...' 287 usage = 'Usage: %prog [options] result-directories...'
252 parser = optparse.OptionParser(usage=usage) 288 parser = optparse.OptionParser(usage=usage)
253 parser.add_option('--color', dest='color', action='store_true', 289 parser.add_option('--color', dest='color', action='store_true',
254 default=_STDOUT_IS_TTY, 290 default=_STDOUT_IS_TTY,
255 help='Use color for text reports [default if TTY stdout]') 291 help='Use color for text reports [default if TTY stdout]')
256 parser.add_option('--no-color', dest='color', action='store_false', 292 parser.add_option('--no-color', dest='color', action='store_false',
257 help='Don\'t use color for text reports') 293 help='Don\'t use color for text reports')
294 parser.add_option('--no-crash-detection', dest='crash_detection',
295 action='store_false', default=True,
296 help='Don\'t report crashes or error out when detected')
258 parser.add_option('--perf', dest='perf', action='store_true', 297 parser.add_option('--perf', dest='perf', action='store_true',
259 default=True, 298 default=True,
260 help='Include perf keyvals in the report [default]') 299 help='Include perf keyvals in the report [default]')
261 parser.add_option('--no-perf', dest='perf', action='store_false', 300 parser.add_option('--no-perf', dest='perf', action='store_false',
262 help='Don\'t include perf keyvals in the report') 301 help='Don\'t include perf keyvals in the report')
263 parser.add_option('--strip', dest='strip', type='string', action='store', 302 parser.add_option('--strip', dest='strip', type='string', action='store',
264 default='results.', 303 default='results.',
265 help='Strip a prefix from test directory names' 304 help='Strip a prefix from test directory names'
266 ' [default: \'%default\']') 305 ' [default: \'%default\']')
267 parser.add_option('--no-strip', dest='strip', const='', action='store_const', 306 parser.add_option('--no-strip', dest='strip', const='', action='store_const',
268 help='Don\'t strip a prefix from test directory names') 307 help='Don\'t strip a prefix from test directory names')
269 parser.add_option('--no-debug', dest='print_debug', action='store_false', 308 parser.add_option('--no-debug', dest='print_debug', action='store_false',
270 default=True, 309 default=True,
271 help='Do not print out the debug log when a test fails.') 310 help='Don\'t print out the debug log when a test fails.')
272 (options, args) = parser.parse_args() 311 (options, args) = parser.parse_args()
273 312
274 if not args: 313 if not args:
275 parser.print_help() 314 parser.print_help()
276 Die('no result directories provided') 315 Die('no result directories provided')
277 316
278 generator = ReportGenerator(options, args) 317 generator = ReportGenerator(options, args)
279 generator.Run() 318 generator.Run()
280 319
281 320
282 if __name__ == '__main__': 321 if __name__ == '__main__':
283 main() 322 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