Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(241)

Side by Side Diff: build/android/pylib/base/shard.py

Issue 12378048: [Android] Switch instrumentation tests to the new shard/runner paradigm. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add retries to the end of the queue instead of the beginning Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 test_result
15 15
16 16
17 class _ThreadSafeCounter(object):
18 """A threadsafe counter."""
19 def __init__(self):
20 self._lock = threading.Lock()
21 self._value = 0
22
23 def GetAndIncrement(self):
24 """Get the current value and increment it atomically.
25
26 Returns:
27 The value before incrementing.
28 """
29 with self._lock:
30 pre_increment = self._value
31 self._value += 1
32 return pre_increment
33
34
17 class _Test(object): 35 class _Test(object):
18 """Holds a test with additional metadata.""" 36 """Holds a test with additional metadata."""
19 def __init__(self, test, tries=0): 37 def __init__(self, test, tries=0):
20 """Initializes the _Test object. 38 """Initializes the _Test object.
21 39
22 Args: 40 Args:
23 test: the test. 41 test: the test.
24 tries: number of tries so far. 42 tries: number of tries so far.
25 """ 43 """
26 self.test = test 44 self.test = test
(...skipping 24 matching lines...) Expand all
51 A test or None if all tests have been handled. 69 A test or None if all tests have been handled.
52 """ 70 """
53 while True: 71 while True:
54 # Wait for a test to be avaliable or all tests to have been handled. 72 # Wait for a test to be avaliable or all tests to have been handled.
55 self._item_avaliable_or_all_done.wait() 73 self._item_avaliable_or_all_done.wait()
56 with self._lock: 74 with self._lock:
57 # Check which of the two conditions triggered the signal. 75 # Check which of the two conditions triggered the signal.
58 if self._tests_in_progress == 0: 76 if self._tests_in_progress == 0:
59 return None 77 return None
60 try: 78 try:
61 return self._tests.pop() 79 return self._tests.pop(0)
62 except IndexError: 80 except IndexError:
63 # Another thread beat us to the avaliable test, wait again. 81 # Another thread beat us to the avaliable test, wait again.
64 self._item_avaliable_or_all_done.clear() 82 self._item_avaliable_or_all_done.clear()
65 83
66 def add(self, test): 84 def add(self, test):
67 """Add an test to the collection. 85 """Add an test to the collection.
68 86
69 Args: 87 Args:
70 item: A test to add. 88 item: A test to add.
71 """ 89 """
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 if not android_commands.IsDeviceAttached(runner.device): 125 if not android_commands.IsDeviceAttached(runner.device):
108 # Device is unresponsive, stop handling tests on this device. 126 # Device is unresponsive, stop handling tests on this device.
109 msg = 'Device %s is unresponsive.' % runner.device 127 msg = 'Device %s is unresponsive.' % runner.device
110 logging.warning(msg) 128 logging.warning(msg)
111 raise android_commands.errors.DeviceUnresponsiveError(msg) 129 raise android_commands.errors.DeviceUnresponsiveError(msg)
112 result, retry = runner.RunTest(test.test) 130 result, retry = runner.RunTest(test.test)
113 test.tries += 1 131 test.tries += 1
114 if retry and test.tries <= 3: 132 if retry and test.tries <= 3:
115 # Retry non-passing results, only record passing results. 133 # Retry non-passing results, only record passing results.
116 out_results.append(test_result.TestResults.FromRun(ok=result.ok)) 134 out_results.append(test_result.TestResults.FromRun(ok=result.ok))
117 logging.warning('****Retrying test, try #%s.' % test.tries) 135 logging.warning('****Will retry test, try #%s.' % test.tries)
118 test_collection.add(_Test(test=retry, tries=test.tries)) 136 test_collection.add(_Test(test=retry, tries=test.tries))
119 else: 137 else:
120 # All tests passed or retry limit reached. Either way, record results. 138 # All tests passed or retry limit reached. Either way, record results.
121 out_results.append(result) 139 out_results.append(result)
122 except android_commands.errors.DeviceUnresponsiveError: 140 except android_commands.errors.DeviceUnresponsiveError:
123 # Device is unresponsive, stop handling tests on this device and ensure 141 # Device is unresponsive, stop handling tests on this device and ensure
124 # current test gets runs by another device. Don't reraise this exception 142 # current test gets runs by another device. Don't reraise this exception
125 # on the main thread. 143 # on the main thread.
126 test_collection.add(test) 144 test_collection.add(test)
127 return 145 return
128 except: 146 except:
129 # An unhandleable exception, ensure tests get run by another device and 147 # An unhandleable exception, ensure tests get run by another device and
130 # reraise this exception on the main thread. 148 # reraise this exception on the main thread.
131 test_collection.add(test) 149 test_collection.add(test)
132 raise 150 raise
133 finally: 151 finally:
134 # Retries count as separate tasks so always mark the popped test as done. 152 # Retries count as separate tasks so always mark the popped test as done.
135 test_collection.test_completed() 153 test_collection.test_completed()
136 154
137 155
138 def _SetUp(runner_factory, device, out_runners): 156 def _SetUp(runner_factory, device, out_runners, threadsafe_counter):
139 """Creates a test runner for each device and calls SetUp() in parallel. 157 """Creates a test runner for each device and calls SetUp() in parallel.
140 158
141 Note: if a device is unresponsive the corresponding TestRunner will not be 159 Note: if a device is unresponsive the corresponding TestRunner will not be
142 added to out_runners. 160 added to out_runners.
143 161
144 Args: 162 Args:
145 runner_factory: callable that takes a device and returns a TestRunner. 163 runner_factory: callable that takes a device and index and returns a
164 TestRunner object.
146 device: the device serial number to set up. 165 device: the device serial number to set up.
147 out_runners: list to add the successfully set up TestRunner object. 166 out_runners: list to add the successfully set up TestRunner object.
167 threadsafe_counter: a _ThreadSafeCounter object used to get shard indices.
148 """ 168 """
149 try: 169 try:
150 logging.warning('*****Creating shard for %s.', device) 170 index = threadsafe_counter.GetAndIncrement()
151 runner = runner_factory(device) 171 logging.warning('*****Creating shard %s for device %s.', index, device)
172 runner = runner_factory(device, index)
152 runner.SetUp() 173 runner.SetUp()
153 out_runners.append(runner) 174 out_runners.append(runner)
154 except android_commands.errors.DeviceUnresponsiveError as e: 175 except android_commands.errors.DeviceUnresponsiveError as e:
155 logging.warning('****Failed to create shard for %s: [%s]', (device, e)) 176 logging.warning('****Failed to create shard for %s: [%s]', device, e)
156 177
157 178
158 def _RunAllTests(runners, tests): 179 def _RunAllTests(runners, tests):
159 """Run all tests using the given TestRunners. 180 """Run all tests using the given TestRunners.
160 181
161 Args: 182 Args:
162 runners: a list of TestRunner objects. 183 runners: a list of TestRunner objects.
163 tests: a list of Tests to run using the given TestRunners. 184 tests: a list of Tests to run using the given TestRunners.
164 185
165 Returns: 186 Returns:
(...skipping 10 matching lines...) Expand all
176 return test_result.TestResults.FromTestResults(results) 197 return test_result.TestResults.FromTestResults(results)
177 198
178 199
179 def _CreateRunners(runner_factory, devices): 200 def _CreateRunners(runner_factory, devices):
180 """Creates a test runner for each device and calls SetUp() in parallel. 201 """Creates a test runner for each device and calls SetUp() in parallel.
181 202
182 Note: if a device is unresponsive the corresponding TestRunner will not be 203 Note: if a device is unresponsive the corresponding TestRunner will not be
183 included in the returned list. 204 included in the returned list.
184 205
185 Args: 206 Args:
186 runner_factory: callable that takes a device and returns a TestRunner. 207 runner_factory: callable that takes a device and index and returns a
208 TestRunner object.
187 devices: list of device serial numbers as strings. 209 devices: list of device serial numbers as strings.
188 210
189 Returns: 211 Returns:
190 A list of TestRunner objects. 212 A list of TestRunner objects.
191 """ 213 """
192 logging.warning('****Creating %s test runners.' % len(devices)) 214 logging.warning('****Creating %s test runners.' % len(devices))
193 test_runners = [] 215 runners = []
216 counter = _ThreadSafeCounter()
194 threads = reraiser_thread.ReraiserThreadGroup( 217 threads = reraiser_thread.ReraiserThreadGroup(
195 [reraiser_thread.ReraiserThread(_SetUp, [runner_factory, d, test_runners]) 218 [reraiser_thread.ReraiserThread(_SetUp, [runner_factory, d, runners,
219 counter])
196 for d in devices]) 220 for d in devices])
197 threads.StartAll() 221 threads.StartAll()
198 threads.JoinAll() 222 threads.JoinAll()
199 return test_runners 223 return runners
200 224
201 225
202 def _TearDownRunners(runners): 226 def _TearDownRunners(runners):
203 """Calls TearDown() for each test runner in parallel. 227 """Calls TearDown() for each test runner in parallel.
204 Args: 228 Args:
205 runners: a list of TestRunner objects. 229 runners: a list of TestRunner objects.
206 """ 230 """
207 threads = reraiser_thread.ReraiserThreadGroup( 231 threads = reraiser_thread.ReraiserThreadGroup(
208 [reraiser_thread.ReraiserThread(runner.TearDown) 232 [reraiser_thread.ReraiserThread(runner.TearDown)
209 for runner in runners]) 233 for runner in runners])
210 threads.StartAll() 234 threads.StartAll()
211 threads.JoinAll() 235 threads.JoinAll()
212 236
213 237
214 def ShardAndRunTests(runner_factory, devices, tests, build_type='Debug'): 238 def ShardAndRunTests(runner_factory, devices, tests, build_type='Debug'):
215 """Run all tests on attached devices, retrying tests that don't pass. 239 """Run all tests on attached devices, retrying tests that don't pass.
216 240
217 Args: 241 Args:
218 runner_factory: callable that takes a device and returns a TestRunner. 242 runner_factory: callable that takes a device and index and returns a
243 TestRunner object.
219 devices: list of attached device serial numbers as strings. 244 devices: list of attached device serial numbers as strings.
220 tests: list of tests to run. 245 tests: list of tests to run.
221 build_type: either 'Debug' or 'Release'. 246 build_type: either 'Debug' or 'Release'.
222 247
223 Returns: 248 Returns:
224 A test_result.TestResults object. 249 A test_result.TestResults object.
225 """ 250 """
226 forwarder.Forwarder.KillHost(build_type) 251 forwarder.Forwarder.KillHost(build_type)
227 runners = _CreateRunners(runner_factory, devices) 252 runners = _CreateRunners(runner_factory, devices)
228 try: 253 try:
229 return _RunAllTests(runners, tests) 254 return _RunAllTests(runners, tests)
230 finally: 255 finally:
231 try: 256 try:
232 _TearDownRunners(runners) 257 _TearDownRunners(runners)
233 except android_commands.errors.DeviceUnresponsiveError as e: 258 except android_commands.errors.DeviceUnresponsiveError as e:
234 logging.warning('****Device unresponsive during TearDown: [%s]', e) 259 logging.warning('****Device unresponsive during TearDown: [%s]', e)
235 finally: 260 finally:
236 forwarder.Forwarder.KillHost(build_type) 261 forwarder.Forwarder.KillHost(build_type)
OLDNEW
« no previous file with comments | « build/android/pylib/base/new_base_test_runner.py ('k') | build/android/pylib/base/shard_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698