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 3# found in the LICENSE file. 4 5import copy 6import logging 7import os 8import pickle 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 test_result 18from pylib.instrumentation import instrumentation_parser 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 instrumentation_tracing 24from pylib.utils import proguard 25from pylib.utils import shared_preference_utils 26from pylib.utils import test_filter 27 28 29with host_paths.SysPath(host_paths.BUILD_COMMON_PATH): 30 import unittest_util # pylint: disable=import-error 31 32# Ref: http://developer.android.com/reference/android/app/Activity.html 33_ACTIVITY_RESULT_CANCELED = 0 34_ACTIVITY_RESULT_OK = -1 35 36_COMMAND_LINE_PARAMETER = 'cmdlinearg-parameter' 37_DEFAULT_ANNOTATIONS = [ 38 'SmallTest', 'MediumTest', 'LargeTest', 'EnormousTest', 'IntegrationTest'] 39_EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS = [ 40 'DisabledTest', 'FlakyTest', 'Manual'] 41_VALID_ANNOTATIONS = set(_DEFAULT_ANNOTATIONS + 42 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS) 43 44_EXTRA_DRIVER_TEST_LIST = ( 45 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList') 46_EXTRA_DRIVER_TEST_LIST_FILE = ( 47 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestListFile') 48_EXTRA_DRIVER_TARGET_PACKAGE = ( 49 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetPackage') 50_EXTRA_DRIVER_TARGET_CLASS = ( 51 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetClass') 52_EXTRA_TIMEOUT_SCALE = ( 53 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TimeoutScale') 54_TEST_LIST_JUNIT4_RUNNERS = [ 55 'org.chromium.base.test.BaseChromiumAndroidJUnitRunner'] 56 57_SKIP_PARAMETERIZATION = 'SkipCommandLineParameterization' 58_PARAMETERIZED_COMMAND_LINE_FLAGS = 'ParameterizedCommandLineFlags' 59_PARAMETERIZED_COMMAND_LINE_FLAGS_SWITCHES = ( 60 'ParameterizedCommandLineFlags$Switches') 61_NATIVE_CRASH_RE = re.compile('(process|native) crash', re.IGNORECASE) 62_PICKLE_FORMAT_VERSION = 12 63 64 65class MissingSizeAnnotationError(test_exception.TestException): 66 def __init__(self, class_name): 67 super(MissingSizeAnnotationError, self).__init__(class_name + 68 ': Test method is missing required size annotation. Add one of: ' + 69 ', '.join('@' + a for a in _VALID_ANNOTATIONS)) 70 71 72class CommandLineParameterizationException(test_exception.TestException): 73 74 def __init__(self, msg): 75 super(CommandLineParameterizationException, self).__init__(msg) 76 77 78class TestListPickleException(test_exception.TestException): 79 pass 80 81 82# TODO(jbudorick): Make these private class methods of 83# InstrumentationTestInstance once the instrumentation junit3_runner_class is 84# deprecated. 85def ParseAmInstrumentRawOutput(raw_output): 86 """Parses the output of an |am instrument -r| call. 87 88 Args: 89 raw_output: the output of an |am instrument -r| call as a list of lines 90 Returns: 91 A 3-tuple containing: 92 - the instrumentation code as an integer 93 - the instrumentation result as a list of lines 94 - the instrumentation statuses received as a list of 2-tuples 95 containing: 96 - the status code as an integer 97 - the bundle dump as a dict mapping string keys to a list of 98 strings, one for each line. 99 """ 100 parser = instrumentation_parser.InstrumentationParser(raw_output) 101 statuses = list(parser.IterStatus()) 102 code, bundle = parser.GetResult() 103 return (code, bundle, statuses) 104 105 106def GenerateTestResults( 107 result_code, result_bundle, statuses, start_ms, duration_ms, device_abi, 108 symbolizer): 109 """Generate test results from |statuses|. 110 111 Args: 112 result_code: The overall status code as an integer. 113 result_bundle: The summary bundle dump as a dict. 114 statuses: A list of 2-tuples containing: 115 - the status code as an integer 116 - the bundle dump as a dict mapping string keys to string values 117 Note that this is the same as the third item in the 3-tuple returned by 118 |_ParseAmInstrumentRawOutput|. 119 start_ms: The start time of the test in milliseconds. 120 duration_ms: The duration of the test in milliseconds. 121 device_abi: The device_abi, which is needed for symbolization. 122 symbolizer: The symbolizer used to symbolize stack. 123 124 Returns: 125 A list containing an instance of InstrumentationTestResult for each test 126 parsed. 127 """ 128 129 results = [] 130 131 current_result = None 132 133 for status_code, bundle in statuses: 134 test_class = bundle.get('class', '') 135 test_method = bundle.get('test', '') 136 if test_class and test_method: 137 test_name = '%s#%s' % (test_class, test_method) 138 else: 139 continue 140 141 if status_code == instrumentation_parser.STATUS_CODE_START: 142 if current_result: 143 results.append(current_result) 144 current_result = test_result.InstrumentationTestResult( 145 test_name, base_test_result.ResultType.UNKNOWN, start_ms, duration_ms) 146 else: 147 if status_code == instrumentation_parser.STATUS_CODE_OK: 148 if bundle.get('test_skipped', '').lower() in ('true', '1', 'yes'): 149 current_result.SetType(base_test_result.ResultType.SKIP) 150 elif current_result.GetType() == base_test_result.ResultType.UNKNOWN: 151 current_result.SetType(base_test_result.ResultType.PASS) 152 elif status_code == instrumentation_parser.STATUS_CODE_SKIP: 153 current_result.SetType(base_test_result.ResultType.SKIP) 154 elif status_code == instrumentation_parser.STATUS_CODE_ASSUMPTION_FAILURE: 155 current_result.SetType(base_test_result.ResultType.SKIP) 156 else: 157 if status_code not in (instrumentation_parser.STATUS_CODE_ERROR, 158 instrumentation_parser.STATUS_CODE_FAILURE): 159 logging.error('Unrecognized status code %d. Handling as an error.', 160 status_code) 161 current_result.SetType(base_test_result.ResultType.FAIL) 162 if 'stack' in bundle: 163 if symbolizer and device_abi: 164 current_result.SetLog( 165 '%s\n%s' % ( 166 bundle['stack'], 167 '\n'.join(symbolizer.ExtractAndResolveNativeStackTraces( 168 bundle['stack'], device_abi)))) 169 else: 170 current_result.SetLog(bundle['stack']) 171 172 if current_result: 173 if current_result.GetType() == base_test_result.ResultType.UNKNOWN: 174 crashed = (result_code == _ACTIVITY_RESULT_CANCELED 175 and any(_NATIVE_CRASH_RE.search(l) 176 for l in result_bundle.itervalues())) 177 if crashed: 178 current_result.SetType(base_test_result.ResultType.CRASH) 179 180 results.append(current_result) 181 182 return results 183 184 185def FilterTests(tests, filter_str=None, annotations=None, 186 excluded_annotations=None): 187 """Filter a list of tests 188 189 Args: 190 tests: a list of tests. e.g. [ 191 {'annotations": {}, 'class': 'com.example.TestA', 'method':'test1'}, 192 {'annotations": {}, 'class': 'com.example.TestB', 'method':'test2'}] 193 filter_str: googletest-style filter string. 194 annotations: a dict of wanted annotations for test methods. 195 exclude_annotations: a dict of annotations to exclude. 196 197 Return: 198 A list of filtered tests 199 """ 200 def gtest_filter(t): 201 if not filter_str: 202 return True 203 # Allow fully-qualified name as well as an omitted package. 204 unqualified_class_test = { 205 'class': t['class'].split('.')[-1], 206 'method': t['method'] 207 } 208 names = [ 209 GetTestName(t, sep='.'), 210 GetTestName(unqualified_class_test, sep='.'), 211 GetUniqueTestName(t, sep='.') 212 ] 213 214 if t['is_junit4']: 215 names += [ 216 GetTestNameWithoutParameterPostfix(t, sep='.'), 217 GetTestNameWithoutParameterPostfix(unqualified_class_test, sep='.') 218 ] 219 220 pattern_groups = filter_str.split('-') 221 if len(pattern_groups) > 1: 222 negative_filter = pattern_groups[1] 223 if unittest_util.FilterTestNames(names, negative_filter): 224 return [] 225 226 positive_filter = pattern_groups[0] 227 return unittest_util.FilterTestNames(names, positive_filter) 228 229 def annotation_filter(all_annotations): 230 if not annotations: 231 return True 232 return any_annotation_matches(annotations, all_annotations) 233 234 def excluded_annotation_filter(all_annotations): 235 if not excluded_annotations: 236 return True 237 return not any_annotation_matches(excluded_annotations, 238 all_annotations) 239 240 def any_annotation_matches(filter_annotations, all_annotations): 241 return any( 242 ak in all_annotations 243 and annotation_value_matches(av, all_annotations[ak]) 244 for ak, av in filter_annotations) 245 246 def annotation_value_matches(filter_av, av): 247 if filter_av is None: 248 return True 249 elif isinstance(av, dict): 250 tav_from_dict = av['value'] 251 # If tav_from_dict is an int, the 'in' operator breaks, so convert 252 # filter_av and manually compare. See https://crbug.com/1019707 253 if isinstance(tav_from_dict, int): 254 return int(filter_av) == tav_from_dict 255 else: 256 return filter_av in tav_from_dict 257 elif isinstance(av, list): 258 return filter_av in av 259 return filter_av == av 260 261 filtered_tests = [] 262 for t in tests: 263 # Gtest filtering 264 if not gtest_filter(t): 265 continue 266 267 # Enforce that all tests declare their size. 268 if not any(a in _VALID_ANNOTATIONS for a in t['annotations']): 269 raise MissingSizeAnnotationError(GetTestName(t)) 270 271 if (not annotation_filter(t['annotations']) 272 or not excluded_annotation_filter(t['annotations'])): 273 continue 274 275 filtered_tests.append(t) 276 277 return filtered_tests 278 279 280# TODO(yolandyan): remove this once the tests are converted to junit4 281def GetAllTestsFromJar(test_jar): 282 pickle_path = '%s-proguard.pickle' % test_jar 283 try: 284 tests = GetTestsFromPickle(pickle_path, os.path.getmtime(test_jar)) 285 except TestListPickleException as e: 286 logging.info('Could not get tests from pickle: %s', e) 287 logging.info('Getting tests from JAR via proguard.') 288 tests = _GetTestsFromProguard(test_jar) 289 SaveTestsToPickle(pickle_path, tests) 290 return tests 291 292 293def GetAllTestsFromApk(test_apk): 294 pickle_path = '%s-dexdump.pickle' % test_apk 295 try: 296 tests = GetTestsFromPickle(pickle_path, os.path.getmtime(test_apk)) 297 except TestListPickleException as e: 298 logging.info('Could not get tests from pickle: %s', e) 299 logging.info('Getting tests from dex via dexdump.') 300 tests = _GetTestsFromDexdump(test_apk) 301 SaveTestsToPickle(pickle_path, tests) 302 return tests 303 304def GetTestsFromPickle(pickle_path, test_mtime): 305 if not os.path.exists(pickle_path): 306 raise TestListPickleException('%s does not exist.' % pickle_path) 307 if os.path.getmtime(pickle_path) <= test_mtime: 308 raise TestListPickleException('File is stale: %s' % pickle_path) 309 310 with open(pickle_path, 'r') as f: 311 pickle_data = pickle.load(f) 312 if pickle_data['VERSION'] != _PICKLE_FORMAT_VERSION: 313 raise TestListPickleException('PICKLE_FORMAT_VERSION has changed.') 314 return pickle_data['TEST_METHODS'] 315 316 317# TODO(yolandyan): remove this once the test listing from java runner lands 318@instrumentation_tracing.no_tracing 319def _GetTestsFromProguard(jar_path): 320 p = proguard.Dump(jar_path) 321 class_lookup = dict((c['class'], c) for c in p['classes']) 322 323 def is_test_class(c): 324 return c['class'].endswith('Test') 325 326 def is_test_method(m): 327 return m['method'].startswith('test') 328 329 def recursive_class_annotations(c): 330 s = c['superclass'] 331 if s in class_lookup: 332 a = recursive_class_annotations(class_lookup[s]) 333 else: 334 a = {} 335 a.update(c['annotations']) 336 return a 337 338 def stripped_test_class(c): 339 return { 340 'class': c['class'], 341 'annotations': recursive_class_annotations(c), 342 'methods': [m for m in c['methods'] if is_test_method(m)], 343 'superclass': c['superclass'], 344 } 345 346 return [stripped_test_class(c) for c in p['classes'] 347 if is_test_class(c)] 348 349 350def _GetTestsFromDexdump(test_apk): 351 dump = dexdump.Dump(test_apk) 352 tests = [] 353 354 def get_test_methods(methods): 355 return [ 356 { 357 'method': m, 358 # No annotation info is available from dexdump. 359 # Set MediumTest annotation for default. 360 'annotations': {'MediumTest': None}, 361 } for m in methods if m.startswith('test')] 362 363 for package_name, package_info in dump.iteritems(): 364 for class_name, class_info in package_info['classes'].iteritems(): 365 if class_name.endswith('Test'): 366 tests.append({ 367 'class': '%s.%s' % (package_name, class_name), 368 'annotations': {}, 369 'methods': get_test_methods(class_info['methods']), 370 'superclass': class_info['superclass'], 371 }) 372 return tests 373 374def SaveTestsToPickle(pickle_path, tests): 375 pickle_data = { 376 'VERSION': _PICKLE_FORMAT_VERSION, 377 'TEST_METHODS': tests, 378 } 379 with open(pickle_path, 'w') as pickle_file: 380 pickle.dump(pickle_data, pickle_file) 381 382 383class MissingJUnit4RunnerException(test_exception.TestException): 384 """Raised when JUnit4 runner is not provided or specified in apk manifest""" 385 386 def __init__(self): 387 super(MissingJUnit4RunnerException, self).__init__( 388 'JUnit4 runner is not provided or specified in test apk manifest.') 389 390 391def GetTestName(test, sep='#'): 392 """Gets the name of the given test. 393 394 Note that this may return the same name for more than one test, e.g. if a 395 test is being run multiple times with different parameters. 396 397 Args: 398 test: the instrumentation test dict. 399 sep: the character(s) that should join the class name and the method name. 400 Returns: 401 The test name as a string. 402 """ 403 test_name = '%s%s%s' % (test['class'], sep, test['method']) 404 assert ' *-:' not in test_name, ( 405 'The test name must not contain any of the characters in " *-:". See ' 406 'https://crbug.com/912199') 407 return test_name 408 409 410def GetTestNameWithoutParameterPostfix( 411 test, sep='#', parameterization_sep='__'): 412 """Gets the name of the given JUnit4 test without parameter postfix. 413 414 For most WebView JUnit4 javatests, each test is parameterizatized with 415 "__sandboxed_mode" to run in both non-sandboxed mode and sandboxed mode. 416 417 This function returns the name of the test without parameterization 418 so test filters can match both parameterized and non-parameterized tests. 419 420 Args: 421 test: the instrumentation test dict. 422 sep: the character(s) that should join the class name and the method name. 423 parameterization_sep: the character(s) that seperate method name and method 424 parameterization postfix. 425 Returns: 426 The test name without parameter postfix as a string. 427 """ 428 name = GetTestName(test, sep=sep) 429 return name.split(parameterization_sep)[0] 430 431 432def GetUniqueTestName(test, sep='#'): 433 """Gets the unique name of the given test. 434 435 This will include text to disambiguate between tests for which GetTestName 436 would return the same name. 437 438 Args: 439 test: the instrumentation test dict. 440 sep: the character(s) that should join the class name and the method name. 441 Returns: 442 The unique test name as a string. 443 """ 444 display_name = GetTestName(test, sep=sep) 445 if test.get('flags', [None])[0]: 446 sanitized_flags = [x.replace('-', '_') for x in test['flags']] 447 display_name = '%s_with_%s' % (display_name, '_'.join(sanitized_flags)) 448 449 assert ' *-:' not in display_name, ( 450 'The test name must not contain any of the characters in " *-:". See ' 451 'https://crbug.com/912199') 452 453 return display_name 454 455 456class InstrumentationTestInstance(test_instance.TestInstance): 457 458 def __init__(self, args, data_deps_delegate, error_func): 459 super(InstrumentationTestInstance, self).__init__() 460 461 self._additional_apks = [] 462 self._apk_under_test = None 463 self._apk_under_test_incremental_install_json = None 464 self._modules = None 465 self._fake_modules = None 466 self._package_info = None 467 self._suite = None 468 self._test_apk = None 469 self._test_apk_incremental_install_json = None 470 self._test_jar = None 471 self._test_package = None 472 self._junit3_runner_class = None 473 self._junit4_runner_class = None 474 self._junit4_runner_supports_listing = None 475 self._test_support_apk = None 476 self._initializeApkAttributes(args, error_func) 477 478 self._data_deps = None 479 self._data_deps_delegate = None 480 self._runtime_deps_path = None 481 self._initializeDataDependencyAttributes(args, data_deps_delegate) 482 483 self._annotations = None 484 self._excluded_annotations = None 485 self._test_filter = None 486 self._initializeTestFilterAttributes(args) 487 488 self._flags = None 489 self._use_apk_under_test_flags_file = False 490 self._initializeFlagAttributes(args) 491 492 self._driver_apk = None 493 self._driver_package = None 494 self._driver_name = None 495 self._initializeDriverAttributes() 496 497 self._screenshot_dir = None 498 self._timeout_scale = None 499 self._wait_for_java_debugger = None 500 self._initializeTestControlAttributes(args) 501 502 self._coverage_directory = None 503 self._initializeTestCoverageAttributes(args) 504 505 self._store_tombstones = False 506 self._symbolizer = None 507 self._enable_java_deobfuscation = False 508 self._deobfuscator = None 509 self._initializeLogAttributes(args) 510 511 self._edit_shared_prefs = [] 512 self._initializeEditPrefsAttributes(args) 513 514 self._replace_system_package = None 515 self._initializeReplaceSystemPackageAttributes(args) 516 517 self._use_webview_provider = None 518 self._initializeUseWebviewProviderAttributes(args) 519 520 self._skia_gold_properties = None 521 self._initializeSkiaGoldAttributes(args) 522 523 self._external_shard_index = args.test_launcher_shard_index 524 self._total_external_shards = args.test_launcher_total_shards 525 526 def _initializeApkAttributes(self, args, error_func): 527 if args.apk_under_test: 528 apk_under_test_path = args.apk_under_test 529 if (not args.apk_under_test.endswith('.apk') 530 and not args.apk_under_test.endswith('.apks')): 531 apk_under_test_path = os.path.join( 532 constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR, 533 '%s.apk' % args.apk_under_test) 534 535 # TODO(jbudorick): Move the realpath up to the argument parser once 536 # APK-by-name is no longer supported. 537 apk_under_test_path = os.path.realpath(apk_under_test_path) 538 539 if not os.path.exists(apk_under_test_path): 540 error_func('Unable to find APK under test: %s' % apk_under_test_path) 541 542 self._apk_under_test = apk_helper.ToHelper(apk_under_test_path) 543 544 test_apk_path = args.test_apk 545 if not os.path.exists(test_apk_path): 546 test_apk_path = os.path.join( 547 constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR, 548 '%s.apk' % args.test_apk) 549 # TODO(jbudorick): Move the realpath up to the argument parser once 550 # APK-by-name is no longer supported. 551 test_apk_path = os.path.realpath(test_apk_path) 552 553 if not os.path.exists(test_apk_path): 554 error_func('Unable to find test APK: %s' % test_apk_path) 555 556 self._test_apk = apk_helper.ToHelper(test_apk_path) 557 self._suite = os.path.splitext(os.path.basename(args.test_apk))[0] 558 559 self._apk_under_test_incremental_install_json = ( 560 args.apk_under_test_incremental_install_json) 561 self._test_apk_incremental_install_json = ( 562 args.test_apk_incremental_install_json) 563 564 if self._test_apk_incremental_install_json: 565 assert self._suite.endswith('_incremental') 566 self._suite = self._suite[:-len('_incremental')] 567 568 self._modules = args.modules 569 self._fake_modules = args.fake_modules 570 571 self._test_jar = args.test_jar 572 self._test_support_apk = apk_helper.ToHelper(os.path.join( 573 constants.GetOutDirectory(), constants.SDK_BUILD_TEST_JAVALIB_DIR, 574 '%sSupport.apk' % self._suite)) 575 576 if not self._test_jar: 577 logging.warning('Test jar not specified. Test runner will not have ' 578 'Java annotation info available. May not handle test ' 579 'timeouts correctly.') 580 elif not os.path.exists(self._test_jar): 581 error_func('Unable to find test JAR: %s' % self._test_jar) 582 583 self._test_package = self._test_apk.GetPackageName() 584 all_instrumentations = self._test_apk.GetAllInstrumentations() 585 all_junit3_runner_classes = [ 586 x for x in all_instrumentations if ('0xffffffff' in x.get( 587 'chromium-junit3', ''))] 588 all_junit4_runner_classes = [ 589 x for x in all_instrumentations if ('0xffffffff' not in x.get( 590 'chromium-junit3', ''))] 591 592 if len(all_junit3_runner_classes) > 1: 593 logging.warning('This test apk has more than one JUnit3 instrumentation') 594 if len(all_junit4_runner_classes) > 1: 595 logging.warning('This test apk has more than one JUnit4 instrumentation') 596 597 self._junit3_runner_class = ( 598 all_junit3_runner_classes[0]['android:name'] 599 if all_junit3_runner_classes else self.test_apk.GetInstrumentationName()) 600 601 self._junit4_runner_class = ( 602 all_junit4_runner_classes[0]['android:name'] 603 if all_junit4_runner_classes else None) 604 605 if self._junit4_runner_class: 606 if self._test_apk_incremental_install_json: 607 self._junit4_runner_supports_listing = next( 608 (True for x in self._test_apk.GetAllMetadata() 609 if 'real-instr' in x[0] and x[1] in _TEST_LIST_JUNIT4_RUNNERS), 610 False) 611 else: 612 self._junit4_runner_supports_listing = ( 613 self._junit4_runner_class in _TEST_LIST_JUNIT4_RUNNERS) 614 615 self._package_info = None 616 if self._apk_under_test: 617 package_under_test = self._apk_under_test.GetPackageName() 618 for package_info in constants.PACKAGE_INFO.itervalues(): 619 if package_under_test == package_info.package: 620 self._package_info = package_info 621 break 622 if not self._package_info: 623 logging.warning(("Unable to find package info for %s. " + 624 "(This may just mean that the test package is " + 625 "currently being installed.)"), 626 self._test_package) 627 628 for apk in args.additional_apks: 629 if not os.path.exists(apk): 630 error_func('Unable to find additional APK: %s' % apk) 631 self._additional_apks = ( 632 [apk_helper.ToHelper(x) for x in args.additional_apks]) 633 634 def _initializeDataDependencyAttributes(self, args, data_deps_delegate): 635 self._data_deps = [] 636 self._data_deps_delegate = data_deps_delegate 637 self._runtime_deps_path = args.runtime_deps_path 638 639 if not self._runtime_deps_path: 640 logging.warning('No data dependencies will be pushed.') 641 642 def _initializeTestFilterAttributes(self, args): 643 self._test_filter = test_filter.InitializeFilterFromArgs(args) 644 645 def annotation_element(a): 646 a = a.split('=', 1) 647 return (a[0], a[1] if len(a) == 2 else None) 648 649 if args.annotation_str: 650 self._annotations = [ 651 annotation_element(a) for a in args.annotation_str.split(',')] 652 elif not self._test_filter: 653 self._annotations = [ 654 annotation_element(a) for a in _DEFAULT_ANNOTATIONS] 655 else: 656 self._annotations = [] 657 658 if args.exclude_annotation_str: 659 self._excluded_annotations = [ 660 annotation_element(a) for a in args.exclude_annotation_str.split(',')] 661 else: 662 self._excluded_annotations = [] 663 664 requested_annotations = set(a[0] for a in self._annotations) 665 if not args.run_disabled: 666 self._excluded_annotations.extend( 667 annotation_element(a) for a in _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS 668 if a not in requested_annotations) 669 670 def _initializeFlagAttributes(self, args): 671 self._use_apk_under_test_flags_file = args.use_apk_under_test_flags_file 672 self._flags = ['--enable-test-intents'] 673 if args.command_line_flags: 674 self._flags.extend(args.command_line_flags) 675 if args.device_flags_file: 676 with open(args.device_flags_file) as device_flags_file: 677 stripped_lines = (l.strip() for l in device_flags_file) 678 self._flags.extend(flag for flag in stripped_lines if flag) 679 if args.strict_mode and args.strict_mode != 'off' and ( 680 # TODO(yliuyliu): Turn on strict mode for coverage once 681 # crbug/1006397 is fixed. 682 not args.coverage_dir): 683 self._flags.append('--strict-mode=' + args.strict_mode) 684 685 def _initializeDriverAttributes(self): 686 self._driver_apk = os.path.join( 687 constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR, 688 'OnDeviceInstrumentationDriver.apk') 689 if os.path.exists(self._driver_apk): 690 driver_apk = apk_helper.ApkHelper(self._driver_apk) 691 self._driver_package = driver_apk.GetPackageName() 692 self._driver_name = driver_apk.GetInstrumentationName() 693 else: 694 self._driver_apk = None 695 696 def _initializeTestControlAttributes(self, args): 697 self._screenshot_dir = args.screenshot_dir 698 self._timeout_scale = args.timeout_scale or 1 699 self._wait_for_java_debugger = args.wait_for_java_debugger 700 701 def _initializeTestCoverageAttributes(self, args): 702 self._coverage_directory = args.coverage_dir 703 704 def _initializeLogAttributes(self, args): 705 self._enable_java_deobfuscation = args.enable_java_deobfuscation 706 self._store_tombstones = args.store_tombstones 707 self._symbolizer = stack_symbolizer.Symbolizer( 708 self.apk_under_test.path if self.apk_under_test else None) 709 710 def _initializeEditPrefsAttributes(self, args): 711 if not hasattr(args, 'shared_prefs_file') or not args.shared_prefs_file: 712 return 713 if not isinstance(args.shared_prefs_file, str): 714 logging.warning("Given non-string for a filepath") 715 return 716 self._edit_shared_prefs = shared_preference_utils.ExtractSettingsFromJson( 717 args.shared_prefs_file) 718 719 def _initializeReplaceSystemPackageAttributes(self, args): 720 if (not hasattr(args, 'replace_system_package') 721 or not args.replace_system_package): 722 return 723 self._replace_system_package = args.replace_system_package 724 725 def _initializeUseWebviewProviderAttributes(self, args): 726 if (not hasattr(args, 'use_webview_provider') 727 or not args.use_webview_provider): 728 return 729 self._use_webview_provider = args.use_webview_provider 730 731 def _initializeSkiaGoldAttributes(self, args): 732 self._skia_gold_properties = gold_utils.SkiaGoldProperties(args) 733 734 @property 735 def additional_apks(self): 736 return self._additional_apks 737 738 @property 739 def apk_under_test(self): 740 return self._apk_under_test 741 742 @property 743 def apk_under_test_incremental_install_json(self): 744 return self._apk_under_test_incremental_install_json 745 746 @property 747 def modules(self): 748 return self._modules 749 750 @property 751 def fake_modules(self): 752 return self._fake_modules 753 754 @property 755 def coverage_directory(self): 756 return self._coverage_directory 757 758 @property 759 def driver_apk(self): 760 return self._driver_apk 761 762 @property 763 def driver_package(self): 764 return self._driver_package 765 766 @property 767 def driver_name(self): 768 return self._driver_name 769 770 @property 771 def edit_shared_prefs(self): 772 return self._edit_shared_prefs 773 774 @property 775 def external_shard_index(self): 776 return self._external_shard_index 777 778 @property 779 def flags(self): 780 return self._flags 781 782 @property 783 def junit3_runner_class(self): 784 return self._junit3_runner_class 785 786 @property 787 def junit4_runner_class(self): 788 return self._junit4_runner_class 789 790 @property 791 def junit4_runner_supports_listing(self): 792 return self._junit4_runner_supports_listing 793 794 @property 795 def package_info(self): 796 return self._package_info 797 798 @property 799 def replace_system_package(self): 800 return self._replace_system_package 801 802 @property 803 def use_webview_provider(self): 804 return self._use_webview_provider 805 806 @property 807 def screenshot_dir(self): 808 return self._screenshot_dir 809 810 @property 811 def skia_gold_properties(self): 812 return self._skia_gold_properties 813 814 @property 815 def store_tombstones(self): 816 return self._store_tombstones 817 818 @property 819 def suite(self): 820 return self._suite 821 822 @property 823 def symbolizer(self): 824 return self._symbolizer 825 826 @property 827 def test_apk(self): 828 return self._test_apk 829 830 @property 831 def test_apk_incremental_install_json(self): 832 return self._test_apk_incremental_install_json 833 834 @property 835 def test_jar(self): 836 return self._test_jar 837 838 @property 839 def test_support_apk(self): 840 return self._test_support_apk 841 842 @property 843 def test_package(self): 844 return self._test_package 845 846 @property 847 def timeout_scale(self): 848 return self._timeout_scale 849 850 @property 851 def total_external_shards(self): 852 return self._total_external_shards 853 854 @property 855 def use_apk_under_test_flags_file(self): 856 return self._use_apk_under_test_flags_file 857 858 @property 859 def wait_for_java_debugger(self): 860 return self._wait_for_java_debugger 861 862 #override 863 def TestType(self): 864 return 'instrumentation' 865 866 #override 867 def GetPreferredAbis(self): 868 # We could alternatively take the intersection of what they all support, 869 # but it should never be the case that they support different things. 870 apks = [self._test_apk, self._apk_under_test] + self._additional_apks 871 for apk in apks: 872 if apk: 873 ret = apk.GetAbis() 874 if ret: 875 return ret 876 return [] 877 878 #override 879 def SetUp(self): 880 self._data_deps.extend( 881 self._data_deps_delegate(self._runtime_deps_path)) 882 if self._enable_java_deobfuscation: 883 self._deobfuscator = deobfuscator.DeobfuscatorPool( 884 self.test_apk.path + '.mapping') 885 886 def GetDataDependencies(self): 887 return self._data_deps 888 889 def GetTests(self): 890 if self.test_jar: 891 raw_tests = GetAllTestsFromJar(self.test_jar) 892 else: 893 raw_tests = GetAllTestsFromApk(self.test_apk.path) 894 return self.ProcessRawTests(raw_tests) 895 896 def MaybeDeobfuscateLines(self, lines): 897 if not self._deobfuscator: 898 return lines 899 return self._deobfuscator.TransformLines(lines) 900 901 def ProcessRawTests(self, raw_tests): 902 inflated_tests = self._ParameterizeTestsWithFlags( 903 self._InflateTests(raw_tests)) 904 if self._junit4_runner_class is None and any( 905 t['is_junit4'] for t in inflated_tests): 906 raise MissingJUnit4RunnerException() 907 filtered_tests = FilterTests( 908 inflated_tests, self._test_filter, self._annotations, 909 self._excluded_annotations) 910 if self._test_filter and not filtered_tests: 911 for t in inflated_tests: 912 logging.debug(' %s', GetUniqueTestName(t)) 913 logging.warning('Unmatched Filter: %s', self._test_filter) 914 return filtered_tests 915 916 # pylint: disable=no-self-use 917 def _InflateTests(self, tests): 918 inflated_tests = [] 919 for c in tests: 920 for m in c['methods']: 921 a = dict(c['annotations']) 922 a.update(m['annotations']) 923 inflated_tests.append({ 924 'class': c['class'], 925 'method': m['method'], 926 'annotations': a, 927 'is_junit4': c['superclass'] == 'java.lang.Object' 928 }) 929 return inflated_tests 930 931 def _ParameterizeTestsWithFlags(self, tests): 932 933 def _checkParameterization(annotations): 934 types = [ 935 _PARAMETERIZED_COMMAND_LINE_FLAGS_SWITCHES, 936 _PARAMETERIZED_COMMAND_LINE_FLAGS, 937 ] 938 if types[0] in annotations and types[1] in annotations: 939 raise CommandLineParameterizationException( 940 'Multiple command-line parameterization types: {}.'.format( 941 ', '.join(types))) 942 943 def _switchesToFlags(switches): 944 return ['--{}'.format(s) for s in switches if s] 945 946 def _annotationToSwitches(clazz, methods): 947 if clazz == _PARAMETERIZED_COMMAND_LINE_FLAGS_SWITCHES: 948 return [methods['value']] 949 elif clazz == _PARAMETERIZED_COMMAND_LINE_FLAGS: 950 list_of_switches = [] 951 for annotation in methods['value']: 952 for clazz, methods in annotation.iteritems(): 953 list_of_switches += _annotationToSwitches(clazz, methods) 954 return list_of_switches 955 else: 956 return [] 957 958 def _setTestFlags(test, flags): 959 if flags: 960 test['flags'] = flags 961 elif 'flags' in test: 962 del test['flags'] 963 964 new_tests = [] 965 for t in tests: 966 annotations = t['annotations'] 967 list_of_switches = [] 968 _checkParameterization(annotations) 969 if _SKIP_PARAMETERIZATION not in annotations: 970 for clazz, methods in annotations.iteritems(): 971 list_of_switches += _annotationToSwitches(clazz, methods) 972 if list_of_switches: 973 _setTestFlags(t, _switchesToFlags(list_of_switches[0])) 974 for p in list_of_switches[1:]: 975 parameterized_t = copy.copy(t) 976 _setTestFlags(parameterized_t, _switchesToFlags(p)) 977 new_tests.append(parameterized_t) 978 return tests + new_tests 979 980 def GetDriverEnvironmentVars( 981 self, test_list=None, test_list_file_path=None): 982 env = { 983 _EXTRA_DRIVER_TARGET_PACKAGE: self.test_package, 984 _EXTRA_DRIVER_TARGET_CLASS: self.junit3_runner_class, 985 _EXTRA_TIMEOUT_SCALE: self._timeout_scale, 986 } 987 988 if test_list: 989 env[_EXTRA_DRIVER_TEST_LIST] = ','.join(test_list) 990 991 if test_list_file_path: 992 env[_EXTRA_DRIVER_TEST_LIST_FILE] = ( 993 os.path.basename(test_list_file_path)) 994 995 return env 996 997 @staticmethod 998 def ParseAmInstrumentRawOutput(raw_output): 999 return ParseAmInstrumentRawOutput(raw_output) 1000 1001 @staticmethod 1002 def GenerateTestResults( 1003 result_code, result_bundle, statuses, start_ms, duration_ms, 1004 device_abi, symbolizer): 1005 return GenerateTestResults(result_code, result_bundle, statuses, 1006 start_ms, duration_ms, device_abi, symbolizer) 1007 1008 #override 1009 def TearDown(self): 1010 self.symbolizer.CleanUp() 1011 if self._deobfuscator: 1012 self._deobfuscator.Close() 1013 self._deobfuscator = None 1014