summaryrefslogtreecommitdiffstats
path: root/Tools/Scripts/webkitpy/common
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-18 13:36:51 +0100
committerSteve Block <steveblock@google.com>2011-05-24 15:38:28 +0100
commit2fc2651226baac27029e38c9d6ef883fa32084db (patch)
treee396d4bf89dcce6ed02071be66212495b1df1dec /Tools/Scripts/webkitpy/common
parentb3725cedeb43722b3b175aaeff70552e562d2c94 (diff)
downloadexternal_webkit-2fc2651226baac27029e38c9d6ef883fa32084db.zip
external_webkit-2fc2651226baac27029e38c9d6ef883fa32084db.tar.gz
external_webkit-2fc2651226baac27029e38c9d6ef883fa32084db.tar.bz2
Merge WebKit at r78450: Initial merge by git.
Change-Id: I6d3e5f1f868ec266a0aafdef66182ddc3f265dc1
Diffstat (limited to 'Tools/Scripts/webkitpy/common')
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/api.py4
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/changelog.py21
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/changelog_unittest.py86
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/deps.py61
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm.py24
-rw-r--r--Tools/Scripts/webkitpy/common/checkout/scm_unittest.py11
-rw-r--r--Tools/Scripts/webkitpy/common/config/committers.py8
-rw-r--r--Tools/Scripts/webkitpy/common/config/urls.py1
-rw-r--r--Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py5
-rw-r--r--Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py6
-rw-r--r--Tools/Scripts/webkitpy/common/net/irc/ircbot.py2
-rw-r--r--Tools/Scripts/webkitpy/common/net/layouttestresults.py4
-rw-r--r--Tools/Scripts/webkitpy/common/net/testoutput.py179
-rw-r--r--Tools/Scripts/webkitpy/common/net/testoutput_unittest.py133
-rw-r--r--Tools/Scripts/webkitpy/common/net/testoutputset.py130
-rw-r--r--Tools/Scripts/webkitpy/common/net/testoutputset_unittest.py125
-rw-r--r--Tools/Scripts/webkitpy/common/system/directoryfileset.py14
-rw-r--r--Tools/Scripts/webkitpy/common/system/fileset.py6
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem.py16
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem_mock.py71
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem_unittest.py13
-rw-r--r--Tools/Scripts/webkitpy/common/system/logutils.py2
-rw-r--r--Tools/Scripts/webkitpy/common/system/logutils_unittest.py2
-rw-r--r--Tools/Scripts/webkitpy/common/system/ospath.py8
-rw-r--r--Tools/Scripts/webkitpy/common/system/stack_utils.py67
-rw-r--r--Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py76
-rw-r--r--Tools/Scripts/webkitpy/common/system/urlfetcher.py55
-rw-r--r--Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py46
-rw-r--r--Tools/Scripts/webkitpy/common/system/zip_mock.py55
-rw-r--r--Tools/Scripts/webkitpy/common/system/zipfileset.py10
-rw-r--r--Tools/Scripts/webkitpy/common/system/zipfileset_mock.py51
-rw-r--r--Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py6
32 files changed, 1131 insertions, 167 deletions
diff --git a/Tools/Scripts/webkitpy/common/checkout/api.py b/Tools/Scripts/webkitpy/common/checkout/api.py
index a87bb5a..170b822 100644
--- a/Tools/Scripts/webkitpy/common/checkout/api.py
+++ b/Tools/Scripts/webkitpy/common/checkout/api.py
@@ -33,6 +33,7 @@ from webkitpy.common.config import urls
from webkitpy.common.checkout.changelog import ChangeLog
from webkitpy.common.checkout.commitinfo import CommitInfo
from webkitpy.common.checkout.scm import CommitMessage
+from webkitpy.common.checkout.deps import DEPS
from webkitpy.common.memoized import memoized
from webkitpy.common.net.bugzilla import parse_bug_id
from webkitpy.common.system.executive import Executive, run_command, ScriptError
@@ -148,6 +149,9 @@ class Checkout(object):
except ScriptError, e:
pass # We might not have ChangeLogs.
+ def chromium_deps(self):
+ return DEPS(os.path.join(self._scm.checkout_root, "Source", "WebKit", "chromium", "DEPS"))
+
def apply_patch(self, patch, force=False):
# It's possible that the patch was not made from the root directory.
# We should detect and handle that case.
diff --git a/Tools/Scripts/webkitpy/common/checkout/changelog.py b/Tools/Scripts/webkitpy/common/checkout/changelog.py
index 07f905d..c81318c 100644
--- a/Tools/Scripts/webkitpy/common/checkout/changelog.py
+++ b/Tools/Scripts/webkitpy/common/checkout/changelog.py
@@ -36,9 +36,7 @@ import textwrap
from webkitpy.common.system.deprecated_logging import log
from webkitpy.common.config.committers import CommitterList
-from webkitpy.common.config import urls
from webkitpy.common.net.bugzilla import parse_bug_id
-from webkitpy.tool.grammar import join_with_separators
class ChangeLogEntry(object):
@@ -145,29 +143,14 @@ class ChangeLog(object):
lines = [self._wrap_line(line) for line in message.splitlines()]
return "\n".join(lines)
- # This probably does not belong in changelogs.py
- def _message_for_revert(self, revision_list, reason, bug_url):
- message = "Unreviewed, rolling out %s.\n" % join_with_separators(['r' + str(revision) for revision in revision_list])
- for revision in revision_list:
- message += "%s\n" % urls.view_revision_url(revision)
- if bug_url:
- message += "%s\n" % bug_url
- # Add an extra new line after the rollout links, before any reason.
- message += "\n"
- if reason:
- message += "%s\n\n" % reason
- return self._wrap_lines(message)
-
- def update_for_revert(self, revision_list, reason, bug_url=None):
+ def update_with_unreviewed_message(self, message):
reviewed_by_regexp = re.compile(
"%sReviewed by NOBODY \(OOPS!\)\." % self._changelog_indent)
removing_boilerplate = False
# inplace=1 creates a backup file and re-directs stdout to the file
for line in fileinput.FileInput(self.path, inplace=1):
if reviewed_by_regexp.search(line):
- message_lines = self._message_for_revert(revision_list,
- reason,
- bug_url)
+ message_lines = self._wrap_lines(message)
print reviewed_by_regexp.sub(message_lines, line),
# Remove all the ChangeLog boilerplate between the Reviewed by
# line and the first changed file.
diff --git a/Tools/Scripts/webkitpy/common/checkout/changelog_unittest.py b/Tools/Scripts/webkitpy/common/checkout/changelog_unittest.py
index 20c6cfa..299d509 100644
--- a/Tools/Scripts/webkitpy/common/checkout/changelog_unittest.py
+++ b/Tools/Scripts/webkitpy/common/checkout/changelog_unittest.py
@@ -142,89 +142,3 @@ class ChangeLogTest(unittest.TestCase):
expected_contents = changelog_contents.replace("Need a short description and bug URL (OOPS!)", expected_message)
os.remove(changelog_path)
self.assertEquals(actual_contents, expected_contents)
-
- _revert_message = """ Unreviewed, rolling out r12345.
- http://trac.webkit.org/changeset/12345
- http://example.com/123
-
- This is a very long reason which should be long enough so that
- _message_for_revert will need to wrap it. We'll also include
- a
- https://veryveryveryveryverylongbugurl.com/reallylongbugthingy.cgi?bug_id=12354
- link so that we can make sure we wrap that right too.
-"""
-
- def test_message_for_revert(self):
- changelog = ChangeLog("/fake/path")
- long_reason = "This is a very long reason which should be long enough so that _message_for_revert will need to wrap it. We'll also include a https://veryveryveryveryverylongbugurl.com/reallylongbugthingy.cgi?bug_id=12354 link so that we can make sure we wrap that right too."
- message = changelog._message_for_revert([12345], long_reason, "http://example.com/123")
- self.assertEquals(message, self._revert_message)
-
- _revert_entry_with_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- Unreviewed, rolling out r12345.
- http://trac.webkit.org/changeset/12345
- http://example.com/123
-
- Reason
-
- * Scripts/bugzilla-tool:
-'''
-
- _revert_entry_without_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- Unreviewed, rolling out r12345.
- http://trac.webkit.org/changeset/12345
-
- Reason
-
- * Scripts/bugzilla-tool:
-'''
-
- _multiple_revert_entry_with_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- Unreviewed, rolling out r12345, r12346, and r12347.
- http://trac.webkit.org/changeset/12345
- http://trac.webkit.org/changeset/12346
- http://trac.webkit.org/changeset/12347
- http://example.com/123
-
- Reason
-
- * Scripts/bugzilla-tool:
-'''
-
- _multiple_revert_entry_without_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org>
-
- Unreviewed, rolling out r12345, r12346, and r12347.
- http://trac.webkit.org/changeset/12345
- http://trac.webkit.org/changeset/12346
- http://trac.webkit.org/changeset/12347
-
- Reason
-
- * Scripts/bugzilla-tool:
-'''
-
- def _assert_update_for_revert_output(self, args, expected_entry):
- changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
- changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8"))
- changelog = ChangeLog(changelog_path)
- changelog.update_for_revert(*args)
- actual_entry = changelog.latest_entry()
- os.remove(changelog_path)
- self.assertEquals(actual_entry.contents(), expected_entry)
- self.assertEquals(actual_entry.reviewer_text(), None)
- # These checks could be removed to allow this to work on other entries:
- self.assertEquals(actual_entry.author_name(), "Eric Seidel")
- self.assertEquals(actual_entry.author_email(), "eric@webkit.org")
-
- def test_update_for_revert(self):
- self._assert_update_for_revert_output([[12345], "Reason"], self._revert_entry_without_bug_url)
- self._assert_update_for_revert_output([[12345], "Reason", "http://example.com/123"], self._revert_entry_with_bug_url)
- self._assert_update_for_revert_output([[12345, 12346, 12347], "Reason"], self._multiple_revert_entry_without_bug_url)
- self._assert_update_for_revert_output([[12345, 12346, 12347], "Reason", "http://example.com/123"], self._multiple_revert_entry_with_bug_url)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/checkout/deps.py b/Tools/Scripts/webkitpy/common/checkout/deps.py
new file mode 100644
index 0000000..6b87ff1
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/checkout/deps.py
@@ -0,0 +1,61 @@
+# 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.
+#
+# WebKit's Python module for parsing and modifying ChangeLog files
+
+import codecs
+import fileinput
+import os.path
+import re
+import textwrap
+
+
+class DEPS(object):
+
+ _variable_regexp = r"\s+'%s':\s+'(?P<value>\d+)'"
+
+ def __init__(self, path):
+ self._path = path
+
+ def read_variable(self, name):
+ pattern = re.compile(self._variable_regexp % name)
+ for line in fileinput.FileInput(self._path):
+ match = pattern.match(line)
+ if match:
+ return int(match.group("value"))
+
+ def write_variable(self, name, value):
+ pattern = re.compile(self._variable_regexp % name)
+ replacement_line = " '%s': '%s'" % (name, value)
+ # inplace=1 creates a backup file and re-directs stdout to the file
+ for line in fileinput.FileInput(self._path, inplace=1):
+ if pattern.match(line):
+ print replacement_line
+ continue
+ # Trailing comma suppresses printing newline
+ print line,
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm.py b/Tools/Scripts/webkitpy/common/checkout/scm.py
index 421c0dc..3fa2db5 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm.py
@@ -34,10 +34,10 @@ import re
import sys
import shutil
-from webkitpy.common.system.executive import Executive, run_command, ScriptError
-from webkitpy.common.system.deprecated_logging import error, log
-import webkitpy.common.system.ospath as ospath
from webkitpy.common.memoized import memoized
+from webkitpy.common.system.deprecated_logging import error, log
+from webkitpy.common.system.executive import Executive, run_command, ScriptError
+from webkitpy.common.system import ospath
def find_checkout_root():
@@ -746,6 +746,22 @@ class Git(SCM):
def display_name(self):
return "git"
+ def prepend_svn_revision(self, diff):
+ revision = None
+ tries = 0
+ while not revision and tries < 10:
+ # If the git checkout is not tracking an SVN repo, then svn_revision_from_git_commit throws.
+ try:
+ revision = self.svn_revision_from_git_commit('HEAD~' + str(tries))
+ except:
+ return diff
+ tries += 1
+
+ if not revision:
+ return diff
+
+ return "Subversion Revision: " + str(revision) + '\n' + diff
+
def create_patch(self, git_commit=None, changed_files=None):
"""Returns a byte array (str()) representing the patch file.
Patch files are effectively binary since they may contain
@@ -753,7 +769,7 @@ class Git(SCM):
command = ['git', 'diff', '--binary', "--no-ext-diff", "--full-index", "-M", self.merge_base(git_commit), "--"]
if changed_files:
command += changed_files
- return self.run(command, decode_output=False, cwd=self.checkout_root)
+ return self.prepend_svn_revision(self.run(command, decode_output=False, cwd=self.checkout_root))
def _run_git_svn_find_rev(self, arg):
# git svn find-rev always exits 0, even when the revision or commit is not found.
diff --git a/Tools/Scripts/webkitpy/common/checkout/scm_unittest.py b/Tools/Scripts/webkitpy/common/checkout/scm_unittest.py
index 64122b4..decfae0 100644
--- a/Tools/Scripts/webkitpy/common/checkout/scm_unittest.py
+++ b/Tools/Scripts/webkitpy/common/checkout/scm_unittest.py
@@ -810,6 +810,16 @@ class GitTest(SCMTest):
run_command(['git', 'config', '--add', 'svn-remote.svn.fetch', 'trunk:remote2'])
self.assertEqual(self.tracking_scm.remote_branch_ref(), 'remote1')
+ def test_create_patch(self):
+ write_into_file_at_path('test_file_commit1', 'contents')
+ run_command(['git', 'add', 'test_file_commit1'])
+ scm = detect_scm_system(self.untracking_checkout_path)
+ scm.commit_locally_with_message('message')
+
+ patch = scm.create_patch()
+ self.assertFalse(re.search(r'Subversion Revision:', patch))
+
+
class GitSVNTest(SCMTest):
def _setup_git_checkout(self):
@@ -1126,6 +1136,7 @@ class GitSVNTest(SCMTest):
patch = scm.create_patch()
self.assertTrue(re.search(r'test_file_commit2', patch))
self.assertTrue(re.search(r'test_file_commit1', patch))
+ self.assertTrue(re.search(r'Subversion Revision: 5', patch))
def test_create_patch_with_changed_files(self):
self._one_local_commit_plus_working_copy_changes()
diff --git a/Tools/Scripts/webkitpy/common/config/committers.py b/Tools/Scripts/webkitpy/common/config/committers.py
index f7d59fe..5c571ab 100644
--- a/Tools/Scripts/webkitpy/common/config/committers.py
+++ b/Tools/Scripts/webkitpy/common/config/committers.py
@@ -91,16 +91,17 @@ committers_unable_to_review = [
Committer("Cameron McCormack", "cam@webkit.org", "heycam"),
Committer("Carlos Garcia Campos", ["cgarcia@igalia.com", "carlosgc@gnome.org", "carlosgc@webkit.org"], "KaL"),
Committer("Carol Szabo", "carol.szabo@nokia.com"),
- Committer("Chang Shu", "Chang.Shu@nokia.com"),
+ Committer("Chang Shu", ["Chang.Shu@nokia.com", "cshu@webkit.org"], "cshu"),
Committer("Chris Evans", "cevans@google.com"),
Committer("Chris Petersen", "cpetersen@apple.com", "cpetersen"),
Committer("Chris Rogers", "crogers@google.com", "crogers"),
Committer("Christian Dywan", ["christian@twotoasts.de", "christian@webkit.org"]),
Committer("Collin Jackson", "collinj@webkit.org"),
+ Committer("Daniel Cheng", "dcheng@chromium.org", "dcheng"),
Committer("David Smith", ["catfish.man@gmail.com", "dsmith@webkit.org"], "catfishman"),
Committer("Dean Jackson", "dino@apple.com", "dino"),
Committer("Diego Gonzalez", ["diegohcg@webkit.org", "diego.gonzalez@openbossa.org"], "diegohcg"),
- Committer("Dirk Pranke", "dpranke@chromium.org"),
+ Committer("Dirk Pranke", "dpranke@chromium.org", "dpranke"),
Committer("Drew Wilson", "atwilson@chromium.org", "atwilson"),
Committer("Eli Fidler", "eli@staikos.net", "QBin"),
Committer("Enrica Casucci", "enrica@apple.com"),
@@ -113,6 +114,7 @@ committers_unable_to_review = [
Committer("Feng Qian", "feng@chromium.org"),
Committer("Fumitoshi Ukai", "ukai@chromium.org", "ukai"),
Committer("Gabor Loki", "loki@webkit.org", "loki04"),
+ Committer("Gabor Rapcsanyi", ["rgabor@webkit.org", "rgabor@inf.u-szeged.hu"], "rgabor"),
Committer("Girish Ramakrishnan", ["girish@forwardbias.in", "ramakrishnan.girish@gmail.com"]),
Committer("Graham Dennis", ["Graham.Dennis@gmail.com", "gdennis@webkit.org"]),
Committer("Greg Bolsinga", "bolsinga@apple.com"),
@@ -121,6 +123,7 @@ committers_unable_to_review = [
Committer("Hayato Ito", "hayato@chromium.org", "hayato"),
Committer("Hin-Chung Lam", ["hclam@google.com", "hclam@chromium.org"]),
Committer("Ilya Tikhonovsky", "loislo@chromium.org", "loislo"),
+ Committer("Ivan Krsti\u0107", "ike@apple.com"),
Committer("Jakob Petsovits", ["jpetsovits@rim.com", "jpetso@gmx.at"], "jpetso"),
Committer("Jakub Wieczorek", "jwieczorek@webkit.org", "fawek"),
Committer("James Hawkins", ["jhawkins@chromium.org", "jhawkins@google.com"], "jhawkins"),
@@ -137,6 +140,7 @@ committers_unable_to_review = [
Committer("John Gregg", ["johnnyg@google.com", "johnnyg@chromium.org"], "johnnyg"),
Committer("John Knottenbelt", ["jknotten@chromium.org"], "jknotten"),
Committer("Johnny Ding", ["jnd@chromium.org", "johnnyding.webkit@gmail.com"], "johnnyding"),
+ Committer("Joone Hur", ["joone.hur@collabora.co.uk", "joone@kldp.org", "joone@webkit.org"], "joone"),
Committer("Joost de Valk", ["joost@webkit.org", "webkit-dev@joostdevalk.nl"], "Altha"),
Committer("Julie Parent", ["jparent@google.com", "jparent@chromium.org"], "jparent"),
Committer("Julien Chaffraix", ["jchaffraix@webkit.org", "julien.chaffraix@gmail.com"]),
diff --git a/Tools/Scripts/webkitpy/common/config/urls.py b/Tools/Scripts/webkitpy/common/config/urls.py
index dfa6d69..ddaef97 100644
--- a/Tools/Scripts/webkitpy/common/config/urls.py
+++ b/Tools/Scripts/webkitpy/common/config/urls.py
@@ -34,5 +34,6 @@ def view_source_url(local_path):
def view_revision_url(revision_number):
return "http://trac.webkit.org/changeset/%s" % revision_number
+chromium_lkgr_url = "http://chromium-status.appspot.com/lkgr"
contribution_guidelines = "http://webkit.org/coding/contributing.html"
diff --git a/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py b/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py
index 3cb6da5..76cd31d 100644
--- a/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py
+++ b/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py
@@ -266,10 +266,11 @@ class BuildBot(object):
# See https://bugs.webkit.org/show_bug.cgi?id=33296 and related bugs.
self.core_builder_names_regexps = [
"SnowLeopard.*Build",
- "SnowLeopard.*\(Test", # Exclude WebKit2 for now.
+ "SnowLeopard.*\(Test",
+ "SnowLeopard.*\(WebKit2 Test",
"Leopard",
- "Tiger",
"Windows.*Build",
+ "EFL",
"GTK.*32",
"GTK.*64.*Debug", # Disallow the 64-bit Release bot which is broken.
"Qt",
diff --git a/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py b/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py
index 57290d1..f158827 100644
--- a/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py
+++ b/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py
@@ -222,7 +222,6 @@ class BuildBotTest(unittest.TestCase):
# For complete testing, this list should match the list of builders at build.webkit.org:
example_builders = [
- {'name': u'Tiger Intel Release', },
{'name': u'Leopard Intel Release (Build)', },
{'name': u'Leopard Intel Release (Tests)', },
{'name': u'Leopard Intel Debug (Build)', },
@@ -256,22 +255,23 @@ class BuildBotTest(unittest.TestCase):
name_regexps = [
"SnowLeopard.*Build",
"SnowLeopard.*\(Test",
+ "SnowLeopard.*\(WebKit2 Test",
"Leopard",
- "Tiger",
"Windows.*Build",
+ "EFL",
"GTK.*32",
"GTK.*64.*Debug", # Disallow the 64-bit Release bot which is broken.
"Qt",
"Chromium.*Release$",
]
expected_builders = [
- {'name': u'Tiger Intel Release', },
{'name': u'Leopard Intel Release (Build)', },
{'name': u'Leopard Intel Release (Tests)', },
{'name': u'Leopard Intel Debug (Build)', },
{'name': u'Leopard Intel Debug (Tests)', },
{'name': u'SnowLeopard Intel Release (Build)', },
{'name': u'SnowLeopard Intel Release (Tests)', },
+ {'name': u'SnowLeopard Intel Release (WebKit2 Tests)', },
{'name': u'Windows Release (Build)', },
{'name': u'Windows Debug (Build)', },
{'name': u'GTK Linux 32-bit Release', },
diff --git a/Tools/Scripts/webkitpy/common/net/irc/ircbot.py b/Tools/Scripts/webkitpy/common/net/irc/ircbot.py
index f742867..061a43c 100644
--- a/Tools/Scripts/webkitpy/common/net/irc/ircbot.py
+++ b/Tools/Scripts/webkitpy/common/net/irc/ircbot.py
@@ -26,7 +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 webkitpy.common.config.irc as config_irc
+from webkitpy.common.config import irc as config_irc
from webkitpy.common.thread.messagepump import MessagePump, MessagePumpDelegate
from webkitpy.thirdparty.autoinstalled.irc import ircbot
diff --git a/Tools/Scripts/webkitpy/common/net/layouttestresults.py b/Tools/Scripts/webkitpy/common/net/layouttestresults.py
index 28caad4..249ecc9 100644
--- a/Tools/Scripts/webkitpy/common/net/layouttestresults.py
+++ b/Tools/Scripts/webkitpy/common/net/layouttestresults.py
@@ -51,11 +51,13 @@ class LayoutTestResults(object):
timeout_key = u'Tests that timed out:'
crash_key = u'Tests that caused the DumpRenderTree tool to crash:'
missing_key = u'Tests that had no expected results (probably new):'
+ webprocess_crash_key = u'Tests that caused the Web process to crash:'
expected_keys = [
stderr_key,
fail_key,
crash_key,
+ webprocess_crash_key,
timeout_key,
missing_key,
]
@@ -87,6 +89,8 @@ class LayoutTestResults(object):
return cls._failures_from_fail_row(row)
if table_title == cls.crash_key:
return [test_failures.FailureCrash()]
+ if table_title == cls.webprocess_crash_key:
+ return [test_failures.FailureCrash()]
if table_title == cls.timeout_key:
return [test_failures.FailureTimeout()]
if table_title == cls.missing_key:
diff --git a/Tools/Scripts/webkitpy/common/net/testoutput.py b/Tools/Scripts/webkitpy/common/net/testoutput.py
new file mode 100644
index 0000000..37c1445
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/net/testoutput.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 os
+import re
+
+
+class NaiveImageDiffer(object):
+ def same_image(self, img1, img2):
+ return img1 == img2
+
+
+class TestOutput(object):
+ """Represents the output that a single layout test generates when it is run
+ on a particular platform.
+ Note that this is the raw output that is produced when the layout test is
+ run, not the results of the subsequent comparison between that output and
+ the expected output."""
+ def __init__(self, platform, output_type, files):
+ self._output_type = output_type
+ self._files = files
+ file = files[0] # Pick some file to do test name calculation.
+ self._name = self._extract_test_name(file.name())
+ self._is_actual = '-actual.' in file.name()
+
+ self._platform = platform or self._extract_platform(file.name())
+
+ def _extract_platform(self, filename):
+ """Calculates the platform from the name of the file if it isn't known already"""
+ path = re.split(os.path.sep, filename)
+ if 'platform' in path:
+ return path[path.index('platform') + 1]
+ return None
+
+ def _extract_test_name(self, filename):
+ path = re.split(os.path.sep, filename)
+ if 'LayoutTests' in path:
+ path = path[1 + path.index('LayoutTests'):]
+ if 'layout-test-results' in path:
+ path = path[1 + path.index('layout-test-results'):]
+ if 'platform' in path:
+ path = path[2 + path.index('platform'):]
+
+ filename = path[-1]
+ filename = re.sub('-expected\..*$', '', filename)
+ filename = re.sub('-actual\..*$', '', filename)
+ path[-1] = filename
+ return os.path.sep.join(path)
+
+ def save_to(self, path):
+ """Have the files in this TestOutput write themselves to the disk at the specified location."""
+ for file in self._files:
+ file.save_to(path)
+
+ def is_actual(self):
+ """Is this output the actual output of a test? (As opposed to expected output.)"""
+ return self._is_actual
+
+ def name(self):
+ """The name of this test (doesn't include extension)"""
+ return self._name
+
+ def __eq__(self, other):
+ return (other != None and
+ self.name() == other.name() and
+ self.type() == other.type() and
+ self.platform() == other.platform() and
+ self.is_actual() == other.is_actual() and
+ self.same_content(other))
+
+ def __hash__(self):
+ return hash(str(self.name()) + str(self.type()) + str(self.platform()))
+
+ def is_new_baseline_for(self, other):
+ return (self.name() == other.name() and
+ self.type() == other.type() and
+ self.platform() == other.platform() and
+ self.is_actual() and
+ (not other.is_actual()))
+
+ def __str__(self):
+ actual_str = '[A] ' if self.is_actual() else ''
+ return "TestOutput[%s/%s] %s%s" % (self._platform, self._output_type, actual_str, self.name())
+
+ def type(self):
+ return self._output_type
+
+ def platform(self):
+ return self._platform
+
+ def _path_to_platform(self):
+ """Returns the path that tests for this platform are stored in."""
+ if self._platform is None:
+ return ""
+ else:
+ return os.path.join("self._platform", self._platform)
+
+ def _save_expected_result(self, file, path):
+ path = os.path.join(path, self._path_to_platform())
+ extension = os.path.splitext(file.name())[1]
+ filename = self.name() + '-expected' + extension
+ file.save_to(path, filename)
+
+ def save_expected_results(self, path_to_layout_tests):
+ """Save the files of this TestOutput to the appropriate directory
+ inside the LayoutTests directory. Typically this means that these files
+ will be saved in "LayoutTests/platform/<platform>/, or simply
+ LayoutTests if the platform is None."""
+ for file in self._files:
+ self._save_expected_result(file, path_to_layout_tests)
+
+ def delete(self):
+ """Deletes the files that comprise this TestOutput from disk. This
+ fails if the files are virtual files (eg: the files may reside inside a
+ remote zip file)."""
+ for file in self._files:
+ file.delete()
+
+
+class TextTestOutput(TestOutput):
+ """Represents a text output of a single test on a single platform"""
+ def __init__(self, platform, text_file):
+ self._text_file = text_file
+ TestOutput.__init__(self, platform, 'text', [text_file])
+
+ def same_content(self, other):
+ return self._text_file.contents() == other._text_file.contents()
+
+ def retarget(self, platform):
+ return TextTestOutput(platform, self._text_file)
+
+
+class ImageTestOutput(TestOutput):
+ image_differ = NaiveImageDiffer()
+ """Represents an image output of a single test on a single platform"""
+ def __init__(self, platform, image_file, checksum_file):
+ self._checksum_file = checksum_file
+ self._image_file = image_file
+ files = filter(bool, [self._checksum_file, self._image_file])
+ TestOutput.__init__(self, platform, 'image', files)
+
+ def has_checksum(self):
+ return self._checksum_file is not None
+
+ def same_content(self, other):
+ # FIXME This should not assume that checksums are up to date.
+ if self.has_checksum() and other.has_checksum():
+ return self._checksum_file.contents() == other._checksum_file.contents()
+ else:
+ self_contents = self._image_file.contents()
+ other_contents = other._image_file.contents()
+ return ImageTestOutput.image_differ.same_image(self_contents, other_contents)
+
+ def retarget(self, platform):
+ return ImageTestOutput(platform, self._image_file, self._checksum_file)
+
+ def checksum(self):
+ return self._checksum_file.contents()
diff --git a/Tools/Scripts/webkitpy/common/net/testoutput_unittest.py b/Tools/Scripts/webkitpy/common/net/testoutput_unittest.py
new file mode 100644
index 0000000..ad38ca6
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/net/testoutput_unittest.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 re
+import testoutput
+import unittest
+
+
+class FakeFile(object):
+ def __init__(self, filename, contents="fake contents"):
+ self._filename = filename
+ self._contents = contents
+
+ def name(self):
+ return self._filename
+
+ def contents(self):
+ return self._contents
+
+
+class FakeTestOutput(testoutput.TestOutput):
+ def __init__(self, platform, output_type, contents, is_expected=False):
+ self._output_type = output_type
+ self._contents = contents
+ self._is_expected = is_expected
+ actual = 'actual'
+ if is_expected:
+ actual = 'expected'
+ test_name = 'anonymous-test-%s.txt' % actual
+ file = FakeFile(test_name, contents)
+ super(FakeTestOutput, self).__init__(platform, output_type, [file])
+
+ def contents(self):
+ return self._contents
+
+ def retarget(self, platform):
+ return FakeTestOutput(platform, self._output_type, self._contents, self._is_expected)
+
+
+class TestOutputTest(unittest.TestCase):
+ def _check_name(self, filename, expected_test_name):
+ r = testoutput.TextTestOutput(None, FakeFile(filename))
+ self.assertEquals(expected_test_name, r.name())
+
+ def _check_platform(self, filename, expected_platform):
+ r = testoutput.TextTestOutput(None, FakeFile(filename))
+ self.assertEquals(expected_platform, r.platform())
+
+ def test_extracts_name_correctly(self):
+ self._check_name('LayoutTests/fast/dom/a-expected.txt', 'fast/dom/a')
+ self._check_name('LayoutTests/fast/dom/a-actual.txt', 'fast/dom/a')
+ self._check_name('LayoutTests/platform/win/fast/a-expected.txt', 'fast/a')
+ self._check_name('LayoutTests/platform/win/fast/a-expected.checksum', 'fast/a')
+ self._check_name('fast/dom/test-expected.txt', 'fast/dom/test')
+ self._check_name('layout-test-results/fast/a-actual.checksum', 'fast/a')
+
+ def test_extracts_platform_correctly(self):
+ self._check_platform('LayoutTests/platform/win/fast/a-expected.txt', 'win')
+ self._check_platform('platform/win/fast/a-expected.txt', 'win')
+ self._check_platform('platform/mac/fast/a-expected.txt', 'mac')
+ self._check_platform('fast/a-expected.txt', None)
+
+ def test_outputs_from_an_actual_file_are_marked_as_such(self):
+ r = testoutput.TextTestOutput(None, FakeFile('test-actual.txt'))
+ self.assertTrue(r.is_actual())
+
+ def test_outputs_from_an_expected_file_are_not_actual(self):
+ r = testoutput.TextTestOutput(None, FakeFile('test-expected.txt'))
+ self.assertFalse(r.is_actual())
+
+ def test_is_new_baseline_for(self):
+ expected = testoutput.TextTestOutput('mac', FakeFile('test-expected.txt'))
+ actual = testoutput.TextTestOutput('mac', FakeFile('test-actual.txt'))
+ self.assertTrue(actual.is_new_baseline_for(expected))
+ self.assertFalse(expected.is_new_baseline_for(actual))
+
+ def test__eq__(self):
+ r1 = testoutput.TextTestOutput('mac', FakeFile('test-expected.txt', 'contents'))
+ r2 = testoutput.TextTestOutput('mac', FakeFile('test-expected.txt', 'contents'))
+ r3 = testoutput.TextTestOutput('win', FakeFile('test-expected.txt', 'contents'))
+
+ self.assertEquals(r1, r2)
+ self.assertEquals(r1, r2.retarget('mac'))
+ self.assertNotEquals(r1, r2.retarget('win'))
+
+ def test__hash__(self):
+ r1 = testoutput.TextTestOutput('mac', FakeFile('test-expected.txt', 'contents'))
+ r2 = testoutput.TextTestOutput('mac', FakeFile('test-expected.txt', 'contents'))
+ r3 = testoutput.TextTestOutput(None, FakeFile('test-expected.txt', None))
+
+ x = set([r1, r2])
+ self.assertEquals(1, len(set([r1, r2])))
+ self.assertEquals(2, len(set([r1, r2, r3])))
+
+ def test_image_diff_is_invoked_for_image_outputs_without_checksum(self):
+ r1 = testoutput.ImageTestOutput('mac', FakeFile('test-expected.png', 'asdf'), FakeFile('test-expected.checksum', 'check'))
+ r2 = testoutput.ImageTestOutput('mac', FakeFile('test-expected.png', 'asdf'), None)
+
+ # Default behaviour is to just compare on image contents.
+ self.assertTrue(r1.same_content(r2))
+
+ class AllImagesAreDifferent(object):
+ def same_image(self, image1, image2):
+ return False
+
+ # But we can install other image differs.
+ testoutput.ImageTestOutput.image_differ = AllImagesAreDifferent()
+
+ self.assertFalse(r1.same_content(r2))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/net/testoutputset.py b/Tools/Scripts/webkitpy/common/net/testoutputset.py
new file mode 100644
index 0000000..4074686
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/net/testoutputset.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+
+from webkitpy.common.system.directoryfileset import DirectoryFileSet
+from webkitpy.common.system.zipfileset import ZipFileSet
+import re
+import testoutput
+import urllib
+
+
+class TestOutputSet(object):
+ def __init__(self, name, platform, zip_file, **kwargs):
+ self._name = name
+ self._platform = platform
+ self._zip_file = zip_file
+ self._include_expected = kwargs.get('include_expected', True)
+
+ @classmethod
+ def from_zip_url(cls, platform, zip_path):
+ return TestOutputSet('local zip %s builder' % platform, platform, ZipFileSet(zip_path))
+
+ @classmethod
+ def from_zip(cls, platform, zip):
+ return TestOutputSet('local zip %s builder' % platform, platform, zip)
+
+ @classmethod
+ def from_zip_map(cls, zip_map):
+ output_sets = []
+ for k, v in zip_map.items():
+ output_sets.append(TestOutputSet.from_zip(k, v))
+ return AggregateTestOutputSet(output_sets)
+
+ @classmethod
+ def from_path(self, path, platform=None):
+ return TestOutputSet('local %s builder' % platform, platform, DirectoryFileSet(path))
+
+ def name(self):
+ return self._name
+
+ def set_platform(self, platform):
+ self._platform = platform
+
+ def files(self):
+ return [self._zip_file.open(filename) for filename in self._zip_file.namelist()]
+
+ def _extract_output_files(self, name, exact_match):
+ name_matcher = re.compile(name)
+ actual_matcher = re.compile(r'-actual\.')
+ expected_matcher = re.compile(r'-expected\.')
+
+ checksum_files = []
+ text_files = []
+ image_files = []
+ for output_file in self.files():
+ name_match = name_matcher.search(output_file.name())
+ actual_match = actual_matcher.search(output_file.name())
+ expected_match = expected_matcher.search(output_file.name())
+ if not (name_match and (actual_match or (self._include_expected and expected_match))):
+ continue
+ if output_file.name().endswith('.checksum'):
+ checksum_files.append(output_file)
+ elif output_file.name().endswith('.txt'):
+ text_files.append(output_file)
+ elif output_file.name().endswith('.png'):
+ image_files.append(output_file)
+
+ return (checksum_files, text_files, image_files)
+
+ def _extract_file_with_name(self, name, files):
+ for file in files:
+ if file.name() == name:
+ return file
+ return None
+
+ def _make_output_from_image(self, image_file, checksum_files):
+ checksum_file_name = re.sub('\.png', '.checksum', image_file.name())
+ checksum_file = self._extract_file_with_name(checksum_file_name, checksum_files)
+ return testoutput.ImageTestOutput(self._platform, image_file, checksum_file)
+
+ def outputs_for(self, name, **kwargs):
+ target_type = kwargs.get('target_type', None)
+ exact_match = kwargs.get('exact_match', False)
+ if re.search(r'\.x?html', name):
+ name = name[:name.rindex('.')]
+
+ (checksum_files, text_files, image_files) = self._extract_output_files(name, exact_match)
+
+ outputs = [self._make_output_from_image(image_file, checksum_files) for image_file in image_files]
+
+ outputs += [testoutput.TextTestOutput(self._platform, text_file) for text_file in text_files]
+
+ if exact_match:
+ outputs = filter(lambda output: output.name() == name, outputs)
+
+ outputs = filter(lambda r: target_type in [None, r.type()], outputs)
+
+ return outputs
+
+
+class AggregateTestOutputSet(object):
+ """Set of test outputs from a list of builders"""
+ def __init__(self, builders):
+ self._builders = builders
+
+ def outputs_for(self, name, **kwargs):
+ return sum([builder.outputs_for(name, **kwargs) for builder in self._builders], [])
+
+ def builders(self):
+ return self._builders
diff --git a/Tools/Scripts/webkitpy/common/net/testoutputset_unittest.py b/Tools/Scripts/webkitpy/common/net/testoutputset_unittest.py
new file mode 100644
index 0000000..a70a539
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/net/testoutputset_unittest.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+
+from webkitpy.common.system.zip_mock import MockZip
+import testoutputset
+import unittest
+
+
+class TestOutputSetTest(unittest.TestCase):
+ def _outputset_with_zip(self, zip, **kwargs):
+ return testoutputset.TestOutputSet('<fake-outputset>', '<fake-platform>', zip, **kwargs)
+
+ def test_text_files_get_interpreted_as_text_outputs(self):
+ zip = MockZip()
+ zip.insert('fast/dom/some-test-actual.txt', 'actual outputs')
+ b = self._outputset_with_zip(zip)
+ self.assertEquals(1, len(b.outputs_for('fast/dom/some-test')))
+ self.assertEquals('fast/dom/some-test', b.outputs_for('fast/dom/some-test.html')[0].name())
+
+ def test_image_and_checksum_files_get_interpreted_as_a_single_image_output(self):
+ zip = MockZip()
+ zip.insert('fast/dom/some-test-actual.checksum', 'abc123')
+ zip.insert('fast/dom/some-test-actual.png', '<image data>')
+ b = self._outputset_with_zip(zip)
+ outputs = b.outputs_for('fast/dom/some-test')
+ self.assertEquals(1, len(outputs))
+ output = outputs[0]
+ self.assertEquals('image', output.type())
+ self.assertEquals('abc123', output.checksum())
+
+ def test_multiple_image_outputs_are_detected(self):
+ zip = MockZip()
+ zip.insert('platform/win/fast/dom/some-test-actual.checksum', 'checksum1')
+ zip.insert('platform/win/fast/dom/some-test-actual.png', '<image data 1>')
+ zip.insert('platform/mac/fast/dom/some-test-actual.checksum', 'checksum2')
+ zip.insert('platform/mac/fast/dom/some-test-actual.png', '<image data 2>')
+ b = self._outputset_with_zip(zip)
+ outputs = b.outputs_for('fast/dom/some-test')
+ self.assertEquals(2, len(outputs))
+ self.assertFalse(outputs[0].same_content(outputs[1]))
+
+ def test_aggregate_output_set_correctly_retrieves_tests_from_multiple_output_sets(self):
+ outputset1_zip = MockZip()
+ outputset1_zip.insert('fast/dom/test-actual.txt', 'linux text output')
+ outputset1 = testoutputset.TestOutputSet('linux-outputset', 'linux', outputset1_zip)
+ outputset2_zip = MockZip()
+ outputset2_zip.insert('fast/dom/test-actual.txt', 'windows text output')
+ outputset2 = testoutputset.TestOutputSet('win-outputset', 'win', outputset2_zip)
+
+ b = testoutputset.AggregateTestOutputSet([outputset1, outputset2])
+ self.assertEquals(2, len(b.outputs_for('fast/dom/test')))
+
+ def test_can_infer_platform_from_path_if_none_provided(self):
+ zip = MockZip()
+ zip.insert('platform/win/some-test-expected.png', '<image data>')
+ zip.insert('platform/win/some-test-expected.checksum', 'abc123')
+ b = testoutputset.TestOutputSet('local LayoutTests outputset', None, zip)
+
+ outputs = b.outputs_for('some-test')
+ self.assertEquals(1, len(outputs))
+ self.assertEquals('win', outputs[0].platform())
+
+ def test_test_extension_is_ignored(self):
+ zip = MockZip()
+ zip.insert('test/test-a-actual.txt', 'actual outputs')
+ b = self._outputset_with_zip(zip)
+ outputs = b.outputs_for('test/test-a.html')
+ self.assertEquals(1, len(outputs))
+ self.assertEquals('test/test-a', outputs[0].name())
+
+ def test_existing_outputs_are_marked_as_such(self):
+ zip = MockZip()
+ zip.insert('test/test-a-expected.txt', 'expected outputs')
+ b = self._outputset_with_zip(zip)
+ outputs = b.outputs_for('test/test-a.html')
+ self.assertEquals(1, len(outputs))
+ self.assertFalse(outputs[0].is_actual())
+
+ def test_only_returns_outputs_of_specified_type(self):
+ zip = MockZip()
+ zip.insert('test/test-a-expected.txt', 'expected outputs')
+ zip.insert('test/test-a-expected.checksum', 'expected outputs')
+ zip.insert('test/test-a-expected.png', 'expected outputs')
+ b = self._outputset_with_zip(zip)
+
+ outputs = b.outputs_for('test/test-a.html')
+ text_outputs = b.outputs_for('test/test-a.html', target_type='text')
+ image_outputs = b.outputs_for('test/test-a.html', target_type='image')
+
+ self.assertEquals(2, len(outputs))
+ self.assertEquals(1, len(text_outputs))
+ self.assertEquals(1, len(image_outputs))
+ self.assertEquals('text', text_outputs[0].type())
+ self.assertEquals('image', image_outputs[0].type())
+
+ def test_exclude_expected_outputs_works(self):
+ zip = MockZip()
+ zip.insert('test-expected.txt', 'expected outputs stored on server for some reason')
+ b = self._outputset_with_zip(zip, include_expected=False)
+ outputs = b.outputs_for('test', target_type=None)
+ self.assertEquals(0, len(outputs))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/system/directoryfileset.py b/Tools/Scripts/webkitpy/common/system/directoryfileset.py
index 11acaf4..a5cab0e 100644
--- a/Tools/Scripts/webkitpy/common/system/directoryfileset.py
+++ b/Tools/Scripts/webkitpy/common/system/directoryfileset.py
@@ -21,14 +21,8 @@
# (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 __future__ import with_statement
-
-import os
-import shutil
-
from webkitpy.common.system.fileset import FileSetFileHandle
from webkitpy.common.system.filesystem import FileSystem
-import webkitpy.common.system.ospath as ospath
class DirectoryFileSet(object):
@@ -36,8 +30,8 @@ class DirectoryFileSet(object):
def __init__(self, path, filesystem=None):
self._path = path
self._filesystem = filesystem or FileSystem()
- if not self._path.endswith(os.path.sep):
- self._path += os.path.sep
+ if not self._path.endswith(self._filesystem.sep):
+ self._path += self._filesystem.sep
def _full_path(self, filename):
assert self._is_under(self._path, filename)
@@ -52,7 +46,7 @@ class DirectoryFileSet(object):
return self._filesystem.files_under(self._path)
def _is_under(self, dir, filename):
- return bool(ospath.relpath(self._filesystem.join(dir, filename), dir))
+ return bool(self._filesystem.relpath(self._filesystem.join(dir, filename), dir))
def open(self, filename):
return FileSetFileHandle(self, filename, self._filesystem)
@@ -69,7 +63,7 @@ class DirectoryFileSet(object):
dest = self._filesystem.join(path, filename)
# As filename may have slashes in it, we must ensure that the same
# directory hierarchy exists at the output path.
- self._filesystem.maybe_make_directory(os.path.split(dest)[0])
+ self._filesystem.maybe_make_directory(self._filesystem.dirname(dest))
self._filesystem.copyfile(src, dest)
def delete(self, filename):
diff --git a/Tools/Scripts/webkitpy/common/system/fileset.py b/Tools/Scripts/webkitpy/common/system/fileset.py
index 22f7c4d..598c1c5 100644
--- a/Tools/Scripts/webkitpy/common/system/fileset.py
+++ b/Tools/Scripts/webkitpy/common/system/fileset.py
@@ -22,7 +22,6 @@
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import with_statement
-import os
from webkitpy.common.system.filesystem import FileSystem
@@ -38,6 +37,9 @@ class FileSetFileHandle(object):
def __str__(self):
return "%s:%s" % (self._fileset, self._filename)
+ def close(self):
+ pass
+
def contents(self):
if self._contents is None:
self._contents = self._fileset.read(self._filename)
@@ -61,4 +63,4 @@ class FileSetFileHandle(object):
return self._filename
def splitext(self):
- return os.path.splitext(self.name())
+ return self._filesystem.splitext(self.name())
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem.py b/Tools/Scripts/webkitpy/common/system/filesystem.py
index 05513a9..b876807 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem.py
@@ -39,13 +39,20 @@ import shutil
import tempfile
import time
+from webkitpy.common.system import ospath
+
class FileSystem(object):
"""FileSystem interface for webkitpy.
Unless otherwise noted, all paths are allowed to be either absolute
or relative."""
def __init__(self):
- self.sep = os.sep
+ self._sep = os.sep
+
+ def _get_sep(self):
+ return self._sep
+
+ sep = property(_get_sep, doc="pathname separator")
def abspath(self, path):
return os.path.abspath(path)
@@ -165,8 +172,8 @@ class FileSystem(object):
if e.errno != errno.EEXIST:
raise
- def move(self, src, dest):
- shutil.move(src, dest)
+ def move(self, source, destination):
+ shutil.move(source, destination)
def mtime(self, path):
return os.stat(path).st_mtime
@@ -200,6 +207,9 @@ class FileSystem(object):
with codecs.open(path, 'r', 'utf8') as f:
return f.read()
+ def relpath(self, path, start='.'):
+ return ospath.relpath(path, start)
+
class _WindowsError(exceptions.OSError):
"""Fake exception for Linux and Mac."""
pass
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
index 0004944..aa79a8c 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
@@ -28,9 +28,11 @@
import errno
import os
-import path
import re
+from webkitpy.common.system import path
+from webkitpy.common.system import ospath
+
class MockFileSystem(object):
def __init__(self, files=None):
@@ -44,17 +46,23 @@ class MockFileSystem(object):
"""
self.files = files or {}
self.written_files = {}
- self.sep = '/'
+ self._sep = '/'
self.current_tmpno = 0
+ def _get_sep(self):
+ return self._sep
+
+ sep = property(_get_sep, doc="pathname separator")
+
def _raise_not_found(self, path):
raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT))
def _split(self, path):
- idx = path.rfind('/')
- return (path[0:idx], path[idx + 1:])
+ return path.rsplit(self.sep, 1)
def abspath(self, path):
+ if path.endswith(self.sep):
+ return path[:-1]
return path
def basename(self, path):
@@ -69,6 +77,7 @@ class MockFileSystem(object):
raise IOError(errno.EISDIR, destination, os.strerror(errno.ISDIR))
self.files[destination] = self.files[source]
+ self.written_files[destination] = self.files[source]
def dirname(self, path):
return self._split(path)[0]
@@ -90,10 +99,10 @@ class MockFileSystem(object):
if self.basename(path) in dirs_to_skip:
return []
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
- dir_substrings = ['/' + d + '/' for d in dirs_to_skip]
+ dir_substrings = [self.sep + d + self.sep for d in dirs_to_skip]
for filename in self.files:
if not filename.startswith(path):
continue
@@ -117,7 +126,7 @@ class MockFileSystem(object):
return [f for f in self.files if f == path]
def isabs(self, path):
- return path.startswith('/')
+ return path.startswith(self.sep)
def isfile(self, path):
return path in self.files and self.files[path] is not None
@@ -125,8 +134,8 @@ class MockFileSystem(object):
def isdir(self, path):
if path in self.files:
return False
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
# 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
@@ -135,22 +144,24 @@ class MockFileSystem(object):
return any(f.startswith(path) for f in files)
def join(self, *comps):
- return re.sub(re.escape(os.path.sep), '/', os.path.join(*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))
def listdir(self, path):
if not self.isdir(path):
raise OSError("%s is not a directory" % path)
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
dirs = []
files = []
for f in self.files:
if self.exists(f) and f.startswith(path):
remaining = f[len(path):]
- if '/' in remaining:
- dir = remaining[:remaining.index('/')]
+ if self.sep in remaining:
+ dir = remaining[:remaining.index(self.sep)]
if not dir in dirs:
dirs.append(dir)
else:
@@ -164,7 +175,7 @@ class MockFileSystem(object):
def _mktemp(self, suffix='', prefix='tmp', dir=None, **kwargs):
if dir is None:
- dir = '/__im_tmp'
+ dir = self.sep + '__im_tmp'
curno = self.current_tmpno
self.current_tmpno += 1
return self.join(dir, "%s_%u_%s" % (prefix, curno, suffix))
@@ -196,24 +207,26 @@ class MockFileSystem(object):
# FIXME: Implement such that subsequent calls to isdir() work?
pass
- def move(self, src, dst):
- if self.files[src] is None:
- self._raise_not_found(src)
- self.files[dst] = self.files[src]
- self.files[src] = None
+ def move(self, source, destination):
+ if self.files[source] is None:
+ self._raise_not_found(source)
+ self.files[destination] = self.files[source]
+ self.written_files[destination] = self.files[destination]
+ self.files[source] = None
+ self.written_files[source] = None
def normpath(self, path):
return path
- def open_binary_tempfile(self, suffix):
+ def open_binary_tempfile(self, suffix=''):
path = self._mktemp(suffix)
- return WritableFileObject(self, path), path
+ return (WritableFileObject(self, path), path)
def open_text_file_for_writing(self, path, append=False):
return WritableFileObject(self, path, append)
def read_text_file(self, path):
- return self.read_binary_file(path)
+ return self.read_binary_file(path).decode('utf-8')
def read_binary_file(self, path):
# Intentionally raises KeyError if we don't recognize the path.
@@ -221,14 +234,18 @@ class MockFileSystem(object):
self._raise_not_found(path)
return self.files[path]
+ def relpath(self, path, start='.'):
+ return ospath.relpath(path, start, self.abspath, self.sep)
+
def remove(self, path):
if self.files[path] is None:
self._raise_not_found(path)
self.files[path] = None
+ self.written_files[path] = None
def rmtree(self, path):
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
for f in self.files:
if f.startswith(path):
@@ -241,7 +258,7 @@ class MockFileSystem(object):
return (path[0:idx], path[idx:])
def write_text_file(self, path, contents):
- return self.write_binary_file(path, contents)
+ return self.write_binary_file(path, contents.encode('utf-8'))
def write_binary_file(self, path, contents):
self.files[path] = contents
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py b/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
index 267ca13..8455d72 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
@@ -167,6 +167,19 @@ class FileSystemTest(unittest.TestCase):
self.assertTrue(fs.remove('filename', remove_with_exception))
self.assertEquals(-1, FileSystemTest._remove_failures)
+ def test_sep(self):
+ fs = FileSystem()
+
+ self.assertEquals(fs.sep, os.sep)
+ self.assertEquals(fs.join("foo", "bar"),
+ os.path.join("foo", "bar"))
+
+ def test_sep__is_readonly(self):
+ def assign_sep():
+ fs.sep = ' '
+ fs = FileSystem()
+ self.assertRaises(AttributeError, assign_sep)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/system/logutils.py b/Tools/Scripts/webkitpy/common/system/logutils.py
index cd4e60f..eef4636 100644
--- a/Tools/Scripts/webkitpy/common/system/logutils.py
+++ b/Tools/Scripts/webkitpy/common/system/logutils.py
@@ -83,7 +83,7 @@ def get_logger(path):
Sample usage:
- import webkitpy.common.system.logutils as logutils
+ from webkitpy.common.system import logutils
_log = logutils.get_logger(__file__)
diff --git a/Tools/Scripts/webkitpy/common/system/logutils_unittest.py b/Tools/Scripts/webkitpy/common/system/logutils_unittest.py
index b77c284..f1b494d 100644
--- a/Tools/Scripts/webkitpy/common/system/logutils_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/logutils_unittest.py
@@ -28,7 +28,7 @@ import unittest
from webkitpy.common.system.logtesting import LogTesting
from webkitpy.common.system.logtesting import TestLogStream
-import webkitpy.common.system.logutils as logutils
+from webkitpy.common.system import logutils
class GetLoggerTest(unittest.TestCase):
diff --git a/Tools/Scripts/webkitpy/common/system/ospath.py b/Tools/Scripts/webkitpy/common/system/ospath.py
index aed7a3d..2504645 100644
--- a/Tools/Scripts/webkitpy/common/system/ospath.py
+++ b/Tools/Scripts/webkitpy/common/system/ospath.py
@@ -32,7 +32,7 @@ import os
#
# It should behave essentially the same as os.path.relpath(), except for
# returning None on paths not contained in abs_start_path.
-def relpath(path, start_path, os_path_abspath=None):
+def relpath(path, start_path, os_path_abspath=None, sep=None):
"""Return a path relative to the given start path, or None.
Returns None if the path is not contained in the directory start_path.
@@ -44,10 +44,12 @@ def relpath(path, start_path, os_path_abspath=None):
os_path_abspath: A replacement function for unit testing. This
function should strip trailing slashes just like
os.path.abspath(). Defaults to os.path.abspath.
+ sep: Path separator. Defaults to os.path.sep
"""
if os_path_abspath is None:
os_path_abspath = os.path.abspath
+ sep = sep or os.sep
# Since os_path_abspath() calls os.path.normpath()--
#
@@ -67,11 +69,11 @@ def relpath(path, start_path, os_path_abspath=None):
if not rel_path:
# Then the paths are the same.
pass
- elif rel_path[0] == os.sep:
+ elif rel_path[0] == sep:
# It is probably sufficient to remove just the first character
# since os.path.normpath() collapses separators, but we use
# lstrip() just to be sure.
- rel_path = rel_path.lstrip(os.sep)
+ rel_path = rel_path.lstrip(sep)
else:
# We are in the case typified by the following example:
#
diff --git a/Tools/Scripts/webkitpy/common/system/stack_utils.py b/Tools/Scripts/webkitpy/common/system/stack_utils.py
new file mode 100644
index 0000000..a343807
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/stack_utils.py
@@ -0,0 +1,67 @@
+# 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.
+
+"""Simple routines for logging, obtaining thread stack information."""
+
+import sys
+import traceback
+
+
+def log_thread_state(logger, name, thread_id, msg=''):
+ """Log information about the given thread state."""
+ stack = _find_thread_stack(thread_id)
+ assert(stack is not None)
+ logger("")
+ logger("%s (tid %d) %s" % (name, thread_id, msg))
+ _log_stack(logger, stack)
+ logger("")
+
+
+def _find_thread_stack(thread_id):
+ """Returns a stack object that can be used to dump a stack trace for
+ the given thread id (or None if the id is not found)."""
+ for tid, stack in sys._current_frames().items():
+ if tid == thread_id:
+ return stack
+ return None
+
+
+def _log_stack(logger, stack):
+ """Log a stack trace to the logger callback."""
+ for filename, lineno, name, line in traceback.extract_stack(stack):
+ logger('File: "%s", line %d, in %s' % (filename, lineno, name))
+ if line:
+ logger(' %s' % line.strip())
+
+
+def log_traceback(logger, tb):
+ stack = traceback.extract_tb(tb)
+ for frame_str in traceback.format_list(stack):
+ for line in frame_str.split('\n'):
+ if line:
+ logger(" %s" % line)
diff --git a/Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py b/Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py
new file mode 100644
index 0000000..b21319f
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py
@@ -0,0 +1,76 @@
+# 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 sys
+import unittest
+
+from webkitpy.common.system import outputcapture
+from webkitpy.common.system import stack_utils
+
+
+def current_thread_id():
+ thread_id, _ = sys._current_frames().items()[0]
+ return thread_id
+
+
+class Test(unittest.TestCase):
+ def test_find_thread_stack_found(self):
+ thread_id = current_thread_id()
+ found_stack = stack_utils._find_thread_stack(thread_id)
+ self.assertNotEqual(found_stack, None)
+
+ def test_find_thread_stack_not_found(self):
+ found_stack = stack_utils._find_thread_stack(0)
+ self.assertEqual(found_stack, None)
+
+ def test_log_thread_state(self):
+ msgs = []
+
+ def logger(msg):
+ msgs.append(msg)
+
+ thread_id = current_thread_id()
+ stack_utils.log_thread_state(logger, "test-thread", thread_id,
+ "is tested")
+ self.assertTrue(msgs)
+
+ def test_log_traceback(self):
+ msgs = []
+
+ def logger(msg):
+ msgs.append(msg)
+
+ try:
+ raise ValueError
+ except:
+ stack_utils.log_traceback(logger, sys.exc_info()[2])
+ self.assertTrue(msgs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/system/urlfetcher.py b/Tools/Scripts/webkitpy/common/system/urlfetcher.py
new file mode 100644
index 0000000..2d9e5ec
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/urlfetcher.py
@@ -0,0 +1,55 @@
+# 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.
+
+"""Wrapper module for fetching URLs."""
+
+import urllib
+
+
+class UrlFetcher(object):
+ """Class with restricted interface to fetch URLs (makes testing easier)"""
+ def __init__(self, filesystem):
+ self._filesystem = filesystem
+
+ def fetch(self, url):
+ """Fetches the contents of the URL as a string."""
+ file_object = urllib.urlopen(url)
+ content = file_object.read()
+ file_object.close()
+ return content
+
+ def fetch_into_file(self, url):
+ """Fetches the contents of the URL into a temporary file and return the filename.
+
+ This is the equivalent of urllib.retrieve() except that we don't return any headers.
+ """
+ file_object, filename = self._filesystem.open_binary_tempfile('-fetched')
+ contents = self.fetch(url)
+ file_object.write(contents)
+ file_object.close()
+ return filename
diff --git a/Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py b/Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py
new file mode 100644
index 0000000..e8a7532
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py
@@ -0,0 +1,46 @@
+# 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.
+
+
+def make_fetcher_cls(urls):
+ """UrlFetcher factory routine that simulates network access
+ using a dict of URLs -> contents."""
+ class MockFetcher(object):
+ def __init__(self, filesystem):
+ self._filesystem = filesystem
+
+ def fetch(self, url):
+ return urls[url]
+
+ def fetch_into_file(self, url):
+ f, fn = self._filesystem.open_binary_tempfile('mockfetcher')
+ f.write(self.fetch(url))
+ f.close()
+ return fn
+
+ return MockFetcher
diff --git a/Tools/Scripts/webkitpy/common/system/zip_mock.py b/Tools/Scripts/webkitpy/common/system/zip_mock.py
new file mode 100644
index 0000000..dcfaba7
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/zip_mock.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+
+from webkitpy.common.system.fileset import FileSetFileHandle
+from webkitpy.common.system.filesystem_mock import MockFileSystem
+
+
+class MockZip(object):
+ """A mock zip file that can have new files inserted into it."""
+ def __init__(self, filesystem=None):
+ self._filesystem = filesystem or MockFileSystem()
+ self._files = {}
+
+ def __str__(self):
+ return "MockZip"
+
+ def insert(self, filename, content):
+ self._files[filename] = content
+
+ def namelist(self):
+ return self._files.keys()
+
+ def open(self, filename):
+ return FileSetFileHandle(self, filename)
+
+ def read(self, filename):
+ return self._files[filename]
+
+ def extract(self, filename, path):
+ full_path = self._filesystem.join(path, filename)
+ contents = self.open(filename).contents()
+ self._filesystem.write_text_file(full_path, contents)
+
+ def delete(self, filename):
+ self._files[filename] = None
diff --git a/Tools/Scripts/webkitpy/common/system/zipfileset.py b/Tools/Scripts/webkitpy/common/system/zipfileset.py
index fa2b762..5cf3616 100644
--- a/Tools/Scripts/webkitpy/common/system/zipfileset.py
+++ b/Tools/Scripts/webkitpy/common/system/zipfileset.py
@@ -33,22 +33,28 @@ class ZipFileSet(object):
"""The set of files in a zip file that resides at a URL (local or remote)"""
def __init__(self, zip_url, filesystem=None, zip_factory=None):
self._zip_url = zip_url
+ self._temp_file = None
self._zip_file = None
self._filesystem = filesystem or FileSystem()
self._zip_factory = zip_factory or self._retrieve_zip_file
def _retrieve_zip_file(self, zip_url):
temp_file = NetworkTransaction().run(lambda: urllib.urlretrieve(zip_url)[0])
- return zipfile.ZipFile(temp_file)
+ return (temp_file, zipfile.ZipFile(temp_file))
def _load(self):
if self._zip_file is None:
- self._zip_file = self._zip_factory(self._zip_url)
+ self._temp_file, self._zip_file = self._zip_factory(self._zip_url)
def open(self, filename):
self._load()
return FileSetFileHandle(self, filename, self._filesystem)
+ def close(self):
+ if self._temp_file:
+ self._filesystem.remove(self._temp_file)
+ self._temp_file = None
+
def namelist(self):
self._load()
return self._zip_file.namelist()
diff --git a/Tools/Scripts/webkitpy/common/system/zipfileset_mock.py b/Tools/Scripts/webkitpy/common/system/zipfileset_mock.py
new file mode 100644
index 0000000..24ac8cb
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/zipfileset_mock.py
@@ -0,0 +1,51 @@
+# 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.
+
+
+def make_factory(ziphashes):
+ """ZipFileSet factory routine that looks up zipfiles in a dict;
+ each zipfile should also be a dict of member names -> contents."""
+ class MockZipFileSet(object):
+ def __init__(self, url):
+ self._url = url
+ self._ziphash = ziphashes[url]
+
+ def namelist(self):
+ return self._ziphash.keys()
+
+ def read(self, member):
+ return self._ziphash[member]
+
+ def close(self):
+ pass
+
+ def maker(url):
+ # We return None because there's no tempfile to delete.
+ return (None, MockZipFileSet(url))
+
+ return maker
diff --git a/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py b/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
index a9ba5ad..6801406 100644
--- a/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
@@ -64,13 +64,17 @@ class ZipFileSetTest(unittest.TestCase):
result = FakeZip(self._filesystem)
result.add_file('some-file', 'contents')
result.add_file('a/b/some-other-file', 'other contents')
- return result
+ return (None, result)
def test_open(self):
file = self._zip.open('a/b/some-other-file')
self.assertEquals('a/b/some-other-file', file.name())
self.assertEquals('other contents', file.contents())
+ def test_close(self):
+ zipfileset = ZipFileSet('blah', self._filesystem, self.make_fake_zip)
+ zipfileset.close()
+
def test_read(self):
self.assertEquals('contents', self._zip.read('some-file'))