blob: 2ee659c20409e730092d501e31415a37fdc3ed8d [file] [log] [blame]
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001# Copyright (c) 2012 Google Inc. All rights reserved.
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions are
5# met:
6#
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
12# distribution.
13# * Neither the Google name 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.
16#
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.
28
29"""
30This is an implementation of the Port interface that overrides other
31ports and changes the Driver binary to "MockDRT".
32
33The MockDRT objects emulate what a real DRT would do. In particular, they
34return the output a real DRT would return for a given test, assuming that
35test actually passes (except for reftests, which currently cause the
36MockDRT to crash).
37"""
38
39import base64
40import logging
41import optparse
42import os
43import sys
44
45# Since we execute this script directly as part of the unit tests, we need to ensure
46# that Tools/Scripts is in sys.path for the next imports to work correctly.
47script_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
48if script_dir not in sys.path:
49 sys.path.append(script_dir)
50
51from webkitpy.common.system.systemhost import SystemHost
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +010052from webkitpy.layout_tests.port.driver import DriverInput, DriverOutput
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000053from webkitpy.layout_tests.port.factory import PortFactory
54
55_log = logging.getLogger(__name__)
56
57
58class MockDRTPort(object):
59 port_name = 'mock'
60
61 @classmethod
62 def determine_full_port_name(cls, host, options, port_name):
63 return port_name
64
65 def __init__(self, host, port_name, **kwargs):
66 self.__delegate = PortFactory(host).get(port_name.replace('mock-', ''), **kwargs)
67
68 def __getattr__(self, name):
69 return getattr(self.__delegate, name)
70
71 def check_build(self, needs_http):
72 return True
73
74 def check_sys_deps(self, needs_http):
75 return True
76
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +010077 def _driver_class(self):
78 return self._mocked_driver_maker
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000079
80 @staticmethod
81 def _mocked_driver_maker(port, worker_number, pixel_tests, no_timeout=False):
82 path_to_this_file = port.host.filesystem.abspath(__file__.replace('.pyc', '.py'))
83 driver = port.__delegate._driver_class()(port, worker_number, pixel_tests, no_timeout)
84 driver.cmd_line = port._overriding_cmd_line(driver.cmd_line,
85 port.__delegate._path_to_driver(),
86 sys.executable,
87 path_to_this_file,
88 port.__delegate.name())
89 return driver
90
91 @staticmethod
92 def _overriding_cmd_line(original_cmd_line, driver_path, python_exe, this_file, port_name):
93 def new_cmd_line(pixel_tests, per_test_args):
94 cmd_line = original_cmd_line(pixel_tests, per_test_args)
95 index = cmd_line.index(driver_path)
96 cmd_line[index:index + 1] = [python_exe, this_file, '--platform', port_name]
97 return cmd_line
98
99 return new_cmd_line
100
101 def start_helper(self):
102 pass
103
104 def start_http_server(self, number_of_servers):
105 pass
106
107 def start_websocket_server(self):
108 pass
109
110 def acquire_http_lock(self):
111 pass
112
113 def stop_helper(self):
114 pass
115
116 def stop_http_server(self):
117 pass
118
119 def stop_websocket_server(self):
120 pass
121
122 def release_http_lock(self):
123 pass
124
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000125 def show_results_html_file(self, results_filename):
126 pass
127
Ben Murdoch83750172013-07-24 10:36:59 +0100128 def _make_wdiff_available(self):
129 self.__delegate._wdiff_available = True
130
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000131
132def main(argv, host, stdin, stdout, stderr):
133 """Run the tests."""
134
135 options, args = parse_options(argv)
136 if options.test_shell:
137 drt = MockTestShell(options, args, host, stdin, stdout, stderr)
138 else:
139 drt = MockDRT(options, args, host, stdin, stdout, stderr)
140 return drt.run()
141
142
143def parse_options(argv):
144 # FIXME: We have to do custom arg parsing instead of using the optparse
145 # module. First, Chromium and non-Chromium DRTs have a different argument
146 # syntax. Chromium uses --pixel-tests=<path>, and non-Chromium uses
147 # --pixel-tests as a boolean flag. Second, we don't want to have to list
148 # every command line flag DRT accepts, but optparse complains about
149 # unrecognized flags. At some point it might be good to share a common
150 # DRT options class between this file and webkit.py and chromium.py
151 # just to get better type checking.
152 platform_index = argv.index('--platform')
153 platform = argv[platform_index + 1]
154
155 pixel_tests = False
156 pixel_path = None
157 test_shell = '--test-shell' in argv
158 if test_shell:
159 for arg in argv:
160 if arg.startswith('--pixel-tests'):
161 pixel_tests = True
162 pixel_path = arg[len('--pixel-tests='):]
163 else:
164 pixel_tests = '--pixel-tests' in argv
165 options = optparse.Values({'test_shell': test_shell, 'platform': platform, 'pixel_tests': pixel_tests, 'pixel_path': pixel_path})
166 return (options, argv)
167
168
169class MockDRT(object):
170 def __init__(self, options, args, host, stdin, stdout, stderr):
171 self._options = options
172 self._args = args
173 self._host = host
174 self._stdout = stdout
175 self._stdin = stdin
176 self._stderr = stderr
177
178 port_name = None
179 if options.platform:
180 port_name = options.platform
181 self._port = PortFactory(host).get(port_name=port_name, options=options)
182 self._driver = self._port.create_driver(0)
183
184 def run(self):
185 while True:
186 line = self._stdin.readline()
187 if not line:
188 return 0
189 driver_input = self.input_from_line(line)
190 dirname, basename = self._port.split_test(driver_input.test_name)
191 is_reftest = (self._port.reference_files(driver_input.test_name) or
192 self._port.is_reference_html_file(self._port._filesystem, dirname, basename))
193 output = self.output_for_test(driver_input, is_reftest)
194 self.write_test_output(driver_input, output, is_reftest)
195
196 def input_from_line(self, line):
197 vals = line.strip().split("'")
198 if len(vals) == 1:
199 uri = vals[0]
200 checksum = None
201 else:
202 uri = vals[0]
203 checksum = vals[1]
204 if uri.startswith('http://') or uri.startswith('https://'):
205 test_name = self._driver.uri_to_test(uri)
206 else:
207 test_name = self._port.relative_test_filename(uri)
208
209 return DriverInput(test_name, 0, checksum, self._options.pixel_tests)
210
211 def output_for_test(self, test_input, is_reftest):
212 port = self._port
213 actual_text = port.expected_text(test_input.test_name)
214 actual_audio = port.expected_audio(test_input.test_name)
215 actual_image = None
216 actual_checksum = None
217 if is_reftest:
218 # Make up some output for reftests.
219 actual_text = 'reference text\n'
220 actual_checksum = 'mock-checksum'
221 actual_image = 'blank'
222 if test_input.test_name.endswith('-mismatch.html'):
223 actual_text = 'not reference text\n'
224 actual_checksum = 'not-mock-checksum'
225 actual_image = 'not blank'
226 elif self._options.pixel_tests and test_input.image_hash:
227 actual_checksum = port.expected_checksum(test_input.test_name)
228 actual_image = port.expected_image(test_input.test_name)
229
230 return DriverOutput(actual_text, actual_image, actual_checksum, actual_audio)
231
232 def write_test_output(self, test_input, output, is_reftest):
233 if output.audio:
234 self._stdout.write('Content-Type: audio/wav\n')
235 self._stdout.write('Content-Transfer-Encoding: base64\n')
236 self._stdout.write(base64.b64encode(output.audio))
237 else:
238 self._stdout.write('Content-Type: text/plain\n')
239 # FIXME: Note that we don't ensure there is a trailing newline!
240 # This mirrors actual (Mac) DRT behavior but is a bug.
241 if output.text:
242 self._stdout.write(output.text)
243
244 self._stdout.write('#EOF\n')
245
246 if self._options.pixel_tests and output.image_hash:
247 self._stdout.write('\n')
248 self._stdout.write('ActualHash: %s\n' % output.image_hash)
249 self._stdout.write('ExpectedHash: %s\n' % test_input.image_hash)
250 if output.image_hash != test_input.image_hash:
251 self._stdout.write('Content-Type: image/png\n')
252 self._stdout.write('Content-Length: %s\n' % len(output.image))
253 self._stdout.write(output.image)
254 self._stdout.write('#EOF\n')
255 self._stdout.flush()
256 self._stderr.write('#EOF\n')
257 self._stderr.flush()
258
259
260class MockTestShell(MockDRT):
261 def input_from_line(self, line):
262 vals = line.strip().split()
263 if len(vals) == 3:
264 uri, timeout, checksum = vals
265 else:
266 uri, timeout = vals
267 checksum = None
268
269 test_name = self._driver.uri_to_test(uri)
270 return DriverInput(test_name, timeout, checksum, self._options.pixel_tests)
271
272 def output_for_test(self, test_input, is_reftest):
273 # FIXME: This is a hack to make virtual tests work. Need something more general.
274 original_test_name = test_input.test_name
275 if '--enable-accelerated-2d-canvas' in self._args and 'canvas' in test_input.test_name:
276 test_input.test_name = 'platform/chromium/virtual/gpu/' + test_input.test_name
277 output = super(MockTestShell, self).output_for_test(test_input, is_reftest)
278 test_input.test_name = original_test_name
279 return output
280
281 def write_test_output(self, test_input, output, is_reftest):
282 self._stdout.write("#URL:%s\n" % self._driver.test_to_uri(test_input.test_name))
283 if self._options.pixel_tests and output.image_hash:
284 self._stdout.write("#MD5:%s\n" % output.image_hash)
285 if output.image:
286 self._host.filesystem.maybe_make_directory(self._host.filesystem.dirname(self._options.pixel_path))
287 self._host.filesystem.write_binary_file(self._options.pixel_path, output.image)
288 if output.text:
289 self._stdout.write(output.text)
290
291 if output.text and not output.text.endswith('\n'):
292 self._stdout.write('\n')
293 self._stdout.write('#EOF\n')
294 self._stdout.flush()
295
296
297if __name__ == '__main__':
298 # Note that the Mock in MockDRT refers to the fact that it is emulating a
299 # real DRT, and as such, it needs access to a real SystemHost, not a MockSystemHost.
300 sys.exit(main(sys.argv[1:], SystemHost(), sys.stdin, sys.stdout, sys.stderr))