Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #! /usr/bin/env python | 1 #! /usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright 2017 The Chromium Authors. All rights reserved. | 3 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 import argparse | 7 import argparse |
| 8 import collections | 8 import collections |
| 9 import json | 9 import json |
| 10 import tempfile | 10 import tempfile |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 if footer_row[FAIL_COUNT_INDEX]['data'] > 0: | 243 if footer_row[FAIL_COUNT_INDEX]['data'] > 0: |
| 244 footer_row[FAIL_COUNT_INDEX]['class'] += ' failure' | 244 footer_row[FAIL_COUNT_INDEX]['class'] += ' failure' |
| 245 else: | 245 else: |
| 246 footer_row[FAIL_COUNT_INDEX]['class'] += ' success' | 246 footer_row[FAIL_COUNT_INDEX]['class'] += ' success' |
| 247 | 247 |
| 248 return (header_row, | 248 return (header_row, |
| 249 [[suite_row] for suite_row in suite_row_dict.values()], | 249 [[suite_row] for suite_row in suite_row_dict.values()], |
| 250 footer_row) | 250 footer_row) |
| 251 | 251 |
| 252 | 252 |
| 253 def results_to_html(results_dict, cs_base_url, bucket, server_url): | 253 def feedback_url(result_details_link): |
| 254 return ('https://bugs.chromium.org/p/chromium/issues/entry?' | |
|
jbudorick
2017/05/04 18:09:27
nit: this might be a bit more readable if you hand
BigBossZhiling
2017/05/05 21:57:57
Done.
| |
| 255 'labels=Pri-2,Type-Bug,Restrict-View-Google&' | |
| 256 'summary=Result Details Feedback:' | |
| 257 '&components=Test%3EAndroid&' | |
| 258 'comment=Please check out: ') + result_details_link | |
| 259 | |
| 260 | |
| 261 def results_to_html(results_dict, cs_base_url, bucket, server_url, test_name, | |
| 262 builder_name, build_number): | |
| 254 """Convert list of test results into html format.""" | 263 """Convert list of test results into html format.""" |
| 255 | 264 |
| 256 test_rows_header, test_rows = create_test_table(results_dict, cs_base_url) | 265 test_rows_header, test_rows = create_test_table(results_dict, cs_base_url) |
| 257 suite_rows_header, suite_rows, suite_row_footer = create_suite_table( | 266 suite_rows_header, suite_rows, suite_row_footer = create_suite_table( |
| 258 results_dict) | 267 results_dict) |
| 259 | 268 |
| 260 suite_table_values = { | 269 suite_table_values = { |
| 261 'table_id': 'suite-table', | 270 'table_id': 'suite-table', |
| 262 'table_headers': suite_rows_header, | 271 'table_headers': suite_rows_header, |
| 263 'table_row_blocks': suite_rows, | 272 'table_row_blocks': suite_rows, |
| 264 'table_footer': suite_row_footer, | 273 'table_footer': suite_row_footer, |
| 265 } | 274 } |
| 266 | 275 |
| 267 test_table_values = { | 276 test_table_values = { |
| 268 'table_id': 'test-table', | 277 'table_id': 'test-table', |
| 269 'table_headers': test_rows_header, | 278 'table_headers': test_rows_header, |
| 270 'table_row_blocks': test_rows, | 279 'table_row_blocks': test_rows, |
| 271 } | 280 } |
| 272 | 281 |
| 273 main_template = JINJA_ENVIRONMENT.get_template( | 282 main_template = JINJA_ENVIRONMENT.get_template( |
| 274 os.path.join('template', 'main.html')) | 283 os.path.join('template', 'main.html')) |
| 275 return main_template.render( # pylint: disable=no-member | 284 |
| 285 dest = 'html/%s_%s_%s_%s.html' % ( | |
| 286 test_name, builder_name, build_number, | |
| 287 time.strftime('%Y_%m_%d_T%H_%M_%S')) | |
| 288 | |
| 289 result_details_link = '%s/%s/%s' % (server_url, bucket, dest) | |
| 290 | |
| 291 return (main_template.render( # pylint: disable=no-member | |
| 276 {'tb_values': [suite_table_values, test_table_values], | 292 {'tb_values': [suite_table_values, test_table_values], |
| 277 'bucket': bucket, 'server_url': server_url}) | 293 'bucket': bucket, 'server_url': server_url, |
| 294 'feedback_url': feedback_url(result_details_link)}), | |
| 295 dest, result_details_link) | |
| 278 | 296 |
| 279 | 297 |
| 280 def result_details(json_path, cs_base_url, bucket, server_url): | 298 def result_details(json_path, cs_base_url, bucket, server_url, test_name, |
| 299 builder_name, build_number): | |
| 281 """Get result details from json path and then convert results to html.""" | 300 """Get result details from json path and then convert results to html.""" |
| 282 | 301 |
| 283 with open(json_path) as json_file: | 302 with open(json_path) as json_file: |
| 284 json_object = json.loads(json_file.read()) | 303 json_object = json.loads(json_file.read()) |
| 285 | 304 |
| 286 if not 'per_iteration_data' in json_object: | 305 if not 'per_iteration_data' in json_object: |
| 287 return 'Error: json file missing per_iteration_data.' | 306 return 'Error: json file missing per_iteration_data.' |
| 288 | 307 |
| 289 results_dict = collections.defaultdict(list) | 308 results_dict = collections.defaultdict(list) |
| 290 for testsuite_run in json_object['per_iteration_data']: | 309 for testsuite_run in json_object['per_iteration_data']: |
| 291 for test, test_runs in testsuite_run.iteritems(): | 310 for test, test_runs in testsuite_run.iteritems(): |
| 292 results_dict[test].extend(test_runs) | 311 results_dict[test].extend(test_runs) |
| 293 return results_to_html(results_dict, cs_base_url, bucket, server_url) | 312 |
| 313 return results_to_html(results_dict, cs_base_url, bucket, server_url, | |
| 314 test_name, builder_name, build_number) | |
| 294 | 315 |
| 295 | 316 |
| 296 def upload_to_google_bucket(html, test_name, builder_name, build_number, | 317 def upload_to_google_bucket(html, bucket, dest, content_type): |
| 297 bucket, server_url, content_type): | |
| 298 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file: | 318 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file: |
| 299 temp_file.write(html) | 319 temp_file.write(html) |
| 300 temp_file.flush() | 320 temp_file.flush() |
| 301 dest = 'html/%s_%s_%s_%s.html' % ( | |
| 302 test_name, builder_name, build_number, | |
| 303 time.strftime('%Y_%m_%d_T%H_%M_%S')) | |
| 304 gsutil_path = os.path.join(BASE_DIR, 'third_party', 'catapult', | 321 gsutil_path = os.path.join(BASE_DIR, 'third_party', 'catapult', |
| 305 'third_party', 'gsutil', 'gsutil.py') | 322 'third_party', 'gsutil', 'gsutil.py') |
| 306 subprocess.check_call([ | 323 subprocess.check_call([ |
| 307 sys.executable, gsutil_path, '-h', "Content-Type:%s" % content_type, | 324 sys.executable, gsutil_path, '-h', "Content-Type:%s" % content_type, |
| 308 'cp', temp_file.name, 'gs://%s/%s' % (bucket, dest)]) | 325 'cp', temp_file.name, 'gs://%s/%s' % (bucket, dest)]) |
| 309 | 326 |
| 310 return '%s/%s/%s' % (server_url, bucket, dest) | |
| 311 | |
| 312 | 327 |
| 313 def main(): | 328 def main(): |
| 314 parser = argparse.ArgumentParser() | 329 parser = argparse.ArgumentParser() |
| 315 parser.add_argument('--json-file', help='Path of json file.') | 330 parser.add_argument('--json-file', help='Path of json file.') |
| 316 parser.add_argument('--cs-base-url', help='Base url for code search.', | 331 parser.add_argument('--cs-base-url', help='Base url for code search.', |
| 317 default='http://cs.chromium.org') | 332 default='http://cs.chromium.org') |
| 318 parser.add_argument('--bucket', help='Google storage bucket.', required=True) | 333 parser.add_argument('--bucket', help='Google storage bucket.', required=True) |
| 319 parser.add_argument('--builder-name', help='Builder name.') | 334 parser.add_argument('--builder-name', help='Builder name.') |
| 320 parser.add_argument('--build-number', help='Build number.') | 335 parser.add_argument('--build-number', help='Build number.') |
| 321 parser.add_argument('--test-name', help='The name of the test.', | 336 parser.add_argument('--test-name', help='The name of the test.', |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 381 if args.positional: | 396 if args.positional: |
| 382 if not len(args.positional) == 1: | 397 if not len(args.positional) == 1: |
| 383 raise parser.error('More than 1 json file specified.') | 398 raise parser.error('More than 1 json file specified.') |
| 384 json_file = args.positional[0] | 399 json_file = args.positional[0] |
| 385 elif args.json_file: | 400 elif args.json_file: |
| 386 json_file = args.json_file | 401 json_file = args.json_file |
| 387 | 402 |
| 388 if not os.path.exists(json_file): | 403 if not os.path.exists(json_file): |
| 389 raise IOError('--json-file %s not found.' % json_file) | 404 raise IOError('--json-file %s not found.' % json_file) |
| 390 | 405 |
| 391 result_html_string = result_details(json_file, args.cs_base_url, | 406 # Link to result details presentation page is a part of the page. |
| 392 args.bucket, args.server_url) | 407 result_html_string, dest, result_details_link = result_details( |
| 393 result_details_link = upload_to_google_bucket( | 408 json_file, args.cs_base_url, args.bucket, args.server_url, |
| 409 args.test_name, builder_name, build_number) | |
| 410 | |
| 411 upload_to_google_bucket( | |
| 394 result_html_string.encode('UTF-8'), | 412 result_html_string.encode('UTF-8'), |
| 395 args.test_name, builder_name, | 413 args.bucket, dest, args.content_type) |
| 396 build_number, args.bucket, | |
| 397 args.server_url, args.content_type) | |
| 398 | 414 |
| 399 if args.output_json: | 415 if args.output_json: |
| 400 with open(json_file) as original_json_file: | 416 with open(json_file) as original_json_file: |
| 401 json_object = json.load(original_json_file) | 417 json_object = json.load(original_json_file) |
| 402 json_object['links'] = {'result_details': result_details_link} | 418 json_object['links'] = {'result_details': result_details_link} |
| 403 with open(args.output_json, 'w') as f: | 419 with open(args.output_json, 'w') as f: |
| 404 json.dump(json_object, f) | 420 json.dump(json_object, f) |
| 405 else: | 421 else: |
| 406 print result_details_link | 422 print result_details_link |
| 407 | 423 |
| 408 if __name__ == '__main__': | 424 if __name__ == '__main__': |
| 409 sys.exit(main()) | 425 sys.exit(main()) |
| OLD | NEW |