2011-02-11 Dirk Pranke <dpranke@chromium.org>
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / port / mock_drt.py
1 #!/usr/bin/env python
2 # Copyright (C) 2011 Google Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the Google name nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """
31 This is an implementation of the Port interface that overrides other
32 ports and changes the Driver binary to "MockDRT".
33 """
34
35 import logging
36 import optparse
37 import os
38 import sys
39
40 from webkitpy.common.system import filesystem
41
42 from webkitpy.layout_tests.port import base
43 from webkitpy.layout_tests.port import factory
44
45 _log = logging.getLogger(__name__)
46
47
48 class MockDRTPort(object):
49     """MockPort implementation of the Port interface."""
50
51     def __init__(self, **kwargs):
52         prefix = 'mock-'
53         if 'port_name' in kwargs:
54             kwargs['port_name'] = kwargs['port_name'][len(prefix):]
55         self.__delegate = factory.get(**kwargs)
56         self.__real_name = prefix + self.__delegate.name()
57
58     def real_name(self):
59         return self.__real_name
60
61     def __getattr__(self, name):
62         return getattr(self.__delegate, name)
63
64     def acquire_http_lock(self):
65         pass
66
67     def release_http_lock(self):
68         pass
69
70     def check_build(self, needs_http):
71         return True
72
73     def check_sys_deps(self, needs_http):
74         return True
75
76     def driver_cmd_line(self):
77         driver = self.create_driver(0)
78         return driver.cmd_line()
79
80     def _path_to_driver(self):
81         return os.path.abspath(__file__)
82
83     def create_driver(self, worker_number):
84         # We need to create a driver object as the delegate would, but
85         # overwrite the path to the driver binary in its command line. We do
86         # this by actually overwriting its cmd_line() method with a proxy
87         # method that splices in the mock_drt path and command line arguments
88         # in place of the actual path to the driver binary.
89
90         def overriding_cmd_line():
91             cmd = self.__original_driver_cmd_line()
92             index = cmd.index(self.__delegate._path_to_driver())
93             cmd[index:index + 1] = [sys.executable, self._path_to_driver(),
94                                     '--platform', self.name()]
95             return cmd
96
97         delegated_driver = self.__delegate.create_driver(worker_number)
98         self.__original_driver_cmd_line = delegated_driver.cmd_line
99         delegated_driver.cmd_line = overriding_cmd_line
100         return delegated_driver
101
102     def start_helper(self):
103         pass
104
105     def start_http_server(self):
106         pass
107
108     def start_websocket_server(self):
109         pass
110
111     def stop_helper(self):
112         pass
113
114     def stop_http_server(self):
115         pass
116
117     def stop_websocket_server(self):
118         pass
119
120
121 def main(argv, fs, stdin, stdout, stderr):
122     """Run the tests."""
123
124     options, args = parse_options(argv)
125     if options.chromium:
126         drt = MockChromiumDRT(options, args, fs, stdin, stdout, stderr)
127     else:
128         drt = MockDRT(options, args, fs, stdin, stdout, stderr)
129     return drt.run()
130
131
132 def parse_options(argv):
133     # FIXME: We have to do custom arg parsing instead of using the optparse
134     # module.  First, Chromium and non-Chromium DRTs have a different argument
135     # syntax.  Chromium uses --pixel-tests=<path>, and non-Chromium uses
136     # --pixel-tests as a boolean flag. Second, we don't want to have to list
137     # every command line flag DRT accepts, but optparse complains about
138     # unrecognized flags. At some point it might be good to share a common
139     # DRT options class between this file and webkit.py and chromium.py
140     # just to get better type checking.
141     platform_index = argv.index('--platform')
142     platform = argv[platform_index + 1]
143
144     pixel_tests = False
145     pixel_path = None
146     chromium = False
147     if platform.startswith('chromium'):
148         chromium = True
149         for arg in argv:
150             if arg.startswith('--pixel-tests'):
151                 pixel_tests = True
152                 pixel_path = arg[len('--pixel-tests='):]
153     else:
154         pixel_tests = '--pixel-tests' in argv
155     options = base.DummyOptions(chromium=chromium,
156                                 platform=platform,
157                                 pixel_tests=pixel_tests,
158                                 pixel_path=pixel_path)
159     return (options, [])
160
161
162 # FIXME: Should probably change this to use DriverInput after
163 # https://bugs.webkit.org/show_bug.cgi?id=53004 lands.
164 class _DRTInput(object):
165     def __init__(self, line):
166         vals = line.strip().split("'")
167         if len(vals) == 1:
168             self.uri = vals[0]
169             self.checksum = None
170         else:
171             self.uri = vals[0]
172             self.checksum = vals[1]
173
174
175 class MockDRT(object):
176     def __init__(self, options, args, filesystem, stdin, stdout, stderr):
177         self._options = options
178         self._args = args
179         self._filesystem = filesystem
180         self._stdout = stdout
181         self._stdin = stdin
182         self._stderr = stderr
183
184         port_name = None
185         if options.platform:
186             port_name = options.platform
187         self._port = factory.get(port_name, options=options, filesystem=filesystem)
188
189     def run(self):
190         while True:
191             line = self._stdin.readline()
192             if not line:
193                 break
194             self.run_one_test(self.parse_input(line))
195         return 0
196
197     def parse_input(self, line):
198         return _DRTInput(line)
199
200     def run_one_test(self, test_input):
201         port = self._port
202         if test_input.uri.startswith('http'):
203             test_name = port.uri_to_test_name(test_input.uri)
204             test_path = self._filesystem.join(port.layout_tests_dir(), test_name)
205         else:
206             test_path = test_input.uri
207
208         actual_text = port.expected_text(test_path)
209         if self._options.pixel_tests and test_input.checksum:
210             actual_checksum = port.expected_checksum(test_path)
211             actual_image = port.expected_image(test_path)
212
213         self._stdout.write('Content-Type: text/plain\n')
214
215         # FIXME: Note that we don't ensure there is a trailing newline!
216         # This mirrors actual (Mac) DRT behavior but is a bug.
217         self._stdout.write(actual_text)
218         self._stdout.write('#EOF\n')
219
220         if self._options.pixel_tests and test_input.checksum:
221             self._stdout.write('\n')
222             self._stdout.write('ActualHash: %s\n' % actual_checksum)
223             self._stdout.write('ExpectedHash: %s\n' % test_input.checksum)
224             if actual_checksum != test_input.checksum:
225                 self._stdout.write('Content-Type: image/png\n')
226                 self._stdout.write('Content-Length: %s\n\n' % len(actual_image))
227                 self._stdout.write(actual_image)
228         self._stdout.write('#EOF\n')
229         self._stdout.flush()
230         self._stderr.flush()
231
232
233 # FIXME: Should probably change this to use DriverInput after
234 # https://bugs.webkit.org/show_bug.cgi?id=53004 lands.
235 class _ChromiumDRTInput(_DRTInput):
236     def __init__(self, line):
237         vals = line.strip().split()
238         if len(vals) == 3:
239             self.uri, self.timeout, self.checksum = vals
240         else:
241             self.uri = vals[0]
242             self.timeout = vals[1]
243             self.checksum = None
244
245
246 class MockChromiumDRT(MockDRT):
247     def parse_input(self, line):
248         return _ChromiumDRTInput(line)
249
250     def run_one_test(self, test_input):
251         port = self._port
252         test_name = self._port.uri_to_test_name(test_input.uri)
253         test_path = self._filesystem.join(port.layout_tests_dir(), test_name)
254
255         actual_text = port.expected_text(test_path)
256         actual_image = ''
257         actual_checksum = ''
258         if self._options.pixel_tests and test_input.checksum:
259             actual_checksum = port.expected_checksum(test_path)
260             if actual_checksum != test_input.checksum:
261                 actual_image = port.expected_image(test_path)
262
263         self._stdout.write("#URL:%s\n" % test_input.uri)
264         if self._options.pixel_tests and test_input.checksum:
265             self._stdout.write("#MD5:%s\n" % actual_checksum)
266             self._filesystem.write_binary_file(self._options.pixel_path,
267                                                actual_image)
268         self._stdout.write(actual_text)
269
270         # FIXME: (See above FIXME as well). Chromium DRT appears to always
271         # ensure the text output has a trailing newline. Mac DRT does not.
272         if not actual_text.endswith('\n'):
273             self._stdout.write('\n')
274         self._stdout.write('#EOF\n')
275         self._stdout.flush()
276
277
278 if __name__ == '__main__':
279     fs = filesystem.FileSystem()
280     sys.exit(main(sys.argv[1:], fs, sys.stdin, sys.stdout, sys.stderr))