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

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

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

Powered by Google App Engine
This is Rietveld 408576698