Chromium Code Reviews| Index: build/android/pylib/local/device/local_device_instrumentation_test_run.py |
| diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py |
| index a8d9f5e6737828ccf5010f9070d4cdb553f407dd..e4a4563336d166ec6dde6900e644156868a222ce 100644 |
| --- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py |
| +++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py |
| @@ -6,6 +6,7 @@ import logging |
| import os |
| import posixpath |
| import re |
| +import tempfile |
| import time |
| from devil.android import device_errors |
| @@ -14,15 +15,25 @@ from devil.android.sdk import shared_prefs |
| from devil.utils import reraiser_thread |
| from pylib import valgrind_tools |
| from pylib.android import logdog_logcat_monitor |
| +from pylib.constants import host_paths |
| from pylib.base import base_test_result |
| from pylib.instrumentation import instrumentation_test_instance |
| from pylib.local.device import local_device_environment |
| from pylib.local.device import local_device_test_run |
| +from pylib.utils import google_storage_helper |
| from pylib.utils import logdog_helper |
| from py_trace_event import trace_event |
| from py_utils import contextlib_ext |
| +from py_utils import tempfile_ext |
| import tombstones |
| +try: |
| + from PIL import Image # pylint: disable=import-error |
| + from PIL import ImageChops # pylint: disable=import-error |
| + can_compute_diffs = True |
| +except ImportError: |
| + can_compute_diffs = False |
|
jbudorick
2017/04/11 00:36:44
I've usually seen (and written) this kind of thing
|
| + |
| _TAG = 'test_runner_py' |
| TIMEOUT_ANNOTATIONS = [ |
| @@ -37,6 +48,16 @@ TIMEOUT_ANNOTATIONS = [ |
| LOGCAT_FILTERS = ['*:e', 'chromium:v', 'cr_*:v'] |
| +RE_RENDER_IMAGE_NAME = re.compile( |
| + r'(?P<test_class>\w+)\.' |
| + r'(?P<description>\w+)\.' |
| + r'(?P<device_model>\w+)\.' |
| + r'(?P<orientation>port|land)\.png') |
| + |
| +RENDER_TESTS_RESULTS_DIR = { |
| + 'ChromePublicTest': 'chrome/test/data/android/render_tests' |
| +} |
| + |
| # TODO(jbudorick): Make this private once the instrumentation test_runner is |
| # deprecated. |
| def DidPackageCrashOnDevice(package_name, device): |
| @@ -327,6 +348,8 @@ class LocalDeviceInstrumentationTestRun( |
| if logcat_url: |
| result.SetLink('logcat', logcat_url) |
| + self._ProcessRenderTestResults(device, results) |
| + |
| # Update the result name if the test used flags. |
| if flags: |
| for r in results: |
| @@ -358,11 +381,19 @@ class LocalDeviceInstrumentationTestRun( |
| file_name = '%s-%s.png' % ( |
| test_display_name, |
| time.strftime('%Y%m%dT%H%M%S', time.localtime())) |
| - saved_dir = device.TakeScreenshot( |
| + screenshot_file = device.TakeScreenshot( |
| os.path.join(self._test_instance.screenshot_dir, file_name)) |
| logging.info( |
| 'Saved screenshot for %s to %s.', |
| - test_display_name, saved_dir) |
| + test_display_name, screenshot_file) |
| + if self._test_instance.should_save_images: |
| + link = google_storage_helper.upload( |
| + google_storage_helper.unique_name('screenshot', device=device), |
| + screenshot_file, |
| + bucket='chromium-result-details/images') |
|
jbudorick
2017/04/11 00:36:43
We shouldn't be hardcoding the bucket. If we use s
mikecase (-- gone --)
2017/04/26 18:01:51
Agreed. Added test runner arg --gs-results-bucket.
|
| + for result in results: |
| + result.SetLink('failure_screenshot', link) |
| + |
| logging.info('detected failure in %s. raw output:', test_display_name) |
| for l in output: |
| logging.info(' %s', l) |
| @@ -374,7 +405,6 @@ class LocalDeviceInstrumentationTestRun( |
| else None) |
| device.ClearApplicationState(self._test_instance.package_info.package, |
| permissions=permissions) |
| - |
| else: |
| logging.debug('raw output from %s:', test_display_name) |
| for l in output: |
| @@ -403,6 +433,91 @@ class LocalDeviceInstrumentationTestRun( |
| result.SetLink('tombstones', tombstones_url) |
| return results, None |
| + def _ProcessRenderTestResults(self, device, results): |
| + if not self._test_instance.should_save_images: |
| + return |
| + |
| + render_results_dir = RENDER_TESTS_RESULTS_DIR.get(self._test_instance.suite) |
| + if not render_results_dir: |
| + return |
| + |
| + failure_images_device_dir = posixpath.join( |
| + device.GetExternalStoragePath(), |
| + 'chromium_tests_root', render_results_dir, 'failures') |
| + if not device.FileExists(failure_images_device_dir): |
| + return |
| + |
| + with tempfile_ext.NamedTemporaryDirectory() as temp_dir: |
| + device.PullFile(failure_images_device_dir, temp_dir) |
| + device.RemovePath(failure_images_device_dir, recursive=True) |
| + |
| + for failure_filename in os.listdir(os.path.join(temp_dir, 'failures')): |
| + |
| + m = RE_RENDER_IMAGE_NAME.match(failure_filename) |
| + if not m: |
| + logging.warning('Unexpected file in render test failures: %s', |
| + failure_filename) |
| + continue |
| + |
| + failure_filepath = os.path.join(temp_dir, 'failures', failure_filename) |
| + failure_link = google_storage_helper.upload( |
| + google_storage_helper.unique_name(failure_filename, device=device), |
| + failure_filepath, |
| + bucket='chromium-render-tests') |
|
jbudorick
2017/04/11 00:36:43
Again, I'm concerned about the hard-coded bucket n
mikecase (-- gone --)
2017/04/26 18:01:51
Fixed
|
| + |
| + golden_filepath = os.path.join( |
| + host_paths.DIR_SOURCE_ROOT, render_results_dir, failure_filename) |
| + if not os.path.exists(golden_filepath): |
| + logging.error('Cannot find golden image for %s', failure_filename) |
| + continue |
| + golden_link = google_storage_helper.upload( |
| + google_storage_helper.unique_name(failure_filename, device=device), |
| + golden_filepath, |
| + bucket='chromium-render-tests') |
| + |
| + if can_compute_diffs: |
| + diff_filename = '_diff'.join(os.path.splitext(failure_filename)) |
| + diff_filepath = os.path.join(temp_dir, diff_filename) |
| + (ImageChops.difference( |
| + Image.open(failure_filepath), Image.open(golden_filepath)) |
| + .convert('L') |
| + .point(lambda i: 255 if i else 0) |
| + .save(diff_filepath)) |
| + diff_link = google_storage_helper.upload( |
| + google_storage_helper.unique_name(diff_filename, device=device), |
| + diff_filepath, |
| + bucket='chromium-render-tests') |
| + else: |
| + diff_link = '' |
| + logging.error('Error importing PIL library. Image diffs for ' |
| + 'render test results will not be computed.') |
| + |
| + with tempfile.NamedTemporaryFile(suffix='.html') as temp_html: |
| + temp_html.write(''' |
| + <html> |
| + <table> |
| + <tr> |
| + <th>Failure</th> |
| + <th>Golden</th> |
| + <th>Diff</th> |
| + </tr> |
| + <tr> |
| + <td><img src="%s"/></td> |
| + <td><img src="%s"/></td> |
| + <td><img src="%s"/></td> |
| + </tr> |
| + </table> |
| + </html> |
| + ''' % (failure_link, golden_link, diff_link)) |
| + temp_html.flush() |
| + html_results_link = google_storage_helper.upload( |
| + google_storage_helper.unique_name('render_html', device=device), |
| + temp_html.name, |
| + bucket='chromium-render-tests', |
| + content_type='text/html') |
| + for result in results: |
| + result.SetLink(failure_filename, html_results_link) |
| + |
| #override |
| def _ShouldRetry(self, test): |
| if 'RetryOnFailure' in test.get('annotations', {}): |