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

Side by Side Diff: build/android/pylib/python_test_sharder.py

Issue 10703165: Android: adds instrumentation test runners. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 5 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
« no previous file with comments | « build/android/pylib/python_test_caller.py ('k') | build/android/pylib/run_java_tests.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2012 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 """Takes care of sharding the python-drive tests in multiple devices."""
6
7 import logging
8 import multiprocessing
9
10 from python_test_caller import CallPythonTest
11 from run_java_tests import FatalTestException
12 import sharded_tests_queue
13 from test_result import TestResults
14
15
16 def SetTestsContainer(tests_container):
17 """Sets PythonTestSharder as a top-level field.
18
19 PythonTestSharder uses multiprocessing.Pool, which creates a pool of
20 processes. This is used to initialize each worker in the pool, ensuring that
21 each worker has access to this shared pool of tests.
22
23 The multiprocessing module requires that this be a top-level method.
24
25 Args:
26 tests_container: the container for all the tests.
27 """
28 PythonTestSharder.tests_container = tests_container
29
30
31 def _DefaultRunnable(test_runner):
32 """A default runnable for a PythonTestRunner.
33
34 Args:
35 test_runner: A PythonTestRunner which will run tests.
36
37 Returns:
38 The test results.
39 """
40 return test_runner.RunTests()
41
42
43 class PythonTestRunner(object):
44 """Thin wrapper around a list of PythonTestBase instances.
45
46 This is meant to be a long-lived object which can run multiple Python tests
47 within its lifetime. Tests will receive the device_id and shard_index.
48
49 The shard index affords the ability to create unique port numbers (e.g.
50 DEFAULT_PORT + shard_index) if the test so wishes.
51 """
52
53 def __init__(self, device_id, shard_index):
54 """Constructor.
55
56 Args:
57 device_id: ID of the device which this test will talk to.
58 shard_index: shard index, used to create such as unique port numbers.
59 """
60 self.device_id = device_id
61 self.shard_index = shard_index
62
63 def RunTests(self):
64 """Runs tests from the shared pool of tests, aggregating results.
65
66 Returns:
67 A list of test results for all of the tests which this runner executed.
68 """
69 tests = PythonTestSharder.tests_container
70
71 results = []
72 for t in tests:
73 res = CallPythonTest(t, self.device_id, self.shard_index)
74 results.append(res)
75
76 return TestResults.FromTestResults(results)
77
78
79 class PythonTestSharder(object):
80 """Runs Python tests in parallel on multiple devices.
81
82 This is lifted more or less wholesale from BaseTestRunner.
83
84 Under the covers, it creates a pool of long-lived PythonTestRunners, which
85 execute tests from the pool of tests.
86
87 Args:
88 attached_devices: a list of device IDs attached to the host.
89 shard_retries: number of retries for any given test.
90 available_tests: a list of tests to run which subclass PythonTestBase.
91
92 Returns:
93 An aggregated list of test results.
94 """
95 tests_container = None
96
97 def __init__(self, attached_devices, shard_retries, available_tests):
98 self.attached_devices = attached_devices
99 self.retries = shard_retries
100 self.tests = available_tests
101
102 def _SetupSharding(self, tests):
103 """Creates the shared pool of tests and makes it available to test runners.
104
105 Args:
106 tests: the list of tests which will be consumed by workers.
107 """
108 SetTestsContainer(sharded_tests_queue.ShardedTestsQueue(
109 len(self.attached_devices), tests))
110
111 def RunShardedTests(self):
112 """Runs tests in parallel using a pool of workers.
113
114 Returns:
115 A list of test results aggregated from all test runs.
116 """
117 logging.warning('*' * 80)
118 logging.warning('Sharding in ' + str(len(self.attached_devices)) +
119 ' devices.')
120 logging.warning('Note that the output is not synchronized.')
121 logging.warning('Look for the "Final result" banner in the end.')
122 logging.warning('*' * 80)
123 all_passed = []
124 test_results = TestResults()
125 tests_to_run = self.tests
126 for retry in xrange(self.retries):
127 logging.warning('Try %d of %d', retry + 1, self.retries)
128 self._SetupSharding(self.tests)
129 test_runners = self._MakeTestRunners(self.attached_devices)
130 logging.warning('Starting...')
131 pool = multiprocessing.Pool(len(self.attached_devices),
132 SetTestsContainer,
133 [PythonTestSharder.tests_container])
134
135 # List of TestResults objects from each test execution.
136 try:
137 results_lists = pool.map(_DefaultRunnable, test_runners)
138 except Exception:
139 logging.exception('Unable to run tests. Something with the '
140 'PythonTestRunners has gone wrong.')
141 raise FatalTestException('PythonTestRunners were unable to run tests.')
142
143 test_results = TestResults.FromTestResults(results_lists)
144 # Accumulate passing results.
145 all_passed += test_results.ok
146 # If we have failed tests, map them to tests to retry.
147 failed_tests = test_results.GetAllBroken()
148 tests_to_run = self._GetTestsToRetry(self.tests,
149 failed_tests)
150
151 # Bail out early if we have no more tests. This can happen if all tests
152 # pass before we're out of retries, for example.
153 if not tests_to_run:
154 break
155
156 final_results = TestResults()
157 # all_passed has accumulated all passing test results.
158 # test_results will have the results from the most recent run, which could
159 # include a variety of failure modes (unknown, crashed, failed, etc).
160 final_results = test_results
161 final_results.ok = all_passed
162
163 return final_results
164
165 def _MakeTestRunners(self, attached_devices):
166 """Initialize and return a list of PythonTestRunners.
167
168 Args:
169 attached_devices: list of device IDs attached to host.
170
171 Returns:
172 A list of PythonTestRunners, one for each device.
173 """
174 test_runners = []
175 for index, device in enumerate(attached_devices):
176 logging.warning('*' * 80)
177 logging.warning('Creating shard %d for %s', index, device)
178 logging.warning('*' * 80)
179 # Bind the PythonTestRunner to a device & shard index. Give it the
180 # runnable which it will use to actually execute the tests.
181 test_runner = PythonTestRunner(device, index)
182 test_runners.append(test_runner)
183
184 return test_runners
185
186 def _GetTestsToRetry(self, available_tests, failed_tests):
187 """Infers a list of tests to retry from failed tests and available tests.
188
189 Args:
190 available_tests: a list of tests which subclass PythonTestBase.
191 failed_tests: a list of SingleTestResults representing failed tests.
192
193 Returns:
194 A list of test objects which correspond to test names found in
195 failed_tests, or an empty list if there is no correspondence.
196 """
197 failed_test_names = map(lambda t: t.test_name, failed_tests)
198 tests_to_retry = [t for t in available_tests
199 if t.qualified_name in failed_test_names]
200 return tests_to_retry
OLDNEW
« no previous file with comments | « build/android/pylib/python_test_caller.py ('k') | build/android/pylib/run_java_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698