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 import logging | 5 import logging |
6 import os | 6 import os |
| 7 import re |
7 | 8 |
8 from pylib import android_commands | 9 from pylib import android_commands |
9 from pylib import constants | 10 from pylib import constants |
10 from pylib.android_commands import errors | 11 from pylib import pexpect |
11 from pylib.base import base_test_result | 12 from pylib.base import base_test_result |
12 from pylib.base import base_test_runner | 13 from pylib.base import base_test_runner |
13 from pylib.utils import run_tests_helper | 14 from pylib.utils import run_tests_helper |
14 | 15 |
15 import test_package_apk | 16 import test_package_apk |
16 import test_package_executable | 17 import test_package_exe |
17 | 18 |
18 | 19 |
19 # We're moving to using isolate files instead of harcoding | 20 # We're moving to using isolate files instead of harcoding |
20 # dependencies here. Look at the TODO in dispatch.py. | 21 # dependencies here. Look at the TODO in dispatch.py. |
21 def _GetDataFilesForTestSuite(test_suite_basename): | 22 def _GetDataFilesForTestSuite(test_suite_basename): |
22 """Returns a list of data files/dirs needed by the test suite. | 23 """Returns a list of data files/dirs needed by the test suite. |
23 | 24 |
24 Args: | 25 Args: |
25 test_suite_basename: The test suite basename (e.g. base_unittests). | 26 test_suite_basename: The test suite basename (e.g. base_unittests). |
26 | 27 |
(...skipping 14 matching lines...) Expand all Loading... |
41 def _TestSuiteRequiresMockTestServer(test_suite_basename): | 42 def _TestSuiteRequiresMockTestServer(test_suite_basename): |
42 """Returns True if the test suite requires mock test server.""" | 43 """Returns True if the test suite requires mock test server.""" |
43 tests_require_net_test_server = ['unit_tests', 'net_unittests', | 44 tests_require_net_test_server = ['unit_tests', 'net_unittests', |
44 'content_unittests', | 45 'content_unittests', |
45 'content_browsertests'] | 46 'content_browsertests'] |
46 return (test_suite_basename in | 47 return (test_suite_basename in |
47 tests_require_net_test_server) | 48 tests_require_net_test_server) |
48 | 49 |
49 | 50 |
50 class TestRunner(base_test_runner.BaseTestRunner): | 51 class TestRunner(base_test_runner.BaseTestRunner): |
51 """Single test suite attached to a single device. | |
52 | |
53 Args: | |
54 device: Device to run the tests. | |
55 test_suite: A specific test suite to run, empty to run all. | |
56 test_arguments: Additional arguments to pass to the test binary. | |
57 timeout: Timeout for each test. | |
58 cleanup_test_files: Whether or not to cleanup test files on device. | |
59 tool_name: Name of the Valgrind tool. | |
60 build_type: 'Release' or 'Debug'. | |
61 in_webkit_checkout: Whether the suite is being run from a WebKit checkout. | |
62 push_deps: If True, push all dependencies to the device. | |
63 test_apk_package_name: Apk package name for tests running in APKs. | |
64 test_activity_name: Test activity to invoke for APK tests. | |
65 command_line_file: Filename to use to pass arguments to tests. | |
66 deps_dir: The path to the dependency dir on the host to push to the device. | |
67 """ | |
68 | |
69 def __init__(self, device, test_suite, test_arguments, timeout, | 52 def __init__(self, device, test_suite, test_arguments, timeout, |
70 cleanup_test_files, tool_name, build_type, | 53 cleanup_test_files, tool_name, build_type, |
71 in_webkit_checkout, push_deps, test_apk_package_name=None, | 54 in_webkit_checkout, push_deps, test_apk_package_name=None, |
72 test_activity_name=None, command_line_file=None, deps_dir=None): | 55 test_activity_name=None, command_line_file=None, deps_dir=None): |
73 super(TestRunner, self).__init__(device, tool_name, build_type, push_deps) | 56 """Single test suite attached to a single device. |
| 57 |
| 58 Args: |
| 59 device: Device to run the tests. |
| 60 test_suite: A specific test suite to run, empty to run all. |
| 61 test_arguments: Additional arguments to pass to the test binary. |
| 62 timeout: Timeout for each test. |
| 63 cleanup_test_files: Whether or not to cleanup test files on device. |
| 64 tool_name: Name of the Valgrind tool. |
| 65 build_type: 'Release' or 'Debug'. |
| 66 in_webkit_checkout: Whether the suite is being run from a WebKit checkout. |
| 67 push_deps: If True, push all dependencies to the device. |
| 68 test_apk_package_name: Apk package name for tests running in APKs. |
| 69 test_activity_name: Test activity to invoke for APK tests. |
| 70 command_line_file: Filename to use to pass arguments to tests. |
| 71 deps_dir: The path to the dependency dir to push to the device. |
| 72 """ |
| 73 super(TestRunner, self).__init__(device, tool_name, build_type, push_deps, |
| 74 cleanup_test_files) |
74 self._running_on_emulator = self.device.startswith('emulator') | 75 self._running_on_emulator = self.device.startswith('emulator') |
75 self._test_arguments = test_arguments | 76 self._test_arguments = test_arguments |
76 self.in_webkit_checkout = in_webkit_checkout | 77 self.in_webkit_checkout = in_webkit_checkout |
77 self._cleanup_test_files = cleanup_test_files | |
78 self._deps_dir = deps_dir | 78 self._deps_dir = deps_dir |
| 79 if timeout == 0: |
| 80 timeout = 60 |
| 81 # On a VM (e.g. chromium buildbots), this timeout is way too small. |
| 82 if os.environ.get('BUILDBOT_SLAVENAME'): |
| 83 timeout = timeout * 2 |
| 84 self.timeout = timeout * self.tool.GetTimeoutScale() |
79 | 85 |
80 logging.warning('Test suite: ' + test_suite) | 86 logging.warning('Test suite: ' + test_suite) |
81 if os.path.splitext(test_suite)[1] == '.apk': | 87 if os.path.splitext(test_suite)[1] == '.apk': |
82 self.test_package = test_package_apk.TestPackageApk( | 88 self.test_package = test_package_apk.TestPackageApk( |
83 self.adb, | 89 self.adb, |
84 device, | 90 device, |
85 test_suite, | 91 test_suite, |
86 timeout, | |
87 self._cleanup_test_files, | |
88 self.tool, | 92 self.tool, |
89 test_apk_package_name, | 93 test_apk_package_name, |
90 test_activity_name, | 94 test_activity_name, |
91 command_line_file) | 95 command_line_file) |
92 else: | 96 else: |
93 # Put a copy into the android out/target directory, to allow stack trace | 97 # Put a copy into the android out/target directory, to allow stack trace |
94 # generation. | 98 # generation. |
95 symbols_dir = os.path.join(constants.DIR_SOURCE_ROOT, 'out', build_type, | 99 symbols_dir = os.path.join(constants.DIR_SOURCE_ROOT, 'out', build_type, |
96 'lib.target') | 100 'lib.target') |
97 self.test_package = test_package_executable.TestPackageExecutable( | 101 self.test_package = test_package_exe.TestPackageExecutable( |
98 self.adb, | 102 self.adb, |
99 device, | 103 device, |
100 test_suite, | 104 test_suite, |
101 timeout, | |
102 self._cleanup_test_files, | |
103 self.tool, | 105 self.tool, |
104 symbols_dir) | 106 symbols_dir) |
105 | 107 |
106 #override | 108 #override |
107 def InstallTestPackage(self): | 109 def InstallTestPackage(self): |
108 self.test_package.StripAndCopyExecutable() | 110 self.test_package.Install() |
109 | 111 |
110 #override | 112 #override |
111 def PushDataDeps(self): | 113 def PushDataDeps(self): |
112 self.adb.WaitForSdCardReady(20) | 114 self.adb.WaitForSdCardReady(20) |
113 self.tool.CopyFiles() | 115 self.tool.CopyFiles() |
114 if self.test_package.test_suite_basename == 'webkit_unit_tests': | 116 if self.test_package.test_suite_basename == 'webkit_unit_tests': |
115 self.PushWebKitUnitTestsData() | 117 self.PushWebKitUnitTestsData() |
116 return | 118 return |
117 | 119 |
118 if not self._deps_dir: | 120 if not self._deps_dir: |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 'filter', | 169 'filter', |
168 self.test_package.test_suite_basename) | 170 self.test_package.test_suite_basename) |
169 disabled_tests = run_tests_helper.GetExpectations( | 171 disabled_tests = run_tests_helper.GetExpectations( |
170 gtest_filter_base_path + '_disabled') | 172 gtest_filter_base_path + '_disabled') |
171 if self._running_on_emulator: | 173 if self._running_on_emulator: |
172 # Append emulator's filter file. | 174 # Append emulator's filter file. |
173 disabled_tests.extend(run_tests_helper.GetExpectations( | 175 disabled_tests.extend(run_tests_helper.GetExpectations( |
174 gtest_filter_base_path + '_emulator_additional_disabled')) | 176 gtest_filter_base_path + '_emulator_additional_disabled')) |
175 return disabled_tests | 177 return disabled_tests |
176 | 178 |
| 179 def _ParseTestOutput(self, p): |
| 180 """Process the test output. |
| 181 |
| 182 Args: |
| 183 p: An instance of pexpect spawn class. |
| 184 |
| 185 Returns: |
| 186 A TestRunResults object. |
| 187 """ |
| 188 results = base_test_result.TestRunResults() |
| 189 |
| 190 # Test case statuses. |
| 191 re_run = re.compile('\[ RUN \] ?(.*)\r\n') |
| 192 re_fail = re.compile('\[ FAILED \] ?(.*)\r\n') |
| 193 re_ok = re.compile('\[ OK \] ?(.*?) .*\r\n') |
| 194 |
| 195 # Test run statuses. |
| 196 re_passed = re.compile('\[ PASSED \] ?(.*)\r\n') |
| 197 re_runner_fail = re.compile('\[ RUNNER_FAILED \] ?(.*)\r\n') |
| 198 # Signal handlers are installed before starting tests |
| 199 # to output the CRASHED marker when a crash happens. |
| 200 re_crash = re.compile('\[ CRASHED \](.*)\r\n') |
| 201 |
| 202 log = '' |
| 203 try: |
| 204 while True: |
| 205 full_test_name = None |
| 206 found = p.expect([re_run, re_passed, re_runner_fail], |
| 207 timeout=self.timeout) |
| 208 if found == 1: # re_passed |
| 209 break |
| 210 elif found == 2: # re_runner_fail |
| 211 break |
| 212 else: # re_run |
| 213 full_test_name = p.match.group(1).replace('\r', '') |
| 214 found = p.expect([re_ok, re_fail, re_crash], timeout=self.timeout) |
| 215 log = p.before.replace('\r', '') |
| 216 if found == 0: # re_ok |
| 217 if full_test_name == p.match.group(1).replace('\r', ''): |
| 218 results.AddResult(base_test_result.BaseTestResult( |
| 219 full_test_name, base_test_result.ResultType.PASS, |
| 220 log=log)) |
| 221 elif found == 2: # re_crash |
| 222 results.AddResult(base_test_result.BaseTestResult( |
| 223 full_test_name, base_test_result.ResultType.CRASH, |
| 224 log=log)) |
| 225 break |
| 226 else: # re_fail |
| 227 results.AddResult(base_test_result.BaseTestResult( |
| 228 full_test_name, base_test_result.ResultType.FAIL, log=log)) |
| 229 except pexpect.EOF: |
| 230 logging.error('Test terminated - EOF') |
| 231 # We're here because either the device went offline, or the test harness |
| 232 # crashed without outputting the CRASHED marker (crbug.com/175538). |
| 233 if not self.adb.IsOnline(): |
| 234 raise android_commands.errors.DeviceUnresponsiveError( |
| 235 'Device %s went offline.' % self.device) |
| 236 if full_test_name: |
| 237 results.AddResult(base_test_result.BaseTestResult( |
| 238 full_test_name, base_test_result.ResultType.CRASH, |
| 239 log=p.before.replace('\r', ''))) |
| 240 except pexpect.TIMEOUT: |
| 241 logging.error('Test terminated after %d second timeout.', |
| 242 self.timeout) |
| 243 if full_test_name: |
| 244 results.AddResult(base_test_result.BaseTestResult( |
| 245 full_test_name, base_test_result.ResultType.TIMEOUT, |
| 246 log=p.before.replace('\r', ''))) |
| 247 finally: |
| 248 p.close() |
| 249 |
| 250 ret_code = self.test_package.GetGTestReturnCode() |
| 251 if ret_code: |
| 252 logging.critical( |
| 253 'gtest exit code: %d\npexpect.before: %s\npexpect.after: %s', |
| 254 ret_code, p.before, p.after) |
| 255 |
| 256 return results |
| 257 |
177 #override | 258 #override |
178 def RunTest(self, test): | 259 def RunTest(self, test): |
179 test_results = base_test_result.TestRunResults() | 260 test_results = base_test_result.TestRunResults() |
180 if not test: | 261 if not test: |
181 return test_results, None | 262 return test_results, None |
182 | 263 |
183 try: | 264 try: |
184 self.test_package.ClearApplicationState() | 265 self.test_package.ClearApplicationState() |
185 self.test_package.CreateTestRunnerScript(test, self._test_arguments) | 266 self.test_package.CreateTestRunnerScript(test, self._test_arguments) |
186 test_results = self.test_package.RunTestsAndListResults() | 267 test_results = self._ParseTestOutput(self.test_package.SpawnTestProcess()) |
187 finally: | 268 finally: |
188 self.CleanupSpawningServerState() | 269 self.CleanupSpawningServerState() |
189 # Calculate unknown test results. | 270 # Calculate unknown test results. |
190 all_tests = set(test.split(':')) | 271 all_tests = set(test.split(':')) |
191 all_tests_ran = set([t.GetName() for t in test_results.GetAll()]) | 272 all_tests_ran = set([t.GetName() for t in test_results.GetAll()]) |
192 unknown_tests = all_tests - all_tests_ran | 273 unknown_tests = all_tests - all_tests_ran |
193 test_results.AddResults( | 274 test_results.AddResults( |
194 [base_test_result.BaseTestResult(t, base_test_result.ResultType.UNKNOWN) | 275 [base_test_result.BaseTestResult(t, base_test_result.ResultType.UNKNOWN) |
195 for t in unknown_tests]) | 276 for t in unknown_tests]) |
196 retry = ':'.join([t.GetName() for t in test_results.GetNotPass()]) | 277 retry = ':'.join([t.GetName() for t in test_results.GetNotPass()]) |
197 return test_results, retry | 278 return test_results, retry |
198 | 279 |
199 #override | 280 #override |
200 def SetUp(self): | 281 def SetUp(self): |
201 """Sets up necessary test enviroment for the test suite.""" | 282 """Sets up necessary test enviroment for the test suite.""" |
202 super(TestRunner, self).SetUp() | 283 super(TestRunner, self).SetUp() |
203 if _TestSuiteRequiresMockTestServer(self.test_package.test_suite_basename): | 284 if _TestSuiteRequiresMockTestServer(self.test_package.test_suite_basename): |
204 self.LaunchChromeTestServerSpawner() | 285 self.LaunchChromeTestServerSpawner() |
205 self.tool.SetupEnvironment() | 286 self.tool.SetupEnvironment() |
206 | 287 |
207 #override | 288 #override |
208 def TearDown(self): | 289 def TearDown(self): |
209 """Cleans up the test enviroment for the test suite.""" | 290 """Cleans up the test enviroment for the test suite.""" |
210 self.tool.CleanUpEnvironment() | 291 self.tool.CleanUpEnvironment() |
211 if self._cleanup_test_files: | |
212 self.adb.RemovePushedFiles() | |
213 super(TestRunner, self).TearDown() | 292 super(TestRunner, self).TearDown() |
OLD | NEW |