OLD | NEW |
---|---|
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 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 """Dispatches tests, either sharding or replicating them. | 5 """Dispatches tests, either sharding or replicating them. |
6 | 6 |
7 Performs the following steps: | 7 Performs the following steps: |
8 * Create a test collection factory, using the given tests | 8 * Create a test collection factory, using the given tests |
9 - If sharding: test collection factory returns the same shared test collection | 9 - If sharding: test collection factory returns the same shared test collection |
10 to all test runners | 10 to all test runners |
11 - If replciating: test collection factory returns a unique test collection to | 11 - If replciating: test collection factory returns a unique test collection to |
12 each test runner, with the same set of tests in each. | 12 each test runner, with the same set of tests in each. |
13 * Create a test runner for each device. | 13 * Create a test runner for each device. |
14 * Run each test runner in its own thread, grabbing tests from the test | 14 * Run each test runner in its own thread, grabbing tests from the test |
15 collection until there are no tests left. | 15 collection until there are no tests left. |
16 """ | 16 """ |
17 | 17 |
18 # TODO(jbudorick) Deprecate and remove this class after any relevant parts have | 18 # TODO(jbudorick) Deprecate and remove this class after any relevant parts have |
19 # been ported to the new environment / test instance model. | 19 # been ported to the new environment / test instance model. |
20 | 20 |
21 import logging | 21 import logging |
22 import threading | 22 import threading |
23 | 23 |
24 from devil.android import device_blacklist | |
24 from devil.android import device_errors | 25 from devil.android import device_errors |
25 from devil.utils import reraiser_thread | 26 from devil.utils import reraiser_thread |
26 from devil.utils import watchdog_timer | 27 from devil.utils import watchdog_timer |
27 from pylib import constants | 28 from pylib import constants |
28 from pylib.base import base_test_result | 29 from pylib.base import base_test_result |
29 from pylib.base import test_collection | 30 from pylib.base import test_collection |
30 | 31 |
31 | 32 |
32 DEFAULT_TIMEOUT = 7 * 60 # seven minutes | 33 DEFAULT_TIMEOUT = 7 * 60 # seven minutes |
33 | 34 |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
123 except: | 124 except: |
124 # An unhandleable exception, ensure tests get run by another device and | 125 # An unhandleable exception, ensure tests get run by another device and |
125 # reraise this exception on the main thread. | 126 # reraise this exception on the main thread. |
126 collection.add(test) | 127 collection.add(test) |
127 raise | 128 raise |
128 finally: | 129 finally: |
129 # Retries count as separate tasks so always mark the popped test as done. | 130 # Retries count as separate tasks so always mark the popped test as done. |
130 collection.test_completed() | 131 collection.test_completed() |
131 | 132 |
132 | 133 |
133 def _SetUp(runner_factory, device, out_runners, threadsafe_counter): | 134 def _SetUp(runner_factory, device, out_runners, threadsafe_counter, |
135 blacklist_file=None): | |
134 """Creates a test runner for each device and calls SetUp() in parallel. | 136 """Creates a test runner for each device and calls SetUp() in parallel. |
135 | 137 |
136 Note: if a device is unresponsive the corresponding TestRunner will not be | 138 Note: if a device is unresponsive the corresponding TestRunner will not be |
137 added to out_runners. | 139 added to out_runners. |
138 | 140 |
139 Args: | 141 Args: |
140 runner_factory: Callable that takes a device and index and returns a | 142 runner_factory: Callable that takes a device and index and returns a |
141 TestRunner object. | 143 TestRunner object. |
142 device: The device serial number to set up. | 144 device: The device serial number to set up. |
143 out_runners: List to add the successfully set up TestRunner object. | 145 out_runners: List to add the successfully set up TestRunner object. |
144 threadsafe_counter: A _ThreadSafeCounter object used to get shard indices. | 146 threadsafe_counter: A _ThreadSafeCounter object used to get shard indices. |
147 blacklist_file: Path to device blacklist. | |
145 """ | 148 """ |
146 try: | 149 try: |
147 index = threadsafe_counter.GetAndIncrement() | 150 index = threadsafe_counter.GetAndIncrement() |
148 logging.warning('Creating shard %s for device %s.', index, device) | 151 logging.warning('Creating shard %s for device %s.', index, device) |
152 | |
153 blacklist = (device_blacklist.Blacklist(blacklist_file).Read() | |
jbudorick
2016/06/06 22:52:27
This isn't the right place to do this; it'll get r
rnephew (Reviews Here)
2016/06/06 23:33:21
Yeah, that makes for a much much simpler solution.
| |
154 if blacklist_file | |
155 else None) | |
156 if blacklist and str(device) in blacklist: | |
157 logging.info('Device %s is in blacklist. Not creating shard %s', | |
158 str(device), index) | |
159 return | |
160 | |
149 runner = runner_factory(device, index) | 161 runner = runner_factory(device, index) |
150 runner.SetUp() | 162 runner.SetUp() |
151 out_runners.append(runner) | 163 out_runners.append(runner) |
152 except (device_errors.CommandFailedError, | 164 except (device_errors.CommandFailedError, |
153 device_errors.CommandTimeoutError, | 165 device_errors.CommandTimeoutError, |
154 device_errors.DeviceUnreachableError): | 166 device_errors.DeviceUnreachableError): |
155 logging.exception('Failed to create shard for %s', str(device)) | 167 logging.exception('Failed to create shard for %s', str(device)) |
156 | 168 |
157 | 169 |
158 def _RunAllTests(runners, test_collection_factory, num_retries, timeout=None, | 170 def _RunAllTests(runners, test_collection_factory, num_retries, timeout=None, |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
207 run_results.AddResults(base_test_result.BaseTestResult( | 219 run_results.AddResults(base_test_result.BaseTestResult( |
208 t, base_test_result.ResultType.UNKNOWN) for t in tc.test_names()) | 220 t, base_test_result.ResultType.UNKNOWN) for t in tc.test_names()) |
209 | 221 |
210 for r in results: | 222 for r in results: |
211 run_results.AddTestRunResults(r) | 223 run_results.AddTestRunResults(r) |
212 if not run_results.DidRunPass(): | 224 if not run_results.DidRunPass(): |
213 exit_code = constants.ERROR_EXIT_CODE | 225 exit_code = constants.ERROR_EXIT_CODE |
214 return (run_results, exit_code) | 226 return (run_results, exit_code) |
215 | 227 |
216 | 228 |
217 def _CreateRunners(runner_factory, devices, timeout=None): | 229 def _CreateRunners(runner_factory, devices, timeout=None, blacklist_file=None): |
218 """Creates a test runner for each device and calls SetUp() in parallel. | 230 """Creates a test runner for each device and calls SetUp() in parallel. |
219 | 231 |
220 Note: if a device is unresponsive the corresponding TestRunner will not be | 232 Note: if a device is unresponsive the corresponding TestRunner will not be |
221 included in the returned list. | 233 included in the returned list. |
222 | 234 |
223 Args: | 235 Args: |
224 runner_factory: Callable that takes a device and index and returns a | 236 runner_factory: Callable that takes a device and index and returns a |
225 TestRunner object. | 237 TestRunner object. |
226 devices: List of device serial numbers as strings. | 238 devices: List of device serial numbers as strings. |
227 timeout: Watchdog timeout in seconds, defaults to the default timeout. | 239 timeout: Watchdog timeout in seconds, defaults to the default timeout. |
240 blacklist_file: Path to device blacklist file. | |
228 | 241 |
229 Returns: | 242 Returns: |
230 A list of TestRunner objects. | 243 A list of TestRunner objects. |
231 """ | 244 """ |
232 logging.warning('Creating %s test %s.', len(devices), | 245 logging.warning('Creating %s test %s.', len(devices), |
233 'runners' if len(devices) != 1 else 'runner') | 246 'runners' if len(devices) != 1 else 'runner') |
234 runners = [] | 247 runners = [] |
235 counter = _ThreadSafeCounter() | 248 counter = _ThreadSafeCounter() |
236 threads = reraiser_thread.ReraiserThreadGroup( | 249 threads = reraiser_thread.ReraiserThreadGroup( |
237 [reraiser_thread.ReraiserThread(_SetUp, | 250 [reraiser_thread.ReraiserThread(_SetUp, |
238 [runner_factory, d, runners, counter], | 251 [runner_factory, d, runners, counter, |
252 blacklist_file], | |
239 name=str(d)[-4:]) | 253 name=str(d)[-4:]) |
240 for d in devices]) | 254 for d in devices]) |
241 threads.StartAll() | 255 threads.StartAll() |
242 threads.JoinAll(watchdog_timer.WatchdogTimer(timeout)) | 256 threads.JoinAll(watchdog_timer.WatchdogTimer(timeout)) |
243 return runners | 257 return runners |
244 | 258 |
245 | 259 |
246 def _TearDownRunners(runners, timeout=None): | 260 def _TearDownRunners(runners, timeout=None): |
247 """Calls TearDown() for each test runner in parallel. | 261 """Calls TearDown() for each test runner in parallel. |
248 | 262 |
(...skipping 25 matching lines...) Expand all Loading... | |
274 tests_expanded.append(test_group) | 288 tests_expanded.append(test_group) |
275 else: | 289 else: |
276 test_split = test_group.split(':') | 290 test_split = test_group.split(':') |
277 for i in range(0, len(test_split), max_per_run): | 291 for i in range(0, len(test_split), max_per_run): |
278 tests_expanded.append(':'.join(test_split[i:i+max_per_run])) | 292 tests_expanded.append(':'.join(test_split[i:i+max_per_run])) |
279 return tests_expanded | 293 return tests_expanded |
280 | 294 |
281 | 295 |
282 def RunTests(tests, runner_factory, devices, shard=True, | 296 def RunTests(tests, runner_factory, devices, shard=True, |
283 test_timeout=DEFAULT_TIMEOUT, setup_timeout=DEFAULT_TIMEOUT, | 297 test_timeout=DEFAULT_TIMEOUT, setup_timeout=DEFAULT_TIMEOUT, |
284 num_retries=2, max_per_run=256): | 298 num_retries=2, max_per_run=256, blacklist_file=None): |
285 """Run all tests on attached devices, retrying tests that don't pass. | 299 """Run all tests on attached devices, retrying tests that don't pass. |
286 | 300 |
287 Args: | 301 Args: |
288 tests: List of tests to run. | 302 tests: List of tests to run. |
289 runner_factory: Callable that takes a device and index and returns a | 303 runner_factory: Callable that takes a device and index and returns a |
290 TestRunner object. | 304 TestRunner object. |
291 devices: List of attached devices. | 305 devices: List of attached devices. |
292 shard: True if we should shard, False if we should replicate tests. | 306 shard: True if we should shard, False if we should replicate tests. |
293 - Sharding tests will distribute tests across all test runners through a | 307 - Sharding tests will distribute tests across all test runners through a |
294 shared test collection. | 308 shared test collection. |
295 - Replicating tests will copy all tests to each test runner through a | 309 - Replicating tests will copy all tests to each test runner through a |
296 unique test collection for each test runner. | 310 unique test collection for each test runner. |
297 test_timeout: Watchdog timeout in seconds for running tests. | 311 test_timeout: Watchdog timeout in seconds for running tests. |
298 setup_timeout: Watchdog timeout in seconds for creating and cleaning up | 312 setup_timeout: Watchdog timeout in seconds for creating and cleaning up |
299 test runners. | 313 test runners. |
300 num_retries: Number of retries for a test. | 314 num_retries: Number of retries for a test. |
301 max_per_run: Maximum number of tests to run in any group. | 315 max_per_run: Maximum number of tests to run in any group. |
316 blacklist_file: Path to blacklist file. | |
302 | 317 |
303 Returns: | 318 Returns: |
304 A tuple of (base_test_result.TestRunResults object, exit code). | 319 A tuple of (base_test_result.TestRunResults object, exit code). |
305 """ | 320 """ |
306 if not tests: | 321 if not tests: |
307 logging.critical('No tests to run.') | 322 logging.critical('No tests to run.') |
308 return (base_test_result.TestRunResults(), constants.ERROR_EXIT_CODE) | 323 return (base_test_result.TestRunResults(), constants.ERROR_EXIT_CODE) |
309 | 324 |
310 tests_expanded = ApplyMaxPerRun(tests, max_per_run) | 325 tests_expanded = ApplyMaxPerRun(tests, max_per_run) |
311 if shard: | 326 if shard: |
312 # Generate a shared TestCollection object for all test runners, so they | 327 # Generate a shared TestCollection object for all test runners, so they |
313 # draw from a common pool of tests. | 328 # draw from a common pool of tests. |
314 shared_test_collection = test_collection.TestCollection( | 329 shared_test_collection = test_collection.TestCollection( |
315 [_Test(t) for t in tests_expanded]) | 330 [_Test(t) for t in tests_expanded]) |
316 test_collection_factory = lambda: shared_test_collection | 331 test_collection_factory = lambda: shared_test_collection |
317 tag_results_with_device = False | 332 tag_results_with_device = False |
318 log_string = 'sharded across devices' | 333 log_string = 'sharded across devices' |
319 else: | 334 else: |
320 # Generate a unique TestCollection object for each test runner, but use | 335 # Generate a unique TestCollection object for each test runner, but use |
321 # the same set of tests. | 336 # the same set of tests. |
322 test_collection_factory = lambda: test_collection.TestCollection( | 337 test_collection_factory = lambda: test_collection.TestCollection( |
323 [_Test(t) for t in tests_expanded]) | 338 [_Test(t) for t in tests_expanded]) |
324 tag_results_with_device = True | 339 tag_results_with_device = True |
325 log_string = 'replicated on each device' | 340 log_string = 'replicated on each device' |
326 | 341 |
327 logging.info('Will run %d tests (%s): %s', | 342 logging.info('Will run %d tests (%s): %s', |
328 len(tests_expanded), log_string, str(tests_expanded)) | 343 len(tests_expanded), log_string, str(tests_expanded)) |
329 runners = _CreateRunners(runner_factory, devices, setup_timeout) | 344 runners = _CreateRunners(runner_factory, devices, setup_timeout, |
345 blacklist_file=blacklist_file) | |
330 try: | 346 try: |
331 return _RunAllTests(runners, test_collection_factory, | 347 return _RunAllTests(runners, test_collection_factory, |
332 num_retries, test_timeout, tag_results_with_device) | 348 num_retries, test_timeout, tag_results_with_device) |
333 finally: | 349 finally: |
334 try: | 350 try: |
335 _TearDownRunners(runners, setup_timeout) | 351 _TearDownRunners(runners, setup_timeout) |
336 except device_errors.DeviceUnreachableError as e: | 352 except device_errors.DeviceUnreachableError as e: |
337 logging.warning('Device unresponsive during TearDown: [%s]', e) | 353 logging.warning('Device unresponsive during TearDown: [%s]', e) |
338 except Exception: # pylint: disable=broad-except | 354 except Exception: # pylint: disable=broad-except |
339 logging.exception('Unexpected exception caught during TearDown') | 355 logging.exception('Unexpected exception caught during TearDown') |
OLD | NEW |