diff options
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/layout_tests/port')
20 files changed, 705 insertions, 214 deletions
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py index 6a5d43b..cd7d663 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py @@ -42,33 +42,35 @@ import sys import time import apache_http_server -import test_files +import http_lock import http_server +import test_files import websocket_server from webkitpy.common.system import logutils from webkitpy.common.system.executive import Executive, ScriptError +from webkitpy.common.system.path import abspath_to_uri from webkitpy.common.system.user import User _log = logutils.get_logger(__file__) -# Python's Popen has a bug that causes any pipes opened to a -# process that can't be executed to be leaked. Since this -# code is specifically designed to tolerate exec failures -# to gracefully handle cases where wdiff is not installed, -# the bug results in a massive file descriptor leak. As a -# workaround, if an exec failure is ever experienced for -# wdiff, assume it's not available. This will leak one -# file descriptor but that's better than leaking each time -# wdiff would be run. -# -# http://mail.python.org/pipermail/python-list/ -# 2008-August/505753.html -# http://bugs.python.org/issue3210 -_wdiff_available = True -_pretty_patch_available = True +class DummyOptions(object): + """Fake implementation of optparse.Values. Cloned from + webkitpy.tool.mocktool.MockOptions. + + """ + + def __init__(self, **kwargs): + # The caller can set option values using keyword arguments. We don't + # set any values by default because we don't know how this + # object will be used. Generally speaking unit tests should + # subclass this or provider wrapper functions that set a common + # set of options. + for key, value in kwargs.items(): + self.__dict__[key] = value + # FIXME: This class should merge with webkitpy.webkit_port at some point. class Port(object): @@ -85,13 +87,41 @@ class Port(object): def __init__(self, **kwargs): self._name = kwargs.get('port_name', None) - self._options = kwargs.get('options', None) + self._options = kwargs.get('options') + if self._options is None: + # FIXME: Ideally we'd have a package-wide way to get a + # well-formed options object that had all of the necessary + # options defined on it. + self._options = DummyOptions() self._executive = kwargs.get('executive', Executive()) self._user = kwargs.get('user', User()) self._helper = None self._http_server = None self._webkit_base_dir = None self._websocket_server = None + self._http_lock = None + + # Python's Popen has a bug that causes any pipes opened to a + # process that can't be executed to be leaked. Since this + # code is specifically designed to tolerate exec failures + # to gracefully handle cases where wdiff is not installed, + # the bug results in a massive file descriptor leak. As a + # workaround, if an exec failure is ever experienced for + # wdiff, assume it's not available. This will leak one + # file descriptor but that's better than leaking each time + # wdiff would be run. + # + # http://mail.python.org/pipermail/python-list/ + # 2008-August/505753.html + # http://bugs.python.org/issue3210 + self._wdiff_available = True + + self._pretty_patch_path = self.path_from_webkit_base("BugsSite", + "PrettyPatch", "prettify.rb") + self._pretty_patch_available = True + self.set_option_default('configuration', None) + if self._options.configuration is None: + self._options.configuration = self.default_configuration() def default_child_processes(self): """Return the number of DumpRenderTree instances to use for this @@ -125,6 +155,27 @@ class Port(object): """This routine is used to check whether image_diff binary exists.""" raise NotImplementedError('Port.check_image_diff') + def check_pretty_patch(self): + """Checks whether we can use the PrettyPatch ruby script.""" + + # check if Ruby is installed + try: + result = self._executive.run_command(['ruby', '--version']) + except OSError, e: + if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]: + _log.error("Ruby is not installed; " + "can't generate pretty patches.") + _log.error('') + return False + + if not self.path_exists(self._pretty_patch_path): + _log.error('Unable to find %s .' % self._pretty_patch_path) + _log.error("Can't generate pretty patches.") + _log.error('') + return False + + return True + def compare_text(self, expected_text, actual_text): """Return whether or not the two strings are *not* equal. This routine is used to diff text output. @@ -259,7 +310,10 @@ class Port(object): path = self.expected_filename(test, extension) if not os.path.exists(path): return None - with codecs.open(path, 'r', encoding) as file: + open_mode = 'r' + if encoding is None: + open_mode = 'r+b' + with codecs.open(path, open_mode, encoding) as file: return file.read() def expected_checksum(self, test): @@ -281,22 +335,18 @@ class Port(object): return text.strip("\r\n").replace("\r\n", "\n") + "\n" def filename_to_uri(self, filename): - """Convert a test file to a URI.""" + """Convert a test file (which is an absolute path) to a URI.""" LAYOUTTEST_HTTP_DIR = "http/tests/" - LAYOUTTEST_WEBSOCKET_DIR = "websocket/tests/" + LAYOUTTEST_WEBSOCKET_DIR = "http/tests/websocket/tests/" relative_path = self.relative_test_filename(filename) port = None use_ssl = False - if relative_path.startswith(LAYOUTTEST_HTTP_DIR): - # http/tests/ run off port 8000 and ssl/ off 8443 + if (relative_path.startswith(LAYOUTTEST_WEBSOCKET_DIR) + or relative_path.startswith(LAYOUTTEST_HTTP_DIR)): relative_path = relative_path[len(LAYOUTTEST_HTTP_DIR):] port = 8000 - elif relative_path.startswith(LAYOUTTEST_WEBSOCKET_DIR): - # websocket/tests/ run off port 8880 and 9323 - # Note: the root is /, not websocket/tests/ - port = 8880 # Make http/tests/local run as local files. This is to mimic the # logic in run-webkit-tests. @@ -311,9 +361,7 @@ class Port(object): protocol = "http" return "%s://127.0.0.1:%u/%s" % (protocol, port, relative_path) - if sys.platform in ('cygwin', 'win32'): - return "file:///" + self.get_absolute_path(filename) - return "file://" + self.get_absolute_path(filename) + return abspath_to_uri(os.path.abspath(filename)) def tests(self, paths): """Return the list of tests found (relative to layout_tests_dir().""" @@ -349,7 +397,10 @@ class Port(object): data: contents of the baseline. encoding: file encoding to use for the baseline. """ - with codecs.open(path, "w", encoding=encoding) as file: + write_mode = "w" + if encoding is None: + write_mode = "wb" + with codecs.open(path, write_mode, encoding=encoding) as file: file.write(data) def uri_to_test_name(self, uri): @@ -362,12 +413,8 @@ class Port(object): """ test = uri if uri.startswith("file:///"): - if sys.platform == 'win32': - test = test.replace('file:///', '') - test = test.replace('/', '\\') - else: - test = test.replace('file://', '') - return self.relative_test_filename(test) + prefix = abspath_to_uri(self.layout_tests_dir()) + "/" + return test[len(prefix):] if uri.startswith("http://127.0.0.1:8880/"): # websocket tests @@ -382,13 +429,6 @@ class Port(object): raise NotImplementedError('unknown url type: %s' % uri) - def get_absolute_path(self, filename): - """Return the absolute path in unix format for the given filename. - - This routine exists so that platforms that don't use unix filenames - can convert accordingly.""" - return os.path.abspath(filename) - def layout_tests_dir(self): """Return the absolute path to the top of the LayoutTests directory.""" return self.path_from_webkit_base('LayoutTests') @@ -420,6 +460,18 @@ class Port(object): may be different (e.g., 'win-xp' instead of 'chromium-win-xp'.""" return self._name + def get_option(self, name, default_value=None): + # FIXME: Eventually we should not have to do a test for + # hasattr(), and we should be able to just do + # self.options.value. See additional FIXME in the constructor. + if hasattr(self._options, name): + return getattr(self._options, name) + return default_value + + def set_option_default(self, name, default_value): + if not hasattr(self._options, name): + return setattr(self._options, name, default_value) + # FIXME: This could be replaced by functions in webkitpy.common.checkout.scm. def path_from_webkit_base(self, *comps): """Returns the full path to path made by joining the top of the @@ -445,7 +497,7 @@ class Port(object): """Relative unix-style path for a filename under the LayoutTests directory. Filenames outside the LayoutTests directory should raise an error.""" - assert(filename.startswith(self.layout_tests_dir())) + #assert(filename.startswith(self.layout_tests_dir())) return filename[len(self.layout_tests_dir()) + 1:] def results_directory(self): @@ -484,12 +536,12 @@ class Port(object): """Start a web server if it is available. Do nothing if it isn't. This routine is allowed to (and may) fail if a server is already running.""" - if self._options.use_apache: + if self.get_option('use_apache'): self._http_server = apache_http_server.LayoutTestApacheHttpd(self, - self._options.results_directory) + self.get_option('results_directory')) else: self._http_server = http_server.Lighttpd(self, - self._options.results_directory) + self.get_option('results_directory')) self._http_server.start() def start_websocket_server(self): @@ -497,9 +549,13 @@ class Port(object): it isn't. This routine is allowed to (and may) fail if a server is already running.""" self._websocket_server = websocket_server.PyWebSocket(self, - self._options.results_directory) + self.get_option('results_directory')) self._websocket_server.start() + def acquire_http_lock(self): + self._http_lock = http_lock.HttpLock(None) + self._http_lock.wait_for_httpd_lock() + def stop_helper(self): """Shut down the test helper if it is running. Do nothing if it isn't, or it isn't available. If a port overrides start_helper() @@ -518,6 +574,10 @@ class Port(object): if self._websocket_server: self._websocket_server.stop() + def release_http_lock(self): + if self._http_lock: + self._http_lock.cleanup_http_lock() + def test_expectations(self): """Returns the test expectations for this port. @@ -628,8 +688,7 @@ class Port(object): """Returns a string of HTML indicating the word-level diff of the contents of the two filenames. Returns an empty string if word-level diffing isn't available.""" - global _wdiff_available # See explaination at top of file. - if not _wdiff_available: + if not self._wdiff_available: return "" try: # It's possible to raise a ScriptError we pass wdiff invalid paths. @@ -637,33 +696,33 @@ class Port(object): except OSError, e: if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]: # Silently ignore cases where wdiff is missing. - _wdiff_available = False + self._wdiff_available = False return "" raise - _pretty_patch_error_html = "Failed to run PrettyPatch, see error console." + # This is a class variable so we can test error output easily. + _pretty_patch_error_html = "Failed to run PrettyPatch, see error log." def pretty_patch_text(self, diff_path): - # FIXME: Much of this function could move to prettypatch.rb - global _pretty_patch_available - if not _pretty_patch_available: + if not self._pretty_patch_available: return self._pretty_patch_error_html - pretty_patch_path = self.path_from_webkit_base("BugsSite", "PrettyPatch") - prettify_path = os.path.join(pretty_patch_path, "prettify.rb") - command = ["ruby", "-I", pretty_patch_path, prettify_path, diff_path] + command = ("ruby", "-I", os.path.dirname(self._pretty_patch_path), + self._pretty_patch_path, diff_path) try: # Diffs are treated as binary (we pass decode_output=False) as they # may contain multiple files of conflicting encodings. return self._executive.run_command(command, decode_output=False) except OSError, e: # If the system is missing ruby log the error and stop trying. - _pretty_patch_available = False + self._pretty_patch_available = False _log.error("Failed to run PrettyPatch (%s): %s" % (command, e)) return self._pretty_patch_error_html except ScriptError, e: - # If ruby failed to run for some reason, log the command output and stop trying. - _pretty_patch_available = False - _log.error("Failed to run PrettyPatch (%s):\n%s" % (command, e.message_with_output())) + # If ruby failed to run for some reason, log the command + # output and stop trying. + self._pretty_patch_available = False + _log.error("Failed to run PrettyPatch (%s):\n%s" % (command, + e.message_with_output())) return self._pretty_patch_error_html def _webkit_build_directory(self, args): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py index 71877b3..93f8808 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py @@ -26,16 +26,19 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import base +import optparse import os import StringIO import sys import tempfile import unittest +from webkitpy.common.system.path import abspath_to_uri from webkitpy.common.system.executive import Executive, ScriptError from webkitpy.thirdparty.mock import Mock +from webkitpy.tool import mocktool +import base # FIXME: This makes StringIO objects work with "with". Remove # when we upgrade to 2.6. @@ -139,11 +142,11 @@ class PortTest(unittest.TestCase): expected_wdiff = "<head><style>.del { background: #faa; } .add { background: #afa; }</style></head><pre><span class=del>foo</span><span class=add>bar</span></pre>" self.assertEqual(wdiff, expected_wdiff) # Running the full wdiff_text method should give the same result. - base._wdiff_available = True # In case it's somehow already disabled. + port._wdiff_available = True # In case it's somehow already disabled. wdiff = port.wdiff_text(actual.name, expected.name) self.assertEqual(wdiff, expected_wdiff) # wdiff should still be available after running wdiff_text with a valid diff. - self.assertTrue(base._wdiff_available) + self.assertTrue(port._wdiff_available) actual.close() expected.close() @@ -151,7 +154,7 @@ class PortTest(unittest.TestCase): self.assertRaises(ScriptError, port._run_wdiff, "/does/not/exist", "/does/not/exist2") self.assertRaises(ScriptError, port.wdiff_text, "/does/not/exist", "/does/not/exist2") # wdiff will still be available after running wdiff_text with invalid paths. - self.assertTrue(base._wdiff_available) + self.assertTrue(port._wdiff_available) base._wdiff_available = True # If wdiff does not exist _run_wdiff should throw an OSError. @@ -161,8 +164,7 @@ class PortTest(unittest.TestCase): # wdiff_text should not throw an error if wdiff does not exist. self.assertEqual(port.wdiff_text("foo", "bar"), "") # However wdiff should not be available after running wdiff_text if wdiff is missing. - self.assertFalse(base._wdiff_available) - base._wdiff_available = True + self.assertFalse(port._wdiff_available) def test_diff_text(self): port = base.Port() @@ -226,6 +228,63 @@ class PortTest(unittest.TestCase): self.assertTrue('canvas' in dirs) self.assertTrue('css2.1' in dirs) + def test_filename_to_uri(self): + + port = base.Port() + layout_test_dir = port.layout_tests_dir() + test_file = os.path.join(layout_test_dir, "foo", "bar.html") + + # On Windows, absolute paths are of the form "c:\foo.txt". However, + # all current browsers (except for Opera) normalize file URLs by + # prepending an additional "/" as if the absolute path was + # "/c:/foo.txt". This means that all file URLs end up with "file:///" + # at the beginning. + if sys.platform == 'win32': + prefix = "file:///" + path = test_file.replace("\\", "/") + else: + prefix = "file://" + path = test_file + + self.assertEqual(port.filename_to_uri(test_file), + abspath_to_uri(test_file)) + + def test_get_option__set(self): + options, args = optparse.OptionParser().parse_args() + options.foo = 'bar' + port = base.Port(options=options) + self.assertEqual(port.get_option('foo'), 'bar') + + def test_get_option__unset(self): + port = base.Port() + self.assertEqual(port.get_option('foo'), None) + + def test_get_option__default(self): + port = base.Port() + self.assertEqual(port.get_option('foo', 'bar'), 'bar') + + def test_set_option_default__unset(self): + port = base.Port() + port.set_option_default('foo', 'bar') + self.assertEqual(port.get_option('foo'), 'bar') + + def test_set_option_default__set(self): + options, args = optparse.OptionParser().parse_args() + options.foo = 'bar' + port = base.Port(options=options) + # This call should have no effect. + port.set_option_default('foo', 'new_bar') + self.assertEqual(port.get_option('foo'), 'bar') + + def test_name__unset(self): + port = base.Port() + self.assertEqual(port.name(), None) + + def test_name__set(self): + port = base.Port(port_name='foo') + self.assertEqual(port.name(), 'foo') + + class VirtualTest(unittest.TestCase): """Tests that various methods expected to be virtual are.""" def assertVirtual(self, method, *args, **kwargs): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py index a72627a..4d17b51 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py @@ -43,6 +43,10 @@ import tempfile import time import webbrowser +from webkitpy.common.system.executive import Executive +from webkitpy.common.system.path import cygpath +from webkitpy.layout_tests.layout_package import test_expectations + import base import http_server @@ -84,11 +88,6 @@ class ChromiumPort(base.Port): def __init__(self, **kwargs): base.Port.__init__(self, **kwargs) - if 'options' in kwargs: - options = kwargs['options'] - if (options and (not hasattr(options, 'configuration') or - options.configuration is None)): - options.configuration = self.default_configuration() self._chromium_base_dir = None def baseline_path(self): @@ -100,9 +99,9 @@ class ChromiumPort(base.Port): dump_render_tree_binary_path = self._path_to_driver() result = check_file_exists(dump_render_tree_binary_path, 'test driver') and result - if result and self._options.build: + if result and self.get_option('build'): result = self._check_driver_build_up_to_date( - self._options.configuration) + self.get_option('configuration')) else: _log.error('') @@ -111,10 +110,14 @@ class ChromiumPort(base.Port): result = check_file_exists(helper_path, 'layout test helper') and result - if self._options.pixel_tests: + if self.get_option('pixel_tests'): result = self.check_image_diff( 'To override, invoke with --no-pixel-tests') and result + # It's okay if pretty patch isn't available, but we will at + # least log a message. + self.check_pretty_patch() + return result def check_sys_deps(self, needs_http): @@ -134,28 +137,43 @@ class ChromiumPort(base.Port): def diff_image(self, expected_contents, actual_contents, diff_filename=None, tolerance=0): executable = self._path_to_image_diff() - expected_tmpfile = tempfile.NamedTemporaryFile() - expected_tmpfile.write(expected_contents) - actual_tmpfile = tempfile.NamedTemporaryFile() - actual_tmpfile.write(actual_contents) + + tempdir = tempfile.mkdtemp() + expected_filename = os.path.join(tempdir, "expected.png") + with open(expected_filename, 'w+b') as file: + file.write(expected_contents) + actual_filename = os.path.join(tempdir, "actual.png") + with open(actual_filename, 'w+b') as file: + file.write(actual_contents) + if diff_filename: - cmd = [executable, '--diff', expected_tmpfile.name, - actual_tmpfile.name, diff_filename] + cmd = [executable, '--diff', expected_filename, + actual_filename, diff_filename] else: - cmd = [executable, expected_tmpfile.name, actual_tmpfile.name] + cmd = [executable, expected_filename, actual_filename] result = True try: - if self._executive.run_command(cmd, return_exit_code=True) == 0: - return False + exit_code = self._executive.run_command(cmd, return_exit_code=True) + if exit_code == 0: + # The images are the same. + result = False + elif exit_code != 1: + _log.error("image diff returned an exit code of " + + str(exit_code)) + # Returning False here causes the script to think that we + # successfully created the diff even though we didn't. If + # we return True, we think that the images match but the hashes + # don't match. + # FIXME: Figure out why image_diff returns other values. + result = False except OSError, e: if e.errno == errno.ENOENT or e.errno == errno.EACCES: _compare_available = False else: raise e finally: - expected_tmpfile.close() - actual_tmpfile.close() + shutil.rmtree(tempdir, ignore_errors=True) return result def driver_name(self): @@ -182,10 +200,11 @@ class ChromiumPort(base.Port): def results_directory(self): try: return self.path_from_chromium_base('webkit', - self._options.configuration, self._options.results_directory) + self.get_option('configuration'), + self.get_option('results_directory')) except AssertionError: - return self._build_path(self._options.configuration, - self._options.results_directory) + return self._build_path(self.get_option('configuration'), + self.get_option('results_directory')) def setup_test_run(self): # Delete the disk cache if any to ensure a clean test run. @@ -239,7 +258,7 @@ class ChromiumPort(base.Port): # FIXME: This drt_overrides handling should be removed when we switch # from tes_shell to DRT. drt_overrides = '' - if self._options and self._options.use_drt: + if self.get_option('use_drt'): drt_overrides_path = self.path_from_webkit_base('LayoutTests', 'platform', 'chromium', 'drt_expectations.txt') if os.path.exists(drt_overrides_path): @@ -325,11 +344,18 @@ class ChromiumPort(base.Port): platform = self.name() return self.path_from_webkit_base('LayoutTests', 'platform', platform) + def _convert_path(self, path): + """Handles filename conversion for subprocess command line args.""" + # See note above in diff_image() for why we need this. + if sys.platform == 'cygwin': + return cygpath(path) + return path + def _path_to_image_diff(self): binary_name = 'image_diff' - if self._options.use_drt: + if self.get_option('use_drt'): binary_name = 'ImageDiff' - return self._build_path(self._options.configuration, binary_name) + return self._build_path(self.get_option('configuration'), binary_name) class ChromiumDriver(base.Driver): @@ -344,29 +370,31 @@ class ChromiumDriver(base.Driver): def _driver_args(self): driver_args = [] if self._image_path: - driver_args.append("--pixel-tests=" + self._image_path) + # See note above in diff_image() for why we need _convert_path(). + driver_args.append("--pixel-tests=" + + self._port._convert_path(self._image_path)) - if self._options.use_drt: + if self._port.get_option('use_drt'): driver_args.append('--test-shell') else: driver_args.append('--layout-tests') - if self._options.startup_dialog: + if self._port.get_option('startup_dialog'): driver_args.append('--testshell-startup-dialog') - if self._options.gp_fault_error_box: + if self._port.get_option('gp_fault_error_box'): driver_args.append('--gp-fault-error-box') - if self._options.accelerated_compositing: + if self._port.get_option('accelerated_compositing'): driver_args.append('--enable-accelerated-compositing') - if self._options.accelerated_2d_canvas: + if self._port.get_option('accelerated_2d_canvas'): driver_args.append('--enable-accelerated-2d-canvas') return driver_args def start(self): # FIXME: Should be an error to call this method twice. - cmd = self._command_wrapper(self._options.wrapper) + cmd = self._command_wrapper(self._port.get_option('wrapper')) cmd.append(self._port._path_to_driver()) cmd += self._driver_args() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py index 80602d9..95c716e 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu.py @@ -66,7 +66,7 @@ def get(**kwargs): def _set_gpu_options(options): if options: if options.accelerated_compositing is None: - options.accelerated_composting = True + options.accelerated_compositing = True if options.accelerated_2d_canvas is None: options.accelerated_2d_canvas = True @@ -90,7 +90,8 @@ class ChromiumGpuLinuxPort(chromium_linux.ChromiumLinuxPort): chromium_linux.ChromiumLinuxPort.__init__(self, **kwargs) def baseline_search_path(self): - return ([self._webkit_baseline_path('chromium-gpu-linux')] + + # Mimic the Linux -> Win expectations fallback in the ordinary Chromium port. + return (map(self._webkit_baseline_path, ['chromium-gpu-linux', 'chromium-gpu-win', 'chromium-gpu']) + chromium_linux.ChromiumLinuxPort.baseline_search_path(self)) def path_to_test_expectations_file(self): @@ -108,7 +109,7 @@ class ChromiumGpuMacPort(chromium_mac.ChromiumMacPort): chromium_mac.ChromiumMacPort.__init__(self, **kwargs) def baseline_search_path(self): - return ([self._webkit_baseline_path('chromium-gpu-mac')] + + return (map(self._webkit_baseline_path, ['chromium-gpu-mac', 'chromium-gpu']) + chromium_mac.ChromiumMacPort.baseline_search_path(self)) def path_to_test_expectations_file(self): @@ -126,7 +127,7 @@ class ChromiumGpuWinPort(chromium_win.ChromiumWinPort): chromium_win.ChromiumWinPort.__init__(self, **kwargs) def baseline_search_path(self): - return ([self._webkit_baseline_path('chromium-gpu-win')] + + return (map(self._webkit_baseline_path, ['chromium-gpu-win', 'chromium-gpu']) + chromium_win.ChromiumWinPort.baseline_search_path(self)) def path_to_test_expectations_file(self): diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py index 5c79a3f..7a13b1c 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_gpu_unittest.py @@ -26,6 +26,8 @@ import os import unittest + +from webkitpy.tool import mocktool import chromium_gpu @@ -41,16 +43,25 @@ class ChromiumGpuTest(unittest.TestCase): def assertOverridesWorked(self, port_name): # test that we got the right port - port = chromium_gpu.get(port_name=port_name, options=None) + mock_options = mocktool.MockOptions(accelerated_compositing=None, + accelerated_2d_canvas=None) + port = chromium_gpu.get(port_name=port_name, options=mock_options) + self.assertTrue(port._options.accelerated_compositing) + self.assertTrue(port._options.accelerated_2d_canvas) # we use startswith() instead of Equal to gloss over platform versions. self.assertTrue(port.name().startswith(port_name)) - # test that it has the right directory in front of the search path. - path = port.baseline_search_path()[0] - self.assertEqual(port._webkit_baseline_path(port_name), path) + # test that it has the right directories in front of the search path. + paths = port.baseline_search_path() + self.assertEqual(port._webkit_baseline_path(port_name), paths[0]) + if port_name == 'chromium-gpu-linux': + self.assertEqual(port._webkit_baseline_path('chromium-gpu-win'), paths[1]) + self.assertEqual(port._webkit_baseline_path('chromium-gpu'), paths[2]) + else: + self.assertEqual(port._webkit_baseline_path('chromium-gpu'), paths[1]) - # test that we have the right expectations file. + # Test that we have the right expectations file. self.assertTrue('chromium-gpu' in port.path_to_test_expectations_file()) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py index 176991b..b26a6b5 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_linux.py @@ -52,7 +52,7 @@ class ChromiumLinuxPort(chromium.ChromiumPort): def check_build(self, needs_http): result = chromium.ChromiumPort.check_build(self, needs_http) if needs_http: - if self._options.use_apache: + if self.get_option('use_apache'): result = self._check_apache_install() and result else: result = self._check_lighttpd_install() and result @@ -81,7 +81,7 @@ class ChromiumLinuxPort(chromium.ChromiumPort): base = self.path_from_chromium_base() if os.path.exists(os.path.join(base, 'sconsbuild')): return os.path.join(base, 'sconsbuild', *comps) - if os.path.exists(os.path.join(base, 'out', *comps)) or not self._options.use_drt: + if os.path.exists(os.path.join(base, 'out', *comps)) or not self.get_option('use_drt'): return os.path.join(base, 'out', *comps) base = self.path_from_webkit_base() if os.path.exists(os.path.join(base, 'sconsbuild')): @@ -147,9 +147,9 @@ class ChromiumLinuxPort(chromium.ChromiumPort): def _path_to_driver(self, configuration=None): if not configuration: - configuration = self._options.configuration + configuration = self.get_option('configuration') binary_name = 'test_shell' - if self._options.use_drt: + if self.get_option('use_drt'): binary_name = 'DumpRenderTree' return self._build_path(configuration, binary_name) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py index 64016ab..d1c383c 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_mac.py @@ -73,7 +73,7 @@ class ChromiumMacPort(chromium.ChromiumPort): def driver_name(self): """name for this port's equivalent of DumpRenderTree.""" - if self._options.use_drt: + if self.get_option('use_drt'): return "DumpRenderTree" return "TestShell" @@ -100,7 +100,7 @@ class ChromiumMacPort(chromium.ChromiumPort): def _build_path(self, *comps): path = self.path_from_chromium_base('xcodebuild', *comps) - if os.path.exists(path) or not self._options.use_drt: + if os.path.exists(path) or not self.get_option('use_drt'): return path return self.path_from_webkit_base('WebKit', 'chromium', 'xcodebuild', *comps) @@ -138,15 +138,15 @@ class ChromiumMacPort(chromium.ChromiumPort): # FIXME: make |configuration| happy with case-sensitive file # systems. if not configuration: - configuration = self._options.configuration + configuration = self.get_option('configuration') return self._build_path(configuration, self.driver_name() + '.app', 'Contents', 'MacOS', self.driver_name()) def _path_to_helper(self): binary_name = 'layout_test_helper' - if self._options.use_drt: + if self.get_option('use_drt'): binary_name = 'LayoutTestHelper' - return self._build_path(self._options.configuration, binary_name) + return self._build_path(self.get_option('configuration'), binary_name) def _path_to_wdiff(self): return 'wdiff' diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py index a4a9ea6..cb45430 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py @@ -26,24 +26,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import chromium -import chromium_linux -import chromium_mac -import chromium_win +import os import unittest import StringIO -import os +from webkitpy.tool import mocktool from webkitpy.thirdparty.mock import Mock +import chromium +import chromium_linux +import chromium_mac +import chromium_win class ChromiumDriverTest(unittest.TestCase): def setUp(self): mock_port = Mock() - # FIXME: The Driver should not be grabbing at port._options! - mock_port._options = Mock() - mock_port._options.wrapper = "" self.driver = chromium.ChromiumDriver(mock_port, image_path=None, options=None) def test_test_shell_command(self): @@ -106,25 +104,19 @@ class ChromiumPortTest(unittest.TestCase): return 'default' def test_path_to_image_diff(self): - class MockOptions: - def __init__(self): - self.use_drt = True - - port = ChromiumPortTest.TestLinuxPort(options=MockOptions()) + mock_options = mocktool.MockOptions(use_drt=True) + port = ChromiumPortTest.TestLinuxPort(options=mock_options) self.assertTrue(port._path_to_image_diff().endswith( '/out/default/ImageDiff'), msg=port._path_to_image_diff()) - port = ChromiumPortTest.TestMacPort(options=MockOptions()) + port = ChromiumPortTest.TestMacPort(options=mock_options) self.assertTrue(port._path_to_image_diff().endswith( '/xcodebuild/default/ImageDiff')) # FIXME: Figure out how this is going to work on Windows. #port = chromium_win.ChromiumWinPort('test-port', options=MockOptions()) def test_skipped_layout_tests(self): - class MockOptions: - def __init__(self): - self.use_drt = True - - port = ChromiumPortTest.TestLinuxPort(options=MockOptions()) + mock_options = mocktool.MockOptions(use_drt=True) + port = ChromiumPortTest.TestLinuxPort(options=mock_options) fake_test = os.path.join(port.layout_tests_dir(), "fast/js/not-good.js") @@ -138,23 +130,56 @@ DEFER LINUX WIN : fast/js/very-good.js = TIMEOUT PASS""" self.assertTrue("fast/js/not-good.js" in skipped_tests) def test_default_configuration(self): - class EmptyOptions: - def __init__(self): - pass - - options = EmptyOptions() - port = ChromiumPortTest.TestLinuxPort(options) - self.assertEquals(options.configuration, 'default') + mock_options = mocktool.MockOptions() + port = ChromiumPortTest.TestLinuxPort(options=mock_options) + self.assertEquals(mock_options.configuration, 'default') self.assertTrue(port.default_configuration_called) - class OptionsWithUnsetConfiguration: - def __init__(self): - self.configuration = None - - options = OptionsWithUnsetConfiguration() - port = ChromiumPortTest.TestLinuxPort(options) - self.assertEquals(options.configuration, 'default') + mock_options = mocktool.MockOptions(configuration=None) + port = ChromiumPortTest.TestLinuxPort(mock_options) + self.assertEquals(mock_options.configuration, 'default') self.assertTrue(port.default_configuration_called) + def test_diff_image(self): + class TestPort(ChromiumPortTest.TestLinuxPort): + def _path_to_image_diff(self): + return "/path/to/image_diff" + + class MockExecute: + def __init__(self, result): + self._result = result + + def run_command(self, + args, + cwd=None, + input=None, + error_handler=None, + return_exit_code=False, + return_stderr=True, + decode_output=False): + if return_exit_code: + return self._result + return '' + + mock_options = mocktool.MockOptions(use_drt=False) + port = ChromiumPortTest.TestLinuxPort(mock_options) + + # Images are different. + port._executive = MockExecute(0) + self.assertEquals(False, port.diff_image("EXPECTED", "ACTUAL")) + + # Images are the same. + port._executive = MockExecute(1) + self.assertEquals(True, port.diff_image("EXPECTED", "ACTUAL")) + + # There was some error running image_diff. + port._executive = MockExecute(2) + exception_raised = False + try: + port.diff_image("EXPECTED", "ACTUAL") + except ValueError, e: + exception_raised = True + self.assertFalse(exception_raised) + if __name__ == '__main__': unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py index d2b0265..69b529a 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium_win.py @@ -55,9 +55,7 @@ class ChromiumWinPort(chromium.ChromiumPort): # python executable to run cgi program. env["CYGWIN_PATH"] = self.path_from_chromium_base( "third_party", "cygwin", "bin") - if (sys.platform == "win32" and self._options and - hasattr(self._options, "register_cygwin") and - self._options.register_cygwin): + if (sys.platform == "win32" and self.get_option('register_cygwin')): setup_mount = self.path_from_chromium_base("third_party", "cygwin", "setup_mount.bat") @@ -84,11 +82,6 @@ class ChromiumWinPort(chromium.ChromiumPort): 'build-instructions-windows') return result - def get_absolute_path(self, filename): - """Return the absolute path in unix format for the given filename.""" - abspath = os.path.abspath(filename) - return abspath.replace('\\', '/') - def relative_test_filename(self, filename): path = filename[len(self.layout_tests_dir()) + 1:] return path.replace('\\', '/') @@ -118,7 +111,7 @@ class ChromiumWinPort(chromium.ChromiumPort): if os.path.exists(p): return p p = self.path_from_chromium_base('chrome', *comps) - if os.path.exists(p) or not self._options.use_drt: + if os.path.exists(p) or not self.get_option('use_drt'): return p return os.path.join(self.path_from_webkit_base(), 'WebKit', 'chromium', *comps) @@ -146,23 +139,23 @@ class ChromiumWinPort(chromium.ChromiumPort): def _path_to_driver(self, configuration=None): if not configuration: - configuration = self._options.configuration + configuration = self.get_option('configuration') binary_name = 'test_shell.exe' - if self._options.use_drt: + if self.get_option('use_drt'): binary_name = 'DumpRenderTree.exe' return self._build_path(configuration, binary_name) def _path_to_helper(self): binary_name = 'layout_test_helper.exe' - if self._options.use_drt: + if self.get_option('use_drt'): binary_name = 'LayoutTestHelper.exe' - return self._build_path(self._options.configuration, binary_name) + return self._build_path(self.get_option('configuration'), binary_name) def _path_to_image_diff(self): binary_name = 'image_diff.exe' - if self._options.use_drt: + if self.get_option('use_drt'): binary_name = 'ImageDiff.exe' - return self._build_path(self._options.configuration, binary_name) + return self._build_path(self.get_option('configuration'), binary_name) def _path_to_wdiff(self): return self.path_from_chromium_base('third_party', 'cygwin', 'bin', diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py index 648ccad..8a6af56 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py @@ -101,7 +101,6 @@ class DryrunDriver(base.Driver): def __init__(self, port, image_path, options, executive): self._port = port - self._options = options self._image_path = image_path self._executive = executive self._layout_tests_dir = None diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py index 81c3732..978a557 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/factory_unittest.py @@ -29,6 +29,8 @@ import sys import unittest +from webkitpy.tool import mocktool + import chromium_gpu import chromium_linux import chromium_mac @@ -52,21 +54,11 @@ class FactoryTest(unittest.TestCase): # FIXME: The ports themselves should expose what options they require, # instead of passing generic "options". - class WebKitOptions(object): - """Represents the minimum options for WebKit port.""" - def __init__(self): - self.pixel_tests = False - - class ChromiumOptions(WebKitOptions): - """Represents minimum options for Chromium port.""" - def __init__(self): - FactoryTest.WebKitOptions.__init__(self) - self.chromium = True - def setUp(self): self.real_sys_platform = sys.platform - self.webkit_options = FactoryTest.WebKitOptions() - self.chromium_options = FactoryTest.ChromiumOptions() + self.webkit_options = mocktool.MockOptions(pixel_tests=False) + self.chromium_options = mocktool.MockOptions(pixel_tests=False, + chromium=True) def tearDown(self): sys.platform = self.real_sys_platform diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py index bffc860..8d94bb5 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome.py @@ -24,6 +24,30 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +from __future__ import with_statement + +import codecs +import os + + +def _test_expectations_overrides(port, super): + # The chrome ports use the regular overrides plus anything in the + # official test_expectations as well. Hopefully we don't get collisions. + chromium_overrides = super.test_expectations_overrides(port) + + # FIXME: It used to be that AssertionError would get raised by + # path_from_chromium_base() if we weren't in a Chromium checkout, but + # this changed in r60427. This should probably be changed back. + overrides_path = port.path_from_chromium_base('webkit', 'tools', + 'layout_tests', 'test_expectations_chrome.txt') + if not os.path.exists(overrides_path): + return chromium_overrides + + with codecs.open(overrides_path, "r", "utf-8") as file: + if chromium_overrides: + return chromium_overrides + file.read() + else: + return file.read() def GetGoogleChromePort(**kwargs): """Some tests have slightly different results when compiled as Google @@ -41,6 +65,11 @@ def GetGoogleChromePort(**kwargs): paths.insert(0, self._webkit_baseline_path( 'google-chrome-linux32')) return paths + + def test_expectations_overrides(self): + return _test_expectations_overrides(self, + chromium_linux.ChromiumLinuxPort) + return GoogleChromeLinux32Port(**kwargs) elif port_name == 'google-chrome-linux64': import chromium_linux @@ -52,6 +81,11 @@ def GetGoogleChromePort(**kwargs): paths.insert(0, self._webkit_baseline_path( 'google-chrome-linux64')) return paths + + def test_expectations_overrides(self): + return _test_expectations_overrides(self, + chromium_linux.ChromiumLinuxPort) + return GoogleChromeLinux64Port(**kwargs) elif port_name.startswith('google-chrome-mac'): import chromium_mac @@ -63,6 +97,11 @@ def GetGoogleChromePort(**kwargs): paths.insert(0, self._webkit_baseline_path( 'google-chrome-mac')) return paths + + def test_expectations_overrides(self): + return _test_expectations_overrides(self, + chromium_mac.ChromiumMacPort) + return GoogleChromeMacPort(**kwargs) elif port_name.startswith('google-chrome-win'): import chromium_win @@ -74,5 +113,10 @@ def GetGoogleChromePort(**kwargs): paths.insert(0, self._webkit_baseline_path( 'google-chrome-win')) return paths + + def test_expectations_overrides(self): + return _test_expectations_overrides(self, + chromium_win.ChromiumWinPort) + return GoogleChromeWinPort(**kwargs) raise NotImplementedError('unsupported port: %s' % port_name) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py index 85e9338..c4c885d 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/google_chrome_unittest.py @@ -24,8 +24,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import codecs import os import unittest + +import base_unittest +import factory import google_chrome @@ -35,6 +39,7 @@ class GetGoogleChromePortTest(unittest.TestCase): 'google-chrome-mac', 'google-chrome-win') for port in test_ports: self._verify_baseline_path(port, port) + self._verify_expectations_overrides(port) self._verify_baseline_path('google-chrome-mac', 'google-chrome-mac-leopard') self._verify_baseline_path('google-chrome-win', 'google-chrome-win-xp') @@ -45,3 +50,53 @@ class GetGoogleChromePortTest(unittest.TestCase): options=None) path = port.baseline_search_path()[0] self.assertEqual(expected_path, os.path.split(path)[1]) + + def _verify_expectations_overrides(self, port_name): + # FIXME: make this more robust when we have the Tree() abstraction. + # we should be able to test for the files existing or not, and + # be able to control the contents better. + + chromium_port = factory.get("chromium-mac") + chromium_overrides = chromium_port.test_expectations_overrides() + port = google_chrome.GetGoogleChromePort(port_name=port_name, + options=None) + + orig_exists = os.path.exists + orig_open = codecs.open + expected_string = "// hello, world\n" + + def mock_exists_chrome_not_found(path): + if 'test_expectations_chrome.txt' in path: + return False + return orig_exists(path) + + def mock_exists_chrome_found(path): + if 'test_expectations_chrome.txt' in path: + return True + return orig_exists(path) + + def mock_open(path, mode, encoding): + if 'test_expectations_chrome.txt' in path: + return base_unittest.NewStringIO(expected_string) + return orig_open(path, mode, encoding) + + try: + os.path.exists = mock_exists_chrome_not_found + chrome_overrides = port.test_expectations_overrides() + self.assertEqual(chromium_overrides, chrome_overrides) + + os.path.exists = mock_exists_chrome_found + codecs.open = mock_open + chrome_overrides = port.test_expectations_overrides() + if chromium_overrides: + self.assertEqual(chrome_overrides, + chromium_overrides + expected_string) + else: + self.assertEqual(chrome_overrides, expected_string) + finally: + os.path.exists = orig_exists + codecs.open = orig_open + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock.py new file mode 100644 index 0000000..73200a0 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged +# Copyright (C) 2010 Andras Becsi (abecsi@inf.u-szeged.hu), University of Szeged +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""This class helps to block NRWT threads when more NRWTs run +http and websocket tests in a same time.""" + +import glob +import os +import sys +import tempfile +import time + + +class HttpLock(object): + + def __init__(self, lock_path, lock_file_prefix="WebKitHttpd.lock.", + guard_lock="WebKit.lock"): + if not lock_path: + self._lock_path = tempfile.gettempdir() + self._lock_file_prefix = lock_file_prefix + self._lock_file_path_prefix = os.path.join(self._lock_path, + self._lock_file_prefix) + self._guard_lock_file = os.path.join(self._lock_path, guard_lock) + self._process_lock_file_name = "" + + def cleanup_http_lock(self): + """Delete the lock file if exists.""" + if os.path.exists(self._process_lock_file_name): + os.unlink(self._process_lock_file_name) + + def _extract_lock_number(self, lock_file_name): + """Return the lock number from lock file.""" + prefix_length = len(self._lock_file_path_prefix) + return int(lock_file_name[prefix_length:]) + + def _lock_file_list(self): + """Return the list of lock files sequentially.""" + lock_list = glob.glob(self._lock_file_path_prefix + '*') + lock_list.sort(key=self._extract_lock_number) + return lock_list + + def _next_lock_number(self): + """Return the next available lock number.""" + lock_list = self._lock_file_list() + if not lock_list: + return 0 + return self._extract_lock_number(lock_list[-1]) + 1 + + def _check_pid(self, current_pid): + """Return True if pid is alive, otherwise return False.""" + try: + os.kill(current_pid, 0) + except OSError: + return False + else: + return True + + def _curent_lock_pid(self): + """Return with the current lock pid. If the lock is not valid + it deletes the lock file.""" + lock_list = self._lock_file_list() + if not lock_list: + return + try: + current_lock_file = open(lock_list[0], 'r') + current_pid = current_lock_file.readline() + current_lock_file.close() + if not (current_pid and + sys.platform in ('darwin', 'linux2') and + self._check_pid(int(current_pid))): + os.unlink(lock_list[0]) + return + except IOError, OSError: + return + return int(current_pid) + + def _create_lock_file(self): + """The lock files are used to schedule the running test sessions in first + come first served order. The sequential guard lock ensures that the lock + numbers are sequential.""" + while(True): + try: + sequential_guard_lock = os.open(self._guard_lock_file, + os.O_CREAT | os.O_NONBLOCK | os.O_EXCL) + + self._process_lock_file_name = (self._lock_file_path_prefix + + str(self._next_lock_number())) + lock_file = open(self._process_lock_file_name, 'w') + lock_file.write(str(os.getpid())) + lock_file.close() + os.close(sequential_guard_lock) + os.unlink(self._guard_lock_file) + break + except OSError: + pass + + def wait_for_httpd_lock(self): + """Create a lock file and wait until it's turn comes.""" + self._create_lock_file() + while self._curent_lock_pid() != os.getpid(): + time.sleep(1) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock_unittest.py new file mode 100644 index 0000000..85c760a --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/http_lock_unittest.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import glob +import http_lock +import os +import unittest + + +class HttpLockTest(unittest.TestCase): + + def __init__(self, testFunc): + self.http_lock_obj = http_lock.HttpLock(None, "WebKitTestHttpd.lock.", "WebKitTest.lock") + self.lock_file_path_prefix = os.path.join(self.http_lock_obj._lock_path, + self.http_lock_obj._lock_file_prefix) + self.lock_file_name = self.lock_file_path_prefix + "0" + self.guard_lock_file = self.http_lock_obj._guard_lock_file + self.clean_all_lockfile() + unittest.TestCase.__init__(self, testFunc) + + def clean_all_lockfile(self): + if os.path.exists(self.guard_lock_file): + os.unlink(self.guard_lock_file) + lock_list = glob.glob(self.lock_file_path_prefix + '*') + for file_name in lock_list: + os.unlink(file_name) + + def assertEqual(self, first, second): + if first != second: + self.clean_all_lockfile() + unittest.TestCase.assertEqual(self, first, second) + + def _check_lock_file(self): + if os.path.exists(self.lock_file_name): + pid = os.getpid() + lock_file = open(self.lock_file_name, 'r') + lock_file_pid = lock_file.readline() + lock_file.close() + self.assertEqual(pid, int(lock_file_pid)) + return True + return False + + def test_lock_lifecycle(self): + self.http_lock_obj._create_lock_file() + + self.assertEqual(True, self._check_lock_file()) + self.assertEqual(1, self.http_lock_obj._next_lock_number()) + + self.http_lock_obj.cleanup_http_lock() + + self.assertEqual(False, self._check_lock_file()) + self.assertEqual(0, self.http_lock_obj._next_lock_number()) + + def test_extract_lock_number(self,): + lock_file_list = ( + self.lock_file_path_prefix + "00", + self.lock_file_path_prefix + "9", + self.lock_file_path_prefix + "001", + self.lock_file_path_prefix + "021", + ) + + expected_number_list = (0, 9, 1, 21) + + for lock_file, expected in zip(lock_file_list, expected_number_list): + self.assertEqual(self.http_lock_obj._extract_lock_number(lock_file), expected) + + def test_lock_file_list(self): + lock_file_list = [ + self.lock_file_path_prefix + "6", + self.lock_file_path_prefix + "1", + self.lock_file_path_prefix + "4", + self.lock_file_path_prefix + "3", + ] + + expected_file_list = [ + self.lock_file_path_prefix + "1", + self.lock_file_path_prefix + "3", + self.lock_file_path_prefix + "4", + self.lock_file_path_prefix + "6", + ] + + for file_name in lock_file_list: + open(file_name, 'w') + + self.assertEqual(self.http_lock_obj._lock_file_list(), expected_file_list) + + for file_name in lock_file_list: + os.unlink(file_name) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py index 327b19e..d383a4c 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/mac_unittest.py @@ -35,7 +35,7 @@ import port_testcase class MacTest(port_testcase.PortTestCase): - def make_port(self, options=port_testcase.MockOptions()): + def make_port(self, options=port_testcase.mock_options): if sys.platform != 'darwin': return None port_obj = mac.MacPort(options=options) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py index 47597d6..04ada50 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/port_testcase.py @@ -32,20 +32,15 @@ import os import tempfile import unittest - -class MockOptions(object): - def __init__(self, - results_directory='layout-test-results', - use_apache=True, - configuration='Release'): - self.results_directory = results_directory - self.use_apache = use_apache - self.configuration = configuration +from webkitpy.tool import mocktool +mock_options = mocktool.MockOptions(results_directory='layout-test-results', + use_apache=True, + configuration='Release') class PortTestCase(unittest.TestCase): """Tests the WebKit port implementation.""" - def make_port(self, options=MockOptions()): + def make_port(self, options=mock_options): """Override in subclass.""" raise NotImplementedError() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py index 3b81167..3691c5a 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py @@ -215,14 +215,11 @@ class TestPort(base.Port): def name(self): return self._name - def options(self): - return self._options - def _path_to_wdiff(self): return None def results_directory(self): - return '/tmp/' + self._options.results_directory + return '/tmp/' + self.get_option('results_directory') def setup_test_run(self): pass @@ -285,7 +282,6 @@ class TestDriver(base.Driver): def __init__(self, port, image_path, options, executive): self._port = port self._image_path = image_path - self._options = options self._executive = executive self._image_written = False @@ -302,7 +298,7 @@ class TestDriver(base.Driver): if test.hang: time.sleep((float(timeoutms) * 4) / 1000.0) - if self._port.options().pixel_tests and test.actual_image: + if self._port.get_option('pixel_tests') and test.actual_image: with open(self._image_path, 'w') as file: file.write(test.actual_image) diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py index ed19c09..c940f1e 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py @@ -65,9 +65,7 @@ class WebKitPort(base.Port): # FIXME: disable pixel tests until they are run by default on the # build machines. - if self._options and (not hasattr(self._options, "pixel_tests") or - self._options.pixel_tests is None): - self._options.pixel_tests = False + self.set_option_default('pixel_tests', False) def baseline_path(self): return self._webkit_baseline_path(self._name) @@ -86,7 +84,7 @@ class WebKitPort(base.Port): def _build_driver(self): exit_code = self._executive.run_command([ self.script_path("build-dumprendertree"), - self.flag_from_configuration(self._options.configuration), + self.flag_from_configuration(self.get_option('configuration')), ], return_exit_code=True) if exit_code != 0: _log.error("Failed to build DumpRenderTree") @@ -101,11 +99,11 @@ class WebKitPort(base.Port): return True def check_build(self, needs_http): - if self._options.build and not self._build_driver(): + if self.get_option('build') and not self._build_driver(): return False if not self._check_driver(): return False - if self._options.pixel_tests: + if self.get_option('pixel_tests'): if not self.check_image_diff(): return False if not self._check_port_build(): @@ -184,7 +182,7 @@ class WebKitPort(base.Port): def results_directory(self): # Results are store relative to the built products to make it easy # to have multiple copies of webkit checked out and built. - return self._build_path(self._options.results_directory) + return self._build_path(self.get_option('results_directory')) def setup_test_run(self): # This port doesn't require any specific configuration. @@ -360,7 +358,7 @@ class WebKitPort(base.Port): if not self._cached_build_root: self._cached_build_root = self._webkit_build_directory([ "--configuration", - self.flag_from_configuration(self._options.configuration), + self.flag_from_configuration(self.get_option('configuration')), ]) return os.path.join(self._cached_build_root, *comps) @@ -401,7 +399,6 @@ class WebKitDriver(base.Driver): def __init__(self, port, image_path, options, executive=Executive()): self._port = port self._image_path = image_path - self._options = options self._executive = executive self._driver_tempdir = tempfile.mkdtemp(prefix='DumpRenderTree-') @@ -414,17 +411,17 @@ class WebKitDriver(base.Driver): if self._image_path: driver_args.append('--pixel-tests') - if self._options.use_drt: - if self._options.accelerated_compositing: + if self._port.get_option('use_drt'): + if self._port.get_option('accelerated_compositing'): driver_args.append('--enable-accelerated-compositing') - if self._options.accelerated_2d_canvas: + if self._port.get_option('accelerated_2d_canvas'): driver_args.append('--enable-accelerated-2d-canvas') return driver_args def start(self): - command = self._command_wrapper(self._options.wrapper) + command = self._command_wrapper(self._port.get_option('wrapper')) command += [self._port._path_to_driver(), '-'] command += self._driver_args() diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py index 7346671..926bc04 100644 --- a/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py +++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/websocket_server.py @@ -124,12 +124,13 @@ class PyWebSocket(http_server.Lighttpd): if self._root: self._layout_tests = os.path.abspath(self._root) self._web_socket_tests = os.path.abspath( - os.path.join(self._root, 'websocket', 'tests')) + os.path.join(self._root, 'http', 'tests', + 'websocket', 'tests')) else: try: self._layout_tests = self._port_obj.layout_tests_dir() self._web_socket_tests = os.path.join(self._layout_tests, - 'websocket', 'tests') + 'http', 'tests', 'websocket', 'tests') except: self._web_socket_tests = None @@ -164,10 +165,10 @@ class PyWebSocket(http_server.Lighttpd): pywebsocket_script = os.path.join(pywebsocket_base, 'mod_pywebsocket', 'standalone.py') start_cmd = [ - python_interp, pywebsocket_script, + python_interp, '-u', pywebsocket_script, '--server-host', '127.0.0.1', '--port', str(self._port), - '--document-root', self._layout_tests, + '--document-root', os.path.join(self._layout_tests, 'http', 'tests'), '--scan-dir', self._web_socket_tests, '--cgi-paths', '/websocket/tests', '--log-file', error_log, |