370327fb01b583c0dd02587e643d978b184eb01e
[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
57     def __getattr__(self, name):
58         return getattr(self.__delegate, name)
59
60     def acquire_http_lock(self):
61         pass
62
63     def release_http_lock(self):
64         pass
65
66     def check_build(self, needs_http):
67         return True
68
69     def check_sys_deps(self, needs_http):
70         return True
71
72     def driver_cmd_line(self):
73         driver = self.create_driver(0)
74         return driver.cmd_line()
75
76     def _path_to_driver(self):
77         return os.path.abspath(__file__)
78
79     def create_driver(self, worker_number):
80         # We need to create a driver object as the delegate would, but
81         # overwrite the path to the driver binary in its command line. We do
82         # this by actually overwriting its cmd_line() method with a proxy
83         # method that splices in the mock_drt path and command line arguments
84         # in place of the actual path to the driver binary.
85
86         def overriding_cmd_line():
87             cmd = self.__original_driver_cmd_line()
88             index = cmd.index(self.__delegate._path_to_driver())
89             cmd[index:index + 1] = [sys.executable, self._path_to_driver(),
90                                     '--platform', self.name()]
91             return cmd
92
93         delegated_driver = self.__delegate.create_driver(worker_number)
94         self.__original_driver_cmd_line = delegated_driver.cmd_line
95         delegated_driver.cmd_line = overriding_cmd_line
96         return delegated_driver
97
98     def start_helper(self):
99         pass
100
101     def start_http_server(self):
102         pass
103
104     def start_websocket_server(self):
105         pass
106
107     def stop_helper(self):
108         pass
109
110     def stop_http_server(self):
111         pass
112
113     def stop_websocket_server(self):
114         pass
115
116
117 def main(argv, fs, stdin, stdout, stderr):
118     """Run the tests."""
119
120     options, args = parse_options(argv)
121     if options.chromium:
122         drt = MockChromiumDRT(options, args, fs, stdin, stdout, stderr)
123     else:
124         drt = MockDRT(options, args, fs, stdin, stdout, stderr)
125     return drt.run()
126
127
128 def parse_options(argv):
129     # FIXME: We have to do custom arg parsing instead of using the optparse
130     # module.  First, Chromium and non-Chromium DRTs have a different argument
131     # syntax.  Chromium uses --pixel-tests=<path>, and non-Chromium uses
132     # --pixel-tests as a boolean flag. Second, we don't want to have to list
133     # every command line flag DRT accepts, but optparse complains about
134     # unrecognized flags. At some point it might be good to share a common
135     # DRT options class between this file and webkit.py and chromium.py
136     # just to get better type checking.
137     platform_index = argv.index('--platform')
138     platform = argv[platform_index + 1]
139
140     pixel_tests = False
141     pixel_path = None
142     chromium = False
143     if platform.startswith('chromium'):
144         chromium = True
145         for arg in argv:
146             if arg.startswith('--pixel-tests'):
147                 pixel_tests = True
148                 pixel_path = arg[len('--pixel-tests='):]
149     else:
150         pixel_tests = '--pixel-tests' in argv
151     options = base.DummyOptions(chromium=chromium,
152                                 platform=platform,
153                                 pixel_tests=pixel_tests,
154                                 pixel_path=pixel_path)
155     return (options, [])
156
157
158 # FIXME: Should probably change this to use DriverInput after
159 # https://bugs.webkit.org/show_bug.cgi?id=53004 lands.
160 class _DRTInput(object):
161     def __init__(self, line):
162         vals = line.strip().split("'")
163         if len(vals) == 1:
164             self.uri = vals[0]
165             self.checksum = None
166         else:
167             self.uri = vals[0]
168             self.checksum = vals[1]
169
170
171 class MockDRT(object):
172     def __init__(self, options, args, filesystem, stdin, stdout, stderr):
173         self._options = options
174         self._args = args
175         self._filesystem = filesystem
176         self._stdout = stdout
177         self._stdin = stdin
178         self._stderr = stderr
179
180         port_name = None
181         if options.platform:
182             port_name = options.platform
183         self._port = factory.get(port_name, options=options, filesystem=filesystem)
184
185     def run(self):
186         while True:
187             line = self._stdin.readline()
188             if not line:
189                 break
190             self.run_one_test(self.parse_input(line))
191         return 0
192
193     def parse_input(self, line):
194         return _DRTInput(line)
195
196     def run_one_test(self, test_input):
197         port = self._port
198         if test_input.uri.startswith('http'):
199             test_name = port.uri_to_test_name(test_input.uri)
200             test_path = self._filesystem.join(port.layout_tests_dir(), test_name)
201         else:
202             test_path = test_input.uri
203
204         actual_text = port.expected_text(test_path)
205         if self._options.pixel_tests and test_input.checksum:
206             actual_checksum = port.expected_checksum(test_path)
207             actual_image = port.expected_image(test_path)
208
209         self._stdout.write('Content-Type: text/plain\n')
210
211         # FIXME: Note that we don't ensure there is a trailing newline!
212         # This mirrors actual (Mac) DRT behavior but is a bug.
213         self._stdout.write(actual_text)
214         self._stdout.write('#EOF\n')
215
216         if self._options.pixel_tests and test_input.checksum:
217             self._stdout.write('\n')
218             self._stdout.write('ActualHash: %s\n' % actual_checksum)
219             self._stdout.write('ExpectedHash: %s\n' % test_input.checksum)
220             if actual_checksum != test_input.checksum:
221                 self._stdout.write('Content-Type: image/png\n')
222                 self._stdout.write('Content-Length: %s\n\n' % len(actual_image))
223                 self._stdout.write(actual_image)
224         self._stdout.write('#EOF\n')
225         self._stdout.flush()
226         self._stderr.flush()
227
228
229 # FIXME: Should probably change this to use DriverInput after
230 # https://bugs.webkit.org/show_bug.cgi?id=53004 lands.
231 class _ChromiumDRTInput(_DRTInput):
232     def __init__(self, line):
233         vals = line.strip().split()
234         if len(vals) == 3:
235             self.uri, self.timeout, self.checksum = vals
236         else:
237             self.uri = vals[0]
238             self.timeout = vals[1]
239             self.checksum = None
240
241
242 class MockChromiumDRT(MockDRT):
243     def parse_input(self, line):
244         return _ChromiumDRTInput(line)
245
246     def run_one_test(self, test_input):
247         port = self._port
248         test_name = self._port.uri_to_test_name(test_input.uri)
249         test_path = self._filesystem.join(port.layout_tests_dir(), test_name)
250
251         actual_text = port.expected_text(test_path)
252         actual_image = ''
253         actual_checksum = ''
254         if self._options.pixel_tests and test_input.checksum:
255             actual_checksum = port.expected_checksum(test_path)
256             if actual_checksum != test_input.checksum:
257                 actual_image = port.expected_image(test_path)
258
259         self._stdout.write("#URL:%s\n" % test_input.uri)
260         if self._options.pixel_tests and test_input.checksum:
261             self._stdout.write("#MD5:%s\n" % actual_checksum)
262             self._filesystem.write_binary_file(self._options.pixel_path,
263                                                actual_image)
264         self._stdout.write(actual_text)
265
266         # FIXME: (See above FIXME as well). Chromium DRT appears to always
267         # ensure the text output has a trailing newline. Mac DRT does not.
268         if not actual_text.endswith('\n'):
269             self._stdout.write('\n')
270         self._stdout.write('#EOF\n')
271         self._stdout.flush()
272
273
274
275 if __name__ == '__main__':
276     fs = filesystem.FileSystem()
277     sys.exit(main(sys.argv[1:], fs, sys.stdin, sys.stdout, sys.stderr))