OLD | NEW |
---|---|
(Empty) | |
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 | |
3 # found in the LICENSE file. | |
4 | |
5 """Implements test sharding logic.""" | |
6 | |
7 import logging | |
8 import sys | |
9 import threading | |
10 | |
11 from pylib import android_commands | |
12 from pylib import forwarder | |
13 | |
14 import test_result | |
15 | |
16 | |
17 class _Worker(threading.Thread): | |
18 """Runs tests from the test_queue using the given runner in a separate thread. | |
19 | |
20 Places results in the results_list. | |
frankf
2013/02/20 18:55:31
results_list -> out_results
craigdh
2013/02/20 19:38:09
Done.
| |
21 """ | |
22 def __init__(self, runner, test_queue, out_results, out_retry): | |
23 """Initializes the worker. | |
24 | |
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 | |
39 def run(self): | |
40 """Run tests from the queue until it is empty, storing results. | |
frankf
2013/02/20 18:55:31
be more precise than "storing results" :)
craigdh
2013/02/20 19:38:09
Done.
| |
41 | |
42 Overriden from threading.Thread, runs in a separate thread. | |
frankf
2013/02/20 18:55:31
I think we do something like #Override before the
craigdh
2013/02/20 19:38:09
Done.
| |
43 """ | |
44 try: | |
45 while True: | |
46 test = self._test_queue.pop() | |
47 result, retry = self._runner.Run(test) | |
48 self._out_results.append(result) | |
49 if retry: | |
50 self._out_retry.append(retry) | |
51 except IndexError: | |
52 pass | |
53 except: | |
54 self._exc_info = sys.exc_info() | |
55 raise | |
56 | |
57 def Reraise(self): | |
58 """Reraise an exception raised in the thread.""" | |
frankf
2013/02/20 18:55:31
Reraise _if_ an exception...
Maybe rename to bett
craigdh
2013/02/20 19:38:09
Done.
| |
59 if self._exc_info: | |
60 raise self._exc_info[0], self._exc_info[1], self._exc_info[2] | |
61 | |
62 | |
63 def _RunAllTests(runners, tests): | |
64 """Run all tests using the given TestRunners. | |
65 | |
66 Args: | |
67 runners: a list of TestRunner objects. | |
68 tests: a list of Tests to run using the given TestRunners. | |
69 | |
70 Returns: | |
71 Tuple: (list of TestResults, list of tests to retry) | |
72 """ | |
73 tests_queue = list(tests) | |
74 workers = [] | |
75 results = [] | |
76 retry = [] | |
77 for r in runners: | |
78 worker = _Worker(r, tests_queue, results, retry) | |
79 worker.start() | |
80 workers.append(worker) | |
81 while workers: | |
82 for w in workers[:]: | |
83 # Allow the main thread to periodically check for keyboard interrupts. | |
84 w.join(0.1) | |
85 if not w.isAlive(): | |
86 w.Reraise() | |
87 workers.remove(w) | |
88 return (results, retry) | |
89 | |
90 | |
91 def _CreateRunners(runner_factory, devices): | |
92 """Creates a test runner for each device. | |
93 | |
94 Note: if a device is unresponsive the corresponding TestRunner will not be | |
95 included in the returned list. | |
96 | |
97 Args: | |
98 runner_factory: callable that takes a device and returns a TestRunner. | |
99 devices: list of device serial numbers as strings. | |
100 | |
101 Returns: | |
102 A list of TestRunner objects. | |
103 """ | |
104 test_runners = [] | |
105 for index, device in enumerate(devices): | |
106 logging.warning('*' * 80) | |
107 logging.warning('Creating shard %d for %s', index, device) | |
108 logging.warning('*' * 80) | |
109 try: | |
110 test_runners.append(runner_factory(device)) | |
111 except android_commands.errors.DeviceUnresponsiveError as e: | |
112 logging.warning('****Failed to create a shard: [%s]', e) | |
frankf
2013/02/20 18:55:31
Does this print the device that failed?
craigdh
2013/02/20 19:38:09
I'm not sure if the exception message includes the
| |
113 return test_runners | |
114 | |
115 | |
116 def ShardAndRunTests( | |
117 runner_factory, devices, tests, build_type='Debug', tries=3, | |
118 get_attached_devices_callable=android_commands.GetAttachedDevices): | |
119 """Run all tests on attached devices, retrying tests that don't pass. | |
120 | |
121 Args: | |
122 runner_factory: callable that takes a device and returns a TestRunner. | |
123 devices: list of attached device serial numbers as strings. | |
124 tests: list of tests to run. | |
125 build_type: either 'Debug' or 'Release'. | |
126 retries: number of retries before accepting failure. | |
frankf
2013/02/20 18:55:31
update this.
craigdh
2013/02/20 19:38:09
Done.
| |
127 get_attached_devices_callable: only override default value for testing. | |
frankf
2013/02/20 18:55:31
Can you use pymox to mock out the calls to adb ins
craigdh
2013/02/20 19:38:09
Removed this, it was a trivial one-liner to mock o
| |
128 | |
129 Returns: | |
130 A test_result.TestResults object. | |
131 """ | |
132 final_results = test_result.TestResults() | |
133 results = test_result.TestResults() | |
134 forwarder.Forwarder.KillHost(build_type) | |
135 try_count = 0 | |
136 while tests: | |
137 devices = set(devices).intersection(get_attached_devices_callable()) | |
138 if not devices: | |
139 logging.critical('No devices attached and visible to run tests!') | |
frankf
2013/02/20 18:55:31
msg = ...
craigdh
2013/02/20 19:38:09
Done.
| |
140 raise Exception('No devices attached and visible to run tests!') | |
141 if try_count >= tries: | |
frankf
2013/02/20 18:55:31
Add some comments for these termination conditions
craigdh
2013/02/20 19:38:09
Done.
| |
142 results.ok = final_results.ok | |
143 final_results = results | |
144 break | |
145 try_count += 1 | |
146 runners = _CreateRunners(runner_factory, devices) | |
147 try: | |
148 results_list, tests = _RunAllTests(runners, tests) | |
149 results = test_result.TestResults.FromTestResults(results_list) | |
150 final_results.ok += results.ok | |
151 except android_commands.errors.DeviceUnresponsiveError as e: | |
152 logging.warning('****Failed to run test: [%s]', e) | |
153 forwarder.Forwarder.KillHost(build_type) | |
154 return final_results | |
OLD | NEW |