blob: 4517918e4d67ac8654ce27a22cf7efb86d3a421b [file] [log] [blame]
Caroline Tice48462062016-11-18 16:49:00 -08001#!/usr/bin/env python2
2"""Generate summary report for ChromeOS toolchain waterfalls."""
3
4# Desired future features (to be added):
5# - arguments to allow generating only the main waterfall report,
6# or only the rotating builder reports, or only the failures
7# report; or the waterfall reports without the failures report.
8# - Better way of figuring out which dates/builds to generate
9# reports for: probably an argument specifying a date or a date
10# range, then use something like the new buildbot utils to
11# query the build logs to find the right build numbers for the
12# builders for the specified dates.
13# - Store/get the json/data files in mobiletc-prebuild's x20 area.
14# - Update data in json file to reflect, for each testsuite, which
15# tests are not expected to run on which boards; update this
16# script to use that data appropriately.
17# - Make sure user's prodaccess is up-to-date before trying to use
18# this script.
19# - Add some nice formatting/highlighting to reports.
20
21from __future__ import print_function
22
Caroline Ticee02e9f82016-12-01 13:14:41 -080023import getpass
Caroline Tice48462062016-11-18 16:49:00 -080024import json
25import os
Caroline Ticee02e9f82016-12-01 13:14:41 -080026import shutil
Caroline Tice48462062016-11-18 16:49:00 -080027import sys
28import time
29
30from cros_utils import command_executer
31
32# All the test suites whose data we might want for the reports.
33TESTS = (
34 ('bvt-inline', 'HWTest'),
35 ('bvt-cq', 'HWTest'),
36 ('toolchain-tests', 'HWTest'),
37 ('security', 'HWTest'),
38 ('kernel_daily_regression', 'HWTest'),
39 ('kernel_daily_benchmarks', 'HWTest'),)
40
41# The main waterfall builders, IN THE ORDER IN WHICH WE WANT THEM
42# LISTED IN THE REPORT.
43WATERFALL_BUILDERS = [
44 'amd64-gcc-toolchain', 'arm-gcc-toolchain', 'arm64-gcc-toolchain',
45 'x86-gcc-toolchain', 'amd64-llvm-toolchain', 'arm-llvm-toolchain',
46 'arm64-llvm-toolchain', 'x86-llvm-toolchain', 'amd64-llvm-next-toolchain',
47 'arm-llvm-next-toolchain', 'arm64-llvm-next-toolchain',
48 'x86-llvm-next-toolchain'
49]
50
Caroline Tice48462062016-11-18 16:49:00 -080051DATA_DIR = '/google/data/rw/users/mo/mobiletc-prebuild/waterfall-report-data/'
Caroline Ticee02e9f82016-12-01 13:14:41 -080052ARCHIVE_DIR = '/google/data/rw/users/mo/mobiletc-prebuild/waterfall-reports/'
Caroline Tice48462062016-11-18 16:49:00 -080053DOWNLOAD_DIR = '/tmp/waterfall-logs'
Caroline Ticee02e9f82016-12-01 13:14:41 -080054MAX_SAVE_RECORDS = 7
Caroline Tice48462062016-11-18 16:49:00 -080055BUILD_DATA_FILE = '%s/build-data.txt' % DATA_DIR
56ROTATING_BUILDERS = ['gcc_toolchain', 'llvm_toolchain']
57
58# For int-to-string date conversion. Note, the index of the month in this
59# list needs to correspond to the month's integer value. i.e. 'Sep' must
60# be as MONTHS[9].
61MONTHS = [
62 '', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
63 'Nov', 'Dec'
64]
65
66
67def format_date(int_date):
68 """Convert an integer date to a string date. YYYYMMDD -> YYYY-MMM-DD"""
69
70 if int_date == 0:
71 return 'today'
72
73 tmp_date = int_date
74 day = tmp_date % 100
75 tmp_date = tmp_date / 100
76 month = tmp_date % 100
77 year = tmp_date / 100
78
79 month_str = MONTHS[month]
80 date_str = '%d-%s-%d' % (year, month_str, day)
81 return date_str
82
83
Caroline Ticee02e9f82016-12-01 13:14:41 -080084def EmailReport(report_file, report_type, date):
85 subject = '%s Waterfall Summary report, %s' % (report_type, date)
86 email_to = getpass.getuser()
87 command = ('sendgmr --to=%s@google.com --subject="%s" --body_file=%s' %
88 (email_to, subject, report_file))
89 command_executer.GetCommandExecuter().RunCommand(command)
90
91
92def PruneOldFailures(failure_dict, int_date):
93 earliest_date = int_date - MAX_SAVE_RECORDS
94 for suite in failure_dict:
95 suite_dict = failure_dict[suite]
96 test_keys_to_remove = []
97 for test in suite_dict:
98 test_dict = suite_dict[test]
99 msg_keys_to_remove = []
100 for msg in test_dict:
101 fails = test_dict[msg]
102 i = 0
103 while i < len(fails) and fails[i][0] <= earliest_date:
104 i += 1
105 new_fails = fails[i:]
106 test_dict[msg] = new_fails
107 if len(new_fails) == 0:
108 msg_keys_to_remove.append(msg)
109
110 for k in msg_keys_to_remove:
111 del test_dict[k]
112
113 suite_dict[test] = test_dict
114 if len(test_dict) == 0:
115 test_keys_to_remove.append(test)
116
117 for k in test_keys_to_remove:
118 del suite_dict[k]
119
120 failure_dict[suite] = suite_dict
121
122
Caroline Tice48462062016-11-18 16:49:00 -0800123def GenerateWaterfallReport(report_dict, fail_dict, waterfall_type, date):
124 """Write out the actual formatted report."""
125
126 filename = 'waterfall_report.%s_waterfall.%s.txt' % (waterfall_type, date)
127
128 date_string = ''
129 date_list = report_dict['date']
130 num_dates = len(date_list)
131 i = 0
132 for d in date_list:
133 date_string += d
134 if i < num_dates - 1:
135 date_string += ', '
136 i += 1
137
138 if waterfall_type == 'main':
139 report_list = WATERFALL_BUILDERS
140 else:
141 report_list = report_dict.keys()
142
143 with open(filename, 'w') as out_file:
144 # Write Report Header
145 out_file.write('\nStatus of %s Waterfall Builds from %s\n\n' %
146 (waterfall_type, date_string))
147 out_file.write(' '
148 ' kernel kernel\n')
149 out_file.write(' Build bvt- bvt-cq '
150 'toolchain- security daily daily\n')
151 out_file.write(' status inline '
152 ' tests regression benchmarks\n')
153 out_file.write(' [P/ F/ DR]* [P/ F /DR]* '
154 '[P/ F/ DR]* [P/ F/ DR]* [P/ F/ DR]* [P/ F/ DR]*\n\n')
155
156 # Write daily waterfall status section.
157 for i in range(0, len(report_list)):
158 builder = report_list[i]
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800159 if builder == 'date':
160 continue
Caroline Tice48462062016-11-18 16:49:00 -0800161
162 if builder not in report_dict:
163 out_file.write('Unable to find information for %s.\n\n' % builder)
164 continue
165
166 build_dict = report_dict[builder]
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800167 status = build_dict.get('build_status', 'bad')
168 inline = build_dict.get('bvt-inline', '[??/ ?? /??]')
169 cq = build_dict.get('bvt-cq', '[??/ ?? /??]')
170 inline_color = build_dict.get('bvt-inline-color', '')
171 cq_color = build_dict.get('bvt-cq-color', '')
Caroline Tice48462062016-11-18 16:49:00 -0800172 if 'x86' not in builder:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800173 toolchain = build_dict.get('toolchain-tests', '[??/ ?? /??]')
174 security = build_dict.get('security', '[??/ ?? /??]')
175 toolchain_color = build_dict.get('toolchain-tests-color', '')
176 security_color = build_dict.get('security-color', '')
Caroline Tice48462062016-11-18 16:49:00 -0800177 if 'gcc' in builder:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800178 regression = build_dict.get('kernel_daily_regression', '[??/ ?? /??]')
179 bench = build_dict.get('kernel_daily_benchmarks', '[??/ ?? /??]')
180 regression_color = build_dict.get('kernel_daily_regression-color', '')
181 bench_color = build_dict.get('kernel_daily_benchmarks-color', '')
182 out_file.write(' %6s %6s'
183 ' %6s %6s %6s %6s\n' %
184 (inline_color, cq_color, toolchain_color,
185 security_color, regression_color, bench_color))
Caroline Tice48462062016-11-18 16:49:00 -0800186 out_file.write('%25s %3s %s %s %s %s %s %s\n' % (builder, status,
187 inline, cq,
188 toolchain, security,
189 regression, bench))
190 else:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800191 out_file.write(' %6s %6s'
192 ' %6s %6s\n' % (inline_color, cq_color,
193 toolchain_color,
194 security_color))
Caroline Tice48462062016-11-18 16:49:00 -0800195 out_file.write('%25s %3s %s %s %s %s\n' % (builder, status, inline,
196 cq, toolchain, security))
197 else:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800198 out_file.write(' %6s %6s\n' %
199 (inline_color, cq_color))
Caroline Tice48462062016-11-18 16:49:00 -0800200 out_file.write('%25s %3s %s %s\n' % (builder, status, inline, cq))
201 if 'build_link' in build_dict:
202 out_file.write('%s\n\n' % build_dict['build_link'])
203
204 out_file.write('\n\n*P = Number of tests in suite that Passed; F = '
205 'Number of tests in suite that Failed; DR = Number of tests'
206 ' in suite that Didn\'t Run.\n')
207
208 # Write failure report section.
209 out_file.write('\n\nSummary of Test Failures as of %s\n\n' % date_string)
210
211 # We want to sort the errors and output them in order of the ones that occur
212 # most often. So we have to collect the data about all of them, then sort
213 # it.
214 error_groups = []
215 for suite in fail_dict:
216 suite_dict = fail_dict[suite]
217 if suite_dict:
218 for test in suite_dict:
219 test_dict = suite_dict[test]
220 for err_msg in test_dict:
221 err_list = test_dict[err_msg]
222 sorted_list = sorted(err_list, key=lambda x: x[0], reverse=True)
223 err_group = [len(sorted_list), suite, test, err_msg, sorted_list]
224 error_groups.append(err_group)
225
226 # Sort the errors by the number of errors of each type. Then output them in
227 # order.
228 sorted_errors = sorted(error_groups, key=lambda x: x[0], reverse=True)
229 for i in range(0, len(sorted_errors)):
230 err_group = sorted_errors[i]
231 suite = err_group[1]
232 test = err_group[2]
233 err_msg = err_group[3]
234 err_list = err_group[4]
235 out_file.write('Suite: %s\n' % suite)
236 out_file.write(' %s (%d failures)\n' % (test, len(err_list)))
237 out_file.write(' (%s)\n' % err_msg)
238 for i in range(0, len(err_list)):
239 err = err_list[i]
240 out_file.write(' %s, %s, %s\n' % (format_date(err[0]), err[1],
241 err[2]))
242 out_file.write('\n')
243
244 print('Report generated in %s.' % filename)
Caroline Ticee02e9f82016-12-01 13:14:41 -0800245 return filename
Caroline Tice48462062016-11-18 16:49:00 -0800246
247
248def UpdateReport(report_dict, builder, test, report_date, build_link,
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800249 test_summary, board, color):
Caroline Tice48462062016-11-18 16:49:00 -0800250 """Update the data in our report dictionary with current test's data."""
251
252 if 'date' not in report_dict:
253 report_dict['date'] = [report_date]
254 elif report_date not in report_dict['date']:
255 # It is possible that some of the builders started/finished on different
256 # days, so we allow for multiple dates in the reports.
257 report_dict['date'].append(report_date)
258
259 build_key = ''
260 if builder == 'gcc_toolchain':
261 build_key = '%s-gcc-toolchain' % board
262 elif builder == 'llvm_toolchain':
263 build_key = '%s-llvm-toolchain' % board
264 else:
265 build_key = builder
266
267 if build_key not in report_dict.keys():
268 build_dict = dict()
269 else:
270 build_dict = report_dict[build_key]
271
272 if 'build_link' not in build_dict:
273 build_dict['build_link'] = build_link
274
275 if 'date' not in build_dict:
276 build_dict['date'] = report_date
277
278 if 'board' in build_dict and build_dict['board'] != board:
279 raise RuntimeError('Error: Two different boards (%s,%s) in one build (%s)!'
280 % (board, build_dict['board'], build_link))
281 build_dict['board'] = board
282
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800283 color_key = '%s-color' % test
284 build_dict[color_key] = color
285
Caroline Tice48462062016-11-18 16:49:00 -0800286 # Check to see if we already have a build status for this build_key
287 status = ''
288 if 'build_status' in build_dict.keys():
289 # Use current build_status, unless current test failed (see below).
290 status = build_dict['build_status']
291
292 if not test_summary:
293 # Current test data was not available, so something was bad with build.
294 build_dict['build_status'] = 'bad'
295 build_dict[test] = '[ no data ]'
296 else:
297 build_dict[test] = test_summary
298 if not status:
299 # Current test ok; no other data, so assume build was ok.
300 build_dict['build_status'] = 'ok'
301
302 report_dict[build_key] = build_dict
303
304
305def UpdateBuilds(builds):
306 """Update the data in our build-data.txt file."""
307
308 # The build data file records the last build number for which we
309 # generated a report. When we generate the next report, we read
310 # this data and increment it to get the new data; when we finish
311 # generating the reports, we write the updated values into this file.
312 # NOTE: One side effect of doing this at the end: If the script
313 # fails in the middle of generating a report, this data does not get
314 # updated.
315 with open(BUILD_DATA_FILE, 'w') as fp:
316 gcc_max = 0
317 llvm_max = 0
318 for b in builds:
319 if b[0] == 'gcc_toolchain':
320 gcc_max = max(gcc_max, b[1])
321 elif b[0] == 'llvm_toolchain':
322 llvm_max = max(llvm_max, b[1])
323 else:
324 fp.write('%s,%d\n' % (b[0], b[1]))
325 if gcc_max > 0:
326 fp.write('gcc_toolchain,%d\n' % gcc_max)
327 if llvm_max > 0:
328 fp.write('llvm_toolchain,%d\n' % llvm_max)
329
330
331def GetBuilds():
332 """Read build-data.txt to determine values for current report."""
333
334 # Read the values of the last builds used to generate a report, and
335 # increment them appropriately, to get values for generating the
336 # current report. (See comments in UpdateBuilds).
337 with open(BUILD_DATA_FILE, 'r') as fp:
338 lines = fp.readlines()
339
340 builds = []
341 for l in lines:
342 l = l.rstrip()
343 words = l.split(',')
344 builder = words[0]
345 build = int(words[1])
346 builds.append((builder, build + 1))
347 # NOTE: We are assuming here that there are always 2 daily builds in
348 # each of the rotating builders. I am not convinced this is a valid
349 # assumption.
350 if builder == 'gcc_toolchain' or builder == 'llvm_toolchain':
351 builds.append((builder, build + 2))
352
353 return builds
354
355
356def RecordFailures(failure_dict, platform, suite, builder, int_date, log_file,
357 build_num, failed):
358 """Read and update the stored data about test failures."""
359
360 # Get the dictionary for this particular test suite from the failures
361 # dictionary.
362 suite_dict = failure_dict[suite]
363
364 # Read in the entire log file for this test/build.
365 with open(log_file, 'r') as in_file:
366 lines = in_file.readlines()
367
368 # Update the entries in the failure dictionary for each test within this suite
369 # that failed.
370 for test in failed:
371 # Check to see if there is already an entry in the suite dictionary for this
372 # test; if so use that, otherwise create a new entry.
373 if test in suite_dict:
374 test_dict = suite_dict[test]
375 else:
376 test_dict = dict()
377 # Parse the lines from the log file, looking for lines that indicate this
378 # test failed.
379 msg = ''
380 for l in lines:
381 words = l.split()
382 if len(words) < 3:
383 continue
384 if ((words[0] == test and words[1] == 'ERROR:') or
385 (words[0] == 'provision' and words[1] == 'FAIL:')):
386 words = words[2:]
387 # Get the error message for the failure.
388 msg = ' '.join(words)
389 if not msg:
390 msg = 'Unknown_Error'
391
392 # Look for an existing entry for this error message in the test dictionary.
393 # If found use that, otherwise create a new entry for this error message.
394 if msg in test_dict:
395 error_list = test_dict[msg]
396 else:
397 error_list = list()
398 # Create an entry for this new failure
399 new_item = [int_date, platform, builder, build_num]
400 # Add this failure to the error list if it's not already there.
401 if new_item not in error_list:
402 error_list.append([int_date, platform, builder, build_num])
403 # Sort the error list by date.
404 error_list.sort(key=lambda x: x[0])
405 # Calculate the earliest date to save; delete records for older failures.
406 earliest_date = int_date - MAX_SAVE_RECORDS
407 i = 0
Caroline Ticee02e9f82016-12-01 13:14:41 -0800408 while i < len(error_list) and error_list[i][0] <= earliest_date:
Caroline Tice48462062016-11-18 16:49:00 -0800409 i += 1
410 if i > 0:
411 error_list = error_list[i:]
412 # Save the error list in the test's dictionary, keyed on error_msg.
413 test_dict[msg] = error_list
414
415 # Save the updated test dictionary in the test_suite dictionary.
416 suite_dict[test] = test_dict
417
418 # Save the updated test_suite dictionary in the failure dictionary.
419 failure_dict[suite] = suite_dict
420
421
422def ParseLogFile(log_file, test_data_dict, failure_dict, test, builder,
423 build_num, build_link):
424 """Parse the log file from the given builder, build_num and test.
425
426 Also adds the results for this test to our test results dictionary,
427 and calls RecordFailures, to update our test failure data.
428 """
429
430 lines = []
431 with open(log_file, 'r') as infile:
432 lines = infile.readlines()
433
434 passed = {}
435 failed = {}
436 not_run = {}
437 date = ''
438 status = ''
439 board = ''
440 num_provision_errors = 0
441 build_ok = True
442 afe_line = ''
443
444 for line in lines:
445 if line.rstrip() == '<title>404 Not Found</title>':
446 print('Warning: File for %s (build number %d), %s was not found.' %
447 (builder, build_num, test))
448 build_ok = False
449 break
450 if '[ PASSED ]' in line:
451 test_name = line.split()[0]
452 if test_name != 'Suite':
453 passed[test_name] = True
454 elif '[ FAILED ]' in line:
455 test_name = line.split()[0]
456 if test_name == 'provision':
457 num_provision_errors += 1
458 not_run[test_name] = True
459 elif test_name != 'Suite':
460 failed[test_name] = True
461 elif line.startswith('started: '):
462 date = line.rstrip()
463 date = date[9:]
464 date_obj = time.strptime(date, '%a %b %d %H:%M:%S %Y')
465 int_date = (
466 date_obj.tm_year * 10000 + date_obj.tm_mon * 100 + date_obj.tm_mday)
467 date = time.strftime('%a %b %d %Y', date_obj)
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800468 elif not status and line.startswith('status: '):
Caroline Tice48462062016-11-18 16:49:00 -0800469 status = line.rstrip()
470 words = status.split(':')
471 status = words[-1]
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800472 elif line.find('Suite passed with a warning') != -1:
473 status = 'WARNING'
Caroline Tice48462062016-11-18 16:49:00 -0800474 elif line.startswith('@@@STEP_LINK@Link to suite@'):
475 afe_line = line.rstrip()
476 words = afe_line.split('@')
477 for w in words:
478 if w.startswith('http'):
479 afe_line = w
480 afe_line = afe_line.replace('&amp;', '&')
481 elif 'INFO: RunCommand:' in line:
482 words = line.split()
483 for i in range(0, len(words) - 1):
484 if words[i] == '--board':
485 board = words[i + 1]
486
487 test_dict = test_data_dict[test]
488 test_list = test_dict['tests']
489
490 if build_ok:
491 for t in test_list:
492 if not t in passed and not t in failed:
493 not_run[t] = True
494
495 total_pass = len(passed)
496 total_fail = len(failed)
497 total_notrun = len(not_run)
498
499 else:
500 total_pass = 0
501 total_fail = 0
502 total_notrun = 0
503 status = 'Not found.'
504 if not build_ok:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800505 return [], date, board, 0, ' '
Caroline Tice48462062016-11-18 16:49:00 -0800506
507 build_dict = dict()
508 build_dict['id'] = build_num
509 build_dict['builder'] = builder
510 build_dict['date'] = date
511 build_dict['build_link'] = build_link
512 build_dict['total_pass'] = total_pass
513 build_dict['total_fail'] = total_fail
514 build_dict['total_not_run'] = total_notrun
515 build_dict['afe_job_link'] = afe_line
516 build_dict['provision_errors'] = num_provision_errors
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800517 if status.strip() == 'SUCCESS':
518 build_dict['color'] = 'green '
519 elif status.strip() == 'FAILURE':
520 build_dict['color'] = ' red '
521 elif status.strip() == 'WARNING':
522 build_dict['color'] = 'orange'
523 else:
524 build_dict['color'] = ' '
Caroline Tice48462062016-11-18 16:49:00 -0800525
526 # Use YYYYMMDD (integer) as the build record key
527 if build_ok:
528 if board in test_dict:
529 board_dict = test_dict[board]
530 else:
531 board_dict = dict()
532 board_dict[int_date] = build_dict
533
534 # Only keep the last 5 records (based on date)
535 keys_list = board_dict.keys()
536 if len(keys_list) > MAX_SAVE_RECORDS:
537 min_key = min(keys_list)
538 del board_dict[min_key]
539
540 # Make sure changes get back into the main dictionary
541 test_dict[board] = board_dict
542 test_data_dict[test] = test_dict
543
544 if len(failed) > 0:
545 RecordFailures(failure_dict, board, test, builder, int_date, log_file,
546 build_num, failed)
547
548 summary_result = '[%2d/ %2d/ %2d]' % (total_pass, total_fail, total_notrun)
549
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800550 return summary_result, date, board, int_date, build_dict['color']
Caroline Tice48462062016-11-18 16:49:00 -0800551
552
553def DownloadLogFile(builder, buildnum, test, test_family):
554
555 ce = command_executer.GetCommandExecuter()
556 os.system('mkdir -p %s/%s/%s' % (DOWNLOAD_DIR, builder, test))
557 if builder == 'gcc_toolchain' or builder == 'llvm_toolchain':
558 source = ('https://uberchromegw.corp.google.com/i/chromiumos.tryserver'
559 '/builders/%s/builds/%d/steps/%s%%20%%5B%s%%5D/logs/stdio' %
560 (builder, buildnum, test_family, test))
561 build_link = ('https://uberchromegw.corp.google.com/i/chromiumos.tryserver'
562 '/builders/%s/builds/%d' % (builder, buildnum))
563 else:
564 source = ('https://uberchromegw.corp.google.com/i/chromeos/builders/%s/'
565 'builds/%d/steps/%s%%20%%5B%s%%5D/logs/stdio' %
566 (builder, buildnum, test_family, test))
567 build_link = ('https://uberchromegw.corp.google.com/i/chromeos/builders/%s'
568 '/builds/%d' % (builder, buildnum))
569
570 target = '%s/%s/%s/%d' % (DOWNLOAD_DIR, builder, test, buildnum)
571 if not os.path.isfile(target) or os.path.getsize(target) == 0:
572 cmd = 'sso_client %s > %s' % (source, target)
573 status = ce.RunCommand(cmd)
574 if status != 0:
575 return '', ''
576
577 return target, build_link
578
579
580def Main():
581 """Main function for this script."""
582
583 test_data_dict = dict()
584 failure_dict = dict()
585 with open('%s/waterfall-test-data.json' % DATA_DIR, 'r') as input_file:
586 test_data_dict = json.load(input_file)
587
588 with open('%s/test-failure-data.json' % DATA_DIR, 'r') as fp:
589 failure_dict = json.load(fp)
590
591 builds = GetBuilds()
592
593 waterfall_report_dict = dict()
594 rotating_report_dict = dict()
595 int_date = 0
596 for test_desc in TESTS:
597 test, test_family = test_desc
598 for build in builds:
599 (builder, buildnum) = build
600 if test.startswith('kernel') and 'llvm' in builder:
601 continue
602 if 'x86' in builder and not test.startswith('bvt'):
603 continue
604 target, build_link = DownloadLogFile(builder, buildnum, test, test_family)
605
606 if os.path.exists(target):
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800607 test_summary, report_date, board, tmp_date, color = ParseLogFile(
Caroline Tice48462062016-11-18 16:49:00 -0800608 target, test_data_dict, failure_dict, test, builder, buildnum,
609 build_link)
610
611 if tmp_date != 0:
612 int_date = tmp_date
613
614 if builder in ROTATING_BUILDERS:
615 UpdateReport(rotating_report_dict, builder, test, report_date,
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800616 build_link, test_summary, board, color)
Caroline Tice48462062016-11-18 16:49:00 -0800617 else:
618 UpdateReport(waterfall_report_dict, builder, test, report_date,
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800619 build_link, test_summary, board, color)
Caroline Tice48462062016-11-18 16:49:00 -0800620
Caroline Ticee02e9f82016-12-01 13:14:41 -0800621 PruneOldFailures(failure_dict, int_date)
622
Caroline Tice48462062016-11-18 16:49:00 -0800623 if waterfall_report_dict:
Caroline Ticee02e9f82016-12-01 13:14:41 -0800624 main_report = GenerateWaterfallReport(waterfall_report_dict, failure_dict,
625 'main', int_date)
626 EmailReport(main_report, 'Main', format_date(int_date))
627 shutil.copy(main_report, ARCHIVE_DIR)
Caroline Tice48462062016-11-18 16:49:00 -0800628 if rotating_report_dict:
Caroline Ticee02e9f82016-12-01 13:14:41 -0800629 rotating_report = GenerateWaterfallReport(rotating_report_dict,
630 failure_dict, 'rotating',
631 int_date)
632 EmailReport(rotating_report, 'Rotating', format_date(int_date))
633 shutil.copy(rotating_report, ARCHIVE_DIR)
Caroline Tice48462062016-11-18 16:49:00 -0800634
635 with open('%s/waterfall-test-data.json' % DATA_DIR, 'w') as out_file:
636 json.dump(test_data_dict, out_file, indent=2)
637
638 with open('%s/test-failure-data.json' % DATA_DIR, 'w') as out_file:
639 json.dump(failure_dict, out_file, indent=2)
640
641 UpdateBuilds(builds)
642
643
644if __name__ == '__main__':
645 Main()
646 sys.exit(0)