| 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 |
| 11 import os | 11 import os |
| 12 import sys | 12 import sys |
| 13 import urllib |
| 13 | 14 |
| 14 CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) | 15 CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| 15 BASE_DIR = os.path.abspath(os.path.join( | 16 BASE_DIR = os.path.abspath(os.path.join( |
| 16 CURRENT_DIR, '..', '..', '..', '..', '..')) | 17 CURRENT_DIR, '..', '..', '..', '..', '..')) |
| 17 | 18 |
| 18 sys.path.append(os.path.join(BASE_DIR, 'build', 'android')) | 19 sys.path.append(os.path.join(BASE_DIR, 'build', 'android')) |
| 19 from pylib.utils import google_storage_helper # pylint: disable=import-error | 20 from pylib.utils import google_storage_helper # pylint: disable=import-error |
| 20 | 21 |
| 21 sys.path.append(os.path.join(BASE_DIR, 'third_party')) | 22 sys.path.append(os.path.join(BASE_DIR, 'third_party')) |
| 22 import jinja2 # pylint: disable=import-error | 23 import jinja2 # pylint: disable=import-error |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 if footer_row[FAIL_COUNT_INDEX]['data'] > 0: | 246 if footer_row[FAIL_COUNT_INDEX]['data'] > 0: |
| 246 footer_row[FAIL_COUNT_INDEX]['class'] += ' failure' | 247 footer_row[FAIL_COUNT_INDEX]['class'] += ' failure' |
| 247 else: | 248 else: |
| 248 footer_row[FAIL_COUNT_INDEX]['class'] += ' success' | 249 footer_row[FAIL_COUNT_INDEX]['class'] += ' success' |
| 249 | 250 |
| 250 return (header_row, | 251 return (header_row, |
| 251 [[suite_row] for suite_row in suite_row_dict.values()], | 252 [[suite_row] for suite_row in suite_row_dict.values()], |
| 252 footer_row) | 253 footer_row) |
| 253 | 254 |
| 254 | 255 |
| 255 def results_to_html(results_dict, cs_base_url): | 256 def feedback_url(result_details_link): |
| 257 url_args = urllib.urlencode([ |
| 258 ('labels', 'Pri-2,Type-Bug,Restrict-View-Google'), |
| 259 ('summary', 'Result Details Feedback:'), |
| 260 ('components', 'Test>Android'), |
| 261 ('comment', 'Please check out: %s' % result_details_link)]) |
| 262 return 'https://bugs.chromium.org/p/chromium/issues/entry?%s' % url_args |
| 263 |
| 264 |
| 265 def results_to_html(results_dict, cs_base_url, bucket, test_name, |
| 266 builder_name, build_number): |
| 256 """Convert list of test results into html format.""" | 267 """Convert list of test results into html format.""" |
| 257 | 268 |
| 258 test_rows_header, test_rows = create_test_table(results_dict, cs_base_url) | 269 test_rows_header, test_rows = create_test_table(results_dict, cs_base_url) |
| 259 suite_rows_header, suite_rows, suite_row_footer = create_suite_table( | 270 suite_rows_header, suite_rows, suite_row_footer = create_suite_table( |
| 260 results_dict) | 271 results_dict) |
| 261 | 272 |
| 262 suite_table_values = { | 273 suite_table_values = { |
| 263 'table_id': 'suite-table', | 274 'table_id': 'suite-table', |
| 264 'table_headers': suite_rows_header, | 275 'table_headers': suite_rows_header, |
| 265 'table_row_blocks': suite_rows, | 276 'table_row_blocks': suite_rows, |
| 266 'table_footer': suite_row_footer, | 277 'table_footer': suite_row_footer, |
| 267 } | 278 } |
| 268 | 279 |
| 269 test_table_values = { | 280 test_table_values = { |
| 270 'table_id': 'test-table', | 281 'table_id': 'test-table', |
| 271 'table_headers': test_rows_header, | 282 'table_headers': test_rows_header, |
| 272 'table_row_blocks': test_rows, | 283 'table_row_blocks': test_rows, |
| 273 } | 284 } |
| 274 | 285 |
| 275 main_template = JINJA_ENVIRONMENT.get_template( | 286 main_template = JINJA_ENVIRONMENT.get_template( |
| 276 os.path.join('template', 'main.html')) | 287 os.path.join('template', 'main.html')) |
| 277 return main_template.render( # pylint: disable=no-member | 288 dest = google_storage_helper.unique_name( |
| 278 {'tb_values': [suite_table_values, test_table_values]}) | 289 '%s_%s_%s' % (test_name, builder_name, build_number)) |
| 290 |
| 291 result_details_link = google_storage_helper.get_url_link( |
| 292 dest, '%s/html' % bucket) |
| 293 |
| 294 return (main_template.render( # pylint: disable=no-member |
| 295 {'tb_values': [suite_table_values, test_table_values], |
| 296 'feedback_url': feedback_url(result_details_link) |
| 297 }), dest, result_details_link) |
| 279 | 298 |
| 280 | 299 |
| 281 def result_details(json_path, cs_base_url): | 300 def result_details(json_path, cs_base_url, bucket, test_name, |
| 301 builder_name, build_number): |
| 282 """Get result details from json path and then convert results to html.""" | 302 """Get result details from json path and then convert results to html.""" |
| 283 | 303 |
| 284 with open(json_path) as json_file: | 304 with open(json_path) as json_file: |
| 285 json_object = json.loads(json_file.read()) | 305 json_object = json.loads(json_file.read()) |
| 286 | 306 |
| 287 if not 'per_iteration_data' in json_object: | 307 if not 'per_iteration_data' in json_object: |
| 288 return 'Error: json file missing per_iteration_data.' | 308 return 'Error: json file missing per_iteration_data.' |
| 289 | 309 |
| 290 results_dict = collections.defaultdict(list) | 310 results_dict = collections.defaultdict(list) |
| 291 for testsuite_run in json_object['per_iteration_data']: | 311 for testsuite_run in json_object['per_iteration_data']: |
| 292 for test, test_runs in testsuite_run.iteritems(): | 312 for test, test_runs in testsuite_run.iteritems(): |
| 293 results_dict[test].extend(test_runs) | 313 results_dict[test].extend(test_runs) |
| 294 return results_to_html(results_dict, cs_base_url) | 314 return results_to_html(results_dict, cs_base_url, bucket, |
| 315 test_name, builder_name, build_number) |
| 295 | 316 |
| 296 | 317 |
| 297 def upload_to_google_bucket(html, test_name, builder_name, build_number, | 318 def upload_to_google_bucket(html, bucket, dest): |
| 298 bucket): | |
| 299 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file: | 319 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file: |
| 300 temp_file.write(html) | 320 temp_file.write(html) |
| 301 temp_file.flush() | 321 temp_file.flush() |
| 302 | |
| 303 return google_storage_helper.upload( | 322 return google_storage_helper.upload( |
| 304 name=google_storage_helper.unique_name( | 323 name=dest, |
| 305 '%s_%s_%s' % (test_name, builder_name, build_number), | |
| 306 suffix='.html'), | |
| 307 filepath=temp_file.name, | 324 filepath=temp_file.name, |
| 308 bucket='%s/html' % bucket, | 325 bucket='%s/html' % bucket, |
| 309 content_type='text/html', | 326 content_type='text/html', |
| 310 authenticated_link=True) | 327 authenticated_link=True) |
| 311 | 328 |
| 312 | 329 |
| 313 def main(): | 330 def main(): |
| 314 parser = argparse.ArgumentParser() | 331 parser = argparse.ArgumentParser() |
| 315 parser.add_argument('--json-file', help='Path of json file.') | 332 parser.add_argument('--json-file', help='Path of json file.') |
| 316 parser.add_argument('--cs-base-url', help='Base url for code search.', | 333 parser.add_argument('--cs-base-url', help='Base url for code search.', |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 373 if args.positional: | 390 if args.positional: |
| 374 if not len(args.positional) == 1: | 391 if not len(args.positional) == 1: |
| 375 raise parser.error('More than 1 json file specified.') | 392 raise parser.error('More than 1 json file specified.') |
| 376 json_file = args.positional[0] | 393 json_file = args.positional[0] |
| 377 elif args.json_file: | 394 elif args.json_file: |
| 378 json_file = args.json_file | 395 json_file = args.json_file |
| 379 | 396 |
| 380 if not os.path.exists(json_file): | 397 if not os.path.exists(json_file): |
| 381 raise IOError('--json-file %s not found.' % json_file) | 398 raise IOError('--json-file %s not found.' % json_file) |
| 382 | 399 |
| 383 result_html_string = result_details(json_file, args.cs_base_url) | 400 # Link to result details presentation page is a part of the page. |
| 384 result_details_link = upload_to_google_bucket( | 401 result_html_string, dest, result_details_link = result_details( |
| 402 json_file, args.cs_base_url, args.bucket, |
| 403 args.test_name, builder_name, build_number) |
| 404 |
| 405 result_details_link_2 = upload_to_google_bucket( |
| 385 result_html_string.encode('UTF-8'), | 406 result_html_string.encode('UTF-8'), |
| 386 args.test_name, builder_name, | 407 args.bucket, dest) |
| 387 build_number, args.bucket) | 408 |
| 409 assert result_details_link == result_details_link_2, ( |
| 410 'Result details link do not match. The link returned by get_url_link' |
| 411 ' should be the same as that returned by upload.') |
| 388 | 412 |
| 389 if args.output_json: | 413 if args.output_json: |
| 390 with open(json_file) as original_json_file: | 414 with open(json_file) as original_json_file: |
| 391 json_object = json.load(original_json_file) | 415 json_object = json.load(original_json_file) |
| 392 json_object['links'] = {'result_details': result_details_link} | 416 json_object['links'] = {'result_details': result_details_link} |
| 393 with open(args.output_json, 'w') as f: | 417 with open(args.output_json, 'w') as f: |
| 394 json.dump(json_object, f) | 418 json.dump(json_object, f) |
| 395 else: | 419 else: |
| 396 print result_details_link | 420 print result_details_link |
| 397 | 421 |
| 398 if __name__ == '__main__': | 422 if __name__ == '__main__': |
| 399 sys.exit(main()) | 423 sys.exit(main()) |
| OLD | NEW |