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

Side by Side Diff: build/android/pylib/results/presentation/test_results_presentation.py

Issue 2933993002: Add local results details pages.
Patch Set: Add --local-output arg which enables local results detail pages. Created 3 years, 6 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
OLDNEW
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 256 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 def feedback_url(result_details_link): 267 def feedback_url(result_details_link):
268 url_args = urllib.urlencode([ 268 url_args = urllib.urlencode([
269 ('labels', 'Pri-2,Type-Bug,Restrict-View-Google'), 269 ('labels', 'Pri-2,Type-Bug,Restrict-View-Google'),
270 ('summary', 'Result Details Feedback:'), 270 ('summary', 'Result Details Feedback:'),
271 ('components', 'Test>Android'), 271 ('components', 'Test>Android'),
272 ('comment', 'Please check out: %s' % result_details_link)]) 272 ('comment', 'Please check out: %s' % result_details_link)])
273 return 'https://bugs.chromium.org/p/chromium/issues/entry?%s' % url_args 273 return 'https://bugs.chromium.org/p/chromium/issues/entry?%s' % url_args
274 274
275 275
276 def results_to_html(results_dict, cs_base_url, bucket, test_name, 276 def results_to_html(results_dict, cs_base_url, bucket, test_name,
277 builder_name, build_number): 277 builder_name, build_number, local_output):
278 """Convert list of test results into html format.""" 278 """Convert list of test results into html format.
279 279
280 test_rows_header, test_rows = create_test_table(results_dict, cs_base_url, 280 Args:
281 test_name) 281 local_output: Whether this results file is uploaded to Google Storage or
282 just a local file.
283 """
284 test_rows_header, test_rows = create_test_table(
285 results_dict, cs_base_url, test_name)
282 suite_rows_header, suite_rows, suite_row_footer = create_suite_table( 286 suite_rows_header, suite_rows, suite_row_footer = create_suite_table(
283 results_dict) 287 results_dict)
284 288
285 suite_table_values = { 289 suite_table_values = {
286 'table_id': 'suite-table', 290 'table_id': 'suite-table',
287 'table_headers': suite_rows_header, 291 'table_headers': suite_rows_header,
288 'table_row_blocks': suite_rows, 292 'table_row_blocks': suite_rows,
289 'table_footer': suite_row_footer, 293 'table_footer': suite_row_footer,
290 } 294 }
291 295
292 test_table_values = { 296 test_table_values = {
293 'table_id': 'test-table', 297 'table_id': 'test-table',
294 'table_headers': test_rows_header, 298 'table_headers': test_rows_header,
295 'table_row_blocks': test_rows, 299 'table_row_blocks': test_rows,
296 } 300 }
297 301
298 main_template = JINJA_ENVIRONMENT.get_template( 302 main_template = JINJA_ENVIRONMENT.get_template(
299 os.path.join('template', 'main.html')) 303 os.path.join('template', 'main.html'))
300 dest = google_storage_helper.unique_name(
301 '%s_%s_%s' % (test_name, builder_name, build_number))
302 304
303 result_details_link = google_storage_helper.get_url_link( 305 if local_output:
304 dest, '%s/html' % bucket) 306 html_render = main_template.render( # pylint: disable=no-member
305 307 {
306 return (main_template.render( # pylint: disable=no-member 308 'tb_values': [suite_table_values, test_table_values]
307 {'tb_values': [suite_table_values, test_table_values], 309 })
308 'feedback_url': feedback_url(result_details_link) 310 return html_render
309 }), dest, result_details_link) 311 else:
312 dest = google_storage_helper.unique_name(
313 '%s_%s_%s' % (test_name, builder_name, build_number))
314 result_details_link = google_storage_helper.get_url_link(
315 dest, '%s/html' % bucket)
316 html_render = main_template.render( # pylint: disable=no-member
317 {
318 'tb_values': [suite_table_values, test_table_values],
319 'feedback_url': feedback_url(result_details_link),
320 })
321 return (html_render, dest, result_details_link)
310 322
311 323
312 def result_details(json_path, cs_base_url, bucket, test_name, 324 def result_details(json_path, test_name, cs_base_url, bucket=None,
313 builder_name, build_number): 325 builder_name=None, build_number=None, local_output=False):
314 """Get result details from json path and then convert results to html.""" 326 """Get result details from json path and then convert results to html.
327
328 Args:
329 local_output: Whether this results file is uploaded to Google Storage or
330 just a local file.
331 """
315 332
316 with open(json_path) as json_file: 333 with open(json_path) as json_file:
317 json_object = json.loads(json_file.read()) 334 json_object = json.loads(json_file.read())
318 335
319 if not 'per_iteration_data' in json_object: 336 if not 'per_iteration_data' in json_object:
320 return 'Error: json file missing per_iteration_data.' 337 return 'Error: json file missing per_iteration_data.'
321 338
322 results_dict = collections.defaultdict(list) 339 results_dict = collections.defaultdict(list)
323 for testsuite_run in json_object['per_iteration_data']: 340 for testsuite_run in json_object['per_iteration_data']:
324 for test, test_runs in testsuite_run.iteritems(): 341 for test, test_runs in testsuite_run.iteritems():
325 results_dict[test].extend(test_runs) 342 results_dict[test].extend(test_runs)
326 return results_to_html(results_dict, cs_base_url, bucket, 343 return results_to_html(results_dict, cs_base_url, bucket, test_name,
327 test_name, builder_name, build_number) 344 builder_name, build_number, local_output)
328 345
329 346
330 def upload_to_google_bucket(html, bucket, dest): 347 def upload_to_google_bucket(html, bucket, dest):
331 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file: 348 with tempfile.NamedTemporaryFile(suffix='.html') as temp_file:
332 temp_file.write(html) 349 temp_file.write(html)
333 temp_file.flush() 350 temp_file.flush()
334 return google_storage_helper.upload( 351 return google_storage_helper.upload(
335 name=dest, 352 name=dest,
336 filepath=temp_file.name, 353 filepath=temp_file.name,
337 bucket='%s/html' % bucket, 354 bucket='%s/html' % bucket,
338 content_type='text/html', 355 content_type='text/html',
339 authenticated_link=True) 356 authenticated_link=True)
340 357
341 358
342 def main(): 359 def main():
343 parser = argparse.ArgumentParser() 360 parser = argparse.ArgumentParser()
344 parser.add_argument('--json-file', help='Path of json file.') 361 parser.add_argument('--json-file', help='Path of json file.')
345 parser.add_argument('--cs-base-url', help='Base url for code search.', 362 parser.add_argument('--cs-base-url', help='Base url for code search.',
346 default='http://cs.chromium.org') 363 default='http://cs.chromium.org')
347 parser.add_argument('--bucket', help='Google storage bucket.', required=True) 364 parser.add_argument('--bucket', help='Google storage bucket.', required=True)
348 parser.add_argument('--builder-name', help='Builder name.') 365 parser.add_argument('--builder-name', help='Builder name.')
349 parser.add_argument('--build-number', help='Build number.') 366 parser.add_argument('--build-number', help='Build number.')
350 parser.add_argument('--test-name', help='The name of the test.', 367 parser.add_argument('--test-name', help='The name of the test.',
351 required=True) 368 required=True)
352 parser.add_argument( 369 parser.add_argument(
353 '-o', '--output-json', 370 '-o', '--output-json',
354 help='(Swarming Merge Script API)' 371 help='(Swarming Merge Script API) '
355 ' Output JSON file to create.') 372 'Output JSON file to create.')
356 parser.add_argument( 373 parser.add_argument(
357 '--build-properties', 374 '--build-properties',
358 help='(Swarming Merge Script API) ' 375 help='(Swarming Merge Script API) '
359 'Build property JSON file provided by recipes.') 376 'Build property JSON file provided by recipes.')
360 parser.add_argument( 377 parser.add_argument(
361 '--summary-json', 378 '--summary-json',
362 help='(Swarming Merge Script API)' 379 help='(Swarming Merge Script API) '
363 ' Summary of shard state running on swarming.' 380 'Summary of shard state running on swarming. '
364 ' (Output of the swarming.py collect' 381 '(Output of the swarming.py collect '
365 ' --task-summary-json=XXX command.)') 382 '--task-summary-json=XXX command.)')
366 parser.add_argument( 383 parser.add_argument(
367 'positional', nargs='*', 384 'positional', nargs='*',
368 help='output.json from shards.') 385 help='output.json from shards.')
369 386
370 args = parser.parse_args() 387 args = parser.parse_args()
371 388
372 if ((args.build_properties is None) == 389 if ((args.build_properties is None) ==
373 (args.build_number is None or args.builder_name is None)): 390 (args.build_number is None or args.builder_name is None)):
374 raise parser.error('Exactly one of build_perperties or ' 391 raise parser.error('Exactly one of build_perperties or '
375 '(build_number or builder_name) should be given.') 392 '(build_number or builder_name) should be given.')
(...skipping 28 matching lines...) Expand all
404 raise parser.error('More than 1 json file specified.') 421 raise parser.error('More than 1 json file specified.')
405 json_file = args.positional[0] 422 json_file = args.positional[0]
406 elif args.json_file: 423 elif args.json_file:
407 json_file = args.json_file 424 json_file = args.json_file
408 425
409 if not os.path.exists(json_file): 426 if not os.path.exists(json_file):
410 raise IOError('--json-file %s not found.' % json_file) 427 raise IOError('--json-file %s not found.' % json_file)
411 428
412 # Link to result details presentation page is a part of the page. 429 # Link to result details presentation page is a part of the page.
413 result_html_string, dest, result_details_link = result_details( 430 result_html_string, dest, result_details_link = result_details(
414 json_file, args.cs_base_url, args.bucket, 431 json_file, args.test_name, args.cs_base_url, args.bucket,
415 args.test_name, builder_name, build_number) 432 builder_name, build_number)
416 433
417 result_details_link_2 = upload_to_google_bucket( 434 result_details_link_2 = upload_to_google_bucket(
418 result_html_string.encode('UTF-8'), 435 result_html_string.encode('UTF-8'),
419 args.bucket, dest) 436 args.bucket, dest)
420
421 assert result_details_link == result_details_link_2, ( 437 assert result_details_link == result_details_link_2, (
422 'Result details link do not match. The link returned by get_url_link' 438 'Result details link do not match. The link returned by get_url_link'
423 ' should be the same as that returned by upload.') 439 ' should be the same as that returned by upload.')
424 440
425 if args.output_json: 441 if args.output_json:
426 with open(json_file) as original_json_file: 442 with open(json_file) as original_json_file:
427 json_object = json.load(original_json_file) 443 json_object = json.load(original_json_file)
428 json_object['links'] = {'result_details': result_details_link} 444 json_object['links'] = {'result_details': result_details_link}
429 with open(args.output_json, 'w') as f: 445 with open(args.output_json, 'w') as f:
430 json.dump(json_object, f) 446 json.dump(json_object, f)
431 else: 447 else:
432 print result_details_link 448 print result_details_link
433 449
434 if __name__ == '__main__': 450 if __name__ == '__main__':
435 sys.exit(main()) 451 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698