1dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#!/usr/bin/env python
2dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# Copyright (C) 2010 Google Inc. All rights reserved.
35abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick# Copyright (C) 2010 Gabor Rapcsanyi <rgabor@inf.u-szeged.hu>, University of Szeged
4dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#
5dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# Redistribution and use in source and binary forms, with or without
6dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# modification, are permitted provided that the following conditions are
7dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# met:
8dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#
9dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#     * Redistributions of source code must retain the above copyright
10dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# notice, this list of conditions and the following disclaimer.
11dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#     * Redistributions in binary form must reproduce the above
12dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# copyright notice, this list of conditions and the following disclaimer
13dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# in the documentation and/or other materials provided with the
14dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# distribution.
15dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#     * Neither the Google name nor the names of its
16dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# contributors may be used to endorse or promote products derived from
17dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# this software without specific prior written permission.
18dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block#
19dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
31dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block"""WebKit implementations of the Port interface."""
32dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
332daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdochimport base64
34dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport logging
35ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdochimport operator
36dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport os
37dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport re
38dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport signal
39dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport sys
40dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport time
41dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockimport webbrowser
42dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
432fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockfrom webkitpy.common.system import ospath
442fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockfrom webkitpy.layout_tests.port import base
452fc2651226baac27029e38c9d6ef883fa32084dbSteve Blockfrom webkitpy.layout_tests.port import server_process
46dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
47dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block_log = logging.getLogger("webkitpy.layout_tests.port.webkit")
48dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
49dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
50dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockclass WebKitPort(base.Port):
51dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    """WebKit implementation of the Port class."""
52dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
53bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    def __init__(self, **kwargs):
54bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen        base.Port.__init__(self, **kwargs)
55dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        self._cached_apache_path = None
56dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
57dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # FIXME: disable pixel tests until they are run by default on the
58dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # build machines.
592fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if not hasattr(self._options, "pixel_tests") or self._options.pixel_tests == None:
602fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            self._options.pixel_tests = False
61dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
62dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def baseline_path(self):
63dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return self._webkit_baseline_path(self._name)
64dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
65dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def baseline_search_path(self):
66dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return [self._webkit_baseline_path(self._name)]
67dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
68dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def path_to_test_expectations_file(self):
69ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        return self._filesystem.join(self._webkit_baseline_path(self._name),
70ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                                     'test_expectations.txt')
71dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
72dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _build_driver(self):
736b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        configuration = self.get_option('configuration')
746b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        return self._config.build_dumprendertree(configuration)
75dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
76dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _check_driver(self):
77dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        driver_path = self._path_to_driver()
78ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        if not self._filesystem.exists(driver_path):
79dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            _log.error("DumpRenderTree was not found at %s" % driver_path)
80dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            return False
81dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return True
82dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
83dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def check_build(self, needs_http):
84a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        if self.get_option('build') and not self._build_driver():
85dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            return False
86dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if not self._check_driver():
87dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            return False
88a94275402997c11dd2e778633dacf4b7e630a35dBen Murdoch        if self.get_option('pixel_tests'):
89dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if not self.check_image_diff():
90dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                return False
91dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if not self._check_port_build():
92dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            return False
93dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return True
94dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
95dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _check_port_build(self):
96dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # Ports can override this method to do additional checks.
97dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return True
98dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
99dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def check_image_diff(self, override_step=None, logging=True):
100dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        image_diff_path = self._path_to_image_diff()
101ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        if not self._filesystem.exists(image_diff_path):
102dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            _log.error("ImageDiff was not found at %s" % image_diff_path)
103dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            return False
104dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return True
105dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
106bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    def diff_image(self, expected_contents, actual_contents,
107e14391e94c850b8bd03680c23b38978db68687a8John Reck                   diff_filename=None):
108dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        """Return True if the two files are different. Also write a delta
109dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        image of the two images into |diff_filename| if it is not None."""
110dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
111dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # Handle the case where the test didn't actually generate an image.
1122daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # FIXME: need unit tests for this.
1132daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if not actual_contents and not expected_contents:
1142daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            return False
1152daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if not actual_contents or not expected_contents:
116dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            return True
117dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
118e14391e94c850b8bd03680c23b38978db68687a8John Reck        sp = self._diff_image_request(expected_contents, actual_contents)
119bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen        return self._diff_image_reply(sp, diff_filename)
120dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
121e14391e94c850b8bd03680c23b38978db68687a8John Reck    def _diff_image_request(self, expected_contents, actual_contents):
1222fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        # FIXME: There needs to be a more sane way of handling default
1232fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        # values for options so that you can distinguish between a default
1242fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        # value of None and a default value that wasn't set.
125e14391e94c850b8bd03680c23b38978db68687a8John Reck        if self.get_option('tolerance') is not None:
126e14391e94c850b8bd03680c23b38978db68687a8John Reck            tolerance = self.get_option('tolerance')
127e14391e94c850b8bd03680c23b38978db68687a8John Reck        else:
128e14391e94c850b8bd03680c23b38978db68687a8John Reck            tolerance = 0.1
129e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke        command = [self._path_to_image_diff(), '--tolerance', str(tolerance)]
130dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        sp = server_process.ServerProcess(self, 'ImageDiff', command)
131dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
132dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        sp.write('Content-Length: %d\n%sContent-Length: %d\n%s' %
133bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen                 (len(actual_contents), actual_contents,
134bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen                  len(expected_contents), expected_contents))
135dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
136dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return sp
137dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
138bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen    def _diff_image_reply(self, sp, diff_filename):
139dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        timeout = 2.0
140dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        deadline = time.time() + timeout
141dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        output = sp.read_line(timeout)
142dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        while not sp.timed_out and not sp.crashed and output:
143dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if output.startswith('Content-Length'):
144dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                m = re.match('Content-Length: (\d+)', output)
145dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                content_length = int(m.group(1))
146dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                timeout = deadline - time.time()
147dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                output = sp.read(timeout, content_length)
148dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                break
149dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            elif output.startswith('diff'):
150dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                break
151dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            else:
152dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                timeout = deadline - time.time()
153dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                output = sp.read_line(deadline)
154dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
155dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        result = True
156dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if output.startswith('diff'):
157dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            m = re.match('diff: (.+)% (passed|failed)', output)
158dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if m.group(2) == 'passed':
159dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                result = False
160dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        elif output and diff_filename:
1612fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            self._filesystem.write_binary_file(diff_filename, output)
162dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        elif sp.timed_out:
163bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen            _log.error("ImageDiff timed out")
164dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        elif sp.crashed:
165dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            _log.error("ImageDiff crashed")
166dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        sp.stop()
167dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return result
168dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
1692daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    def default_results_directory(self):
170dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # Results are store relative to the built products to make it easy
171dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # to have multiple copies of webkit checked out and built.
1722daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return self._build_path('layout-test-results')
173dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
174dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def setup_test_run(self):
175dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # This port doesn't require any specific configuration.
176dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        pass
177dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
1784576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang    def create_driver(self, worker_number):
1794576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang        return WebKitDriver(self, worker_number)
180dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
181dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _tests_for_other_platforms(self):
1822daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # By default we will skip any directory under LayoutTests/platform
1832daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # that isn't in our baseline search path (this mirrors what
1842daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # old-run-webkit-tests does in findTestsToRun()).
1852daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # Note this returns LayoutTests/platform/*, not platform/*/*.
1862daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        entries = self._filesystem.glob(self._webkit_baseline_path('*'))
1872daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        dirs_to_skip = []
1882daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        for entry in entries:
1892daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            if self._filesystem.isdir(entry) and not entry in self.baseline_search_path():
1902daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                basename = self._filesystem.basename(entry)
1912daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                dirs_to_skip.append('platform/%s' % basename)
1922daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return dirs_to_skip
193dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
1945abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def _runtime_feature_list(self):
1955abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        """Return the supported features of DRT. If a port doesn't support
1965abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        this DRT switch, it has to override this method to return None"""
1975abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        driver_path = self._path_to_driver()
1985abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        feature_list = ' '.join(os.popen(driver_path + " --print-supported-features 2>&1").readlines())
1995abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        if "SupportedFeatures:" in feature_list:
2005abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            return feature_list
2015abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return None
2025abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
2035abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def _supported_symbol_list(self):
2045abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        """Return the supported symbols of WebCore."""
2055abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        webcore_library_path = self._path_to_webcore_library()
2065abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        if not webcore_library_path:
2075abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            return None
2085abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        symbol_list = ' '.join(os.popen("nm " + webcore_library_path).readlines())
2095abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return symbol_list
2105abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
2115abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def _directories_for_features(self):
2125abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        """Return the supported feature dictionary. The keys are the
2135abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        features and the values are the directories in lists."""
2145abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        directories_for_features = {
2155abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "Accelerated Compositing": ["compositing"],
2165abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "3D Rendering": ["animations/3d", "transforms/3d"],
2175abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        }
2185abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return directories_for_features
2195abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
2205abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def _directories_for_symbols(self):
2215abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        """Return the supported feature dictionary. The keys are the
2225abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        symbols and the values are the directories in lists."""
2235abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        directories_for_symbol = {
2245abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "MathMLElement": ["mathml"],
2255abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "GraphicsLayer": ["compositing"],
2265abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "WebCoreHas3DRendering": ["animations/3d", "transforms/3d"],
227bec39347bb3bb5bf1187ccaf471d26247f28b585Kristian Monsen            "WebGLShader": ["fast/canvas/webgl", "compositing/webgl", "http/tests/canvas/webgl"],
2285abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "WMLElement": ["http/tests/wml", "fast/wml", "wml"],
2295abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "parseWCSSInputProperty": ["fast/wcss"],
2305abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            "isXHTMLMPDocument": ["fast/xhtmlmp"],
2315abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        }
2325abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return directories_for_symbol
2335abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
2345abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def _skipped_tests_for_unsupported_features(self):
2355abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        """Return the directories of unsupported tests. Search for the
2365abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        symbols in the symbol_list, if found add the corresponding
2375abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        directories to the skipped directory list."""
2385abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        feature_list = self._runtime_feature_list()
2395abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        directories = self._directories_for_features()
2405abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
2415abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        # if DRT feature detection not supported
2425abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        if not feature_list:
2435abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            feature_list = self._supported_symbol_list()
2445abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            directories = self._directories_for_symbols()
2455abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
2465abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        if not feature_list:
2475abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick            return []
2485abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
2495abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        skipped_directories = [directories[feature]
2505abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick                              for feature in directories.keys()
2515abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick                              if feature not in feature_list]
2525abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return reduce(operator.add, skipped_directories)
2535abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
254dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _tests_for_disabled_features(self):
255dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # FIXME: This should use the feature detection from
256dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # webkitperl/features.pm to match run-webkit-tests.
257dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # For now we hard-code a list of features known to be disabled on
258dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # the Mac platform.
259dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        disabled_feature_tests = [
260dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "fast/xhtmlmp",
261dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "http/tests/wml",
262dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "mathml",
263dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "wml",
264dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        ]
265dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # FIXME: webarchive tests expect to read-write from
266dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # -expected.webarchive files instead of .txt files.
267dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # This script doesn't know how to do that yet, so pretend they're
268dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # just "disabled".
269dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        webarchive_tests = [
270dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "webarchive",
271dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "svg/webarchive",
272dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "http/tests/webarchive",
273dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            "svg/custom/image-with-prefix-in-webarchive.svg",
274dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        ]
2755abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        unsupported_feature_tests = self._skipped_tests_for_unsupported_features()
2765abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return disabled_feature_tests + webarchive_tests + unsupported_feature_tests
277dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
2782fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    def _tests_from_skipped_file_contents(self, skipped_file_contents):
279dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        tests_to_skip = []
2802fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        for line in skipped_file_contents.split('\n'):
281dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            line = line.strip()
282dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if line.startswith('#') or not len(line):
283dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                continue
284dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            tests_to_skip.append(line)
285dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return tests_to_skip
286dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
287dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _skipped_file_paths(self):
288ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        return [self._filesystem.join(self._webkit_baseline_path(self._name), 'Skipped')]
289dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
290dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _expectations_from_skipped_files(self):
291dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        tests_to_skip = []
292dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        for filename in self._skipped_file_paths():
293ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch            if not self._filesystem.exists(filename):
294dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                _log.warn("Failed to open Skipped file: %s" % filename)
295dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                continue
2962fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            skipped_file_contents = self._filesystem.read_text_file(filename)
2972fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            tests_to_skip.extend(self._tests_from_skipped_file_contents(skipped_file_contents))
298dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return tests_to_skip
299dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
300dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def test_expectations(self):
301dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # The WebKit mac port uses a combination of a test_expectations file
302dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # and 'Skipped' files.
30321939df44de1705786c545cd1bf519d47250322dBen Murdoch        expectations_path = self.path_to_test_expectations_file()
304ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        return self._filesystem.read_text_file(expectations_path) + self._skips()
305dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
306dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _skips(self):
307dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # Each Skipped file contains a list of files
308dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # or directories to be skipped during the test run. The total list
309dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # of tests to skipped is given by the contents of the generic
310dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # Skipped file found in platform/X plus a version-specific file
311dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # found in platform/X-version. Duplicate entries are allowed.
312dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # This routine reads those files and turns contents into the
313dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # format expected by test_expectations.
314dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
3155abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        tests_to_skip = self.skipped_layout_tests()
3165abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        skip_lines = map(lambda test_path: "BUG_SKIPPED SKIP : %s = FAIL" %
3175abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick                                test_path, tests_to_skip)
3185abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return "\n".join(skip_lines)
3195abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
3205abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def skipped_layout_tests(self):
321dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # Use a set to allow duplicates
322dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        tests_to_skip = set(self._expectations_from_skipped_files())
323dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        tests_to_skip.update(self._tests_for_other_platforms())
324dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        tests_to_skip.update(self._tests_for_disabled_features())
3255abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return tests_to_skip
326dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
327dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _build_path(self, *comps):
3286b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        return self._filesystem.join(self._config.build_directory(
3296b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner            self.get_option('configuration')), *comps)
330dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
331dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _path_to_driver(self):
332dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return self._build_path('DumpRenderTree')
333dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
3345abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def _path_to_webcore_library(self):
3355abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick        return None
3365abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
337dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _path_to_helper(self):
338dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return None
339dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
340dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _path_to_image_diff(self):
341dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return self._build_path('ImageDiff')
342dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
343dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _path_to_wdiff(self):
344dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        # FIXME: This does not exist on a default Mac OS X Leopard install.
345dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return 'wdiff'
346dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
347dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def _path_to_apache(self):
348dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if not self._cached_apache_path:
349dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            # The Apache binary path can vary depending on OS and distribution
350dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            # See http://wiki.apache.org/httpd/DistrosDefaultLayout
351dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            for path in ["/usr/sbin/httpd", "/usr/sbin/apache2"]:
352ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch                if self._filesystem.exists(path):
353dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                    self._cached_apache_path = path
354dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                    break
355dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
356dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            if not self._cached_apache_path:
357dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block                _log.error("Could not find apache. Not installed or unknown path.")
358dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
359dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return self._cached_apache_path
360dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
361dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
362dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Blockclass WebKitDriver(base.Driver):
363dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    """WebKit implementation of the DumpRenderTree interface."""
364dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
3654576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang    def __init__(self, port, worker_number):
3664576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang        self._worker_number = worker_number
367dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        self._port = port
368ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        self._driver_tempdir = port._filesystem.mkdtemp(prefix='DumpRenderTree-')
3695abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick
3705abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306Iain Merrick    def __del__(self):
371ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        self._port._filesystem.rmtree(str(self._driver_tempdir))
372dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
3734576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang    def cmd_line(self):
3744576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang        cmd = self._command_wrapper(self._port.get_option('wrapper'))
3752daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        cmd.append(self._port._path_to_driver())
3764576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang        if self._port.get_option('pixel_tests'):
3774576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang            cmd.append('--pixel-tests')
3782daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        cmd.extend(self._port.get_option('additional_drt_flag', []))
3792daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        cmd.append('-')
3804576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang        return cmd
38168513a70bcd92384395513322f1b801e7bf9c729Steve Block
38221939df44de1705786c545cd1bf519d47250322dBen Murdoch    def start(self):
383e458d70a0d18538346f41b503114c9ebe6b2ce12Leon Clarke        environment = self._port.setup_environ_for_server()
384dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        environment['DYLD_FRAMEWORK_PATH'] = self._port._build_path()
385ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch        environment['DUMPRENDERTREE_TEMP'] = str(self._driver_tempdir)
386dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        self._server_process = server_process.ServerProcess(self._port,
3874576aa36e9a9671459299c7963ac95aa94beaea9Shimeng (Simon) Wang            "DumpRenderTree", self.cmd_line(), environment)
388dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
389dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def poll(self):
390dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return self._server_process.poll()
391dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
392dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def restart(self):
393dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        self._server_process.stop()
394dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        self._server_process.start()
395dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        return
396dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
397dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    # FIXME: This function is huge.
3982fc2651226baac27029e38c9d6ef883fa32084dbSteve Block    def run_test(self, driver_input):
3992fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        uri = self._port.filename_to_uri(driver_input.filename)
400dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if uri.startswith("file:///"):
401dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            command = uri[7:]
402dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        else:
403dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            command = uri
404dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
4052fc2651226baac27029e38c9d6ef883fa32084dbSteve Block        if driver_input.image_hash:
4062fc2651226baac27029e38c9d6ef883fa32084dbSteve Block            command += "'" + driver_input.image_hash
407dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        command += "\n"
408dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
4096b70adc33054f8aee8c54d0f460458a9df11b8a5Russell Brenner        start_time = time.time()
410dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        self._server_process.write(command)
411dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
4122daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        text = None
4132daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        image = None
414dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        actual_image_hash = None
4152daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        audio = None
4162daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        deadline = time.time() + int(driver_input.timeout) / 1000.0
417dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
4182daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # First block is either text or audio
4192daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        block = self._read_block(deadline)
4202daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if block.content_type == 'audio/wav':
4212daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            audio = block.decoded_content
4222daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        else:
4232daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            text = block.decoded_content
424dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
4252daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # Now read an optional second block of image data
4262daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        block = self._read_block(deadline)
4272daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if block.content and block.content_type == 'image/png':
4282daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            image = block.decoded_content
4292daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            actual_image_hash = block.content_hash
430dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
43121939df44de1705786c545cd1bf519d47250322dBen Murdoch        error_lines = self._server_process.error.splitlines()
43221939df44de1705786c545cd1bf519d47250322dBen Murdoch        # FIXME: This is a hack.  It is unclear why sometimes
43321939df44de1705786c545cd1bf519d47250322dBen Murdoch        # we do not get any error lines from the server_process
43421939df44de1705786c545cd1bf519d47250322dBen Murdoch        # probably we are not flushing stderr.
43521939df44de1705786c545cd1bf519d47250322dBen Murdoch        if error_lines and error_lines[-1] == "#EOF":
43621939df44de1705786c545cd1bf519d47250322dBen Murdoch            error_lines.pop()  # Remove the expected "#EOF"
43721939df44de1705786c545cd1bf519d47250322dBen Murdoch        error = "\n".join(error_lines)
43821939df44de1705786c545cd1bf519d47250322dBen Murdoch        # FIXME: This seems like the wrong section of code to be doing
43921939df44de1705786c545cd1bf519d47250322dBen Murdoch        # this reset in.
44021939df44de1705786c545cd1bf519d47250322dBen Murdoch        self._server_process.error = ""
4412daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return base.DriverOutput(text, image, actual_image_hash, audio,
4422daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            crash=self._server_process.crashed, test_time=time.time() - start_time,
4432daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            timeout=self._server_process.timed_out, error=error)
4442daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
4452daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    def _read_block(self, deadline):
4462daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        LENGTH_HEADER = 'Content-Length: '
4472daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        HASH_HEADER = 'ActualHash: '
4482daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        TYPE_HEADER = 'Content-Type: '
4492daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        ENCODING_HEADER = 'Content-Transfer-Encoding: '
4502daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        content_type = None
4512daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        encoding = None
4522daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        content_hash = None
4532daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        content_length = None
4542daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
4552daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # Content is treated as binary data even though the text output
4562daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        # is usually UTF-8.
4572daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        content = ''
4582daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        timeout = deadline - time.time()
4592daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        line = self._server_process.read_line(timeout)
4602daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        while (not self._server_process.timed_out
4612daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch               and not self._server_process.crashed
4622daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch               and line.rstrip() != "#EOF"):
4632daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            if line.startswith(TYPE_HEADER) and content_type is None:
4642daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                content_type = line.split()[1]
4652daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            elif line.startswith(ENCODING_HEADER) and encoding is None:
4662daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                encoding = line.split()[1]
4672daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            elif line.startswith(LENGTH_HEADER) and content_length is None:
4682daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                timeout = deadline - time.time()
4692daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                content_length = int(line[len(LENGTH_HEADER):])
4702daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                # FIXME: Technically there should probably be another blank
4712daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                # line here, but DRT doesn't write one.
4722daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                content = self._server_process.read(timeout, content_length)
4732daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            elif line.startswith(HASH_HEADER):
4742daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                content_hash = line.split()[1]
4752daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            else:
4762daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch                content += line
4772daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            line = self._server_process.read_line(timeout)
4782daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            timeout = deadline - time.time()
4792daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        return ContentBlock(content_type, encoding, content_hash, content)
480dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block
481dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block    def stop(self):
482dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block        if self._server_process:
483dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            self._server_process.stop()
484dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block            self._server_process = None
4852daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
4862daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch
4872daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdochclass ContentBlock(object):
4882daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch    def __init__(self, content_type, encoding, content_hash, content):
4892daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        self.content_type = content_type
4902daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        self.encoding = encoding
4912daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        self.content_hash = content_hash
4922daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        self.content = content
4932daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        if self.encoding == 'base64':
4942daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            self.decoded_content = base64.b64decode(content)
4952daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch        else:
4962daae5fd11344eaa88a0d92b0f6d65f8d2255c00Ben Murdoch            self.decoded_content = content
497