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

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

Issue 12317059: [Andoid] Threaded TestRunner creation and SetUp and TearDown calls. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 10 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 sys
9 import threading
10 8
11 from pylib import android_commands 9 from pylib import android_commands
12 from pylib import forwarder 10 from pylib import forwarder
11 from pylib.utils import reraiser_thread
13 12
14 import test_result 13 import test_result
15 14
16 15
17 class _Worker(threading.Thread): 16 def _RunTestsFromQueue(runner, test_queue, out_results, out_retry):
18 """Runs tests from the test_queue using the given runner in a separate thread. 17 """Runs tests from the test_queue until empty using the given runner.
19 18
20 Places results in the out_results. 19 Adds TestResults objects to the out_results list and may add tests to the
20 out_retry list.
21
22 Args:
23 runner: A TestRunner object used to run the tests.
24 test_queue: A list from which to get tests to run.
25 out_results: A list to add TestResults to.
26 out_retry: A list to add tests to retry.
21 """ 27 """
22 def __init__(self, runner, test_queue, out_results, out_retry): 28 try:
23 """Initializes the worker. 29 while True:
30 test = test_queue.pop()
31 result, retry = runner.RunTest(test)
32 out_results.append(result)
33 if retry:
34 out_retry.append(retry)
35 except IndexError:
36 pass
24 37
25 Args:
26 runner: A TestRunner object used to run the tests.
27 test_queue: A list from which to get tests to run.
28 out_results: A list to add TestResults to.
29 out_retry: A list to add tests to retry.
30 """
31 super(_Worker, self).__init__()
32 self.daemon = True
33 self._exc_info = None
34 self._runner = runner
35 self._test_queue = test_queue
36 self._out_results = out_results
37 self._out_retry = out_retry
38 38
39 #override 39 def _SetUp(runner_factory, device, out_runners):
40 def run(self): 40 """Creates a test runner for each device and calls SetUp() in parallel.
41 """Run tests from the queue in a seperate thread until it is empty.
42 41
43 Adds TestResults objects to the out_results list and may add tests to the 42 Note: if a device is unresponsive the corresponding TestRunner will not be
44 out_retry list. 43 added to out_runners.
45 """
46 try:
47 while True:
48 test = self._test_queue.pop()
49 result, retry = self._runner.Run(test)
50 self._out_results.append(result)
51 if retry:
52 self._out_retry.append(retry)
53 except IndexError:
54 pass
55 except:
56 self._exc_info = sys.exc_info()
57 raise
58 44
59 def ReraiseIfException(self): 45 Args:
60 """Reraise exception if an exception was raised in the thread.""" 46 runner_factory: callable that takes a device and returns a TestRunner.
61 if self._exc_info: 47 device: the device serial number to set up.
62 raise self._exc_info[0], self._exc_info[1], self._exc_info[2] 48 out_runners: list to add the successfully set up TestRunner object.
49 """
50 try:
51 logging.warning('****Creating shard for %s', device)
52 runner = runner_factory(device)
53 runner.SetUp()
54 out_runners.append(runner)
55 except android_commands.errors.DeviceUnresponsiveError as e:
56 logging.warning('****Failed to create shard for %s: [%s]', (device, e))
63 57
64 58
65 def _RunAllTests(runners, tests): 59 def _RunAllTests(runners, tests):
66 """Run all tests using the given TestRunners. 60 """Run all tests using the given TestRunners.
67 61
68 Args: 62 Args:
69 runners: a list of TestRunner objects. 63 runners: a list of TestRunner objects.
70 tests: a list of Tests to run using the given TestRunners. 64 tests: a list of Tests to run using the given TestRunners.
71 65
72 Returns: 66 Returns:
73 Tuple: (list of TestResults, list of tests to retry) 67 Tuple: (list of TestResults, list of tests to retry)
74 """ 68 """
75 tests_queue = list(tests) 69 tests_queue = list(tests)
76 workers = []
77 results = [] 70 results = []
78 retry = [] 71 retry = []
79 for r in runners: 72 workers = reraiser_thread.ReraiserThreadGroup([reraiser_thread.ReraiserThread(
80 worker = _Worker(r, tests_queue, results, retry) 73 _RunTestsFromQueue, [r, tests_queue, results, retry]) for r in runners])
81 worker.start() 74 workers.StartAll()
82 workers.append(worker) 75 workers.JoinAll()
83 while workers:
84 for w in workers[:]:
85 # Allow the main thread to periodically check for keyboard interrupts.
86 w.join(0.1)
87 if not w.isAlive():
88 w.ReraiseIfException()
89 workers.remove(w)
90 return (results, retry) 76 return (results, retry)
91 77
92 78
93 def _CreateRunners(runner_factory, devices): 79 def _CreateRunners(runner_factory, devices):
94 """Creates a test runner for each device. 80 """Creates a test runner for each device and calls SetUp() in parallel.
95 81
96 Note: if a device is unresponsive the corresponding TestRunner will not be 82 Note: if a device is unresponsive the corresponding TestRunner will not be
97 included in the returned list. 83 included in the returned list.
98 84
99 Args: 85 Args:
100 runner_factory: callable that takes a device and returns a TestRunner. 86 runner_factory: callable that takes a device and returns a TestRunner.
101 devices: list of device serial numbers as strings. 87 devices: list of device serial numbers as strings.
102 88
103 Returns: 89 Returns:
104 A list of TestRunner objects. 90 A list of TestRunner objects.
105 """ 91 """
106 test_runners = [] 92 test_runners = []
107 for index, device in enumerate(devices): 93 threads = reraiser_thread.ReraiserThreadGroup(
108 logging.warning('*' * 80) 94 [reraiser_thread.ReraiserThread(_SetUp, [runner_factory, d, test_runners])
109 logging.warning('Creating shard %d for %s', index, device) 95 for d in devices])
110 logging.warning('*' * 80) 96 threads.StartAll()
111 try: 97 threads.JoinAll()
frankf 2013/02/22 02:42:55 For all these JoinAll invokations, we need to thin
craigdh 2013/02/22 17:24:20 I thought about it a bit. Here's what's currently
112 test_runners.append(runner_factory(device))
113 except android_commands.errors.DeviceUnresponsiveError as e:
114 logging.warning('****Failed to create a shard: [%s]', e)
115 return test_runners 98 return test_runners
116 99
117 100
101 def _TearDownRunners(runners):
102 """Calls TearDown() for each test runner in parallel.
103 Args:
104 runners: a list of TestRunner objects.
105 """
106 threads = reraiser_thread.ReraiserThreadGroup(
107 [reraiser_thread.ReraiserThread(runner.TearDown)
108 for runner in runners])
109 threads.StartAll()
110 threads.JoinAll()
111
112
118 def ShardAndRunTests(runner_factory, devices, tests, build_type='Debug', 113 def ShardAndRunTests(runner_factory, devices, tests, build_type='Debug',
119 tries=3): 114 tries=3):
120 """Run all tests on attached devices, retrying tests that don't pass. 115 """Run all tests on attached devices, retrying tests that don't pass.
121 116
122 Args: 117 Args:
123 runner_factory: callable that takes a device and returns a TestRunner. 118 runner_factory: callable that takes a device and returns a TestRunner.
124 devices: list of attached device serial numbers as strings. 119 devices: list of attached device serial numbers as strings.
125 tests: list of tests to run. 120 tests: list of tests to run.
126 build_type: either 'Debug' or 'Release'. 121 build_type: either 'Debug' or 'Release'.
127 tries: number of tries before accepting failure. 122 tries: number of tries before accepting failure.
128 123
129 Returns: 124 Returns:
130 A test_result.TestResults object. 125 A test_result.TestResults object.
131 """ 126 """
132 final_results = test_result.TestResults() 127 final_results = test_result.TestResults()
133 results = test_result.TestResults() 128 results = test_result.TestResults()
134 forwarder.Forwarder.KillHost(build_type) 129 forwarder.Forwarder.KillHost(build_type)
135 try_count = 0 130 try_count = 0
136 while tests: 131 runners = _CreateRunners(runner_factory, set(devices))
nilesh 2013/02/22 02:00:29 I think the numbers of runners we create should al
craigdh 2013/02/22 17:24:20 There's an issue, though, if devices fail. I think
137 devices = set(devices).intersection(android_commands.GetAttachedDevices()) 132 try:
138 if not devices: 133 while tests:
139 # There are no visible devices attached, this is unrecoverable. 134 devices = set(devices).intersection(android_commands.GetAttachedDevices())
140 msg = 'No devices attached and visible to run tests!' 135 runners = [r for r in runners if r.device in devices]
141 logging.critical(msg) 136 if not devices:
142 raise Exception(msg) 137 # There are no visible devices attached, this is unrecoverable.
143 if try_count >= tries: 138 msg = 'No devices attached and visible to run tests!'
144 # We've retried too many times, return the TestResults up to this point. 139 logging.critical(msg)
145 results.ok = final_results.ok 140 raise Exception(msg)
146 final_results = results 141 if try_count >= tries:
147 break 142 # We've retried too many times, return the TestResults up to this point.
148 try_count += 1 143 results.ok = final_results.ok
149 runners = _CreateRunners(runner_factory, devices) 144 final_results = results
150 try: 145 break
151 results_list, tests = _RunAllTests(runners, tests) 146 try_count += 1
152 results = test_result.TestResults.FromTestResults(results_list) 147 try:
153 final_results.ok += results.ok 148 results_list, tests = _RunAllTests(runners, tests)
154 except android_commands.errors.DeviceUnresponsiveError as e: 149 results = test_result.TestResults.FromTestResults(results_list)
155 logging.warning('****Failed to run test: [%s]', e) 150 final_results.ok += results.ok
151 except android_commands.errors.DeviceUnresponsiveError as e:
152 logging.warning('****Failed to run test: [%s]', e)
153 finally:
154 _TearDownRunners(runners)
156 forwarder.Forwarder.KillHost(build_type) 155 forwarder.Forwarder.KillHost(build_type)
157 return final_results 156 return final_results
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698