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 |