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

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

Issue 2786773002: (Reland) Add failure screenshots and images to results detail. (Closed)
Patch Set: yolands nit Created 3 years, 7 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 logging 5 import logging
6 import os 6 import os
7 import posixpath 7 import posixpath
8 import re 8 import re
9 import time 9 import time
10 10
11 from devil.android import device_errors 11 from devil.android import device_errors
12 from devil.android import flag_changer 12 from devil.android import flag_changer
13 from devil.android.sdk import shared_prefs 13 from devil.android.sdk import shared_prefs
14 from devil.utils import reraiser_thread 14 from devil.utils import reraiser_thread
15 from pylib import valgrind_tools 15 from pylib import valgrind_tools
16 from pylib.android import logdog_logcat_monitor 16 from pylib.android import logdog_logcat_monitor
17 from pylib.base import base_test_result 17 from pylib.base import base_test_result
18 from pylib.instrumentation import instrumentation_test_instance 18 from pylib.instrumentation import instrumentation_test_instance
19 from pylib.local.device import local_device_environment 19 from pylib.local.device import local_device_environment
20 from pylib.local.device import local_device_test_run 20 from pylib.local.device import local_device_test_run
21 from pylib.utils import google_storage_helper
21 from pylib.utils import logdog_helper 22 from pylib.utils import logdog_helper
22 from py_trace_event import trace_event 23 from py_trace_event import trace_event
23 from py_utils import contextlib_ext 24 from py_utils import contextlib_ext
25 from py_utils import tempfile_ext
24 import tombstones 26 import tombstones
25 27
26 _TAG = 'test_runner_py' 28 _TAG = 'test_runner_py'
27 29
28 TIMEOUT_ANNOTATIONS = [ 30 TIMEOUT_ANNOTATIONS = [
29 ('Manual', 10 * 60 * 60), 31 ('Manual', 10 * 60 * 60),
30 ('IntegrationTest', 30 * 60), 32 ('IntegrationTest', 30 * 60),
31 ('External', 10 * 60), 33 ('External', 10 * 60),
32 ('EnormousTest', 10 * 60), 34 ('EnormousTest', 10 * 60),
33 ('LargeTest', 5 * 60), 35 ('LargeTest', 5 * 60),
34 ('MediumTest', 3 * 60), 36 ('MediumTest', 3 * 60),
35 ('SmallTest', 1 * 60), 37 ('SmallTest', 1 * 60),
36 ] 38 ]
37 39
38 LOGCAT_FILTERS = ['*:e', 'chromium:v', 'cr_*:v'] 40 LOGCAT_FILTERS = ['*:e', 'chromium:v', 'cr_*:v']
39 41
42
40 # TODO(jbudorick): Make this private once the instrumentation test_runner is 43 # TODO(jbudorick): Make this private once the instrumentation test_runner is
41 # deprecated. 44 # deprecated.
42 def DidPackageCrashOnDevice(package_name, device): 45 def DidPackageCrashOnDevice(package_name, device):
43 # Dismiss any error dialogs. Limit the number in case we have an error 46 # Dismiss any error dialogs. Limit the number in case we have an error
44 # loop or we are failing to dismiss. 47 # loop or we are failing to dismiss.
45 try: 48 try:
46 for _ in xrange(10): 49 for _ in xrange(10):
47 package = device.DismissCrashDialogIfNeeded() 50 package = device.DismissCrashDialogIfNeeded()
48 if not package: 51 if not package:
49 return False 52 return False
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 312
310 try: 313 try:
311 device.RunShellCommand( 314 device.RunShellCommand(
312 ['log', '-p', 'i', '-t', _TAG, 'START %s' % test_name], 315 ['log', '-p', 'i', '-t', _TAG, 'START %s' % test_name],
313 check_return=True) 316 check_return=True)
314 time_ms = lambda: int(time.time() * 1e3) 317 time_ms = lambda: int(time.time() * 1e3)
315 start_ms = time_ms() 318 start_ms = time_ms()
316 319
317 stream_name = 'logcat_%s_%s_%s' % ( 320 stream_name = 'logcat_%s_%s_%s' % (
318 test_name.replace('#', '.'), 321 test_name.replace('#', '.'),
319 time.strftime('%Y%m%dT%H%M%S', time.localtime()), 322 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()),
320 device.serial) 323 device.serial)
321 logmon = logdog_logcat_monitor.LogdogLogcatMonitor( 324 logmon = logdog_logcat_monitor.LogdogLogcatMonitor(
322 device.adb, stream_name, filter_specs=LOGCAT_FILTERS) 325 device.adb, stream_name, filter_specs=LOGCAT_FILTERS)
323 326
324 with contextlib_ext.Optional( 327 with contextlib_ext.Optional(
325 logmon, self._test_instance.should_save_logcat): 328 logmon, self._test_instance.should_save_logcat):
326 with contextlib_ext.Optional( 329 with contextlib_ext.Optional(
327 trace_event.trace(test_name), 330 trace_event.trace(test_name),
328 self._env.trace_output): 331 self._env.trace_output):
329 output = device.StartInstrumentation( 332 output = device.StartInstrumentation(
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 if r.GetType() == base_test_result.ResultType.UNKNOWN: 373 if r.GetType() == base_test_result.ResultType.UNKNOWN:
371 r.SetType(base_test_result.ResultType.CRASH) 374 r.SetType(base_test_result.ResultType.CRASH)
372 375
373 # Handle failures by: 376 # Handle failures by:
374 # - optionally taking a screenshot 377 # - optionally taking a screenshot
375 # - logging the raw output at INFO level 378 # - logging the raw output at INFO level
376 # - clearing the application state while persisting permissions 379 # - clearing the application state while persisting permissions
377 if any(r.GetType() not in (base_test_result.ResultType.PASS, 380 if any(r.GetType() not in (base_test_result.ResultType.PASS,
378 base_test_result.ResultType.SKIP) 381 base_test_result.ResultType.SKIP)
379 for r in results): 382 for r in results):
380 if self._test_instance.screenshot_dir: 383 with contextlib_ext.Optional(
381 file_name = '%s-%s.png' % ( 384 tempfile_ext.NamedTemporaryDirectory(),
382 test_display_name, 385 self._test_instance.screenshot_dir is None and
383 time.strftime('%Y%m%dT%H%M%S', time.localtime())) 386 self._test_instance.gs_results_bucket) as screenshot_host_dir:
384 saved_dir = device.TakeScreenshot( 387 screenshot_host_dir = (
385 os.path.join(self._test_instance.screenshot_dir, file_name)) 388 self._test_instance.screenshot_dir or screenshot_host_dir)
386 logging.info( 389 if screenshot_host_dir:
387 'Saved screenshot for %s to %s.', 390 file_name = '%s-%s.png' % (
388 test_display_name, saved_dir) 391 test_display_name,
392 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()))
393 screenshot_file = device.TakeScreenshot(
394 os.path.join(screenshot_host_dir, file_name))
395 logging.info(
396 'Saved screenshot for %s to %s.',
397 test_display_name, screenshot_file)
398 if self._test_instance.gs_results_bucket:
399 link = google_storage_helper.upload(
400 google_storage_helper.unique_name('screenshot', device=device),
401 screenshot_file,
402 bucket=self._test_instance.gs_results_bucket + '/screenshots')
403 for result in results:
404 result.SetLink('post_test_screenshot', link)
405
389 logging.info('detected failure in %s. raw output:', test_display_name) 406 logging.info('detected failure in %s. raw output:', test_display_name)
390 for l in output: 407 for l in output:
391 logging.info(' %s', l) 408 logging.info(' %s', l)
392 if (not self._env.skip_clear_data 409 if (not self._env.skip_clear_data
393 and self._test_instance.package_info): 410 and self._test_instance.package_info):
394 permissions = ( 411 permissions = (
395 self._test_instance.apk_under_test.GetPermissions() 412 self._test_instance.apk_under_test.GetPermissions()
396 if self._test_instance.apk_under_test 413 if self._test_instance.apk_under_test
397 else None) 414 else None)
398 device.ClearApplicationState(self._test_instance.package_info.package, 415 device.ClearApplicationState(self._test_instance.package_info.package,
399 permissions=permissions) 416 permissions=permissions)
400
401 else: 417 else:
402 logging.debug('raw output from %s:', test_display_name) 418 logging.debug('raw output from %s:', test_display_name)
403 for l in output: 419 for l in output:
404 logging.debug(' %s', l) 420 logging.debug(' %s', l)
405 if self._test_instance.coverage_directory: 421 if self._test_instance.coverage_directory:
406 device.PullFile(coverage_directory, 422 device.PullFile(coverage_directory,
407 self._test_instance.coverage_directory) 423 self._test_instance.coverage_directory)
408 device.RunShellCommand( 424 device.RunShellCommand(
409 'rm -f %s' % posixpath.join(coverage_directory, '*'), 425 'rm -f %s' % posixpath.join(coverage_directory, '*'),
410 check_return=True, shell=True) 426 check_return=True, shell=True)
411 if self._test_instance.store_tombstones: 427 if self._test_instance.store_tombstones:
412 tombstones_url = None 428 tombstones_url = None
413 for result in results: 429 for result in results:
414 if result.GetType() == base_test_result.ResultType.CRASH: 430 if result.GetType() == base_test_result.ResultType.CRASH:
415 if not tombstones_url: 431 if not tombstones_url:
416 resolved_tombstones = tombstones.ResolveTombstones( 432 resolved_tombstones = tombstones.ResolveTombstones(
417 device, 433 device,
418 resolve_all_tombstones=True, 434 resolve_all_tombstones=True,
419 include_stack_symbols=False, 435 include_stack_symbols=False,
420 wipe_tombstones=True) 436 wipe_tombstones=True)
421 stream_name = 'tombstones_%s_%s' % ( 437 stream_name = 'tombstones_%s_%s' % (
422 time.strftime('%Y%m%dT%H%M%S', time.localtime()), 438 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()),
423 device.serial) 439 device.serial)
424 tombstones_url = logdog_helper.text( 440 tombstones_url = logdog_helper.text(
425 stream_name, '\n'.join(resolved_tombstones)) 441 stream_name, '\n'.join(resolved_tombstones))
426 result.SetLink('tombstones', tombstones_url) 442 result.SetLink('tombstones', tombstones_url)
427 return results, None 443 return results, None
428 444
429 #override 445 #override
430 def _ShouldRetry(self, test): 446 def _ShouldRetry(self, test):
431 if 'RetryOnFailure' in test.get('annotations', {}): 447 if 'RetryOnFailure' in test.get('annotations', {}):
432 return True 448 return True
(...skipping 21 matching lines...) Expand all
454 if k in annotations: 470 if k in annotations:
455 timeout = v 471 timeout = v
456 break 472 break
457 else: 473 else:
458 logging.warning('Using default 1 minute timeout for %s', test_name) 474 logging.warning('Using default 1 minute timeout for %s', test_name)
459 timeout = 60 475 timeout = 60
460 476
461 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) 477 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations)
462 478
463 return timeout 479 return timeout
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698