1# Copyright 2015 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5 6import copy 7import logging 8import os 9import re 10 11from devil.android import apk_helper 12from pylib import constants 13from pylib.base import base_test_result 14from pylib.base import test_exception 15from pylib.base import test_instance 16from pylib.constants import host_paths 17from pylib.instrumentation import instrumentation_parser 18from pylib.instrumentation import test_result 19from pylib.symbols import deobfuscator 20from pylib.symbols import stack_symbolizer 21from pylib.utils import dexdump 22from pylib.utils import gold_utils 23from pylib.utils import test_filter 24 25 26with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): 27 import unittest_util # pylint: disable=import-error 28 29# Ref: http://developer.android.com/reference/android/app/Activity.html 30_ACTIVITY_RESULT_CANCELED = 0 31_ACTIVITY_RESULT_OK = -1 32 33_COMMAND_LINE_PARAMETER = 'cmdlinearg-parameter' 34_DEFAULT_ANNOTATIONS = [ 35 'SmallTest', 'MediumTest', 'LargeTest', 'EnormousTest', 'IntegrationTest'] 36# This annotation is for disabled tests that should not be run in Test Reviver. 37_DO_NOT_REVIVE_ANNOTATIONS = ['DoNotRevive', 'Manual'] 38_EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS = [ 39 'DisabledTest', 'FlakyTest', 'Manual'] 40_VALID_ANNOTATIONS = set(_DEFAULT_ANNOTATIONS + _DO_NOT_REVIVE_ANNOTATIONS + 41 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS) 42 43_BASE_INSTRUMENTATION_CLASS_NAME = ( 44 'org.chromium.base.test.BaseChromiumAndroidJUnitRunner') 45 46_SKIP_PARAMETERIZATION = 'SkipCommandLineParameterization' 47_PARAMETERIZED_COMMAND_LINE_FLAGS = 'ParameterizedCommandLineFlags' 48_PARAMETERIZED_COMMAND_LINE_FLAGS_SWITCHES = ( 49 'ParameterizedCommandLineFlags$Switches') 50_NATIVE_CRASH_RE = re.compile('(process|native) crash', re.IGNORECASE) 51 52# The ID of the bundle value Instrumentation uses to report which test index the 53# results are for in a collection of tests. Note that this index is 1-based. 54_BUNDLE_CURRENT_ID = 'current' 55# The ID of the bundle value Instrumentation uses to report the test class. 56_BUNDLE_CLASS_ID = 'class' 57# The ID of the bundle value Instrumentation uses to report the test name. 58_BUNDLE_TEST_ID = 'test' 59# The ID of the bundle value Instrumentation uses to report the crash stack, if 60# the test crashed. 61_BUNDLE_STACK_ID = 'stack' 62 63# The ID of the bundle value Chrome uses to report the test duration. 64_BUNDLE_DURATION_ID = 'duration_ms' 65 66class MissingSizeAnnotationError(test_exception.TestException): 67 def __init__(self, class_name): 68 super().__init__( 69 class_name + 70 ': Test method is missing required size annotation. Add one of: ' + 71 ', '.join('@' + a for a in _VALID_ANNOTATIONS)) 72 73 74class CommandLineParameterizationException(test_exception.TestException): 75 pass 76 77 78def GenerateTestResults(result_code, result_bundle, statuses, duration_ms, 79 device_abi, symbolizer): 80 """Generate test results from |statuses|. 81 82 Args: 83 result_code: The overall status code as an integer. 84 result_bundle: The summary bundle dump as a dict. 85 statuses: A list of 2-tuples containing: 86 - the status code as an integer 87 - the bundle dump as a dict mapping string keys to string values 88 Note that this is the same as the third item in the 3-tuple returned by 89 |_ParseAmInstrumentRawOutput|. 90 duration_ms: The duration of the test in milliseconds. 91 device_abi: The device_abi, which is needed for symbolization. 92 symbolizer: The symbolizer used to symbolize stack. 93 94 Returns: 95 A list containing an instance of InstrumentationTestResult for each test 96 parsed. 97 """ 98 99 results = [] 100 # Results from synthetic ClassName#null tests, which occur from exceptions in 101 # @BeforeClass / @AfterClass. 102 class_failure_results = [] 103 104 def add_result(result): 105 if result.GetName().endswith('#null'): 106 assert result.GetType() == base_test_result.ResultType.FAIL 107 class_failure_results.append(result) 108 else: 109 results.append(result) 110 111 current_result = None 112 cumulative_duration = 0 113 114 for status_code, bundle in statuses: 115 # If the last test was a failure already, don't override that failure with 116 # post-test failures that could be caused by the original failure. 117 if (status_code == instrumentation_parser.STATUS_CODE_BATCH_FAILURE 118 and current_result.GetType() != base_test_result.ResultType.FAIL): 119 current_result.SetType(base_test_result.ResultType.FAIL) 120 _MaybeSetLog(bundle, current_result, symbolizer, device_abi) 121 continue 122 123 if status_code == instrumentation_parser.STATUS_CODE_TEST_DURATION: 124 # For the first result, duration will be set below to the difference 125 # between the reported and actual durations to account for overhead like 126 # starting instrumentation. 127 if results: 128 current_duration = int(bundle.get(_BUNDLE_DURATION_ID, duration_ms)) 129 current_result.SetDuration(current_duration) 130 cumulative_duration += current_duration 131 continue 132 133 test_class = bundle.get(_BUNDLE_CLASS_ID, '') 134 test_method = bundle.get(_BUNDLE_TEST_ID, '') 135 if test_class and test_method: 136 test_name = '%s#%s' % (test_class, test_method) 137 else: 138 continue 139 140 if status_code == instrumentation_parser.STATUS_CODE_START: 141 if current_result: 142 add_result(current_result) 143 current_result = test_result.InstrumentationTestResult( 144 test_name, base_test_result.ResultType.UNKNOWN, duration_ms) 145 else: 146 if status_code == instrumentation_parser.STATUS_CODE_OK: 147 if current_result.GetType() == base_test_result.ResultType.UNKNOWN: 148 current_result.SetType(base_test_result.ResultType.PASS) 149 elif status_code == instrumentation_parser.STATUS_CODE_SKIP: 150 current_result.SetType(base_test_result.ResultType.SKIP) 151 elif status_code == instrumentation_parser.STATUS_CODE_ASSUMPTION_FAILURE: 152 current_result.SetType(base_test_result.ResultType.SKIP) 153 else: 154 if status_code not in (instrumentation_parser.STATUS_CODE_ERROR, 155 instrumentation_parser.STATUS_CODE_FAILURE): 156 logging.error('Unrecognized status code %d. Handling as an error.', 157 status_code) 158 current_result.SetType(base_test_result.ResultType.FAIL) 159 _MaybeSetLog(bundle, current_result, symbolizer, device_abi) 160 161 if current_result: 162 if current_result.GetType() == base_test_result.ResultType.UNKNOWN: 163 crashed = (result_code == _ACTIVITY_RESULT_CANCELED and any( 164 _NATIVE_CRASH_RE.search(l) for l in result_bundle.values())) 165 if crashed: 166 current_result.SetType(base_test_result.ResultType.CRASH) 167 168 add_result(current_result) 169 170 if results: 171 logging.info('Adding cumulative overhead to test %s: %dms', 172 results[0].GetName(), duration_ms - cumulative_duration) 173 results[0].SetDuration(duration_ms - cumulative_duration) 174 175 # Copy failures from @BeforeClass / @AfterClass into all tests that are 176 # marked as passing. 177 for class_result in class_failure_results: 178 prefix = class_result.GetName()[:-len('null')] 179 for result in results: 180 if (result.GetName().startswith(prefix) 181 and result.GetType() == base_test_result.ResultType.PASS): 182 result.SetType(base_test_result.ResultType.FAIL) 183 result.SetLog(class_result.GetLog()) 184 result.SetFailureReason(class_result.GetFailureReason()) 185 186 return results 187 188 189def _MaybeSetLog(bundle, current_result, symbolizer, device_abi): 190 if _BUNDLE_STACK_ID in bundle: 191 stack = bundle[_BUNDLE_STACK_ID] 192 if symbolizer and device_abi: 193 current_result.SetLog('%s\n%s' % (stack, '\n'.join( 194 symbolizer.ExtractAndResolveNativeStackTraces(stack, device_abi)))) 195 else: 196 current_result.SetLog(stack) 197 198 current_result.SetFailureReason(_ParseExceptionMessage(stack)) 199 200 201def _ParseExceptionMessage(stack): 202 """Extracts the exception message from the given stack trace. 203 """ 204 # This interprets stack traces reported via InstrumentationResultPrinter: 205 # https://source.chromium.org/chromium/chromium/src/+/main:third_party/android_support_test_runner/runner/src/main/java/android/support/test/internal/runner/listener/InstrumentationResultPrinter.java;l=181?q=InstrumentationResultPrinter&type=cs 206 # This is a standard Java stack trace, of the form: 207 # <Result of Exception.toString()> 208 # at SomeClass.SomeMethod(...) 209 # at ... 210 lines = stack.split('\n') 211 for i, line in enumerate(lines): 212 if line.startswith('\tat'): 213 return '\n'.join(lines[0:i]) 214 # No call stack found, so assume everything is the exception message. 215 return stack 216 217 218def FilterTests(tests, 219 filter_strs=None, 220 annotations=None, 221 excluded_annotations=None): 222 """Filter a list of tests 223 224 Args: 225 tests: a list of tests. e.g. [ 226 {'annotations": {}, 'class': 'com.example.TestA', 'method':'test1'}, 227 {'annotations": {}, 'class': 'com.example.TestB', 'method':'test2'}] 228 filter_strs: list of googletest-style filter string. 229 annotations: a dict of wanted annotations for test methods. 230 excluded_annotations: a dict of annotations to exclude. 231 232 Return: 233 A list of filtered tests 234 """ 235 236 def test_names_from_pattern(combined_pattern, test_names): 237 patterns = combined_pattern.split(':') 238 239 hashable_patterns = set() 240 filename_patterns = [] 241 for pattern in patterns: 242 if ('*' in pattern or '?' in pattern or '[' in pattern): 243 filename_patterns.append(pattern) 244 else: 245 hashable_patterns.add(pattern) 246 247 filter_test_names = set( 248 unittest_util.FilterTestNames(test_names, ':'.join( 249 filename_patterns))) if len(filename_patterns) > 0 else set() 250 251 for test_name in test_names: 252 if test_name in hashable_patterns: 253 filter_test_names.add(test_name) 254 255 return filter_test_names 256 257 def get_test_names(test): 258 test_names = set() 259 # Allow fully-qualified name as well as an omitted package. 260 unqualified_class_test = { 261 'class': test['class'].split('.')[-1], 262 'method': test['method'] 263 } 264 265 test_name = GetTestName(test, sep='.') 266 test_names.add(test_name) 267 268 unqualified_class_test_name = GetTestName(unqualified_class_test, sep='.') 269 test_names.add(unqualified_class_test_name) 270 271 unique_test_name = GetUniqueTestName(test, sep='.') 272 test_names.add(unique_test_name) 273 274 junit4_test_name = GetTestNameWithoutParameterSuffix(test, sep='.') 275 test_names.add(junit4_test_name) 276 277 unqualified_junit4_test_name = GetTestNameWithoutParameterSuffix( 278 unqualified_class_test, sep='.') 279 test_names.add(unqualified_junit4_test_name) 280 return test_names 281 282 def get_tests_from_names(tests, test_names, tests_to_names): 283 ''' Returns the tests for which the given names apply 284 285 Args: 286 tests: a list of tests. e.g. [ 287 {'annotations": {}, 'class': 'com.example.TestA', 'method':'test1'}, 288 {'annotations": {}, 'class': 'com.example.TestB', 'method':'test2'}] 289 test_names: a collection of names determining tests to return. 290 291 Return: 292 A list of tests that match the given test names 293 ''' 294 filtered_tests = [] 295 for t in tests: 296 current_test_names = tests_to_names[id(t)] 297 298 for current_test_name in current_test_names: 299 if current_test_name in test_names: 300 filtered_tests.append(t) 301 break 302 303 return filtered_tests 304 305 def remove_tests_from_names(tests, remove_test_names, tests_to_names): 306 ''' Returns the tests from the given list with given names removed 307 308 Args: 309 tests: a list of tests. e.g. [ 310 {'annotations": {}, 'class': 'com.example.TestA', 'method':'test1'}, 311 {'annotations": {}, 'class': 'com.example.TestB', 'method':'test2'}] 312 remove_test_names: a collection of names determining tests to remove. 313 tests_to_names: a dcitionary of test ids to a collection of applicable 314 names for that test 315 316 Return: 317 A list of tests that don't match the given test names 318 ''' 319 filtered_tests = [] 320 321 for t in tests: 322 for name in tests_to_names[id(t)]: 323 if name in remove_test_names: 324 break 325 else: 326 filtered_tests.append(t) 327 return filtered_tests 328 329 def gtests_filter(tests, combined_filters): 330 ''' Returns the tests after the combined_filters have been applied 331 332 Args: 333 tests: a list of tests. e.g. [ 334 {'annotations": {}, 'class': 'com.example.TestA', 'method':'test1'}, 335 {'annotations": {}, 'class': 'com.example.TestB', 'method':'test2'}] 336 combined_filters: the filter string representing tests to exclude 337 338 Return: 339 A list of tests that should still be included after the combined_filters 340 are applied to their names 341 ''' 342 343 if not combined_filters: 344 return tests 345 346 # Collect all test names 347 all_test_names = set() 348 tests_to_names = {} 349 for t in tests: 350 tests_to_names[id(t)] = get_test_names(t) 351 for name in tests_to_names[id(t)]: 352 all_test_names.add(name) 353 354 for combined_filter in combined_filters: 355 pattern_groups = combined_filter.split('-') 356 negative_pattern = pattern_groups[1] if len(pattern_groups) > 1 else None 357 positive_pattern = pattern_groups[0] 358 if positive_pattern: 359 # Only use the test names that match the positive pattern 360 positive_test_names = test_names_from_pattern(positive_pattern, 361 all_test_names) 362 tests = get_tests_from_names(tests, positive_test_names, tests_to_names) 363 364 if negative_pattern: 365 # Remove any test the negative filter matches 366 remove_names = test_names_from_pattern(negative_pattern, all_test_names) 367 tests = remove_tests_from_names(tests, remove_names, tests_to_names) 368 369 return tests 370 371 def annotation_filter(all_annotations): 372 if not annotations: 373 return True 374 return any_annotation_matches(annotations, all_annotations) 375 376 def excluded_annotation_filter(all_annotations): 377 if not excluded_annotations: 378 return True 379 return not any_annotation_matches(excluded_annotations, 380 all_annotations) 381 382 def any_annotation_matches(filter_annotations, all_annotations): 383 return any( 384 ak in all_annotations 385 and annotation_value_matches(av, all_annotations[ak]) 386 for ak, av in filter_annotations) 387 388 def annotation_value_matches(filter_av, av): 389 if filter_av is None: 390 return True 391 if isinstance(av, dict): 392 tav_from_dict = av['value'] 393 # If tav_from_dict is an int, the 'in' operator breaks, so convert 394 # filter_av and manually compare. See https://crbug.com/1019707 395 if isinstance(tav_from_dict, int): 396 return int(filter_av) == tav_from_dict 397 return filter_av in tav_from_dict 398 if isinstance(av, list): 399 return filter_av in av 400 return filter_av == av 401 402 return_tests = [] 403 for t in gtests_filter(tests, filter_strs): 404 # Enforce that all tests declare their size. 405 if not any(a in _VALID_ANNOTATIONS for a in t['annotations']): 406 raise MissingSizeAnnotationError(GetTestName(t)) 407 408 if (not annotation_filter(t['annotations']) 409 or not excluded_annotation_filter(t['annotations'])): 410 continue 411 return_tests.append(t) 412 413 return return_tests 414 415 416def GetTestsFromDexdump(test_apk): 417 dex_dumps = dexdump.Dump(test_apk) 418 tests = [] 419 420 def get_test_methods(methods, annotations): 421 test_methods = [] 422 423 for method in methods: 424 if method.startswith('test'): 425 method_annotations = annotations.get(method, {}) 426 427 # Dexdump used to not return any annotation info 428 # So MediumTest annotation was added to all methods 429 # Preserving this behaviour by adding MediumTest if none of the 430 # size annotations are included in these annotations 431 if not any(valid in method_annotations for valid in _VALID_ANNOTATIONS): 432 method_annotations.update({'MediumTest': None}) 433 434 test_methods.append({ 435 'method': method, 436 'annotations': method_annotations 437 }) 438 439 return test_methods 440 441 for dump in dex_dumps: 442 for package_name, package_info in dump.items(): 443 for class_name, class_info in package_info['classes'].items(): 444 if class_name.endswith('Test') and not class_info['is_abstract']: 445 classAnnotations, methodsAnnotations = class_info['annotations'] 446 tests.append({ 447 'class': 448 '%s.%s' % (package_name, class_name), 449 'annotations': 450 classAnnotations, 451 'methods': 452 get_test_methods(class_info['methods'], methodsAnnotations), 453 }) 454 return tests 455 456 457def GetTestName(test, sep='#'): 458 """Gets the name of the given test. 459 460 Note that this may return the same name for more than one test, e.g. if a 461 test is being run multiple times with different parameters. 462 463 Args: 464 test: the instrumentation test dict. 465 sep: the character(s) that should join the class name and the method name. 466 Returns: 467 The test name as a string. 468 """ 469 test_name = '%s%s%s' % (test['class'], sep, test['method']) 470 assert not any(char in test_name for char in ' *-:'), ( 471 'The test name must not contain any of the characters in " *-:". See ' 472 'https://crbug.com/912199') 473 return test_name 474 475 476def GetTestNameWithoutParameterSuffix(test, sep='#', parameterization_sep='__'): 477 """Gets the name of the given JUnit4 test without parameter suffix. 478 479 For most WebView JUnit4 javatests, each test is parameterizatized with 480 "__sandboxed_mode" to run in both non-sandboxed mode and sandboxed mode. 481 482 This function returns the name of the test without parameterization 483 so test filters can match both parameterized and non-parameterized tests. 484 485 Args: 486 test: the instrumentation test dict. 487 sep: the character(s) that should join the class name and the method name. 488 parameterization_sep: the character(s) that separate method name and method 489 parameterization suffix. 490 Returns: 491 The test name without parameter suffix as a string. 492 """ 493 name = GetTestName(test, sep=sep) 494 return name.split(parameterization_sep)[0] 495 496 497def GetUniqueTestName(test, sep='#'): 498 """Gets the unique name of the given test. 499 500 This will include text to disambiguate between tests for which GetTestName 501 would return the same name. 502 503 Args: 504 test: the instrumentation test dict. 505 sep: the character(s) that should join the class name and the method name. 506 Returns: 507 The unique test name as a string. 508 """ 509 display_name = GetTestName(test, sep=sep) 510 if test.get('flags', [None])[0]: 511 sanitized_flags = [x.replace('-', '_') for x in test['flags']] 512 display_name = '%s_with_%s' % (display_name, '_'.join(sanitized_flags)) 513 514 assert not any(char in display_name for char in ' *-:'), ( 515 'The test name must not contain any of the characters in " *-:". See ' 516 'https://crbug.com/912199') 517 518 return display_name 519 520 521class InstrumentationTestInstance(test_instance.TestInstance): 522 523 def __init__(self, args, data_deps_delegate, error_func): 524 super().__init__() 525 526 self._additional_apks = [] 527 self._additional_apexs = [] 528 self._forced_queryable_additional_apks = [] 529 self._instant_additional_apks = [] 530 self._apk_under_test = None 531 self._apk_under_test_incremental_install_json = None 532 self._modules = None 533 self._fake_modules = None 534 self._additional_locales = None 535 self._package_info = None 536 self._suite = None 537 self._test_apk = None 538 self._test_apk_as_instant = False 539 self._test_apk_incremental_install_json = None 540 self._test_package = None 541 self._junit4_runner_class = None 542 self._uses_base_instrumentation = None 543 self._has_chromium_test_listener = None 544 self._test_support_apk = None 545 self._initializeApkAttributes(args, error_func) 546 547 self._data_deps = None 548 self._data_deps_delegate = None 549 self._runtime_deps_path = None 550 self._variations_test_seed_path = args.variations_test_seed_path 551 self._webview_variations_test_seed_path = ( 552 args.webview_variations_test_seed_path) 553 self._store_data_dependencies_in_temp = False 554 self._initializeDataDependencyAttributes(args, data_deps_delegate) 555 self._annotations = None 556 self._excluded_annotations = None 557 self._has_external_annotation_filters = None 558 self._test_filters = None 559 self._initializeTestFilterAttributes(args) 560 561 self._run_setup_commands = [] 562 self._run_teardown_commands = [] 563 self._initializeSetupTeardownCommandAttributes(args) 564 565 self._flags = None 566 self._use_apk_under_test_flags_file = False 567 self._webview_flags = args.webview_command_line_arg 568 self._initializeFlagAttributes(args) 569 570 self._screenshot_dir = None 571 self._timeout_scale = None 572 self._wait_for_java_debugger = None 573 self._initializeTestControlAttributes(args) 574 575 self._coverage_directory = None 576 self._initializeTestCoverageAttributes(args) 577 578 self._store_tombstones = False 579 self._symbolizer = None 580 self._enable_breakpad_dump = False 581 self._proguard_mapping_path = None 582 self._deobfuscator = None 583 self._initializeLogAttributes(args) 584 585 self._replace_system_package = None 586 self._initializeReplaceSystemPackageAttributes(args) 587 588 self._system_packages_to_remove = None 589 self._initializeSystemPackagesToRemoveAttributes(args) 590 591 self._use_voice_interaction_service = None 592 self._initializeUseVoiceInteractionService(args) 593 594 self._use_webview_provider = None 595 self._initializeUseWebviewProviderAttributes(args) 596 597 self._skia_gold_properties = None 598 self._initializeSkiaGoldAttributes(args) 599 600 self._test_launcher_batch_limit = None 601 self._initializeTestLauncherAttributes(args) 602 603 self._approve_app_links_domain = None 604 self._approve_app_links_package = None 605 self._initializeApproveAppLinksAttributes(args) 606 607 self._webview_process_mode = args.webview_process_mode 608 609 self._wpr_enable_record = args.wpr_enable_record 610 611 self._external_shard_index = args.test_launcher_shard_index 612 self._total_external_shards = args.test_launcher_total_shards 613 614 self._is_unit_test = False 615 self._initializeUnitTestFlag(args) 616 617 self._run_disabled = args.run_disabled 618 619 def _initializeApkAttributes(self, args, error_func): 620 if args.apk_under_test: 621 apk_under_test_path = args.apk_under_test 622 if (not args.apk_under_test.endswith('.apk') 623 and not args.apk_under_test.endswith('.apks')): 624 apk_under_test_path = os.path.join( 625 constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR, 626 '%s.apk' % args.apk_under_test) 627 628 # TODO(jbudorick): Move the realpath up to the argument parser once 629 # APK-by-name is no longer supported. 630 apk_under_test_path = os.path.realpath(apk_under_test_path) 631 632 if not os.path.exists(apk_under_test_path): 633 error_func('Unable to find APK under test: %s' % apk_under_test_path) 634 635 self._apk_under_test = apk_helper.ToHelper(apk_under_test_path) 636 637 test_apk_path = args.test_apk 638 if (not args.test_apk.endswith('.apk') 639 and not args.test_apk.endswith('.apks')): 640 test_apk_path = os.path.join( 641 constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR, 642 '%s.apk' % args.test_apk) 643 644 # TODO(jbudorick): Move the realpath up to the argument parser once 645 # APK-by-name is no longer supported. 646 test_apk_path = os.path.realpath(test_apk_path) 647 648 if not os.path.exists(test_apk_path): 649 error_func('Unable to find test APK: %s' % test_apk_path) 650 651 self._test_apk = apk_helper.ToHelper(test_apk_path) 652 self._suite = os.path.splitext(os.path.basename(args.test_apk))[0] 653 654 self._test_apk_as_instant = args.test_apk_as_instant 655 656 self._apk_under_test_incremental_install_json = ( 657 args.apk_under_test_incremental_install_json) 658 self._test_apk_incremental_install_json = ( 659 args.test_apk_incremental_install_json) 660 661 if self._test_apk_incremental_install_json: 662 assert self._suite.endswith('_incremental') 663 self._suite = self._suite[:-len('_incremental')] 664 665 self._modules = args.modules 666 self._fake_modules = args.fake_modules 667 self._additional_locales = args.additional_locales 668 669 self._test_support_apk = apk_helper.ToHelper(os.path.join( 670 constants.GetOutDirectory(), constants.SDK_BUILD_TEST_JAVALIB_DIR, 671 '%sSupport.apk' % self._suite)) 672 673 self._test_package = self._test_apk.GetPackageName() 674 all_instrumentations = self._test_apk.GetAllInstrumentations() 675 676 if len(all_instrumentations) > 1: 677 logging.warning('This test apk has more than one instrumentation') 678 679 self._junit4_runner_class = (all_instrumentations[0]['android:name'] 680 if all_instrumentations else None) 681 682 test_apk_metadata = dict(self._test_apk.GetAllMetadata()) 683 self._has_chromium_test_listener = bool( 684 test_apk_metadata.get('org.chromium.hasTestRunListener')) 685 if self._junit4_runner_class: 686 if self._test_apk_incremental_install_json: 687 for name, value in test_apk_metadata.items(): 688 if (name.startswith('incremental-install-instrumentation-') 689 and value == _BASE_INSTRUMENTATION_CLASS_NAME): 690 self._uses_base_instrumentation = True 691 break 692 else: 693 self._uses_base_instrumentation = ( 694 self._junit4_runner_class == _BASE_INSTRUMENTATION_CLASS_NAME) 695 696 self._package_info = None 697 if self._apk_under_test: 698 package_under_test = self._apk_under_test.GetPackageName() 699 for package_info in constants.PACKAGE_INFO.values(): 700 if package_under_test == package_info.package: 701 self._package_info = package_info 702 break 703 if not self._package_info: 704 logging.warning( 705 'Unable to find package info for %s. ' 706 '(This may just mean that the test package is ' 707 'currently being installed.)', self._test_package) 708 709 for x in set(args.additional_apks + args.forced_queryable_additional_apks + 710 args.instant_additional_apks): 711 if not os.path.exists(x): 712 error_func('Unable to find additional APK: %s' % x) 713 714 apk = apk_helper.ToHelper(x) 715 self._additional_apks.append(apk) 716 717 if x in args.forced_queryable_additional_apks: 718 self._forced_queryable_additional_apks.append(apk) 719 720 if x in args.instant_additional_apks: 721 self._instant_additional_apks.append(apk) 722 723 self._additional_apexs = args.additional_apexs 724 725 def _initializeDataDependencyAttributes(self, args, data_deps_delegate): 726 self._data_deps = [] 727 self._data_deps_delegate = data_deps_delegate 728 self._store_data_dependencies_in_temp = args.store_data_dependencies_in_temp 729 self._runtime_deps_path = args.runtime_deps_path 730 731 if not self._runtime_deps_path: 732 logging.warning('No data dependencies will be pushed.') 733 734 def _initializeTestFilterAttributes(self, args): 735 self._test_filters = test_filter.InitializeFiltersFromArgs(args) 736 self._has_external_annotation_filters = bool(args.annotation_str 737 or args.exclude_annotation_str) 738 739 def annotation_element(a): 740 a = a.split('=', 1) 741 return (a[0], a[1] if len(a) == 2 else None) 742 743 if args.annotation_str: 744 self._annotations = [ 745 annotation_element(a) for a in args.annotation_str.split(',')] 746 elif not self._test_filters: 747 self._annotations = [ 748 annotation_element(a) for a in _DEFAULT_ANNOTATIONS] 749 else: 750 self._annotations = [] 751 752 if args.exclude_annotation_str: 753 self._excluded_annotations = [ 754 annotation_element(a) for a in args.exclude_annotation_str.split(',')] 755 else: 756 self._excluded_annotations = [] 757 758 requested_annotations = set(a[0] for a in self._annotations) 759 if args.run_disabled: 760 self._excluded_annotations.extend( 761 annotation_element(a) for a in _DO_NOT_REVIVE_ANNOTATIONS 762 if a not in requested_annotations) 763 else: 764 self._excluded_annotations.extend( 765 annotation_element(a) for a in _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS 766 if a not in requested_annotations) 767 768 def _initializeSetupTeardownCommandAttributes(self, args): 769 self._run_setup_commands = args.run_setup_commands 770 self._run_teardown_commands = args.run_teardown_commands 771 772 def _initializeFlagAttributes(self, args): 773 self._use_apk_under_test_flags_file = args.use_apk_under_test_flags_file 774 self._flags = ['--enable-test-intents'] 775 if args.command_line_flags: 776 self._flags.extend(args.command_line_flags) 777 if args.device_flags_file: 778 with open(args.device_flags_file) as device_flags_file: 779 stripped_lines = (l.strip() for l in device_flags_file) 780 self._flags.extend(flag for flag in stripped_lines if flag) 781 if args.strict_mode and args.strict_mode != 'off' and ( 782 # TODO(yliuyliu): Turn on strict mode for coverage once 783 # crbug/1006397 is fixed. 784 not args.coverage_dir): 785 self._flags.append('--strict-mode=' + args.strict_mode) 786 787 def _initializeTestControlAttributes(self, args): 788 self._screenshot_dir = args.screenshot_dir 789 self._timeout_scale = args.timeout_scale or 1 790 self._wait_for_java_debugger = args.wait_for_java_debugger 791 792 def _initializeTestCoverageAttributes(self, args): 793 self._coverage_directory = args.coverage_dir 794 795 def _initializeLogAttributes(self, args): 796 self._enable_breakpad_dump = args.enable_breakpad_dump 797 self._proguard_mapping_path = args.proguard_mapping_path 798 self._store_tombstones = args.store_tombstones 799 self._symbolizer = stack_symbolizer.Symbolizer( 800 self.apk_under_test.path if self.apk_under_test else None) 801 802 def _initializeReplaceSystemPackageAttributes(self, args): 803 if (not hasattr(args, 'replace_system_package') 804 or not args.replace_system_package): 805 return 806 self._replace_system_package = args.replace_system_package 807 808 def _initializeSystemPackagesToRemoveAttributes(self, args): 809 if (not hasattr(args, 'system_packages_to_remove') 810 or not args.system_packages_to_remove): 811 return 812 self._system_packages_to_remove = args.system_packages_to_remove 813 814 def _initializeUseVoiceInteractionService(self, args): 815 if (not hasattr(args, 'use_voice_interaction_service') 816 or not args.use_voice_interaction_service): 817 return 818 self._use_voice_interaction_service = args.use_voice_interaction_service 819 820 def _initializeUseWebviewProviderAttributes(self, args): 821 if (not hasattr(args, 'use_webview_provider') 822 or not args.use_webview_provider): 823 return 824 self._use_webview_provider = args.use_webview_provider 825 826 def _initializeSkiaGoldAttributes(self, args): 827 self._skia_gold_properties = gold_utils.AndroidSkiaGoldProperties(args) 828 829 def _initializeTestLauncherAttributes(self, args): 830 if hasattr(args, 'test_launcher_batch_limit'): 831 self._test_launcher_batch_limit = args.test_launcher_batch_limit 832 833 def _initializeApproveAppLinksAttributes(self, args): 834 if (not hasattr(args, 'approve_app_links') or not args.approve_app_links): 835 return 836 837 # The argument will be formatted as com.android.thing:www.example.com . 838 app_links = args.approve_app_links.split(':') 839 840 if (len(app_links) != 2 or not app_links[0] or not app_links[1]): 841 logging.warning('--approve_app_links option provided, but malformed.') 842 return 843 844 self._approve_app_links_package = app_links[0] 845 self._approve_app_links_domain = app_links[1] 846 847 def _initializeUnitTestFlag(self, args): 848 self._is_unit_test = args.is_unit_test 849 850 @property 851 def additional_apks(self): 852 return self._additional_apks 853 854 @property 855 def additional_apexs(self): 856 return self._additional_apexs 857 858 @property 859 def apk_under_test(self): 860 return self._apk_under_test 861 862 @property 863 def apk_under_test_incremental_install_json(self): 864 return self._apk_under_test_incremental_install_json 865 866 @property 867 def approve_app_links_package(self): 868 return self._approve_app_links_package 869 870 @property 871 def approve_app_links_domain(self): 872 return self._approve_app_links_domain 873 874 @property 875 def modules(self): 876 return self._modules 877 878 @property 879 def fake_modules(self): 880 return self._fake_modules 881 882 @property 883 def additional_locales(self): 884 return self._additional_locales 885 886 @property 887 def coverage_directory(self): 888 return self._coverage_directory 889 890 @property 891 def enable_breakpad_dump(self): 892 return self._enable_breakpad_dump 893 894 @property 895 def external_shard_index(self): 896 return self._external_shard_index 897 898 @property 899 def flags(self): 900 return self._flags[:] 901 902 @property 903 def is_unit_test(self): 904 return self._is_unit_test 905 906 @property 907 def junit4_runner_class(self): 908 return self._junit4_runner_class 909 910 @property 911 def has_chromium_test_listener(self): 912 return self._has_chromium_test_listener 913 914 @property 915 def has_external_annotation_filters(self): 916 return self._has_external_annotation_filters 917 918 @property 919 def uses_base_instrumentation(self): 920 return self._uses_base_instrumentation 921 922 @property 923 def package_info(self): 924 return self._package_info 925 926 @property 927 def replace_system_package(self): 928 return self._replace_system_package 929 930 @property 931 def run_setup_commands(self): 932 return self._run_setup_commands 933 934 @property 935 def run_teardown_commands(self): 936 return self._run_teardown_commands 937 938 @property 939 def use_voice_interaction_service(self): 940 return self._use_voice_interaction_service 941 942 @property 943 def use_webview_provider(self): 944 return self._use_webview_provider 945 946 @property 947 def webview_flags(self): 948 return self._webview_flags[:] 949 950 @property 951 def screenshot_dir(self): 952 return self._screenshot_dir 953 954 @property 955 def skia_gold_properties(self): 956 return self._skia_gold_properties 957 958 @property 959 def store_data_dependencies_in_temp(self): 960 return self._store_data_dependencies_in_temp 961 962 @property 963 def store_tombstones(self): 964 return self._store_tombstones 965 966 @property 967 def suite(self): 968 return self._suite 969 970 @property 971 def symbolizer(self): 972 return self._symbolizer 973 974 @property 975 def system_packages_to_remove(self): 976 return self._system_packages_to_remove 977 978 @property 979 def test_apk(self): 980 return self._test_apk 981 982 @property 983 def test_apk_as_instant(self): 984 return self._test_apk_as_instant 985 986 @property 987 def test_apk_incremental_install_json(self): 988 return self._test_apk_incremental_install_json 989 990 @property 991 def test_filters(self): 992 return self._test_filters 993 994 @property 995 def test_launcher_batch_limit(self): 996 return self._test_launcher_batch_limit 997 998 @property 999 def test_support_apk(self): 1000 return self._test_support_apk 1001 1002 @property 1003 def test_package(self): 1004 return self._test_package 1005 1006 @property 1007 def timeout_scale(self): 1008 return self._timeout_scale 1009 1010 @property 1011 def total_external_shards(self): 1012 return self._total_external_shards 1013 1014 @property 1015 def use_apk_under_test_flags_file(self): 1016 return self._use_apk_under_test_flags_file 1017 1018 @property 1019 def variations_test_seed_path(self): 1020 return self._variations_test_seed_path 1021 1022 @property 1023 def webview_variations_test_seed_path(self): 1024 return self._webview_variations_test_seed_path 1025 1026 @property 1027 def wait_for_java_debugger(self): 1028 return self._wait_for_java_debugger 1029 1030 @property 1031 def wpr_record_mode(self): 1032 return self._wpr_enable_record 1033 1034 @property 1035 def webview_process_mode(self): 1036 return self._webview_process_mode 1037 1038 @property 1039 def wpr_replay_mode(self): 1040 return not self._wpr_enable_record 1041 1042 #override 1043 def TestType(self): 1044 return 'instrumentation' 1045 1046 #override 1047 def GetPreferredAbis(self): 1048 # We could alternatively take the intersection of what they all support, 1049 # but it should never be the case that they support different things. 1050 apks = [self._test_apk, self._apk_under_test] + self._additional_apks 1051 for apk in apks: 1052 if apk: 1053 ret = apk.GetAbis() 1054 if ret: 1055 return ret 1056 return [] 1057 1058 #override 1059 def SetUp(self): 1060 self._data_deps.extend( 1061 self._data_deps_delegate(self._runtime_deps_path)) 1062 if self._proguard_mapping_path: 1063 self._deobfuscator = deobfuscator.DeobfuscatorPool( 1064 self._proguard_mapping_path) 1065 1066 def GetDataDependencies(self): 1067 return self._data_deps 1068 1069 def GetRunDisabledFlag(self): 1070 return self._run_disabled 1071 1072 def MaybeDeobfuscateLines(self, lines): 1073 if not self._deobfuscator: 1074 return lines 1075 return self._deobfuscator.TransformLines(lines) 1076 1077 def ProcessRawTests(self, raw_tests): 1078 inflated_tests = self._ParameterizeTestsWithFlags( 1079 self._InflateTests(raw_tests)) 1080 filtered_tests = FilterTests(inflated_tests, self._test_filters, 1081 self._annotations, self._excluded_annotations) 1082 if self._test_filters and not filtered_tests: 1083 for t in inflated_tests: 1084 logging.debug(' %s', GetUniqueTestName(t)) 1085 logging.warning('Unmatched Filters: %s', self._test_filters) 1086 return filtered_tests 1087 1088 def IsApkForceQueryable(self, apk): 1089 return apk in self._forced_queryable_additional_apks 1090 1091 def IsApkInstant(self, apk): 1092 return apk in self._instant_additional_apks 1093 1094 # pylint: disable=no-self-use 1095 def _InflateTests(self, tests): 1096 inflated_tests = [] 1097 for clazz in tests: 1098 for method in clazz['methods']: 1099 annotations = dict(clazz['annotations']) 1100 annotations.update(method['annotations']) 1101 1102 # Preserve historic default. 1103 if (not self._uses_base_instrumentation 1104 and not any(a in _VALID_ANNOTATIONS for a in annotations)): 1105 annotations['MediumTest'] = None 1106 1107 inflated_tests.append({ 1108 'class': clazz['class'], 1109 'method': method['method'], 1110 'annotations': annotations, 1111 }) 1112 return inflated_tests 1113 1114 def _ParameterizeTestsWithFlags(self, tests): 1115 1116 def _checkParameterization(annotations): 1117 types = [ 1118 _PARAMETERIZED_COMMAND_LINE_FLAGS_SWITCHES, 1119 _PARAMETERIZED_COMMAND_LINE_FLAGS, 1120 ] 1121 if types[0] in annotations and types[1] in annotations: 1122 raise CommandLineParameterizationException( 1123 'Multiple command-line parameterization types: {}.'.format( 1124 ', '.join(types))) 1125 1126 def _switchesToFlags(switches): 1127 return ['--{}'.format(s) for s in switches if s] 1128 1129 def _annotationToSwitches(clazz, methods): 1130 if clazz == _PARAMETERIZED_COMMAND_LINE_FLAGS_SWITCHES: 1131 return [methods['value']] 1132 if clazz == _PARAMETERIZED_COMMAND_LINE_FLAGS: 1133 list_of_switches = [] 1134 for annotation in methods['value']: 1135 for c, m in annotation.items(): 1136 list_of_switches += _annotationToSwitches(c, m) 1137 return list_of_switches 1138 return [] 1139 1140 def _setTestFlags(test, flags): 1141 if flags: 1142 test['flags'] = flags 1143 elif 'flags' in test: 1144 del test['flags'] 1145 1146 new_tests = [] 1147 for t in tests: 1148 annotations = t['annotations'] 1149 list_of_switches = [] 1150 _checkParameterization(annotations) 1151 if _SKIP_PARAMETERIZATION not in annotations: 1152 for clazz, methods in annotations.items(): 1153 list_of_switches += _annotationToSwitches(clazz, methods) 1154 if list_of_switches: 1155 _setTestFlags(t, _switchesToFlags(list_of_switches[0])) 1156 for p in list_of_switches[1:]: 1157 parameterized_t = copy.copy(t) 1158 _setTestFlags(parameterized_t, _switchesToFlags(p)) 1159 new_tests.append(parameterized_t) 1160 return tests + new_tests 1161 1162 @staticmethod 1163 def GenerateTestResults(result_code, result_bundle, statuses, duration_ms, 1164 device_abi, symbolizer): 1165 return GenerateTestResults(result_code, result_bundle, statuses, 1166 duration_ms, device_abi, symbolizer) 1167 1168 #override 1169 def TearDown(self): 1170 self.symbolizer.CleanUp() 1171 if self._deobfuscator: 1172 self._deobfuscator.Close() 1173 self._deobfuscator = None 1174