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

Side by Side Diff: build/android/pylib/local/device/local_device_instrumentation_test_run.py

Issue 2933993002: Add local results details pages.
Patch Set: Fix some of Johns comments. Created 3 years, 4 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 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import contextlib 5 import contextlib
6 import hashlib 6 import hashlib
7 import json 7 import json
8 import logging 8 import logging
9 import os 9 import os
10 import posixpath 10 import posixpath
11 import re 11 import re
12 import sys 12 import sys
13 import tempfile
14 import time 13 import time
15 14
16 from devil.android import crash_handler 15 from devil.android import crash_handler
17 from devil.android import device_errors 16 from devil.android import device_errors
18 from devil.android import device_temp_file 17 from devil.android import device_temp_file
19 from devil.android import flag_changer 18 from devil.android import flag_changer
20 from devil.android.sdk import shared_prefs 19 from devil.android.sdk import shared_prefs
20 from devil.android import logcat_monitor
21 from devil.android.tools import system_app 21 from devil.android.tools import system_app
22 from devil.utils import reraiser_thread 22 from devil.utils import reraiser_thread
23 from incremental_install import installer 23 from incremental_install import installer
24 from pylib import valgrind_tools 24 from pylib import valgrind_tools
25 from pylib.android import logdog_logcat_monitor
26 from pylib.base import base_test_result 25 from pylib.base import base_test_result
26 from pylib.base import output_manager
27 from pylib.constants import host_paths 27 from pylib.constants import host_paths
28 from pylib.instrumentation import instrumentation_test_instance 28 from pylib.instrumentation import instrumentation_test_instance
29 from pylib.local.device import local_device_environment 29 from pylib.local.device import local_device_environment
30 from pylib.local.device import local_device_test_run 30 from pylib.local.device import local_device_test_run
31 from pylib.utils import google_storage_helper
32 from pylib.utils import instrumentation_tracing 31 from pylib.utils import instrumentation_tracing
33 from pylib.utils import logdog_helper
34 from pylib.utils import shared_preference_utils 32 from pylib.utils import shared_preference_utils
33
35 from py_trace_event import trace_event 34 from py_trace_event import trace_event
36 from py_trace_event import trace_time 35 from py_trace_event import trace_time
37 from py_utils import contextlib_ext 36 from py_utils import contextlib_ext
38 from py_utils import tempfile_ext 37 from py_utils import tempfile_ext
39 import tombstones 38 import tombstones
40 39
41 with host_paths.SysPath( 40 with host_paths.SysPath(
42 os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party'), 0): 41 os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party'), 0):
43 import jinja2 # pylint: disable=import-error 42 import jinja2 # pylint: disable=import-error
44 import markupsafe # pylint: disable=import-error,unused-import 43 import markupsafe # pylint: disable=import-error,unused-import
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 return False 117 return False
119 118
120 119
121 _CURRENT_FOCUS_CRASH_RE = re.compile( 120 _CURRENT_FOCUS_CRASH_RE = re.compile(
122 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') 121 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
123 122
124 123
125 class LocalDeviceInstrumentationTestRun( 124 class LocalDeviceInstrumentationTestRun(
126 local_device_test_run.LocalDeviceTestRun): 125 local_device_test_run.LocalDeviceTestRun):
127 def __init__(self, env, test_instance): 126 def __init__(self, env, test_instance):
128 super(LocalDeviceInstrumentationTestRun, self).__init__(env, test_instance) 127 super(LocalDeviceInstrumentationTestRun, self).__init__(
128 env, test_instance)
129 self._flag_changers = {} 129 self._flag_changers = {}
130 self._ui_capture_dir = dict() 130 self._ui_capture_dir = dict()
131 self._replace_package_contextmanager = None 131 self._replace_package_contextmanager = None
132 132
133 #override 133 #override
134 def TestPackage(self): 134 def TestPackage(self):
135 return self._test_instance.suite 135 return self._test_instance.suite
136 136
137 #override 137 #override
138 def SetUp(self): 138 def SetUp(self):
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method'] 355 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method']
356 if isinstance(test, list) else test['method']) 356 if isinstance(test, list) else test['method'])
357 extras['coverage'] = 'true' 357 extras['coverage'] = 'true'
358 coverage_directory = os.path.join( 358 coverage_directory = os.path.join(
359 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage') 359 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage')
360 coverage_device_file = os.path.join( 360 coverage_device_file = os.path.join(
361 coverage_directory, coverage_basename) 361 coverage_directory, coverage_basename)
362 extras['coverageFile'] = coverage_device_file 362 extras['coverageFile'] = coverage_device_file
363 # Save screenshot if screenshot dir is specified (save locally) or if 363 # Save screenshot if screenshot dir is specified (save locally) or if
364 # a GS bucket is passed (save in cloud). 364 # a GS bucket is passed (save in cloud).
365 screenshot_device_file = None 365 screenshot_device_file = device_temp_file.DeviceTempFile(
366 if (self._test_instance.screenshot_dir or 366 device.adb, suffix='.png', dir=device.GetExternalStoragePath())
367 self._test_instance.gs_results_bucket): 367 extras[EXTRA_SCREENSHOT_FILE] = screenshot_device_file.name
368 screenshot_device_file = device_temp_file.DeviceTempFile(
369 device.adb, suffix='.png', dir=device.GetExternalStoragePath())
370 extras[EXTRA_SCREENSHOT_FILE] = screenshot_device_file.name
371 368
372 extras[EXTRA_UI_CAPTURE_DIR] = self._ui_capture_dir[device] 369 extras[EXTRA_UI_CAPTURE_DIR] = self._ui_capture_dir[device]
373 370
374 if self._env.trace_output: 371 if self._env.trace_output:
375 trace_device_file = device_temp_file.DeviceTempFile( 372 trace_device_file = device_temp_file.DeviceTempFile(
376 device.adb, suffix='.json', dir=device.GetExternalStoragePath()) 373 device.adb, suffix='.json', dir=device.GetExternalStoragePath())
377 extras[EXTRA_TRACE_FILE] = trace_device_file.name 374 extras[EXTRA_TRACE_FILE] = trace_device_file.name
378 375
379 if isinstance(test, list): 376 if isinstance(test, list):
380 if not self._test_instance.driver_apk: 377 if not self._test_instance.driver_apk:
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
437 self._CreateFlagChangerIfNeeded(device) 434 self._CreateFlagChangerIfNeeded(device)
438 self._flag_changers[str(device)].PushFlags(add=flags_to_add) 435 self._flag_changers[str(device)].PushFlags(add=flags_to_add)
439 436
440 time_ms = lambda: int(time.time() * 1e3) 437 time_ms = lambda: int(time.time() * 1e3)
441 start_ms = time_ms() 438 start_ms = time_ms()
442 439
443 stream_name = 'logcat_%s_%s_%s' % ( 440 stream_name = 'logcat_%s_%s_%s' % (
444 test_name.replace('#', '.'), 441 test_name.replace('#', '.'),
445 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), 442 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()),
446 device.serial) 443 device.serial)
447 logmon = logdog_logcat_monitor.LogdogLogcatMonitor(
448 device.adb, stream_name, filter_specs=LOGCAT_FILTERS)
449 444
450 with contextlib_ext.Optional( 445 with self._env.output_manager.ArchivedTempfile(
451 logmon, self._test_instance.should_save_logcat): 446 stream_name, 'logcat') as logcat_file:
452 with _LogTestEndpoints(device, test_name): 447 with logcat_monitor.LogcatMonitor(
453 with contextlib_ext.Optional( 448 device.adb, filter_specs=LOGCAT_FILTERS,
454 trace_event.trace(test_name), 449 output_file=logcat_file.name) as logmon:
455 self._env.trace_output): 450 with _LogTestEndpoints(device, test_name):
456 output = device.StartInstrumentation( 451 with contextlib_ext.Optional(
457 target, raw=True, extras=extras, timeout=timeout, retries=0) 452 trace_event.trace(test_name),
453 self._env.trace_output):
454 output = device.StartInstrumentation(
455 target, raw=True, extras=extras, timeout=timeout, retries=0)
456 logmon.Close()
458 457
459 logcat_url = logmon.GetLogcatURL()
460 duration_ms = time_ms() - start_ms 458 duration_ms = time_ms() - start_ms
461 459
462 if self._env.trace_output: 460 if self._env.trace_output:
463 self._SaveTraceData(trace_device_file, device, test['class']) 461 self._SaveTraceData(trace_device_file, device, test['class'])
464 462
465 # TODO(jbudorick): Make instrumentation tests output a JSON so this 463 # TODO(jbudorick): Make instrumentation tests output a JSON so this
466 # doesn't have to parse the output. 464 # doesn't have to parse the output.
467 result_code, result_bundle, statuses = ( 465 result_code, result_bundle, statuses = (
468 self._test_instance.ParseAmInstrumentRawOutput(output)) 466 self._test_instance.ParseAmInstrumentRawOutput(output))
469 results = self._test_instance.GenerateTestResults( 467 results = self._test_instance.GenerateTestResults(
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 handle_coverage_data, handle_render_test_data] 505 handle_coverage_data, handle_render_test_data]
508 if self._env.concurrent_adb: 506 if self._env.concurrent_adb:
509 post_test_step_thread_group = reraiser_thread.ReraiserThreadGroup( 507 post_test_step_thread_group = reraiser_thread.ReraiserThreadGroup(
510 reraiser_thread.ReraiserThread(f) for f in post_test_steps) 508 reraiser_thread.ReraiserThread(f) for f in post_test_steps)
511 post_test_step_thread_group.StartAll(will_block=True) 509 post_test_step_thread_group.StartAll(will_block=True)
512 else: 510 else:
513 for step in post_test_steps: 511 for step in post_test_steps:
514 step() 512 step()
515 513
516 for result in results: 514 for result in results:
517 if logcat_url: 515 if logcat_file:
518 result.SetLink('logcat', logcat_url) 516 result.SetLink('logcat', logcat_file.Link())
519 517
520 # Update the result name if the test used flags. 518 # Update the result name if the test used flags.
521 if flags_to_add: 519 if flags_to_add:
522 for r in results: 520 for r in results:
523 if r.GetName() == test_name: 521 if r.GetName() == test_name:
524 r.SetName(test_display_name) 522 r.SetName(test_display_name)
525 523
526 # Add UNKNOWN results for any missing tests. 524 # Add UNKNOWN results for any missing tests.
527 iterable_test = test if isinstance(test, list) else [test] 525 iterable_test = test if isinstance(test, list) else [test]
528 test_names = set(self._GetUniqueTestName(t) for t in iterable_test) 526 test_names = set(self._GetUniqueTestName(t) for t in iterable_test)
529 results_names = set(r.GetName() for r in results) 527 results_names = set(r.GetName() for r in results)
530 results.extend( 528 results.extend(
531 base_test_result.BaseTestResult(u, base_test_result.ResultType.UNKNOWN) 529 base_test_result.BaseTestResult(u, base_test_result.ResultType.UNKNOWN)
532 for u in test_names.difference(results_names)) 530 for u in test_names.difference(results_names))
533 531
534 # Update the result type if we detect a crash. 532 # Update the result type if we detect a crash.
535 if DidPackageCrashOnDevice(self._test_instance.test_package, device): 533 if DidPackageCrashOnDevice(self._test_instance.test_package, device):
536 for r in results: 534 for r in results:
537 if r.GetType() == base_test_result.ResultType.UNKNOWN: 535 if r.GetType() == base_test_result.ResultType.UNKNOWN:
538 r.SetType(base_test_result.ResultType.CRASH) 536 r.SetType(base_test_result.ResultType.CRASH)
539 537
540 # Handle failures by: 538 # Handle failures by:
541 # - optionally taking a screenshot 539 # - optionally taking a screenshot
542 # - logging the raw output at INFO level 540 # - logging the raw output at INFO level
543 # - clearing the application state while persisting permissions 541 # - clearing the application state while persisting permissions
544 if any(r.GetType() not in (base_test_result.ResultType.PASS, 542 if any(r.GetType() not in (base_test_result.ResultType.PASS,
545 base_test_result.ResultType.SKIP) 543 base_test_result.ResultType.SKIP)
546 for r in results): 544 for r in results):
547 with contextlib_ext.Optional( 545 self._SaveScreenshot(device, screenshot_device_file, test_display_name,
548 tempfile_ext.NamedTemporaryDirectory(), 546 results)
549 self._test_instance.screenshot_dir is None and
550 self._test_instance.gs_results_bucket) as screenshot_host_dir:
551 screenshot_host_dir = (
552 self._test_instance.screenshot_dir or screenshot_host_dir)
553 self._SaveScreenshot(device, screenshot_host_dir,
554 screenshot_device_file, test_display_name,
555 results)
556 547
557 logging.info('detected failure in %s. raw output:', test_display_name) 548 logging.info('detected failure in %s. raw output:', test_display_name)
558 for l in output: 549 for l in output:
559 logging.info(' %s', l) 550 logging.info(' %s', l)
560 if (not self._env.skip_clear_data 551 if (not self._env.skip_clear_data
561 and self._test_instance.package_info): 552 and self._test_instance.package_info):
562 permissions = ( 553 permissions = (
563 self._test_instance.apk_under_test.GetPermissions() 554 self._test_instance.apk_under_test.GetPermissions()
564 if self._test_instance.apk_under_test 555 if self._test_instance.apk_under_test
565 else None) 556 else None)
566 device.ClearApplicationState(self._test_instance.package_info.package, 557 device.ClearApplicationState(self._test_instance.package_info.package,
567 permissions=permissions) 558 permissions=permissions)
568 else: 559 else:
569 logging.debug('raw output from %s:', test_display_name) 560 logging.debug('raw output from %s:', test_display_name)
570 for l in output: 561 for l in output:
571 logging.debug(' %s', l) 562 logging.debug(' %s', l)
572 if self._test_instance.store_tombstones: 563 if self._test_instance.store_tombstones:
573 tombstones_url = None 564 tombstones_url = None
574 for result in results: 565 for result in results:
575 if result.GetType() == base_test_result.ResultType.CRASH: 566 if result.GetType() == base_test_result.ResultType.CRASH:
576 if not tombstones_url: 567 if not tombstones_url:
577 resolved_tombstones = tombstones.ResolveTombstones( 568 resolved_tombstones = tombstones.ResolveTombstones(
578 device, 569 device,
579 resolve_all_tombstones=True, 570 resolve_all_tombstones=True,
580 include_stack_symbols=False, 571 include_stack_symbols=False,
581 wipe_tombstones=True, 572 wipe_tombstones=True,
582 tombstone_symbolizer=self._test_instance.symbolizer) 573 tombstone_symbolizer=self._test_instance.symbolizer)
583 stream_name = 'tombstones_%s_%s' % ( 574 tombstone_filename = 'tombstones_%s_%s' % (
584 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), 575 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()),
585 device.serial) 576 device.serial)
586 tombstones_url = logdog_helper.text( 577 with self._env.output_manager.ArchivedTempfile(
587 stream_name, '\n'.join(resolved_tombstones)) 578 tombstone_filename, 'tombstones') as tombstone_file:
588 result.SetLink('tombstones', tombstones_url) 579 tombstone_file.write('\n'.join(resolved_tombstones))
589 580 result.SetLink('tombstones', tombstone_file.Link())
590 if self._env.concurrent_adb: 581 if self._env.concurrent_adb:
591 post_test_step_thread_group.JoinAll() 582 post_test_step_thread_group.JoinAll()
592 return results, None 583 return results, None
593 584
594 def _GetTestsFromRunner(self): 585 def _GetTestsFromRunner(self):
595 test_apk_path = self._test_instance.test_apk.path 586 test_apk_path = self._test_instance.test_apk.path
596 pickle_path = '%s-runner.pickle' % test_apk_path 587 pickle_path = '%s-runner.pickle' % test_apk_path
597 try: 588 try:
598 return instrumentation_test_instance.GetTestsFromPickle( 589 return instrumentation_test_instance.GetTestsFromPickle(
599 pickle_path, test_apk_path) 590 pickle_path, test_apk_path)
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
691 682
692 with open(trace_host_file, 'r') as host_handle: 683 with open(trace_host_file, 'r') as host_handle:
693 host_contents = host_handle.readline() 684 host_contents = host_handle.readline()
694 685
695 if host_contents: 686 if host_contents:
696 java_trace_json = ',%s' % java_trace_json.lstrip(' [') 687 java_trace_json = ',%s' % java_trace_json.lstrip(' [')
697 688
698 with open(trace_host_file, 'a') as host_handle: 689 with open(trace_host_file, 'a') as host_handle:
699 host_handle.write(java_trace_json) 690 host_handle.write(java_trace_json)
700 691
701 def _SaveScreenshot(self, device, screenshot_host_dir, screenshot_device_file, 692 def _SaveScreenshot(self, device, screenshot_device_file, test_name, results):
702 test_name, results): 693 screenshot_filename = '%s-%s.png' % (
703 if screenshot_host_dir: 694 test_name, time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()))
704 screenshot_host_file = os.path.join(
705 screenshot_host_dir,
706 '%s-%s.png' % (
707 test_name,
708 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime())))
709 if device.FileExists(screenshot_device_file.name): 695 if device.FileExists(screenshot_device_file.name):
710 try: 696 with self._env.output_manager.ArchivedTempfile(
711 device.PullFile(screenshot_device_file.name, screenshot_host_file) 697 screenshot_filename, 'screenshot',
712 finally: 698 output_manager.Datatype.IMAGE) as screenshot_host_file:
713 screenshot_device_file.close() 699 try:
714 700 device.PullFile(screenshot_device_file.name,
715 logging.info( 701 screenshot_host_file.name)
716 'Saved screenshot for %s to %s.', 702 finally:
717 test_name, screenshot_host_file) 703 screenshot_device_file.close()
718 if self._test_instance.gs_results_bucket: 704 for result in results:
719 link = google_storage_helper.upload( 705 result.SetLink('post_test_screenshot', screenshot_host_file.Link())
720 google_storage_helper.unique_name(
721 'screenshot', device=device),
722 screenshot_host_file,
723 bucket=('%s/screenshots' %
724 self._test_instance.gs_results_bucket))
725 for result in results:
726 result.SetLink('post_test_screenshot', link)
727 706
728 def _ProcessRenderTestResults( 707 def _ProcessRenderTestResults(
729 self, device, render_tests_device_output_dir, results): 708 self, device, render_tests_device_output_dir, results):
730 # If GS results bucket is specified, will archive render result images.
731 # If render image dir is specified, will pull the render result image from
732 # the device and leave in the directory.
733 if not (bool(self._test_instance.gs_results_bucket) or
734 bool(self._test_instance.render_results_dir)):
735 return
736 709
737 failure_images_device_dir = posixpath.join( 710 failure_images_device_dir = posixpath.join(
738 render_tests_device_output_dir, 'failures') 711 render_tests_device_output_dir, 'failures')
739 if not device.FileExists(failure_images_device_dir): 712 if not device.FileExists(failure_images_device_dir):
740 return 713 return
741 714
742 diff_images_device_dir = posixpath.join( 715 diff_images_device_dir = posixpath.join(
743 render_tests_device_output_dir, 'diffs') 716 render_tests_device_output_dir, 'diffs')
744 717
745 golden_images_device_dir = posixpath.join( 718 golden_images_device_dir = posixpath.join(
746 render_tests_device_output_dir, 'goldens') 719 render_tests_device_output_dir, 'goldens')
747 720
748 with contextlib_ext.Optional( 721 for failure_filename in device.ListDirectory(failure_images_device_dir):
749 tempfile_ext.NamedTemporaryDirectory(),
750 not bool(self._test_instance.render_results_dir)) as render_temp_dir:
751 render_host_dir = (
752 self._test_instance.render_results_dir or render_temp_dir)
753 722
754 if not os.path.exists(render_host_dir): 723 with self._env.output_manager.ArchivedTempfile(
755 os.makedirs(render_host_dir) 724 'fail_%s' % failure_filename, 'render_tests',
725 output_manager.Datatype.IMAGE) as failure_image_host_file:
726 device.PullFile(
727 posixpath.join(failure_images_device_dir, failure_filename),
728 failure_image_host_file)
729 failure_link = failure_image_host_file.Link()
756 730
757 # Pull all render test results from device. 731 golden_image_device_file = posixpath.join(
758 device.PullFile(failure_images_device_dir, render_host_dir) 732 golden_images_device_dir, failure_filename)
759 733 if device.PathExists(golden_image_device_file):
760 if device.FileExists(diff_images_device_dir): 734 with self._env.output_manager.ArchivedTempfile(
761 device.PullFile(diff_images_device_dir, render_host_dir) 735 'golden_%s' % failure_filename, 'render_tests',
762 else: 736 output_manager.Datatype.IMAGE) as golden_image_host_file:
763 logging.error('Diff images not found on device.') 737 device.PullFile(
764 738 golden_image_device_file, golden_image_host_file)
765 if device.FileExists(golden_images_device_dir): 739 golden_link = golden_image_host_file.Link()
766 device.PullFile(golden_images_device_dir, render_host_dir)
767 else:
768 logging.error('Golden images not found on device.')
769
770 # Upload results to Google Storage.
771 if self._test_instance.gs_results_bucket:
772 self._UploadRenderTestResults(render_host_dir, results)
773
774 def _UploadRenderTestResults(self, render_host_dir, results):
775 render_tests_bucket = (
776 self._test_instance.gs_results_bucket + '/render_tests')
777
778 for failure_filename in os.listdir(
779 os.path.join(render_host_dir, 'failures')):
780 m = RE_RENDER_IMAGE_NAME.match(failure_filename)
781 if not m:
782 logging.warning('Unexpected file in render test failures: %s',
783 failure_filename)
784 continue
785
786 failure_filepath = os.path.join(
787 render_host_dir, 'failures', failure_filename)
788 failure_link = google_storage_helper.upload_content_addressed(
789 failure_filepath, bucket=render_tests_bucket)
790
791 golden_filepath = os.path.join(
792 render_host_dir, 'goldens', failure_filename)
793 if os.path.exists(golden_filepath):
794 golden_link = google_storage_helper.upload_content_addressed(
795 golden_filepath, bucket=render_tests_bucket)
796 else: 740 else:
797 golden_link = '' 741 golden_link = ''
798 742
799 diff_filepath = os.path.join( 743 diff_image_device_file = posixpath.join(
800 render_host_dir, 'diffs', failure_filename) 744 diff_images_device_dir, failure_filename)
801 if os.path.exists(diff_filepath): 745 if device.PathExists(diff_image_device_file):
802 diff_link = google_storage_helper.upload_content_addressed( 746 with self._env.output_manager.ArchivedTempfile(
803 diff_filepath, bucket=render_tests_bucket) 747 'diff_%s' % failure_filename, 'render_tests',
748 output_manager.Datatype.IMAGE) as diff_image_host_file:
749 device.PullFile(
750 diff_image_device_file, diff_image_host_file)
751 diff_link = diff_image_host_file.Link()
804 else: 752 else:
805 diff_link = '' 753 diff_link = ''
806 754
807 with tempfile.NamedTemporaryFile(suffix='.html') as temp_html: 755 jinja2_env = jinja2.Environment(
808 jinja2_env = jinja2.Environment( 756 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR),
809 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR), 757 trim_blocks=True)
810 trim_blocks=True) 758 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME)
811 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME) 759 # pylint: disable=no-member
812 # pylint: disable=no-member 760 processed_template_output = template.render(
813 processed_template_output = template.render( 761 test_name=failure_filename,
814 test_name=failure_filename, 762 failure_link=failure_link,
815 failure_link=failure_link, 763 golden_link=golden_link,
816 golden_link=golden_link, 764 diff_link=diff_link)
817 diff_link=diff_link)
818 765
819 temp_html.write(processed_template_output) 766 with self._env.output_manager.ArchivedTempfile(
820 temp_html.flush() 767 '%s.html' % failure_filename, 'render_tests',
821 html_results_link = google_storage_helper.upload_content_addressed( 768 output_manager.Datatype.HTML) as html_results:
822 temp_html.name, 769 html_results.write(processed_template_output)
823 bucket=render_tests_bucket, 770 html_results.flush()
824 content_type='text/html') 771 for result in results:
825 for result in results: 772 result.SetLink(failure_filename, html_results.Link())
826 result.SetLink(failure_filename, html_results_link)
827 773
828 #override 774 #override
829 def _ShouldRetry(self, test, result): 775 def _ShouldRetry(self, test, result):
830 def not_run(res): 776 def not_run(res):
831 if isinstance(res, list): 777 if isinstance(res, list):
832 return any(not_run(r) for r in res) 778 return any(not_run(r) for r in res)
833 return res.GetType() == base_test_result.ResultType.NOTRUN 779 return res.GetType() == base_test_result.ResultType.NOTRUN
834 780
835 if 'RetryOnFailure' in test.get('annotations', {}) or not_run(result): 781 if 'RetryOnFailure' in test.get('annotations', {}) or not_run(result):
836 return True 782 return True
(...skipping 28 matching lines...) Expand all
865 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) 811 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations)
866 812
867 return timeout 813 return timeout
868 814
869 def _IsRenderTest(test): 815 def _IsRenderTest(test):
870 """Determines if a test or list of tests has a RenderTest amongst them.""" 816 """Determines if a test or list of tests has a RenderTest amongst them."""
871 if not isinstance(test, list): 817 if not isinstance(test, list):
872 test = [test] 818 test = [test]
873 return any([RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get( 819 return any([RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get(
874 FEATURE_ANNOTATION, {}).get('value', ()) for t in test]) 820 FEATURE_ANNOTATION, {}).get('value', ()) for t in test])
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698