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 29import optparse 30import sys 31import tempfile 32import unittest 33 34from webkitpy.common.system.executive import Executive, ScriptError 35from webkitpy.common.system import executive_mock 36from webkitpy.common.system.filesystem_mock import MockFileSystem 37from webkitpy.common.system import outputcapture 38from webkitpy.common.system.path import abspath_to_uri 39from webkitpy.thirdparty.mock import Mock 40from webkitpy.tool import mocktool 41 42import base 43import config 44import config_mock 45 46 47class 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 263class 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 305class 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 321if __name__ == '__main__': 322 unittest.main() 323