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 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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |