93125bec231b1d3b5ff55b2c4bcafb7303343a36
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / port / driver.py
1 # Copyright (C) 2011 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 import shlex
30
31 from webkitpy.common.system import path
32
33
34 class DriverInput(object):
35     def __init__(self, test_name, timeout, image_hash, is_reftest):
36         self.test_name = test_name
37         self.timeout = timeout  # in ms
38         self.image_hash = image_hash
39         self.is_reftest = is_reftest
40
41
42 class DriverOutput(object):
43     """Groups information about a output from driver for easy passing of data."""
44
45     def __init__(self, text, image, image_hash, audio, crash=False,
46             test_time=0, timeout=False, error='', crashed_process_name=None):
47         # FIXME: Args could be renamed to better clarify what they do.
48         self.text = text
49         self.image = image  # May be empty-string if the test crashes.
50         self.image_hash = image_hash
51         self.image_diff = None  # image_diff gets filled in after construction.
52         self.audio = audio  # Binary format is port-dependent.
53         self.crash = crash
54         self.crashed_process_name = crashed_process_name
55         self.test_time = test_time
56         self.timeout = timeout
57         self.error = error  # stderr output
58
59     def has_stderr(self):
60         return bool(self.error)
61
62
63 class Driver(object):
64     """Abstract interface for the DumpRenderTree interface."""
65
66     def __init__(self, port, worker_number, pixel_tests):
67         """Initialize a Driver to subsequently run tests.
68
69         Typically this routine will spawn DumpRenderTree in a config
70         ready for subsequent input.
71
72         port - reference back to the port object.
73         worker_number - identifier for a particular worker/driver instance
74         """
75         self._port = port
76         self._worker_number = worker_number
77         self._pixel_tests = pixel_tests
78
79     def run_test(self, driver_input):
80         """Run a single test and return the results.
81
82         Note that it is okay if a test times out or crashes and leaves
83         the driver in an indeterminate state. The upper layers of the program
84         are responsible for cleaning up and ensuring things are okay.
85
86         Returns a DriverOuput object.
87         """
88         raise NotImplementedError('Driver.run_test')
89
90     # FIXME: Seems this could just be inlined into callers.
91     def _command_wrapper(cls, wrapper_option):
92         # Hook for injecting valgrind or other runtime instrumentation,
93         # used by e.g. tools/valgrind/valgrind_tests.py.
94         return shlex.split(wrapper_option) if wrapper_option else []
95
96     HTTP_DIR = "http/tests/"
97     HTTP_LOCAL_DIR = "http/tests/local/"
98
99     def is_http_test(self, test_name):
100         return test_name.startswith(self.HTTP_DIR) and not test_name.startswith(self.HTTP_LOCAL_DIR)
101
102     def test_to_uri(self, test_name):
103         """Convert a test name to a URI."""
104         if not self.is_http_test(test_name):
105             return path.abspath_to_uri(self._port.abspath_for_test(test_name))
106
107         relative_path = test_name[len(self.HTTP_DIR):]
108
109         # TODO(dpranke): remove the SSL reference?
110         if relative_path.startswith("ssl/"):
111             return "https://127.0.0.1:8443/" + relative_path
112         return "http://127.0.0.1:8000/" + relative_path
113
114     def uri_to_test(self, uri):
115         """Return the base layout test name for a given URI.
116
117         This returns the test name for a given URI, e.g., if you passed in
118         "file:///src/LayoutTests/fast/html/keygen.html" it would return
119         "fast/html/keygen.html".
120
121         """
122         if uri.startswith("file:///"):
123             return uri[len(path.abspath_to_uri(self._port.layout_tests_dir()) + "/"):]
124         if uri.startswith("http://"):
125             return uri.replace('http://127.0.0.1:8000/', self.HTTP_DIR)
126         if uri.startswith("https://"):
127             return uri.replace('https://127.0.0.1:8443/', self.HTTP_DIR)
128         raise NotImplementedError('unknown url type: %s' % uri)
129
130     def has_crashed(self):
131         return False
132
133     def stop(self):
134         raise NotImplementedError('Driver.stop')
135
136     def cmd_line(self):
137         raise NotImplementedError('Driver.cmd_line')
138
139
140 class DriverProxy(object):
141     """A wrapper for managing two Driver instances, one with pixel tests and
142     one without. This allows us to handle plain text tests and ref tests with a
143     single driver."""
144
145     def __init__(self, port, worker_number, driver_instance_constructor, pixel_tests):
146         self._driver = driver_instance_constructor(port, worker_number, pixel_tests)
147         if pixel_tests:
148             self._reftest_driver = self._driver
149         else:
150             self._reftest_driver = driver_instance_constructor(port, worker_number, pixel_tests=True)
151
152     def is_http_test(self, test_name):
153         return self._driver.is_http_test(test_name)
154
155     def test_to_uri(self, test_name):
156         return self._driver.test_to_uri(test_name)
157
158     def uri_to_test(self, uri):
159         return self._driver.uri_to_test(uri)
160
161     def run_test(self, driver_input):
162         if driver_input.is_reftest:
163             return self._reftest_driver.run_test(driver_input)
164         return self._driver.run_test(driver_input)
165
166     def has_crashed(self):
167         return self._driver.has_crashed() or self._reftest_driver.has_crashed()
168
169     def stop(self):
170         self._driver.stop()
171         self._reftest_driver.stop()
172
173     def cmd_line(self):
174         cmd_line = self._driver.cmd_line()
175         if self._driver != self._reftest_driver:
176             cmd_line += ['; '] + self._reftest_driver.cmd_line()
177         return cmd_line