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