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.py155
1 files changed, 114 insertions, 41 deletions
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
index 2b25e29..fb6fddf 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
@@ -36,27 +36,49 @@ import errno
import os
import subprocess
import sys
+import time
import apache_http_server
import http_server
import websocket_server
+from webkitpy.common.system import logutils
+from webkitpy.common.system.executive import Executive, ScriptError
+
+
+_log = logutils.get_logger(__file__)
+
+
# Python bug workaround. See Port.wdiff_text() for an explanation.
_wdiff_available = True
-
+_pretty_patch_available = True
# 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):
+ @staticmethod
+ def flag_from_configuration(configuration):
+ flags_by_configuration = {
+ "Debug": "--debug",
+ "Release": "--release",
+ }
+ return flags_by_configuration[configuration]
+
+ def __init__(self, port_name=None, options=None, executive=Executive()):
self._name = port_name
self._options = options
self._helper = None
self._http_server = None
self._webkit_base_dir = None
self._websocket_server = None
+ self._executive = executive
+
+ 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
@@ -68,38 +90,53 @@ class Port(object):
baselines. The directories are searched in order."""
raise NotImplementedError('Port.baseline_search_path')
- def check_sys_deps(self):
+ 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, they should be done here.
+ 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."""
- raise NotImplementedError('Port.check_sys_deps')
+ return True
+
+ def check_image_diff(self, override_step=None, logging=True):
+ """This routine is used to check whether image_diff binary exists."""
+ raise NotImplemented('Port.check_image_diff')
- def compare_text(self, actual_text, expected_text):
+ 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 actual_text != expected_text
+ return expected_text != actual_text
- def diff_image(self, actual_filename, expected_filename,
+ def diff_image(self, expected_filename, actual_filename,
diff_filename=None):
"""Compare two image files and produce a delta image file.
- Return 1 if the two files are different, 0 if they are the same.
+ Return True if the two files 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.
While this is a generic routine, we include it in the Port
interface so that it can be overriden for testing purposes."""
executable = self._path_to_image_diff()
- cmd = [executable, '--diff', actual_filename, expected_filename]
+
if diff_filename:
- cmd.append(diff_filename)
- result = 1
+ cmd = [executable, '--diff', expected_filename, actual_filename,
+ diff_filename]
+ else:
+ cmd = [executable, expected_filename, actual_filename]
+
+ result = True
try:
- result = subprocess.call(cmd)
+ if subprocess.call(cmd) == 0:
+ return False
except OSError, e:
if e.errno == errno.ENOENT or e.errno == errno.EACCES:
_compare_available = False
@@ -111,8 +148,8 @@ class Port(object):
pass
return result
- def diff_text(self, actual_text, expected_text,
- actual_filename, expected_filename):
+ 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.
@@ -124,6 +161,13 @@ class Port(object):
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.
@@ -262,14 +306,7 @@ class Port(object):
may be different (e.g., 'win-xp' instead of 'chromium-win-xp'."""
return self._name
- def num_cores(self):
- """Return the number of cores/cpus available on this machine.
-
- This routine is used to determine the default amount of parallelism
- used by run-chromium-webkit-tests."""
- raise NotImplementedError('Port.num_cores')
-
- # FIXME: This could be replaced by functions in webkitpy.scm.
+ # 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
WebKit source tree and the list of path components in |*comps|."""
@@ -288,7 +325,7 @@ class Port(object):
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 remove_directory(self, *path):
"""Recursively removes a directory, even if it's marked read-only.
@@ -321,7 +358,7 @@ class Port(object):
win32 = False
def remove_with_retry(rmfunc, path):
- os.chmod(path, stat.S_IWRITE)
+ os.chmod(path, os.stat.S_IWRITE)
if win32:
win32api.SetFileAttributes(path,
win32con.FILE_ATTRIBUTE_NORMAL)
@@ -381,10 +418,10 @@ class Port(object):
raise NotImplementedError('Port.start_driver')
def start_helper(self):
- """Start a layout test helper if needed on this port. The test helper
- is used to reconfigure graphics settings and do other things that
- may be necessary to ensure a known test configuration."""
- raise NotImplementedError('Port.start_helper')
+ """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
@@ -408,8 +445,9 @@ class Port(object):
def stop_helper(self):
"""Shut down the test helper if it is running. Do nothing if
- it isn't, or it isn't available."""
- raise NotImplementedError('Port.stop_helper')
+ 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
@@ -430,6 +468,15 @@ class Port(object):
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,
@@ -458,6 +505,12 @@ class Port(object):
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'.
@@ -476,8 +529,9 @@ class Port(object):
'--end-delete=##WDIFF_END##',
'--start-insert=##WDIFF_ADD##',
'--end-insert=##WDIFF_END##',
- expected_filename,
- actual_filename]
+ actual_filename,
+ expected_filename]
+ # FIXME: Why not just check os.exists(executable) once?
global _wdiff_available
result = ''
try:
@@ -500,6 +554,7 @@ class Port(object):
# http://bugs.python.org/issue1236
if _wdiff_available:
try:
+ # FIXME: Use Executive() here.
wdiff = subprocess.Popen(cmd,
stdout=subprocess.PIPE).communicate()[0]
except ValueError, e:
@@ -521,6 +576,31 @@ class Port(object):
raise e
return result
+ _pretty_patch_error_html = "Failed to run PrettyPatch, see error console."
+
+ def pretty_patch_text(self, diff_path):
+ global _pretty_patch_available
+ if not _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]
+ try:
+ return self._executive.run_command(command)
+ except OSError, e:
+ # If the system is missing ruby log the error and stop trying.
+ _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()))
+ return self._pretty_patch_error_html
+
+ def default_configuration(self):
+ return "Release"
+
#
# PROTECTED ROUTINES
#
@@ -528,13 +608,6 @@ class Port(object):
# or any of its subclasses.
#
- def _kill_process(self, pid):
- """Forcefully kill a process.
-
- This routine should not be used or needed generically, but can be
- used in helper files like http_server.py."""
- raise NotImplementedError('Port.kill_process')
-
def _path_to_apache(self):
"""Returns the full path to the apache binary.
@@ -547,7 +620,7 @@ class Port(object):
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):
+ def _path_to_driver(self, configuration=None):
"""Returns the full path to the test driver (DumpRenderTree)."""
raise NotImplementedError('Port.path_to_driver')