| OLD | NEW |
| 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 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 """Implements test sharding logic.""" | 5 """Implements test sharding logic.""" |
| 6 | 6 |
| 7 import logging | 7 import logging |
| 8 import threading | 8 import threading |
| 9 | 9 |
| 10 from pylib import android_commands | 10 from pylib import android_commands |
| 11 from pylib import forwarder | 11 from pylib import forwarder |
| 12 from pylib.utils import reraiser_thread | 12 from pylib.utils import reraiser_thread |
| 13 | 13 |
| 14 import test_result | 14 import base_test_result |
| 15 | 15 |
| 16 | 16 |
| 17 class _ThreadSafeCounter(object): | 17 class _ThreadSafeCounter(object): |
| 18 """A threadsafe counter.""" | 18 """A threadsafe counter.""" |
| 19 def __init__(self): | 19 def __init__(self): |
| 20 self._lock = threading.Lock() | 20 self._lock = threading.Lock() |
| 21 self._value = 0 | 21 self._value = 0 |
| 22 | 22 |
| 23 def GetAndIncrement(self): | 23 def GetAndIncrement(self): |
| 24 """Get the current value and increment it atomically. | 24 """Get the current value and increment it atomically. |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 while True: | 105 while True: |
| 106 r = self._pop() | 106 r = self._pop() |
| 107 if r is None: | 107 if r is None: |
| 108 break | 108 break |
| 109 yield r | 109 yield r |
| 110 | 110 |
| 111 | 111 |
| 112 def _RunTestsFromQueue(runner, test_collection, out_results): | 112 def _RunTestsFromQueue(runner, test_collection, out_results): |
| 113 """Runs tests from the test_collection until empty using the given runner. | 113 """Runs tests from the test_collection until empty using the given runner. |
| 114 | 114 |
| 115 Adds TestResults objects to the out_results list and may add tests to the | 115 Adds TestRunResults objects to the out_results list and may add tests to the |
| 116 out_retry list. | 116 out_retry list. |
| 117 | 117 |
| 118 Args: | 118 Args: |
| 119 runner: A TestRunner object used to run the tests. | 119 runner: A TestRunner object used to run the tests. |
| 120 test_collection: A _TestCollection from which to get _Test objects to run. | 120 test_collection: A _TestCollection from which to get _Test objects to run. |
| 121 out_results: A list to add TestResults to. | 121 out_results: A list to add TestRunResults to. |
| 122 """ | 122 """ |
| 123 for test in test_collection: | 123 for test in test_collection: |
| 124 try: | 124 try: |
| 125 if not android_commands.IsDeviceAttached(runner.device): | 125 if not android_commands.IsDeviceAttached(runner.device): |
| 126 # Device is unresponsive, stop handling tests on this device. | 126 # Device is unresponsive, stop handling tests on this device. |
| 127 msg = 'Device %s is unresponsive.' % runner.device | 127 msg = 'Device %s is unresponsive.' % runner.device |
| 128 logging.warning(msg) | 128 logging.warning(msg) |
| 129 raise android_commands.errors.DeviceUnresponsiveError(msg) | 129 raise android_commands.errors.DeviceUnresponsiveError(msg) |
| 130 result, retry = runner.RunTest(test.test) | 130 result, retry = runner.RunTest(test.test) |
| 131 test.tries += 1 | 131 test.tries += 1 |
| 132 if retry and test.tries <= 3: | 132 if retry and test.tries <= 3: |
| 133 # Retry non-passing results, only record passing results. | 133 # Retry non-passing results, only record passing results. |
| 134 out_results.append(test_result.TestResults.FromRun(ok=result.ok)) | 134 pass_results = base_test_result.TestRunResults() |
| 135 pass_results.AddResults(result.GetPass()) |
| 136 out_results.append(pass_results) |
| 135 logging.warning('****Will retry test, try #%s.' % test.tries) | 137 logging.warning('****Will retry test, try #%s.' % test.tries) |
| 136 test_collection.add(_Test(test=retry, tries=test.tries)) | 138 test_collection.add(_Test(test=retry, tries=test.tries)) |
| 137 else: | 139 else: |
| 138 # All tests passed or retry limit reached. Either way, record results. | 140 # All tests passed or retry limit reached. Either way, record results. |
| 139 out_results.append(result) | 141 out_results.append(result) |
| 140 except android_commands.errors.DeviceUnresponsiveError: | 142 except android_commands.errors.DeviceUnresponsiveError: |
| 141 # Device is unresponsive, stop handling tests on this device and ensure | 143 # Device is unresponsive, stop handling tests on this device and ensure |
| 142 # current test gets runs by another device. Don't reraise this exception | 144 # current test gets runs by another device. Don't reraise this exception |
| 143 # on the main thread. | 145 # on the main thread. |
| 144 test_collection.add(test) | 146 test_collection.add(test) |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 | 179 |
| 178 | 180 |
| 179 def _RunAllTests(runners, tests): | 181 def _RunAllTests(runners, tests): |
| 180 """Run all tests using the given TestRunners. | 182 """Run all tests using the given TestRunners. |
| 181 | 183 |
| 182 Args: | 184 Args: |
| 183 runners: a list of TestRunner objects. | 185 runners: a list of TestRunner objects. |
| 184 tests: a list of Tests to run using the given TestRunners. | 186 tests: a list of Tests to run using the given TestRunners. |
| 185 | 187 |
| 186 Returns: | 188 Returns: |
| 187 A TestResults object. | 189 A TestRunResults object. |
| 188 """ | 190 """ |
| 189 logging.warning('****Running %s tests with %s test runners.' % | 191 logging.warning('****Running %s tests with %s test runners.' % |
| 190 (len(tests), len(runners))) | 192 (len(tests), len(runners))) |
| 191 tests_collection = _TestCollection([_Test(t) for t in tests]) | 193 tests_collection = _TestCollection([_Test(t) for t in tests]) |
| 192 results = [] | 194 results = [] |
| 193 workers = reraiser_thread.ReraiserThreadGroup([reraiser_thread.ReraiserThread( | 195 workers = reraiser_thread.ReraiserThreadGroup([reraiser_thread.ReraiserThread( |
| 194 _RunTestsFromQueue, [r, tests_collection, results]) for r in runners]) | 196 _RunTestsFromQueue, [r, tests_collection, results]) for r in runners]) |
| 195 workers.StartAll() | 197 workers.StartAll() |
| 196 workers.JoinAll() | 198 workers.JoinAll() |
| 197 return test_result.TestResults.FromTestResults(results) | 199 run_results = base_test_result.TestRunResults() |
| 200 for r in results: |
| 201 run_results.AddTestRunResults(r) |
| 202 return run_results |
| 198 | 203 |
| 199 | 204 |
| 200 def _CreateRunners(runner_factory, devices): | 205 def _CreateRunners(runner_factory, devices): |
| 201 """Creates a test runner for each device and calls SetUp() in parallel. | 206 """Creates a test runner for each device and calls SetUp() in parallel. |
| 202 | 207 |
| 203 Note: if a device is unresponsive the corresponding TestRunner will not be | 208 Note: if a device is unresponsive the corresponding TestRunner will not be |
| 204 included in the returned list. | 209 included in the returned list. |
| 205 | 210 |
| 206 Args: | 211 Args: |
| 207 runner_factory: callable that takes a device and index and returns a | 212 runner_factory: callable that takes a device and index and returns a |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 239 """Run all tests on attached devices, retrying tests that don't pass. | 244 """Run all tests on attached devices, retrying tests that don't pass. |
| 240 | 245 |
| 241 Args: | 246 Args: |
| 242 runner_factory: callable that takes a device and index and returns a | 247 runner_factory: callable that takes a device and index and returns a |
| 243 TestRunner object. | 248 TestRunner object. |
| 244 devices: list of attached device serial numbers as strings. | 249 devices: list of attached device serial numbers as strings. |
| 245 tests: list of tests to run. | 250 tests: list of tests to run. |
| 246 build_type: either 'Debug' or 'Release'. | 251 build_type: either 'Debug' or 'Release'. |
| 247 | 252 |
| 248 Returns: | 253 Returns: |
| 249 A test_result.TestResults object. | 254 A base_test_result.TestRunResults object. |
| 250 """ | 255 """ |
| 251 forwarder.Forwarder.KillHost(build_type) | 256 forwarder.Forwarder.KillHost(build_type) |
| 252 runners = _CreateRunners(runner_factory, devices) | 257 runners = _CreateRunners(runner_factory, devices) |
| 253 try: | 258 try: |
| 254 return _RunAllTests(runners, tests) | 259 return _RunAllTests(runners, tests) |
| 255 finally: | 260 finally: |
| 256 try: | 261 try: |
| 257 _TearDownRunners(runners) | 262 _TearDownRunners(runners) |
| 258 except android_commands.errors.DeviceUnresponsiveError as e: | 263 except android_commands.errors.DeviceUnresponsiveError as e: |
| 259 logging.warning('****Device unresponsive during TearDown: [%s]', e) | 264 logging.warning('****Device unresponsive during TearDown: [%s]', e) |
| 260 finally: | 265 finally: |
| 261 forwarder.Forwarder.KillHost(build_type) | 266 forwarder.Forwarder.KillHost(build_type) |
| OLD | NEW |