summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2010-04-27 16:31:00 +0100
committerSteve Block <steveblock@google.com>2010-05-11 14:42:12 +0100
commitdcc8cf2e65d1aa555cce12431a16547e66b469ee (patch)
tree92a8d65cd5383bca9749f5327fb5e440563926e6 /WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
parentccac38a6b48843126402088a309597e682f40fe6 (diff)
downloadexternal_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.zip
external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.tar.gz
external_webkit-dcc8cf2e65d1aa555cce12431a16547e66b469ee.tar.bz2
Merge webkit.org at r58033 : Initial merge by git
Change-Id: If006c38561af287c50cd578d251629b51e4d8cd1
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py')
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py239
1 files changed, 186 insertions, 53 deletions
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
index 1123376..8bae2a9 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
@@ -36,11 +36,43 @@ import signal
import subprocess
import sys
import time
+import webbrowser
import base
import http_server
+
+# FIXME: To use the DRT-based version of this file, we need to be able to
+# run the webkit code, which uses server_process, which requires UNIX-style
+# non-blocking I/O with selects(), which requires fcntl() which doesn't exist
+# on Windows.
+if sys.platform not in ('win32', 'cygwin'):
+ import webkit
+
import websocket_server
+_log = logging.getLogger("webkitpy.layout_tests.port.chromium")
+
+
+# FIXME: This function doesn't belong in this package.
+def check_file_exists(path_to_file, file_description, override_step=None,
+ logging=True):
+ """Verify the file is present where expected or log an error.
+
+ Args:
+ file_name: The (human friendly) name or description of the file
+ you're looking for (e.g., "HTTP Server"). Used for error logging.
+ override_step: An optional string to be logged if the check fails.
+ logging: Whether or not log the error messages."""
+ if not os.path.exists(path_to_file):
+ if logging:
+ _log.error('Unable to find %s' % file_description)
+ _log.error(' at %s' % path_to_file)
+ if override_step:
+ _log.error(' %s' % override_step)
+ _log.error('')
+ return False
+ return True
+
class ChromiumPort(base.Port):
"""Abstract base class for Chromium implementations of the Port class."""
@@ -50,81 +82,116 @@ class ChromiumPort(base.Port):
self._chromium_base_dir = None
def baseline_path(self):
- return self._chromium_baseline_path(self._name)
+ return self._webkit_baseline_path(self._name)
- def check_sys_deps(self):
+ def check_build(self, needs_http):
result = True
- test_shell_binary_path = self._path_to_driver()
- if os.path.exists(test_shell_binary_path):
- proc = subprocess.Popen([test_shell_binary_path,
- '--check-layout-test-sys-deps'])
- if proc.wait() != 0:
- logging.error("Aborting because system dependencies check "
- "failed.")
- logging.error("To override, invoke with --nocheck-sys-deps")
- result = False
- else:
- logging.error('test driver is not found at %s' %
- test_shell_binary_path)
- result = False
- image_diff_path = self._path_to_image_diff()
- if (not os.path.exists(image_diff_path) and not
- self._options.no_pixel_tests):
- logging.error('image diff not found at %s' % image_diff_path)
- logging.error("To override, invoke with --no-pixel-tests")
+ # FIXME: see comment above re: import webkit
+ if (sys.platform in ('win32', 'cygwin') and self._options and
+ hasattr(self._options, 'use_drt') and self._options.use_drt):
+ _log.error('--use-drt is not supported on Windows yet')
+ _log.error('')
result = False
+ 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:
+ result = self._check_driver_build_up_to_date(
+ self._options.configuration)
+ else:
+ _log.error('')
+
+ helper_path = self._path_to_helper()
+ if helper_path:
+ result = check_file_exists(helper_path,
+ 'layout test helper') and result
+
+ if self._options.pixel_tests:
+ result = self.check_image_diff(
+ 'To override, invoke with --no-pixel-tests') and result
+
return result
- def compare_text(self, actual_text, expected_text):
- return actual_text != expected_text
+ def check_sys_deps(self, needs_http):
+ dump_render_tree_binary_path = self._path_to_driver()
+ proc = subprocess.Popen([dump_render_tree_binary_path,
+ '--check-layout-test-sys-deps'])
+ if proc.wait():
+ _log.error('System dependencies check failed.')
+ _log.error('To override, invoke with --nocheck-sys-deps')
+ _log.error('')
+ return False
+ return True
+
+ def check_image_diff(self, override_step=None, logging=True):
+ image_diff_path = self._path_to_image_diff()
+ return check_file_exists(image_diff_path, 'image diff exe',
+ override_step, logging)
+
+ def driver_name(self):
+ return "test_shell"
def path_from_chromium_base(self, *comps):
"""Returns the full path to path made by joining the top of the
Chromium source tree and the list of path components in |*comps|."""
if not self._chromium_base_dir:
abspath = os.path.abspath(__file__)
- self._chromium_base_dir = abspath[0:abspath.find('third_party')]
+ offset = abspath.find('third_party')
+ if offset == -1:
+ raise AssertionError('could not find Chromium base dir from ' +
+ abspath)
+ self._chromium_base_dir = abspath[0:offset]
return os.path.join(self._chromium_base_dir, *comps)
def path_to_test_expectations_file(self):
- return self.path_from_chromium_base('webkit', 'tools', 'layout_tests',
- 'test_expectations.txt')
+ return self.path_from_webkit_base('LayoutTests', 'platform',
+ 'chromium', 'test_expectations.txt')
def results_directory(self):
- return self.path_from_chromium_base('webkit', self._options.target,
- self._options.results_directory)
+ try:
+ return self.path_from_chromium_base('webkit',
+ self._options.configuration, self._options.results_directory)
+ except AssertionError:
+ return self.path_from_webkit_base('WebKit', 'chromium',
+ 'xcodebuild', self._options.configuration,
+ self._options.results_directory)
def setup_test_run(self):
# Delete the disk cache if any to ensure a clean test run.
- test_shell_binary_path = self._path_to_driver()
- cachedir = os.path.split(test_shell_binary_path)[0]
+ dump_render_tree_binary_path = self._path_to_driver()
+ cachedir = os.path.split(dump_render_tree_binary_path)[0]
cachedir = os.path.join(cachedir, "cache")
if os.path.exists(cachedir):
shutil.rmtree(cachedir)
def show_results_html_file(self, results_filename):
- subprocess.Popen([self._path_to_driver(),
- self.filename_to_uri(results_filename)])
+ uri = self.filename_to_uri(results_filename)
+ if self._options.use_drt:
+ webbrowser.open(uri, new=1)
+ else:
+ subprocess.Popen([self._path_to_driver(), uri])
def start_driver(self, image_path, options):
"""Starts a new Driver and returns a handle to it."""
+ if self._options.use_drt:
+ return webkit.WebKitDriver(self, image_path, options)
return ChromiumDriver(self, image_path, options)
def start_helper(self):
helper_path = self._path_to_helper()
if helper_path:
- logging.debug("Starting layout helper %s" % helper_path)
+ _log.debug("Starting layout helper %s" % helper_path)
self._helper = subprocess.Popen([helper_path],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None)
is_ready = self._helper.stdout.readline()
if not is_ready.startswith('ready'):
- logging.error("layout_test_helper failed to be ready")
+ _log.error("layout_test_helper failed to be ready")
def stop_helper(self):
if self._helper:
- logging.debug("Stopping layout test helper")
+ _log.debug("Stopping layout test helper")
self._helper.stdin.write("x\n")
self._helper.stdin.close()
self._helper.wait()
@@ -140,10 +207,27 @@ class ChromiumPort(base.Port):
expectations_file = self.path_to_test_expectations_file()
return file(expectations_file, "r").read()
+ def test_expectations_overrides(self):
+ try:
+ overrides_file = self.path_from_chromium_base('webkit', 'tools',
+ 'layout_tests', 'test_expectations.txt')
+ except AssertionError:
+ return None
+ if os.path.exists(overrides_file):
+ return file(overrides_file, "r").read()
+ else:
+ return None
+
def test_platform_names(self):
return self.test_base_platform_names() + ('win-xp',
'win-vista', 'win-7')
+ def test_platform_name_to_name(self, test_platform_name):
+ if test_platform_name in self.test_platform_names():
+ return 'chromium-' + test_platform_name
+ raise ValueError('Unsupported test_platform_name: %s' %
+ test_platform_name)
+
#
# PROTECTED METHODS
#
@@ -151,11 +235,34 @@ class ChromiumPort(base.Port):
# or any subclasses.
#
+ def _check_driver_build_up_to_date(self, configuration):
+ if configuration in ('Debug', 'Release'):
+ try:
+ debug_path = self._path_to_driver('Debug')
+ release_path = self._path_to_driver('Release')
+
+ debug_mtime = os.stat(debug_path).st_mtime
+ release_mtime = os.stat(release_path).st_mtime
+
+ if (debug_mtime > release_mtime and configuration == 'Release' or
+ release_mtime > debug_mtime and configuration == 'Debug'):
+ _log.warning('You are not running the most '
+ 'recent DumpRenderTree binary. You need to '
+ 'pass --debug or not to select between '
+ 'Debug and Release.')
+ _log.warning('')
+ # This will fail if we don't have both a debug and release binary.
+ # That's fine because, in this case, we must already be running the
+ # most up-to-date one.
+ except OSError:
+ pass
+ return True
+
def _chromium_baseline_path(self, platform):
if platform is None:
platform = self.name()
- return self.path_from_chromium_base('webkit', 'data', 'layout_tests',
- 'platform', platform, 'LayoutTests')
+ return self.path_from_webkit_base('LayoutTests', 'platform', platform)
+
class ChromiumDriver(base.Driver):
"""Abstract interface for the DumpRenderTree interface."""
@@ -163,7 +270,7 @@ class ChromiumDriver(base.Driver):
def __init__(self, port, image_path, options):
self._port = port
self._options = options
- self._target = port._options.target
+ self._configuration = port._options.configuration
self._image_path = image_path
cmd = []
@@ -181,10 +288,17 @@ class ChromiumDriver(base.Driver):
cmd += [port._path_to_driver(), '--layout-tests']
if options:
cmd += options
+
+ # We need to pass close_fds=True to work around Python bug #2320
+ # (otherwise we can hang when we kill DumpRenderTree when we are running
+ # multiple threads). See http://bugs.python.org/issue2320 .
+ # Note that close_fds isn't supported on Windows, but this bug only
+ # shows up on Mac and Linux.
+ close_flag = sys.platform not in ('win32', 'cygwin')
self._proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT)
-
+ stderr=subprocess.STDOUT,
+ close_fds=close_flag)
def poll(self):
return self._proc.poll()
@@ -207,14 +321,19 @@ class ChromiumDriver(base.Driver):
cmd += ' ' + checksum
cmd += "\n"
- self._proc.stdin.write(cmd)
- line = self._proc.stdout.readline()
- while line.rstrip() != "#EOF":
+ try:
+ self._proc.stdin.write(cmd)
+ line = self._proc.stdout.readline()
+ except IOError, e:
+ _log.error("IOError communicating w/ test_shell: " + str(e))
+ crash = True
+
+ while not crash and line.rstrip() != "#EOF":
# Make sure we haven't crashed.
if line == '' and self.poll() is not None:
# This is hex code 0xc000001d, which is used for abrupt
# termination. This happens if we hit ctrl+c from the prompt
- # and we happen to be waiting on the test_shell.
+ # and we happen to be waiting on the DumpRenderTree.
# sdoyon: Not sure for which OS and in what circumstances the
# above code is valid. What works for me under Linux to detect
# ctrl+c is for the subprocess returncode to be negative
@@ -229,8 +348,8 @@ class ChromiumDriver(base.Driver):
if line.startswith("#URL:"):
actual_uri = line.rstrip()[5:]
if uri != actual_uri:
- logging.fatal("Test got out of sync:\n|%s|\n|%s|" %
- (uri, actual_uri))
+ _log.fatal("Test got out of sync:\n|%s|\n|%s|" %
+ (uri, actual_uri))
raise AssertionError("test out of sync")
elif line.startswith("#MD5:"):
actual_checksum = line.rstrip()[5:]
@@ -242,7 +361,11 @@ class ChromiumDriver(base.Driver):
else:
error.append(line)
- line = self._proc.stdout.readline()
+ try:
+ line = self._proc.stdout.readline()
+ except IOError, e:
+ _log.error("IOError while reading: " + str(e))
+ crash = True
return (crash, timeout, actual_checksum, ''.join(output),
''.join(error))
@@ -253,10 +376,20 @@ class ChromiumDriver(base.Driver):
self._proc.stdout.close()
if self._proc.stderr:
self._proc.stderr.close()
- if (sys.platform not in ('win32', 'cygwin') and
- not self._proc.poll()):
- # Closing stdin/stdout/stderr hangs sometimes on OS X.
- null = open(os.devnull, "w")
- subprocess.Popen(["kill", "-9",
- str(self._proc.pid)], stderr=null)
- null.close()
+ if sys.platform not in ('win32', 'cygwin'):
+ # Closing stdin/stdout/stderr hangs sometimes on OS X,
+ # (see __init__(), above), and anyway we don't want to hang
+ # the harness if DumpRenderTree is buggy, so we wait a couple
+ # seconds to give DumpRenderTree a chance to clean up, but then
+ # force-kill the process if necessary.
+ KILL_TIMEOUT = 3.0
+ timeout = time.time() + KILL_TIMEOUT
+ while self._proc.poll() is None and time.time() < timeout:
+ time.sleep(0.1)
+ if self._proc.poll() is None:
+ _log.warning('stopping test driver timed out, '
+ 'killing it')
+ null = open(os.devnull, "w")
+ subprocess.Popen(["kill", "-9",
+ str(self._proc.pid)], stderr=null)
+ null.close()