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 logging | 6 import logging |
7 import os | 7 import os |
8 import posixpath | 8 import posixpath |
9 import re | 9 import re |
10 import sys | 10 import sys |
11 import tempfile | 11 import tempfile |
12 import time | 12 import time |
13 | 13 |
14 from devil.android import crash_handler | 14 from devil.android import crash_handler |
15 from devil.android import device_errors | 15 from devil.android import device_errors |
16 from devil.android import device_temp_file | 16 from devil.android import device_temp_file |
17 from devil.android import flag_changer | 17 from devil.android import flag_changer |
18 from devil.android import logcat_monitor | |
18 from devil.android.tools import system_app | 19 from devil.android.tools import system_app |
19 from devil.utils import reraiser_thread | 20 from devil.utils import reraiser_thread |
20 from pylib import valgrind_tools | 21 from pylib import valgrind_tools |
21 from pylib.android import logdog_logcat_monitor | |
22 from pylib.base import base_test_result | 22 from pylib.base import base_test_result |
23 from pylib.base import output_manager | |
23 from pylib.constants import host_paths | 24 from pylib.constants import host_paths |
24 from pylib.instrumentation import instrumentation_test_instance | 25 from pylib.instrumentation import instrumentation_test_instance |
25 from pylib.local.device import local_device_environment | 26 from pylib.local.device import local_device_environment |
26 from pylib.local.device import local_device_test_run | 27 from pylib.local.device import local_device_test_run |
27 from pylib.utils import google_storage_helper | 28 |
28 from pylib.utils import logdog_helper | |
29 from pylib.utils import shared_preference_utils | 29 from pylib.utils import shared_preference_utils |
30 | |
30 from py_trace_event import trace_event | 31 from py_trace_event import trace_event |
31 from py_utils import contextlib_ext | 32 from py_utils import contextlib_ext |
32 from py_utils import tempfile_ext | |
33 import tombstones | 33 import tombstones |
34 | 34 |
35 with host_paths.SysPath( | 35 with host_paths.SysPath( |
36 os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party'), 0): | 36 os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party'), 0): |
37 import jinja2 # pylint: disable=import-error | 37 import jinja2 # pylint: disable=import-error |
38 import markupsafe # pylint: disable=import-error,unused-import | 38 import markupsafe # pylint: disable=import-error,unused-import |
39 | 39 |
40 | 40 |
41 _JINJA_TEMPLATE_DIR = os.path.join( | 41 _JINJA_TEMPLATE_DIR = os.path.join( |
42 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'pylib', 'instrumentation') | 42 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'pylib', 'instrumentation') |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
105 return False | 105 return False |
106 | 106 |
107 | 107 |
108 _CURRENT_FOCUS_CRASH_RE = re.compile( | 108 _CURRENT_FOCUS_CRASH_RE = re.compile( |
109 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') | 109 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') |
110 | 110 |
111 | 111 |
112 class LocalDeviceInstrumentationTestRun( | 112 class LocalDeviceInstrumentationTestRun( |
113 local_device_test_run.LocalDeviceTestRun): | 113 local_device_test_run.LocalDeviceTestRun): |
114 def __init__(self, env, test_instance): | 114 def __init__(self, env, test_instance): |
115 super(LocalDeviceInstrumentationTestRun, self).__init__(env, test_instance) | 115 super(LocalDeviceInstrumentationTestRun, self).__init__( |
116 env, test_instance) | |
116 self._flag_changers = {} | 117 self._flag_changers = {} |
117 self._ui_capture_dir = dict() | 118 self._ui_capture_dir = dict() |
118 self._replace_package_contextmanager = None | 119 self._replace_package_contextmanager = None |
119 | 120 |
120 #override | 121 #override |
121 def TestPackage(self): | 122 def TestPackage(self): |
122 return self._test_instance.suite | 123 return self._test_instance.suite |
123 | 124 |
124 #override | 125 #override |
125 def SetUp(self): | 126 def SetUp(self): |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
325 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method'] | 326 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method'] |
326 if isinstance(test, list) else test['method']) | 327 if isinstance(test, list) else test['method']) |
327 extras['coverage'] = 'true' | 328 extras['coverage'] = 'true' |
328 coverage_directory = os.path.join( | 329 coverage_directory = os.path.join( |
329 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage') | 330 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage') |
330 coverage_device_file = os.path.join( | 331 coverage_device_file = os.path.join( |
331 coverage_directory, coverage_basename) | 332 coverage_directory, coverage_basename) |
332 extras['coverageFile'] = coverage_device_file | 333 extras['coverageFile'] = coverage_device_file |
333 # Save screenshot if screenshot dir is specified (save locally) or if | 334 # Save screenshot if screenshot dir is specified (save locally) or if |
334 # a GS bucket is passed (save in cloud). | 335 # a GS bucket is passed (save in cloud). |
335 screenshot_device_file = None | 336 screenshot_device_file = device_temp_file.DeviceTempFile( |
336 if (self._test_instance.screenshot_dir or | 337 device.adb, suffix='.png', dir=device.GetExternalStoragePath()) |
337 self._test_instance.gs_results_bucket): | 338 extras[EXTRA_SCREENSHOT_FILE] = screenshot_device_file.name |
338 screenshot_device_file = device_temp_file.DeviceTempFile( | |
339 device.adb, suffix='.png', dir=device.GetExternalStoragePath()) | |
340 extras[EXTRA_SCREENSHOT_FILE] = screenshot_device_file.name | |
341 | 339 |
342 extras[EXTRA_UI_CAPTURE_DIR] = self._ui_capture_dir[device] | 340 extras[EXTRA_UI_CAPTURE_DIR] = self._ui_capture_dir[device] |
343 | 341 |
344 if isinstance(test, list): | 342 if isinstance(test, list): |
345 if not self._test_instance.driver_apk: | 343 if not self._test_instance.driver_apk: |
346 raise Exception('driver_apk does not exist. ' | 344 raise Exception('driver_apk does not exist. ' |
347 'Please build it and try again.') | 345 'Please build it and try again.') |
348 if any(t.get('is_junit4') for t in test): | 346 if any(t.get('is_junit4') for t in test): |
349 raise Exception('driver apk does not support JUnit4 tests') | 347 raise Exception('driver apk does not support JUnit4 tests') |
350 | 348 |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
403 self._flag_changers[str(device)].PushFlags( | 401 self._flag_changers[str(device)].PushFlags( |
404 add=flags_to_add, remove=flags_to_remove) | 402 add=flags_to_add, remove=flags_to_remove) |
405 | 403 |
406 time_ms = lambda: int(time.time() * 1e3) | 404 time_ms = lambda: int(time.time() * 1e3) |
407 start_ms = time_ms() | 405 start_ms = time_ms() |
408 | 406 |
409 stream_name = 'logcat_%s_%s_%s' % ( | 407 stream_name = 'logcat_%s_%s_%s' % ( |
410 test_name.replace('#', '.'), | 408 test_name.replace('#', '.'), |
411 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), | 409 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), |
412 device.serial) | 410 device.serial) |
413 logmon = logdog_logcat_monitor.LogdogLogcatMonitor( | |
414 device.adb, stream_name, filter_specs=LOGCAT_FILTERS) | |
415 | 411 |
416 with contextlib_ext.Optional( | 412 try: |
jbudorick
2017/07/19 22:45:09
It would be nice if we could make ArchiveAndDelete
mikecase (-- gone --)
2017/07/26 21:21:37
This is what I wanted, unfortunatly it didnt work
| |
417 logmon, self._test_instance.should_save_logcat): | 413 logcat_file = tempfile.NamedTemporaryFile(delete=False) |
418 with _LogTestEndpoints(device, test_name): | 414 with logcat_monitor.LogcatMonitor( |
419 with contextlib_ext.Optional( | 415 device.adb, filter_specs=LOGCAT_FILTERS, |
420 trace_event.trace(test_name), | 416 output_file=logcat_file.name) as logmon: |
421 self._env.trace_output): | 417 with _LogTestEndpoints(device, test_name): |
422 output = device.StartInstrumentation( | 418 with contextlib_ext.Optional( |
423 target, raw=True, extras=extras, timeout=timeout, retries=0) | 419 trace_event.trace(test_name), |
420 self._env.trace_output): | |
421 output = device.StartInstrumentation( | |
422 target, raw=True, extras=extras, timeout=timeout, retries=0) | |
423 logmon.Close() | |
424 finally: | |
425 logcat_url = self._env.output_manager.ArchiveAndDeleteFile( | |
426 logcat_file.name, stream_name, 'logcat') | |
424 | 427 |
425 logcat_url = logmon.GetLogcatURL() | |
426 duration_ms = time_ms() - start_ms | 428 duration_ms = time_ms() - start_ms |
427 if flags_to_add or flags_to_remove: | 429 if flags_to_add or flags_to_remove: |
428 self._flag_changers[str(device)].Restore() | 430 self._flag_changers[str(device)].Restore() |
429 if test_timeout_scale: | 431 if test_timeout_scale: |
430 valgrind_tools.SetChromeTimeoutScale( | 432 valgrind_tools.SetChromeTimeoutScale( |
431 device, self._test_instance.timeout_scale) | 433 device, self._test_instance.timeout_scale) |
432 | 434 |
433 # TODO(jbudorick): Make instrumentation tests output a JSON so this | 435 # TODO(jbudorick): Make instrumentation tests output a JSON so this |
434 # doesn't have to parse the output. | 436 # doesn't have to parse the output. |
435 result_code, result_bundle, statuses = ( | 437 result_code, result_bundle, statuses = ( |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
471 if r.GetType() == base_test_result.ResultType.UNKNOWN: | 473 if r.GetType() == base_test_result.ResultType.UNKNOWN: |
472 r.SetType(base_test_result.ResultType.CRASH) | 474 r.SetType(base_test_result.ResultType.CRASH) |
473 | 475 |
474 # Handle failures by: | 476 # Handle failures by: |
475 # - optionally taking a screenshot | 477 # - optionally taking a screenshot |
476 # - logging the raw output at INFO level | 478 # - logging the raw output at INFO level |
477 # - clearing the application state while persisting permissions | 479 # - clearing the application state while persisting permissions |
478 if any(r.GetType() not in (base_test_result.ResultType.PASS, | 480 if any(r.GetType() not in (base_test_result.ResultType.PASS, |
479 base_test_result.ResultType.SKIP) | 481 base_test_result.ResultType.SKIP) |
480 for r in results): | 482 for r in results): |
481 with contextlib_ext.Optional( | 483 self._SaveScreenshot(device, screenshot_device_file, test_display_name, |
482 tempfile_ext.NamedTemporaryDirectory(), | 484 results) |
483 self._test_instance.screenshot_dir is None and | |
484 self._test_instance.gs_results_bucket) as screenshot_host_dir: | |
485 screenshot_host_dir = ( | |
486 self._test_instance.screenshot_dir or screenshot_host_dir) | |
487 self._SaveScreenshot(device, screenshot_host_dir, | |
488 screenshot_device_file, test_display_name, | |
489 results) | |
490 | 485 |
491 logging.info('detected failure in %s. raw output:', test_display_name) | 486 logging.info('detected failure in %s. raw output:', test_display_name) |
492 for l in output: | 487 for l in output: |
493 logging.info(' %s', l) | 488 logging.info(' %s', l) |
494 if (not self._env.skip_clear_data | 489 if (not self._env.skip_clear_data |
495 and self._test_instance.package_info): | 490 and self._test_instance.package_info): |
496 permissions = ( | 491 permissions = ( |
497 self._test_instance.apk_under_test.GetPermissions() | 492 self._test_instance.apk_under_test.GetPermissions() |
498 if self._test_instance.apk_under_test | 493 if self._test_instance.apk_under_test |
499 else None) | 494 else None) |
(...skipping 12 matching lines...) Expand all Loading... | |
512 if self._test_instance.store_tombstones: | 507 if self._test_instance.store_tombstones: |
513 tombstones_url = None | 508 tombstones_url = None |
514 for result in results: | 509 for result in results: |
515 if result.GetType() == base_test_result.ResultType.CRASH: | 510 if result.GetType() == base_test_result.ResultType.CRASH: |
516 if not tombstones_url: | 511 if not tombstones_url: |
517 resolved_tombstones = tombstones.ResolveTombstones( | 512 resolved_tombstones = tombstones.ResolveTombstones( |
518 device, | 513 device, |
519 resolve_all_tombstones=True, | 514 resolve_all_tombstones=True, |
520 include_stack_symbols=False, | 515 include_stack_symbols=False, |
521 wipe_tombstones=True) | 516 wipe_tombstones=True) |
522 stream_name = 'tombstones_%s_%s' % ( | 517 try: |
523 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), | 518 tombstone_file = tempfile.NamedTemporaryFile(delete=False) |
524 device.serial) | 519 tombstone_filename = 'tombstones_%s_%s' % ( |
525 tombstones_url = logdog_helper.text( | 520 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), |
526 stream_name, '\n'.join(resolved_tombstones)) | 521 device.serial) |
527 result.SetLink('tombstones', tombstones_url) | 522 tombstone_file.write('\n'.join(resolved_tombstones)) |
523 tombstone_file.flush() | |
524 finally: | |
525 tombstones_url = self._env.output_manager.ArchiveAndDeleteFile( | |
526 tombstone_file.name, tombstone_filename, 'tombstones') | |
527 result.SetLink('tombstones', tombstones_url) | |
528 return results, None | 528 return results, None |
529 | 529 |
530 def _SaveScreenshot(self, device, screenshot_host_dir, screenshot_device_file, | 530 def _SaveScreenshot(self, device, screenshot_device_file, test_name, results): |
531 test_name, results): | 531 screenshot_filename = '%s-%s.png' % ( |
532 if screenshot_host_dir: | 532 test_name, time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime())) |
533 screenshot_host_file = os.path.join( | |
534 screenshot_host_dir, | |
535 '%s-%s.png' % ( | |
536 test_name, | |
537 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()))) | |
538 if device.FileExists(screenshot_device_file.name): | 533 if device.FileExists(screenshot_device_file.name): |
539 try: | 534 try: |
540 device.PullFile(screenshot_device_file.name, screenshot_host_file) | 535 screenshot_host_file = tempfile.NamedTemporaryFile(delete=False) |
536 try: | |
537 device.PullFile(screenshot_device_file.name, | |
538 screenshot_host_file.name) | |
539 finally: | |
540 screenshot_device_file.close() | |
541 screenshot_host_file.flush() | |
541 finally: | 542 finally: |
542 screenshot_device_file.close() | 543 screenshot_url = self._env.output_manager.ArchiveAndDeleteFile( |
543 | 544 screenshot_host_file.name, screenshot_filename, |
544 logging.info( | 545 'screenshot', output_manager.Datatype.IMAGE) |
545 'Saved screenshot for %s to %s.', | |
546 test_name, screenshot_host_file) | |
547 if self._test_instance.gs_results_bucket: | |
548 link = google_storage_helper.upload( | |
549 google_storage_helper.unique_name( | |
550 'screenshot', device=device), | |
551 screenshot_host_file, | |
552 bucket=('%s/screenshots' % | |
553 self._test_instance.gs_results_bucket)) | |
554 for result in results: | 546 for result in results: |
555 result.SetLink('post_test_screenshot', link) | 547 result.SetLink('post_test_screenshot', screenshot_url) |
556 | 548 |
557 def _ProcessRenderTestResults( | 549 def _ProcessRenderTestResults( |
558 self, device, render_tests_device_output_dir, results): | 550 self, device, render_tests_device_output_dir, results): |
559 # If GS results bucket is specified, will archive render result images. | |
560 # If render image dir is specified, will pull the render result image from | |
561 # the device and leave in the directory. | |
562 if not (bool(self._test_instance.gs_results_bucket) or | |
563 bool(self._test_instance.render_results_dir)): | |
564 return | |
565 | 551 |
566 failure_images_device_dir = posixpath.join( | 552 failure_images_device_dir = posixpath.join( |
567 render_tests_device_output_dir, 'failures') | 553 render_tests_device_output_dir, 'failures') |
568 if not device.FileExists(failure_images_device_dir): | 554 if not device.FileExists(failure_images_device_dir): |
569 return | 555 return |
570 | 556 |
571 diff_images_device_dir = posixpath.join( | 557 diff_images_device_dir = posixpath.join( |
572 render_tests_device_output_dir, 'diffs') | 558 render_tests_device_output_dir, 'diffs') |
573 | 559 |
574 golden_images_device_dir = posixpath.join( | 560 golden_images_device_dir = posixpath.join( |
575 render_tests_device_output_dir, 'goldens') | 561 render_tests_device_output_dir, 'goldens') |
576 | 562 |
577 with contextlib_ext.Optional( | 563 for failure_filename in device.ListDirectory(failure_images_device_dir): |
578 tempfile_ext.NamedTemporaryDirectory(), | |
579 not bool(self._test_instance.render_results_dir)) as render_temp_dir: | |
580 render_host_dir = ( | |
581 self._test_instance.render_results_dir or render_temp_dir) | |
582 | 564 |
583 if not os.path.exists(render_host_dir): | 565 try: |
584 os.makedirs(render_host_dir) | 566 failure_image_host_file = tempfile.NamedTemporaryFile(delete=False) |
567 device.PullFile( | |
568 posixpath.join(failure_images_device_dir, failure_filename), | |
569 failure_image_host_file) | |
570 failure_image_host_file.flush() | |
571 finally: | |
572 failure_link = self._env.output_manager.ArchiveAndDeleteFile( | |
573 failure_image_host_file.name, 'fail_%s' % failure_filename, | |
574 'render_tests', output_manager.Datatype.IMAGE) | |
585 | 575 |
586 # Pull all render test results from device. | 576 if device.PathExists( |
587 device.PullFile(failure_images_device_dir, render_host_dir) | 577 posixpath.join(golden_images_device_dir, failure_filename)): |
588 | 578 try: |
589 if device.FileExists(diff_images_device_dir): | 579 golden_image_host_file = tempfile.NamedTemporaryFile(delete=False) |
590 device.PullFile(diff_images_device_dir, render_host_dir) | 580 device.PullFile( |
591 else: | 581 posixpath.join(golden_images_device_dir, failure_filename), |
592 logging.error('Diff images not found on device.') | 582 golden_image_host_file) |
593 | 583 golden_image_host_file.flush() |
594 if device.FileExists(golden_images_device_dir): | 584 finally: |
595 device.PullFile(golden_images_device_dir, render_host_dir) | 585 golden_link = self._env.output_manager.ArchiveAndDeleteFile( |
596 else: | 586 golden_image_host_file.name, 'golden_%s' % failure_filename, |
597 logging.error('Golden images not found on device.') | 587 'render_tests', output_manager.Datatype.IMAGE) |
598 | |
599 # Upload results to Google Storage. | |
600 if self._test_instance.gs_results_bucket: | |
601 self._UploadRenderTestResults(render_host_dir, results) | |
602 | |
603 def _UploadRenderTestResults(self, render_host_dir, results): | |
604 render_tests_bucket = ( | |
605 self._test_instance.gs_results_bucket + '/render_tests') | |
606 | |
607 for failure_filename in os.listdir( | |
608 os.path.join(render_host_dir, 'failures')): | |
609 m = RE_RENDER_IMAGE_NAME.match(failure_filename) | |
610 if not m: | |
611 logging.warning('Unexpected file in render test failures: %s', | |
612 failure_filename) | |
613 continue | |
614 | |
615 failure_filepath = os.path.join( | |
616 render_host_dir, 'failures', failure_filename) | |
617 failure_link = google_storage_helper.upload_content_addressed( | |
618 failure_filepath, bucket=render_tests_bucket) | |
619 | |
620 golden_filepath = os.path.join( | |
621 render_host_dir, 'goldens', failure_filename) | |
622 if os.path.exists(golden_filepath): | |
623 golden_link = google_storage_helper.upload_content_addressed( | |
624 golden_filepath, bucket=render_tests_bucket) | |
625 else: | 588 else: |
626 golden_link = '' | 589 golden_link = '' |
627 | 590 |
628 diff_filepath = os.path.join( | 591 if device.PathExists( |
629 render_host_dir, 'diffs', failure_filename) | 592 posixpath.join(diff_images_device_dir, failure_filename)): |
630 if os.path.exists(diff_filepath): | 593 try: |
631 diff_link = google_storage_helper.upload_content_addressed( | 594 diff_image_host_file = tempfile.NamedTemporaryFile(delete=False) |
632 diff_filepath, bucket=render_tests_bucket) | 595 device.PullFile( |
596 posixpath.join(diff_images_device_dir, failure_filename), | |
597 diff_image_host_file) | |
598 diff_image_host_file.flush() | |
599 finally: | |
600 diff_link = self._env.output_manager.ArchiveAndDeleteFile( | |
601 diff_image_host_file.name, 'diff_%s' % failure_filename, | |
602 'render_tests', output_manager.Datatype.IMAGE) | |
633 else: | 603 else: |
634 diff_link = '' | 604 diff_link = '' |
635 | 605 |
636 with tempfile.NamedTemporaryFile(suffix='.html') as temp_html: | 606 jinja2_env = jinja2.Environment( |
637 jinja2_env = jinja2.Environment( | 607 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR), |
638 loader=jinja2.FileSystemLoader(_JINJA_TEMPLATE_DIR), | 608 trim_blocks=True) |
639 trim_blocks=True) | 609 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME) |
640 template = jinja2_env.get_template(_JINJA_TEMPLATE_FILENAME) | 610 # pylint: disable=no-member |
641 # pylint: disable=no-member | 611 processed_template_output = template.render( |
642 processed_template_output = template.render( | 612 test_name=failure_filename, |
643 test_name=failure_filename, | 613 failure_link=failure_link, |
644 failure_link=failure_link, | 614 golden_link=golden_link, |
645 golden_link=golden_link, | 615 diff_link=diff_link) |
646 diff_link=diff_link) | |
647 | 616 |
648 temp_html.write(processed_template_output) | 617 try: |
649 temp_html.flush() | 618 html_results = tempfile.NamedTemporaryFile(delete=False) |
650 html_results_link = google_storage_helper.upload_content_addressed( | 619 html_results.write(processed_template_output) |
651 temp_html.name, | 620 html_results.flush() |
652 bucket=render_tests_bucket, | 621 finally: |
653 content_type='text/html') | 622 html_results_link = self._env.output_manager.ArchiveAndDeleteFile( |
623 html_results.name, | |
624 '%s.html' % failure_filename, 'render_tests', | |
625 output_manager.Datatype.HTML) | |
654 for result in results: | 626 for result in results: |
655 result.SetLink(failure_filename, html_results_link) | 627 result.SetLink(failure_filename, html_results_link) |
656 | 628 |
657 #override | 629 #override |
658 def _ShouldRetry(self, test): | 630 def _ShouldRetry(self, test): |
659 if 'RetryOnFailure' in test.get('annotations', {}): | 631 if 'RetryOnFailure' in test.get('annotations', {}): |
660 return True | 632 return True |
661 | 633 |
662 # TODO(jbudorick): Remove this log message once @RetryOnFailure has been | 634 # TODO(jbudorick): Remove this log message once @RetryOnFailure has been |
663 # enabled for a while. See crbug.com/619055 for more details. | 635 # enabled for a while. See crbug.com/619055 for more details. |
(...skipping 25 matching lines...) Expand all Loading... | |
689 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) | 661 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) |
690 | 662 |
691 return timeout | 663 return timeout |
692 | 664 |
693 def _IsRenderTest(test): | 665 def _IsRenderTest(test): |
694 """Determines if a test or list of tests has a RenderTest amongst them.""" | 666 """Determines if a test or list of tests has a RenderTest amongst them.""" |
695 if not isinstance(test, list): | 667 if not isinstance(test, list): |
696 test = [test] | 668 test = [test] |
697 return any([RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get( | 669 return any([RENDER_TEST_FEATURE_ANNOTATION in t['annotations'].get( |
698 FEATURE_ANNOTATION, {}).get('value', ()) for t in test]) | 670 FEATURE_ANNOTATION, {}).get('value', ()) for t in test]) |
OLD | NEW |