Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 345 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method'] | 345 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method'] |
| 346 if isinstance(test, list) else test['method']) | 346 if isinstance(test, list) else test['method']) |
| 347 extras['coverage'] = 'true' | 347 extras['coverage'] = 'true' |
| 348 coverage_directory = os.path.join( | 348 coverage_directory = os.path.join( |
| 349 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage') | 349 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage') |
| 350 coverage_device_file = os.path.join( | 350 coverage_device_file = os.path.join( |
| 351 coverage_directory, coverage_basename) | 351 coverage_directory, coverage_basename) |
| 352 extras['coverageFile'] = coverage_device_file | 352 extras['coverageFile'] = coverage_device_file |
| 353 # Save screenshot if screenshot dir is specified (save locally) or if | 353 # Save screenshot if screenshot dir is specified (save locally) or if |
| 354 # a GS bucket is passed (save in cloud). | 354 # a GS bucket is passed (save in cloud). |
| 355 screenshot_device_file = None | 355 screenshot_device_file = device_temp_file.DeviceTempFile( |
| 356 if (self._test_instance.screenshot_dir or | 356 device.adb, suffix='.png', dir=device.GetExternalStoragePath()) |
| 357 self._test_instance.gs_results_bucket): | 357 extras[EXTRA_SCREENSHOT_FILE] = screenshot_device_file.name |
| 358 screenshot_device_file = device_temp_file.DeviceTempFile( | |
| 359 device.adb, suffix='.png', dir=device.GetExternalStoragePath()) | |
| 360 extras[EXTRA_SCREENSHOT_FILE] = screenshot_device_file.name | |
| 361 | 358 |
| 362 extras[EXTRA_UI_CAPTURE_DIR] = self._ui_capture_dir[device] | 359 extras[EXTRA_UI_CAPTURE_DIR] = self._ui_capture_dir[device] |
| 363 | 360 |
| 364 if self._env.trace_output: | 361 if self._env.trace_output: |
| 365 trace_device_file = device_temp_file.DeviceTempFile( | 362 trace_device_file = device_temp_file.DeviceTempFile( |
| 366 device.adb, suffix='.json', dir=device.GetExternalStoragePath()) | 363 device.adb, suffix='.json', dir=device.GetExternalStoragePath()) |
| 367 extras[EXTRA_TRACE_FILE] = trace_device_file.name | 364 extras[EXTRA_TRACE_FILE] = trace_device_file.name |
| 368 | 365 |
| 369 if isinstance(test, list): | 366 if isinstance(test, list): |
| 370 if not self._test_instance.driver_apk: | 367 if not self._test_instance.driver_apk: |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 427 self._CreateFlagChangerIfNeeded(device) | 424 self._CreateFlagChangerIfNeeded(device) |
| 428 self._flag_changers[str(device)].PushFlags(add=flags_to_add) | 425 self._flag_changers[str(device)].PushFlags(add=flags_to_add) |
| 429 | 426 |
| 430 time_ms = lambda: int(time.time() * 1e3) | 427 time_ms = lambda: int(time.time() * 1e3) |
| 431 start_ms = time_ms() | 428 start_ms = time_ms() |
| 432 | 429 |
| 433 stream_name = 'logcat_%s_%s_%s' % ( | 430 stream_name = 'logcat_%s_%s_%s' % ( |
| 434 test_name.replace('#', '.'), | 431 test_name.replace('#', '.'), |
| 435 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), | 432 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), |
| 436 device.serial) | 433 device.serial) |
| 437 logmon = logdog_logcat_monitor.LogdogLogcatMonitor( | |
| 438 device.adb, stream_name, filter_specs=LOGCAT_FILTERS) | |
| 439 | 434 |
| 440 with contextlib_ext.Optional( | 435 with self._env.output_manager.ArchivedTempfile( |
|
jbudorick
2017/08/23 16:16:20
This is fantastic.
mikecase (-- gone --)
2017/08/24 05:29:07
^.^
| |
| 441 logmon, self._test_instance.should_save_logcat): | 436 stream_name, 'logcat') as logcat_file: |
| 442 with _LogTestEndpoints(device, test_name): | 437 with logcat_monitor.LogcatMonitor( |
| 443 with contextlib_ext.Optional( | 438 device.adb, filter_specs=LOGCAT_FILTERS, |
| 444 trace_event.trace(test_name), | 439 output_file=logcat_file.name) as logmon: |
| 445 self._env.trace_output): | 440 with _LogTestEndpoints(device, test_name): |
| 446 output = device.StartInstrumentation( | 441 with contextlib_ext.Optional( |
| 447 target, raw=True, extras=extras, timeout=timeout, retries=0) | 442 trace_event.trace(test_name), |
| 443 self._env.trace_output): | |
| 444 output = device.StartInstrumentation( | |
| 445 target, raw=True, extras=extras, timeout=timeout, retries=0) | |
| 446 logmon.Close() | |
|
jbudorick
2017/08/23 16:16:20
I think this should be closed inside the logcat_fi
mikecase (-- gone --)
2017/08/24 05:29:07
Done
| |
| 448 | 447 |
| 449 logcat_url = logmon.GetLogcatURL() | |
| 450 duration_ms = time_ms() - start_ms | 448 duration_ms = time_ms() - start_ms |
| 451 | 449 |
| 452 if self._env.trace_output: | 450 if self._env.trace_output: |
| 453 self._SaveTraceData(trace_device_file, device, test['class']) | 451 self._SaveTraceData(trace_device_file, device, test['class']) |
| 454 | 452 |
| 455 # TODO(jbudorick): Make instrumentation tests output a JSON so this | 453 # TODO(jbudorick): Make instrumentation tests output a JSON so this |
| 456 # doesn't have to parse the output. | 454 # doesn't have to parse the output. |
| 457 result_code, result_bundle, statuses = ( | 455 result_code, result_bundle, statuses = ( |
| 458 self._test_instance.ParseAmInstrumentRawOutput(output)) | 456 self._test_instance.ParseAmInstrumentRawOutput(output)) |
| 459 results = self._test_instance.GenerateTestResults( | 457 results = self._test_instance.GenerateTestResults( |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 496 handle_coverage_data, handle_render_test_data] | 494 handle_coverage_data, handle_render_test_data] |
| 497 if self._env.concurrent_adb: | 495 if self._env.concurrent_adb: |
| 498 post_test_step_thread_group = reraiser_thread.ReraiserThreadGroup( | 496 post_test_step_thread_group = reraiser_thread.ReraiserThreadGroup( |
| 499 reraiser_thread.ReraiserThread(f) for f in post_test_steps) | 497 reraiser_thread.ReraiserThread(f) for f in post_test_steps) |
| 500 post_test_step_thread_group.StartAll(will_block=True) | 498 post_test_step_thread_group.StartAll(will_block=True) |
| 501 else: | 499 else: |
| 502 for step in post_test_steps: | 500 for step in post_test_steps: |
| 503 step() | 501 step() |
| 504 | 502 |
| 505 for result in results: | 503 for result in results: |
| 506 if logcat_url: | 504 if logcat_file: |
| 507 result.SetLink('logcat', logcat_url) | 505 result.SetLink('logcat', logcat_file.Link()) |
| 508 | 506 |
| 509 # Update the result name if the test used flags. | 507 # Update the result name if the test used flags. |
| 510 if flags_to_add: | 508 if flags_to_add: |
| 511 for r in results: | 509 for r in results: |
| 512 if r.GetName() == test_name: | 510 if r.GetName() == test_name: |
| 513 r.SetName(test_display_name) | 511 r.SetName(test_display_name) |
| 514 | 512 |
| 515 # Add UNKNOWN results for any missing tests. | 513 # Add UNKNOWN results for any missing tests. |
| 516 iterable_test = test if isinstance(test, list) else [test] | 514 iterable_test = test if isinstance(test, list) else [test] |
| 517 test_names = set(self._GetUniqueTestName(t) for t in iterable_test) | 515 test_names = set(self._GetUniqueTestName(t) for t in iterable_test) |
| 518 results_names = set(r.GetName() for r in results) | 516 results_names = set(r.GetName() for r in results) |
| 519 results.extend( | 517 results.extend( |
| 520 base_test_result.BaseTestResult(u, base_test_result.ResultType.UNKNOWN) | 518 base_test_result.BaseTestResult(u, base_test_result.ResultType.UNKNOWN) |
| 521 for u in test_names.difference(results_names)) | 519 for u in test_names.difference(results_names)) |
| 522 | 520 |
| 523 # Update the result type if we detect a crash. | 521 # Update the result type if we detect a crash. |
| 524 if DidPackageCrashOnDevice(self._test_instance.test_package, device): | 522 if DidPackageCrashOnDevice(self._test_instance.test_package, device): |
| 525 for r in results: | 523 for r in results: |
| 526 if r.GetType() == base_test_result.ResultType.UNKNOWN: | 524 if r.GetType() == base_test_result.ResultType.UNKNOWN: |
| 527 r.SetType(base_test_result.ResultType.CRASH) | 525 r.SetType(base_test_result.ResultType.CRASH) |
| 528 | 526 |
| 529 # Handle failures by: | 527 # Handle failures by: |
| 530 # - optionally taking a screenshot | 528 # - optionally taking a screenshot |
| 531 # - logging the raw output at INFO level | 529 # - logging the raw output at INFO level |
| 532 # - clearing the application state while persisting permissions | 530 # - clearing the application state while persisting permissions |
| 533 if any(r.GetType() not in (base_test_result.ResultType.PASS, | 531 if any(r.GetType() not in (base_test_result.ResultType.PASS, |
| 534 base_test_result.ResultType.SKIP) | 532 base_test_result.ResultType.SKIP) |
| 535 for r in results): | 533 for r in results): |
| 536 with contextlib_ext.Optional( | 534 self._SaveScreenshot(device, screenshot_device_file, test_display_name, |
| 537 tempfile_ext.NamedTemporaryDirectory(), | 535 results) |
| 538 self._test_instance.screenshot_dir is None and | |
| 539 self._test_instance.gs_results_bucket) as screenshot_host_dir: | |
| 540 screenshot_host_dir = ( | |
| 541 self._test_instance.screenshot_dir or screenshot_host_dir) | |
| 542 self._SaveScreenshot(device, screenshot_host_dir, | |
| 543 screenshot_device_file, test_display_name, | |
| 544 results) | |
| 545 | 536 |
| 546 logging.info('detected failure in %s. raw output:', test_display_name) | 537 logging.info('detected failure in %s. raw output:', test_display_name) |
| 547 for l in output: | 538 for l in output: |
| 548 logging.info(' %s', l) | 539 logging.info(' %s', l) |
| 549 if (not self._env.skip_clear_data | 540 if (not self._env.skip_clear_data |
| 550 and self._test_instance.package_info): | 541 and self._test_instance.package_info): |
| 551 permissions = ( | 542 permissions = ( |
| 552 self._test_instance.apk_under_test.GetPermissions() | 543 self._test_instance.apk_under_test.GetPermissions() |
| 553 if self._test_instance.apk_under_test | 544 if self._test_instance.apk_under_test |
| 554 else None) | 545 else None) |
| 555 device.ClearApplicationState(self._test_instance.package_info.package, | 546 device.ClearApplicationState(self._test_instance.package_info.package, |
| 556 permissions=permissions) | 547 permissions=permissions) |
| 557 else: | 548 else: |
| 558 logging.debug('raw output from %s:', test_display_name) | 549 logging.debug('raw output from %s:', test_display_name) |
| 559 for l in output: | 550 for l in output: |
| 560 logging.debug(' %s', l) | 551 logging.debug(' %s', l) |
| 561 if self._test_instance.store_tombstones: | 552 if self._test_instance.store_tombstones: |
| 562 tombstones_url = None | 553 tombstones_url = None |
| 563 for result in results: | 554 for result in results: |
| 564 if result.GetType() == base_test_result.ResultType.CRASH: | 555 if result.GetType() == base_test_result.ResultType.CRASH: |
| 565 if not tombstones_url: | 556 if not tombstones_url: |
| 566 resolved_tombstones = tombstones.ResolveTombstones( | 557 resolved_tombstones = tombstones.ResolveTombstones( |
| 567 device, | 558 device, |
| 568 resolve_all_tombstones=True, | 559 resolve_all_tombstones=True, |
| 569 include_stack_symbols=False, | 560 include_stack_symbols=False, |
| 570 wipe_tombstones=True, | 561 wipe_tombstones=True, |
| 571 tombstone_symbolizer=self._test_instance.symbolizer) | 562 tombstone_symbolizer=self._test_instance.symbolizer) |
| 572 stream_name = 'tombstones_%s_%s' % ( | 563 tombstone_filename = 'tombstones_%s_%s' % ( |
| 573 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), | 564 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), |
| 574 device.serial) | 565 device.serial) |
| 575 tombstones_url = logdog_helper.text( | 566 with self._env.output_manager.ArchivedTempfile( |
| 576 stream_name, '\n'.join(resolved_tombstones)) | 567 tombstone_filename, 'tombstones') as tombstone_file: |
| 577 result.SetLink('tombstones', tombstones_url) | 568 tombstone_file.write('\n'.join(resolved_tombstones)) |
| 578 | 569 result.SetLink('tombstones', tombstone_file.Link()) |
| 579 if self._env.concurrent_adb: | 570 if self._env.concurrent_adb: |
| 580 post_test_step_thread_group.JoinAll() | 571 post_test_step_thread_group.JoinAll() |
| 581 return results, None | 572 return results, None |
| 582 | 573 |
| 583 def _GetTestsFromRunner(self): | 574 def _GetTestsFromRunner(self): |
| 584 test_apk_path = self._test_instance.test_apk.path | 575 test_apk_path = self._test_instance.test_apk.path |
| 585 pickle_path = '%s-runner.pickle' % test_apk_path | 576 pickle_path = '%s-runner.pickle' % test_apk_path |
| 586 try: | 577 try: |
| 587 return instrumentation_test_instance.GetTestsFromPickle( | 578 return instrumentation_test_instance.GetTestsFromPickle( |
| 588 pickle_path, test_apk_path) | 579 pickle_path, test_apk_path) |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 680 | 671 |
| 681 with open(trace_host_file, 'r') as host_handle: | 672 with open(trace_host_file, 'r') as host_handle: |
| 682 host_contents = host_handle.readline() | 673 host_contents = host_handle.readline() |
| 683 | 674 |
| 684 if host_contents: | 675 if host_contents: |
| 685 java_trace_json = ',%s' % java_trace_json.lstrip(' [') | 676 java_trace_json = ',%s' % java_trace_json.lstrip(' [') |
| 686 | 677 |
| 687 with open(trace_host_file, 'a') as host_handle: | 678 with open(trace_host_file, 'a') as host_handle: |
| 688 host_handle.write(java_trace_json) | 679 host_handle.write(java_trace_json) |
| 689 | 680 |
| 690 def _SaveScreenshot(self, device, screenshot_host_dir, screenshot_device_file, | 681 def _SaveScreenshot(self, device, screenshot_device_file, test_name, results): |
| 691 test_name, results): | 682 screenshot_filename = '%s-%s.png' % ( |
| 692 if screenshot_host_dir: | 683 test_name, time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime())) |
| 693 screenshot_host_file = os.path.join( | |
| 694 screenshot_host_dir, | |
| 695 '%s-%s.png' % ( | |
| 696 test_name, | |
| 697 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()))) | |
| 698 if device.FileExists(screenshot_device_file.name): | 684 if device.FileExists(screenshot_device_file.name): |
| 699 try: | 685 with self._env.output_manager.ArchivedTempfile( |
| 700 device.PullFile(screenshot_device_file.name, screenshot_host_file) | 686 screenshot_filename, 'screenshot', |
| 701 finally: | 687 output_manager.Datatype.IMAGE) as screenshot_host_file: |
| 702 screenshot_device_file.close() | 688 try: |
| 703 | 689 device.PullFile(screenshot_device_file.name, |
| 704 logging.info( | 690 screenshot_host_file.name) |
| 705 'Saved screenshot for %s to %s.', | 691 finally: |
| 706 test_name, screenshot_host_file) | 692 screenshot_device_file.close() |
| 707 if self._test_instance.gs_results_bucket: | 693 for result in results: |
| 708 link = google_storage_helper.upload( | 694 result.SetLink('post_test_screenshot', screenshot_host_file.Link()) |
| 709 google_storage_helper.unique_name( | |
| 710 'screenshot', device=device), | |
| 711 screenshot_host_file, | |
| 712 bucket=('%s/screenshots' % | |
| 713 self._test_instance.gs_results_bucket)) | |
| 714 for result in results: | |
| 715 result.SetLink('post_test_screenshot', link) | |
| 716 | 695 |
| 717 def _ProcessRenderTestResults( | 696 def _ProcessRenderTestResults( |
| 718 self, device, render_tests_device_output_dir, results): | 697 self, device, render_tests_device_output_dir, results): |
| 719 # If GS results bucket is specified, will archive render result images. | |
| 720 # If render image dir is specified, will pull the render result image from | |
| 721 # the device and leave in the directory. | |
| 722 if not (bool(self._test_instance.gs_results_bucket) or | |
| 723 bool(self._test_instance.render_results_dir)): | |
| 724 return | |
| 725 | 698 |
| 726 failure_images_device_dir = posixpath.join( | 699 failure_images_device_dir = posixpath.join( |
| 727 render_tests_device_output_dir, 'failures') | 700 render_tests_device_output_dir, 'failures') |
| 728 if not device.FileExists(failure_images_device_dir): | 701 if not device.FileExists(failure_images_device_dir): |
| 729 return | 702 return |
| 730 | 703 |
| 731 diff_images_device_dir = posixpath.join( | 704 diff_images_device_dir = posixpath.join( |
| 732 render_tests_device_output_dir, 'diffs') | 705 render_tests_device_output_dir, 'diffs') |
| 733 | 706 |
| 734 golden_images_device_dir = posixpath.join( | 707 golden_images_device_dir = posixpath.join( |
| 735 render_tests_device_output_dir, 'goldens') | 708 render_tests_device_output_dir, 'goldens') |
| 736 | 709 |
| 737 with contextlib_ext.Optional( | 710 for failure_filename in device.ListDirectory(failure_images_device_dir): |
| 738 tempfile_ext.NamedTemporaryDirectory(), | |
| 739 not bool(self._test_instance.render_results_dir)) as render_temp_dir: | |
| 740 render_host_dir = ( | |
| 741 self._test_instance.render_results_dir or render_temp_dir) | |
| 742 | 711 |
| 743 if not os.path.exists(render_host_dir): | 712 with self._env.output_manager.ArchivedTempfile( |
| 744 os.makedirs(render_host_dir) | 713 'fail_%s' % failure_filename, 'render_tests', |
| 714 output_manager.Datatype.IMAGE) as failure_image_host_file: | |
| 715 device.PullFile( | |
| 716 posixpath.join(failure_images_device_dir, failure_filename), | |
| 717 failure_image_host_file) | |
| 718 failure_link = failure_image_host_file.Link() | |
| 745 | 719 |
| 746 # Pull all render test results from device. | 720 if device.PathExists( |
| 747 device.PullFile(failure_images_device_dir, render_host_dir) | 721 posixpath.join(golden_images_device_dir, failure_filename)): |
|
jbudorick
2017/08/23 16:16:20
nit: maybe save this as golden_image_device_file,
mikecase (-- gone --)
2017/08/24 05:29:07
Done
| |
| 748 | 722 with self._env.output_manager.ArchivedTempfile( |
| 749 if device.FileExists(diff_images_device_dir): | 723 'golden_%s' % failure_filename, 'render_tests', |
| 750 device.PullFile(diff_images_device_dir, render_host_dir) | 724 output_manager.Datatype.IMAGE) as golden_image_host_file: |
| 751 else: | 725 device.PullFile( |
| 752 logging.error('Diff images not found on device.') | 726 posixpath.join(golden_images_device_dir, failure_filename), |
| 753 | 727 golden_image_host_file) |
| 754 if device.FileExists(golden_images_device_dir): | 728 golden_link = golden_image_host_file.Link() |
| 755 device.PullFile(golden_images_device_dir, render_host_dir) | |
| 756 else: | |
| 757 logging.error('Golden images not found on device.') | |
| 758 | |
| 759 # Upload results to Google Storage. | |
| 760 if self._test_instance.gs_results_bucket: | |
| 761 self._UploadRenderTestResults(render_host_dir, results) | |
| 762 | |
| 763 def _UploadRenderTestResults(self, render_host_dir, results): | |
| 764 render_tests_bucket = ( | |
| 765 self._test_instance.gs_results_bucket + '/render_tests') | |
| 766 | |
| 767 for failure_filename in os.listdir( | |
| 768 os.path.join(render_host_dir, 'failures')): | |
| 769 m = RE_RENDER_IMAGE_NAME.match(failure_filename) | |
| 770 if not m: | |
| 771 logging.warning('Unexpected file in render test failures: %s', | |
| 772 failure_filename) | |
| 773 continue | |
| 774 | |
| 775 failure_filepath = os.path.join( | |
| 776 render_host_dir, 'failures', failure_filename) | |
| 777 failure_link = google_storage_helper.upload_content_addressed( | |
| 778 failure_filepath, bucket=render_tests_bucket) | |
| 779 | |
| 780 golden_filepath = os.path.join( | |
| 781 render_host_dir, 'goldens', failure_filename) | |
| 782 if os.path.exists(golden_filepath): | |
| 783 golden_link = google_storage_helper.upload_content_addressed( | |
| 784 golden_filepath, bucket=render_tests_bucket) | |
| 785 else: | 729 else: |
| 786 golden_link = '' | 730 golden_link = '' |
| 787 | 731 |
| 788 diff_filepath = os.path.join( | 732 if device.PathExists( |
| 789 render_host_dir, 'diffs', failure_filename) | 733 posixpath.join(diff_images_device_dir, failure_filename)): |
|
jbudorick
2017/08/23 16:16:20
nit: same
mikecase (-- gone --)
2017/08/24 05:29:07
Done
| |
| 790 if os.path.exists(diff_filepath): | 734 with self._env.output_manager.ArchivedTempfile( |
| 791 diff_link = google_storage_helper.upload_content_addressed( | 735 'diff_%s' % failure_filename, 'render_tests', |
| 792 diff_filepath, bucket=render_tests_bucket) | 736 output_manager.Datatype.IMAGE) as diff_image_host_file: |
| 737 device.PullFile( | |
| 738 posixpath.join(diff_images_device_dir, failure_filename), | |
| 739 diff_image_host_file) | |
| 740 diff_link = diff_image_host_file.Link() | |
| 793 else: | 741 else: |
| 794 diff_link = '' | 742 diff_link = '' |
| 795 | 743 |
| 796 with tempfile.NamedTemporaryFile(suffix='.html') as temp_html: | 744 jinja2_env = jinja2.Environment( |
| 797 jinja2_env = jinja2.Environment( | 745 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR), |
| 798 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR), | 746 trim_blocks=True) |
| 799 trim_blocks=True) | 747 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME) |
| 800 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME) | 748 # pylint: disable=no-member |
| 801 # pylint: disable=no-member | 749 processed_template_output = template.render( |
| 802 processed_template_output = template.render( | 750 test_name=failure_filename, |
| 803 test_name=failure_filename, | 751 failure_link=failure_link, |
| 804 failure_link=failure_link, | 752 golden_link=golden_link, |
| 805 golden_link=golden_link, | 753 diff_link=diff_link) |
| 806 diff_link=diff_link) | |
| 807 | 754 |
| 808 temp_html.write(processed_template_output) | 755 with self._env.output_manager.ArchivedTempfile( |
| 809 temp_html.flush() | 756 '%s.html' % failure_filename, 'render_tests', |
| 810 html_results_link = google_storage_helper.upload_content_addressed( | 757 output_manager.Datatype.HTML) as html_results: |
| 811 temp_html.name, | 758 html_results.write(processed_template_output) |
| 812 bucket=render_tests_bucket, | 759 html_results.flush() |
| 813 content_type='text/html') | 760 for result in results: |
| 814 for result in results: | 761 result.SetLink(failure_filename, html_results.Link()) |
| 815 result.SetLink(failure_filename, html_results_link) | |
| 816 | 762 |
| 817 #override | 763 #override |
| 818 def _ShouldRetry(self, test, result): | 764 def _ShouldRetry(self, test, result): |
| 819 def not_run(res): | 765 def not_run(res): |
| 820 if isinstance(res, list): | 766 if isinstance(res, list): |
| 821 return any(not_run(r) for r in res) | 767 return any(not_run(r) for r in res) |
| 822 return res.GetType() == base_test_result.ResultType.NOTRUN | 768 return res.GetType() == base_test_result.ResultType.NOTRUN |
| 823 | 769 |
| 824 if 'RetryOnFailure' in test.get('annotations', {}) or not_run(result): | 770 if 'RetryOnFailure' in test.get('annotations', {}) or not_run(result): |
| 825 return True | 771 return True |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 854 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) | 800 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) |
| 855 | 801 |
| 856 return timeout | 802 return timeout |
| 857 | 803 |
| 858 def _IsRenderTest(test): | 804 def _IsRenderTest(test): |
| 859 """Determines if a test or list of tests has a RenderTest amongst them.""" | 805 """Determines if a test or list of tests has a RenderTest amongst them.""" |
| 860 if not isinstance(test, list): | 806 if not isinstance(test, list): |
| 861 test = [test] | 807 test = [test] |
| 862 return any([RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get( | 808 return any([RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get( |
| 863 FEATURE_ANNOTATION, {}).get('value', ()) for t in test]) | 809 FEATURE_ANNOTATION, {}).get('value', ()) for t in test]) |
| OLD | NEW |