181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# Copyright (C) 2011 Google Inc. All rights reserved. 281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# 381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# Redistribution and use in source and binary forms, with or without 481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# modification, are permitted provided that the following conditions are 581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# met: 681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# 781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# * Redistributions of source code must retain the above copyright 881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# notice, this list of conditions and the following disclaimer. 981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# * Redistributions in binary form must reproduce the above 1081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# copyright notice, this list of conditions and the following disclaimer 1181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# in the documentation and/or other materials provided with the 1281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# distribution. 1381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# * Neither the name of Google Inc. nor the names of its 1481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# contributors may be used to endorse or promote products derived from 1581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# this software without specific prior written permission. 1681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# 1781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 2981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochimport logging 3081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochimport sys 3181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochimport threading 3281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochimport time 3381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 3481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochfrom webkitpy.layout_tests.layout_package import single_test_runner 3581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochfrom webkitpy.layout_tests.layout_package import test_results 3681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 3781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch_log = logging.getLogger(__name__) 3881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 3981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 4081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdochclass WorkerMixin(object): 4181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch """This class holds logic common to Worker and TestShellThread that 4281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch doesn't directly have to do with running the tests (which is 4381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch SingleTestRunner's responsibility. This class cannot stand on its own.""" 4481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def __init__(self): 4581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch assert False, "WorkerMixin can't be directly instantiated" 4681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 4781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def safe_init(self, port): 4881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch """This method should only be called when it is is safe for the mixin 4981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch to create state that can't be Pickled. 5081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 5181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch This routine exists so that the mixin can be created and then marshaled 5281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch across into a child process.""" 5381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._port = port 5481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._filesystem = port._filesystem 5581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._batch_count = 0 5681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._batch_size = self._options.batch_size 5781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._driver = None 582daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch tests_run_filename = self._filesystem.join(port.results_directory(), 5981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch "tests_run%d.txt" % self._worker_number) 6081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._tests_run_file = self._filesystem.open_text_file_for_writing(tests_run_filename) 6181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 6281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # FIXME: it's goofy that we have to track this at all, but it's due to 6381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # the awkward logic in TestShellThread._run(). When we remove that 6481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # file, we should rewrite this code so that caller keeps track of whether 6581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # the lock is held. 6681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._has_http_lock = False 6781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 6881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def cleanup(self): 6981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if self._driver: 7081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self.kill_driver() 7181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if self._has_http_lock: 7281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self.stop_servers_with_lock() 7381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if self._tests_run_file: 7481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._tests_run_file.close() 7581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._tests_run_file = None 7681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 7781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def timeout(self, test_input): 7881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch """Compute the appropriate timeout value for a test.""" 7981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # The DumpRenderTree watchdog uses 2.5x the timeout; we want to be 8081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # larger than that. We also add a little more padding if we're 8181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # running tests in a separate thread. 8281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # 8381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # Note that we need to convert the test timeout from a 8481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # string value in milliseconds to a float for Python. 8581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch driver_timeout_sec = 3.0 * float(test_input.timeout) / 1000.0 8681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if not self._options.run_singly: 8781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return driver_timeout_sec 8881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 8981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch thread_padding_sec = 1.0 9081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch thread_timeout_sec = driver_timeout_sec + thread_padding_sec 9181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return thread_timeout_sec 9281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 9381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def start_servers_with_lock(self): 9481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug('Acquiring http lock ...') 9581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._port.acquire_http_lock() 9681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug('Starting HTTP server ...') 9781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._port.start_http_server() 9881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug('Starting WebSocket server ...') 9981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._port.start_websocket_server() 10081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._has_http_lock = True 10181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 10281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def stop_servers_with_lock(self): 10381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if self._has_http_lock: 10481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug('Stopping HTTP server ...') 10581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._port.stop_http_server() 10681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug('Stopping WebSocket server ...') 10781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._port.stop_websocket_server() 10881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug('Releasing server lock ...') 10981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._port.release_http_lock() 11081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._has_http_lock = False 11181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 11281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def kill_driver(self): 11381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if self._driver: 11481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._driver.stop() 11581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._driver = None 11681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 11781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def run_test_with_timeout(self, test_input, timeout): 11881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if self._options.run_singly: 11981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return self._run_test_in_another_thread(test_input, timeout) 12081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch else: 12181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return self._run_test_in_this_thread(test_input) 12281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return result 12381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 12481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def clean_up_after_test(self, test_input, result): 12581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._batch_count += 1 12681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._tests_run_file.write(test_input.filename + "\n") 12781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch test_name = self._port.relative_test_filename(test_input.filename) 12881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 12981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if result.failures: 13081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # Check and kill DumpRenderTree if we need to. 13181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if any([f.should_kill_dump_render_tree() for f in result.failures]): 13281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self.kill_driver() 13381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # Reset the batch count since the shell just bounced. 13481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._batch_count = 0 13581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 13681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # Print the error message(s). 13781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug("%s %s failed:" % (self._name, test_name)) 13881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch for f in result.failures: 13981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug("%s %s" % (self._name, f.message())) 14081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch else: 14181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.debug("%s %s passed" % (self._name, test_name)) 14281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 14381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if self._batch_size > 0 and self._batch_count >= self._batch_size: 14481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # Bounce the shell and reset count. 14581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self.kill_driver() 14681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._batch_count = 0 14781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 14881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def _run_test_in_another_thread(self, test_input, thread_timeout_sec): 14981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch """Run a test in a separate thread, enforcing a hard time limit. 15081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 15181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Since we can only detect the termination of a thread, not any internal 15281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch state or progress, we can only run per-test timeouts when running test 15381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch files singly. 15481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 15581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Args: 15681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch test_input: Object containing the test filename and timeout 15781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch thread_timeout_sec: time to wait before killing the driver process. 15881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Returns: 15981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch A TestResult 16081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch """ 16181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch worker = self 16281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 16381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch driver = worker._port.create_driver(worker._worker_number) 16481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch driver.start() 16581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 16681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch class SingleTestThread(threading.Thread): 16781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def run(self): 1682daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch self.result = worker._run_single_test(driver, test_input) 16981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 17081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch thread = SingleTestThread() 17181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch thread.start() 17281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch thread.join(thread_timeout_sec) 1732daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch result = getattr(thread, 'result', None) 17481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if thread.isAlive(): 17581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # If join() returned with the thread still running, the 17681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # DumpRenderTree is completely hung and there's nothing 17781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # more we can do with it. We have to kill all the 17881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # DumpRenderTrees to free it up. If we're running more than 17981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # one DumpRenderTree thread, we'll end up killing the other 18081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # DumpRenderTrees too, introducing spurious crashes. We accept 18181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # that tradeoff in order to avoid losing the rest of this 18281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # thread's results. 18381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch _log.error('Test thread hung: killing all DumpRenderTrees') 18481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 18581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch driver.stop() 18681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 18781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if not result: 18881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch result = test_results.TestResult(test_input.filename, failures=[], test_run_time=0) 18981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return result 19081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 19181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def _run_test_in_this_thread(self, test_input): 19281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch """Run a single test file using a shared DumpRenderTree process. 19381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 19481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Args: 19581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch test_input: Object containing the test filename, uri and timeout 19681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 19781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Returns: a TestResult object. 19881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch """ 19981bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # poll() is not threadsafe and can throw OSError due to: 20081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch # http://bugs.python.org/issue1731717 20181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if not self._driver or self._driver.poll() is not None: 20281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._driver = self._port.create_driver(self._worker_number) 20381bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch self._driver.start() 20481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return self._run_single_test(self._driver, test_input) 20581bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch 20681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch def _run_single_test(self, driver, test_input): 20781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch return single_test_runner.run_single_test(self._port, self._options, 20881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch test_input, driver, self._name) 209