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 def _TestSuiteRequiresMockTestServer(test_suite_basename): | 20 def _TestSuiteRequiresMockTestServer(test_suite_basename): |
20 """Returns True if the test suite requires mock test server.""" | 21 """Returns True if the test suite requires mock test server.""" |
21 tests_require_net_test_server = ['unit_tests', 'net_unittests', | 22 tests_require_net_test_server = ['unit_tests', 'net_unittests', |
22 'content_unittests', | 23 'content_unittests', |
23 'content_browsertests'] | 24 'content_browsertests'] |
24 return (test_suite_basename in | 25 return (test_suite_basename in |
25 tests_require_net_test_server) | 26 tests_require_net_test_server) |
26 | 27 |
27 | 28 |
28 class TestRunner(base_test_runner.BaseTestRunner): | 29 class TestRunner(base_test_runner.BaseTestRunner): |
29 """Single test suite attached to a single device. | |
30 | |
31 Args: | |
32 device: Device to run the tests. | |
33 test_suite: A specific test suite to run, empty to run all. | |
34 test_arguments: Additional arguments to pass to the test binary. | |
35 timeout: Timeout for each test. | |
36 cleanup_test_files: Whether or not to cleanup test files on device. | |
37 tool_name: Name of the Valgrind tool. | |
38 build_type: 'Release' or 'Debug'. | |
39 in_webkit_checkout: Whether the suite is being run from a WebKit checkout. | |
40 push_deps: If True, push all dependencies to the device. | |
41 test_apk_package_name: Apk package name for tests running in APKs. | |
42 test_activity_name: Test activity to invoke for APK tests. | |
43 command_line_file: Filename to use to pass arguments to tests. | |
44 """ | |
45 | |
46 def __init__(self, device, test_suite, test_arguments, timeout, | 30 def __init__(self, device, test_suite, test_arguments, timeout, |
47 cleanup_test_files, tool_name, build_type, | 31 cleanup_test_files, tool_name, build_type, |
48 in_webkit_checkout, push_deps, test_apk_package_name=None, | 32 in_webkit_checkout, push_deps, test_apk_package_name=None, |
49 test_activity_name=None, command_line_file=None): | 33 test_activity_name=None, command_line_file=None): |
50 super(TestRunner, self).__init__(device, tool_name, build_type, push_deps) | 34 """Single test suite attached to a single device. |
| 35 |
| 36 Args: |
| 37 device: Device to run the tests. |
| 38 test_suite: A specific test suite to run, empty to run all. |
| 39 test_arguments: Additional arguments to pass to the test binary. |
| 40 timeout: Timeout for each test. |
| 41 cleanup_test_files: Whether or not to cleanup test files on device. |
| 42 tool_name: Name of the Valgrind tool. |
| 43 build_type: 'Release' or 'Debug'. |
| 44 in_webkit_checkout: Whether the suite is being run from a WebKit checkout. |
| 45 push_deps: If True, push all dependencies to the device. |
| 46 test_apk_package_name: Apk package name for tests running in APKs. |
| 47 test_activity_name: Test activity to invoke for APK tests. |
| 48 command_line_file: Filename to use to pass arguments to tests. |
| 49 """ |
| 50 super(TestRunner, self).__init__(device, tool_name, build_type, push_deps, |
| 51 cleanup_test_files) |
51 self._running_on_emulator = self.device.startswith('emulator') | 52 self._running_on_emulator = self.device.startswith('emulator') |
52 self._test_arguments = test_arguments | 53 self._test_arguments = test_arguments |
53 self.in_webkit_checkout = in_webkit_checkout | 54 self.in_webkit_checkout = in_webkit_checkout |
54 self._cleanup_test_files = cleanup_test_files | 55 if timeout == 0: |
| 56 timeout = 60 |
| 57 # On a VM (e.g. chromium buildbots), this timeout is way too small. |
| 58 if os.environ.get('BUILDBOT_SLAVENAME'): |
| 59 timeout = timeout * 2 |
| 60 self.timeout = timeout * self.tool.GetTimeoutScale() |
55 | 61 |
56 logging.warning('Test suite: ' + test_suite) | 62 logging.warning('Test suite: ' + test_suite) |
57 if os.path.splitext(test_suite)[1] == '.apk': | 63 if os.path.splitext(test_suite)[1] == '.apk': |
58 self.test_package = test_package_apk.TestPackageApk( | 64 self.test_package = test_package_apk.TestPackageApk( |
59 self.adb, | 65 self.adb, |
60 device, | 66 device, |
61 test_suite, | 67 test_suite, |
62 timeout, | |
63 self._cleanup_test_files, | |
64 self.tool, | 68 self.tool, |
65 test_apk_package_name, | 69 test_apk_package_name, |
66 test_activity_name, | 70 test_activity_name, |
67 command_line_file) | 71 command_line_file) |
68 else: | 72 else: |
69 # Put a copy into the android out/target directory, to allow stack trace | 73 # Put a copy into the android out/target directory, to allow stack trace |
70 # generation. | 74 # generation. |
71 symbols_dir = os.path.join(constants.DIR_SOURCE_ROOT, 'out', build_type, | 75 symbols_dir = os.path.join(constants.DIR_SOURCE_ROOT, 'out', build_type, |
72 'lib.target') | 76 'lib.target') |
73 self.test_package = test_package_executable.TestPackageExecutable( | 77 self.test_package = test_package_exe.TestPackageExecutable( |
74 self.adb, | 78 self.adb, |
75 device, | 79 device, |
76 test_suite, | 80 test_suite, |
77 timeout, | |
78 self._cleanup_test_files, | |
79 self.tool, | 81 self.tool, |
80 symbols_dir) | 82 symbols_dir) |
81 | 83 |
82 #override | 84 #override |
83 def InstallTestPackage(self): | 85 def InstallTestPackage(self): |
84 self.test_package.StripAndCopyExecutable() | 86 self.test_package.Install() |
85 | 87 |
86 #override | 88 #override |
87 def PushDataDeps(self): | 89 def PushDataDeps(self): |
88 self.adb.WaitForSdCardReady(20) | 90 self.adb.WaitForSdCardReady(20) |
89 self.tool.CopyFiles() | 91 self.tool.CopyFiles() |
90 if self.test_package.test_suite_basename == 'webkit_unit_tests': | 92 if self.test_package.test_suite_basename == 'webkit_unit_tests': |
91 self.PushWebKitUnitTestsData() | 93 self.PushWebKitUnitTestsData() |
92 return | 94 return |
93 | 95 |
94 if os.path.exists(constants.ISOLATE_DEPS_DIR): | 96 if os.path.exists(constants.ISOLATE_DEPS_DIR): |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 'filter', | 138 'filter', |
137 self.test_package.test_suite_basename) | 139 self.test_package.test_suite_basename) |
138 disabled_tests = run_tests_helper.GetExpectations( | 140 disabled_tests = run_tests_helper.GetExpectations( |
139 gtest_filter_base_path + '_disabled') | 141 gtest_filter_base_path + '_disabled') |
140 if self._running_on_emulator: | 142 if self._running_on_emulator: |
141 # Append emulator's filter file. | 143 # Append emulator's filter file. |
142 disabled_tests.extend(run_tests_helper.GetExpectations( | 144 disabled_tests.extend(run_tests_helper.GetExpectations( |
143 gtest_filter_base_path + '_emulator_additional_disabled')) | 145 gtest_filter_base_path + '_emulator_additional_disabled')) |
144 return disabled_tests | 146 return disabled_tests |
145 | 147 |
| 148 def _ParseTestOutput(self, p): |
| 149 """Process the test output. |
| 150 |
| 151 Args: |
| 152 p: An instance of pexpect spawn class. |
| 153 |
| 154 Returns: |
| 155 A TestRunResults object. |
| 156 """ |
| 157 results = base_test_result.TestRunResults() |
| 158 |
| 159 # Test case statuses. |
| 160 re_run = re.compile('\[ RUN \] ?(.*)\r\n') |
| 161 re_fail = re.compile('\[ FAILED \] ?(.*)\r\n') |
| 162 re_ok = re.compile('\[ OK \] ?(.*?) .*\r\n') |
| 163 |
| 164 # Test run statuses. |
| 165 re_passed = re.compile('\[ PASSED \] ?(.*)\r\n') |
| 166 re_runner_fail = re.compile('\[ RUNNER_FAILED \] ?(.*)\r\n') |
| 167 # Signal handlers are installed before starting tests |
| 168 # to output the CRASHED marker when a crash happens. |
| 169 re_crash = re.compile('\[ CRASHED \](.*)\r\n') |
| 170 |
| 171 log = '' |
| 172 try: |
| 173 while True: |
| 174 full_test_name = None |
| 175 found = p.expect([re_run, re_passed, re_runner_fail], |
| 176 timeout=self.timeout) |
| 177 if found == 1: # re_passed |
| 178 break |
| 179 elif found == 2: # re_runner_fail |
| 180 break |
| 181 else: # re_run |
| 182 full_test_name = p.match.group(1).replace('\r', '') |
| 183 found = p.expect([re_ok, re_fail, re_crash], timeout=self.timeout) |
| 184 log = p.before.replace('\r', '') |
| 185 if found == 0: # re_ok |
| 186 if full_test_name == p.match.group(1).replace('\r', ''): |
| 187 results.AddResult(base_test_result.BaseTestResult( |
| 188 full_test_name, base_test_result.ResultType.PASS, |
| 189 log=log)) |
| 190 elif found == 2: # re_crash |
| 191 results.AddResult(base_test_result.BaseTestResult( |
| 192 full_test_name, base_test_result.ResultType.CRASH, |
| 193 log=log)) |
| 194 break |
| 195 else: # re_fail |
| 196 results.AddResult(base_test_result.BaseTestResult( |
| 197 full_test_name, base_test_result.ResultType.FAIL, log=log)) |
| 198 except pexpect.EOF: |
| 199 logging.error('Test terminated - EOF') |
| 200 # We're here because either the device went offline, or the test harness |
| 201 # crashed without outputting the CRASHED marker (crbug.com/175538). |
| 202 if not self.adb.IsOnline(): |
| 203 raise android_commands.errors.DeviceUnresponsiveError( |
| 204 'Device %s went offline.' % self.device) |
| 205 if full_test_name: |
| 206 results.AddResult(base_test_result.BaseTestResult( |
| 207 full_test_name, base_test_result.ResultType.CRASH, |
| 208 log=p.before.replace('\r', ''))) |
| 209 except pexpect.TIMEOUT: |
| 210 logging.error('Test terminated after %d second timeout.', |
| 211 self.timeout) |
| 212 if full_test_name: |
| 213 results.AddResult(base_test_result.BaseTestResult( |
| 214 full_test_name, base_test_result.ResultType.TIMEOUT, |
| 215 log=p.before.replace('\r', ''))) |
| 216 finally: |
| 217 p.close() |
| 218 |
| 219 ret_code = self.test_package.GetGTestReturnCode() |
| 220 if ret_code: |
| 221 logging.critical( |
| 222 'gtest exit code: %d\npexpect.before: %s\npexpect.after: %s', |
| 223 ret_code, p.before, p.after) |
| 224 |
| 225 return results |
| 226 |
146 #override | 227 #override |
147 def RunTest(self, test): | 228 def RunTest(self, test): |
148 test_results = base_test_result.TestRunResults() | 229 test_results = base_test_result.TestRunResults() |
149 if not test: | 230 if not test: |
150 return test_results, None | 231 return test_results, None |
151 | 232 |
152 try: | 233 try: |
153 self.test_package.ClearApplicationState() | 234 self.test_package.ClearApplicationState() |
154 self.test_package.CreateTestRunnerScript(test, self._test_arguments) | 235 self.test_package.CreateCommandLineFileOnDevice( |
155 test_results = self.test_package.RunTestsAndListResults() | 236 test, self._test_arguments) |
| 237 test_results = self._ParseTestOutput(self.test_package.SpawnTestProcess()) |
156 finally: | 238 finally: |
157 self.CleanupSpawningServerState() | 239 self.CleanupSpawningServerState() |
158 # Calculate unknown test results. | 240 # Calculate unknown test results. |
159 all_tests = set(test.split(':')) | 241 all_tests = set(test.split(':')) |
160 all_tests_ran = set([t.GetName() for t in test_results.GetAll()]) | 242 all_tests_ran = set([t.GetName() for t in test_results.GetAll()]) |
161 unknown_tests = all_tests - all_tests_ran | 243 unknown_tests = all_tests - all_tests_ran |
162 test_results.AddResults( | 244 test_results.AddResults( |
163 [base_test_result.BaseTestResult(t, base_test_result.ResultType.UNKNOWN) | 245 [base_test_result.BaseTestResult(t, base_test_result.ResultType.UNKNOWN) |
164 for t in unknown_tests]) | 246 for t in unknown_tests]) |
165 retry = ':'.join([t.GetName() for t in test_results.GetNotPass()]) | 247 retry = ':'.join([t.GetName() for t in test_results.GetNotPass()]) |
166 return test_results, retry | 248 return test_results, retry |
167 | 249 |
168 #override | 250 #override |
169 def SetUp(self): | 251 def SetUp(self): |
170 """Sets up necessary test enviroment for the test suite.""" | 252 """Sets up necessary test enviroment for the test suite.""" |
171 super(TestRunner, self).SetUp() | 253 super(TestRunner, self).SetUp() |
172 if _TestSuiteRequiresMockTestServer(self.test_package.test_suite_basename): | 254 if _TestSuiteRequiresMockTestServer(self.test_package.test_suite_basename): |
173 self.LaunchChromeTestServerSpawner() | 255 self.LaunchChromeTestServerSpawner() |
174 self.tool.SetupEnvironment() | 256 self.tool.SetupEnvironment() |
175 | 257 |
176 #override | 258 #override |
177 def TearDown(self): | 259 def TearDown(self): |
178 """Cleans up the test enviroment for the test suite.""" | 260 """Cleans up the test enviroment for the test suite.""" |
179 self.tool.CleanUpEnvironment() | 261 self.tool.CleanUpEnvironment() |
180 if self._cleanup_test_files: | |
181 self.adb.RemovePushedFiles() | |
182 super(TestRunner, self).TearDown() | 262 super(TestRunner, self).TearDown() |
OLD | NEW |