Rename WebKitTools to Tools
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / port / base_unittest.py
1 # Copyright (C) 2010 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 name of Google Inc. 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 optparse
30 import os
31 import sys
32 import tempfile
33 import unittest
34
35 from webkitpy.common.system.executive import Executive, ScriptError
36 from webkitpy.common.system import executive_mock
37 from webkitpy.common.system import filesystem
38 from webkitpy.common.system import outputcapture
39 from webkitpy.common.system.path import abspath_to_uri
40 from webkitpy.thirdparty.mock import Mock
41 from webkitpy.tool import mocktool
42
43 import base
44 import config
45 import config_mock
46
47
48 class PortTest(unittest.TestCase):
49     def test_format_wdiff_output_as_html(self):
50         output = "OUTPUT %s %s %s" % (base.Port._WDIFF_DEL, base.Port._WDIFF_ADD, base.Port._WDIFF_END)
51         html = base.Port()._format_wdiff_output_as_html(output)
52         expected_html = "<head><style>.del { background: #faa; } .add { background: #afa; }</style></head><pre>OUTPUT <span class=del> <span class=add> </span></pre>"
53         self.assertEqual(html, expected_html)
54
55     def test_wdiff_command(self):
56         port = base.Port()
57         port._path_to_wdiff = lambda: "/path/to/wdiff"
58         command = port._wdiff_command("/actual/path", "/expected/path")
59         expected_command = [
60             "/path/to/wdiff",
61             "--start-delete=##WDIFF_DEL##",
62             "--end-delete=##WDIFF_END##",
63             "--start-insert=##WDIFF_ADD##",
64             "--end-insert=##WDIFF_END##",
65             "/actual/path",
66             "/expected/path",
67         ]
68         self.assertEqual(command, expected_command)
69
70     def _file_with_contents(self, contents, encoding="utf-8"):
71         new_file = tempfile.NamedTemporaryFile()
72         new_file.write(contents.encode(encoding))
73         new_file.flush()
74         return new_file
75
76     def test_pretty_patch_os_error(self):
77         port = base.Port(executive=executive_mock.MockExecutive2(exception=OSError))
78         oc = outputcapture.OutputCapture()
79         oc.capture_output()
80         self.assertEqual(port.pretty_patch_text("patch.txt"),
81                          port._pretty_patch_error_html)
82
83         # This tests repeated calls to make sure we cache the result.
84         self.assertEqual(port.pretty_patch_text("patch.txt"),
85                          port._pretty_patch_error_html)
86         oc.restore_output()
87
88     def test_pretty_patch_script_error(self):
89         # FIXME: This is some ugly white-box test hacking ...
90         base._pretty_patch_available = True
91         port = base.Port(executive=executive_mock.MockExecutive2(exception=ScriptError))
92         self.assertEqual(port.pretty_patch_text("patch.txt"),
93                          port._pretty_patch_error_html)
94
95         # This tests repeated calls to make sure we cache the result.
96         self.assertEqual(port.pretty_patch_text("patch.txt"),
97                          port._pretty_patch_error_html)
98
99     def test_run_wdiff(self):
100         executive = Executive()
101         # This may fail on some systems.  We could ask the port
102         # object for the wdiff path, but since we don't know what
103         # port object to use, this is sufficient for now.
104         try:
105             wdiff_path = executive.run_command(["which", "wdiff"]).rstrip()
106         except Exception, e:
107             wdiff_path = None
108
109         port = base.Port()
110         port._path_to_wdiff = lambda: wdiff_path
111
112         if wdiff_path:
113             # "with tempfile.NamedTemporaryFile() as actual" does not seem to work in Python 2.5
114             actual = self._file_with_contents(u"foo")
115             expected = self._file_with_contents(u"bar")
116             wdiff = port._run_wdiff(actual.name, expected.name)
117             expected_wdiff = "<head><style>.del { background: #faa; } .add { background: #afa; }</style></head><pre><span class=del>foo</span><span class=add>bar</span></pre>"
118             self.assertEqual(wdiff, expected_wdiff)
119             # Running the full wdiff_text method should give the same result.
120             port._wdiff_available = True  # In case it's somehow already disabled.
121             wdiff = port.wdiff_text(actual.name, expected.name)
122             self.assertEqual(wdiff, expected_wdiff)
123             # wdiff should still be available after running wdiff_text with a valid diff.
124             self.assertTrue(port._wdiff_available)
125             actual.close()
126             expected.close()
127
128             # Bogus paths should raise a script error.
129             self.assertRaises(ScriptError, port._run_wdiff, "/does/not/exist", "/does/not/exist2")
130             self.assertRaises(ScriptError, port.wdiff_text, "/does/not/exist", "/does/not/exist2")
131             # wdiff will still be available after running wdiff_text with invalid paths.
132             self.assertTrue(port._wdiff_available)
133             base._wdiff_available = True
134
135         # If wdiff does not exist _run_wdiff should throw an OSError.
136         port._path_to_wdiff = lambda: "/invalid/path/to/wdiff"
137         self.assertRaises(OSError, port._run_wdiff, "foo", "bar")
138
139         # wdiff_text should not throw an error if wdiff does not exist.
140         self.assertEqual(port.wdiff_text("foo", "bar"), "")
141         # However wdiff should not be available after running wdiff_text if wdiff is missing.
142         self.assertFalse(port._wdiff_available)
143
144     def test_diff_text(self):
145         port = base.Port()
146         # Make sure that we don't run into decoding exceptions when the
147         # filenames are unicode, with regular or malformed input (expected or
148         # actual input is always raw bytes, not unicode).
149         port.diff_text('exp', 'act', 'exp.txt', 'act.txt')
150         port.diff_text('exp', 'act', u'exp.txt', 'act.txt')
151         port.diff_text('exp', 'act', u'a\xac\u1234\u20ac\U00008000', 'act.txt')
152
153         port.diff_text('exp' + chr(255), 'act', 'exp.txt', 'act.txt')
154         port.diff_text('exp' + chr(255), 'act', u'exp.txt', 'act.txt')
155
156         # Though expected and actual files should always be read in with no
157         # encoding (and be stored as str objects), test unicode inputs just to
158         # be safe.
159         port.diff_text(u'exp', 'act', 'exp.txt', 'act.txt')
160         port.diff_text(
161             u'a\xac\u1234\u20ac\U00008000', 'act', 'exp.txt', 'act.txt')
162
163         # And make sure we actually get diff output.
164         diff = port.diff_text('foo', 'bar', 'exp.txt', 'act.txt')
165         self.assertTrue('foo' in diff)
166         self.assertTrue('bar' in diff)
167         self.assertTrue('exp.txt' in diff)
168         self.assertTrue('act.txt' in diff)
169         self.assertFalse('nosuchthing' in diff)
170
171     def test_default_configuration_notfound(self):
172         # Test that we delegate to the config object properly.
173         port = base.Port(config=config_mock.MockConfig(default_configuration='default'))
174         self.assertEqual(port.default_configuration(), 'default')
175
176     def test_layout_tests_skipping(self):
177         port = base.Port()
178         port.skipped_layout_tests = lambda: ['foo/bar.html', 'media']
179         self.assertTrue(port.skips_layout_test('foo/bar.html'))
180         self.assertTrue(port.skips_layout_test('media/video-zoom.html'))
181         self.assertFalse(port.skips_layout_test('foo/foo.html'))
182
183     def test_setup_test_run(self):
184         port = base.Port()
185         # This routine is a no-op. We just test it for coverage.
186         port.setup_test_run()
187
188     def test_test_dirs(self):
189         port = base.Port()
190         dirs = port.test_dirs()
191         self.assertTrue('canvas' in dirs)
192         self.assertTrue('css2.1' in dirs)
193
194     def test_filename_to_uri(self):
195         port = base.Port()
196         layout_test_dir = port.layout_tests_dir()
197         test_file = os.path.join(layout_test_dir, "foo", "bar.html")
198
199         # On Windows, absolute paths are of the form "c:\foo.txt". However,
200         # all current browsers (except for Opera) normalize file URLs by
201         # prepending an additional "/" as if the absolute path was
202         # "/c:/foo.txt". This means that all file URLs end up with "file:///"
203         # at the beginning.
204         if sys.platform == 'win32':
205             prefix = "file:///"
206             path = test_file.replace("\\", "/")
207         else:
208             prefix = "file://"
209             path = test_file
210
211         self.assertEqual(port.filename_to_uri(test_file),
212                          abspath_to_uri(test_file))
213
214     def test_get_option__set(self):
215         options, args = optparse.OptionParser().parse_args([])
216         options.foo = 'bar'
217         port = base.Port(options=options)
218         self.assertEqual(port.get_option('foo'), 'bar')
219
220     def test_get_option__unset(self):
221         port = base.Port()
222         self.assertEqual(port.get_option('foo'), None)
223
224     def test_get_option__default(self):
225         port = base.Port()
226         self.assertEqual(port.get_option('foo', 'bar'), 'bar')
227
228     def test_set_option_default__unset(self):
229         port = base.Port()
230         port.set_option_default('foo', 'bar')
231         self.assertEqual(port.get_option('foo'), 'bar')
232
233     def test_set_option_default__set(self):
234         options, args = optparse.OptionParser().parse_args([])
235         options.foo = 'bar'
236         port = base.Port(options=options)
237         # This call should have no effect.
238         port.set_option_default('foo', 'new_bar')
239         self.assertEqual(port.get_option('foo'), 'bar')
240
241     def test_name__unset(self):
242         port = base.Port()
243         self.assertEqual(port.name(), None)
244
245     def test_name__set(self):
246         port = base.Port(port_name='foo')
247         self.assertEqual(port.name(), 'foo')
248
249
250 class VirtualTest(unittest.TestCase):
251     """Tests that various methods expected to be virtual are."""
252     def assertVirtual(self, method, *args, **kwargs):
253         self.assertRaises(NotImplementedError, method, *args, **kwargs)
254
255     def test_virtual_methods(self):
256         port = base.Port()
257         self.assertVirtual(port.baseline_path)
258         self.assertVirtual(port.baseline_search_path)
259         self.assertVirtual(port.check_build, None)
260         self.assertVirtual(port.check_image_diff)
261         self.assertVirtual(port.create_driver, 0)
262         self.assertVirtual(port.diff_image, None, None)
263         self.assertVirtual(port.path_to_test_expectations_file)
264         self.assertVirtual(port.test_platform_name)
265         self.assertVirtual(port.results_directory)
266         self.assertVirtual(port.test_expectations)
267         self.assertVirtual(port.test_base_platform_names)
268         self.assertVirtual(port.test_platform_name)
269         self.assertVirtual(port.test_platforms)
270         self.assertVirtual(port.test_platform_name_to_name, None)
271         self.assertVirtual(port.version)
272         self.assertVirtual(port._path_to_apache)
273         self.assertVirtual(port._path_to_apache_config_file)
274         self.assertVirtual(port._path_to_driver)
275         self.assertVirtual(port._path_to_helper)
276         self.assertVirtual(port._path_to_image_diff)
277         self.assertVirtual(port._path_to_lighttpd)
278         self.assertVirtual(port._path_to_lighttpd_modules)
279         self.assertVirtual(port._path_to_lighttpd_php)
280         self.assertVirtual(port._path_to_wdiff)
281         self.assertVirtual(port._shut_down_http_server, None)
282
283     def test_virtual_driver_method(self):
284         self.assertRaises(NotImplementedError, base.Driver, base.Port(),
285                           0)
286
287     def test_virtual_driver_methods(self):
288         class VirtualDriver(base.Driver):
289             def __init__(self):
290                 pass
291
292         driver = VirtualDriver()
293         self.assertVirtual(driver.run_test, None)
294         self.assertVirtual(driver.poll)
295         self.assertVirtual(driver.stop)
296
297
298 class DriverTest(unittest.TestCase):
299
300     def _assert_wrapper(self, wrapper_string, expected_wrapper):
301         wrapper = base.Driver._command_wrapper(wrapper_string)
302         self.assertEqual(wrapper, expected_wrapper)
303
304     def test_command_wrapper(self):
305         self._assert_wrapper(None, [])
306         self._assert_wrapper("valgrind", ["valgrind"])
307
308         # Validate that shlex works as expected.
309         command_with_spaces = "valgrind --smc-check=\"check with spaces!\" --foo"
310         expected_parse = ["valgrind", "--smc-check=check with spaces!", "--foo"]
311         self._assert_wrapper(command_with_spaces, expected_parse)
312
313
314 if __name__ == '__main__':
315     unittest.main()