diff options
author | Steve Block <steveblock@google.com> | 2010-04-27 16:31:00 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2010-05-11 14:42:12 +0100 |
commit | dcc8cf2e65d1aa555cce12431a16547e66b469ee (patch) | |
tree | 92a8d65cd5383bca9749f5327fb5e440563926e6 /WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py | |
parent | ccac38a6b48843126402088a309597e682f40fe6 (diff) | |
download | external_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.py | 239 |
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() |