summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/layout_tests/port/base.py')
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base.py861
1 files changed, 0 insertions, 861 deletions
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
deleted file mode 100644
index 757318d..0000000
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
+++ /dev/null
@@ -1,861 +0,0 @@
-#!/usr/bin/env python
-# Copyright (C) 2010 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * 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.
-# * Neither the Google name nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "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 THE COPYRIGHT
-# OWNER 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.
-
-"""Abstract base class of Port-specific entrypoints for the layout tests
-test infrastructure (the Port and Driver classes)."""
-
-import cgi
-import difflib
-import errno
-import os
-import shlex
-import sys
-import time
-
-import apache_http_server
-import config as port_config
-import http_lock
-import http_server
-import test_files
-import websocket_server
-
-from webkitpy.common import system
-from webkitpy.common.system import filesystem
-from webkitpy.common.system import logutils
-from webkitpy.common.system import path
-from webkitpy.common.system.executive import Executive, ScriptError
-from webkitpy.common.system.user import User
-
-
-_log = logutils.get_logger(__file__)
-
-
-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):
- """Abstract class for Port-specific hooks for the layout_test package."""
-
- def __init__(self, port_name=None, options=None,
- executive=None,
- user=None,
- filesystem=None,
- config=None,
- **kwargs):
- self._name = port_name
- self._options = 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 = executive or Executive()
- self._user = user or User()
- self._filesystem = filesystem or system.filesystem.FileSystem()
- self._config = config or port_config.Config(self._executive,
- self._filesystem)
- 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
- port."""
- return self._executive.cpu_count()
-
- def baseline_path(self):
- """Return the absolute path to the directory to store new baselines
- in for this port."""
- raise NotImplementedError('Port.baseline_path')
-
- def baseline_search_path(self):
- """Return a list of absolute paths to directories to search under for
- baselines. The directories are searched in order."""
- raise NotImplementedError('Port.baseline_search_path')
-
- def check_build(self, needs_http):
- """This routine is used to ensure that the build is up to date
- and all the needed binaries are present."""
- raise NotImplementedError('Port.check_build')
-
- def check_sys_deps(self, needs_http):
- """If the port needs to do some runtime checks to ensure that the
- tests can be run successfully, it should override this routine.
- This step can be skipped with --nocheck-sys-deps.
-
- Returns whether the system is properly configured."""
- return True
-
- def check_image_diff(self, override_step=None, logging=True):
- """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.
-
- While this is a generic routine, we include it in the Port
- interface so that it can be overriden for testing purposes."""
- return expected_text != actual_text
-
- def diff_image(self, expected_contents, actual_contents,
- diff_filename=None, tolerance=0):
- """Compare two images and produce a delta image file.
-
- Return True if the two images are different, False if they are the same.
- Also produce a delta image of the two images and write that into
- |diff_filename| if it is not None.
-
- |tolerance| should be a percentage value (0.0 - 100.0).
- If it is omitted, the port default tolerance value is used.
-
- """
- raise NotImplementedError('Port.diff_image')
-
-
- def diff_text(self, expected_text, actual_text,
- expected_filename, actual_filename):
- """Returns a string containing the diff of the two text strings
- in 'unified diff' format.
-
- While this is a generic routine, we include it in the Port
- interface so that it can be overriden for testing purposes."""
-
- # The filenames show up in the diff output, make sure they're
- # raw bytes and not unicode, so that they don't trigger join()
- # trying to decode the input.
- def to_raw_bytes(str):
- if isinstance(str, unicode):
- return str.encode('utf-8')
- return str
- expected_filename = to_raw_bytes(expected_filename)
- actual_filename = to_raw_bytes(actual_filename)
- diff = difflib.unified_diff(expected_text.splitlines(True),
- actual_text.splitlines(True),
- expected_filename,
- actual_filename)
- return ''.join(diff)
-
- def driver_name(self):
- """Returns the name of the actual binary that is performing the test,
- so that it can be referred to in log messages. In most cases this
- will be DumpRenderTree, but if a port uses a binary with a different
- name, it can be overridden here."""
- return "DumpRenderTree"
-
- def expected_baselines(self, filename, suffix, all_baselines=False):
- """Given a test name, finds where the baseline results are located.
-
- Args:
- filename: absolute filename to test file
- suffix: file suffix of the expected results, including dot; e.g.
- '.txt' or '.png'. This should not be None, but may be an empty
- string.
- all_baselines: If True, return an ordered list of all baseline paths
- for the given platform. If False, return only the first one.
- Returns
- a list of ( platform_dir, results_filename ), where
- platform_dir - abs path to the top of the results tree (or test
- tree)
- results_filename - relative path from top of tree to the results
- file
- (os.path.join of the two gives you the full path to the file,
- unless None was returned.)
- Return values will be in the format appropriate for the current
- platform (e.g., "\\" for path separators on Windows). If the results
- file is not found, then None will be returned for the directory,
- but the expected relative pathname will still be returned.
-
- This routine is generic but lives here since it is used in
- conjunction with the other baseline and filename routines that are
- platform specific.
- """
- testname = os.path.splitext(self.relative_test_filename(filename))[0]
-
- baseline_filename = testname + '-expected' + suffix
-
- baseline_search_path = self.baseline_search_path()
-
- baselines = []
- for platform_dir in baseline_search_path:
- if self.path_exists(self._filesystem.join(platform_dir,
- baseline_filename)):
- baselines.append((platform_dir, baseline_filename))
-
- if not all_baselines and baselines:
- return baselines
-
- # If it wasn't found in a platform directory, return the expected
- # result in the test directory, even if no such file actually exists.
- platform_dir = self.layout_tests_dir()
- if self.path_exists(self._filesystem.join(platform_dir,
- baseline_filename)):
- baselines.append((platform_dir, baseline_filename))
-
- if baselines:
- return baselines
-
- return [(None, baseline_filename)]
-
- def expected_filename(self, filename, suffix):
- """Given a test name, returns an absolute path to its expected results.
-
- If no expected results are found in any of the searched directories,
- the directory in which the test itself is located will be returned.
- The return value is in the format appropriate for the platform
- (e.g., "\\" for path separators on windows).
-
- Args:
- filename: absolute filename to test file
- suffix: file suffix of the expected results, including dot; e.g. '.txt'
- or '.png'. This should not be None, but may be an empty string.
- platform: the most-specific directory name to use to build the
- search list of directories, e.g., 'chromium-win', or
- 'chromium-mac-leopard' (we follow the WebKit format)
-
- This routine is generic but is implemented here to live alongside
- the other baseline and filename manipulation routines.
- """
- platform_dir, baseline_filename = self.expected_baselines(
- filename, suffix)[0]
- if platform_dir:
- return self._filesystem.join(platform_dir, baseline_filename)
- return self._filesystem.join(self.layout_tests_dir(), baseline_filename)
-
- def expected_checksum(self, test):
- """Returns the checksum of the image we expect the test to produce, or None if it is a text-only test."""
- path = self.expected_filename(test, '.checksum')
- if not self.path_exists(path):
- return None
- return self._filesystem.read_text_file(path)
-
- def expected_image(self, test):
- """Returns the image we expect the test to produce."""
- path = self.expected_filename(test, '.png')
- if not self.path_exists(path):
- return None
- return self._filesystem.read_binary_file(path)
-
- def expected_text(self, test):
- """Returns the text output we expect the test to produce."""
- # FIXME: DRT output is actually utf-8, but since we don't decode the
- # output from DRT (instead treating it as a binary string), we read the
- # baselines as a binary string, too.
- path = self.expected_filename(test, '.txt')
- if not self.path_exists(path):
- return ''
- text = self._filesystem.read_binary_file(path)
- return text.strip("\r\n").replace("\r\n", "\n") + "\n"
-
- def filename_to_uri(self, filename):
- """Convert a test file (which is an absolute path) to a URI."""
- LAYOUTTEST_HTTP_DIR = "http/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_WEBSOCKET_DIR)
- or relative_path.startswith(LAYOUTTEST_HTTP_DIR)):
- relative_path = relative_path[len(LAYOUTTEST_HTTP_DIR):]
- port = 8000
-
- # Make http/tests/local run as local files. This is to mimic the
- # logic in run-webkit-tests.
- #
- # TODO(dpranke): remove the media reference and the SSL reference?
- if (port and not relative_path.startswith("local/") and
- not relative_path.startswith("media/")):
- if relative_path.startswith("ssl/"):
- port += 443
- protocol = "https"
- else:
- protocol = "http"
- return "%s://127.0.0.1:%u/%s" % (protocol, port, relative_path)
-
- return path.abspath_to_uri(os.path.abspath(filename))
-
- def tests(self, paths):
- """Return the list of tests found (relative to layout_tests_dir()."""
- return test_files.find(self, paths)
-
- def test_dirs(self):
- """Returns the list of top-level test directories.
-
- Used by --clobber-old-results."""
- layout_tests_dir = self.layout_tests_dir()
- return filter(lambda x: self._filesystem.isdir(self._filesystem.join(layout_tests_dir, x)),
- self._filesystem.listdir(layout_tests_dir))
-
- def path_isdir(self, path):
- """Return True if the path refers to a directory of tests."""
- # Used by test_expectations.py to apply rules to whole directories.
- return self._filesystem.isdir(path)
-
- def path_exists(self, path):
- """Return True if the path refers to an existing test or baseline."""
- # Used by test_expectations.py to determine if an entry refers to a
- # valid test and by printing.py to determine if baselines exist.
- return self._filesystem.exists(path)
-
- def driver_cmd_line(self):
- """Prints the DRT command line that will be used."""
- driver = self.create_driver(0)
- return driver.cmd_line()
-
- def update_baseline(self, path, data, encoding):
- """Updates the baseline for a test.
-
- Args:
- path: the actual path to use for baseline, not the path to
- the test. This function is used to update either generic or
- platform-specific baselines, but we can't infer which here.
- data: contents of the baseline.
- encoding: file encoding to use for the baseline.
- """
- # FIXME: remove the encoding parameter in favor of text/binary
- # functions.
- if encoding is None:
- self._filesystem.write_binary_file(path, data)
- else:
- self._filesystem.write_text_file(path, data)
-
- def uri_to_test_name(self, uri):
- """Return the base layout test name for a given URI.
-
- This returns the test name for a given URI, e.g., if you passed in
- "file:///src/LayoutTests/fast/html/keygen.html" it would return
- "fast/html/keygen.html".
-
- """
- test = uri
- if uri.startswith("file:///"):
- prefix = path.abspath_to_uri(self.layout_tests_dir()) + "/"
- return test[len(prefix):]
-
- if uri.startswith("http://127.0.0.1:8880/"):
- # websocket tests
- return test.replace('http://127.0.0.1:8880/', '')
-
- if uri.startswith("http://"):
- # regular HTTP test
- return test.replace('http://127.0.0.1:8000/', 'http/tests/')
-
- if uri.startswith("https://"):
- return test.replace('https://127.0.0.1:8443/', 'http/tests/')
-
- raise NotImplementedError('unknown url type: %s' % uri)
-
- def layout_tests_dir(self):
- """Return the absolute path to the top of the LayoutTests directory."""
- return self.path_from_webkit_base('LayoutTests')
-
- def skips_layout_test(self, test_name):
- """Figures out if the givent test is being skipped or not.
-
- Test categories are handled as well."""
- for test_or_category in self.skipped_layout_tests():
- if test_or_category == test_name:
- return True
- category = self._filesystem.join(self.layout_tests_dir(),
- test_or_category)
- if (self._filesystem.isdir(category) and
- test_name.startswith(test_or_category)):
- return True
- return False
-
- def maybe_make_directory(self, *path):
- """Creates the specified directory if it doesn't already exist."""
- self._filesystem.maybe_make_directory(*path)
-
- def name(self):
- """Return the name of the port (e.g., 'mac', 'chromium-win-xp').
-
- Note that this is different from the test_platform_name(), which
- 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)
-
- def path_from_webkit_base(self, *comps):
- """Returns the full path to path made by joining the top of the
- WebKit source tree and the list of path components in |*comps|."""
- return self._config.path_from_webkit_base(*comps)
-
- def script_path(self, script_name):
- return self._config.script_path(script_name)
-
- def path_to_test_expectations_file(self):
- """Update the test expectations to the passed-in string.
-
- This is used by the rebaselining tool. Raises NotImplementedError
- if the port does not use expectations files."""
- raise NotImplementedError('Port.path_to_test_expectations_file')
-
- def relative_test_filename(self, filename):
- """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()))
- return filename[len(self.layout_tests_dir()) + 1:]
-
- def results_directory(self):
- """Absolute path to the place to store the test results."""
- raise NotImplementedError('Port.results_directory')
-
- def setup_test_run(self):
- """Perform port-specific work at the beginning of a test run."""
- pass
-
- def setup_environ_for_server(self):
- """Perform port-specific work at the beginning of a server launch.
-
- Returns:
- Operating-system's environment.
- """
- return os.environ.copy()
-
- def show_results_html_file(self, results_filename):
- """This routine should display the HTML file pointed at by
- results_filename in a users' browser."""
- return self._user.open_url(results_filename)
-
- def create_driver(self, worker_number):
- """Return a newly created base.Driver subclass for starting/stopping
- the test driver."""
- raise NotImplementedError('Port.create_driver')
-
- def start_helper(self):
- """If a port needs to reconfigure graphics settings or do other
- things to ensure a known test configuration, it should override this
- method."""
- pass
-
- def start_http_server(self):
- """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.get_option('use_apache'):
- self._http_server = apache_http_server.LayoutTestApacheHttpd(self,
- self.get_option('results_directory'))
- else:
- self._http_server = http_server.Lighttpd(self,
- self.get_option('results_directory'))
- self._http_server.start()
-
- def start_websocket_server(self):
- """Start a websocket 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."""
- self._websocket_server = websocket_server.PyWebSocket(self,
- 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()
- it must override this routine as well."""
- pass
-
- def stop_http_server(self):
- """Shut down the http server if it is running. Do nothing if
- it isn't, or it isn't available."""
- if self._http_server:
- self._http_server.stop()
-
- def stop_websocket_server(self):
- """Shut down the websocket server if it is running. Do nothing if
- it isn't, or it isn't available."""
- 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.
-
- Basically this string should contain the equivalent of a
- test_expectations file. See test_expectations.py for more details."""
- raise NotImplementedError('Port.test_expectations')
-
- def test_expectations_overrides(self):
- """Returns an optional set of overrides for the test_expectations.
-
- This is used by ports that have code in two repositories, and where
- it is possible that you might need "downstream" expectations that
- temporarily override the "upstream" expectations until the port can
- sync up the two repos."""
- return None
-
- def test_base_platform_names(self):
- """Return a list of the 'base' platforms on your port. The base
- platforms represent different architectures, operating systems,
- or implementations (as opposed to different versions of a single
- platform). For example, 'mac' and 'win' might be different base
- platforms, wherease 'mac-tiger' and 'mac-leopard' might be
- different platforms. This routine is used by the rebaselining tool
- and the dashboards, and the strings correspond to the identifiers
- in your test expectations (*not* necessarily the platform names
- themselves)."""
- raise NotImplementedError('Port.base_test_platforms')
-
- def test_platform_name(self):
- """Returns the string that corresponds to the given platform name
- in the test expectations. This may be the same as name(), or it
- may be different. For example, chromium returns 'mac' for
- 'chromium-mac'."""
- raise NotImplementedError('Port.test_platform_name')
-
- def test_platforms(self):
- """Returns the list of test platform identifiers as used in the
- test_expectations and on dashboards, the rebaselining tool, etc.
-
- Note that this is not necessarily the same as the list of ports,
- which must be globally unique (e.g., both 'chromium-mac' and 'mac'
- might return 'mac' as a test_platform name'."""
- raise NotImplementedError('Port.platforms')
-
- def test_platform_name_to_name(self, test_platform_name):
- """Returns the Port platform name that corresponds to the name as
- referenced in the expectations file. E.g., "mac" returns
- "chromium-mac" on the Chromium ports."""
- raise NotImplementedError('Port.test_platform_name_to_name')
-
- def version(self):
- """Returns a string indicating the version of a given platform, e.g.
- '-leopard' or '-xp'.
-
- This is used to help identify the exact port when parsing test
- expectations, determining search paths, and logging information."""
- raise NotImplementedError('Port.version')
-
- def test_repository_paths(self):
- """Returns a list of (repository_name, repository_path) tuples
- of its depending code base. By default it returns a list that only
- contains a ('webkit', <webkitRepossitoryPath>) tuple.
- """
- return [('webkit', self.layout_tests_dir())]
-
-
- _WDIFF_DEL = '##WDIFF_DEL##'
- _WDIFF_ADD = '##WDIFF_ADD##'
- _WDIFF_END = '##WDIFF_END##'
-
- def _format_wdiff_output_as_html(self, wdiff):
- wdiff = cgi.escape(wdiff)
- wdiff = wdiff.replace(self._WDIFF_DEL, "<span class=del>")
- wdiff = wdiff.replace(self._WDIFF_ADD, "<span class=add>")
- wdiff = wdiff.replace(self._WDIFF_END, "</span>")
- html = "<head><style>.del { background: #faa; } "
- html += ".add { background: #afa; }</style></head>"
- html += "<pre>%s</pre>" % wdiff
- return html
-
- def _wdiff_command(self, actual_filename, expected_filename):
- executable = self._path_to_wdiff()
- return [executable,
- "--start-delete=%s" % self._WDIFF_DEL,
- "--end-delete=%s" % self._WDIFF_END,
- "--start-insert=%s" % self._WDIFF_ADD,
- "--end-insert=%s" % self._WDIFF_END,
- actual_filename,
- expected_filename]
-
- @staticmethod
- def _handle_wdiff_error(script_error):
- # Exit 1 means the files differed, any other exit code is an error.
- if script_error.exit_code != 1:
- raise script_error
-
- def _run_wdiff(self, actual_filename, expected_filename):
- """Runs wdiff and may throw exceptions.
- This is mostly a hook for unit testing."""
- # Diffs are treated as binary as they may include multiple files
- # with conflicting encodings. Thus we do not decode the output.
- command = self._wdiff_command(actual_filename, expected_filename)
- wdiff = self._executive.run_command(command, decode_output=False,
- error_handler=self._handle_wdiff_error)
- return self._format_wdiff_output_as_html(wdiff)
-
- def wdiff_text(self, actual_filename, expected_filename):
- """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."""
- if not self._wdiff_available:
- return ""
- try:
- # It's possible to raise a ScriptError we pass wdiff invalid paths.
- return self._run_wdiff(actual_filename, expected_filename)
- except OSError, e:
- if e.errno in [errno.ENOENT, errno.EACCES, errno.ECHILD]:
- # Silently ignore cases where wdiff is missing.
- self._wdiff_available = False
- return ""
- raise
-
- # 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):
- if not self._pretty_patch_available:
- return self._pretty_patch_error_html
- 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.
- 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.
- 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 default_configuration(self):
- return self._config.default_configuration()
-
- #
- # PROTECTED ROUTINES
- #
- # The routines below should only be called by routines in this class
- # or any of its subclasses.
- #
- def _webkit_build_directory(self, args):
- return self._config.build_directory(args[0])
-
- def _path_to_apache(self):
- """Returns the full path to the apache binary.
-
- This is needed only by ports that use the apache_http_server module."""
- raise NotImplementedError('Port.path_to_apache')
-
- def _path_to_apache_config_file(self):
- """Returns the full path to the apache binary.
-
- This is needed only by ports that use the apache_http_server module."""
- raise NotImplementedError('Port.path_to_apache_config_file')
-
- def _path_to_driver(self, configuration=None):
- """Returns the full path to the test driver (DumpRenderTree)."""
- raise NotImplementedError('Port._path_to_driver')
-
- def _path_to_webcore_library(self):
- """Returns the full path to a built copy of WebCore."""
- raise NotImplementedError('Port.path_to_webcore_library')
-
- def _path_to_helper(self):
- """Returns the full path to the layout_test_helper binary, which
- is used to help configure the system for the test run, or None
- if no helper is needed.
-
- This is likely only used by start/stop_helper()."""
- raise NotImplementedError('Port._path_to_helper')
-
- def _path_to_image_diff(self):
- """Returns the full path to the image_diff binary, or None if it
- is not available.
-
- This is likely used only by diff_image()"""
- raise NotImplementedError('Port.path_to_image_diff')
-
- def _path_to_lighttpd(self):
- """Returns the path to the LigHTTPd binary.
-
- This is needed only by ports that use the http_server.py module."""
- raise NotImplementedError('Port._path_to_lighttpd')
-
- def _path_to_lighttpd_modules(self):
- """Returns the path to the LigHTTPd modules directory.
-
- This is needed only by ports that use the http_server.py module."""
- raise NotImplementedError('Port._path_to_lighttpd_modules')
-
- def _path_to_lighttpd_php(self):
- """Returns the path to the LigHTTPd PHP executable.
-
- This is needed only by ports that use the http_server.py module."""
- raise NotImplementedError('Port._path_to_lighttpd_php')
-
- def _path_to_wdiff(self):
- """Returns the full path to the wdiff binary, or None if it is
- not available.
-
- This is likely used only by wdiff_text()"""
- raise NotImplementedError('Port._path_to_wdiff')
-
- def _shut_down_http_server(self, pid):
- """Forcefully and synchronously kills the web server.
-
- This routine should only be called from http_server.py or its
- subclasses."""
- raise NotImplementedError('Port._shut_down_http_server')
-
- def _webkit_baseline_path(self, platform):
- """Return the full path to the top of the baseline tree for a
- given platform."""
- return self._filesystem.join(self.layout_tests_dir(), 'platform',
- platform)
-
-
-class Driver:
- """Abstract interface for the DumpRenderTree interface."""
-
- def __init__(self, port, worker_number):
- """Initialize a Driver to subsequently run tests.
-
- Typically this routine will spawn DumpRenderTree in a config
- ready for subsequent input.
-
- port - reference back to the port object.
- worker_number - identifier for a particular worker/driver instance
- """
- raise NotImplementedError('Driver.__init__')
-
- def run_test(self, test_input):
- """Run a single test and return the results.
-
- Note that it is okay if a test times out or crashes and leaves
- the driver in an indeterminate state. The upper layers of the program
- are responsible for cleaning up and ensuring things are okay.
-
- Args:
- test_input: a TestInput object
-
- Returns a TestOutput object.
- """
- raise NotImplementedError('Driver.run_test')
-
- # FIXME: This is static so we can test it w/o creating a Base instance.
- @classmethod
- def _command_wrapper(cls, wrapper_option):
- # Hook for injecting valgrind or other runtime instrumentation,
- # used by e.g. tools/valgrind/valgrind_tests.py.
- wrapper = []
- browser_wrapper = os.environ.get("BROWSER_WRAPPER", None)
- if browser_wrapper:
- # FIXME: There seems to be no reason to use BROWSER_WRAPPER over --wrapper.
- # Remove this code any time after the date listed below.
- _log.error("BROWSER_WRAPPER is deprecated, please use --wrapper instead.")
- _log.error("BROWSER_WRAPPER will be removed any time after June 1st 2010 and your scripts will break.")
- wrapper += [browser_wrapper]
-
- if wrapper_option:
- wrapper += shlex.split(wrapper_option)
- return wrapper
-
- def poll(self):
- """Returns None if the Driver is still running. Returns the returncode
- if it has exited."""
- raise NotImplementedError('Driver.poll')
-
- def stop(self):
- raise NotImplementedError('Driver.stop')