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