summaryrefslogtreecommitdiff
path: root/Tools/Scripts/webkitpy
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-02-24 16:36:50 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2012-02-24 16:36:50 +0100
commitad0d549d4cc13433f77c1ac8f0ab379c83d93f28 (patch)
treeb34b0daceb7c8e7fdde4b4ec43650ab7caadb0a9 /Tools/Scripts/webkitpy
parent03e12282df9aa1e1fb05a8b90f1cfc2e08764cec (diff)
downloadqtwebkit-ad0d549d4cc13433f77c1ac8f0ab379c83d93f28.tar.gz
Imported WebKit commit bb52bf3c0119e8a128cd93afe5572413a8617de9 (http://svn.webkit.org/repository/webkit/trunk@108790)
Diffstat (limited to 'Tools/Scripts/webkitpy')
-rw-r--r--Tools/Scripts/webkitpy/bindings/main.py3
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/checkout_mock.py10
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm/detection_unittest.py45
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm/git.py19
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm/scm.py3
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py67
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm/svn.py16
-rw-r--r--Tools/Scripts/webkitpy/common/config/committers.py7
-rw-r--r--Tools/Scripts/webkitpy/common/config/ports.py213
-rw-r--r--Tools/Scripts/webkitpy/common/config/ports_unittest.py51
-rw-r--r--Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py1
-rwxr-xr-xTools/Scripts/webkitpy/common/system/autoinstall.py40
-rw-r--r--Tools/Scripts/webkitpy/common/system/executive_mock.py14
-rw-r--r--Tools/Scripts/webkitpy/common/system/file_lock.py2
-rw-r--r--Tools/Scripts/webkitpy/common/system/file_lock_integrationtest.py4
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem_mock.py74
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem_mock_unittest.py42
-rw-r--r--Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py1
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/manager.py41
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker.py350
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker_unittest.py156
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/message_broker.py202
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/message_broker_unittest.py76
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py11
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py3
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/worker.py77
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/controllers/worker_unittest.py (renamed from Tools/Scripts/webkitpy/common/array_stream_unittest.py)57
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py1
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py4
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py2
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/port/base.py47
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/builders.py3
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/port/chromium.py8
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py528
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py41
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py14
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/driver.py6
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/factory.py3
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/mac.py2
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py2
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py4
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py25
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/port/port_testcase.py9
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/qt.py21
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py21
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/server_process.py4
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/test.py30
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/webkit.py8
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/win.py4
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py8
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py50
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py87
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py12
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/servers/http_server.py10
-rwxr-xr-xTools/Scripts/webkitpy/layout_tests/servers/http_server_base.py2
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py59
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/views/metered_stream.py4
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/views/metered_stream_unittest.py23
-rw-r--r--Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py212
-rw-r--r--Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py11
-rwxr-xr-xTools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py76
-rw-r--r--Tools/Scripts/webkitpy/style/checker.py5
-rwxr-xr-xTools/Scripts/webkitpy/style/checker_unittest.py3
-rw-r--r--Tools/Scripts/webkitpy/style/checkers/cpp.py30
-rw-r--r--Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py21
-rw-r--r--Tools/Scripts/webkitpy/thirdparty/__init__.py4
-rw-r--r--Tools/Scripts/webkitpy/to_be_moved/rebaseline_chromium_webkit_tests.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py8
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py7
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/queueengine.py9
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/queueengine_unittest.py22
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/sheriff_unittest.py1
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/stylequeuetask.py (renamed from Tools/Scripts/webkitpy/common/array_stream.py)77
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/download.py10
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py41
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py21
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/queues.py76
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/queues_unittest.py61
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/queuestest.py3
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py3
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/sheriffbot.py7
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py4
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/upload.py1
-rwxr-xr-xTools/Scripts/webkitpy/tool/main.py5
-rw-r--r--Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py6
-rw-r--r--Tools/Scripts/webkitpy/tool/servers/reflectionhandler.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/abstractstep.py1
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/attachtobug.py4
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/checkstyle.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/commit_unittest.py1
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/editchangelog.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/preparechangelog.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py7
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/preparechangelogfordepsroll.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py6
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/steps_unittest.py5
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/validatereviewer.py3
100 files changed, 2015 insertions, 1381 deletions
diff --git a/Tools/Scripts/webkitpy/bindings/main.py b/Tools/Scripts/webkitpy/bindings/main.py
index 2268b9f5d..5154f399b 100644
--- a/Tools/Scripts/webkitpy/bindings/main.py
+++ b/Tools/Scripts/webkitpy/bindings/main.py
@@ -76,7 +76,8 @@ class BindingsTests:
'WebCore/bindings/scripts/resolve-supplemental.pl',
'--idlFilesList', idl_files_list[1],
'--defines', '',
- '--supplementalDependencyFile', supplemental_dependency_file]
+ '--supplementalDependencyFile', supplemental_dependency_file,
+ '--idlAttributesFile', 'WebCore/bindings/scripts/IDLAttributes.txt']
exit_code = 0
try:
diff --git a/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py b/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py
index 25b05c33a..696f1427e 100644
--- a/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py
+++ b/Tools/Scripts/webkitpy/common/checkout/checkout_mock.py
@@ -26,14 +26,13 @@
# (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 os
-
from .deps_mock import MockDEPS
from .commitinfo import CommitInfo
# FIXME: These imports are wrong, we should use a shared MockCommittersList.
from webkitpy.common.config.committers import CommitterList
from webkitpy.common.net.bugzilla.bugzilla_mock import _mock_reviewers
+from webkitpy.common.system.filesystem_mock import MockFileSystem
class MockCommitMessage(object):
@@ -42,6 +41,10 @@ class MockCommitMessage(object):
class MockCheckout(object):
+ def __init__(self):
+ # FIXME: It's unclear if a MockCheckout is very useful. A normal Checkout
+ # with a MockSCM/MockFileSystem/MockExecutive is probably better.
+ self._filesystem = MockFileSystem()
# FIXME: This should move onto the Host object, and we should use a MockCommitterList for tests.
_committer_list = CommitterList()
@@ -64,8 +67,7 @@ class MockCheckout(object):
})
def is_path_to_changelog(self, path):
- # FIXME: This should self._filesystem.basename.
- return os.path.basename(path) == "ChangeLog"
+ return self._filesystem.basename(path) == "ChangeLog"
def bug_id_for_revision(self, svn_revision):
return 12345
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/detection_unittest.py b/Tools/Scripts/webkitpy/common/checkout/scm/detection_unittest.py
new file mode 100644
index 000000000..7bee81710
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/detection_unittest.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2009 Google Inc. All rights reserved.
+# Copyright (C) 2009 Apple Inc. All rights reserved.
+# Copyright (C) 2011 Daniel Bates (dbates@intudata.com). 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 name of Google Inc. 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.
+
+import unittest
+
+from .detection import SCMDetector
+from webkitpy.common.system.filesystem_mock import MockFileSystem
+from webkitpy.common.system.executive_mock import MockExecutive
+
+
+class SCMDetectorTest(unittest.TestCase):
+ def test_detect_scm_system(self):
+ filesystem = MockFileSystem()
+ executive = MockExecutive(should_log=True)
+ detector = SCMDetector(filesystem, executive)
+
+ self.assertEqual(detector.detect_scm_system("/"), None)
+ # FIXME: This should make a synthetic tree and test SVN and Git detection in that tree.
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/git.py b/Tools/Scripts/webkitpy/common/checkout/scm/git.py
index ab96b8fa7..7c5d80cc3 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/git.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/git.py
@@ -105,19 +105,18 @@ class Git(SCM, SVNRepository):
# The Windows bots seem to through a WindowsError when git isn't installed.
return False
- @classmethod
- def find_checkout_root(cls, path):
- # FIXME: This should use a FileSystem object instead of os.path.
+ def find_checkout_root(self, path):
# "git rev-parse --show-cdup" would be another way to get to the root
- (checkout_root, dot_git) = os.path.split(run_command(['git', 'rev-parse', '--git-dir'], cwd=(path or "./")))
- if not os.path.isabs(checkout_root): # Sometimes git returns relative paths
- checkout_root = os.path.join(path, checkout_root)
+ git_output = self._executive.run_command(['git', 'rev-parse', '--git-dir'], cwd=(path or "./"))
+ (checkout_root, dot_git) = self._filesystem.split(git_output)
+ if not self._filesystem.isabs(checkout_root): # Sometimes git returns relative paths
+ checkout_root = self._filesystem.join(path, checkout_root)
return checkout_root
- @classmethod
- def to_object_name(cls, filepath):
- # FIXME: This should use a FileSystem object instead of os.path.
- root_end_with_slash = os.path.join(cls.find_checkout_root(os.path.dirname(filepath)), '')
+ def to_object_name(self, filepath):
+ # FIXME: This can't be the right way to append a slash.
+ root_end_with_slash = self._filesystem.join(self.find_checkout_root(self._filesystem.dirname(filepath)), '')
+ # FIXME: This seems to want some sort of rel_path instead?
return filepath.replace(root_end_with_slash, '')
@classmethod
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/scm.py b/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
index 432d6ca99..cbce361e5 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/scm.py
@@ -60,9 +60,9 @@ class AuthenticationError(Exception):
class SCM:
def __init__(self, cwd, executive=None, filesystem=None):
self.cwd = cwd
- self.checkout_root = self.find_checkout_root(self.cwd)
self._executive = executive or Executive()
self._filesystem = filesystem or FileSystem()
+ self.checkout_root = self.find_checkout_root(self.cwd)
# A wrapper used by subclasses to create processes.
def run(self, args, cwd=None, input=None, error_handler=None, return_exit_code=False, return_stderr=True, decode_output=True):
@@ -137,7 +137,6 @@ class SCM:
def in_working_directory(path):
SCM._subclass_must_implement()
- @staticmethod
def find_checkout_root(path):
SCM._subclass_must_implement()
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py b/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py
index b835cdf67..4b88c3da1 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/scm_unittest.py
@@ -52,8 +52,8 @@ from webkitpy.common.system.executive import Executive, ScriptError
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.common.system.executive_mock import MockExecutive
-from .detection import find_checkout_root, default_scm, detect_scm_system
from .git import Git, AmbiguousCommitError
+from .detection import detect_scm_system
from .scm import SCM, CheckoutNeedsUpdate, commit_error_handler, AuthenticationError
from .svn import SVN
@@ -78,7 +78,6 @@ def delete_cached_mock_repo_at_exit():
# Eventually we will want to write tests which work for both scms. (like update_webkit, changed_files, etc.)
# Perhaps through some SCMTest base-class which both SVNTest and GitTest inherit from.
-
def run_command(*args, **kwargs):
# FIXME: This should not be a global static.
# New code should use Executive.run_command directly instead
@@ -231,58 +230,6 @@ class SVNTestRepository:
os.chdir(detect_scm_system(path).checkout_root)
-# FIXME: This should move to testing SCMDetector instead.
-class StandaloneFunctionsTest(unittest.TestCase):
- """This class tests any standalone/top-level functions in the package."""
- def setUp(self):
- self.orig_cwd = os.path.abspath(os.getcwd())
- self.orig_abspath = os.path.abspath
-
- # We capture but ignore the output from stderr to reduce unwanted
- # logging.
- self.output = OutputCapture()
- self.output.capture_output()
-
- def tearDown(self):
- os.chdir(self.orig_cwd)
- os.path.abspath = self.orig_abspath
- self.output.restore_output()
-
- def test_find_checkout_root(self):
- # Test from inside the tree.
- os.chdir(sys.path[0])
- dir = find_checkout_root()
- self.assertNotEqual(dir, None)
- self.assertTrue(os.path.exists(dir))
-
- # Test from outside the tree.
- os.chdir(os.path.expanduser("~"))
- dir = find_checkout_root()
- self.assertNotEqual(dir, None)
- self.assertTrue(os.path.exists(dir))
-
- # Mock out abspath() to test being not in a checkout at all.
- os.path.abspath = lambda x: "/"
- self.assertRaises(SystemExit, find_checkout_root)
- os.path.abspath = self.orig_abspath
-
- def test_default_scm(self):
- # Test from inside the tree.
- os.chdir(sys.path[0])
- scm = default_scm()
- self.assertNotEqual(scm, None)
-
- # Test from outside the tree.
- os.chdir(os.path.expanduser("~"))
- dir = find_checkout_root()
- self.assertNotEqual(dir, None)
-
- # Mock out abspath() to test being not in a checkout at all.
- os.path.abspath = lambda x: "/"
- self.assertRaises(Exception, default_scm)
- os.path.abspath = self.orig_abspath
-
-
# For testing the SCM baseclass directly.
class SCMClassTests(unittest.TestCase):
def setUp(self):
@@ -739,12 +686,6 @@ Q1dTBx0AAAB42itg4GlgYJjGwMDDyODMxMDw34GBgQEAJPQDJA==
self._setup_webkittools_scripts_symlink(scm)
Checkout(scm).apply_patch(patch)
- def test_apply_svn_patch_force(self):
- scm = detect_scm_system(self.svn_checkout_path)
- patch = self._create_patch(_svn_diff("-r3:5"))
- self._setup_webkittools_scripts_symlink(scm)
- self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
-
def test_commit_logs(self):
# Commits have dates and usernames in them, so we can't just direct compare.
self.assertTrue(re.search('fourth commit', self.scm.last_svn_commit_log()))
@@ -1198,12 +1139,6 @@ class GitSVNTest(SCMTest):
self._setup_webkittools_scripts_symlink(scm)
Checkout(scm).apply_patch(patch)
- def test_apply_git_patch_force(self):
- scm = detect_scm_system(self.git_checkout_path)
- patch = self._create_patch(_git_diff('HEAD~2..HEAD'))
- self._setup_webkittools_scripts_symlink(scm)
- self.assertRaises(ScriptError, Checkout(scm).apply_patch, patch, force=True)
-
def test_commit_text_parsing(self):
write_into_file_at_path('test_file', 'more test content')
commit_text = self.scm.commit_with_message("another test commit")
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm/svn.py b/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
index edeee30ae..af03f8d70 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm/svn.py
@@ -86,11 +86,10 @@ class SVN(SCM, SVNRepository):
def in_working_directory(path):
return os.path.isdir(os.path.join(path, '.svn'))
- @classmethod
- def find_uuid(cls, path):
- if not cls.in_working_directory(path):
+ def find_uuid(self, path):
+ if not self.in_working_directory(path):
return None
- return cls.value_from_svn_info(path, 'Repository UUID')
+ return self.value_from_svn_info(path, 'Repository UUID')
@classmethod
def value_from_svn_info(cls, path, field_name):
@@ -102,19 +101,18 @@ class SVN(SCM, SVNRepository):
raise ScriptError(script_args=svn_info_args, message='svn info did not contain a %s.' % field_name)
return match.group('value')
- @staticmethod
- def find_checkout_root(path):
- uuid = SVN.find_uuid(path)
+ def find_checkout_root(self, path):
+ uuid = self.find_uuid(path)
# If |path| is not in a working directory, we're supposed to return |path|.
if not uuid:
return path
# Search up the directory hierarchy until we find a different UUID.
last_path = None
while True:
- if uuid != SVN.find_uuid(path):
+ if uuid != self.find_uuid(path):
return last_path
last_path = path
- (path, last_component) = os.path.split(path)
+ (path, last_component) = self._filesystem.split(path)
if last_path == path:
return None
diff --git a/Tools/Scripts/webkitpy/common/config/committers.py b/Tools/Scripts/webkitpy/common/config/committers.py
index 43a9c68c1..54dbbed80 100644
--- a/Tools/Scripts/webkitpy/common/config/committers.py
+++ b/Tools/Scripts/webkitpy/common/config/committers.py
@@ -110,7 +110,6 @@ contributors_who_are_not_committers = [
Contributor("Alan Stearns", "stearns@adobe.com", 'astearns'),
Contributor("Alexandre Elias", "aelias@chromium.org"),
Contributor("Alexey Marinichev", ["amarinichev@chromium.org", "amarinichev@google.com"], "amarinichev"),
- Contributor("Ami Fischman", ["fischman@chromium.org", "fischman@google.com"], "amifischman0"),
Contributor("Andras Piroska", "pandras@inf.u-szeged.hu", "andris88"),
Contributor("Anne van Kesteren", "annevankesteren+webkit@gmail.com", "annevk"),
Contributor("Annie Sullivan", "sullivan@chromium.org", "annie"),
@@ -142,6 +141,7 @@ contributors_who_are_not_committers = [
Contributor("Peter Linss", "peter.linss@hp.com", "plinss"),
Contributor("Radar WebKit Bug Importer", "webkit-bug-importer@group.apple.com"),
Contributor("Roland Takacs", "takacs.roland@stud.u-szeged.hu", "rtakacs"),
+ Contributor("Stephen Chenney", "schenney@chromium.org", "schenney"),
Contributor("Szilard Ledan-Muntean", "muntean-ledan.szilard@stud.u-szeged.hu", "szledan"),
Contributor("Tab Atkins", ["tabatkins@google.com", "jackalmage@gmail.com"], "tabatkins"),
Contributor("Tamas Czene", ["tczene@inf.u-szeged.hu", "Czene.Tamas@stud.u-szeged.hu"], "tczene"),
@@ -172,6 +172,7 @@ committers_unable_to_review = [
Committer("Alexis Menard", ["alexis.menard@openbossa.org", "menard@kde.org", "alexis.menard@nokia.com"], "darktears"),
Committer("Alice Boxhall", "aboxhall@chromium.org", "aboxhall"),
Committer("Alok Priyadarshi", "alokp@chromium.org", "alokp"),
+ Committer("Ami Fischman", ["fischman@chromium.org", "fischman@google.com"], "fischman"),
Committer("Amruth Raj", "amruthraj@motorola.com", "amruthraj"),
Committer("Andre Boule", "aboule@apple.com"),
Committer("Andrei Popescu", "andreip@google.com", "andreip"),
@@ -179,6 +180,7 @@ committers_unable_to_review = [
Committer("Andrew Scherkus", "scherkus@chromium.org", "scherkus"),
Committer("Andrey Kosyakov", "caseq@chromium.org", "caseq"),
Committer("Andras Becsi", ["abecsi@webkit.org", "andras.becsi@nokia.com"], "bbandix"),
+ Committer("Andy Wingo", "wingo@igalia.com", "wingo"),
Committer("Anna Cavender", "annacc@chromium.org", "annacc"),
Committer("Anthony Ricaud", "rik@webkit.org", "rik"),
Committer("Antoine Labour", "piman@chromium.org", "piman"),
@@ -319,6 +321,7 @@ committers_unable_to_review = [
Committer("Pratik Solanki", "psolanki@apple.com", "psolanki"),
Committer("Qi Zhang", ["qi.2.zhang@nokia.com", "qi.zhang02180@gmail.com"], "qi"),
Committer("Rafael Antognolli", "antognolli@profusion.mobi", "antognolli"),
+ Committer("Rafael Weinstein", "rafaelw@chromium.org", "rafaelw"),
Committer("Raphael Kubo da Costa", ["kubo@profusion.mobi", "rakuco@FreeBSD.org"], "rakuco"),
Committer("Ravi Kasibhatla", "ravi.kasibhatla@motorola.com", "kphanee"),
Committer("Renata Hodovan", "reni@webkit.org", "reni"),
@@ -329,9 +332,11 @@ committers_unable_to_review = [
Committer("Scott Violet", "sky@chromium.org", "sky"),
Committer("Sergio Villar Senin", ["svillar@igalia.com", "sergio@webkit.org"], "svillar"),
Committer("Shawn Singh", "shawnsingh@chromium.org", "shawnsingh"),
+ Committer("Shinya Kawanaka", "shinyak@chromium.org", "shinyak"),
Committer("Siddharth Mathur", "siddharth.mathur@nokia.com", "simathur"),
Committer("Steve Lacey", "sjl@chromium.org", "stevela"),
Committer("Takashi Toyoshima", "toyoshim@chromium.org", "toyoshim"),
+ Committer("Thomas Sepez", "tsepez@chromium.org", "tsepez"),
Committer("Tim Horton", "timothy_horton@apple.com", "thorton"),
Committer("Tom Zakrajsek", "tomz@codeaurora.org", "tomz"),
Committer("Tommy Widenflycht", "tommyw@google.com", "tommyw"),
diff --git a/Tools/Scripts/webkitpy/common/config/ports.py b/Tools/Scripts/webkitpy/common/config/ports.py
index 02c1f5b55..4b3d90a1a 100644
--- a/Tools/Scripts/webkitpy/common/config/ports.py
+++ b/Tools/Scripts/webkitpy/common/config/ports.py
@@ -35,17 +35,24 @@ import sys
from webkitpy.common.system.executive import Executive
-class WebKitPort(object):
+class DeprecatedPort(object):
results_directory = "/tmp/layout-test-results"
+ # FIXME: This is only used by BotInfo.
+ def name(self):
+ return self.__class__
+
+ def flag(self):
+ if self.port_flag_name:
+ return "--port=%s" % self.port_flag_name
+ return None
+
# We might need to pass scm into this function for scm.checkout_root
- @classmethod
- def script_path(cls, script_name):
+ def script_path(self, script_name):
return os.path.join("Tools", "Scripts", script_name)
- @classmethod
- def script_shell_command(cls, script_name):
- script_path = cls.script_path(script_name)
+ def script_shell_command(self, script_name):
+ script_path = self.script_path(script_name)
return Executive.shell_command_for_script(script_path)
@staticmethod
@@ -64,206 +71,124 @@ class WebKitPort(object):
"Darwin": MacPort,
}
# Do we really need MacPort as the ultimate default?
- return ports.get(port_name, default_port.get(platform.system(), MacPort))
+ return ports.get(port_name, default_port.get(platform.system(), MacPort))()
- @staticmethod
- def makeArgs():
+ def makeArgs(self):
+ # FIXME: This shouldn't use a static Executive().
args = '--makeargs="-j%s"' % Executive().cpu_count()
if os.environ.has_key('MAKEFLAGS'):
args = '--makeargs="%s"' % os.environ['MAKEFLAGS']
return args
- @classmethod
- def name(cls):
- raise NotImplementedError("subclasses must implement")
-
- @classmethod
- def flag(cls):
- raise NotImplementedError("subclasses must implement")
-
- @classmethod
- def update_webkit_command(cls, non_interactive=False):
- return cls.script_shell_command("update-webkit")
+ def update_webkit_command(self, non_interactive=False):
+ return self.script_shell_command("update-webkit")
- @classmethod
- def check_webkit_style_command(cls):
- return cls.script_shell_command("check-webkit-style")
+ def check_webkit_style_command(self):
+ return self.script_shell_command("check-webkit-style")
- @classmethod
- def prepare_changelog_command(cls):
- return cls.script_shell_command("prepare-ChangeLog")
+ def prepare_changelog_command(self):
+ return self.script_shell_command("prepare-ChangeLog")
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = cls.script_shell_command("build-webkit")
+ def build_webkit_command(self, build_style=None):
+ command = self.script_shell_command("build-webkit")
if build_style == "debug":
command.append("--debug")
if build_style == "release":
command.append("--release")
return command
- @classmethod
- def run_javascriptcore_tests_command(cls):
- return cls.script_shell_command("run-javascriptcore-tests")
+ def run_javascriptcore_tests_command(self):
+ return self.script_shell_command("run-javascriptcore-tests")
- @classmethod
- def run_webkit_unit_tests_command(cls):
+ def run_webkit_unit_tests_command(self):
return None
- @classmethod
- def run_webkit_tests_command(cls):
- return cls.script_shell_command("run-webkit-tests")
-
- @classmethod
- def run_python_unittests_command(cls):
- return cls.script_shell_command("test-webkitpy")
-
- @classmethod
- def run_perl_unittests_command(cls):
- return cls.script_shell_command("test-webkitperl")
-
- @classmethod
- def layout_tests_results_path(cls):
- return os.path.join(cls.results_directory, "full_results.json")
-
-
-class MacPort(WebKitPort):
+ def run_webkit_tests_command(self):
+ return self.script_shell_command("run-webkit-tests")
- @classmethod
- def name(cls):
- return "Mac"
+ def run_python_unittests_command(self):
+ return self.script_shell_command("test-webkitpy")
- @classmethod
- def flag(cls):
- return "--port=mac"
+ def run_perl_unittests_command(self):
+ return self.script_shell_command("test-webkitperl")
- @classmethod
- def _system_version(cls):
- version_string = platform.mac_ver()[0] # e.g. "10.5.6"
- version_tuple = version_string.split('.')
- return map(int, version_tuple)
+ def layout_tests_results_path(self):
+ return os.path.join(self.results_directory, "full_results.json")
- @classmethod
- def is_leopard(cls):
- return tuple(cls._system_version()[:2]) == (10, 5)
+class MacPort(DeprecatedPort):
+ port_flag_name = "mac"
-class WinPort(WebKitPort):
- @classmethod
- def name(cls):
- return "Win"
+class WinPort(DeprecatedPort):
+ port_flag_name = "win"
- @classmethod
- def flag(cls):
- # FIXME: This is lame. We should autogenerate this from a codename or something.
- return "--port=win"
+class GtkPort(DeprecatedPort):
+ port_flag_name = "gtk"
-class GtkPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Gtk"
-
- @classmethod
- def flag(cls):
- return "--port=gtk"
-
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
+ def build_webkit_command(self, build_style=None):
+ command = super(GtkPort, self).build_webkit_command(build_style=build_style)
command.append("--gtk")
command.append("--update-gtk")
- command.append(WebKitPort.makeArgs())
+ command.append(super(GtkPort, self).makeArgs())
return command
- @classmethod
- def run_webkit_tests_command(cls):
- command = WebKitPort.run_webkit_tests_command()
+ def run_webkit_tests_command(self):
+ command = super(GtkPort, self).run_webkit_tests_command()
command.append("--gtk")
return command
-class QtPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Qt"
-
- @classmethod
- def flag(cls):
- return "--port=qt"
+class QtPort(DeprecatedPort):
+ port_flag_name = "qt"
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
+ def build_webkit_command(self, build_style=None):
+ command = super(QtPort, self).build_webkit_command(build_style=build_style)
command.append("--qt")
- command.append(WebKitPort.makeArgs())
+ command.append(super(QtPort, self).makeArgs())
return command
-class EflPort(WebKitPort):
+class EflPort(DeprecatedPort):
+ port_flag_name = "efl"
- @classmethod
- def name(cls):
- return "Efl"
-
- @classmethod
- def flag(cls):
- return "--port=efl"
-
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
+ def build_webkit_command(self, build_style=None):
+ command = super(EflPort, self).build_webkit_command(build_style=build_style)
command.append("--efl")
- command.append(WebKitPort.makeArgs())
+ command.append(super(EflPort, self).makeArgs())
return command
-class ChromiumPort(WebKitPort):
-
- @classmethod
- def name(cls):
- return "Chromium"
+class ChromiumPort(DeprecatedPort):
+ port_flag_name = "chromium"
- @classmethod
- def flag(cls):
- return "--port=chromium"
-
- @classmethod
- def update_webkit_command(cls, non_interactive=False):
- command = WebKitPort.update_webkit_command(non_interactive=non_interactive)
+ def update_webkit_command(self, non_interactive=False):
+ command = super(ChromiumPort, self).update_webkit_command(non_interactive=non_interactive)
command.append("--chromium")
if non_interactive:
command.append("--force-update")
return command
- @classmethod
- def build_webkit_command(cls, build_style=None):
- command = WebKitPort.build_webkit_command(build_style=build_style)
+ def build_webkit_command(self, build_style=None):
+ command = super(ChromiumPort, self).build_webkit_command(build_style=build_style)
command.append("--chromium")
command.append("--update-chromium")
return command
- @classmethod
- def run_webkit_tests_command(cls):
- command = cls.script_shell_command("new-run-webkit-tests")
+ def run_webkit_tests_command(self):
+ # Note: This could be run-webkit-tests now.
+ command = self.script_shell_command("new-run-webkit-tests")
command.append("--chromium")
command.append("--skip-failing-tests")
return command
- @classmethod
- def run_javascriptcore_tests_command(cls):
+ def run_javascriptcore_tests_command(self):
return None
class ChromiumXVFBPort(ChromiumPort):
+ port_flag_name = "chromium-xvfb"
- @classmethod
- def flag(cls):
- return "--port=chromium-xvfb"
-
- @classmethod
- def run_webkit_tests_command(cls):
- return ["xvfb-run"] + ChromiumPort.run_webkit_tests_command()
+ def run_webkit_tests_command(self):
+ return ["xvfb-run"] + super(ChromiumXVFBPort, self).run_webkit_tests_command()
diff --git a/Tools/Scripts/webkitpy/common/config/ports_unittest.py b/Tools/Scripts/webkitpy/common/config/ports_unittest.py
index 0532512aa..e0b77c82e 100644
--- a/Tools/Scripts/webkitpy/common/config/ports_unittest.py
+++ b/Tools/Scripts/webkitpy/common/config/ports_unittest.py
@@ -32,45 +32,36 @@ import unittest
from webkitpy.common.config.ports import *
-class WebKitPortTest(unittest.TestCase):
+class DeprecatedPortTest(unittest.TestCase):
def test_mac_port(self):
- self.assertEquals(MacPort.name(), "Mac")
- self.assertEquals(MacPort.flag(), "--port=mac")
- self.assertEquals(MacPort.run_webkit_tests_command(), WebKitPort.script_shell_command("run-webkit-tests"))
- self.assertEquals(MacPort.build_webkit_command(), WebKitPort.script_shell_command("build-webkit"))
- self.assertEquals(MacPort.build_webkit_command(build_style="debug"), WebKitPort.script_shell_command("build-webkit") + ["--debug"])
- self.assertEquals(MacPort.build_webkit_command(build_style="release"), WebKitPort.script_shell_command("build-webkit") + ["--release"])
-
- class TestIsLeopard(MacPort):
- @classmethod
- def _system_version(cls):
- return [10, 5]
- self.assertTrue(TestIsLeopard.is_leopard())
+ self.assertEquals(MacPort().flag(), "--port=mac")
+ self.assertEquals(MacPort().run_webkit_tests_command(), DeprecatedPort().script_shell_command("run-webkit-tests"))
+ self.assertEquals(MacPort().build_webkit_command(), DeprecatedPort().script_shell_command("build-webkit"))
+ self.assertEquals(MacPort().build_webkit_command(build_style="debug"), DeprecatedPort().script_shell_command("build-webkit") + ["--debug"])
+ self.assertEquals(MacPort().build_webkit_command(build_style="release"), DeprecatedPort().script_shell_command("build-webkit") + ["--release"])
def test_gtk_port(self):
- self.assertEquals(GtkPort.name(), "Gtk")
- self.assertEquals(GtkPort.flag(), "--port=gtk")
- self.assertEquals(GtkPort.run_webkit_tests_command(), WebKitPort.script_shell_command("run-webkit-tests") + ["--gtk"])
- self.assertEquals(GtkPort.build_webkit_command(), WebKitPort.script_shell_command("build-webkit") + ["--gtk", "--update-gtk", WebKitPort.makeArgs()])
- self.assertEquals(GtkPort.build_webkit_command(build_style="debug"), WebKitPort.script_shell_command("build-webkit") + ["--debug", "--gtk", "--update-gtk", WebKitPort.makeArgs()])
+ self.assertEquals(GtkPort().flag(), "--port=gtk")
+ self.assertEquals(GtkPort().run_webkit_tests_command(), DeprecatedPort().script_shell_command("run-webkit-tests") + ["--gtk"])
+ self.assertEquals(GtkPort().build_webkit_command(), DeprecatedPort().script_shell_command("build-webkit") + ["--gtk", "--update-gtk", DeprecatedPort().makeArgs()])
+ self.assertEquals(GtkPort().build_webkit_command(build_style="debug"), DeprecatedPort().script_shell_command("build-webkit") + ["--debug", "--gtk", "--update-gtk", DeprecatedPort().makeArgs()])
def test_qt_port(self):
- self.assertEquals(QtPort.name(), "Qt")
- self.assertEquals(QtPort.flag(), "--port=qt")
- self.assertEquals(QtPort.run_webkit_tests_command(), WebKitPort.script_shell_command("run-webkit-tests"))
- self.assertEquals(QtPort.build_webkit_command(), WebKitPort.script_shell_command("build-webkit") + ["--qt", WebKitPort.makeArgs()])
- self.assertEquals(QtPort.build_webkit_command(build_style="debug"), WebKitPort.script_shell_command("build-webkit") + ["--debug", "--qt", WebKitPort.makeArgs()])
+ self.assertEquals(QtPort().flag(), "--port=qt")
+ self.assertEquals(QtPort().run_webkit_tests_command(), DeprecatedPort().script_shell_command("run-webkit-tests"))
+ self.assertEquals(QtPort().build_webkit_command(), DeprecatedPort().script_shell_command("build-webkit") + ["--qt", DeprecatedPort().makeArgs()])
+ self.assertEquals(QtPort().build_webkit_command(build_style="debug"), DeprecatedPort().script_shell_command("build-webkit") + ["--debug", "--qt", DeprecatedPort().makeArgs()])
def test_chromium_port(self):
- self.assertEquals(ChromiumPort.name(), "Chromium")
- self.assertEquals(ChromiumPort.flag(), "--port=chromium")
- self.assertEquals(ChromiumPort.run_webkit_tests_command(), WebKitPort.script_shell_command("new-run-webkit-tests") + ["--chromium", "--skip-failing-tests"])
- self.assertEquals(ChromiumPort.build_webkit_command(), WebKitPort.script_shell_command("build-webkit") + ["--chromium", "--update-chromium"])
- self.assertEquals(ChromiumPort.build_webkit_command(build_style="debug"), WebKitPort.script_shell_command("build-webkit") + ["--debug", "--chromium", "--update-chromium"])
- self.assertEquals(ChromiumPort.update_webkit_command(), WebKitPort.script_shell_command("update-webkit") + ["--chromium"])
+ self.assertEquals(ChromiumPort().flag(), "--port=chromium")
+ self.assertEquals(ChromiumPort().run_webkit_tests_command(), DeprecatedPort().script_shell_command("new-run-webkit-tests") + ["--chromium", "--skip-failing-tests"])
+ self.assertEquals(ChromiumPort().build_webkit_command(), DeprecatedPort().script_shell_command("build-webkit") + ["--chromium", "--update-chromium"])
+ self.assertEquals(ChromiumPort().build_webkit_command(build_style="debug"), DeprecatedPort().script_shell_command("build-webkit") + ["--debug", "--chromium", "--update-chromium"])
+ self.assertEquals(ChromiumPort().update_webkit_command(), DeprecatedPort().script_shell_command("update-webkit") + ["--chromium"])
def test_chromium_xvfb_port(self):
- self.assertEquals(ChromiumXVFBPort.run_webkit_tests_command(), ['xvfb-run'] + WebKitPort.script_shell_command('new-run-webkit-tests') + ['--chromium', '--skip-failing-tests'])
+ self.assertEquals(ChromiumXVFBPort().run_webkit_tests_command(), ['xvfb-run'] + DeprecatedPort().script_shell_command('new-run-webkit-tests') + ['--chromium', '--skip-failing-tests'])
+
if __name__ == '__main__':
unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py
index a32e86e13..3d2a96922 100644
--- a/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py
+++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bugzilla.py
@@ -31,7 +31,6 @@
# WebKit's Python module for interacting with Bugzilla
import mimetypes
-import os.path
import re
import StringIO
import urllib
diff --git a/Tools/Scripts/webkitpy/common/system/autoinstall.py b/Tools/Scripts/webkitpy/common/system/autoinstall.py
index a928db63a..e5bc0b2cb 100755
--- a/Tools/Scripts/webkitpy/common/system/autoinstall.py
+++ b/Tools/Scripts/webkitpy/common/system/autoinstall.py
@@ -259,6 +259,44 @@ class AutoInstaller(object):
return target_path
+ # This is a replacement for ZipFile.extractall(), which is
+ # available in Python 2.6 but not in earlier versions.
+ # NOTE: The version in 2.6.1 (which shipped on Snow Leopard) is broken!
+ def _extract_all(self, zip_file, target_dir):
+ self._log_transfer("Extracting zip file...", zip_file, target_dir)
+
+ # This is helpful for debugging purposes.
+ _log.debug("Listing zip file contents...")
+ for name in zip_file.namelist():
+ _log.debug(' * "%s"' % name)
+
+ for name in zip_file.namelist():
+ path = os.path.join(target_dir, name)
+ self._log_transfer("Extracting...", name, path)
+
+ if not os.path.basename(path):
+ # Then the path ends in a slash, so it is a directory.
+ self._create_directory(path)
+ continue
+ # Otherwise, it is a file.
+
+ try:
+ # We open this file w/o encoding, as we're reading/writing
+ # the raw byte-stream from the zip file.
+ outfile = open(path, 'wb')
+ except IOError, err:
+ # Not all zip files seem to list the directories explicitly,
+ # so try again after creating the containing directory.
+ _log.debug("Got IOError: retrying after creating directory...")
+ dir = os.path.dirname(path)
+ self._create_directory(dir)
+ outfile = open(path, 'wb')
+
+ try:
+ outfile.write(zip_file.read(name))
+ finally:
+ outfile.close()
+
def _unzip(self, path, scratch_dir):
# zipfile.extractall() extracts to a path without the
# trailing ".zip".
@@ -276,7 +314,7 @@ class AutoInstaller(object):
raise Exception(message)
try:
- zip_file.extractall(scratch_dir)
+ self._extract_all(zip_file, scratch_dir)
finally:
zip_file.close()
diff --git a/Tools/Scripts/webkitpy/common/system/executive_mock.py b/Tools/Scripts/webkitpy/common/system/executive_mock.py
index f698235e9..a76268129 100644
--- a/Tools/Scripts/webkitpy/common/system/executive_mock.py
+++ b/Tools/Scripts/webkitpy/common/system/executive_mock.py
@@ -32,8 +32,16 @@ from webkitpy.common.system.deprecated_logging import log
from webkitpy.common.system.executive import ScriptError
+class MockProcess(object):
+ def __init__(self):
+ self.pid = 42
+
+
# FIXME: This should be unified with MockExecutive2
class MockExecutive(object):
+ PIPE = "MOCK PIPE"
+ STDOUT = "MOCK STDOUT"
+
@staticmethod
def ignore_error(error):
pass
@@ -55,7 +63,7 @@ class MockExecutive(object):
env_string = ", env=%s" % env
log("MOCK run_and_throw_if_fail: %s, cwd=%s%s" % (args, cwd, env_string))
if self._should_throw_when_run.intersection(args):
- raise ScriptError("Exception for %s" % args)
+ raise ScriptError("Exception for %s" % args, output="MOCK command output")
return "MOCK output of child process"
def run_command(self,
@@ -81,6 +89,10 @@ class MockExecutive(object):
def cpu_count(self):
return 2
+ def popen(self, *args, **kwargs):
+ # FIXME: Implement logging when self._should_log is set.
+ return MockProcess()
+
class MockExecutive2(object):
@staticmethod
diff --git a/Tools/Scripts/webkitpy/common/system/file_lock.py b/Tools/Scripts/webkitpy/common/system/file_lock.py
index fb853c8fb..b4bfffc40 100644
--- a/Tools/Scripts/webkitpy/common/system/file_lock.py
+++ b/Tools/Scripts/webkitpy/common/system/file_lock.py
@@ -71,6 +71,8 @@ class FileLock(object):
os.close(self._lock_file_descriptor)
self._lock_file_descriptor = None
return False
+ # There's no compelling reason to spin hard here, so sleep for a bit.
+ time.sleep(0.01)
def release_lock(self):
try:
diff --git a/Tools/Scripts/webkitpy/common/system/file_lock_integrationtest.py b/Tools/Scripts/webkitpy/common/system/file_lock_integrationtest.py
index c5c1db3de..5cd27d11d 100644
--- a/Tools/Scripts/webkitpy/common/system/file_lock_integrationtest.py
+++ b/Tools/Scripts/webkitpy/common/system/file_lock_integrationtest.py
@@ -36,8 +36,8 @@ class FileLockTest(unittest.TestCase):
def setUp(self):
self._lock_name = "TestWebKit" + str(os.getpid()) + ".lock"
self._lock_path = os.path.join(tempfile.gettempdir(), self._lock_name)
- self._file_lock1 = FileLock(self._lock_path, 1)
- self._file_lock2 = FileLock(self._lock_path, 1)
+ self._file_lock1 = FileLock(self._lock_path, 0.1)
+ self._file_lock2 = FileLock(self._lock_path, 0.1)
def tearDown(self):
self._file_lock1.release_lock()
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
index 6e106dd83..6fb1dcaee 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
@@ -71,7 +71,11 @@ class MockFileSystem(object):
raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT))
def _split(self, path):
- return path.rsplit(self.sep, 1)
+ # This is not quite a full implementation of os.path.split
+ # http://docs.python.org/library/os.path.html#os.path.split
+ if self.sep in path:
+ return path.rsplit(self.sep, 1)
+ return ('', path)
def abspath(self, path):
if os.path.isabs(path):
@@ -169,40 +173,45 @@ class MockFileSystem(object):
return path in self.files and self.files[path] is not None
def isdir(self, path):
- if path in self.files:
- return False
- path = self.normpath(path)
- if path in self.dirs:
- return True
+ return self.normpath(path) in self.dirs
- # We need to use a copy of the keys here in order to avoid switching
- # to a different thread and potentially modifying the dict in
- # mid-iteration.
- files = self.files.keys()[:]
- result = any(f.startswith(path) and len(self.split(f)[0]) >= len(path) for f in files)
- if result:
- self.dirs.add(path)
- return result
+ def _slow_but_correct_join(self, *comps):
+ return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps))
def join(self, *comps):
- # FIXME: might want tests for this and/or a better comment about how
- # it works.
- return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps))
+ # This function is called a lot, so we optimize it; there are
+ # unittests to check that we match _slow_but_correct_join(), above.
+ path = ''
+ sep = self.sep
+ for comp in comps:
+ if not comp:
+ continue
+ if comp[0] == sep:
+ path = comp
+ continue
+ if path:
+ path += sep
+ path += comp
+ if comps[-1] == '' and path:
+ path += '/'
+ path = path.replace(sep + sep, sep)
+ return path
def listdir(self, path):
+ sep = self.sep
if not self.isdir(path):
raise OSError("%s is not a directory" % path)
- if not path.endswith(self.sep):
- path += self.sep
+ if not path.endswith(sep):
+ path += sep
dirs = []
files = []
for f in self.files:
if self.exists(f) and f.startswith(path):
remaining = f[len(path):]
- if self.sep in remaining:
- dir = remaining[:remaining.index(self.sep)]
+ if sep in remaining:
+ dir = remaining[:remaining.index(sep)]
if not dir in dirs:
dirs.append(dir)
else:
@@ -258,11 +267,27 @@ class MockFileSystem(object):
self.files[source] = None
self.written_files[source] = None
- def normpath(self, path):
- # Like join(), relies on os.path functionality but normalizes the
- # path separator to the mock one.
+ def _slow_but_correct_normpath(self, path):
return re.sub(re.escape(os.path.sep), self.sep, os.path.normpath(path))
+ def normpath(self, path):
+ # This function is called a lot, so we try to optimize the common cases
+ # instead of always calling _slow_but_correct_normpath(), above.
+ if '..' in path:
+ # This doesn't happen very often; don't bother trying to optimize it.
+ return self._slow_but_correct_normpath(path)
+ if not path:
+ return '.'
+ if path == '/':
+ return path
+ if path == '/.':
+ return '/'
+ if path.endswith('/.'):
+ return path[:-2]
+ if path.endswith('/'):
+ return path[:-1]
+ return path
+
def open_binary_tempfile(self, suffix=''):
path = self._mktemp(suffix)
return (WritableBinaryFileObject(self, path), path)
@@ -280,6 +305,7 @@ class MockFileSystem(object):
def write_binary_file(self, path, contents):
# FIXME: should this assert if dirname(path) doesn't exist?
+ self.maybe_make_directory(self.dirname(path))
self.files[path] = contents
self.written_files[path] = contents
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem_mock_unittest.py b/Tools/Scripts/webkitpy/common/system/filesystem_mock_unittest.py
index 5169fc598..2a6ccbf4b 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem_mock_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem_mock_unittest.py
@@ -26,8 +26,11 @@
# (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 os
+import re
import unittest
+
from webkitpy.common.system import filesystem_mock
from webkitpy.common.system import filesystem_unittest
@@ -41,6 +44,45 @@ class MockFileSystemTest(unittest.TestCase, filesystem_unittest.GenericFileSyste
self.teardown_generic_test_dir()
self.fs = None
+ def quick_check(self, test_fn, good_fn, *tests):
+ for test in tests:
+ if hasattr(test, '__iter__'):
+ expected = good_fn(*test)
+ actual = test_fn(*test)
+ else:
+ expected = good_fn(test)
+ actual = test_fn(test)
+ self.assertEquals(expected, actual, 'given %s, expected %s, got %s' % (repr(test), repr(expected), repr(actual)))
+
+ def test_join(self):
+ self.quick_check(self.fs.join,
+ self.fs._slow_but_correct_join,
+ ('',),
+ ('', 'bar'),
+ ('foo',),
+ ('foo/',),
+ ('foo', ''),
+ ('foo/', ''),
+ ('foo', 'bar'),
+ ('foo', '/bar'),
+ )
+
+ def test_normpath(self):
+ self.quick_check(self.fs.normpath,
+ self.fs._slow_but_correct_normpath,
+ '',
+ '/',
+ '.',
+ '/.',
+ 'foo',
+ 'foo/',
+ 'foo/.',
+ 'foo/bar',
+ '/foo',
+ 'foo/../bar',
+ 'foo/../bar/baz',
+ '../foo')
+
if __name__ == '__main__':
unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py b/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
index 680140680..16a74cbd7 100644
--- a/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
@@ -21,7 +21,6 @@
# (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 os
import shutil
import tempfile
import unittest
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py b/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
index 7113bcb6a..150a50a4d 100644
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
@@ -752,22 +752,29 @@ class Manager(object):
num_workers = min(int(self._options.child_processes), len(all_shards))
self._log_num_workers(num_workers, len(all_shards), len(locked_shards))
- manager_connection = manager_worker_broker.get(self._port, self._options, self, worker.Worker)
+ manager_connection = manager_worker_broker.get(self._options.worker_model, self, worker.Worker)
if self._options.dry_run:
return (keyboard_interrupted, interrupted, thread_timings, self._group_stats, self._all_results)
self._printer.print_update('Starting %s ...' % grammar.pluralize('worker', num_workers))
for worker_number in xrange(num_workers):
- worker_connection = manager_connection.start_worker(worker_number, self.results_directory())
+ worker_arguments = worker.WorkerArguments(worker_number, self.results_directory(), self._options)
+ worker_connection = manager_connection.start_worker(worker_arguments)
+ if self._options.worker_model == 'inline':
+ # FIXME: We need to be able to share a port with the work so
+ # that some of the tests can query state on the port; ideally
+ # we'd rewrite the tests so that this wasn't necessary.
+ #
+ # Note that this only works because in the inline case
+ # the worker hasn't really started yet and won't start
+ # running until we call run_message_loop(), below.
+ worker_connection.set_inline_arguments(self._port)
+
worker_state = _WorkerState(worker_number, worker_connection)
- self._worker_states[worker_connection.name] = worker_state
+ self._worker_states[worker_connection.name()] = worker_state
- # FIXME: If we start workers up too quickly, DumpRenderTree appears
- # to thrash on something and time out its first few tests. Until
- # we can figure out what's going on, sleep a bit in between
- # workers. This needs a bug filed.
- time.sleep(0.1)
+ time.sleep(self._port.worker_startup_delay_secs())
self._printer.print_update("Starting testing ...")
for shard in all_shards:
@@ -827,20 +834,6 @@ class Manager(object):
def update(self):
self.update_summary(self._current_result_summary)
- def _collect_timing_info(self, threads):
- test_timings = {}
- individual_test_timings = []
- thread_timings = []
-
- for thread in threads:
- thread_timings.append({'name': thread.getName(),
- 'num_tests': thread.get_num_tests(),
- 'total_time': thread.get_total_time()})
- test_timings.update(thread.get_test_group_timing_stats())
- individual_test_timings.extend(thread.get_test_results())
-
- return (thread_timings, test_timings, individual_test_timings)
-
def needs_servers(self):
return any(self._test_requires_lock(test_name) for test_name in self._test_files) and self._options.http
@@ -867,7 +860,7 @@ class Manager(object):
self._clobber_old_results()
# Create the output directory if it doesn't already exist.
- self._port.maybe_make_directory(self._results_directory)
+ self._port.host.filesystem.maybe_make_directory(self._results_directory)
self._port.setup_test_run()
@@ -1519,7 +1512,7 @@ class _WorkerState(object):
self.current_test_name = None
self.next_timeout = None
self.stats = {}
- self.stats['name'] = worker_connection.name
+ self.stats['name'] = worker_connection.name()
self.stats['num_tests'] = 0
self.stats['total_time'] = 0
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker.py b/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker.py
index 34136ff37..70c43a6d4 100755
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker.py
+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker.py
@@ -38,25 +38,48 @@ They interact more or less like:
Manager --> _InlineManager ---> _InlineWorker <-> Worker
^ \ / ^
| v v |
- \-------------------- MessageBroker -------------/
+ \----------------------- Broker ----------------/
+
+The broker simply distributes messages onto topics (named queues); the actual
+queues themselves are provided by the caller, as the queue's implementation
+requirements varies vary depending on the desired concurrency model
+(none/threads/processes).
+
+In order for shared-nothing messaging between processing to be possible,
+Messages must be picklable.
+
+The module defines one interface and two classes. Callers of this package
+must implement the BrokerClient interface, and most callers will create
+_BrokerConnections as well as Brokers.
+
+The classes relate to each other as:
+
+ BrokerClient ------> _BrokerConnection
+ ^ |
+ | v
+ \---------------- _Broker
+
+(The BrokerClient never calls broker directly after it is created, only
+_BrokerConnection. _BrokerConnection passes a reference to BrokerClient to
+_Broker, and _Broker only invokes that reference, never talking directly to
+BrokerConnection).
"""
+import cPickle
import logging
import multiprocessing
import optparse
import Queue
import sys
+import traceback
-# These are needed when workers are launched in new child processes.
-from webkitpy.common.host import Host
-from webkitpy.common.host_mock import MockHost
-from webkitpy.layout_tests.controllers import message_broker
-from webkitpy.layout_tests.views import printing
+from webkitpy.common.system import stack_utils
_log = logging.getLogger(__name__)
+
#
# Topic names for Manager <-> Worker messaging
#
@@ -64,32 +87,18 @@ MANAGER_TOPIC = 'managers'
ANY_WORKER_TOPIC = 'workers'
-def runtime_options():
- """Return a list of optparse.Option objects for any runtime values used
- by this module."""
- options = [
- optparse.make_option("--worker-model", action="store",
- help=("controls worker model. Valid values are "
- "'inline' and 'processes'.")),
- ]
- return options
-
-
-def get(port, options, client, worker_class):
+def get(worker_model, client, worker_class):
"""Return a connection to a manager/worker message_broker
Args:
- port - handle to layout_tests/port object for port-specific stuff
- options - optparse argument for command-line options
- client - message_broker.BrokerClient implementation to dispatch
+ worker_model - concurrency model to use (inline/processes)
+ client - BrokerClient implementation to dispatch
replies to.
- worker_class - type of workers to create. This class must implement
+ worker_class - type of workers to create. This class should override
the methods in AbstractWorker.
Returns:
A handle to an object that will talk to a message broker configured
- for the normal manager/worker communication.
- """
- worker_model = options.worker_model
+ for the normal manager/worker communication."""
if worker_model == 'inline':
queue_class = Queue.Queue
manager_class = _InlineManager
@@ -99,12 +108,143 @@ def get(port, options, client, worker_class):
else:
raise ValueError("unsupported value for --worker-model: %s" % worker_model)
- broker = message_broker.Broker(options, queue_class)
- return manager_class(broker, port, options, client, worker_class)
+ broker = _Broker(queue_class)
+ return manager_class(broker, client, worker_class)
+
+
+class BrokerClient(object):
+ """Abstract base class / interface that all message broker clients must
+ implement. In addition to the methods below, by convention clients
+ implement routines of the signature type
+
+ handle_MESSAGE_NAME(self, src, ...):
+
+ where MESSAGE_NAME matches the string passed to post_message(), and
+ src indicates the name of the sender. If the message contains values in
+ the message body, those will be provided as optparams."""
+
+ def is_done(self):
+ """Called from inside run_message_loop() to indicate whether to exit."""
+ raise NotImplementedError
+
+ def name(self):
+ """Return a name that identifies the client."""
+ raise NotImplementedError
+
+
+class _Broker(object):
+ """Brokers provide the basic model of a set of topics. Clients can post a
+ message to any topic using post_message(), and can process messages on one
+ topic at a time using run_message_loop()."""
+
+ def __init__(self, queue_maker):
+ """Args:
+ queue_maker: a factory method that returns objects implementing a
+ Queue interface (put()/get()).
+ """
+ self._queue_maker = queue_maker
+ self._topics = {}
+
+ def add_topic(self, topic_name):
+ if topic_name not in self._topics:
+ self._topics[topic_name] = self._queue_maker()
+
+ def _get_queue_for_topic(self, topic_name):
+ return self._topics[topic_name]
+
+ def post_message(self, client, topic_name, message_name, *message_args):
+ """Post a message to the appropriate topic name.
+
+ Messages have a name and a tuple of optional arguments. Both must be picklable."""
+ message = _Message(client.name(), topic_name, message_name, message_args)
+ queue = self._get_queue_for_topic(topic_name)
+ queue.put(_Message.dumps(message))
+
+ def run_message_loop(self, topic_name, client, delay_secs=None):
+ """Loop processing messages until client.is_done() or delay passes.
+
+ To run indefinitely, set delay_secs to None."""
+ assert delay_secs is None or delay_secs > 0
+ self._run_loop(topic_name, client, block=True, delay_secs=delay_secs)
+
+ def run_all_pending(self, topic_name, client):
+ """Process messages until client.is_done() or caller would block."""
+ self._run_loop(topic_name, client, block=False, delay_secs=None)
+
+ def _run_loop(self, topic_name, client, block, delay_secs):
+ queue = self._get_queue_for_topic(topic_name)
+ while not client.is_done():
+ try:
+ s = queue.get(block, delay_secs)
+ except Queue.Empty:
+ return
+ msg = _Message.loads(s)
+ self._dispatch_message(msg, client)
+
+ def _dispatch_message(self, message, client):
+ if not hasattr(client, 'handle_' + message.name):
+ raise ValueError(
+ "%s: received message '%s' it couldn't handle" %
+ (client.name(), message.name))
+ optargs = message.args
+ message_handler = getattr(client, 'handle_' + message.name)
+ message_handler(message.src, *optargs)
+
+
+class _Message(object):
+ @staticmethod
+ def loads(string_value):
+ obj = cPickle.loads(string_value)
+ assert(isinstance(obj, _Message))
+ return obj
+
+ def __init__(self, src, topic_name, message_name, message_args):
+ self.src = src
+ self.topic_name = topic_name
+ self.name = message_name
+ self.args = message_args
+
+ def dumps(self):
+ return cPickle.dumps(self)
+
+ def __repr__(self):
+ return ("_Message(from='%s', topic_name='%s', message_name='%s')" %
+ (self.src, self.topic_name, self.name))
+
+
+class _BrokerConnection(object):
+ """_BrokerConnection provides a connection-oriented facade on top of a
+ Broker, so that callers don't have to repeatedly pass the same topic
+ names over and over."""
+
+ def __init__(self, broker, client, run_topic, post_topic):
+ """Create a _BrokerConnection on top of a _Broker. Note that the _Broker
+ is passed in rather than created so that a single _Broker can be used
+ by multiple _BrokerConnections."""
+ self._broker = broker
+ self._client = client
+ self._post_topic = post_topic
+ self._run_topic = run_topic
+ broker.add_topic(run_topic)
+ broker.add_topic(post_topic)
+
+ def run_message_loop(self, delay_secs=None):
+ self._broker.run_message_loop(self._run_topic, self._client, delay_secs)
+
+ def post_message(self, message_name, *message_args):
+ self._broker.post_message(self._client, self._post_topic,
+ message_name, *message_args)
+
+ def raise_exception(self, exc_info):
+ # Since tracebacks aren't picklable, send the extracted stack instead.
+ exception_type, exception_value, exception_traceback = sys.exc_info()
+ stack_utils.log_traceback(_log.debug, exception_traceback)
+ stack = traceback.extract_tb(exception_traceback)
+ self._broker.post_message(self._client, self._post_topic, 'exception', exception_type, exception_value, stack)
-class AbstractWorker(message_broker.BrokerClient):
- def __init__(self, worker_connection, worker_number, results_directory, options):
+class AbstractWorker(BrokerClient):
+ def __init__(self, worker_connection, worker_arguments=None):
"""The constructor should be used to do any simple initialization
necessary, but should not do anything that creates data structures
that cannot be Pickled or sent across processes (like opening
@@ -112,59 +252,88 @@ class AbstractWorker(message_broker.BrokerClient):
start of the run() call.
Args:
- worker_connection - handle to the BrokerConnection object creating
+ worker_connection - handle to the _BrokerConnection object creating
the worker and that can be used for messaging.
- worker_number - identifier for this particular worker
- options - command-line argument object from optparse"""
- message_broker.BrokerClient.__init__(self)
+ worker_arguments - (optional, Picklable) object passed to the worker from the manager"""
+ BrokerClient.__init__(self)
self._worker_connection = worker_connection
- self._options = options
- self._worker_number = worker_number
- self._name = 'worker/%d' % worker_number
- self._results_directory = results_directory
+ self._name = 'worker'
+ self._done = False
+ self._canceled = False
+
+ def name(self):
+ return self._name
+
+ def is_done(self):
+ return self._done or self._canceled
+
+ def stop_handling_messages(self):
+ self._done = True
- def run(self, port):
+ def run(self):
"""Callback for the worker to start executing. Typically does any
remaining initialization and then calls broker_connection.run_message_loop()."""
- raise NotImplementedError
+ exception_msg = ""
+ _log.debug("%s starting" % self._name)
+
+ try:
+ self._worker_connection.run_message_loop()
+ if not self.is_done():
+ raise AssertionError("%s: ran out of messages in worker queue."
+ % self._name)
+ except KeyboardInterrupt:
+ exception_msg = ", interrupted"
+ self._worker_connection.raise_exception(sys.exc_info())
+ except:
+ exception_msg = ", exception raised"
+ self._worker_connection.raise_exception(sys.exc_info())
+ finally:
+ _log.debug("%s done with message loop%s" % (self._name, exception_msg))
def cancel(self):
"""Called when possible to indicate to the worker to stop processing
messages and shut down. Note that workers may be stopped without this
method being called, so clients should not rely solely on this."""
- raise NotImplementedError
+ self._canceled = True
-class _ManagerConnection(message_broker.BrokerConnection):
- def __init__(self, broker, options, client, worker_class):
+class _ManagerConnection(_BrokerConnection):
+ def __init__(self, broker, client, worker_class):
"""Base initialization for all Manager objects.
Args:
broker: handle to the message_broker object
- options: command line options object
client: callback object (the caller)
worker_class: class object to use to create workers.
"""
- message_broker.BrokerConnection.__init__(self, broker, client,
- MANAGER_TOPIC, ANY_WORKER_TOPIC)
- self._options = options
+ _BrokerConnection.__init__(self, broker, client, MANAGER_TOPIC, ANY_WORKER_TOPIC)
self._worker_class = worker_class
- def start_worker(self, worker_number, results_directory):
+ def start_worker(self, worker_arguments=None):
+ """Starts a new worker.
+
+ Args:
+ worker_arguments - an optional Picklable object that is passed to the worker constructor
+ """
raise NotImplementedError
class _InlineManager(_ManagerConnection):
- def __init__(self, broker, port, options, client, worker_class):
- _ManagerConnection.__init__(self, broker, options, client, worker_class)
- self._port = port
+ def __init__(self, broker, client, worker_class):
+ _ManagerConnection.__init__(self, broker, client, worker_class)
self._inline_worker = None
- def start_worker(self, worker_number, results_directory):
- self._inline_worker = _InlineWorkerConnection(self._broker, self._port,
- self._client, self._worker_class, worker_number, results_directory, self._options)
+ def start_worker(self, worker_arguments=None):
+ self._inline_worker = _InlineWorkerConnection(self._broker,
+ self._client, self._worker_class, worker_arguments)
return self._inline_worker
+ def set_inline_arguments(self, arguments=None):
+ # Note that this method only exists here, and not on all
+ # ManagerConnections; calling this method on a MultiProcessManager
+ # will deliberately result in a runtime error.
+ self._inline_worker.set_inline_arguments(arguments)
+
def run_message_loop(self, delay_secs=None):
# Note that delay_secs is ignored in this case since we can't easily
# implement it.
@@ -173,27 +342,20 @@ class _InlineManager(_ManagerConnection):
class _MultiProcessManager(_ManagerConnection):
- def __init__(self, broker, port, options, client, worker_class):
- # Note that this class does not keep a handle to the actual port
- # object, because it isn't Picklable. Instead it keeps the port
- # name and recreates the port in the child process from the name
- # and options.
- _ManagerConnection.__init__(self, broker, options, client, worker_class)
- self._platform_name = port.real_name()
-
- def start_worker(self, worker_number, results_directory):
- worker_connection = _MultiProcessWorkerConnection(self._broker, self._platform_name,
- self._worker_class, worker_number, results_directory, self._options)
+ def start_worker(self, worker_arguments=None):
+ worker_connection = _MultiProcessWorkerConnection(self._broker,
+ self._worker_class, worker_arguments)
worker_connection.start()
return worker_connection
-class _WorkerConnection(message_broker.BrokerConnection):
- def __init__(self, broker, worker_class, worker_number, results_directory, options):
- self._client = worker_class(self, worker_number, results_directory, options)
- self.name = self._client.name()
- message_broker.BrokerConnection.__init__(self, broker, self._client,
- ANY_WORKER_TOPIC, MANAGER_TOPIC)
+class _WorkerConnection(_BrokerConnection):
+ def __init__(self, broker, worker_class, worker_arguments=None):
+ self._client = worker_class(self, worker_arguments)
+ _BrokerConnection.__init__(self, broker, self._client, ANY_WORKER_TOPIC, MANAGER_TOPIC)
+
+ def name(self):
+ return self._client.name()
def cancel(self):
raise NotImplementedError
@@ -209,10 +371,9 @@ class _WorkerConnection(message_broker.BrokerConnection):
class _InlineWorkerConnection(_WorkerConnection):
- def __init__(self, broker, port, manager_client, worker_class, worker_number, results_directory, options):
- _WorkerConnection.__init__(self, broker, worker_class, worker_number, results_directory, options)
+ def __init__(self, broker, manager_client, worker_class, worker_arguments):
+ _WorkerConnection.__init__(self, broker, worker_class, worker_arguments)
self._alive = False
- self._port = port
self._manager_client = manager_client
def cancel(self):
@@ -224,10 +385,15 @@ class _InlineWorkerConnection(_WorkerConnection):
def join(self, timeout):
assert not self._alive
+ def set_inline_arguments(self, arguments):
+ self._client.set_inline_arguments(arguments)
+
def run(self):
self._alive = True
- self._client.run(self._port)
- self._alive = False
+ try:
+ self._client.run()
+ finally:
+ self._alive = False
def yield_to_broker(self):
self._broker.run_all_pending(MANAGER_TOPIC, self._manager_client)
@@ -240,43 +406,19 @@ class _InlineWorkerConnection(_WorkerConnection):
class _Process(multiprocessing.Process):
- def __init__(self, worker_connection, platform_name, options, client):
+ def __init__(self, worker_connection, client):
multiprocessing.Process.__init__(self)
self._worker_connection = worker_connection
- self._platform_name = platform_name
- self._options = options
self._client = client
def run(self):
- # We need to create a new Host object here because this is
- # running in a new process and we can't require the parent's
- # Host to be pickleable and passed to the child.
- if self._platform_name.startswith('test'):
- host = MockHost()
- else:
- host = Host()
- host._initialize_scm()
-
- options = self._options
- port_obj = host.port_factory.get(self._platform_name, options)
-
- # The unix multiprocessing implementation clones the
- # log handler configuration into the child processes,
- # but the win implementation doesn't.
- configure_logging = (sys.platform == 'win32')
-
- # FIXME: this won't work if the calling process is logging
- # somewhere other than sys.stderr and sys.stdout, but I'm not sure
- # if this will be an issue in practice.
- printer = printing.Printer(port_obj, options, sys.stderr, sys.stdout, configure_logging)
- self._client.run(port_obj)
- printer.cleanup()
+ self._client.run()
class _MultiProcessWorkerConnection(_WorkerConnection):
- def __init__(self, broker, platform_name, worker_class, worker_number, results_directory, options):
- _WorkerConnection.__init__(self, broker, worker_class, worker_number, results_directory, options)
- self._proc = _Process(self, platform_name, options, self._client)
+ def __init__(self, broker, worker_class, worker_arguments):
+ _WorkerConnection.__init__(self, broker, worker_class, worker_arguments)
+ self._proc = _Process(self, self._client)
def cancel(self):
return self._proc.terminate()
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker_unittest.py b/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker_unittest.py
index e41e22ca6..5457a2d26 100644
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/manager_worker_broker_unittest.py
@@ -32,11 +32,7 @@ import sys
import unittest
from webkitpy.common.system import outputcapture
-from webkitpy.common.host_mock import MockHost
-from webkitpy.layout_tests import port
from webkitpy.layout_tests.controllers import manager_worker_broker
-from webkitpy.layout_tests.controllers import message_broker
-from webkitpy.layout_tests.views import printing
# In order to reliably control when child workers are starting and stopping,
@@ -48,68 +44,45 @@ starting_queue = None
stopping_queue = None
+WORKER_NAME = 'TestWorker'
+
def make_broker(manager, worker_model, start_queue=None, stop_queue=None):
global starting_queue
global stopping_queue
starting_queue = start_queue
stopping_queue = stop_queue
- options = get_options(worker_model)
- host = MockHost()
- test_port = host.port_factory.get("test")
- return manager_worker_broker.get(test_port, options, manager, _TestWorker)
+ return manager_worker_broker.get(worker_model, manager, _TestWorker)
class _TestWorker(manager_worker_broker.AbstractWorker):
- def __init__(self, broker_connection, worker_number, results_directory, options):
- self._broker_connection = broker_connection
- self._options = options
- self._worker_number = worker_number
- self._name = 'TestWorker/%d' % worker_number
- self._stopped = False
- self._canceled = False
+ def __init__(self, worker_connection, worker_arguments=None):
+ super(_TestWorker, self).__init__(worker_connection)
+ self._name = WORKER_NAME
+ self._thing_to_greet = 'everybody'
self._starting_queue = starting_queue
self._stopping_queue = stopping_queue
+ def set_inline_arguments(self, thing_to_greet):
+ self._thing_to_greet = thing_to_greet
+
def handle_stop(self, src):
- self._stopped = True
+ self.stop_handling_messages()
def handle_test(self, src, an_int, a_str):
assert an_int == 1
assert a_str == "hello, world"
- self._broker_connection.post_message('test', 2, 'hi, everybody')
-
- def is_done(self):
- return self._stopped or self._canceled
-
- def name(self):
- return self._name
-
- def cancel(self):
- self._canceled = True
+ self._worker_connection.post_message('test', 2, 'hi, ' + self._thing_to_greet)
- def run(self, port):
+ def run(self):
if self._starting_queue:
self._starting_queue.put('')
if self._stopping_queue:
self._stopping_queue.get()
try:
- self._broker_connection.run_message_loop()
- self._broker_connection.yield_to_broker()
- self._broker_connection.post_message('done')
- except Exception, e:
- self._broker_connection.post_message('exception', (type(e), str(e), None))
-
-
-def get_options(worker_model):
- option_list = (manager_worker_broker.runtime_options() +
- printing.print_options() +
- [optparse.make_option("--experimental-fully-parallel", default=False),
- optparse.make_option("--child-processes", default='2')])
- parser = optparse.OptionParser(option_list=option_list)
- options, args = parser.parse_args(args=['--worker-model', worker_model])
- return options
-
+ super(_TestWorker, self).run()
+ finally:
+ self._worker_connection.post_message('done')
class FunctionTests(unittest.TestCase):
@@ -131,7 +104,7 @@ class _TestsMixin(object):
contract all implementations must follow."""
def name(self):
- return 'Tester'
+ return 'TesterManager'
def is_done(self):
return self._done
@@ -143,9 +116,8 @@ class _TestsMixin(object):
self._an_int = an_int
self._a_str = a_str
- def handle_exception(self, src, exc_info):
- self._exception = exc_info
- self._done = True
+ def handle_exception(self, src, exception_type, exception_value, stack):
+ raise exception_type(exception_value)
def setUp(self):
self._an_int = None
@@ -159,17 +131,25 @@ class _TestsMixin(object):
self._broker = make_broker(self, self._worker_model, starting_queue,
stopping_queue)
+ def test_name(self):
+ self.make_broker()
+ worker = self._broker.start_worker()
+ self.assertEquals(worker.name(), WORKER_NAME)
+ worker.cancel()
+ worker.join(0.1)
+ self.assertFalse(worker.is_alive())
+
def test_cancel(self):
self.make_broker()
- worker = self._broker.start_worker(0, None)
+ worker = self._broker.start_worker()
worker.cancel()
self._broker.post_message('test', 1, 'hello, world')
- worker.join(0.5)
+ worker.join(0.1)
self.assertFalse(worker.is_alive())
def test_done(self):
self.make_broker()
- worker = self._broker.start_worker(0, None)
+ worker = self._broker.start_worker()
self._broker.post_message('test', 1, 'hello, world')
self._broker.post_message('stop')
self._broker.run_message_loop()
@@ -181,16 +161,32 @@ class _TestsMixin(object):
def test_unknown_message(self):
self.make_broker()
- worker = self._broker.start_worker(0, None)
+ worker = self._broker.start_worker()
self._broker.post_message('unknown')
- self._broker.run_message_loop()
- worker.join(0.5)
-
- self.assertTrue(self.is_done())
+ try:
+ self._broker.run_message_loop()
+ self.fail()
+ except ValueError, e:
+ self.assertEquals(str(e),
+ "%s: received message 'unknown' it couldn't handle" % WORKER_NAME)
+ finally:
+ worker.join(0.5)
self.assertFalse(worker.is_alive())
- self.assertEquals(self._exception[0], ValueError)
- self.assertEquals(self._exception[1],
- "TestWorker/0: received message 'unknown' it couldn't handle")
+
+
+class InlineBrokerTests(_TestsMixin, unittest.TestCase):
+ def setUp(self):
+ _TestsMixin.setUp(self)
+ self._worker_model = 'inline'
+
+ def test_inline_arguments(self):
+ self.make_broker()
+ worker = self._broker.start_worker()
+ worker.set_inline_arguments('me')
+ self._broker.post_message('test', 1, 'hello, world')
+ self._broker.post_message('stop')
+ self._broker.run_message_loop()
+ self.assertEquals(self._a_str, 'hi, me')
# FIXME: https://bugs.webkit.org/show_bug.cgi?id=54520.
@@ -202,35 +198,57 @@ if sys.platform not in ('cygwin', 'win32'):
self._worker_model = 'processes'
-class FunctionsTest(unittest.TestCase):
- def test_runtime_options(self):
- option_list = manager_worker_broker.runtime_options()
- parser = optparse.OptionParser(option_list=option_list)
- options, args = parser.parse_args([])
- self.assertTrue(options)
-
-
class InterfaceTest(unittest.TestCase):
# These tests mostly exist to pacify coverage.
# FIXME: There must be a better way to do this and also verify
# that classes do implement every abstract method in an interface.
+ def test_brokerclient_is_abstract(self):
+ # Test that all the base class methods are abstract and have the
+ # signature we expect.
+ obj = manager_worker_broker.BrokerClient()
+ self.assertRaises(NotImplementedError, obj.is_done)
+ self.assertRaises(NotImplementedError, obj.name)
+
def test_managerconnection_is_abstract(self):
# Test that all the base class methods are abstract and have the
# signature we expect.
broker = make_broker(self, 'inline')
- obj = manager_worker_broker._ManagerConnection(broker._broker, None, self, None)
- self.assertRaises(NotImplementedError, obj.start_worker, 0, None)
+ obj = manager_worker_broker._ManagerConnection(broker._broker, self, None)
+ self.assertRaises(NotImplementedError, obj.start_worker)
def test_workerconnection_is_abstract(self):
# Test that all the base class methods are abstract and have the
# signature we expect.
broker = make_broker(self, 'inline')
- obj = manager_worker_broker._WorkerConnection(broker._broker, _TestWorker, 0, None, None)
+ obj = manager_worker_broker._WorkerConnection(broker._broker, _TestWorker, None)
self.assertRaises(NotImplementedError, obj.cancel)
self.assertRaises(NotImplementedError, obj.is_alive)
self.assertRaises(NotImplementedError, obj.join, None)
+class MessageTest(unittest.TestCase):
+ def test__no_body(self):
+ msg = manager_worker_broker._Message('src', 'topic_name', 'message_name', None)
+ self.assertTrue(repr(msg))
+ s = msg.dumps()
+ new_msg = manager_worker_broker._Message.loads(s)
+ self.assertEqual(new_msg.name, 'message_name')
+ self.assertEqual(new_msg.args, None)
+ self.assertEqual(new_msg.topic_name, 'topic_name')
+ self.assertEqual(new_msg.src, 'src')
+
+ def test__body(self):
+ msg = manager_worker_broker._Message('src', 'topic_name', 'message_name', ('body', 0))
+ self.assertTrue(repr(msg))
+ s = msg.dumps()
+ new_msg = manager_worker_broker._Message.loads(s)
+ self.assertEqual(new_msg.name, 'message_name')
+ self.assertEqual(new_msg.args, ('body', 0))
+ self.assertEqual(new_msg.topic_name, 'topic_name')
+ self.assertEqual(new_msg.src, 'src')
+
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/message_broker.py b/Tools/Scripts/webkitpy/layout_tests/controllers/message_broker.py
deleted file mode 100644
index d58a6cd11..000000000
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/message_broker.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# Copyright (C) 2011 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 name of Google Inc. 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.
-
-"""Module for handling messaging for run-webkit-tests.
-
-This module implements a simple message broker abstraction that will be
-used to coordinate messages between the main run-webkit-tests thread
-(aka TestRunner) and the individual worker threads (previously known as
-dump_render_tree_threads).
-
-The broker simply distributes messages onto topics (named queues); the actual
-queues themselves are provided by the caller, as the queue's implementation
-requirements varies vary depending on the desired concurrency model
-(none/threads/processes).
-
-In order for shared-nothing messaging between processing to be possible,
-Messages must be picklable.
-
-The module defines one interface and two classes. Callers of this package
-must implement the BrokerClient interface, and most callers will create
-BrokerConnections as well as Brokers.
-
-The classes relate to each other as:
-
- BrokerClient ------> BrokerConnection
- ^ |
- | v
- \---------------- Broker
-
-(The BrokerClient never calls broker directly after it is created, only
-BrokerConnection. BrokerConnection passes a reference to BrokerClient to
-Broker, and Broker only invokes that reference, never talking directly to
-BrokerConnection).
-"""
-import sys
-import traceback
-
-import cPickle
-import logging
-import Queue
-
-from webkitpy.common.system import stack_utils
-
-_log = logging.getLogger(__name__)
-
-
-class BrokerClient(object):
- """Abstract base class / interface that all message broker clients must
- implement. In addition to the methods below, by convention clients
- implement routines of the signature type
-
- handle_MESSAGE_NAME(self, src, ...):
-
- where MESSAGE_NAME matches the string passed to post_message(), and
- src indicates the name of the sender. If the message contains values in
- the message body, those will be provided as optparams."""
-
- def is_done(self):
- """Called from inside run_message_loop() to indicate whether to exit."""
- raise NotImplementedError
-
- def name(self):
- """Return a name that identifies the client."""
- raise NotImplementedError
-
-
-class Broker(object):
- """Brokers provide the basic model of a set of topics. Clients can post a
- message to any topic using post_message(), and can process messages on one
- topic at a time using run_message_loop()."""
-
- def __init__(self, options, queue_maker):
- """Args:
- options: a runtime option class from optparse
- queue_maker: a factory method that returns objects implementing a
- Queue interface (put()/get()).
- """
- self._options = options
- self._queue_maker = queue_maker
- self._topics = {}
-
- def add_topic(self, topic_name):
- if topic_name not in self._topics:
- self._topics[topic_name] = self._queue_maker()
-
- def _get_queue_for_topic(self, topic_name):
- return self._topics[topic_name]
-
- def post_message(self, client, topic_name, message_name, *message_args):
- """Post a message to the appropriate topic name.
-
- Messages have a name and a tuple of optional arguments. Both must be picklable."""
- message = _Message(client.name(), topic_name, message_name, message_args)
- queue = self._get_queue_for_topic(topic_name)
- queue.put(_Message.dumps(message))
-
- def run_message_loop(self, topic_name, client, delay_secs=None):
- """Loop processing messages until client.is_done() or delay passes.
-
- To run indefinitely, set delay_secs to None."""
- assert delay_secs is None or delay_secs > 0
- self._run_loop(topic_name, client, block=True, delay_secs=delay_secs)
-
- def run_all_pending(self, topic_name, client):
- """Process messages until client.is_done() or caller would block."""
- self._run_loop(topic_name, client, block=False, delay_secs=None)
-
- def _run_loop(self, topic_name, client, block, delay_secs):
- queue = self._get_queue_for_topic(topic_name)
- while not client.is_done():
- try:
- s = queue.get(block, delay_secs)
- except Queue.Empty:
- return
- msg = _Message.loads(s)
- self._dispatch_message(msg, client)
-
- def _dispatch_message(self, message, client):
- if not hasattr(client, 'handle_' + message.name):
- raise ValueError(
- "%s: received message '%s' it couldn't handle" %
- (client.name(), message.name))
- optargs = message.args
- message_handler = getattr(client, 'handle_' + message.name)
- message_handler(message.src, *optargs)
-
-
-class _Message(object):
- @staticmethod
- def loads(string_value):
- obj = cPickle.loads(string_value)
- assert(isinstance(obj, _Message))
- return obj
-
- def __init__(self, src, topic_name, message_name, message_args):
- self.src = src
- self.topic_name = topic_name
- self.name = message_name
- self.args = message_args
-
- def dumps(self):
- return cPickle.dumps(self)
-
- def __repr__(self):
- return ("_Message(from='%s', topic_name='%s', message_name='%s')" %
- (self.src, self.topic_name, self.name))
-
-
-class BrokerConnection(object):
- """BrokerConnection provides a connection-oriented facade on top of a
- Broker, so that callers don't have to repeatedly pass the same topic
- names over and over."""
-
- def __init__(self, broker, client, run_topic, post_topic):
- """Create a BrokerConnection on top of a Broker. Note that the Broker
- is passed in rather than created so that a single Broker can be used
- by multiple BrokerConnections."""
- self._broker = broker
- self._client = client
- self._post_topic = post_topic
- self._run_topic = run_topic
- broker.add_topic(run_topic)
- broker.add_topic(post_topic)
-
- def run_message_loop(self, delay_secs=None):
- self._broker.run_message_loop(self._run_topic, self._client, delay_secs)
-
- def post_message(self, message_name, *message_args):
- self._broker.post_message(self._client, self._post_topic,
- message_name, *message_args)
-
- def raise_exception(self, exc_info):
- # Since tracebacks aren't picklable, send the extracted stack instead.
- exception_type, exception_value, exception_traceback = sys.exc_info()
- stack_utils.log_traceback(_log.debug, exception_traceback)
- stack = traceback.extract_tb(exception_traceback)
- self._broker.post_message(self._client, self._post_topic, 'exception', exception_type, exception_value, stack)
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/message_broker_unittest.py b/Tools/Scripts/webkitpy/layout_tests/controllers/message_broker_unittest.py
deleted file mode 100644
index cb8d8e6f9..000000000
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/message_broker_unittest.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# Copyright (C) 2011 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 name of Google Inc. 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.
-
-import unittest
-
-from webkitpy.layout_tests.controllers import message_broker
-
-# This file exists to test routines that aren't necessarily covered elsewhere;
-# most of the testing of message_broker will be covered under the tests in
-# the manager_worker_broker module.
-
-
-class MessageTest(unittest.TestCase):
- def test__no_body(self):
- msg = message_broker._Message('src', 'topic_name', 'message_name', None)
- self.assertTrue(repr(msg))
- s = msg.dumps()
- new_msg = message_broker._Message.loads(s)
- self.assertEqual(new_msg.name, 'message_name')
- self.assertEqual(new_msg.args, None)
- self.assertEqual(new_msg.topic_name, 'topic_name')
- self.assertEqual(new_msg.src, 'src')
-
- def test__body(self):
- msg = message_broker._Message('src', 'topic_name', 'message_name',
- ('body', 0))
- self.assertTrue(repr(msg))
- s = msg.dumps()
- new_msg = message_broker._Message.loads(s)
- self.assertEqual(new_msg.name, 'message_name')
- self.assertEqual(new_msg.args, ('body', 0))
- self.assertEqual(new_msg.topic_name, 'topic_name')
- self.assertEqual(new_msg.src, 'src')
-
-
-class InterfaceTest(unittest.TestCase):
- # These tests mostly exist to pacify coverage.
-
- # FIXME: There must be a better way to do this and also verify
- # that classes do implement every abstract method in an interface.
-
- def test_brokerclient_is_abstract(self):
- # Test that all the base class methods are abstract and have the
- # signature we expect.
- obj = message_broker.BrokerClient()
- self.assertRaises(NotImplementedError, obj.is_done)
- self.assertRaises(NotImplementedError, obj.name)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py b/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
index 987a77978..38f75bdac 100644
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
@@ -224,7 +224,7 @@ class SingleTestRunner:
failures = []
if (expected_text and actual_text and
# Assuming expected_text is already normalized.
- self._port.compare_text(self._get_normalized_output_text(actual_text), expected_text)):
+ self._port.do_text_results_differ(self._get_normalized_output_text(actual_text), expected_text)):
failures.append(test_failures.FailureTextMismatch())
elif actual_text and not expected_text:
failures.append(test_failures.FailureMissingResult())
@@ -233,7 +233,7 @@ class SingleTestRunner:
def _compare_audio(self, actual_audio, expected_audio):
failures = []
if (expected_audio and actual_audio and
- self._port.compare_audio(actual_audio, expected_audio)):
+ self._port.do_audio_results_differ(actual_audio, expected_audio)):
failures.append(test_failures.FailureAudioMismatch())
elif actual_audio and not expected_audio:
failures.append(test_failures.FailureMissingAudio())
@@ -264,6 +264,9 @@ class SingleTestRunner:
driver_output.image_diff = diff_result[0]
if driver_output.image_diff:
failures.append(test_failures.FailureImageHashMismatch(diff_result[1]))
+ else:
+ # See https://bugs.webkit.org/show_bug.cgi?id=69444 for why this isn't a full failure.
+ _log.warning('%s -> pixel hash failed (but pixel test still passes)' % self._test_name)
return failures
def _run_reftest(self):
@@ -272,8 +275,8 @@ class SingleTestRunner:
reference_output = None
test_result = None
- # A reftest can have multiple match references and multiple mismatch references;
- # the test fails if any mismatch matches and all of the matches don't match.
+ # A reftest can have multiple match references and multiple mismatch references;
+ # the test fails if any mismatch matches and all of the matches don't match.
# To minimize the number of references we have to check, we run all of the mismatches first,
# then the matches, and short-circuit out as soon as we can.
# Note that sorting by the expectation sorts "!=" before "==" so this is easy to do.
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py b/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py
index 88e2ab691..a40c090b5 100644
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py
+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/test_result_writer.py
@@ -28,7 +28,6 @@
import logging
-import os
from webkitpy.common.system.crashlogs import CrashLogs
from webkitpy.layout_tests.models import test_failures
@@ -106,7 +105,7 @@ class TestResultWriter(object):
"""Creates the output directory (if needed) for a given test filename."""
fs = self._port._filesystem
output_filename = fs.join(self._root_output_dir, self._test_name)
- self._port.maybe_make_directory(fs.dirname(output_filename))
+ fs.maybe_make_directory(fs.dirname(output_filename))
def output_filename(self, modifier):
"""Returns a filename inside the output dir that contains modifier.
diff --git a/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py b/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py
index fc7831dfb..22c0a5f04 100644
--- a/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py
+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py
@@ -33,20 +33,31 @@ import sys
import threading
import time
+from webkitpy.common.host import Host
from webkitpy.layout_tests.controllers import manager_worker_broker
from webkitpy.layout_tests.controllers import single_test_runner
from webkitpy.layout_tests.models import test_expectations
from webkitpy.layout_tests.models import test_results
+from webkitpy.layout_tests.views import printing
_log = logging.getLogger(__name__)
+class WorkerArguments(object):
+ def __init__(self, worker_number, results_directory, options):
+ self.worker_number = worker_number
+ self.results_directory = results_directory
+ self.options = options
+
+
class Worker(manager_worker_broker.AbstractWorker):
- def __init__(self, worker_connection, worker_number, results_directory, options):
- manager_worker_broker.AbstractWorker.__init__(self, worker_connection, worker_number, results_directory, options)
- self._done = False
- self._canceled = False
+ def __init__(self, worker_connection, worker_arguments):
+ super(Worker, self).__init__(worker_connection, worker_arguments)
+ self._worker_number = worker_arguments.worker_number
+ self._name = 'worker/%d' % self._worker_number
+ self._results_directory = worker_arguments.results_directory
+ self._options = worker_arguments.options
self._port = None
self._batch_size = None
self._batch_count = None
@@ -54,52 +65,55 @@ class Worker(manager_worker_broker.AbstractWorker):
self._driver = None
self._tests_run_file = None
self._tests_run_filename = None
+ self._printer = None
def __del__(self):
self.cleanup()
- def safe_init(self, port):
+ def safe_init(self):
"""This method should only be called when it is is safe for the mixin
to create state that can't be Pickled.
This routine exists so that the mixin can be created and then marshaled
across into a child process."""
- self._port = port
- self._filesystem = port.host.filesystem
+ self._filesystem = self._port.host.filesystem
self._batch_count = 0
self._batch_size = self._options.batch_size or 0
tests_run_filename = self._filesystem.join(self._results_directory, "tests_run%d.txt" % self._worker_number)
self._tests_run_file = self._filesystem.open_text_file_for_writing(tests_run_filename)
- def cancel(self):
- """Attempt to abort processing (best effort)."""
- self._canceled = True
+ def set_inline_arguments(self, port):
+ self._port = port
+
+ def run(self):
+ if not self._port:
+ # We are running in a child process and need to create a new Host.
+ if self._options.platform and 'test' in self._options.platform:
+ # It is lame to import mocks into real code, but this allows us to use the test port in multi-process tests as well.
+ from webkitpy.common.host_mock import MockHost
+ host = MockHost()
+ else:
+ host = Host()
- def is_done(self):
- return self._done or self._canceled
+ options = self._options
+ self._port = host.port_factory.get(options.platform, options)
- def name(self):
- return self._name
+ # The unix multiprocessing implementation clones the
+ # log handler configuration into the child processes,
+ # but the win implementation doesn't.
+ configure_logging = (sys.platform == 'win32')
- def run(self, port):
- self.safe_init(port)
+ # FIXME: This won't work if the calling process is logging
+ # somewhere other than sys.stderr and sys.stdout, but I'm not sure
+ # if this will be an issue in practice.
+ self._printer = printing.Printer(self._port, options, sys.stderr, sys.stdout, configure_logging)
- exception_msg = ""
- _log.debug("%s starting" % self._name)
+ self.safe_init()
try:
- self._worker_connection.run_message_loop()
- if not self.is_done():
- raise AssertionError("%s: ran out of messages in worker queue."
- % self._name)
- except KeyboardInterrupt:
- exception_msg = ", interrupted"
- self._worker_connection.raise_exception(sys.exc_info())
- except:
- exception_msg = ", exception raised"
- self._worker_connection.raise_exception(sys.exc_info())
+ _log.debug("%s starting" % self._name)
+ super(Worker, self).run()
finally:
- _log.debug("%s done with message loop%s" % (self._name, exception_msg))
self._worker_connection.post_message('done')
self.cleanup()
_log.debug("%s exiting" % self._name)
@@ -119,7 +133,7 @@ class Worker(manager_worker_broker.AbstractWorker):
self._worker_connection.post_message('finished_list', list_name, num_tests, elapsed_time)
def handle_stop(self, src):
- self._done = True
+ self.stop_handling_messages()
def _run_test(self, test_input):
test_timeout_sec = self.timeout(test_input)
@@ -139,6 +153,9 @@ class Worker(manager_worker_broker.AbstractWorker):
if self._tests_run_file:
self._tests_run_file.close()
self._tests_run_file = None
+ if self._printer:
+ self._printer.cleanup()
+ self._printer = None
def timeout(self, test_input):
"""Compute the appropriate timeout value for a test."""
diff --git a/Tools/Scripts/webkitpy/common/array_stream_unittest.py b/Tools/Scripts/webkitpy/layout_tests/controllers/worker_unittest.py
index 1a9b34a17..6fd5202d9 100644
--- a/Tools/Scripts/webkitpy/common/array_stream_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/controllers/worker_unittest.py
@@ -1,5 +1,4 @@
-#!/usr/bin/python
-# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (c) 2012 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
@@ -27,52 +26,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.
-"""Unit tests for array_stream.py."""
-
-import pdb
import unittest
-from webkitpy.common.array_stream import ArrayStream
-
-
-class ArrayStreamTest(unittest.TestCase):
- def assertEmpty(self, a_stream):
- self.assertTrue(a_stream.empty())
-
- def assertNotEmpty(self, a_stream):
- self.assertFalse(a_stream.empty())
-
- def assertContentsMatch(self, a_stream, contents):
- self.assertEquals(a_stream.get(), contents)
-
- def test_basics(self):
- a = ArrayStream()
- self.assertEmpty(a)
- self.assertContentsMatch(a, [])
+from webkitpy.layout_tests.controllers.worker import Worker, WorkerArguments
+from webkitpy.tool.mocktool import MockOptions
- a.flush()
- self.assertEmpty(a)
- self.assertContentsMatch(a, [])
- a.write("foo")
- a.write("bar")
- self.assertNotEmpty(a)
- self.assertContentsMatch(a, ["foo", "bar"])
+class FakeConnection(object):
+ def run_message_loop(self):
+ pass
- a.flush()
- self.assertNotEmpty(a)
- self.assertContentsMatch(a, ["foo", "bar"])
+ def post_message(self, message_name, *message_args):
+ pass
- a.reset()
- self.assertEmpty(a)
- self.assertContentsMatch(a, [])
- self.assertEquals(str(a), "<ArrayStream: []>")
+class WorkerTest(unittest.TestCase):
+ def test_default_platform_in_worker(self):
+ # This checks that we got a port and didn't raise an exception
+ # if we didn't specify a port with the --platform flag.
+ worker_connection = FakeConnection()
+ worker = Worker(worker_connection, WorkerArguments(1, '/tmp', MockOptions(platform=None, print_options=None, verbose=False, batch_size=0)))
+ worker._done = True
+ worker.run()
+ self.assertNotEquals(worker._port, None)
- a.write("foo")
- self.assertNotEmpty(a)
- self.assertContentsMatch(a, ["foo"])
- self.assertEquals(str(a), "<ArrayStream: ['foo']>")
if __name__ == '__main__':
unittest.main()
diff --git a/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py b/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
index 56ef7abcd..4c4858b3b 100644
--- a/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
+++ b/Tools/Scripts/webkitpy/layout_tests/layout_package/json_results_generator.py
@@ -28,7 +28,6 @@
import json
import logging
-import os
import subprocess
import sys
import time
diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
index 90842007c..ea4837d93 100644
--- a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
+++ b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations.py
@@ -182,7 +182,7 @@ class TestExpectationParser(object):
BUG_MODIFIER_PREFIX = 'bug'
BUG_MODIFIER_REGEX = 'bug\d+'
REBASELINE_MODIFIER = 'rebaseline'
- FAIL_EXPECTATION = 'fail'
+ PASS_EXPECTATION = 'pass'
SKIP_MODIFIER = 'skip'
SLOW_MODIFIER = 'slow'
WONTFIX_MODIFIER = 'wontfix'
@@ -206,7 +206,7 @@ class TestExpectationParser(object):
expectation_line.original_string = test_name
expectation_line.modifiers = [TestExpectationParser.DUMMY_BUG_MODIFIER, TestExpectationParser.SKIP_MODIFIER]
expectation_line.name = test_name
- expectation_line.expectations = [TestExpectationParser.FAIL_EXPECTATION]
+ expectation_line.expectations = [TestExpectationParser.PASS_EXPECTATION]
self._parse_line(expectation_line)
return expectation_line
diff --git a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py
index a2c94cbcd..00f1bfd4c 100644
--- a/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/models/test_expectations_unittest.py
@@ -272,7 +272,7 @@ SKIP : failures/expected/image.html""", is_lint_mode=True)
port._filesystem.files[port._filesystem.join(port.layout_tests_dir(), 'failures/expected/text.html')] = 'foo'
expectations = TestExpectations(port, tests=['failures/expected/text.html'], expectations='', test_config=port.test_configuration())
self.assertEquals(expectations.get_modifiers('failures/expected/text.html'), [TestExpectationParser.DUMMY_BUG_MODIFIER, TestExpectationParser.SKIP_MODIFIER])
- self.assertEquals(expectations.get_expectations('failures/expected/text.html'), set([FAIL]))
+ self.assertEquals(expectations.get_expectations('failures/expected/text.html'), set([PASS]))
def test_add_skipped_tests_duplicate(self):
port = MockHost().port_factory.get('qt')
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/base.py b/Tools/Scripts/webkitpy/layout_tests/port/base.py
index 10e2a822c..1b86bfe0d 100755
--- a/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -169,6 +169,13 @@ class Port(object):
def default_worker_model(self):
return 'processes'
+ def worker_startup_delay_secs(self):
+ # FIXME: If we start workers up too quickly, DumpRenderTree appears
+ # to thrash on something and time out its first few tests. Until
+ # we can figure out what's going on, sleep a bit in between
+ # workers. See https://bugs.webkit.org/show_bug.cgi?id=79147 .
+ return 0.1
+
def baseline_path(self):
"""Return the absolute path to the directory to store new baselines in for this port."""
baseline_search_paths = self.get_option('additional_platform_directory', []) + self.baseline_search_path()
@@ -248,17 +255,10 @@ class Port(object):
_log.error("No httpd found. Cannot run http tests.")
return False
- 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."""
+ def do_text_results_differ(self, expected_text, actual_text):
return expected_text != actual_text
- def compare_audio(self, expected_audio, actual_audio):
- # FIXME: If we give this method a better name it won't need this docstring (e.g. are_audio_results_equal()).
- """Return whether the two audio files are *not* equal."""
+ def do_audio_results_differ(self, expected_audio, actual_audio):
return expected_audio != actual_audio
def diff_image(self, expected_contents, actual_contents, tolerance=None):
@@ -269,14 +269,9 @@ class Port(object):
"""
raise NotImplementedError('Port.diff_image')
-
- def diff_text(self, expected_text, actual_text,
- expected_filename, actual_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.
-
- While this is a generic routine, we include it in the Port
- interface so that it can be overriden for testing purposes."""
+ in 'unified diff' format."""
# The filenames show up in the diff output, make sure they're
# raw bytes and not unicode, so that they don't trigger join()
@@ -309,10 +304,7 @@ class Port(object):
pass
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."""
+ # FIXME: Seems we should get this from the Port's Driver class.
return "DumpRenderTree"
def expected_baselines(self, test_name, suffix, all_baselines=False):
@@ -619,21 +611,12 @@ class Port(object):
return True
return False
- def maybe_make_directory(self, *comps):
- """Creates the specified directory if it doesn't already exist."""
- self._filesystem.maybe_make_directory(*comps)
-
def name(self):
"""Returns a name that uniquely identifies this particular type of port
(e.g., "mac-snowleopard" or "chromium-gpu-linux-x86_x64" and can be passed
to factory.get() to instantiate the port."""
return self._name
- def real_name(self):
- # FIXME: Seems this is only used for MockDRT and should be removed.
- """Returns the name of the port as passed to the --platform command line argument."""
- return self.name()
-
def operating_system(self):
# Subclasses should override this default implementation.
return 'mac'
@@ -769,16 +752,16 @@ class Port(object):
method."""
pass
- def start_http_server(self):
+ def start_http_server(self, additional_dirs=None):
"""Start a web server. Raise an error if it can't start or is already running.
Ports can stub this out if they don't need a web server to be running."""
assert not self._http_server, 'Already running an http server.'
if self._uses_apache():
- server = apache_http_server.LayoutTestApacheHttpd(self, self.results_directory())
+ server = apache_http_server.LayoutTestApacheHttpd(self, self.results_directory(), additional_dirs=additional_dirs)
else:
- server = http_server.Lighttpd(self, self.results_directory())
+ server = http_server.Lighttpd(self, self.results_directory(), additional_dirs=additional_dirs)
server.start()
self._http_server = server
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/builders.py b/Tools/Scripts/webkitpy/layout_tests/port/builders.py
index 42fed50b2..3f7e413ce 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/builders.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/builders.py
@@ -44,8 +44,7 @@ _exact_matches = {
"Webkit Win (dbg)(2)": {"port_name": "chromium-win-xp", "specifiers": set(["win", "debug"])},
"Webkit Linux": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "x86_64", "release"])},
"Webkit Linux 32": {"port_name": "chromium-linux-x86", "specifiers": set(["linux", "x86"])},
- "Webkit Linux (dbg)(1)": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "debug"])},
- "Webkit Linux (dbg)(2)": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "debug"])},
+ "Webkit Linux (dbg)": {"port_name": "chromium-linux-x86_64", "specifiers": set(["linux", "debug"])},
"Webkit Mac10.5": {"port_name": "chromium-mac-leopard", "specifiers": set(["leopard"])},
"Webkit Mac10.5 (dbg)(1)": {"port_name": "chromium-mac-leopard", "specifiers": set(["leopard", "debug"])},
"Webkit Mac10.5 (dbg)(2)": {"port_name": "chromium-mac-leopard", "specifiers": set(["leopard", "debug"])},
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium.py
index 199a2ecb9..83139e0fc 100755
--- a/Tools/Scripts/webkitpy/layout_tests/port/chromium.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium.py
@@ -66,7 +66,8 @@ class ChromiumPort(Port):
('vista', 'x86'),
('win7', 'x86'),
('lucid', 'x86'),
- ('lucid', 'x86_64'))
+ ('lucid', 'x86_64'),
+ ('icecreamsandwich', 'arm'))
ALL_GRAPHICS_TYPES = ('cpu', 'gpu')
@@ -81,6 +82,7 @@ class ChromiumPort(Port):
'mac': ['leopard', 'snowleopard', 'lion'],
'win': ['xp', 'vista', 'win7'],
'linux': ['lucid'],
+ 'android': ['icecreamsandwich'],
}
@classmethod
@@ -592,6 +594,10 @@ class ChromiumDriver(Driver):
return DriverOutput(text, output_image, actual_checksum, audio=audio_bytes,
crash=crash, crashed_process_name=crashed_process_name, test_time=run_time, timeout=timeout, error=error)
+ def start(self):
+ if not self._proc:
+ self._start()
+
def stop(self):
if not self._proc:
return
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py
new file mode 100644
index 000000000..bdb2cbb31
--- /dev/null
+++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py
@@ -0,0 +1,528 @@
+# Copyright (C) 2012 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 name of Google Inc. 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.
+
+import logging
+
+from webkitpy.layout_tests.port import chromium
+from webkitpy.layout_tests.port import factory
+
+
+_log = logging.getLogger(__name__)
+
+
+# The root directory for test resources, which has the same structure as the
+# source root directory of Chromium.
+# This path is defined in base/base_paths_android.cc and
+# webkit/support/platform_support_android.cc.
+DEVICE_SOURCE_ROOT_DIR = '/data/local/tmp/'
+
+DEVICE_DRT_DIR = '/data/drt/'
+DEVICE_DRT_PATH = DEVICE_DRT_DIR + 'DumpRenderTree'
+DEVICE_DRT_STDERR = DEVICE_DRT_DIR + 'DumpRenderTree.stderr'
+DEVICE_FORWARDER_PATH = DEVICE_DRT_DIR + 'forwarder'
+DEVICE_DRT_STAMP_PATH = DEVICE_DRT_DIR + 'DumpRenderTree.stamp'
+
+# This only works for single core devices so far.
+# FIXME: Find a solution for multi-core devices.
+SCALING_GOVERNOR = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
+
+# All the test cases are still served to DumpRenderTree through file protocol,
+# but we use a file-to-http feature to bridge the file request to host's http
+# server to get the real test files and corresponding resources.
+TEST_PATH_PREFIX = '/all-tests'
+
+# All ports the Android forwarder to forward.
+# 8000, 8080 and 8443 are for http/https tests.
+# 8880 and 9323 are for websocket tests
+# (see http_server.py, apache_http_server.py and websocket_server.py).
+FORWARD_PORTS = '8000 8080 8443 8880 9323'
+
+MS_TRUETYPE_FONTS_DIR = '/usr/share/fonts/truetype/msttcorefonts/'
+
+# List of fonts that layout tests expect, copied from DumpRenderTree/gtk/TestShellGtk.cpp.
+HOST_FONT_FILES = [
+ [MS_TRUETYPE_FONTS_DIR, 'Arial.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Arial_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS_Bold.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Courier_New.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Georgia.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Georgia_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Impact.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Verdana.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold_Italic.ttf'],
+ [MS_TRUETYPE_FONTS_DIR, 'Verdana_Italic.ttf'],
+ # The Microsoft font EULA
+ ['/usr/share/doc/ttf-mscorefonts-installer/', 'READ_ME!.gz'],
+ ['/usr/share/fonts/truetype/ttf-dejavu/', 'DejaVuSans.ttf'],
+]
+# Should increase this version after changing HOST_FONT_FILES.
+FONT_FILES_VERSION = 1
+
+DEVICE_FONTS_DIR = DEVICE_DRT_DIR + 'fonts/'
+DEVICE_FIRST_FALLBACK_FONT = '/system/fonts/DroidNaskh-Regular.ttf'
+
+# The layout tests directory on device, which has two usages:
+# 1. as a virtual path in file urls that will be bridged to HTTP.
+# 2. pointing to some files that are pushed to the device for tests that
+# don't work on file-over-http (e.g. blob protocol tests).
+DEVICE_LAYOUT_TESTS_DIR = (DEVICE_SOURCE_ROOT_DIR + 'third_party/WebKit/LayoutTests/')
+FILE_TEST_URI_PREFIX = 'file://' + DEVICE_LAYOUT_TESTS_DIR
+
+# Test resources that need to be accessed as files directly.
+# Each item can be the relative path of a directory or a file.
+TEST_RESOURCES_TO_PUSH = [
+ # Blob tests need to access files directly.
+ 'editing/pasteboard/resources',
+ 'fast/files/resources',
+ 'http/tests/local/resources',
+ 'http/tests/local/formdata/resources',
+ # User style URLs are accessed as local files in webkit_support.
+ 'http/tests/security/resources/cssStyle.css',
+ # Media tests need to access audio/video as files.
+ 'media/content',
+ 'compositing/resources/video.mp4',
+]
+
+
+class ChromiumAndroidPort(chromium.ChromiumPort):
+ port_name = 'chromium-android'
+
+ FALLBACK_PATHS = [
+ 'chromium-android',
+ 'chromium-linux',
+ 'chromium-win',
+ 'chromium',
+ 'win',
+ 'mac',
+ ]
+
+ def __init__(self, host, port_name, **kwargs):
+ chromium.ChromiumPort.__init__(self, host, port_name, **kwargs)
+
+ # The chromium-android port always uses the GPU code path, so we set
+ # these options here, almost as if this was the chromium-gpu-android
+ # port.
+ self._options.accelerated_2d_canvas = True
+ self._options.accelerated_video = True
+
+ self._operating_system = 'android'
+ self._version = 'icecreamsandwich'
+ # FIXME: we may support other architectures in the future.
+ self._architecture = 'arm'
+ self._original_governor = None
+ self._android_base_dir = None
+
+ self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
+
+ self._adb_command = ['adb']
+ adb_args = self.get_option('adb_args')
+ if adb_args:
+ self._adb_command += shlex.split(adb_args)
+
+ def default_child_processes(self):
+ # Currently we only use one process, but it might be helpful to use
+ # more that one process in the future to improve performance.
+ return 1
+
+ def baseline_search_path(self):
+ return map(self._webkit_baseline_path, self.FALLBACK_PATHS)
+
+ def check_build(self, needs_http):
+ return self._host_port.check_build(needs_http)
+
+ def check_sys_deps(self, needs_http):
+ for (font_dir, font_file) in HOST_FONT_FILES:
+ font_path = font_dir + font_file
+ if not self._check_file_exists(font_path, 'font file'):
+ _log.error('You are missing %s. Try installing msttcorefonts. '
+ 'See build instructions.' % font_path)
+ return False
+ return True
+
+ def default_worker_model(self):
+ return 'inline'
+
+ def test_expectations(self):
+ # Automatically apply all expectation rules of chromium-linux to
+ # chromium-android.
+ # FIXME: This is a temporary measure to reduce the manual work when
+ # updating WebKit. This method should be removed when we merge
+ # test_expectations_android.txt into test_expectations.txt.
+ expectations = chromium.ChromiumPort.test_expectations(self)
+ return expectations.replace('LINUX ', 'LINUX ANDROID ')
+
+ def start_http_server(self, additional_dirs=None):
+ # The http server runs during the whole testing period, so ignore this call.
+ pass
+
+ def stop_http_server(self):
+ # Same as start_http_server().
+ pass
+
+ def start_helper(self):
+ self._setup_performance()
+ # Required by webkit_support::GetWebKitRootDirFilePath().
+ # Other directories will be created automatically by adb push.
+ self._run_adb_command(['shell', 'mkdir', '-p',
+ DEVICE_SOURCE_ROOT_DIR + 'chrome'])
+
+ self._push_executable()
+ self._push_fonts()
+ self._setup_system_font_for_test()
+ self._synchronize_datetime()
+
+ # Start the HTTP server so that the device can access the test cases.
+ chromium.ChromiumPort.start_http_server(self, additional_dirs={TEST_PATH_PREFIX: self.layout_tests_dir()})
+
+ _log.debug('Starting forwarder')
+ cmd = self._run_adb_command(['shell', '%s %s' % (DEVICE_FORWARDER_PATH, FORWARD_PORTS)])
+
+ def stop_helper(self):
+ self._restore_system_font()
+ # Leave the forwarder and tests httpd server there because they are
+ # useful for debugging and do no harm to subsequent tests.
+ self._teardown_performance()
+
+ def _build_path(self, *comps):
+ return self._host_port._build_path(*comps)
+
+ def _path_to_apache(self):
+ return self._host_port._path_to_apache()
+
+ def _path_to_apache_config_file(self):
+ return self._host_port._path_to_apache_config_file()
+
+ def _path_to_driver(self, configuration=None):
+ # Returns the host path to driver which will be pushed to the device.
+ if not configuration:
+ configuration = self.get_option('configuration')
+ return self._build_path(configuration, 'DumpRenderTree')
+
+ def _path_to_helper(self):
+ return self._build_path(self.get_option('configuration'), 'forwarder')
+
+ def _path_to_image_diff(self):
+ return self._host_port._path_to_image_diff()
+
+ def _path_to_lighttpd(self):
+ return self._host_port._path_to_lighttpd()
+
+ def _path_to_lighttpd_modules(self):
+ return self._host_port._path_to_lighttpd_modules()
+
+ def _path_to_lighttpd_php(self):
+ return self._host_port._path_to_lighttpd_php()
+
+ def _path_to_wdiff(self):
+ return self._host_port._path_to_wdiff()
+
+ def _shut_down_http_server(self, pid):
+ return self._host_port._shut_down_http_server(pid)
+
+ def _driver_class(self):
+ return ChromiumAndroidDriver
+
+ def _push_executable(self):
+ drt_host_path = self._path_to_driver()
+ forwarder_host_path = self._path_to_helper()
+ drt_jar_host_path = drt_host_path + '.jar'
+ host_stamp = int(float(max(os.stat(drt_host_path).st_mtime,
+ os.stat(forwarder_host_path).st_mtime,
+ os.stat(drt_jar_host_path).st_mtime)))
+ device_stamp = int(float(self._run_adb_command([
+ 'shell', 'cat %s 2>/dev/null || echo 0' % DEVICE_DRT_STAMP_PATH])))
+ if device_stamp < host_stamp:
+ _log.debug('Pushing executable')
+ self._kill_device_process(DEVICE_FORWARDER_PATH)
+ self._push_to_device(forwarder_host_path, DEVICE_FORWARDER_PATH)
+ self._push_to_device(drt_host_path, DEVICE_DRT_PATH)
+ self._push_to_device(drt_host_path + '.pak', DEVICE_DRT_PATH + '.pak')
+ self._push_to_device(drt_host_path + '_resources', DEVICE_DRT_PATH + '_resources')
+ self._push_to_device(drt_jar_host_path, DEVICE_DRT_PATH + '.jar')
+ # Version control of test resources is dependent on executables,
+ # because we will always rebuild executables when resources are
+ # updated.
+ self._push_test_resources()
+ self._run_adb_command(['shell', 'echo %d >%s' % (host_stamp, DEVICE_DRT_STAMP_PATH)])
+
+ def _push_fonts(self):
+ if not self._check_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION):
+ _log.debug('Pushing fonts')
+ path_to_ahem_font = self._build_path(self.get_option('configuration'), 'AHEM____.TTF')
+ self._push_to_device(path_to_ahem_font, DEVICE_FONTS_DIR + 'AHEM____.TTF')
+ for (host_dir, font_file) in HOST_FONT_FILES:
+ self._push_to_device(host_dir + font_file, DEVICE_FONTS_DIR + font_file)
+ self._update_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION)
+
+ def _setup_system_font_for_test(self):
+ # The DejaVu font implicitly used by some CSS 2.1 tests should be added
+ # into the font fallback list of the system. DroidNaskh-Regular.ttf is
+ # the first font in Android Skia's font fallback list. Fortunately the
+ # DejaVu font also contains Naskh glyphs.
+ # First remount /system in read/write mode.
+ self._run_adb_command(['remount'])
+ self._copy_device_file(DEVICE_FONTS_DIR + 'DejaVuSans.ttf', DEVICE_FIRST_FALLBACK_FONT)
+
+ def _restore_system_font(self):
+ # First remount /system in read/write mode.
+ self._run_adb_command(['remount'])
+ self._push_to_device(os.environ['OUT'] + DEVICE_FIRST_FALLBACK_FONT, DEVICE_FIRST_FALLBACK_FONT)
+
+ def _push_test_resources(self):
+ _log.debug('Pushing test resources')
+ for resource in TEST_RESOURCES_TO_PUSH:
+ self._push_to_device(self.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource)
+
+ def _synchronize_datetime(self):
+ # The date/time between host and device may not be synchronized.
+ # We need to make them synchronized, otherwise tests might fail.
+ try:
+ # Get seconds since 1970-01-01 00:00:00 UTC.
+ host_datetime = self._executive.run_command(['date', '-u', '+%s'])
+ except:
+ # Reset to 1970-01-01 00:00:00 UTC.
+ host_datetime = 0
+ self._run_adb_command(['shell', 'date -u %s' % (host_datetime)])
+
+ def _check_version(self, dir, version):
+ assert(dir.endswith('/'))
+ try:
+ device_version = int(self._run_adb_command(['shell', 'cat %sVERSION || echo 0' % dir]))
+ return device_version == version
+ except:
+ return False
+
+ def _update_version(self, dir, version):
+ self._run_adb_command(['shell', 'echo %d > %sVERSION' % (version, dir)])
+
+ def _run_adb_command(self, cmd, ignore_error=False):
+ if ignore_error:
+ error_handler = self._executive.ignore_error
+ else:
+ error_handler = None
+ return self._executive.run_command(self._adb_command + cmd, error_handler=error_handler)
+
+ def _copy_device_file(self, from_file, to_file, ignore_error=False):
+ # 'cp' is unavailable on Android, so use 'dd' instead.
+ return self._run_adb_command(['shell', 'dd', 'if=' + from_file, 'of=' + to_file], ignore_error)
+
+ def _push_to_device(self, host_path, device_path, ignore_error=False):
+ return self._run_adb_command(['push', host_path, device_path], ignore_error)
+
+ def _pull_from_device(self, device_path, host_path, ignore_error=False):
+ return self._run_adb_command(['pull', device_path, host_path], ignore_error)
+
+ def _kill_device_process(self, name):
+ ps_result = self._run_adb_command(['shell', 'ps']).split('\n')
+ for line in ps_result:
+ if line.find(name) > 0:
+ pid = line.split()[1]
+ self._run_adb_command(['shell', 'kill', pid])
+
+ def get_stderr(self):
+ return self._run_adb_command(['shell', 'cat', DEVICE_DRT_STDERR], ignore_error=True)
+
+ def get_last_stacktrace(self):
+ tombstones = self._run_adb_command(['shell', 'ls', '-n', '/data/tombstones'])
+ tombstones = tombstones.rstrip().split('\n')
+ if not tombstones:
+ return ''
+ last_tombstone = tombstones[0].split()
+ for tombstone in tombstones[1:]:
+ # Format of fields:
+ # 0 1 2 3 4 5 6
+ # permission uid gid size date time filename
+ # -rw------- 1000 1000 45859 2011-04-13 06:00 tombstone_00
+ fields = tombstone.split()
+ if (fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]):
+ last_tombstone = fields
+ else:
+ break
+
+ # Use Android tool vendor/google/tools/stack to convert the raw
+ # stack trace into a human readable format, if needed.
+ # It takes a long time, so don't do it here.
+ return self._run_adb_command(['shell', 'cat', '/data/tombstones/' + last_tombstone[6]])
+
+ def _setup_performance(self):
+ # Disable CPU scaling and drop ram cache to reduce noise in tests
+ if not self._original_governor:
+ self._original_governor = self._run_adb_command(['shell', 'cat', SCALING_GOVERNOR])
+ self._run_adb_command(['shell', 'echo', 'performance', '>', SCALING_GOVERNOR])
+
+ def _teardown_performance(self):
+ if self._original_governor:
+ self._run_adb_command(['shell', 'echo', self._original_governor, SCALING_GOVERNOR])
+ self._original_governor = None
+
+
+class ChromiumAndroidDriver(chromium.ChromiumDriver):
+ def __init__(self, port, worker_number, pixel_tests, no_timeout=False):
+ chromium.ChromiumDriver.__init__(self, port, worker_number, pixel_tests, no_timeout)
+ if self._image_path:
+ self._device_image_path = DEVICE_DRT_DIR + port.host.filesystem.basename(self._image_path)
+
+ def _start(self):
+ # Convert the original command line into to two parts:
+ # - the 'adb shell' command line to start an interactive adb shell;
+ # - the DumpRenderTree command line to send to the adb shell.
+ original_cmd = self.cmd_line()
+ shell_cmd = []
+ drt_args = []
+ path_to_driver = self._port._path_to_driver()
+ reading_args_before_driver = True
+ for param in original_cmd:
+ if reading_args_before_driver:
+ if param == path_to_driver:
+ reading_args_before_driver = False
+ else:
+ shell_cmd.append(param)
+ else:
+ if param.startswith('--pixel-tests='):
+ param = '--pixel-tests=' + self._device_image_path
+ drt_args.append(param)
+
+ shell_cmd += self._port._adb_command
+ shell_cmd.append('shell')
+ retries = 0
+ while True:
+ _log.debug('Starting adb shell for DumpRenderTree: ' + ' '.join(shell_cmd))
+ executive = self._port.host.executive
+ self._proc = executive.Popen(shell_cmd, stdin=executive.PIPE, stdout=executive.PIPE, stderr=executive.STDOUT, close_fds=True)
+ # Read back the shell prompt ('# ') to ensure adb shell ready.
+ prompt = self._proc.stdout.read(2)
+ assert(prompt == '# ')
+ # Some tests rely on this to produce proper number format etc.,
+ # e.g. fast/speech/input-appearance-numberandspeech.html.
+ self._write_command_and_read_line("export LC_CTYPE='en_US'\n")
+ self._write_command_and_read_line("export CLASSPATH='/data/drt/DumpRenderTree.jar'\n")
+
+ # When DumpRenderTree crashes, the Android debuggerd will stop the
+ # process before dumping stack to log/tombstone file and terminating
+ # the process. Sleep 1 second (long enough for debuggerd to dump
+ # stack) before exiting the shell to ensure the process has quit,
+ # otherwise the exit will fail because "You have stopped jobs".
+ drt_cmd = '%s %s 2>%s;sleep 1;exit\n' % (DEVICE_DRT_PATH, ' '.join(drt_args), DEVICE_DRT_STDERR)
+ _log.debug('Starting DumpRenderTree: ' + drt_cmd)
+
+ # Wait until DRT echos '#READY'.
+ output = ''
+ (line, crash) = self._write_command_and_read_line(drt_cmd)
+ while not crash and line.rstrip() != '#READY':
+ if line == '': # EOF or crashed
+ crash = True
+ else:
+ output += line
+ (line, crash) = self._write_command_and_read_line()
+
+ if crash:
+ # Sometimes the device is in unstable state (may be out of
+ # memory?) and kills DumpRenderTree just after it is started.
+ # Try to stop and start it again.
+ _log.error('Failed to start DumpRenderTree: \n%s\n%s\n' % (output, self._port.get_stderr()))
+ self.stop()
+ retries += 1
+ if retries > 2:
+ raise AssertionError('Failed multiple times to start DumpRenderTree')
+ else:
+ return
+
+ def run_test(self, driver_input):
+ driver_output = chromium.ChromiumDriver.run_test(self, driver_input)
+ # FIXME: Retrieve stderr from the target.
+ if driver_output.crash:
+ # Fetch the stack trace from the tombstone file.
+ # FIXME: sometimes the crash doesn't really happen so that no
+ # tombstone is generated. In that case we fetch the wrong stack
+ # trace.
+ driver_output.error += self._port.get_last_stacktrace()
+ return driver_output
+
+ def stop(self):
+ _log.debug('Stopping DumpRenderTree')
+ if self._proc:
+ # Send an explicit QUIT command because closing the pipe can't let
+ # DumpRenderTree on Android quit immediately.
+ try:
+ self._proc.stdin.write('QUIT\n')
+ except IOError:
+ # The pipe has already been closed, indicating abnormal
+ # situation occurred. Wait a while to allow the device to
+ # recover. *fingers crossed*
+ time.sleep(1)
+ chromium.ChromiumDriver.stop(self)
+
+ def _test_shell_command(self, uri, timeout_ms, checksum):
+ if uri.startswith('file:///'):
+ # Convert the host uri to a device uri. See comment of
+ # DEVICE_LAYOUT_TESTS_DIR for details.
+ # Not overriding Port.filename_to_uri() because we don't want the
+ # links in the html report point to device paths.
+ uri = FILE_TEST_URI_PREFIX + self.uri_to_test(uri)
+ return chromium.ChromiumDriver._test_shell_command(self, uri, timeout_ms, checksum)
+
+ def _write_command_and_read_line(self, input=None):
+ (line, crash) = chromium.ChromiumDriver._write_command_and_read_line(self, input)
+ url_marker = '#URL:'
+ if not crash and line.startswith(url_marker) and line.find(FILE_TEST_URI_PREFIX) == len(url_marker):
+ # Convert the device test uri back to host uri otherwise
+ # chromium.ChromiumDriver.run_test() will complain.
+ line = '#URL:file://%s/%s' % (self._port.layout_tests_dir(), line[len(url_marker) + len(FILE_TEST_URI_PREFIX):])
+ if not crash and self._has_crash_hint(line):
+ crash = True
+ return (line, crash)
+
+ def _output_image(self):
+ if self._image_path:
+ _log.debug('pulling from device: %s to %s' % (self._device_image_path, self._image_path))
+ self._port._pull_from_device(self._device_image_path, self._image_path, ignore_error=True)
+ return chromium.ChromiumDriver._output_image(self)
+
+ def _has_crash_hint(self, line):
+ # When DRT crashes, it sends a signal to Android Debuggerd, like
+ # SIGSEGV, SIGFPE, etc. When Debuggerd receives the signal, it stops DRT
+ # (which causes Shell to output a message), and dumps the stack strace.
+ # We use the Shell output as a crash hint.
+ return line is not None and line.find('[1] + Stopped (signal)') >= 0
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py
new file mode 100644
index 000000000..dd652fe02
--- /dev/null
+++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_android_unittest.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2012 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 name of Google Inc. 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.
+
+import unittest
+
+from webkitpy.common.system import executive_mock
+from webkitpy.common.system.systemhost_mock import MockSystemHost
+
+from webkitpy.layout_tests.port import chromium_android
+from webkitpy.layout_tests.port import port_testcase
+
+
+class ChromiumAndroidPortTest(port_testcase.PortTestCase):
+ port_name = 'chromium-android'
+ port_maker = chromium_android.ChromiumAndroidPort
+ expected_default_worker_model = 'inline'
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py
index 6f98e9a3d..ac0779574 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/chromium_unittest.py
@@ -39,6 +39,7 @@ from webkitpy.thirdparty.mock import Mock
from webkitpy.tool.mocktool import MockOptions
import chromium
+import chromium_android
import chromium_linux
import chromium_mac
import chromium_win
@@ -161,6 +162,10 @@ class ChromiumPortTest(port_testcase.PortTestCase):
"""Validate the complete set of configurations this port knows about."""
port = self.make_port()
self.assertEquals(set(port.all_test_configurations()), set([
+ TestConfiguration('icecreamsandwich', 'arm', 'debug', 'cpu'),
+ TestConfiguration('icecreamsandwich', 'arm', 'release', 'cpu'),
+ TestConfiguration('icecreamsandwich', 'arm', 'debug', 'gpu'),
+ TestConfiguration('icecreamsandwich', 'arm', 'release', 'gpu'),
TestConfiguration('leopard', 'x86', 'debug', 'cpu'),
TestConfiguration('leopard', 'x86', 'debug', 'gpu'),
TestConfiguration('leopard', 'x86', 'release', 'cpu'),
@@ -216,6 +221,15 @@ class ChromiumPortTest(port_testcase.PortTestCase):
self.default_configuration_called = True
return 'default'
+ class TestAndroidPort(chromium_android.ChromiumAndroidPort):
+ def __init__(self, options=None):
+ options = options or MockOptions()
+ chromium_win.ChromiumAndroidPort.__init__(self, MockSystemHost(os_name='android', os_version='icecreamsandwich'), 'chromium-android', options=options)
+
+ def default_configuration(self):
+ self.default_configuration_called = True
+ return 'default'
+
class TestLinuxPort(chromium_linux.ChromiumLinuxPort):
def __init__(self, options=None):
options = options or MockOptions()
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/driver.py b/Tools/Scripts/webkitpy/layout_tests/port/driver.py
index 8a2857a8a..92e6992d7 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/driver.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/driver.py
@@ -156,6 +156,9 @@ class Driver(object):
def has_crashed(self):
return False
+ def start(self):
+ raise NotImplementedError('Driver.start')
+
def stop(self):
raise NotImplementedError('Driver.stop')
@@ -192,6 +195,9 @@ class DriverProxy(object):
def has_crashed(self):
return self._driver.has_crashed() or self._reftest_driver.has_crashed()
+ def start(self):
+ self._driver.start()
+
def stop(self):
self._driver.stop()
self._reftest_driver.stop()
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/factory.py b/Tools/Scripts/webkitpy/layout_tests/port/factory.py
index a8a1cd0e8..059186eee 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/factory.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/factory.py
@@ -42,6 +42,7 @@ class BuilderOptions(object):
class PortFactory(object):
PORT_CLASSES = (
+ 'chromium_android.ChromiumAndroidPort',
'chromium_gpu.ChromiumGpuLinuxPort',
'chromium_gpu.ChromiumGpuMacPort',
'chromium_gpu.ChromiumGpuWinPort',
@@ -59,7 +60,7 @@ class PortFactory(object):
'qt.QtPort',
'test.TestPort',
'win.WinPort',
- )
+ )
def __init__(self, host):
self._host = host
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/mac.py b/Tools/Scripts/webkitpy/layout_tests/port/mac.py
index 202f2de2a..829b896fa 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/mac.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/mac.py
@@ -128,7 +128,7 @@ class MacPort(ApplePort):
return self._build_path('WebCore.framework/Versions/A/WebCore')
def show_results_html_file(self, results_filename):
- self._run_script('run-safari', ['-NSOpen', results_filename])
+ self._run_script('run-safari', ['--no-saved-state', '-NSOpen', results_filename])
# FIXME: The next two routines turn off the http locking in order
# to work around failures on the bots caused when the slave restarts.
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
index bdef980bd..35e64e49f 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
@@ -161,7 +161,7 @@ java/
port = self.make_port()
# Delay setting a should_log executive to avoid logging from MacPort.__init__.
port._executive = MockExecutive(should_log=True)
- expected_stderr = "MOCK run_command: ['Tools/Scripts/run-safari', '--release', '-NSOpen', 'test.html'], cwd=/mock-checkout\n"
+ expected_stderr = "MOCK run_command: ['Tools/Scripts/run-safari', '--release', '--no-saved-state', '-NSOpen', 'test.html'], cwd=/mock-checkout\n"
OutputCapture().assert_outputs(self, port.show_results_html_file, ["test.html"], expected_stderr=expected_stderr)
def test_operating_system(self):
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
index e56e27b1a..cbb6bb6ce 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt.py
@@ -65,10 +65,6 @@ class MockDRTPort(object):
def __init__(self, host, port_name, **kwargs):
self.__delegate = PortFactory(host).get(port_name.replace('mock-', ''), **kwargs)
- self.__real_name = port_name
-
- def real_name(self):
- return self.__real_name
def __getattr__(self, name):
return getattr(self.__delegate, name)
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
index ed88e8b7c..0b85579f6 100755
--- a/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/mock_drt_unittest.py
@@ -183,17 +183,14 @@ class MockDRTTest(unittest.TestCase):
self.assertTest('http/tests/passes/text.html', True)
def test_pixeltest__fails(self):
- self.assertTest('failures/expected/checksum.html', pixel_tests=True,
- expected_checksum='wrong-checksum',
+ self.assertTest('failures/expected/image_checksum.html', pixel_tests=True,
+ expected_checksum='image_checksum-checksum',
drt_output=['Content-Type: text/plain\n',
- 'checksum-txt',
+ 'image_checksum-txt',
'#EOF\n',
'\n',
- 'ActualHash: checksum-checksum\n',
- 'ExpectedHash: wrong-checksum\n',
- 'Content-Type: image/png\n',
- 'Content-Length: 43\n',
- 'checksum\x8a-pngtEXtchecksum\x00checksum-checksum',
+ 'ActualHash: image_checksum-checksum\n',
+ 'ExpectedHash: image_checksum-checksum\n',
'#EOF\n'])
def test_textonly(self):
@@ -245,17 +242,17 @@ class MockChromiumDRTTest(MockDRTTest):
if sys.platform == 'win32':
host = MockSystemHost(os_name='win', os_version='xp')
url = '#URL:file:///'
- url = url + '%s/failures/expected/checksum.html' % PortFactory(host).get('test').layout_tests_dir()
- self.assertTest('failures/expected/checksum.html', pixel_tests=True,
- expected_checksum='wrong-checksum',
+ url = url + '%s/failures/expected/image_checksum.html' % PortFactory(host).get('test').layout_tests_dir()
+ self.assertTest('failures/expected/image_checksum.html', pixel_tests=True,
+ expected_checksum='image_checksum',
drt_output=[url + '\n',
- '#MD5:checksum-checksum\n',
- 'checksum-txt',
+ '#MD5:image_checksum-checksum\n',
+ 'image_checksum-txt',
'\n',
'#EOF\n'],
host=host)
self.assertEquals(host.filesystem.written_files,
- {'/tmp/png_result0.png': 'checksum\x8a-pngtEXtchecksum\x00checksum-checksum'})
+ {'/tmp/png_result0.png': 'image_checksum\x8a-pngtEXtchecksum\x00image_checksum-checksum'})
def test_chromium_parse_options(self):
options, args = mock_drt.parse_options(['--platform', 'chromium-mac',
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
index 8df039ca2..02d6f23dc 100755
--- a/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
@@ -34,11 +34,13 @@ import sys
import time
import unittest
-from webkitpy.layout_tests.servers import http_server_base
from webkitpy.common.system.filesystem_mock import MockFileSystem
-from webkitpy.tool.mocktool import MockOptions
from webkitpy.common.system.executive_mock import MockExecutive
from webkitpy.common.system.systemhost_mock import MockSystemHost
+from webkitpy.layout_tests.servers import http_server_base
+from webkitpy.layout_tests.servers import http_server_base
+from webkitpy.layout_tests.port import factory
+from webkitpy.tool.mocktool import MockOptions
class PortTestCase(unittest.TestCase):
@@ -50,6 +52,7 @@ class PortTestCase(unittest.TestCase):
os_name = None
os_version = None
port_maker = None
+ expected_default_worker_model = 'processes'
def make_port(self, host=None, port_name=None, options=None, os_name=None, os_version=None, **kwargs):
host = host or MockSystemHost(os_name=(os_name or self.os_name), os_version=(os_version or self.os_version))
@@ -60,7 +63,7 @@ class PortTestCase(unittest.TestCase):
def test_default_worker_model(self):
port = self.make_port()
- self.assertEqual(port.default_worker_model(), 'processes')
+ self.assertEqual(port.default_worker_model(), self.expected_default_worker_model)
def test_driver_cmd_line(self):
port = self.make_port()
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/qt.py b/Tools/Scripts/webkitpy/layout_tests/port/qt.py
index 24458768c..5fc60a590 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/qt.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/qt.py
@@ -31,6 +31,7 @@
import logging
import re
import sys
+import os
import webkit
@@ -46,6 +47,9 @@ class QtPort(WebKitPort):
ALL_VERSIONS = ['linux', 'win', 'mac']
port_name = "qt"
+ def _wk2_port_name(self):
+ return "qt-5.0-wk2"
+
def _port_flag_for_scripts(self):
return "--qt"
@@ -122,10 +126,10 @@ class QtPort(WebKitPort):
search_paths.add('qt-4.8')
elif version:
search_paths.add('qt-5.0')
- if self.get_option('webkit_test_runner'):
- search_paths.update(['qt-wk2', 'wk2'])
- else:
- search_paths.add('qt-wk1')
+ if self.get_option('webkit_test_runner'):
+ search_paths.update(['qt-5.0-wk2', 'wk2'])
+ else:
+ search_paths.add('qt-5.0-wk1')
return search_paths
def _runtime_feature_list(self):
@@ -148,3 +152,12 @@ class QtPort(WebKitPort):
def operating_system(self):
return self._operating_system
+
+ def check_sys_deps(self, needs_http):
+ result = super(QtPort, self).check_sys_deps(needs_http)
+ if not 'WEBKIT_TESTFONTS' in os.environ:
+ _log.error('\nThe WEBKIT_TESTFONTS environment variable is not defined or not set properly.')
+ _log.error('You must set it before running the tests.')
+ _log.error('Use git to grab the actual fonts from http://gitorious.org/qtwebkit/testfonts')
+ return False
+ return result
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py
index 2aa6ff5e7..2679f33f1 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/qt_unittest.py
@@ -27,6 +27,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
+import os
from webkitpy.common.system.executive_mock import MockExecutive, MockExecutive2
from webkitpy.common.system.outputcapture import OutputCapture
@@ -67,9 +68,9 @@ class QtPortTest(port_testcase.PortTestCase):
self._assert_search_path(['qt-win', 'qt-4.8', 'qt'], 'win')
self._assert_search_path(['qt-linux', 'qt-4.8', 'qt'], 'linux')
- self._assert_search_path(['qt-wk2', 'qt-mac', 'qt-5.0', 'qt'], 'mac', use_webkit2=True, qt_version='5.0')
- self._assert_search_path(['qt-wk2', 'qt-win', 'qt-5.0', 'qt'], 'win', use_webkit2=True, qt_version='5.0')
- self._assert_search_path(['qt-wk2', 'qt-linux', 'qt-5.0', 'qt'], 'linux', use_webkit2=True, qt_version='5.0')
+ self._assert_search_path(['qt-5.0-wk2', 'qt-mac', 'qt-5.0', 'qt'], 'mac', use_webkit2=True, qt_version='5.0')
+ self._assert_search_path(['qt-5.0-wk2', 'qt-win', 'qt-5.0', 'qt'], 'win', use_webkit2=True, qt_version='5.0')
+ self._assert_search_path(['qt-5.0-wk2', 'qt-linux', 'qt-5.0', 'qt'], 'linux', use_webkit2=True, qt_version='5.0')
def test_show_results_html_file(self):
port = self.make_port()
@@ -86,3 +87,17 @@ class QtPortTest(port_testcase.PortTestCase):
self.assertEqual('linux', self.make_port(port_name='qt-linux', os_name='linux').operating_system())
self.assertEqual('mac', self.make_port(os_name='mac').operating_system())
self.assertEqual('win', self.make_port(port_name='qt-win', os_name='win').operating_system())
+
+ def test_check_sys_deps(self):
+ port = self.make_port()
+
+ # Success
+ os.environ['WEBKIT_TESTFONTS'] = '/tmp/foo'
+ port._executive = MockExecutive2(exit_code=0)
+ self.assertTrue(port.check_sys_deps(needs_http=False))
+
+ # Failure
+ del os.environ['WEBKIT_TESTFONTS']
+ port._executive = MockExecutive2(exit_code=1,
+ output='testing output failure')
+ self.assertFalse(port.check_sys_deps(needs_http=False))
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/server_process.py b/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
index 2b497d949..6c48fef64 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/server_process.py
@@ -260,6 +260,10 @@ class ServerProcess:
self._wait_for_data_and_update_buffers(deadline)
+ def start(self):
+ if not self._proc:
+ self._start()
+
def stop(self):
if not self._proc:
return
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/test.py b/Tools/Scripts/webkitpy/layout_tests/port/test.py
index 99c7ce921..199460d57 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/test.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/test.py
@@ -100,8 +100,6 @@ class TestList(object):
def unit_test_list():
tests = TestList()
- tests.add('failures/expected/checksum.html',
- actual_checksum='checksum_fail-checksum')
tests.add('failures/expected/crash.html', crash=True)
tests.add('failures/expected/exception.html', exception=True)
tests.add('failures/expected/timeout.html', timeout=True)
@@ -135,6 +133,7 @@ def unit_test_list():
tests.add('failures/expected/newlines_with_excess_CR.html',
expected_text="foo\r\r\r\n", actual_text="foo\n")
tests.add('failures/expected/text.html', actual_text='text_fail-png')
+ tests.add('failures/expected/skip_text.html', actual_text='text diff')
tests.add('failures/unexpected/missing_text.html', expected_text=None)
tests.add('failures/unexpected/missing_image.html', expected_image=None)
tests.add('failures/unexpected/missing_render_tree_dump.html', actual_text="""layer at (0,0) size 800x600
@@ -155,6 +154,7 @@ layer at (0,0) size 800x34
actual_checksum='text-image-checksum_fail-checksum')
tests.add('failures/unexpected/checksum-with-matching-image.html',
actual_checksum='text-image-checksum_fail-checksum')
+ tests.add('failures/unexpected/skip_pass.html')
tests.add('failures/unexpected/timeout.html', timeout=True)
tests.add('http/tests/passes/text.html')
tests.add('http/tests/passes/image.html')
@@ -171,6 +171,10 @@ layer at (0,0) size 800x34
expected_checksum=None,
expected_image='tEXtchecksum\x00checksum_in_image-checksum')
+ # Note that here the checksums don't match but the images do, so this test passes "unexpectedly".
+ # See https://bugs.webkit.org/show_bug.cgi?id=69444 .
+ tests.add('failures/unexpected/checksum.html', actual_checksum='checksum_fail-checksum')
+
# Text output files contain "\r\n" on Windows. This may be
# helpfully filtered to "\r\r\n" by our Python/Cygwin tooling.
tests.add('passes/text.html',
@@ -237,7 +241,6 @@ def add_unit_tests_to_mock_filesystem(filesystem):
filesystem.maybe_make_directory(LAYOUT_TEST_DIR + '/platform/test')
if not filesystem.exists(LAYOUT_TEST_DIR + '/platform/test/test_expectations.txt'):
filesystem.write_text_file(LAYOUT_TEST_DIR + '/platform/test/test_expectations.txt', """
-WONTFIX : failures/expected/checksum.html = IMAGE
WONTFIX : failures/expected/crash.html = CRASH
WONTFIX : failures/expected/image.html = IMAGE
WONTFIX : failures/expected/audio.html = AUDIO
@@ -368,6 +371,9 @@ class TestPort(Port):
def default_worker_model(self):
return 'inline'
+ def worker_startup_delay_secs(self):
+ return 0
+
def check_build(self, needs_http):
return True
@@ -392,6 +398,12 @@ class TestPort(Port):
def webkit_base(self):
return '/test.checkout'
+ def skipped_tests(self, test_list):
+ # This allows us to test the handling Skipped files, both with a test
+ # that actually passes, and a test that does fail.
+ return set(['failures/expected/skip_text.html',
+ 'failures/unexpected/skip_pass.html'])
+
def name(self):
return self._name
@@ -428,6 +440,15 @@ class TestPort(Port):
def release_http_lock(self):
pass
+ def _path_to_lighttpd(self):
+ return "/usr/sbin/lighttpd"
+
+ def _path_to_lighttpd_modules(self):
+ return "/usr/lib/lighttpd"
+
+ def _path_to_lighttpd_php(self):
+ return "/usr/bin/php-cgi"
+
def path_to_test_expectations_file(self):
return self._expectations_path
@@ -499,5 +520,8 @@ class TestDriver(Driver):
crashed_process_name=crashed_process_name,
test_time=time.time() - start_time, timeout=test.timeout, error=test.error)
+ def start(self):
+ pass
+
def stop(self):
pass
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/webkit.py b/Tools/Scripts/webkitpy/layout_tests/port/webkit.py
index 967e1f1ba..aeb918ce0 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/webkit.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/webkit.py
@@ -35,7 +35,6 @@ import base64
import itertools
import logging
import operator
-import os
import re
import sys
import time
@@ -341,7 +340,8 @@ class WebKitPort(Port):
return []
def _wk2_port_name(self):
- # By current convention, the WebKit2 name is always mac-wk2, win-wk2, not mac-leopard-wk2, etc.
+ # By current convention, the WebKit2 name is always mac-wk2, win-wk2, not mac-leopard-wk2, etc,
+ # except for Qt because WebKit2 is only supported by Qt 5.0 (therefore: qt-5.0-wk2).
return "%s-wk2" % self.port_name
def _skipped_file_search_paths(self):
@@ -645,6 +645,10 @@ class WebKitDriver(Driver):
block.decode_content()
return block
+ def start(self):
+ if not self._server_process:
+ self._start()
+
def stop(self):
if self._server_process:
self._server_process.stop()
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/win.py b/Tools/Scripts/webkitpy/layout_tests/port/win.py
index ab7103ce3..e463b02f4 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/win.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/win.py
@@ -45,10 +45,10 @@ class WinPort(ApplePort):
# and the order of fallback between them. Matches ORWT.
VERSION_FALLBACK_ORDER = ["win-xp", "win-vista", "win-7sp0", "win"]
- def compare_text(self, expected_text, actual_text):
+ def do_text_results_differ(self, expected_text, actual_text):
# Sanity was restored in WK2, so we don't need this hack there.
if self.get_option('webkit_test_runner'):
- return ApplePort.compare_text(self, expected_text, actual_text)
+ return ApplePort.do_text_results_differ(self, expected_text, actual_text)
# This is a hack (which dates back to ORWT).
# Windows does not have an EDITING DELEGATE, so we strip any EDITING DELEGATE
diff --git a/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py b/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
index 760f98d97..fc972a4cd 100644
--- a/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
@@ -85,13 +85,13 @@ class WinPortTest(port_testcase.PortTestCase):
def test_compare_text(self):
expected = "EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\nfoo\nEDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n"
port = self.make_port()
- self.assertFalse(port.compare_text(expected, "foo\n"))
- self.assertTrue(port.compare_text(expected, "foo"))
- self.assertTrue(port.compare_text(expected, "bar"))
+ self.assertFalse(port.do_text_results_differ(expected, "foo\n"))
+ self.assertTrue(port.do_text_results_differ(expected, "foo"))
+ self.assertTrue(port.do_text_results_differ(expected, "bar"))
# This hack doesn't exist in WK2.
port._options = MockOptions(webkit_test_runner=True)
- self.assertTrue(port.compare_text(expected, "foo\n"))
+ self.assertTrue(port.do_text_results_differ(expected, "foo\n"))
def test_operating_system(self):
self.assertEqual('win', self.make_port().operating_system())
diff --git a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
index 96508caf7..6b38a3a71 100755
--- a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
+++ b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -199,10 +199,12 @@ def parse_args(args=None):
Returns a tuple of options, args from optparse"""
+ option_group_definitions = []
+
# FIXME: All of these options should be stored closer to the code which
# FIXME: actually uses them. configuration_options should move
# FIXME: to WebKitPort and be shared across all scripts.
- configuration_options = [
+ option_group_definitions.append(("Configuration Options", [
optparse.make_option("-t", "--target", dest="configuration",
help="(DEPRECATED)"),
# FIXME: --help should display which configuration is default.
@@ -218,12 +220,12 @@ def parse_args(args=None):
optparse.make_option('--efl', action='store_const', const='efl', dest="platform", help='Alias for --platform=efl'),
optparse.make_option('--gtk', action='store_const', const='gtk', dest="platform", help='Alias for --platform=gtk'),
optparse.make_option('--qt', action='store_const', const='qt', dest="platform", help='Alias for --platform=qt'),
- ]
+ ]))
- print_options = printing.print_options()
+ option_group_definitions.append(("Printing Options", printing.print_options()))
# FIXME: These options should move onto the ChromiumPort.
- chromium_options = [
+ option_group_definitions.append(("Chromium-specific Options", [
optparse.make_option("--startup-dialog", action="store_true",
default=False, help="create a dialog on DumpRenderTree startup"),
optparse.make_option("--gp-fault-error-box", action="store_true",
@@ -267,9 +269,11 @@ def parse_args(args=None):
optparse.make_option("--per-tile-painting",
action="store_true",
help="Use per-tile painting of composited pages"),
- ]
+ optparse.make_option("--adb-args", type="string",
+ help="Arguments parsed to Android adb, to select device, etc."),
+ ]))
- webkit_options = [
+ option_group_definitions.append(("WebKit Options", [
optparse.make_option("--gc-between-tests", action="store_true", default=False,
help="Force garbage collection between each test"),
optparse.make_option("--complex-text", action="store_true", default=False,
@@ -284,15 +288,15 @@ def parse_args(args=None):
help="Use WebKitTestRunner rather than DumpRenderTree."),
optparse.make_option("--root", action="store",
help="Path to a pre-built root of WebKit (for running tests using a nightly build of WebKit)"),
- ]
+ ]))
- old_run_webkit_tests_compat = [
+ option_group_definitions.append(("ORWT Compatibility Options", [
# FIXME: Remove this option once the bots don't refer to it.
# results.html is smart enough to figure this out itself.
_compat_shim_option("--use-remote-links-to-tests"),
- ]
+ ]))
- results_options = [
+ option_group_definitions.append(("Results Options", [
optparse.make_option("-p", "--pixel-tests", action="store_true",
dest="pixel_tests", help="Enable pixel-to-pixel PNG comparisons"),
optparse.make_option("--no-pixel-tests", action="store_false",
@@ -354,9 +358,9 @@ def parse_args(args=None):
optparse.make_option("--ignore-metrics", action="store_true", dest="ignore_metrics",
default=False, help="Ignore rendering metrics related information from test "
"output, only compare the structure of the rendertree."),
- ]
+ ]))
- test_options = [
+ option_group_definitions.append(("Testing Options", [
optparse.make_option("--build", dest="build",
action="store_true", default=True,
help="Check to ensure the DumpRenderTree build is up-to-date "
@@ -430,16 +434,16 @@ def parse_args(args=None):
help="Don't re-try any tests that produce unexpected results."),
optparse.make_option("--max-locked-shards", type="int",
help="Set the maximum number of locked shards"),
- ]
+ ]))
- misc_options = [
+ option_group_definitions.append(("Miscellaneous Options", [
optparse.make_option("--lint-test-files", action="store_true",
default=False, help=("Makes sure the test files parse for all "
"configurations. Does not run any tests.")),
- ]
+ ]))
# FIXME: Move these into json_results_generator.py
- results_json_options = [
+ option_group_definitions.append(("Result JSON Options", [
optparse.make_option("--master-name", help="The name of the buildbot master."),
optparse.make_option("--builder-name", default="",
help=("The name of the builder shown on the waterfall running "
@@ -452,19 +456,21 @@ def parse_args(args=None):
optparse.make_option("--test-results-server", default="",
help=("If specified, upload results json files to this appengine "
"server.")),
- ]
+ ]))
+
+ option_parser = optparse.OptionParser()
- option_list = (configuration_options + print_options +
- chromium_options + webkit_options + results_options + test_options +
- misc_options + results_json_options + old_run_webkit_tests_compat)
- option_parser = optparse.OptionParser(option_list=option_list)
+ for group_name, group_options in option_group_definitions:
+ option_group = optparse.OptionGroup(option_parser, group_name)
+ option_group.add_options(group_options)
+ option_parser.add_option_group(option_group)
return option_parser.parse_args(args)
def main():
options, args = parse_args()
- if options.platform and options.platform.startswith('test'):
+ if options.platform and 'test' in options.platform:
# It's a bit lame to import mocks into real code, but this allows the user
# to run tests against the test platform interactively, which is useful for
# debugging test failures.
diff --git a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py
index ac9f8bbad..66876cc8c 100755
--- a/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_integrationtest.py
@@ -35,6 +35,7 @@ import json
import logging
import Queue
import re
+import StringIO
import sys
import thread
import time
@@ -47,7 +48,6 @@ from webkitpy.common.system import path
# (bug 63846).
SHOULD_TEST_PROCESSES = sys.platform not in ('cygwin', 'win32')
-from webkitpy.common import array_stream
from webkitpy.common.system import outputcapture
from webkitpy.common.system.crashlogs_unittest import make_mock_crash_report_darwin
from webkitpy.common.host_mock import MockHost
@@ -90,10 +90,10 @@ def passing_run(extra_args=None, port_obj=None, record_results=False, tests_incl
if not port_obj:
host = host or MockHost()
port_obj = host.port_factory.get(port_name=options.platform, options=options)
- buildbot_output = array_stream.ArrayStream()
- regular_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
+ regular_output = StringIO.StringIO()
res = run_webkit_tests.run(port_obj, options, parsed_args, buildbot_output=buildbot_output, regular_output=regular_output)
- return res == 0 and regular_output.empty() and buildbot_output.empty()
+ return res == 0 and not regular_output.getvalue() and not buildbot_output.getvalue()
def logging_run(extra_args=None, port_obj=None, record_results=False, tests_included=False, host=None, new_results=False):
@@ -113,8 +113,8 @@ def run_and_capture(port_obj, options, parsed_args):
oc = outputcapture.OutputCapture()
try:
oc.capture_output()
- buildbot_output = array_stream.ArrayStream()
- regular_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
+ regular_output = StringIO.StringIO()
res = run_webkit_tests.run(port_obj, options, parsed_args,
buildbot_output=buildbot_output,
regular_output=regular_output)
@@ -140,6 +140,9 @@ def get_tests_run(extra_args=None, tests_included=False, flatten_batches=False,
TestDriver.__init__(self, port, worker_number, pixel_tests=port.get_option('pixel_test'), no_timeout=False)
self._current_test_batch = None
+ def start(self):
+ pass
+
def stop(self):
self._current_test_batch = None
@@ -175,7 +178,21 @@ def get_tests_run(extra_args=None, tests_included=False, flatten_batches=False,
unexpected_tests_count = 12
-class LintTest(unittest.TestCase):
+class StreamTestingMixin(object):
+ def assertContains(self, stream, string):
+ self.assertTrue(string in stream.getvalue())
+
+ def assertContainsLine(self, stream, string):
+ self.assertTrue(string in stream.buflist)
+
+ def assertEmpty(self, stream):
+ self.assertFalse(stream.getvalue())
+
+ def assertNotEmpty(self, stream):
+ self.assertTrue(stream.getvalue())
+
+
+class LintTest(unittest.TestCase, StreamTestingMixin):
def test_all_configurations(self):
class FakePort(object):
@@ -230,8 +247,8 @@ class LintTest(unittest.TestCase):
def test_lint_test_files(self):
res, out, err, user = logging_run(['--lint-test-files'])
self.assertEqual(res, 0)
- self.assertTrue(out.empty())
- self.assertTrue(any(['Lint succeeded' in msg for msg in err.get()]))
+ self.assertEmpty(out)
+ self.assertContains(err, 'Lint succeeded')
def test_lint_test_files__errors(self):
options, parsed_args = parse_args(['--lint-test-files'])
@@ -241,11 +258,11 @@ class LintTest(unittest.TestCase):
res, out, err = run_and_capture(port_obj, options, parsed_args)
self.assertEqual(res, -1)
- self.assertTrue(out.empty())
- self.assertTrue(any(['Lint failed' in msg for msg in err.get()]))
+ self.assertEmpty(out)
+ self.assertTrue(any(['Lint failed' in msg for msg in err.buflist]))
-class MainTest(unittest.TestCase):
+class MainTest(unittest.TestCase, StreamTestingMixin):
def test_accelerated_compositing(self):
# This just tests that we recognize the command line args
self.assertTrue(passing_run(['--accelerated-video']))
@@ -268,7 +285,7 @@ class MainTest(unittest.TestCase):
if SHOULD_TEST_PROCESSES:
_, _, regular_output, _ = logging_run(
['--print', 'config', '--worker-model', 'processes', '--child-processes', '1'])
- self.assertTrue(any(['Running 1 ' in line for line in regular_output.get()]))
+ self.assertTrue(any(['Running 1 ' in line for line in regular_output.buflist]))
def test_child_processes_2(self):
# This test seems to fail on win32.
@@ -277,14 +294,14 @@ class MainTest(unittest.TestCase):
if SHOULD_TEST_PROCESSES:
_, _, regular_output, _ = logging_run(
['--print', 'config', '--worker-model', 'processes', '--child-processes', '2'])
- self.assertTrue(any(['Running 2 ' in line for line in regular_output.get()]))
+ self.assertTrue(any(['Running 2 ' in line for line in regular_output.buflist]))
def test_child_processes_min(self):
if SHOULD_TEST_PROCESSES:
_, _, regular_output, _ = logging_run(
['--print', 'config', '--worker-model', 'processes', '--child-processes', '2', 'passes'],
tests_included=True)
- self.assertTrue(any(['Running 1 ' in line for line in regular_output.get()]))
+ self.assertTrue(any(['Running 1 ' in line for line in regular_output.buflist]))
def test_dryrun(self):
batch_tests_run = get_tests_run(['--dry-run'])
@@ -319,16 +336,16 @@ class MainTest(unittest.TestCase):
def test_help_printing(self):
res, out, err, user = logging_run(['--help-printing'])
self.assertEqual(res, 0)
- self.assertTrue(out.empty())
- self.assertFalse(err.empty())
+ self.assertEmpty(out)
+ self.assertNotEmpty(err)
def test_hung_thread(self):
res, out, err, user = logging_run(['--run-singly', '--time-out-ms=50',
'failures/expected/hang.html'],
tests_included=True)
self.assertEqual(res, 0)
- self.assertFalse(out.empty())
- self.assertFalse(err.empty())
+ self.assertNotEmpty(out)
+ self.assertNotEmpty(err)
def test_keyboard_interrupt(self):
# Note that this also tests running a test marked as SKIP if
@@ -344,14 +361,14 @@ class MainTest(unittest.TestCase):
def test_no_tests_found(self):
res, out, err, user = logging_run(['resources'], tests_included=True)
self.assertEqual(res, -1)
- self.assertTrue(out.empty())
- self.assertTrue('No tests to run.\n' in err.get())
+ self.assertEmpty(out)
+ self.assertContainsLine(err, 'No tests to run.\n')
def test_no_tests_found_2(self):
res, out, err, user = logging_run(['foo'], tests_included=True)
self.assertEqual(res, -1)
- self.assertTrue(out.empty())
- self.assertTrue('No tests to run.\n' in err.get())
+ self.assertEmpty(out)
+ self.assertContainsLine(err, 'No tests to run.\n')
def test_randomize_order(self):
# FIXME: verify order was shuffled
@@ -390,8 +407,8 @@ class MainTest(unittest.TestCase):
'--print', 'everything',
'passes/text.html', 'failures/expected/text.html'],
tests_included=True, host=host, record_results=True)
- self.assertTrue("=> Results: 8/16 tests passed (50.0%)\n" in out.get())
- self.assertTrue(err.get()[-2] == "All 16 tests ran as expected.\n")
+ self.assertContainsLine(out, "=> Results: 8/16 tests passed (50.0%)\n")
+ self.assertContainsLine(err, "All 16 tests ran as expected.\n")
def test_run_chunk(self):
# Test that we actually select the right chunk
@@ -472,7 +489,7 @@ class MainTest(unittest.TestCase):
res, out, err, user = logging_run(['--test-list=%s' % filename],
tests_included=True, host=host)
self.assertEqual(res, -1)
- self.assertFalse(err.empty())
+ self.assertNotEmpty(err)
def test_test_list_with_prefix(self):
host = MockHost()
@@ -487,8 +504,8 @@ class MainTest(unittest.TestCase):
res, out, err, user = logging_run(tests_included=True)
self.assertEqual(res, unexpected_tests_count)
- self.assertFalse(out.empty())
- self.assertFalse(err.empty())
+ self.assertNotEmpty(out)
+ self.assertNotEmpty(err)
self.assertEqual(user.opened_urls, [path.abspath_to_uri('/tmp/layout-test-results/results.html')])
def test_missing_and_unexpected_results(self):
@@ -738,7 +755,7 @@ class MainTest(unittest.TestCase):
res, out, err, user = logging_run(['--worker-model', 'inline',
'--child-processes', '2'])
self.assertEqual(res, 0)
- self.assertTrue('--worker-model=inline overrides --child-processes\n' in err.get())
+ self.assertContainsLine(err, '--worker-model=inline overrides --child-processes\n')
def test_worker_model__processes(self):
if SHOULD_TEST_PROCESSES:
@@ -792,7 +809,7 @@ class MainTest(unittest.TestCase):
self.assertTrue(passing_run(['--additional-platform-directory', '/tmp/foo', '--additional-platform-directory', '/tmp/bar']))
res, buildbot_output, regular_output, user = logging_run(['--additional-platform-directory', 'foo'])
- self.assertTrue('--additional-platform-directory=foo is ignored since it is not absolute\n' in regular_output.get())
+ self.assertContainsLine(regular_output, '--additional-platform-directory=foo is ignored since it is not absolute\n')
def test_no_http_and_force(self):
# See test_run_force, using --force raises an exception.
@@ -858,14 +875,14 @@ class EndToEndTest(unittest.TestCase):
{"expected": "PASS", "ref_file": "reftests/foo/matching-ref.html", "actual": "IMAGE", "is_mismatch_reftest": True})
-class RebaselineTest(unittest.TestCase):
+class RebaselineTest(unittest.TestCase, StreamTestingMixin):
def assertBaselines(self, file_list, file, extensions, err):
"assert that the file_list contains the baselines."""
for ext in extensions:
baseline = file + "-expected" + ext
baseline_msg = 'Writing new expected result "%s"\n' % baseline[1:]
self.assertTrue(any(f.find(baseline) != -1 for f in file_list))
- self.assertTrue(baseline_msg in err.get())
+ self.assertContainsLine(err, baseline_msg)
# FIXME: Add tests to ensure that we're *not* writing baselines when we're not
# supposed to be.
@@ -882,7 +899,7 @@ class RebaselineTest(unittest.TestCase):
file_list = host.filesystem.written_files.keys()
file_list.remove('/tmp/layout-test-results/tests_run0.txt')
self.assertEquals(res, 0)
- self.assertTrue(out.empty())
+ self.assertEmpty(out)
self.assertEqual(len(file_list), 4)
self.assertBaselines(file_list, "/passes/image", [".txt", ".png"], err)
self.assertBaselines(file_list, "/failures/expected/missing_image", [".txt", ".png"], err)
@@ -900,7 +917,7 @@ class RebaselineTest(unittest.TestCase):
file_list = host.filesystem.written_files.keys()
file_list.remove('/tmp/layout-test-results/tests_run0.txt')
self.assertEquals(res, 0)
- self.assertFalse(out.empty())
+ self.assertNotEmpty(out)
self.assertEqual(len(file_list), 6)
self.assertBaselines(file_list, "/failures/unexpected/missing_text", [".txt"], err)
self.assertBaselines(file_list, "/platform/test-mac-leopard/failures/unexpected/missing_image", [".png"], err)
@@ -918,7 +935,7 @@ class RebaselineTest(unittest.TestCase):
file_list = host.filesystem.written_files.keys()
file_list.remove('/tmp/layout-test-results/tests_run0.txt')
self.assertEquals(res, 0)
- self.assertTrue(out.empty())
+ self.assertEmpty(out)
self.assertEqual(len(file_list), 4)
self.assertBaselines(file_list,
"/platform/test-mac-leopard/passes/image", [".txt", ".png"], err)
diff --git a/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py b/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py
index 4ea2c4892..a09aa3345 100644
--- a/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py
+++ b/Tools/Scripts/webkitpy/layout_tests/servers/apache_http_server.py
@@ -43,7 +43,7 @@ _log = logging.getLogger(__name__)
class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
- def __init__(self, port_obj, output_dir):
+ def __init__(self, port_obj, output_dir, additional_dirs=None):
"""Args:
port_obj: handle to the platform-specific routines
output_dir: the absolute path to the layout test result directory
@@ -57,7 +57,7 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
{'port': 8081},
{'port': 8443, 'sslcert': True}]
self._output_dir = output_dir
- port_obj.maybe_make_directory(output_dir)
+ self._filesystem.maybe_make_directory(output_dir)
self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % self._name)
@@ -87,6 +87,14 @@ class LayoutTestApacheHttpd(http_server_base.HttpServerBase):
'-c', "\'PidFile %s'" % self._pid_file,
'-k', "start"]
+ if additional_dirs:
+ for alias, path in additional_dirs.iteritems():
+ start_cmd += ['-c', "\'Alias %s \"%s\"\'" % (alias, path),
+ # Disable CGI handler for additional dirs.
+ '-c', "\'<Location %s>\'" % alias,
+ '-c', "\'RemoveHandler .cgi .pl\'",
+ '-c', "\'</Location>\'"]
+
stop_cmd = [executable,
'-f', "\"%s\"" % self._get_apache_config_file_path(test_dir, output_dir),
'-c', "\'PidFile %s'" % self._pid_file,
diff --git a/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py b/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py
index 701e7bd73..1f14e0dad 100755
--- a/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py
+++ b/Tools/Scripts/webkitpy/layout_tests/servers/http_server.py
@@ -43,7 +43,8 @@ _log = logging.getLogger(__name__)
class Lighttpd(http_server_base.HttpServerBase):
def __init__(self, port_obj, output_dir, background=False, port=None,
- root=None, run_background=None, layout_tests_dir=None):
+ root=None, run_background=None, additional_dirs=None,
+ layout_tests_dir=None):
"""Args:
output_dir: the absolute path to the layout test result directory
"""
@@ -54,6 +55,7 @@ class Lighttpd(http_server_base.HttpServerBase):
self._port = port
self._root = root
self._run_background = run_background
+ self._additional_dirs = additional_dirs
self._layout_tests_dir = layout_tests_dir
self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % self._name)
@@ -124,6 +126,10 @@ class Lighttpd(http_server_base.HttpServerBase):
f.write(('alias.url = ( "/js-test-resources" => "%s" )\n\n') %
(self._js_test_resource))
+ if self._additional_dirs:
+ for alias, path in self._additional_dirs.iteritems():
+ f.write(('alias.url += ( "%s" => "%s" )\n\n') % (alias, path))
+
# Setup a link to where the media resources are stored.
f.write(('alias.url += ( "/media-resources" => "%s" )\n\n') %
(self._media_resource))
@@ -189,7 +195,7 @@ class Lighttpd(http_server_base.HttpServerBase):
try:
self._remove_log_files(self._output_dir, log_prefix)
except OSError, e:
- _log.warning('Failed to remove old %s %s files' % self._name, log_prefix)
+ _log.warning('Failed to remove old %s %s files' % (self._name, log_prefix))
def _spawn_process(self):
_log.debug('Starting %s server, cmd="%s"' % (self._name, self._start_cmd))
diff --git a/Tools/Scripts/webkitpy/layout_tests/servers/http_server_base.py b/Tools/Scripts/webkitpy/layout_tests/servers/http_server_base.py
index 8f6c95748..ee5ef06d2 100755
--- a/Tools/Scripts/webkitpy/layout_tests/servers/http_server_base.py
+++ b/Tools/Scripts/webkitpy/layout_tests/servers/http_server_base.py
@@ -65,7 +65,7 @@ class HttpServerBase(object):
tmpdir = '/tmp'
self._runtime_path = self._filesystem.join(tmpdir, "WebKit")
- port_obj.maybe_make_directory(self._runtime_path)
+ self._filesystem.maybe_make_directory(self._runtime_path)
def start(self):
"""Starts the server. It is an error to start an already started server.
diff --git a/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py b/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py
new file mode 100644
index 000000000..a037a37dd
--- /dev/null
+++ b/Tools/Scripts/webkitpy/layout_tests/servers/http_server_unittest.py
@@ -0,0 +1,59 @@
+# Copyright (C) 2012 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 name of Google Inc. 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.
+
+import unittest
+import re
+
+from webkitpy.common.host_mock import MockHost
+from webkitpy.layout_tests.port import test
+from webkitpy.layout_tests.servers.http_server import Lighttpd
+from webkitpy.layout_tests.servers.http_server_base import ServerError
+
+
+class TestHttpServer(unittest.TestCase):
+ def test_start_cmd(self):
+ host = MockHost()
+ test_port = test.TestPort(host)
+ host.filesystem.write_text_file(
+ "/mock-checkout/Tools/Scripts/webkitpy/layout_tests/servers/lighttpd.conf", "Mock Config\n")
+ host.filesystem.write_text_file(
+ "/usr/lib/lighttpd/liblightcomp.dylib", "Mock dylib")
+
+ server = Lighttpd(test_port, "/mock/output_dir",
+ additional_dirs={
+ "/mock/one-additional-dir": "/mock-checkout/one-additional-dir",
+ "/mock/another-additional-dir": "/mock-checkout/one-additional-dir"})
+ self.assertRaises(ServerError, server.start)
+
+ config_file = host.filesystem.read_text_file("/mock/output_dir/lighttpd.conf")
+ self.assertEquals(re.findall(r"alias.url.+", config_file), [
+ 'alias.url = ( "/js-test-resources" => "/test.checkout/LayoutTests/fast/js/resources" )',
+ 'alias.url += ( "/mock/one-additional-dir" => "/mock-checkout/one-additional-dir" )',
+ 'alias.url += ( "/mock/another-additional-dir" => "/mock-checkout/one-additional-dir" )',
+ 'alias.url += ( "/media-resources" => "/test.checkout/LayoutTests/media" )',
+ ])
diff --git a/Tools/Scripts/webkitpy/layout_tests/views/metered_stream.py b/Tools/Scripts/webkitpy/layout_tests/views/metered_stream.py
index a3c2df441..b5c233b10 100644
--- a/Tools/Scripts/webkitpy/layout_tests/views/metered_stream.py
+++ b/Tools/Scripts/webkitpy/layout_tests/views/metered_stream.py
@@ -67,10 +67,6 @@ class MeteredStream:
"""Write a message that will be overwritten by subsequent update() or write() calls."""
self._overwrite(txt)
- def flush(self):
- # This seems to be needed on Python 2.5 for some reason.
- self._stream.flush()
-
def _overwrite(self, txt):
# Print the necessary number of backspaces to erase the previous
# message.
diff --git a/Tools/Scripts/webkitpy/layout_tests/views/metered_stream_unittest.py b/Tools/Scripts/webkitpy/layout_tests/views/metered_stream_unittest.py
index 7fd0b824b..359cfcf60 100644
--- a/Tools/Scripts/webkitpy/layout_tests/views/metered_stream_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/views/metered_stream_unittest.py
@@ -27,31 +27,31 @@
# (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 StringIO
import unittest
-from webkitpy.common.array_stream import ArrayStream
from webkitpy.layout_tests.views.metered_stream import MeteredStream
class TestMeteredStream(unittest.TestCase):
def test_regular(self):
- a = ArrayStream()
- m = MeteredStream(stream=a)
- self.assertTrue(a.empty())
+ a = StringIO.StringIO()
+ m = MeteredStream(a)
+ self.assertFalse(a.getvalue())
# basic test
m.write("foo")
exp = ['foo']
- self.assertEquals(a.get(), exp)
+ self.assertEquals(a.buflist, exp)
# now check that a second write() does not overwrite the first.
m.write("bar")
exp.append('bar')
- self.assertEquals(a.get(), exp)
+ self.assertEquals(a.buflist, exp)
m.update("batter")
exp.append('batter')
- self.assertEquals(a.get(), exp)
+ self.assertEquals(a.buflist, exp)
# The next update() should overwrite the laste update() but not the
# other text. Note that the cursor is effectively positioned at the
@@ -59,22 +59,23 @@ class TestMeteredStream(unittest.TestCase):
m.update("foo")
exp.append('\b\b\b\b\b\b \b\b\b\b\b\b')
exp.append('foo')
- self.assertEquals(a.get(), exp)
+ self.assertEquals(a.buflist, exp)
# now check that a write() does overwrite the update
m.write("foo")
exp.append('\b\b\b \b\b\b')
exp.append('foo')
- self.assertEquals(a.get(), exp)
+ self.assertEquals(a.buflist, exp)
# Now test that we only back up to the most recent newline.
# Note also that we do not back up to erase the most recent write(),
# i.e., write()s do not get erased.
- a.reset()
+ a = StringIO.StringIO()
+ m = MeteredStream(a)
m.update("foo\nbar")
m.update("baz")
- self.assertEquals(a.get(), ['foo\nbar', '\b\b\b \b\b\b', 'baz'])
+ self.assertEquals(a.buflist, ['foo\nbar', '\b\b\b \b\b\b', 'baz'])
if __name__ == '__main__':
diff --git a/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py b/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
index 0916e8ef6..6899d2c5d 100644
--- a/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
+++ b/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
@@ -31,10 +31,10 @@
import logging
import optparse
+import StringIO
import time
import unittest
-from webkitpy.common import array_stream
from webkitpy.common.host_mock import MockHost
from webkitpy.common.system import logtesting
@@ -54,24 +54,33 @@ def get_options(args):
class TestUtilityFunctions(unittest.TestCase):
+ def assertEmpty(self, stream):
+ self.assertFalse(stream.getvalue())
+
+ def assertNotEmpty(self, stream):
+ self.assertTrue(stream.getvalue())
+
+ def assertWritten(self, stream, contents):
+ self.assertEquals(stream.buflist, contents)
+
def test_configure_logging(self):
options, args = get_options([])
- stream = array_stream.ArrayStream()
+ stream = StringIO.StringIO()
handler = printing._configure_logging(stream, options.verbose)
logging.info("this should be logged")
- self.assertFalse(stream.empty())
+ self.assertNotEmpty(stream)
- stream.reset()
+ stream = StringIO.StringIO()
logging.debug("this should not be logged")
- self.assertTrue(stream.empty())
+ self.assertEmpty(stream)
printing._restore_logging(handler)
- stream.reset()
+ stream = StringIO.StringIO()
options, args = get_options(['--verbose'])
handler = printing._configure_logging(stream, options.verbose)
logging.debug("this should be logged")
- self.assertFalse(stream.empty())
+ self.assertNotEmpty(stream)
printing._restore_logging(handler)
def test_print_options(self):
@@ -110,6 +119,19 @@ class TestUtilityFunctions(unittest.TestCase):
class Testprinter(unittest.TestCase):
+ def assertEmpty(self, stream):
+ self.assertFalse(stream.getvalue())
+
+ def assertNotEmpty(self, stream):
+ self.assertTrue(stream.getvalue())
+
+ def assertWritten(self, stream, contents):
+ self.assertEquals(stream.buflist, contents)
+
+ def reset(self, stream):
+ stream.buflist = []
+ stream.buf = ''
+
def get_printer(self, args=None, tty=False):
args = args or []
printing_options = printing.print_options()
@@ -119,8 +141,9 @@ class Testprinter(unittest.TestCase):
self._port = host.port_factory.get('test', options)
nproc = 2
- regular_output = array_stream.ArrayStream(tty=tty)
- buildbot_output = array_stream.ArrayStream()
+ regular_output = StringIO.StringIO()
+ regular_output.isatty = lambda: tty
+ buildbot_output = StringIO.StringIO()
printer = printing.Printer(self._port, options, regular_output,
buildbot_output, configure_logging=True)
return printer, regular_output, buildbot_output
@@ -151,16 +174,16 @@ class Testprinter(unittest.TestCase):
# This routine should print something to stdout. testing what it is
# is kind of pointless.
printer.help_printing()
- self.assertFalse(err.empty())
- self.assertTrue(out.empty())
+ self.assertNotEmpty(err)
+ self.assertEmpty(out)
def do_switch_tests(self, method_name, switch, to_buildbot,
message='hello', exp_err=None, exp_bot=None):
def do_helper(method_name, switch, message, exp_err, exp_bot):
printer, err, bot = self.get_printer(['--print', switch], tty=True)
getattr(printer, method_name)(message)
- self.assertEqual(err.get(), exp_err)
- self.assertEqual(bot.get(), exp_bot)
+ self.assertEqual(err.buflist, exp_err)
+ self.assertEqual(bot.buflist, exp_bot)
if to_buildbot:
if exp_err is None:
@@ -211,31 +234,27 @@ class Testprinter(unittest.TestCase):
def test_print_one_line_summary(self):
printer, err, out = self.get_printer(['--print', 'nothing'])
printer.print_one_line_summary(1, 1, 0)
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
printer, err, out = self.get_printer(['--print', 'one-line-summary'])
printer.print_one_line_summary(1, 1, 0)
- self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"])
+ self.assertWritten(err, ["All 1 tests ran as expected.\n", "\n"])
printer, err, out = self.get_printer(['--print', 'everything'])
printer.print_one_line_summary(1, 1, 0)
- self.assertEquals(err.get(), ["All 1 tests ran as expected.\n", "\n"])
+ self.assertWritten(err, ["All 1 tests ran as expected.\n", "\n"])
- err.reset()
+ printer, err, out = self.get_printer(['--print', 'everything'])
printer.print_one_line_summary(2, 1, 1)
- self.assertEquals(err.get(),
- ["1 test ran as expected, 1 didn't:\n", "\n"])
+ self.assertWritten(err, ["1 test ran as expected, 1 didn't:\n", "\n"])
- err.reset()
+ printer, err, out = self.get_printer(['--print', 'everything'])
printer.print_one_line_summary(3, 2, 1)
- self.assertEquals(err.get(),
- ["2 tests ran as expected, 1 didn't:\n", "\n"])
+ self.assertWritten(err, ["2 tests ran as expected, 1 didn't:\n", "\n"])
- err.reset()
+ printer, err, out = self.get_printer(['--print', 'everything'])
printer.print_one_line_summary(3, 2, 0)
- self.assertEquals(err.get(),
- ['\n', "2 tests ran as expected (1 didn't run).\n",
- '\n'])
+ self.assertWritten(err, ['\n', "2 tests ran as expected (1 didn't run).\n", '\n'])
def test_print_test_result(self):
@@ -253,55 +272,55 @@ class Testprinter(unittest.TestCase):
result = self.get_result('passes/image.html')
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
printer, err, out = self.get_printer(['--print', 'unexpected'])
printer.print_test_result(result, expected=True, exp_str='',
got_str='')
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
- self.assertEquals(err.get(),
- [' passes/image.html -> unexpected pass\n'])
+ self.assertWritten(err, [' passes/image.html -> unexpected pass\n'])
printer, err, out = self.get_printer(['--print', 'everything'])
printer.print_test_result(result, expected=True, exp_str='',
got_str='')
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
- self.assertEquals(err.get(),
- [' passes/image.html -> unexpected pass\n'])
+ self.assertWritten(err, [' passes/image.html -> unexpected pass\n'])
printer, err, out = self.get_printer(['--print', 'nothing'])
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
printer, err, out = self.get_printer(['--print',
'trace-unexpected'])
printer.print_test_result(result, expected=True, exp_str='',
got_str='')
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
printer, err, out = self.get_printer(['--print',
'trace-unexpected'])
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
- self.assertFalse(err.empty())
+ self.assertNotEmpty(err)
printer, err, out = self.get_printer(['--print',
'trace-unexpected'])
result = self.get_result("passes/text.html")
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
- self.assertFalse(err.empty())
+ self.assertNotEmpty(err)
- err.reset()
+ printer, err, out = self.get_printer(['--print',
+ 'trace-unexpected'])
+ result = self.get_result("passes/text.html")
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
- self.assertFalse(err.empty())
+ self.assertNotEmpty(err)
printer, err, out = self.get_printer(['--print', 'trace-everything'])
result = self.get_result('passes/image.html')
@@ -316,9 +335,10 @@ class Testprinter(unittest.TestCase):
result = self.get_result('failures/expected/missing_image.html')
printer.print_test_result(result, expected=True, exp_str='',
got_str='')
- self.assertFalse(err.empty())
+ self.assertNotEmpty(err)
- err.reset()
+ printer, err, out = self.get_printer(['--print', 'trace-everything'])
+ result = self.get_result('passes/image.html')
printer.print_test_result(result, expected=False, exp_str='',
got_str='')
@@ -332,12 +352,12 @@ class Testprinter(unittest.TestCase):
# First, test that we print nothing.
printer.print_progress(rs, False, paths)
- self.assertTrue(out.empty())
- self.assertTrue(err.empty())
+ self.assertEmpty(out)
+ self.assertEmpty(err)
printer.print_progress(rs, True, paths)
- self.assertTrue(out.empty())
- self.assertTrue(err.empty())
+ self.assertEmpty(out)
+ self.assertEmpty(err)
self.times = [1, 2, 12, 13, 14, 23, 33]
@@ -352,69 +372,71 @@ class Testprinter(unittest.TestCase):
printer, err, out = self.get_printer(['--print', 'one-line-progress'])
printer.print_progress(rs, False, paths)
printer.print_progress(rs, False, paths)
- self.assertTrue(out.empty())
- self.assertTrue(err.empty())
+ self.assertEmpty(out)
+ self.assertEmpty(err)
printer.print_progress(rs, False, paths)
- self.assertTrue(out.empty())
- self.assertFalse(err.empty())
+ self.assertEmpty(out)
+ self.assertNotEmpty(err)
- err.reset()
- out.reset()
+ self.reset(err)
+ self.reset(out)
printer.print_progress(rs, True, paths)
- self.assertTrue(out.empty())
- self.assertTrue(err.empty())
+ self.assertEmpty(out)
+ self.assertEmpty(err)
printer.print_progress(rs, True, paths)
- self.assertTrue(out.empty())
- self.assertFalse(err.empty())
+ self.assertEmpty(out)
+ self.assertNotEmpty(err)
# Now reconfigure the printer to test printing to a TTY instead of a file.
self.times = [1, 1.01, 2, 3]
printer, err, out = self.get_printer(['--print', 'one-line-progress'], tty=True)
printer.print_progress(rs, False, paths)
printer.print_progress(rs, False, paths)
- self.assertTrue(out.empty())
- self.assertTrue(err.empty())
+ self.assertEmpty(out)
+ self.assertEmpty(err)
printer.print_progress(rs, False, paths)
- self.assertTrue(out.empty())
- self.assertFalse(err.empty())
+ self.assertEmpty(out)
+ self.assertNotEmpty(err)
- err.reset()
- out.reset()
+ self.reset(err)
+ self.reset(out)
printer.print_progress(rs, True, paths)
- self.assertTrue(out.empty())
- self.assertFalse(err.empty())
+ self.assertEmpty(out)
+ self.assertNotEmpty(err)
finally:
time.time = orig_time
def test_write_nothing(self):
printer, err, out = self.get_printer(['--print', 'nothing'])
printer.write("foo")
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
def test_write_misc(self):
printer, err, out = self.get_printer(['--print', 'misc'])
printer.write("foo")
- self.assertFalse(err.empty())
- err.reset()
+ self.assertNotEmpty(err)
+
+ printer, err, out = self.get_printer(['--print', 'misc'])
printer.write("foo", "config")
- self.assertTrue(err.empty())
+ self.assertEmpty(err)
def test_write_everything(self):
printer, err, out = self.get_printer(['--print', 'everything'])
printer.write("foo")
- self.assertFalse(err.empty())
- err.reset()
+ self.assertNotEmpty(err)
+
+ printer, err, out = self.get_printer(['--print', 'everything'])
printer.write("foo", "config")
- self.assertFalse(err.empty())
+ self.assertNotEmpty(err)
def test_write_verbose(self):
printer, err, out = self.get_printer(['--verbose'])
printer.write("foo")
- self.assertTrue(not err.empty() and "foo" in err.get()[0])
- self.assertTrue(out.empty())
+ self.assertTrue("foo" in err.buflist[0])
+ self.assertEmpty(out)
def test_print_unexpected_results(self):
# This routine is the only one that prints stuff that the bots
@@ -470,68 +492,60 @@ class Testprinter(unittest.TestCase):
printer, err, out = self.get_printer(['--print', 'nothing'])
ur = get_unexpected_results(expected=False, passing=False, flaky=False)
printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertTrue(out.empty())
+ self.assertEmpty(err)
+ self.assertEmpty(out)
printer, err, out = self.get_printer(['--print', 'unexpected-results'])
# test everything running as expected
ur = get_unexpected_results(expected=True, passing=False, flaky=False)
printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertTrue(out.empty())
+ self.assertEmpty(err)
+ self.assertEmpty(out)
# test failures
- err.reset()
- out.reset()
+ printer, err, out = self.get_printer(['--print', 'unexpected-results'])
ur = get_unexpected_results(expected=False, passing=False, flaky=False)
printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
+ self.assertEmpty(err)
+ self.assertNotEmpty(out)
# test unexpected flaky
- err.reset()
- out.reset()
+ printer, err, out = self.get_printer(['--print', 'unexpected-results'])
ur = get_unexpected_results(expected=False, passing=False, flaky=True)
printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
+ self.assertEmpty(err)
+ self.assertNotEmpty(out)
- err.reset()
- out.reset()
printer, err, out = self.get_printer(['--print', 'everything'])
ur = get_unexpected_results(expected=False, passing=False, flaky=False)
printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
+ self.assertEmpty(err)
+ self.assertNotEmpty(out)
expectations = """
BUGX : failures/expected/crash.html = CRASH
BUGX : failures/expected/timeout.html = TIMEOUT
"""
- err.reset()
- out.reset()
+ printer, err, out = self.get_printer(['--print', 'unexpected-results'])
ur = get_unexpected_results(expected=False, passing=False, flaky=False)
printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
+ self.assertEmpty(err)
+ self.assertNotEmpty(out)
- err.reset()
- out.reset()
+ printer, err, out = self.get_printer(['--print', 'unexpected-results'])
ur = get_unexpected_results(expected=False, passing=True, flaky=False)
printer.print_unexpected_results(ur)
- self.assertTrue(err.empty())
- self.assertFalse(out.empty())
+ self.assertEmpty(err)
+ self.assertNotEmpty(out)
# Test handling of --verbose as well.
- err.reset()
- out.reset()
printer, err, out = self.get_printer(['--verbose'])
ur = get_unexpected_results(expected=False, passing=False, flaky=False)
printer.print_unexpected_results(ur)
# FIXME: debug output from the port and scm objects may or may not go
# to stderr, so there's no point in testing its contents here.
- self.assertFalse(out.empty())
+ self.assertNotEmpty(out)
def test_print_unexpected_results_buildbot(self):
# FIXME: Test that print_unexpected_results() produces the printer the
diff --git a/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py b/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
index d605829a0..189500f6a 100644
--- a/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
+++ b/Tools/Scripts/webkitpy/performance_tests/perftestsrunner.py
@@ -79,6 +79,8 @@ class PerfTestsRunner(object):
help='Set the configuration to Release'),
optparse.make_option("--platform",
help="Specify port/platform being tested (i.e. chromium-mac)"),
+ optparse.make_option("--chromium",
+ action="store_const", const='chromium', dest='platform', help='Alias for --platform=chromium'),
optparse.make_option("--builder-name",
help=("The name of the builder shown on the waterfall running this script e.g. google-mac-2.")),
optparse.make_option("--build-number",
@@ -89,6 +91,8 @@ class PerfTestsRunner(object):
help="Path to the directory under which build files are kept (should not include configuration)"),
optparse.make_option("--time-out-ms", default=600 * 1000,
help="Set the timeout for each test"),
+ optparse.make_option("--pause-before-testing", dest="pause_before_testing", action="store_true", default=False,
+ help="Pause before running the tests to let user attach a performance monitor."),
optparse.make_option("--output-json-path",
help="Filename of the JSON file that summaries the results"),
optparse.make_option("--source-json-path",
@@ -215,6 +219,12 @@ class PerfTestsRunner(object):
for test in tests:
driver = port.create_driver(worker_number=1, no_timeout=True)
+ if self._options.pause_before_testing:
+ driver.start()
+ if not self._host.user.confirm("Ready to run test?"):
+ driver.stop()
+ return unexpected
+
relative_test_path = self._host.filesystem.relpath(test, self._base_path)
self._printer.write('Running %s (%d of %d)' % (relative_test_path, expected + unexpected + 1, len(tests)))
@@ -249,6 +259,7 @@ class PerfTestsRunner(object):
_lines_to_ignore_in_parser_result = [
re.compile(r'^Running \d+ times$'),
re.compile(r'^Ignoring warm-up '),
+ re.compile(r'^Info:'),
re.compile(r'^\d+(.\d+)?$'),
# Following are for handle existing test like Dromaeo
re.compile(re.escape("""main frame - has 1 onunload handler(s)""")),
diff --git a/Tools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py b/Tools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py
index 5c4aafdab..e194072c3 100755
--- a/Tools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py
+++ b/Tools/Scripts/webkitpy/performance_tests/perftestsrunner_unittest.py
@@ -33,7 +33,6 @@ import StringIO
import json
import unittest
-from webkitpy.common import array_stream
from webkitpy.common.host_mock import MockHost
from webkitpy.common.system.filesystem_mock import MockFileSystem
from webkitpy.common.system.outputcapture import OutputCapture
@@ -44,6 +43,9 @@ from webkitpy.performance_tests.perftestsrunner import PerfTestsRunner
class MainTest(unittest.TestCase):
+ def assertWritten(self, stream, contents):
+ self.assertEquals(stream.buflist, contents)
+
class TestDriver:
def run_test(self, driver_input):
text = ''
@@ -101,12 +103,15 @@ max 1120
"""
return DriverOutput(text, '', '', '', crash=crash, timeout=timeout)
+ def start(self):
+ """do nothing"""
+
def stop(self):
"""do nothing"""
def create_runner(self, buildbot_output=None, args=[], regular_output=None, driver_class=TestDriver):
- buildbot_output = buildbot_output or array_stream.ArrayStream()
- regular_output = regular_output or array_stream.ArrayStream()
+ buildbot_output = buildbot_output or StringIO.StringIO()
+ regular_output = regular_output or StringIO.StringIO()
options, parsed_args = PerfTestsRunner._parse_args(args)
test_port = TestPort(host=MockHost(), options=options)
@@ -142,28 +147,24 @@ max 1120
self.assertFalse(self.run_test('crash.html'))
def test_run_test_set(self):
- buildbot_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
runner = self.create_runner(buildbot_output)
dirname = runner._base_path + '/inspector/'
tests = [dirname + 'pass.html', dirname + 'silent.html', dirname + 'failed.html',
dirname + 'tonguey.html', dirname + 'timeout.html', dirname + 'crash.html']
unexpected_result_count = runner._run_tests_set(tests, runner._port)
self.assertEqual(unexpected_result_count, len(tests) - 1)
- self.assertEqual(len(buildbot_output.get()), 1)
- self.assertEqual(buildbot_output.get()[0], 'RESULT group_name: test_name= 42 ms\n')
+ self.assertWritten(buildbot_output, ['RESULT group_name: test_name= 42 ms\n'])
def test_run_test_set_kills_drt_per_run(self):
class TestDriverWithStopCount(MainTest.TestDriver):
stop_count = 0
- def __init__(self):
- TestDriverWithStopCount.sotp_count = 0
-
def stop(self):
TestDriverWithStopCount.stop_count += 1
- buildbot_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
runner = self.create_runner(buildbot_output, driver_class=TestDriverWithStopCount)
dirname = runner._base_path + '/inspector/'
@@ -173,28 +174,50 @@ max 1120
unexpected_result_count = runner._run_tests_set(tests, runner._port)
self.assertEqual(TestDriverWithStopCount.stop_count, 6)
+ def test_run_test_set_kills_drt_per_run(self):
+ class TestDriverWithStartCount(MainTest.TestDriver):
+ start_count = 0
+
+ def start(self):
+ TestDriverWithStartCount.start_count += 1
+
+ buildbot_output = StringIO.StringIO()
+ runner = self.create_runner(buildbot_output, args=["--pause-before-testing"], driver_class=TestDriverWithStartCount)
+
+ dirname = runner._base_path + '/inspector/'
+ tests = [dirname + 'pass.html']
+
+ try:
+ output = OutputCapture()
+ output.capture_output()
+ unexpected_result_count = runner._run_tests_set(tests, runner._port)
+ self.assertEqual(TestDriverWithStartCount.start_count, 1)
+ finally:
+ _, stderr, logs = output.restore_output()
+ self.assertEqual(stderr, "Ready to run test?\n")
+ self.assertEqual(logs, "Running inspector/pass.html (1 of 1)\n\n")
+
def test_run_test_set_for_parser_tests(self):
- buildbot_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
runner = self.create_runner(buildbot_output)
tests = [runner._base_path + '/Bindings/event-target-wrapper.html', runner._base_path + '/Parser/some-parser.html']
unexpected_result_count = runner._run_tests_set(tests, runner._port)
self.assertEqual(unexpected_result_count, 0)
- self.assertEqual(buildbot_output.get()[0], 'RESULT Bindings: event-target-wrapper= 1489.05 ms\n')
- self.assertEqual(buildbot_output.get()[1], 'median= 1487.0 ms, stdev= 14.46 ms, min= 1471.0 ms, max= 1510.0 ms\n')
- self.assertEqual(buildbot_output.get()[2], 'RESULT Parser: some-parser= 1100.0 ms\n')
- self.assertEqual(buildbot_output.get()[3], 'median= 1101.0 ms, stdev= 11.0 ms, min= 1080.0 ms, max= 1120.0 ms\n')
+ self.assertWritten(buildbot_output, ['RESULT Bindings: event-target-wrapper= 1489.05 ms\n',
+ 'median= 1487.0 ms, stdev= 14.46 ms, min= 1471.0 ms, max= 1510.0 ms\n',
+ 'RESULT Parser: some-parser= 1100.0 ms\n',
+ 'median= 1101.0 ms, stdev= 11.0 ms, min= 1080.0 ms, max= 1120.0 ms\n'])
def test_run_test_set_with_json_output(self):
- buildbot_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
runner = self.create_runner(buildbot_output, args=['--output-json-path=/mock-checkout/output.json'])
runner._host.filesystem.files[runner._base_path + '/inspector/pass.html'] = True
runner._host.filesystem.files[runner._base_path + '/Bindings/event-target-wrapper.html'] = True
runner._timestamp = 123456789
self.assertEqual(runner.run(), 0)
- self.assertEqual(len(buildbot_output.get()), 3)
- self.assertEqual(buildbot_output.get()[0], 'RESULT Bindings: event-target-wrapper= 1489.05 ms\n')
- self.assertEqual(buildbot_output.get()[1], 'median= 1487.0 ms, stdev= 14.46 ms, min= 1471.0 ms, max= 1510.0 ms\n')
- self.assertEqual(buildbot_output.get()[2], 'RESULT group_name: test_name= 42 ms\n')
+ self.assertWritten(buildbot_output, ['RESULT Bindings: event-target-wrapper= 1489.05 ms\n',
+ 'median= 1487.0 ms, stdev= 14.46 ms, min= 1471.0 ms, max= 1510.0 ms\n',
+ 'RESULT group_name: test_name= 42 ms\n'])
self.assertEqual(json.loads(runner._host.filesystem.files['/mock-checkout/output.json']), {
"timestamp": 123456789, "results":
@@ -203,7 +226,7 @@ max 1120
"webkit-revision": 5678})
def test_run_test_set_with_json_source(self):
- buildbot_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
runner = self.create_runner(buildbot_output, args=['--output-json-path=/mock-checkout/output.json',
'--source-json-path=/mock-checkout/source.json'])
runner._host.filesystem.files['/mock-checkout/source.json'] = '{"key": "value"}'
@@ -211,10 +234,9 @@ max 1120
runner._host.filesystem.files[runner._base_path + '/Bindings/event-target-wrapper.html'] = True
runner._timestamp = 123456789
self.assertEqual(runner.run(), 0)
- self.assertEqual(len(buildbot_output.get()), 3)
- self.assertEqual(buildbot_output.get()[0], 'RESULT Bindings: event-target-wrapper= 1489.05 ms\n')
- self.assertEqual(buildbot_output.get()[1], 'median= 1487.0 ms, stdev= 14.46 ms, min= 1471.0 ms, max= 1510.0 ms\n')
- self.assertEqual(buildbot_output.get()[2], 'RESULT group_name: test_name= 42 ms\n')
+ self.assertWritten(buildbot_output, ['RESULT Bindings: event-target-wrapper= 1489.05 ms\n',
+ 'median= 1487.0 ms, stdev= 14.46 ms, min= 1471.0 ms, max= 1510.0 ms\n',
+ 'RESULT group_name: test_name= 42 ms\n'])
self.assertEqual(json.loads(runner._host.filesystem.files['/mock-checkout/output.json']), {
"timestamp": 123456789, "results":
@@ -224,7 +246,7 @@ max 1120
"key": "value"})
def test_run_test_set_with_multiple_repositories(self):
- buildbot_output = array_stream.ArrayStream()
+ buildbot_output = StringIO.StringIO()
runner = self.create_runner(buildbot_output, args=['--output-json-path=/mock-checkout/output.json'])
runner._host.filesystem.files[runner._base_path + '/inspector/pass.html'] = True
runner._timestamp = 123456789
@@ -261,7 +283,7 @@ max 1120
self.assertEqual(runner.run(), -3)
def test_upload_json(self):
- regular_output = array_stream.ArrayStream()
+ regular_output = StringIO.StringIO()
runner = self.create_runner(regular_output=regular_output)
runner._host.filesystem.files['/mock-checkout/some.json'] = 'some content'
diff --git a/Tools/Scripts/webkitpy/style/checker.py b/Tools/Scripts/webkitpy/style/checker.py
index 78235b3fa..f7c82cfea 100644
--- a/Tools/Scripts/webkitpy/style/checker.py
+++ b/Tools/Scripts/webkitpy/style/checker.py
@@ -237,6 +237,10 @@ _PATH_RULES_SPECIFIER = [
# and __jit_debug_descriptor when integrating with gdb.
"Source/JavaScriptCore/jit/GDBInterface.cpp"],
["-readability/naming"]),
+
+ ([# On some systems the trailing CR is causing parser failure.
+ "Source/JavaScriptCore/parser/Keywords.table"],
+ ["+whitespace/carriage_return"]),
]
@@ -271,6 +275,7 @@ _TEXT_FILE_EXTENSIONS = [
'pro',
'rb',
'sh',
+ 'table',
'txt',
'wm',
'xhtml',
diff --git a/Tools/Scripts/webkitpy/style/checker_unittest.py b/Tools/Scripts/webkitpy/style/checker_unittest.py
index bc1441577..60a959faf 100755
--- a/Tools/Scripts/webkitpy/style/checker_unittest.py
+++ b/Tools/Scripts/webkitpy/style/checker_unittest.py
@@ -268,6 +268,9 @@ class GlobalVariablesTest(unittest.TestCase):
assertNoCheck("Source/JavaScriptCore/jit/GDBInterface.cpp",
"readability/naming")
+ # Javascript keywords.
+ assertCheck("Source/JavaScriptCore/parser/Keywords.table", "whitespace/carriage_return")
+
def test_max_reports_per_category(self):
"""Check that _MAX_REPORTS_PER_CATEGORY is valid."""
all_categories = self._all_categories()
diff --git a/Tools/Scripts/webkitpy/style/checkers/cpp.py b/Tools/Scripts/webkitpy/style/checkers/cpp.py
index 27ffecaa8..f29361766 100644
--- a/Tools/Scripts/webkitpy/style/checkers/cpp.py
+++ b/Tools/Scripts/webkitpy/style/checkers/cpp.py
@@ -2077,6 +2077,29 @@ def check_max_min_macros(clean_lines, line_number, file_state, error):
% (max_min_macro_lower, max_min_macro_lower, max_min_macro))
+def check_ctype_functions(clean_lines, line_number, file_state, error):
+ """Looks for use of the standard functions in ctype.h and suggest they be replaced
+ by use of equivilent ones in <wtf/ASCIICType.h>?.
+
+ Args:
+ clean_lines: A CleansedLines instance containing the file.
+ line_number: The number of the line to check.
+ file_state: A _FileState instance which maintains information about
+ the state of things in the file.
+ error: The function to call with any errors found.
+ """
+
+ line = clean_lines.elided[line_number] # Get rid of comments and strings.
+
+ ctype_function_search = search(r'\b(?P<ctype_function>(isalnum|isalpha|isascii|isblank|iscntrl|isdigit|isgraph|islower|isprint|ispunct|isspace|isupper|isxdigit|toascii|tolower|toupper))\s*\(', line)
+ if not ctype_function_search:
+ return
+
+ ctype_function = ctype_function_search.group('ctype_function')
+ error(line_number, 'runtime/ctype_function', 4,
+ 'Use equivelent function in <wtf/ASCIICType.h> instead of the %s() function.'
+ % (ctype_function))
+
def check_switch_indentation(clean_lines, line_number, error):
"""Looks for indentation errors inside of switch statements.
@@ -2540,6 +2563,7 @@ def check_style(clean_lines, line_number, file_extension, class_state, file_stat
check_namespace_indentation(clean_lines, line_number, file_extension, file_state, error)
check_using_std(clean_lines, line_number, file_state, error)
check_max_min_macros(clean_lines, line_number, file_state, error)
+ check_ctype_functions(clean_lines, line_number, file_state, error)
check_switch_indentation(clean_lines, line_number, error)
check_braces(clean_lines, line_number, error)
check_exit_statement_simplifications(clean_lines, line_number, error)
@@ -2980,6 +3004,11 @@ def check_language(filename, clean_lines, line_number, file_extension, include_s
check_identifier_name_in_declaration(filename, line_number, line, file_state, error)
+ # Check that we're not using static_cast<Text*>.
+ if search(r'\bstatic_cast<Text\*>', line):
+ error(line_number, 'readability/check', 4,
+ 'Consider using toText helper function in WebCore/dom/Text.h '
+ 'instead of static_cast<Text*>')
def check_identifier_name_in_declaration(filename, line_number, line, file_state, error):
"""Checks if identifier names contain any underscores.
@@ -3523,6 +3552,7 @@ class CppChecker(object):
'runtime/arrays',
'runtime/bitfields',
'runtime/casting',
+ 'runtime/ctype_function',
'runtime/explicit',
'runtime/init',
'runtime/int',
diff --git a/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py b/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py
index 0c5c6041f..044f46b19 100644
--- a/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py
+++ b/Tools/Scripts/webkitpy/style/checkers/cpp_unittest.py
@@ -759,6 +759,14 @@ class CppStyleTest(CppStyleTestBase):
self.assert_language_rules_check('foo.cpp', statement, error_message)
self.assert_language_rules_check('foo.h', statement, error_message)
+ # Test for static_cast readability.
+ def test_static_cast_readability(self):
+ self.assert_lint(
+ 'Text* x = static_cast<Text*>(foo);',
+ 'Consider using toText helper function in WebCore/dom/Text.h '
+ 'instead of static_cast<Text*>'
+ ' [readability/check] [4]')
+
# We cannot test this functionality because of difference of
# function definitions. Anyway, we may never enable this.
#
@@ -775,7 +783,7 @@ class CppStyleTest(CppStyleTestBase):
# self.assert_lint('void Method(char* /*x*/);', message)
# self.assert_lint('typedef void (*Method)(int32);', message)
# self.assert_lint('static void operator delete[](void*) throw();', message)
- #
+ #
# self.assert_lint('virtual void D(int* p);', '')
# self.assert_lint('void operator delete(void* x) throw();', '')
# self.assert_lint('void Method(char* x)\n{', '')
@@ -784,7 +792,7 @@ class CppStyleTest(CppStyleTestBase):
# self.assert_lint('typedef void (*Method)(int32 x);', '')
# self.assert_lint('static void operator delete[](void* x) throw();', '')
# self.assert_lint('static void operator delete[](void* /*x*/) throw();', '')
- #
+ #
# # This one should technically warn, but doesn't because the function
# # pointer is confusing.
# self.assert_lint('virtual void E(void (*fn)(int* p));', '')
@@ -3064,7 +3072,7 @@ class CheckForFunctionLengthsTest(CppStyleTestBase):
def test_function_length_check_definition_huge_lines(self):
# 5 is the limit
- self.assert_function_length_check_definition(self.trigger_lines(10), 5)
+ self.assert_function_length_check_definition(self.trigger_lines(6), 5)
def test_function_length_not_determinable(self):
# Macro invocation without terminating semicolon.
@@ -4339,6 +4347,13 @@ class WebKitStyleTest(CppStyleTestBase):
' [runtime/max_min_macros] [4]',
'foo.h')
+ def test_ctype_fucntion(self):
+ self.assert_lint(
+ 'int i = isascii(8);',
+ 'Use equivelent function in <wtf/ASCIICType.h> instead of the '
+ 'isascii() function. [runtime/ctype_function] [4]',
+ 'foo.cpp')
+
def test_names(self):
name_underscore_error_message = " is incorrectly named. Don't use underscores in your identifier names. [readability/naming] [4]"
name_tooshort_error_message = " is incorrectly named. Don't use the single letter 'l' as an identifier name. [readability/naming] [4]"
diff --git a/Tools/Scripts/webkitpy/thirdparty/__init__.py b/Tools/Scripts/webkitpy/thirdparty/__init__.py
index ead89fff0..e7a12e685 100644
--- a/Tools/Scripts/webkitpy/thirdparty/__init__.py
+++ b/Tools/Scripts/webkitpy/thirdparty/__init__.py
@@ -127,8 +127,8 @@ class AutoinstallImportHook(object):
def _install_pywebsocket(self):
pywebsocket_dir = self._fs.join(_AUTOINSTALLED_DIR, "pywebsocket")
installer = AutoInstaller(target_dir=pywebsocket_dir)
- installer.install(url="http://pywebsocket.googlecode.com/files/mod_pywebsocket-0.7.tar.gz",
- url_subpath="pywebsocket-0.7/src/mod_pywebsocket")
+ installer.install(url="http://pywebsocket.googlecode.com/files/mod_pywebsocket-0.7.2.tar.gz",
+ url_subpath="pywebsocket-0.7.2/src/mod_pywebsocket")
def _install_xmlrunner(self):
self._install("http://pypi.python.org/packages/source/u/unittest-xml-reporting/unittest-xml-reporting-1.0.3.tar.gz#md5=cebf83281b0753b5d42bad38c91fd4d6",
diff --git a/Tools/Scripts/webkitpy/to_be_moved/rebaseline_chromium_webkit_tests.py b/Tools/Scripts/webkitpy/to_be_moved/rebaseline_chromium_webkit_tests.py
index 2219879f2..27db72022 100644
--- a/Tools/Scripts/webkitpy/to_be_moved/rebaseline_chromium_webkit_tests.py
+++ b/Tools/Scripts/webkitpy/to_be_moved/rebaseline_chromium_webkit_tests.py
@@ -512,7 +512,7 @@ class Rebaseliner(object):
if is_image:
return self._port.diff_image(output1, output2)[0]
- return self._port.compare_text(output1, output2)
+ return self._port.do_text_results_differ(output1, output2)
def _delete_baseline(self, filename):
"""Remove the file from repository and delete it from disk.
diff --git a/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py b/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py
index 65a71a701..b66cfbc8e 100644
--- a/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py
+++ b/Tools/Scripts/webkitpy/tool/bot/earlywarningsystemtask.py
@@ -26,13 +26,7 @@
# (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 webkitpy.tool.bot.patchanalysistask import PatchAnalysisTask, PatchAnalysisTaskDelegate
-
-
-class UnableToApplyPatch(Exception):
- def __init__(self, patch):
- Exception.__init__(self)
- self.patch = patch
+from webkitpy.tool.bot.patchanalysistask import PatchAnalysisTask, PatchAnalysisTaskDelegate, UnableToApplyPatch
class EarlyWarningSystemTaskDelegate(PatchAnalysisTaskDelegate):
diff --git a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py b/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py
index 47e23a1c4..bcd3d304b 100644
--- a/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py
+++ b/Tools/Scripts/webkitpy/tool/bot/patchanalysistask.py
@@ -30,6 +30,12 @@ from webkitpy.common.system.executive import ScriptError
from webkitpy.common.net.layouttestresults import LayoutTestResults
+class UnableToApplyPatch(Exception):
+ def __init__(self, patch):
+ Exception.__init__(self)
+ self.patch = patch
+
+
class PatchAnalysisTaskDelegate(object):
def parent_command(self):
raise NotImplementedError("subclasses must implement")
@@ -71,7 +77,6 @@ class PatchAnalysisTask(object):
self._results_archive_from_patch_test_run = None
self._results_from_patch_test_run = None
self._expected_failures = delegate.expected_failures()
- assert(self._expected_failures)
def _run_command(self, command, success_message, failure_message):
try:
diff --git a/Tools/Scripts/webkitpy/tool/bot/queueengine.py b/Tools/Scripts/webkitpy/tool/bot/queueengine.py
index 752ef748a..1d7535967 100644
--- a/Tools/Scripts/webkitpy/tool/bot/queueengine.py
+++ b/Tools/Scripts/webkitpy/tool/bot/queueengine.py
@@ -27,9 +27,7 @@
# (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 os
import sys
-import time
import traceback
from datetime import datetime, timedelta
@@ -60,10 +58,6 @@ class QueueEngineDelegate:
def next_work_item(self):
raise NotImplementedError, "subclasses must implement"
- def should_proceed_with_work_item(self, work_item):
- # returns (safe_to_proceed, waiting_message, patch)
- raise NotImplementedError, "subclasses must implement"
-
def process_work_item(self, work_item):
raise NotImplementedError, "subclasses must implement"
@@ -100,9 +94,6 @@ class QueueEngine:
if not work_item:
self._sleep("No work item.")
continue
- if not self._delegate.should_proceed_with_work_item(work_item):
- self._sleep("Not proceeding with work item.")
- continue
# FIXME: Work logs should not depend on bug_id specificaly.
# This looks fixed, no?
diff --git a/Tools/Scripts/webkitpy/tool/bot/queueengine_unittest.py b/Tools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
index d860c6c73..f959ee149 100644
--- a/Tools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
@@ -50,7 +50,6 @@ class LoggingDelegate(QueueEngineDelegate):
'begin_work_queue',
'should_continue_work_queue',
'next_work_item',
- 'should_proceed_with_work_item',
'work_item_log_path',
'process_work_item',
'should_continue_work_queue',
@@ -82,12 +81,6 @@ class LoggingDelegate(QueueEngineDelegate):
self.record("next_work_item")
return "work_item"
- def should_proceed_with_work_item(self, work_item):
- self.record("should_proceed_with_work_item")
- self._test.assertEquals(work_item, "work_item")
- fake_patch = {'bug_id': 50000}
- return (True, "waiting_message", fake_patch)
-
def process_work_item(self, work_item):
self.record("process_work_item")
self._test.assertEquals(work_item, "work_item")
@@ -112,13 +105,6 @@ class RaisingDelegate(LoggingDelegate):
raise self._exception
-class NotSafeToProceedDelegate(LoggingDelegate):
- def should_proceed_with_work_item(self, work_item):
- self.record("should_proceed_with_work_item")
- self._test.assertEquals(work_item, "work_item")
- return False
-
-
class FastQueueEngine(QueueEngine):
def __init__(self, delegate):
QueueEngine.__init__(self, "fast-queue", delegate, threading.Event())
@@ -179,14 +165,6 @@ class QueueEngineTest(unittest.TestCase):
self._test_terminating_queue(KeyboardInterrupt(), "User terminated queue.")
self._test_terminating_queue(TerminateQueue(), "TerminateQueue exception received.")
- def test_not_safe_to_proceed(self):
- delegate = NotSafeToProceedDelegate(self)
- self._run_engine(delegate, engine=FastQueueEngine(delegate))
- expected_callbacks = LoggingDelegate.expected_callbacks[:]
- expected_callbacks.remove('work_item_log_path')
- expected_callbacks.remove('process_work_item')
- self.assertEquals(delegate._callbacks, expected_callbacks)
-
def test_now(self):
"""Make sure there are no typos in the QueueEngine.now() method."""
engine = QueueEngine("test", None, None)
diff --git a/Tools/Scripts/webkitpy/tool/bot/sheriff_unittest.py b/Tools/Scripts/webkitpy/tool/bot/sheriff_unittest.py
index 690af1ffc..3ff5082f6 100644
--- a/Tools/Scripts/webkitpy/tool/bot/sheriff_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/bot/sheriff_unittest.py
@@ -26,7 +26,6 @@
# (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 os
import unittest
from webkitpy.common.net.buildbot import Builder
diff --git a/Tools/Scripts/webkitpy/common/array_stream.py b/Tools/Scripts/webkitpy/tool/bot/stylequeuetask.py
index 7d2c43925..01f7f723f 100644
--- a/Tools/Scripts/webkitpy/common/array_stream.py
+++ b/Tools/Scripts/webkitpy/tool/bot/stylequeuetask.py
@@ -1,5 +1,4 @@
-#!/usr/bin/python
-# Copyright (C) 2010 Google Inc. All rights reserved.
+# Copyright (c) 2012 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
@@ -27,44 +26,50 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Package that private an array-based implementation of a stream."""
+from webkitpy.tool.bot.patchanalysistask import PatchAnalysisTask, PatchAnalysisTaskDelegate, UnableToApplyPatch
-class ArrayStream(object):
- """Simple class that implmements a stream interface on top of an array.
+class StyleQueueTaskDelegate(PatchAnalysisTaskDelegate):
+ def parent_command(self):
+ return "style-queue"
- This is used primarily by unit test classes to mock output streams. It
- performs a similar function to StringIO, but (a) it is write-only, and
- (b) it can be used to retrieve each individual write(); StringIO
- concatenates all of the writes together.
- """
- def __init__(self, tty=False):
- self._contents = []
- self._tty = tty
+class StyleQueueTask(PatchAnalysisTask):
+ def validate(self):
+ self._patch = self._delegate.refetch_patch(self._patch)
+ if self._patch.is_obsolete():
+ return False
+ if self._patch.bug().is_closed():
+ return False
+ if self._patch.review() == "-":
+ return False
+ return True
- def write(self, msg):
- """Implement stream.write() by appending to the stream's contents."""
- self._contents.append(msg)
+ def _check_style(self):
+ return self._run_command([
+ "check-style-local",
+ "--non-interactive",
+ "--quiet",
+ ],
+ "Style checked",
+ "Patch did not pass style check")
- def get(self):
- """Return the contents of a stream (as an array)."""
- return self._contents
+ def _apply_watch_list(self):
+ return self._run_command([
+ "apply-watchlist-local",
+ self._patch.bug_id(),
+ ],
+ "Watchlist applied",
+ "Unabled to apply watchlist")
- def reset(self):
- """Empty the stream."""
- self._contents = []
-
- def empty(self):
- """Return whether the stream is empty."""
- return (len(self._contents) == 0)
-
- def flush(self):
- """Flush the stream (a no-op implemented for compatibility)."""
- pass
-
- def __repr__(self):
- return '<ArrayStream: ' + str(self._contents) + '>'
-
- def isatty(self):
- return self._tty
+ def run(self):
+ if not self._clean():
+ return False
+ if not self._update():
+ return False
+ if not self._apply():
+ raise UnableToApplyPatch(self._patch)
+ self._apply_watch_list()
+ if not self._check_style():
+ return self.report_failure()
+ return True
diff --git a/Tools/Scripts/webkitpy/tool/commands/download.py b/Tools/Scripts/webkitpy/tool/commands/download.py
index f18bf31a5..611ca9254 100644
--- a/Tools/Scripts/webkitpy/tool/commands/download.py
+++ b/Tools/Scripts/webkitpy/tool/commands/download.py
@@ -27,8 +27,6 @@
# (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 os
-
from webkitpy.tool import steps
from webkitpy.common.checkout.changelog import ChangeLog
@@ -130,6 +128,14 @@ class LandCowboy(AbstractSequencedCommand):
options.check_style_filter = "-changelog"
+class CheckStyleLocal(AbstractSequencedCommand):
+ name = "check-style-local"
+ help_text = "Run check-webkit-style on the current working directory diff"
+ steps = [
+ steps.CheckStyle,
+ ]
+
+
class AbstractPatchProcessingCommand(AbstractDeclarativeCommand):
# Subclasses must implement the methods below. We don't declare them here
# because we want to be able to implement them with mix-ins.
diff --git a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
index cbf4ce6d2..4085e366a 100644
--- a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
+++ b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
@@ -26,28 +26,29 @@
# (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 optparse import make_option
+
from webkitpy.common.config.committers import CommitterList
-from webkitpy.common.config.ports import WebKitPort
+from webkitpy.common.config.ports import DeprecatedPort
from webkitpy.common.system.deprecated_logging import error, log
from webkitpy.common.system.executive import ScriptError
+from webkitpy.tool.bot.earlywarningsystemtask import EarlyWarningSystemTask, EarlyWarningSystemTaskDelegate
from webkitpy.tool.bot.expectedfailures import ExpectedFailures
from webkitpy.tool.bot.layouttestresultsreader import LayoutTestResultsReader
+from webkitpy.tool.bot.patchanalysistask import UnableToApplyPatch
from webkitpy.tool.bot.queueengine import QueueEngine
-from webkitpy.tool.bot.earlywarningsystemtask import EarlyWarningSystemTask, EarlyWarningSystemTaskDelegate, UnableToApplyPatch
from webkitpy.tool.commands.queues import AbstractReviewQueue
class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDelegate):
_build_style = "release"
- # FIXME: Switch _run_tests from opt-in to opt-out once more bots are ready to run tests.
- _run_tests = False
+ # FIXME: Switch _default_run_tests from opt-in to opt-out once more bots are ready to run tests.
+ _default_run_tests = False
def __init__(self):
- AbstractReviewQueue.__init__(self)
- self.port = WebKitPort.port(self.port_name)
-
- def should_proceed_with_work_item(self, patch):
- return True
+ options = [make_option("--run-tests", action="store_true", dest="run_tests", default=self._default_run_tests, help="Run the Layout tests for each patch")]
+ AbstractReviewQueue.__init__(self, options=options)
+ self.port = DeprecatedPort.port(self.port_name)
def begin_work_queue(self):
# FIXME: This violates abstraction
@@ -73,7 +74,7 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDele
tool.bugs.set_flag_on_attachment(patch.id(), "commit-queue", "-", message, extra_message_text)
def review_patch(self, patch):
- task = EarlyWarningSystemTask(self, patch, self._run_tests)
+ task = EarlyWarningSystemTask(self, patch, self._options.run_tests)
if not task.validate():
self._did_error(patch, "%s did not process patch." % self.name)
return False
@@ -179,31 +180,13 @@ class ChromiumLinuxEWS(AbstractChromiumEWS):
# a database migration. :(
name = "chromium-ews"
port_name = "chromium-xvfb"
- _run_tests = True
+ _default_run_tests = True
class ChromiumWindowsEWS(AbstractChromiumEWS):
name = "cr-win-ews"
-# For platforms that we can't run inside a VM (like Mac OS X), we require
-# patches to be uploaded by committers, who are generally trustworthy folk. :)
-class AbstractCommitterOnlyEWS(AbstractEarlyWarningSystem):
- def process_work_item(self, patch):
- if not patch.attacher() or not patch.attacher().can_commit:
- self._did_error(patch, "%s cannot process patches from non-committers :(" % self.name)
- return False
- return AbstractEarlyWarningSystem.process_work_item(self, patch)
-
-
-# FIXME: Inheriting from AbstractCommitterOnlyEWS is kinda a hack, but it
-# happens to work because AbstractChromiumEWS and AbstractCommitterOnlyEWS
-# provide disjoint sets of functionality, and Python is otherwise smart
-# enough to handle the diamond inheritance.
-class ChromiumMacEWS(AbstractChromiumEWS, AbstractCommitterOnlyEWS):
- name = "cr-mac-ews"
-
-
class MacEWS(AbstractEarlyWarningSystem):
name = "mac-ews"
port_name = "mac"
diff --git a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
index afb1db0f7..cb349412c 100644
--- a/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.thirdparty.mock import Mock
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.tool.bot.queueengine import QueueEngine
@@ -63,31 +61,24 @@ class EarlyWarningSytemTest(QueuesTest):
"handle_unexpected_error": "Mock error message\n",
"next_work_item": "",
"process_work_item": "MOCK: update_status: %(name)s Pass\nMOCK: release_work_item: %(name)s 10000\n" % string_replacemnts,
- "handle_script_error": "ScriptError error message\n",
+ "handle_script_error": "ScriptError error message\n\nMOCK output\n",
}
return expected_stderr
def _test_builder_ews(self, ews):
ews.bind_to_tool(MockTool())
- self.assert_queue_outputs(ews, expected_stderr=self._default_expected_stderr(ews))
-
- def _test_committer_only_ews(self, ews):
- ews.bind_to_tool(MockTool())
- expected_stderr = self._default_expected_stderr(ews)
- string_replacemnts = {"name": ews.name}
- expected_stderr["process_work_item"] = "MOCK: update_status: %(name)s Error: %(name)s cannot process patches from non-committers :(\nMOCK: release_work_item: %(name)s 10000\n" % string_replacemnts
- self.assert_queue_outputs(ews, expected_stderr=expected_stderr)
+ options = Mock()
+ options.port = None
+ options.run_tests = ews._default_run_tests
+ self.assert_queue_outputs(ews, expected_stderr=self._default_expected_stderr(ews), options=options)
def _test_testing_ews(self, ews):
ews.layout_test_results = lambda: None
ews.bind_to_tool(MockTool())
expected_stderr = self._default_expected_stderr(ews)
- expected_stderr["handle_script_error"] = "ScriptError error message\n"
+ expected_stderr["handle_script_error"] = "ScriptError error message\n\nMOCK output\n"
self.assert_queue_outputs(ews, expected_stderr=expected_stderr)
- def test_committer_only_ewses(self):
- self._test_committer_only_ews(ChromiumMacEWS())
-
def test_builder_ewses(self):
self._test_builder_ews(MacEWS())
self._test_builder_ews(ChromiumWindowsEWS())
diff --git a/Tools/Scripts/webkitpy/tool/commands/queues.py b/Tools/Scripts/webkitpy/tool/commands/queues.py
index ed851781c..18ee4cb16 100644
--- a/Tools/Scripts/webkitpy/tool/commands/queues.py
+++ b/Tools/Scripts/webkitpy/tool/commands/queues.py
@@ -46,9 +46,11 @@ from webkitpy.tool.bot.botinfo import BotInfo
from webkitpy.tool.bot.commitqueuetask import CommitQueueTask, CommitQueueTaskDelegate
from webkitpy.tool.bot.expectedfailures import ExpectedFailures
from webkitpy.tool.bot.feeders import CommitQueueFeeder, EWSFeeder
+from webkitpy.tool.bot.flakytestreporter import FlakyTestReporter
from webkitpy.tool.bot.layouttestresultsreader import LayoutTestResultsReader
+from webkitpy.tool.bot.patchanalysistask import UnableToApplyPatch
from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate
-from webkitpy.tool.bot.flakytestreporter import FlakyTestReporter
+from webkitpy.tool.bot.stylequeuetask import StyleQueueTask, StyleQueueTaskDelegate
from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler
from webkitpy.tool.multicommandtool import Command, TryAgain
@@ -126,9 +128,6 @@ class AbstractQueue(Command, QueueEngineDelegate):
def next_work_item(self):
raise NotImplementedError, "subclasses must implement"
- def should_proceed_with_work_item(self, work_item):
- raise NotImplementedError, "subclasses must implement"
-
def process_work_item(self, work_item):
raise NotImplementedError, "subclasses must implement"
@@ -183,9 +182,6 @@ class FeederQueue(AbstractQueue):
# understands work items.
return "synthetic-work-item"
- def should_proceed_with_work_item(self, work_item):
- return True
-
def process_work_item(self, work_item):
for feeder in self.feeders:
feeder.feed()
@@ -273,11 +269,6 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskD
def next_work_item(self):
return self._next_patch()
- def should_proceed_with_work_item(self, patch):
- patch_text = "rollout patch" if patch.is_rollout() else "patch"
- self._update_status("Processing %s" % patch_text, patch)
- return True
-
def process_work_item(self, patch):
self._cc_watchers(patch.bug_id())
task = CommitQueueTask(self, patch)
@@ -380,9 +371,6 @@ class AbstractReviewQueue(AbstractPatchQueue, StepSequenceErrorHandler):
def next_work_item(self):
return self._next_patch()
- def should_proceed_with_work_item(self, patch):
- raise NotImplementedError("subclasses must implement")
-
def process_work_item(self, patch):
try:
if not self.review_patch(patch):
@@ -405,38 +393,46 @@ class AbstractReviewQueue(AbstractPatchQueue, StepSequenceErrorHandler):
@classmethod
def handle_script_error(cls, tool, state, script_error):
- log(script_error.message_with_output())
+ log(script_error.output)
-class StyleQueue(AbstractReviewQueue):
+class StyleQueue(AbstractReviewQueue, StyleQueueTaskDelegate):
name = "style-queue"
+
def __init__(self):
AbstractReviewQueue.__init__(self)
- def should_proceed_with_work_item(self, patch):
- self._update_status("Checking style", patch)
- return True
-
def review_patch(self, patch):
+ task = StyleQueueTask(self, patch)
+ if not task.validate():
+ self._did_error(patch, "%s did not process patch." % self.name)
+ return False
try:
- # Run the style checks.
- self.run_webkit_patch(["check-style", "--force-clean", "--non-interactive", "--parent-command=style-queue", patch.id()])
- finally:
- # Apply the watch list.
- try:
- self.run_webkit_patch(["apply-watchlist-local", patch.bug_id()])
- except ScriptError, e:
- # Don't turn the style bot block red due to watchlist errors.
- pass
-
+ return task.run()
+ except UnableToApplyPatch, e:
+ self._did_error(patch, "%s unable to apply patch." % self.name)
+ return False
+ except ScriptError, e:
+ message = "Attachment %s did not pass %s:\n\n%s\n\nIf any of these errors are false positives, please file a bug against check-webkit-style." % (patch.id(), self.name, e.output)
+ self._tool.bugs.post_comment_to_bug(patch.bug_id(), message, cc=self.watchers)
+ self._did_fail(patch)
+ return False
return True
- @classmethod
- def handle_script_error(cls, tool, state, script_error):
- is_svn_apply = script_error.command_name() == "svn-apply"
- status_id = cls._update_status_for_script_error(tool, state, script_error, is_error=is_svn_apply)
- if is_svn_apply:
- QueueEngine.exit_after_handled_error(script_error)
- message = "Attachment %s did not pass %s:\n\n%s\n\nIf any of these errors are false positives, please file a bug against check-webkit-style." % (state["patch"].id(), cls.name, script_error.message_with_output(output_limit=3*1024))
- tool.bugs.post_comment_to_bug(state["patch"].bug_id(), message, cc=cls.watchers)
- sys.exit(1)
+ # StyleQueueTaskDelegate methods
+
+ def run_command(self, command):
+ self.run_webkit_patch(command)
+
+ def command_passed(self, message, patch):
+ self._update_status(message, patch=patch)
+
+ def command_failed(self, message, script_error, patch):
+ failure_log = self._log_from_script_error_for_upload(script_error)
+ return self._update_status(message, patch=patch, results_file=failure_log)
+
+ def expected_failures(self):
+ return None
+
+ def refetch_patch(self, patch):
+ return self._tool.bugs.fetch_attachment(patch.id())
diff --git a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py
index 8f8f19835..f2c60d9eb 100644
--- a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py
@@ -132,7 +132,6 @@ class FeederQueueTest(QueuesTest):
tool = MockTool(log_executive=True)
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("feeder-queue"),
- "should_proceed_with_work_item": "",
"next_work_item": "",
"process_work_item": """Warning, attachment 10001 on bug 50000 has invalid committer (non-committer@example.com)
Warning, attachment 10001 on bug 50000 has invalid committer (non-committer@example.com)
@@ -235,7 +234,6 @@ class CommitQueueTest(QueuesTest):
tool.filesystem.write_text_file('/mock-results/full_results.json', '') # Otherwise the commit-queue will hit a KeyError trying to read the results from the MockFileSystem.
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("commit-queue"),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
"next_work_item": "",
"process_work_item": """MOCK: update_status: commit-queue Cleaned working directory
MOCK: update_status: commit-queue Updated working directory
@@ -248,14 +246,13 @@ MOCK: update_status: commit-queue Pass
MOCK: release_work_item: commit-queue 10000
""",
"handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '10000' with comment 'Rejecting attachment 10000 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
+ "handle_script_error": "ScriptError error message\n\nMOCK output\n",
}
self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr)
def test_commit_queue_failure(self):
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("commit-queue"),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
"next_work_item": "",
"process_work_item": """MOCK: update_status: commit-queue Cleaned working directory
MOCK: update_status: commit-queue Updated working directory
@@ -266,7 +263,7 @@ MOCK: update_status: commit-queue Fail
MOCK: release_work_item: commit-queue 10000
""",
"handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '10000' with comment 'Rejecting attachment 10000 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
+ "handle_script_error": "ScriptError error message\n\nMOCK output\n",
}
queue = CommitQueue()
@@ -283,7 +280,6 @@ MOCK: release_work_item: commit-queue 10000
def test_commit_queue_failure_with_failing_tests(self):
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("commit-queue"),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
"next_work_item": "",
"process_work_item": """MOCK: update_status: commit-queue Cleaned working directory
MOCK: update_status: commit-queue Updated working directory
@@ -296,7 +292,7 @@ MOCK: update_status: commit-queue Fail
MOCK: release_work_item: commit-queue 10000
""",
"handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '10000' with comment 'Rejecting attachment 10000 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
+ "handle_script_error": "ScriptError error message\n\nMOCK output\n",
}
queue = CommitQueue()
@@ -317,7 +313,6 @@ MOCK: release_work_item: commit-queue 10000
tool.buildbot.light_tree_on_fire()
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("commit-queue"),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
"next_work_item": "",
"process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'clean'], cwd=/mock-checkout
MOCK: update_status: commit-queue Cleaned working directory
@@ -337,7 +332,7 @@ MOCK: update_status: commit-queue Pass
MOCK: release_work_item: commit-queue 10000
""",
"handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '10000' with comment 'Rejecting attachment 10000 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
+ "handle_script_error": "ScriptError error message\n\nMOCK output\n",
}
self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr)
@@ -348,7 +343,6 @@ MOCK: release_work_item: commit-queue 10000
assert(rollout_patch.is_rollout())
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("commit-queue"),
- "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing rollout patch\n",
"next_work_item": "",
"process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'clean'], cwd=/mock-checkout
MOCK: update_status: commit-queue Cleaned working directory
@@ -364,7 +358,7 @@ MOCK: update_status: commit-queue Pass
MOCK: release_work_item: commit-queue 10005
""",
"handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '10005' with comment 'Rejecting attachment 10005 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "ScriptError error message\n",
+ "handle_script_error": "ScriptError error message\n\nMOCK output\n",
}
self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr)
@@ -448,35 +442,44 @@ class StyleQueueTest(QueuesTest):
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("style-queue"),
"next_work_item": "",
- "should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n",
- "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'check-style', '--force-clean', '--non-interactive', '--parent-command=style-queue', 10000], cwd=/mock-checkout
+ "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'clean'], cwd=/mock-checkout
+MOCK: update_status: style-queue Cleaned working directory
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'update'], cwd=/mock-checkout
+MOCK: update_status: style-queue Updated working directory
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--no-update', '--non-interactive', 10000], cwd=/mock-checkout
+MOCK: update_status: style-queue Applied patch
MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-watchlist-local', 50000], cwd=/mock-checkout
-MOCK: update_status: style-queue Fail
-MOCK: release_work_item: style-queue 10000\n""",
+MOCK: update_status: style-queue Watchlist applied
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'check-style-local', '--non-interactive', '--quiet'], cwd=/mock-checkout
+MOCK: update_status: style-queue Style checked
+MOCK: update_status: style-queue Pass
+MOCK: release_work_item: style-queue 10000
+""",
"handle_unexpected_error": "Mock error message\n",
- "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=50000, cc=[]\n--- Begin comment ---\nAttachment 10000 did not pass style-queue:\n\nScriptError error message\n\nIf any of these errors are false positives, please file a bug against check-webkit-style.\n--- End comment ---\n\n",
- }
- expected_exceptions = {
- "process_work_item": ScriptError,
- "handle_script_error": SystemExit,
+ "handle_script_error": "MOCK output\n",
}
tool = MockTool(log_executive=True, executive_throws_when_run=set(['check-style']))
- self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr, expected_exceptions=expected_exceptions, tool=tool)
+ self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr, tool=tool)
def test_style_queue_with_watch_list_exception(self):
expected_stderr = {
"begin_work_queue": self._default_begin_work_queue_stderr("style-queue"),
"next_work_item": "",
- "should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n",
- "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'check-style', '--force-clean', '--non-interactive', '--parent-command=style-queue', 10000], cwd=/mock-checkout
+ "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'clean'], cwd=/mock-checkout
+MOCK: update_status: style-queue Cleaned working directory
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'update'], cwd=/mock-checkout
+MOCK: update_status: style-queue Updated working directory
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--no-update', '--non-interactive', 10000], cwd=/mock-checkout
+MOCK: update_status: style-queue Applied patch
MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-watchlist-local', 50000], cwd=/mock-checkout
+MOCK: update_status: style-queue Unabled to apply watchlist
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'check-style-local', '--non-interactive', '--quiet'], cwd=/mock-checkout
+MOCK: update_status: style-queue Style checked
MOCK: update_status: style-queue Pass
-MOCK: release_work_item: style-queue 10000\n""",
+MOCK: release_work_item: style-queue 10000
+""",
"handle_unexpected_error": "Mock error message\n",
- "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=50000, cc=[]\n--- Begin comment ---\nAttachment 10000 did not pass style-queue:\n\nScriptError error message\n\nIf any of these errors are false positives, please file a bug against check-webkit-style.\n--- End comment ---\n\n",
- }
- expected_exceptions = {
- "handle_script_error": SystemExit,
+ "handle_script_error": "MOCK output\n",
}
tool = MockTool(log_executive=True, executive_throws_when_run=set(['apply-watchlist-local']))
- self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr, expected_exceptions=expected_exceptions, tool=tool)
+ self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr, tool=tool)
diff --git a/Tools/Scripts/webkitpy/tool/commands/queuestest.py b/Tools/Scripts/webkitpy/tool/commands/queuestest.py
index cb16b530f..b99302c8d 100644
--- a/Tools/Scripts/webkitpy/tool/commands/queuestest.py
+++ b/Tools/Scripts/webkitpy/tool/commands/queuestest.py
@@ -91,9 +91,8 @@ class QueuesTest(unittest.TestCase):
self.assert_outputs(queue.begin_work_queue, "begin_work_queue", [], expected_stdout, expected_stderr, expected_exceptions)
self.assert_outputs(queue.should_continue_work_queue, "should_continue_work_queue", [], expected_stdout, expected_stderr, expected_exceptions)
self.assert_outputs(queue.next_work_item, "next_work_item", [], expected_stdout, expected_stderr, expected_exceptions)
- self.assert_outputs(queue.should_proceed_with_work_item, "should_proceed_with_work_item", [work_item], expected_stdout, expected_stderr, expected_exceptions)
self.assert_outputs(queue.process_work_item, "process_work_item", [work_item], expected_stdout, expected_stderr, expected_exceptions)
self.assert_outputs(queue.handle_unexpected_error, "handle_unexpected_error", [work_item, "Mock error message"], expected_stdout, expected_stderr, expected_exceptions)
# Should we have a different function for testing StepSequenceErrorHandlers?
if isinstance(queue, StepSequenceErrorHandler):
- self.assert_outputs(queue.handle_script_error, "handle_script_error", [tool, {"patch": self.mock_work_item}, ScriptError(message="ScriptError error message", script_args="MockErrorCommand")], expected_stdout, expected_stderr, expected_exceptions)
+ self.assert_outputs(queue.handle_script_error, "handle_script_error", [tool, {"patch": self.mock_work_item}, ScriptError(message="ScriptError error message", script_args="MockErrorCommand", output="MOCK output")], expected_stdout, expected_stderr, expected_exceptions)
diff --git a/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py b/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py
index e7ff86a14..09c6d0bc6 100644
--- a/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py
+++ b/Tools/Scripts/webkitpy/tool/commands/rebaselineserver.py
@@ -30,9 +30,6 @@
results directory), provides comparisons of expected and actual results (both
images and text) and allows one-click rebaselining of tests."""
-import os
-import os.path
-
from webkitpy.common import system
from webkitpy.common.net.resultsjsonparser import for_each_test, JSONTestResult
from webkitpy.layout_tests.layout_package import json_results_generator
diff --git a/Tools/Scripts/webkitpy/tool/commands/sheriffbot.py b/Tools/Scripts/webkitpy/tool/commands/sheriffbot.py
index 547309e88..81f435394 100644
--- a/Tools/Scripts/webkitpy/tool/commands/sheriffbot.py
+++ b/Tools/Scripts/webkitpy/tool/commands/sheriffbot.py
@@ -26,10 +26,7 @@
# (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 os
-
from webkitpy.common.system.deprecated_logging import log
-from webkitpy.common.config.ports import WebKitPort
from webkitpy.tool.bot.sheriff import Sheriff
from webkitpy.tool.bot.sheriffircbot import SheriffIRCBot
from webkitpy.tool.commands.queues import AbstractQueue
@@ -61,10 +58,6 @@ class SheriffBot(AbstractQueue, StepSequenceErrorHandler):
self._irc_bot.process_pending_messages()
return
- def should_proceed_with_work_item(self, failure_map):
- # Currently, we don't have any reasons not to proceed with work items.
- return True
-
def process_work_item(self, failure_map):
return True
diff --git a/Tools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py b/Tools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
index 735ccab69..9aa57b123 100644
--- a/Tools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
@@ -26,11 +26,7 @@
# (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 os
-
from webkitpy.tool.commands.queuestest import QueuesTest
-from webkitpy.tool.commands.sheriffbot import SheriffBot
-from webkitpy.tool.mocktool import *
class SheriffBotTest(QueuesTest):
diff --git a/Tools/Scripts/webkitpy/tool/commands/upload.py b/Tools/Scripts/webkitpy/tool/commands/upload.py
index ff6d22eae..d587e0c97 100644
--- a/Tools/Scripts/webkitpy/tool/commands/upload.py
+++ b/Tools/Scripts/webkitpy/tool/commands/upload.py
@@ -482,6 +482,7 @@ class CreateBug(AbstractDeclarativeCommand):
def prompt_for_bug_title_and_comment(self):
bug_title = User.prompt("Bug title: ")
+ # FIXME: User should provide a function for doing this multi-line prompt.
print "Bug comment (hit ^D on blank line to end):"
lines = sys.stdin.readlines()
try:
diff --git a/Tools/Scripts/webkitpy/tool/main.py b/Tools/Scripts/webkitpy/tool/main.py
index d6a855bd1..d1fde74b8 100755
--- a/Tools/Scripts/webkitpy/tool/main.py
+++ b/Tools/Scripts/webkitpy/tool/main.py
@@ -33,7 +33,7 @@ from optparse import make_option
import os
import threading
-from webkitpy.common.config.ports import WebKitPort
+from webkitpy.common.config.ports import DeprecatedPort
from webkitpy.common.host import Host
from webkitpy.common.net.irc import ircproxy
from webkitpy.common.net.statusserver import StatusServer
@@ -61,6 +61,7 @@ class WebKitPatch(MultiCommandTool, Host):
self._irc = None
self._deprecated_port = None
+ # FIXME: Rename this deprecated_port()
def port(self):
return self._deprecated_port
@@ -98,7 +99,7 @@ class WebKitPatch(MultiCommandTool, Host):
if options.irc_password:
self.irc_password = options.irc_password
# If options.port is None, we'll get the default port for this platform.
- self._deprecated_port = WebKitPort.port(options.port)
+ self._deprecated_port = DeprecatedPort.port(options.port)
def should_execute_command(self, command):
if command.requires_local_commits and not self.scm().supports_local_commits():
diff --git a/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py b/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py
index ae4a05eb0..c8ea6c02c 100644
--- a/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/servers/rebaselineserver_unittest.py
@@ -295,11 +295,9 @@ def get_test_config(test_files=[], result_files=[]):
results_directory = '/WebKitBuild/Debug/layout-test-results'
host = MockHost()
for file in test_files:
- file_path = host.filesystem.join(layout_tests_directory, file)
- host.filesystem.files[file_path] = ''
+ host.filesystem.write_binary_file(host.filesystem.join(layout_tests_directory, file), '')
for file in result_files:
- file_path = host.filesystem.join(results_directory, file)
- host.filesystem.files[file_path] = ''
+ host.filesystem.write_binary_file(host.filesystem.join(results_directory, file), '')
class TestMacPort(WebKitPort):
port_name = "mac"
diff --git a/Tools/Scripts/webkitpy/tool/servers/reflectionhandler.py b/Tools/Scripts/webkitpy/tool/servers/reflectionhandler.py
index 9b73c4efe..6a3f207be 100644
--- a/Tools/Scripts/webkitpy/tool/servers/reflectionhandler.py
+++ b/Tools/Scripts/webkitpy/tool/servers/reflectionhandler.py
@@ -34,7 +34,6 @@ import datetime
import fnmatch
import json
import mimetypes
-import os
import os.path
import shutil
import threading
@@ -43,6 +42,7 @@ import urlparse
import wsgiref.handlers
import BaseHTTPServer
+
class ReflectionHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# Subclasses should override.
STATIC_FILE_NAMES = None
diff --git a/Tools/Scripts/webkitpy/tool/steps/abstractstep.py b/Tools/Scripts/webkitpy/tool/steps/abstractstep.py
index db0c0d5a8..2a5fea628 100644
--- a/Tools/Scripts/webkitpy/tool/steps/abstractstep.py
+++ b/Tools/Scripts/webkitpy/tool/steps/abstractstep.py
@@ -29,7 +29,6 @@
import sys
from webkitpy.common.system.executive import ScriptError
-from webkitpy.common.config.ports import WebKitPort
from webkitpy.tool.steps.options import Options
diff --git a/Tools/Scripts/webkitpy/tool/steps/attachtobug.py b/Tools/Scripts/webkitpy/tool/steps/attachtobug.py
index 4885fcb72..a389e655c 100644
--- a/Tools/Scripts/webkitpy/tool/steps/attachtobug.py
+++ b/Tools/Scripts/webkitpy/tool/steps/attachtobug.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.tool.steps.abstractstep import AbstractStep
from webkitpy.tool.steps.options import Options
@@ -43,7 +41,7 @@ class AttachToBug(AbstractStep):
def run(self, state):
filepath = state["filepath"]
bug_id = state["bug_id"]
- description = self._options.description or filepath.split(os.sep)[-1]
+ description = self._options.description or self._tool.filesystem.basename(filepath)
comment_text = self._options.comment
# add_attachment_to_bug fills in the filename from the file path.
diff --git a/Tools/Scripts/webkitpy/tool/steps/checkstyle.py b/Tools/Scripts/webkitpy/tool/steps/checkstyle.py
index f600d17d0..3304f016f 100644
--- a/Tools/Scripts/webkitpy/tool/steps/checkstyle.py
+++ b/Tools/Scripts/webkitpy/tool/steps/checkstyle.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.common.system.executive import ScriptError
from webkitpy.tool.steps.abstractstep import AbstractStep
from webkitpy.tool.steps.options import Options
diff --git a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
index 27c536361..191352440 100644
--- a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
+++ b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.tool.steps.abstractstep import AbstractStep
from webkitpy.tool.steps.options import Options
diff --git a/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py b/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py
index b0e6fd1cd..43f366492 100644
--- a/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py
@@ -26,7 +26,6 @@
# (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 os
import unittest
from webkitpy.common.system.outputcapture import OutputCapture
diff --git a/Tools/Scripts/webkitpy/tool/steps/editchangelog.py b/Tools/Scripts/webkitpy/tool/steps/editchangelog.py
index 2c5764722..35cd5043e 100644
--- a/Tools/Scripts/webkitpy/tool/steps/editchangelog.py
+++ b/Tools/Scripts/webkitpy/tool/steps/editchangelog.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.tool.steps.abstractstep import AbstractStep
diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py
index caaafa2d4..19caace01 100644
--- a/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py
+++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.common.checkout.changelog import ChangeLog
from webkitpy.common.system.executive import ScriptError
from webkitpy.tool.steps.abstractstep import AbstractStep
diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py
index 71ae51afd..ffed201d2 100644
--- a/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py
@@ -29,14 +29,17 @@
import os
import unittest
-from webkitpy.common.checkout.changelog_unittest import ChangeLogTest
+# Do not import changelog_unittest.ChangeLogTest directly as that will cause it to be run again.
+from webkitpy.common.checkout import changelog_unittest
+
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.tool.mocktool import MockOptions, MockTool
from webkitpy.tool.steps.preparechangelog import PrepareChangeLog
-class PrepareChangeLogTest(ChangeLogTest):
+class PrepareChangeLogTest(changelog_unittest.ChangeLogTest):
def test_ensure_bug_url(self):
+ # FIXME: This should use a MockFileSystem instead of a real FileSystem.
capture = OutputCapture()
step = PrepareChangeLog(MockTool(), MockOptions())
changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelogfordepsroll.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelogfordepsroll.py
index 3387b2483..4bbd383ae 100644
--- a/Tools/Scripts/webkitpy/tool/steps/preparechangelogfordepsroll.py
+++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelogfordepsroll.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.common.checkout.changelog import ChangeLog
from webkitpy.tool.steps.abstractstep import AbstractStep
diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
index 84796d2ce..95a99c320 100644
--- a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
+++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.common.checkout.changelog import ChangeLog
from webkitpy.common.config import urls
from webkitpy.tool.grammar import join_with_separators
diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py
index b5f2d1b4f..076e60252 100644
--- a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py
@@ -31,8 +31,10 @@ import os
import tempfile
import unittest
+# Do not import changelog_unittest.ChangeLogTest directly as that will cause it to be run again.
+from webkitpy.common.checkout import changelog_unittest
+
from webkitpy.common.checkout.changelog import ChangeLog
-from webkitpy.common.checkout.changelog_unittest import ChangeLogTest
from webkitpy.tool.steps.preparechangelogforrevert import *
@@ -107,7 +109,7 @@ class UpdateChangeLogsForRevertTest(unittest.TestCase):
"""
def _assert_message_for_revert_output(self, args, expected_entry):
- changelog_contents = u"%s\n%s" % (ChangeLogTest._new_entry_boilerplate, ChangeLogTest._example_changelog)
+ changelog_contents = u"%s\n%s" % (changelog_unittest.ChangeLogTest._new_entry_boilerplate, changelog_unittest.ChangeLogTest._example_changelog)
changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8"))
changelog = ChangeLog(changelog_path)
changelog.update_with_unreviewed_message(PrepareChangeLogForRevert._message_for_revert(*args))
diff --git a/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py b/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py
index 653aa8d20..99f174932 100644
--- a/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py
+++ b/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py
@@ -29,7 +29,7 @@
import unittest
from webkitpy.common.system.outputcapture import OutputCapture
-from webkitpy.common.config.ports import WebKitPort
+from webkitpy.common.config.ports import DeprecatedPort
from webkitpy.tool.mocktool import MockOptions, MockTool
from webkitpy.tool import steps
@@ -100,8 +100,7 @@ class StepsTest(unittest.TestCase):
mock_options.non_interactive = False
step = steps.RunTests(MockTool(log_executive=True), mock_options)
# FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment.
- mock_port = WebKitPort()
- mock_port.name = lambda: "Mac"
+ mock_port = DeprecatedPort()
tool = MockTool(log_executive=True)
tool.port = lambda: mock_port
step = steps.RunTests(tool, mock_options)
diff --git a/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py b/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
index e8881a3aa..cc3e96525 100644
--- a/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
+++ b/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py
@@ -26,8 +26,6 @@
# (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 os
-
from webkitpy.common.checkout.changelog import ChangeLog
from webkitpy.tool.grammar import pluralize
from webkitpy.tool.steps.abstractstep import AbstractStep
diff --git a/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py b/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py
index 1d4e92569..5e93821ce 100644
--- a/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py
+++ b/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py
@@ -26,9 +26,6 @@
# (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 os
-import re
-
from webkitpy.common.checkout.changelog import ChangeLog
from webkitpy.tool.steps.abstractstep import AbstractStep
from webkitpy.tool.steps.options import Options