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