| 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 collections |
| 6 import copy |
| 5 import logging | 7 import logging |
| 6 import os | 8 import os |
| 7 import pickle | 9 import pickle |
| 8 import re | 10 import re |
| 9 import sys | 11 import sys |
| 10 | 12 |
| 11 from devil.android import apk_helper | 13 from devil.android import apk_helper |
| 12 from devil.android import md5sum | 14 from devil.android import md5sum |
| 13 from pylib import constants | 15 from pylib import constants |
| 14 from pylib.base import base_test_result | 16 from pylib.base import base_test_result |
| 15 from pylib.base import test_instance | 17 from pylib.base import test_instance |
| 16 from pylib.instrumentation import test_result | 18 from pylib.instrumentation import test_result |
| 17 from pylib.instrumentation import instrumentation_parser | 19 from pylib.instrumentation import instrumentation_parser |
| 18 from pylib.utils import proguard | 20 from pylib.utils import proguard |
| 19 | 21 |
| 20 sys.path.append( | 22 sys.path.append( |
| 21 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', 'common')) | 23 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', 'common')) |
| 22 import unittest_util # pylint: disable=import-error | 24 import unittest_util # pylint: disable=import-error |
| 23 | 25 |
| 24 # Ref: http://developer.android.com/reference/android/app/Activity.html | 26 # Ref: http://developer.android.com/reference/android/app/Activity.html |
| 25 _ACTIVITY_RESULT_CANCELED = 0 | 27 _ACTIVITY_RESULT_CANCELED = 0 |
| 26 _ACTIVITY_RESULT_OK = -1 | 28 _ACTIVITY_RESULT_OK = -1 |
| 27 | 29 |
| 30 _COMMAND_LINE_PARAMETER = 'cmdlinearg-parameter' |
| 28 _DEFAULT_ANNOTATIONS = [ | 31 _DEFAULT_ANNOTATIONS = [ |
| 29 'Smoke', 'SmallTest', 'MediumTest', 'LargeTest', | 32 'Smoke', 'SmallTest', 'MediumTest', 'LargeTest', |
| 30 'EnormousTest', 'IntegrationTest'] | 33 'EnormousTest', 'IntegrationTest'] |
| 31 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS = [ | 34 _EXCLUDE_UNLESS_REQUESTED_ANNOTATIONS = [ |
| 32 'DisabledTest', 'FlakyTest'] | 35 'DisabledTest', 'FlakyTest'] |
| 33 _EXTRA_ENABLE_HTTP_SERVER = ( | 36 _EXTRA_ENABLE_HTTP_SERVER = ( |
| 34 'org.chromium.chrome.test.ChromeInstrumentationTestRunner.' | 37 'org.chromium.chrome.test.ChromeInstrumentationTestRunner.' |
| 35 + 'EnableTestHttpServer') | 38 + 'EnableTestHttpServer') |
| 36 _EXTRA_DRIVER_TEST_LIST = ( | 39 _EXTRA_DRIVER_TEST_LIST = ( |
| 37 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList') | 40 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList') |
| 38 _EXTRA_DRIVER_TEST_LIST_FILE = ( | 41 _EXTRA_DRIVER_TEST_LIST_FILE = ( |
| 39 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestListFile') | 42 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TestListFile') |
| 40 _EXTRA_DRIVER_TARGET_PACKAGE = ( | 43 _EXTRA_DRIVER_TARGET_PACKAGE = ( |
| 41 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetPackage') | 44 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetPackage') |
| 42 _EXTRA_DRIVER_TARGET_CLASS = ( | 45 _EXTRA_DRIVER_TARGET_CLASS = ( |
| 43 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetClass') | 46 'org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetClass') |
| 47 _PARAMETERIZED_TEST_ANNOTATION = 'ParameterizedTest' |
| 48 _PARAMETERIZED_TEST_SET_ANNOTATION = 'ParameterizedTest$Set' |
| 44 _NATIVE_CRASH_RE = re.compile('native crash', re.IGNORECASE) | 49 _NATIVE_CRASH_RE = re.compile('native crash', re.IGNORECASE) |
| 45 _PICKLE_FORMAT_VERSION = 10 | 50 _PICKLE_FORMAT_VERSION = 10 |
| 46 | 51 |
| 47 | 52 |
| 48 # TODO(jbudorick): Make these private class methods of | 53 # TODO(jbudorick): Make these private class methods of |
| 49 # InstrumentationTestInstance once the instrumentation test_runner is | 54 # InstrumentationTestInstance once the instrumentation test_runner is |
| 50 # deprecated. | 55 # deprecated. |
| 51 def ParseAmInstrumentRawOutput(raw_output): | 56 def ParseAmInstrumentRawOutput(raw_output): |
| 52 """Parses the output of an |am instrument -r| call. | 57 """Parses the output of an |am instrument -r| call. |
| 53 | 58 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 127 and any(_NATIVE_CRASH_RE.search(l) | 132 and any(_NATIVE_CRASH_RE.search(l) |
| 128 for l in result_bundle.itervalues())) | 133 for l in result_bundle.itervalues())) |
| 129 if crashed: | 134 if crashed: |
| 130 current_result.SetType(base_test_result.ResultType.CRASH) | 135 current_result.SetType(base_test_result.ResultType.CRASH) |
| 131 | 136 |
| 132 results.append(current_result) | 137 results.append(current_result) |
| 133 | 138 |
| 134 return results | 139 return results |
| 135 | 140 |
| 136 | 141 |
| 142 def ParseCommandLineFlagParameters(annotations): |
| 143 """Determines whether the test is parameterized to be run with different |
| 144 command-line flags. |
| 145 |
| 146 Args: |
| 147 annotations: The annotations of the test. |
| 148 |
| 149 Returns: |
| 150 If the test is parameterized, returns a list of named tuples |
| 151 with lists of flags, e.g.: |
| 152 |
| 153 [(add=['--flag-to-add']), (remove=['--flag-to-remove']), ()] |
| 154 |
| 155 That means, the test must be run three times, the first time with |
| 156 "--flag-to-add" added to command-line, the second time with |
| 157 "--flag-to-remove" to be removed from command-line, and the third time |
| 158 with default command-line args. If the same flag is listed both for adding |
| 159 and for removing, it is left unchanged. |
| 160 |
| 161 If the test is not parametrized, returns None. |
| 162 |
| 163 """ |
| 164 ParamsTuple = collections.namedtuple('ParamsTuple', ['add', 'remove']) |
| 165 parameterized_tests = [] |
| 166 if _PARAMETERIZED_TEST_ANNOTATION in annotations: |
| 167 parameterized_tests = [annotations[_PARAMETERIZED_TEST_ANNOTATION]] |
| 168 elif _PARAMETERIZED_TEST_SET_ANNOTATION in annotations: |
| 169 if annotations[_PARAMETERIZED_TEST_SET_ANNOTATION]: |
| 170 parameterized_tests = annotations[ |
| 171 _PARAMETERIZED_TEST_SET_ANNOTATION].get('tests', []) |
| 172 else: |
| 173 return None |
| 174 |
| 175 result = [] |
| 176 for pt in parameterized_tests: |
| 177 if not pt: |
| 178 continue |
| 179 for p in pt['parameters']: |
| 180 if p['tag'] == _COMMAND_LINE_PARAMETER: |
| 181 to_add = [] |
| 182 to_remove = [] |
| 183 for a in p.get('arguments', []): |
| 184 if a['name'] == 'add': |
| 185 to_add = ['--%s' % f for f in a['stringArray']] |
| 186 elif a['name'] == 'remove': |
| 187 to_remove = ['--%s' % f for f in a['stringArray']] |
| 188 result.append(ParamsTuple(to_add, to_remove)) |
| 189 return result if result else None |
| 190 |
| 191 |
| 137 class InstrumentationTestInstance(test_instance.TestInstance): | 192 class InstrumentationTestInstance(test_instance.TestInstance): |
| 138 | 193 |
| 139 def __init__(self, args, isolate_delegate, error_func): | 194 def __init__(self, args, isolate_delegate, error_func): |
| 140 super(InstrumentationTestInstance, self).__init__() | 195 super(InstrumentationTestInstance, self).__init__() |
| 141 | 196 |
| 142 self._additional_apks = [] | 197 self._additional_apks = [] |
| 143 self._apk_under_test = None | 198 self._apk_under_test = None |
| 144 self._apk_under_test_permissions = None | 199 self._apk_under_test_permissions = None |
| 145 self._package_info = None | 200 self._package_info = None |
| 146 self._suite = None | 201 self._suite = None |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 return self._data_deps | 440 return self._data_deps |
| 386 | 441 |
| 387 def GetTests(self): | 442 def GetTests(self): |
| 388 pickle_path = '%s-proguard.pickle' % self.test_jar | 443 pickle_path = '%s-proguard.pickle' % self.test_jar |
| 389 try: | 444 try: |
| 390 tests = self._GetTestsFromPickle(pickle_path, self.test_jar) | 445 tests = self._GetTestsFromPickle(pickle_path, self.test_jar) |
| 391 except self.ProguardPickleException as e: | 446 except self.ProguardPickleException as e: |
| 392 logging.info('Getting tests from JAR via proguard. (%s)', str(e)) | 447 logging.info('Getting tests from JAR via proguard. (%s)', str(e)) |
| 393 tests = self._GetTestsFromProguard(self.test_jar) | 448 tests = self._GetTestsFromProguard(self.test_jar) |
| 394 self._SaveTestsToPickle(pickle_path, self.test_jar, tests) | 449 self._SaveTestsToPickle(pickle_path, self.test_jar, tests) |
| 395 return self._InflateTests(self._FilterTests(tests)) | 450 return self._ParametrizeTestsWithFlags( |
| 451 self._InflateTests(self._FilterTests(tests))) |
| 396 | 452 |
| 397 class ProguardPickleException(Exception): | 453 class ProguardPickleException(Exception): |
| 398 pass | 454 pass |
| 399 | 455 |
| 400 def _GetTestsFromPickle(self, pickle_path, jar_path): | 456 def _GetTestsFromPickle(self, pickle_path, jar_path): |
| 401 if not os.path.exists(pickle_path): | 457 if not os.path.exists(pickle_path): |
| 402 raise self.ProguardPickleException('%s does not exist.' % pickle_path) | 458 raise self.ProguardPickleException('%s does not exist.' % pickle_path) |
| 403 if os.path.getmtime(pickle_path) <= os.path.getmtime(jar_path): | 459 if os.path.getmtime(pickle_path) <= os.path.getmtime(jar_path): |
| 404 raise self.ProguardPickleException( | 460 raise self.ProguardPickleException( |
| 405 '%s newer than %s.' % (jar_path, pickle_path)) | 461 '%s newer than %s.' % (jar_path, pickle_path)) |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 510 for m in c['methods']: | 566 for m in c['methods']: |
| 511 a = dict(c['annotations']) | 567 a = dict(c['annotations']) |
| 512 a.update(m['annotations']) | 568 a.update(m['annotations']) |
| 513 inflated_tests.append({ | 569 inflated_tests.append({ |
| 514 'class': c['class'], | 570 'class': c['class'], |
| 515 'method': m['method'], | 571 'method': m['method'], |
| 516 'annotations': a, | 572 'annotations': a, |
| 517 }) | 573 }) |
| 518 return inflated_tests | 574 return inflated_tests |
| 519 | 575 |
| 576 def _ParametrizeTestsWithFlags(self, tests): |
| 577 new_tests = [] |
| 578 for t in tests: |
| 579 parameters = ParseCommandLineFlagParameters(t['annotations']) |
| 580 if parameters: |
| 581 t['flags'] = parameters[0] |
| 582 for p in parameters[1:]: |
| 583 parameterized_t = copy.copy(t) |
| 584 parameterized_t['flags'] = p |
| 585 new_tests.append(parameterized_t) |
| 586 return tests + new_tests |
| 587 |
| 520 @staticmethod | 588 @staticmethod |
| 521 def GetHttpServerEnvironmentVars(): | 589 def GetHttpServerEnvironmentVars(): |
| 522 return { | 590 return { |
| 523 _EXTRA_ENABLE_HTTP_SERVER: None, | 591 _EXTRA_ENABLE_HTTP_SERVER: None, |
| 524 } | 592 } |
| 525 | 593 |
| 526 def GetDriverEnvironmentVars( | 594 def GetDriverEnvironmentVars( |
| 527 self, test_list=None, test_list_file_path=None): | 595 self, test_list=None, test_list_file_path=None): |
| 528 env = { | 596 env = { |
| 529 _EXTRA_DRIVER_TARGET_PACKAGE: self.test_package, | 597 _EXTRA_DRIVER_TARGET_PACKAGE: self.test_package, |
| (...skipping 17 matching lines...) Expand all Loading... |
| 547 def GenerateTestResults( | 615 def GenerateTestResults( |
| 548 result_code, result_bundle, statuses, start_ms, duration_ms): | 616 result_code, result_bundle, statuses, start_ms, duration_ms): |
| 549 return GenerateTestResults(result_code, result_bundle, statuses, | 617 return GenerateTestResults(result_code, result_bundle, statuses, |
| 550 start_ms, duration_ms) | 618 start_ms, duration_ms) |
| 551 | 619 |
| 552 #override | 620 #override |
| 553 def TearDown(self): | 621 def TearDown(self): |
| 554 if self._isolate_delegate: | 622 if self._isolate_delegate: |
| 555 self._isolate_delegate.Clear() | 623 self._isolate_delegate.Clear() |
| 556 | 624 |
| OLD | NEW |