1 # Copyright (C) 2010 Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 import multiprocessing
37 multiprocessing = None
40 from webkitpy.common.system import outputcapture
42 from webkitpy.layout_tests import port
43 from webkitpy.layout_tests.controllers import manager_worker_broker
44 from webkitpy.layout_tests.controllers import message_broker
45 from webkitpy.layout_tests.views import printing
47 # In order to reliably control when child workers are starting and stopping,
48 # we use a pair of global variables to hold queues used for messaging. Ideally
49 # we wouldn't need globals, but we can't pass these through a lexical closure
50 # because those can't be Pickled and sent to a subprocess, and we'd prefer not
51 # to have to pass extra arguments to the worker in the start_worker() call.
56 def make_broker(manager, worker_model, start_queue=None, stop_queue=None):
59 starting_queue = start_queue
60 stopping_queue = stop_queue
61 options = get_options(worker_model)
62 return manager_worker_broker.get(port.get("test"), options, manager, _TestWorker)
65 class _TestWorker(manager_worker_broker.AbstractWorker):
66 def __init__(self, broker_connection, worker_number, options):
67 self._broker_connection = broker_connection
68 self._options = options
69 self._worker_number = worker_number
70 self._name = 'TestWorker/%d' % worker_number
72 self._canceled = False
73 self._starting_queue = starting_queue
74 self._stopping_queue = stopping_queue
76 def handle_stop(self, src):
79 def handle_test(self, src, an_int, a_str):
81 assert a_str == "hello, world"
82 self._broker_connection.post_message('test', 2, 'hi, everybody')
85 return self._stopped or self._canceled
94 if self._starting_queue:
95 self._starting_queue.put('')
97 if self._stopping_queue:
98 self._stopping_queue.get()
100 self._broker_connection.run_message_loop()
101 self._broker_connection.yield_to_broker()
102 self._broker_connection.post_message('done')
104 self._broker_connection.post_message('exception', (type(e), str(e), None))
107 def get_options(worker_model):
108 option_list = (manager_worker_broker.runtime_options() +
109 printing.print_options() +
110 [optparse.make_option("--experimental-fully-parallel", default=False),
111 optparse.make_option("--child-processes", default='2')])
112 parser = optparse.OptionParser(option_list=option_list)
113 options, args = parser.parse_args(args=['--worker-model', worker_model])
118 class FunctionTests(unittest.TestCase):
119 def test_get__inline(self):
120 self.assertTrue(make_broker(self, 'inline') is not None)
122 def test_get__processes(self):
123 # This test sometimes fails on Windows. See <http://webkit.org/b/55087>.
124 if sys.platform in ('cygwin', 'win32'):
128 self.assertTrue(make_broker(self, 'processes') is not None)
130 self.assertRaises(ValueError, make_broker, self, 'processes')
132 def test_get__unknown(self):
133 self.assertRaises(ValueError, make_broker, self, 'unknown')
136 class _TestsMixin(object):
137 """Mixin class that implements a series of tests to enforce the
138 contract all implementations must follow."""
146 def handle_done(self, src):
149 def handle_test(self, src, an_int, a_str):
150 self._an_int = an_int
153 def handle_exception(self, src, exc_info):
154 self._exception = exc_info
162 self._exception = None
163 self._worker_model = None
165 def make_broker(self, starting_queue=None, stopping_queue=None):
166 self._broker = make_broker(self, self._worker_model, starting_queue,
169 def test_cancel(self):
171 worker = self._broker.start_worker(0)
173 self._broker.post_message('test', 1, 'hello, world')
175 self.assertFalse(worker.is_alive())
179 worker = self._broker.start_worker(0)
180 self._broker.post_message('test', 1, 'hello, world')
181 self._broker.post_message('stop')
182 self._broker.run_message_loop()
184 self.assertFalse(worker.is_alive())
185 self.assertTrue(self.is_done())
186 self.assertEqual(self._an_int, 2)
187 self.assertEqual(self._a_str, 'hi, everybody')
189 def test_unknown_message(self):
191 worker = self._broker.start_worker(0)
192 self._broker.post_message('unknown')
193 self._broker.run_message_loop()
196 self.assertTrue(self.is_done())
197 self.assertFalse(worker.is_alive())
198 self.assertEquals(self._exception[0], ValueError)
199 self.assertEquals(self._exception[1],
200 "TestWorker/0: received message 'unknown' it couldn't handle")
203 # FIXME: https://bugs.webkit.org/show_bug.cgi?id=54520.
204 if multiprocessing and sys.platform not in ('cygwin', 'win32'):
206 class MultiProcessBrokerTests(_TestsMixin, unittest.TestCase):
208 _TestsMixin.setUp(self)
209 self._worker_model = 'processes'
212 return multiprocessing.Queue()
215 class FunctionsTest(unittest.TestCase):
216 def test_runtime_options(self):
217 option_list = manager_worker_broker.runtime_options()
218 parser = optparse.OptionParser(option_list=option_list)
219 options, args = parser.parse_args([])
220 self.assertTrue(options)
223 class InterfaceTest(unittest.TestCase):
224 # These tests mostly exist to pacify coverage.
226 # FIXME: There must be a better way to do this and also verify
227 # that classes do implement every abstract method in an interface.
228 def test_managerconnection_is_abstract(self):
229 # Test that all the base class methods are abstract and have the
230 # signature we expect.
231 broker = make_broker(self, 'inline')
232 obj = manager_worker_broker._ManagerConnection(broker._broker, None, self, None)
233 self.assertRaises(NotImplementedError, obj.start_worker, 0)
235 def test_workerconnection_is_abstract(self):
236 # Test that all the base class methods are abstract and have the
237 # signature we expect.
238 broker = make_broker(self, 'inline')
239 obj = manager_worker_broker._WorkerConnection(broker._broker, _TestWorker, 0, None)
240 self.assertRaises(NotImplementedError, obj.cancel)
241 self.assertRaises(NotImplementedError, obj.is_alive)
242 self.assertRaises(NotImplementedError, obj.join, None)
245 if __name__ == '__main__':