Chromium Code Reviews| 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 |