| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 """Runs the Java tests. See more information on run_instrumentation_tests.py.""" | 5 """Runs the Java tests. See more information on run_instrumentation_tests.py.""" |
| 6 | 6 |
| 7 import logging | 7 import logging |
| 8 import os | 8 import os |
| 9 import re | 9 import re |
| 10 import shutil | 10 import shutil |
| 11 import sys | 11 import sys |
| 12 import time | 12 import time |
| 13 | 13 |
| 14 from pylib import android_commands | 14 from pylib import android_commands |
| 15 from pylib import cmd_helper | 15 from pylib import cmd_helper |
| 16 from pylib import constants | 16 from pylib import constants |
| 17 from pylib import forwarder | 17 from pylib import forwarder |
| 18 from pylib import json_perf_parser | 18 from pylib import json_perf_parser |
| 19 from pylib import perf_tests_helper | 19 from pylib import perf_tests_helper |
| 20 from pylib import valgrind_tools | 20 from pylib import valgrind_tools |
| 21 from pylib.base import base_test_runner | 21 from pylib.base import base_test_runner |
| 22 from pylib.base import base_test_sharder | |
| 23 from pylib.base import test_result | 22 from pylib.base import test_result |
| 24 | 23 |
| 25 import apk_info | 24 import apk_info |
| 26 | 25 |
| 27 | 26 |
| 28 _PERF_TEST_ANNOTATION = 'PerfTest' | 27 _PERF_TEST_ANNOTATION = 'PerfTest' |
| 29 | 28 |
| 30 | 29 |
| 31 class TestRunner(base_test_runner.BaseTestRunner): | 30 class TestRunner(base_test_runner.BaseTestRunner): |
| 32 """Responsible for running a series of tests connected to a single device.""" | 31 """Responsible for running a series of tests connected to a single device.""" |
| 33 | 32 |
| 34 _DEVICE_DATA_DIR = 'chrome/test/data' | 33 _DEVICE_DATA_DIR = 'chrome/test/data' |
| 35 _EMMA_JAR = os.path.join(os.environ.get('ANDROID_BUILD_TOP', ''), | 34 _EMMA_JAR = os.path.join(os.environ.get('ANDROID_BUILD_TOP', ''), |
| 36 'external/emma/lib/emma.jar') | 35 'external/emma/lib/emma.jar') |
| 37 _COVERAGE_MERGED_FILENAME = 'unittest_coverage.es' | 36 _COVERAGE_MERGED_FILENAME = 'unittest_coverage.es' |
| 38 _COVERAGE_WEB_ROOT_DIR = os.environ.get('EMMA_WEB_ROOTDIR') | 37 _COVERAGE_WEB_ROOT_DIR = os.environ.get('EMMA_WEB_ROOTDIR') |
| 39 _COVERAGE_FILENAME = 'coverage.ec' | 38 _COVERAGE_FILENAME = 'coverage.ec' |
| 40 _COVERAGE_RESULT_PATH = ('/data/data/com.google.android.apps.chrome/files/' + | 39 _COVERAGE_RESULT_PATH = ('/data/data/com.google.android.apps.chrome/files/' + |
| 41 _COVERAGE_FILENAME) | 40 _COVERAGE_FILENAME) |
| 42 _COVERAGE_META_INFO_PATH = os.path.join(os.environ.get('ANDROID_BUILD_TOP', | 41 _COVERAGE_META_INFO_PATH = os.path.join(os.environ.get('ANDROID_BUILD_TOP', |
| 43 ''), | 42 ''), |
| 44 'out/target/common/obj/APPS', | 43 'out/target/common/obj/APPS', |
| 45 'Chrome_intermediates/coverage.em') | 44 'Chrome_intermediates/coverage.em') |
| 46 _HOSTMACHINE_PERF_OUTPUT_FILE = '/tmp/chrome-profile' | 45 _HOSTMACHINE_PERF_OUTPUT_FILE = '/tmp/chrome-profile' |
| 47 _DEVICE_PERF_OUTPUT_SEARCH_PREFIX = (constants.DEVICE_PERF_OUTPUT_DIR + | 46 _DEVICE_PERF_OUTPUT_SEARCH_PREFIX = (constants.DEVICE_PERF_OUTPUT_DIR + |
| 48 '/chrome-profile*') | 47 '/chrome-profile*') |
| 49 _DEVICE_HAS_TEST_FILES = {} | 48 _DEVICE_HAS_TEST_FILES = {} |
| 50 | 49 |
| 51 def __init__(self, options, device, tests_iter, coverage, shard_index, apks, | 50 def __init__(self, options, device, shard_index, coverage, apks, |
| 52 ports_to_forward): | 51 ports_to_forward): |
| 53 """Create a new TestRunner. | 52 """Create a new TestRunner. |
| 54 | 53 |
| 55 Args: | 54 Args: |
| 56 options: An options object with the following required attributes: | 55 options: An options object with the following required attributes: |
| 57 - build_type: 'Release' or 'Debug'. | 56 - build_type: 'Release' or 'Debug'. |
| 58 - install_apk: Re-installs the apk if opted. | 57 - install_apk: Re-installs the apk if opted. |
| 59 - save_perf_json: Whether or not to save the JSON file from UI perf | 58 - save_perf_json: Whether or not to save the JSON file from UI perf |
| 60 tests. | 59 tests. |
| 61 - screenshot_failures: Take a screenshot for a test failure | 60 - screenshot_failures: Take a screenshot for a test failure |
| 62 - tool: Name of the Valgrind tool. | 61 - tool: Name of the Valgrind tool. |
| 63 - wait_for_debugger: blocks until the debugger is connected. | 62 - wait_for_debugger: blocks until the debugger is connected. |
| 64 - disable_assertions: Whether to disable java assertions on the device. | 63 - disable_assertions: Whether to disable java assertions on the device. |
| 65 device: Attached android device. | 64 device: Attached android device. |
| 66 tests_iter: A list of tests to be run. | 65 shard_index: Shard index. |
| 67 coverage: Collects coverage information if opted. | 66 coverage: Collects coverage information if opted. |
| 68 shard_index: shard # for this TestRunner, used to create unique port | |
| 69 numbers. | |
| 70 apks: A list of ApkInfo objects need to be installed. The first element | 67 apks: A list of ApkInfo objects need to be installed. The first element |
| 71 should be the tests apk, the rests could be the apks used in test. | 68 should be the tests apk, the rests could be the apks used in test. |
| 72 The default is ChromeTest.apk. | 69 The default is ChromeTest.apk. |
| 73 ports_to_forward: A list of port numbers for which to set up forwarders. | 70 ports_to_forward: A list of port numbers for which to set up forwarders. |
| 74 Can be optionally requested by a test case. | 71 Can be optionally requested by a test case. |
| 75 Raises: | 72 Raises: |
| 76 Exception: if coverage metadata is not available. | 73 Exception: if coverage metadata is not available. |
| 77 """ | 74 """ |
| 78 super(TestRunner, self).__init__( | 75 super(TestRunner, self).__init__(device, options.tool, options.build_type) |
| 79 device, options.tool, shard_index, options.build_type) | 76 self._lighttp_port = constants.LIGHTTPD_RANDOM_PORT_FIRST + shard_index |
| 80 | 77 |
| 81 if not apks: | 78 if not apks: |
| 82 apks = [apk_info.ApkInfo(options.test_apk_path, | 79 apks = [apk_info.ApkInfo(options.test_apk_path, |
| 83 options.test_apk_jar_path)] | 80 options.test_apk_jar_path)] |
| 84 | 81 |
| 85 self.build_type = options.build_type | 82 self.build_type = options.build_type |
| 86 self.install_apk = options.install_apk | 83 self.install_apk = options.install_apk |
| 87 self.test_data = options.test_data | 84 self.test_data = options.test_data |
| 88 self.save_perf_json = options.save_perf_json | 85 self.save_perf_json = options.save_perf_json |
| 89 self.screenshot_failures = options.screenshot_failures | 86 self.screenshot_failures = options.screenshot_failures |
| 90 self.wait_for_debugger = options.wait_for_debugger | 87 self.wait_for_debugger = options.wait_for_debugger |
| 91 self.disable_assertions = options.disable_assertions | 88 self.disable_assertions = options.disable_assertions |
| 92 | 89 |
| 93 self.tests_iter = tests_iter | |
| 94 self.coverage = coverage | 90 self.coverage = coverage |
| 95 self.apks = apks | 91 self.apks = apks |
| 96 self.test_apk = apks[0] | 92 self.test_apk = apks[0] |
| 97 self.instrumentation_class_path = self.test_apk.GetPackageName() | 93 self.instrumentation_class_path = self.test_apk.GetPackageName() |
| 98 self.ports_to_forward = ports_to_forward | 94 self.ports_to_forward = ports_to_forward |
| 99 | 95 |
| 100 self.test_results = test_result.TestResults() | |
| 101 self.forwarder = None | 96 self.forwarder = None |
| 102 | 97 |
| 103 if self.coverage: | 98 if self.coverage: |
| 104 if os.path.exists(TestRunner._COVERAGE_MERGED_FILENAME): | 99 if os.path.exists(TestRunner._COVERAGE_MERGED_FILENAME): |
| 105 os.remove(TestRunner._COVERAGE_MERGED_FILENAME) | 100 os.remove(TestRunner._COVERAGE_MERGED_FILENAME) |
| 106 if not os.path.exists(TestRunner._COVERAGE_META_INFO_PATH): | 101 if not os.path.exists(TestRunner._COVERAGE_META_INFO_PATH): |
| 107 raise Exception('FATAL ERROR in ' + sys.argv[0] + | 102 raise Exception('FATAL ERROR in ' + sys.argv[0] + |
| 108 ' : Coverage meta info [' + | 103 ' : Coverage meta info [' + |
| 109 TestRunner._COVERAGE_META_INFO_PATH + | 104 TestRunner._COVERAGE_META_INFO_PATH + |
| 110 '] does not exist.') | 105 '] does not exist.') |
| 111 if (not TestRunner._COVERAGE_WEB_ROOT_DIR or | 106 if (not TestRunner._COVERAGE_WEB_ROOT_DIR or |
| 112 not os.path.exists(TestRunner._COVERAGE_WEB_ROOT_DIR)): | 107 not os.path.exists(TestRunner._COVERAGE_WEB_ROOT_DIR)): |
| 113 raise Exception('FATAL ERROR in ' + sys.argv[0] + | 108 raise Exception('FATAL ERROR in ' + sys.argv[0] + |
| 114 ' : Path specified in $EMMA_WEB_ROOTDIR [' + | 109 ' : Path specified in $EMMA_WEB_ROOTDIR [' + |
| 115 TestRunner._COVERAGE_WEB_ROOT_DIR + | 110 TestRunner._COVERAGE_WEB_ROOT_DIR + |
| 116 '] does not exist.') | 111 '] does not exist.') |
| 117 | 112 |
| 118 def _GetTestsIter(self): | |
| 119 if not self.tests_iter: | |
| 120 # multiprocessing.Queue can't be pickled across processes if we have it as | |
| 121 # a member set during constructor. Grab one here instead. | |
| 122 self.tests_iter = (base_test_sharder.BaseTestSharder.tests_container) | |
| 123 assert self.tests_iter | |
| 124 return self.tests_iter | |
| 125 | |
| 126 def CopyTestFilesOnce(self): | 113 def CopyTestFilesOnce(self): |
| 127 """Pushes the test data files to the device. Installs the apk if opted.""" | 114 """Pushes the test data files to the device. Installs the apk if opted.""" |
| 128 if TestRunner._DEVICE_HAS_TEST_FILES.get(self.device, False): | 115 if TestRunner._DEVICE_HAS_TEST_FILES.get(self.device, False): |
| 129 logging.warning('Already copied test files to device %s, skipping.', | 116 logging.warning('Already copied test files to device %s, skipping.', |
| 130 self.device) | 117 self.device) |
| 131 return | 118 return |
| 132 for dest_host_pair in self.test_data: | 119 for dest_host_pair in self.test_data: |
| 133 dst_src = dest_host_pair.split(':',1) | 120 dst_src = dest_host_pair.split(':',1) |
| 134 dst_layer = dst_src[0] | 121 dst_layer = dst_src[0] |
| 135 host_src = dst_src[1] | 122 host_src = dst_src[1] |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 219 logging.warning('Unable to enable java asserts for %s, non rooted device', | 206 logging.warning('Unable to enable java asserts for %s, non rooted device', |
| 220 self.device) | 207 self.device) |
| 221 else: | 208 else: |
| 222 if self.adb.SetJavaAssertsEnabled(enable=not self.disable_assertions): | 209 if self.adb.SetJavaAssertsEnabled(enable=not self.disable_assertions): |
| 223 self.adb.Reboot(full_reboot=False) | 210 self.adb.Reboot(full_reboot=False) |
| 224 | 211 |
| 225 # We give different default value to launch HTTP server based on shard index | 212 # We give different default value to launch HTTP server based on shard index |
| 226 # because it may have race condition when multiple processes are trying to | 213 # because it may have race condition when multiple processes are trying to |
| 227 # launch lighttpd with same port at same time. | 214 # launch lighttpd with same port at same time. |
| 228 http_server_ports = self.LaunchTestHttpServer( | 215 http_server_ports = self.LaunchTestHttpServer( |
| 229 os.path.join(constants.CHROME_DIR), | 216 os.path.join(constants.CHROME_DIR), self._lighttp_port) |
| 230 (constants.LIGHTTPD_RANDOM_PORT_FIRST + self.shard_index)) | |
| 231 if self.ports_to_forward: | 217 if self.ports_to_forward: |
| 232 port_pairs = [(port, port) for port in self.ports_to_forward] | 218 port_pairs = [(port, port) for port in self.ports_to_forward] |
| 233 # We need to remember which ports the HTTP server is using, since the | 219 # We need to remember which ports the HTTP server is using, since the |
| 234 # forwarder will stomp on them otherwise. | 220 # forwarder will stomp on them otherwise. |
| 235 port_pairs.append(http_server_ports) | 221 port_pairs.append(http_server_ports) |
| 236 self.forwarder = forwarder.Forwarder(self.adb, self.build_type) | 222 self.forwarder = forwarder.Forwarder(self.adb, self.build_type) |
| 237 self.forwarder.Run(port_pairs, self.tool, '127.0.0.1') | 223 self.forwarder.Run(port_pairs, self.tool, '127.0.0.1') |
| 238 self.CopyTestFilesOnce() | 224 self.CopyTestFilesOnce() |
| 239 self.flags.AddFlags(['--enable-test-intents']) | 225 self.flags.AddFlags(['--enable-test-intents']) |
| 240 | 226 |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 if 'Manual' in annotations: | 369 if 'Manual' in annotations: |
| 384 return 600 * 60 | 370 return 600 * 60 |
| 385 if 'External' in annotations: | 371 if 'External' in annotations: |
| 386 return 10 * 60 | 372 return 10 * 60 |
| 387 if 'LargeTest' in annotations or _PERF_TEST_ANNOTATION in annotations: | 373 if 'LargeTest' in annotations or _PERF_TEST_ANNOTATION in annotations: |
| 388 return 5 * 60 | 374 return 5 * 60 |
| 389 if 'MediumTest' in annotations: | 375 if 'MediumTest' in annotations: |
| 390 return 3 * 60 | 376 return 3 * 60 |
| 391 return 1 * 60 | 377 return 1 * 60 |
| 392 | 378 |
| 393 def RunTests(self): | 379 def RunTest(self, test): |
| 394 """Runs the tests, generating the coverage if needed. | 380 """Runs the test, generating the coverage if needed. |
| 395 | 381 |
| 396 Returns: | 382 Returns: |
| 397 A test_result.TestResults object. | 383 A test_result.TestResults object. |
| 398 """ | 384 """ |
| 399 instrumentation_path = (self.instrumentation_class_path + | 385 instrumentation_path = (self.instrumentation_class_path + |
| 400 '/android.test.InstrumentationTestRunner') | 386 '/android.test.InstrumentationTestRunner') |
| 401 instrumentation_args = self._GetInstrumentationArgs() | 387 instrumentation_args = self._GetInstrumentationArgs() |
| 402 for test in self._GetTestsIter(): | 388 raw_result = None |
| 389 start_date_ms = None |
| 390 test_results = test_result.TestResults() |
| 391 try: |
| 392 self.TestSetup(test) |
| 393 start_date_ms = int(time.time()) * 1000 |
| 394 args_with_filter = dict(instrumentation_args) |
| 395 args_with_filter['class'] = test |
| 396 # |raw_results| is a list that should contain |
| 397 # a single TestResult object. |
| 398 logging.warn(args_with_filter) |
| 399 (raw_results, _) = self.adb.Adb().StartInstrumentation( |
| 400 instrumentation_path=instrumentation_path, |
| 401 instrumentation_args=args_with_filter, |
| 402 timeout_time=(self._GetIndividualTestTimeoutSecs(test) * |
| 403 self._GetIndividualTestTimeoutScale(test) * |
| 404 self.tool.GetTimeoutScale())) |
| 405 duration_ms = int(time.time()) * 1000 - start_date_ms |
| 406 assert len(raw_results) == 1 |
| 407 raw_result = raw_results[0] |
| 408 status_code = raw_result.GetStatusCode() |
| 409 if status_code: |
| 410 log = raw_result.GetFailureReason() |
| 411 if not log: |
| 412 log = 'No information.' |
| 413 if self.screenshot_failures or log.find('INJECT_EVENTS perm') >= 0: |
| 414 self._TakeScreenshot(test) |
| 415 test_results.failed = [test_result.SingleTestResult( |
| 416 test, start_date_ms, duration_ms, log)] |
| 417 else: |
| 418 test_results.ok = [test_result.SingleTestResult(test, start_date_ms, |
| 419 duration_ms)] |
| 420 # Catch exceptions thrown by StartInstrumentation(). |
| 421 # See ../../third_party/android/testrunner/adb_interface.py |
| 422 except (android_commands.errors.WaitForResponseTimedOutError, |
| 423 android_commands.errors.DeviceUnresponsiveError, |
| 424 android_commands.errors.InstrumentationError), e: |
| 425 if start_date_ms: |
| 426 duration_ms = int(time.time()) * 1000 - start_date_ms |
| 427 else: |
| 428 start_date_ms = int(time.time()) * 1000 |
| 429 duration_ms = 0 |
| 430 message = str(e) |
| 431 if not message: |
| 432 message = 'No information.' |
| 433 test_results.crashed = [test_result.SingleTestResult( |
| 434 test, start_date_ms, duration_ms, message)] |
| 403 raw_result = None | 435 raw_result = None |
| 404 start_date_ms = None | 436 self.TestTeardown(test, raw_result) |
| 405 try: | 437 return (test_results, None if test_results.ok else test) |
| 406 self.TestSetup(test) | |
| 407 start_date_ms = int(time.time()) * 1000 | |
| 408 args_with_filter = dict(instrumentation_args) | |
| 409 args_with_filter['class'] = test | |
| 410 # |raw_results| is a list that should contain | |
| 411 # a single TestResult object. | |
| 412 logging.warn(args_with_filter) | |
| 413 (raw_results, _) = self.adb.Adb().StartInstrumentation( | |
| 414 instrumentation_path=instrumentation_path, | |
| 415 instrumentation_args=args_with_filter, | |
| 416 timeout_time=(self._GetIndividualTestTimeoutSecs(test) * | |
| 417 self._GetIndividualTestTimeoutScale(test) * | |
| 418 self.tool.GetTimeoutScale())) | |
| 419 duration_ms = int(time.time()) * 1000 - start_date_ms | |
| 420 assert len(raw_results) == 1 | |
| 421 raw_result = raw_results[0] | |
| 422 status_code = raw_result.GetStatusCode() | |
| 423 if status_code: | |
| 424 log = raw_result.GetFailureReason() | |
| 425 if not log: | |
| 426 log = 'No information.' | |
| 427 if self.screenshot_failures or log.find('INJECT_EVENTS perm') >= 0: | |
| 428 self._TakeScreenshot(test) | |
| 429 self.test_results.failed += [test_result.SingleTestResult( | |
| 430 test, start_date_ms, duration_ms, log)] | |
| 431 else: | |
| 432 result = [test_result.SingleTestResult(test, start_date_ms, | |
| 433 duration_ms)] | |
| 434 self.test_results.ok += result | |
| 435 # Catch exceptions thrown by StartInstrumentation(). | |
| 436 # See ../../third_party/android/testrunner/adb_interface.py | |
| 437 except (android_commands.errors.WaitForResponseTimedOutError, | |
| 438 android_commands.errors.DeviceUnresponsiveError, | |
| 439 android_commands.errors.InstrumentationError), e: | |
| 440 if start_date_ms: | |
| 441 duration_ms = int(time.time()) * 1000 - start_date_ms | |
| 442 else: | |
| 443 start_date_ms = int(time.time()) * 1000 | |
| 444 duration_ms = 0 | |
| 445 message = str(e) | |
| 446 if not message: | |
| 447 message = 'No information.' | |
| 448 self.test_results.crashed += [test_result.SingleTestResult( | |
| 449 test, start_date_ms, duration_ms, message)] | |
| 450 raw_result = None | |
| 451 self.TestTeardown(test, raw_result) | |
| 452 return self.test_results | |
| OLD | NEW |