summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/Scripts')
-rwxr-xr-xWebKitTools/Scripts/build-webkit15
-rwxr-xr-xWebKitTools/Scripts/update-webgl-conformance-tests36
-rwxr-xr-xWebKitTools/Scripts/update-webkit-chromium5
-rw-r--r--WebKitTools/Scripts/webkitdirs.pm1
-rw-r--r--WebKitTools/Scripts/webkitperl/VCSUtils_unittest/fixChangeLogPatch.pl89
-rw-r--r--WebKitTools/Scripts/webkitpy/common/config/committers.py5
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla.py30
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/common/net/statusserver.py5
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py61
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py19
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py71
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base.py19
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py33
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py42
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py8
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/test.py11
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py26
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py22
-rwxr-xr-xWebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py160
-rw-r--r--WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py102
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/cpp.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py5
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py12
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py42
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/download.py13
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py5
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues.py105
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py100
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py16
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/mocktool.py23
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py3
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/options.py1
34 files changed, 885 insertions, 204 deletions
diff --git a/WebKitTools/Scripts/build-webkit b/WebKitTools/Scripts/build-webkit
index 4234905..cd43499 100755
--- a/WebKitTools/Scripts/build-webkit
+++ b/WebKitTools/Scripts/build-webkit
@@ -50,6 +50,7 @@ chdirWebKit();
my $showHelp = 0;
my $clean = 0;
my $minimal = 0;
+my $v8 = 0;
my $installHeaders;
my $installLibs;
my $prefixPath;
@@ -197,9 +198,6 @@ my @features = (
{ option => "xslt", desc => "Toggle XSLT support",
define => "ENABLE_XSLT", default => 1, value => \$xsltSupport },
- { option => "file-writer", desc => "Toggle FileWriter support",
- define => "ENABLE_FILE_WRITER", default => 0, value => \$fileWriterSupport },
-
{ option => "file-system", desc => "Toggle FileSystem support",
define => "ENABLE_FILE_SYSTEM", default => 0, value => \$fileSystemSupport },
@@ -222,7 +220,8 @@ push @ARGV, split(/ /, $ENV{'BUILD_WEBKIT_ARGS'}) if ($ENV{'BUILD_WEBKIT_ARGS'})
foreach (@ARGV) {
if ($_ eq '--minimal') {
$minimal = 1;
- last;
+ } elsif ($_ eq '--v8') {
+ $v8 = 1;
}
}
@@ -250,6 +249,7 @@ Usage: $programName [options] [options to pass to build system]
--install-headers=<path> Set installation path for the headers (Qt only)
--install-libs=<path> Set installation path for the libraries (Qt only)
+ --v8 Use V8 as JavaScript engine (Qt only)
--prefix=<path> Set installation prefix to the given path (Gtk only)
--makeargs=<arguments> Optional Makefile flags
@@ -266,6 +266,7 @@ my %options = (
'prefix=s' => \$prefixPath,
'makeargs=s' => \$makeArgs,
'minimal' => \$minimal,
+ 'v8' => \$v8,
);
# Build usage text and options list from features
@@ -288,7 +289,7 @@ setConfiguration();
my $productDir = productDir();
# Remove 0 byte sized files from productDir after slave lost for Qt buildbots.
-File::Find::find(\&unlinkZeroFiles, $productDir) if isQt();
+File::Find::find(\&unlinkZeroFiles, $productDir) if (isQt() && -e $productDir);
sub unlinkZeroFiles ()
{
@@ -395,6 +396,10 @@ if (isGtk()) {
if ($minimal) {
push @options, "CONFIG+=minimal";
}
+
+ if ($v8) {
+ push @options, "CONFIG+=v8";
+ }
}
# Force re-link of existing libraries if different than expected
diff --git a/WebKitTools/Scripts/update-webgl-conformance-tests b/WebKitTools/Scripts/update-webgl-conformance-tests
new file mode 100755
index 0000000..dfe20a1
--- /dev/null
+++ b/WebKitTools/Scripts/update-webgl-conformance-tests
@@ -0,0 +1,36 @@
+#!/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:
+#
+# * 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 around webkitpy/layout_tests/update-webgl-conformance-tests.py"""
+
+import webkitpy.layout_tests.update_webgl_conformance_tests
+import sys
+
+if __name__ == '__main__':
+ sys.exit(webkitpy.layout_tests.update_webgl_conformance_tests.main())
diff --git a/WebKitTools/Scripts/update-webkit-chromium b/WebKitTools/Scripts/update-webkit-chromium
index 8458f83..1db1826 100755
--- a/WebKitTools/Scripts/update-webkit-chromium
+++ b/WebKitTools/Scripts/update-webkit-chromium
@@ -29,13 +29,16 @@
# Update script for the WebKit Chromium Port.
use File::Path;
+use FindBin;
use Getopt::Long;
+use lib $FindBin::Bin;
+use webkitdirs;
chdir("WebKit/chromium") or die $!;
# Find gclient or install it.
my $gclientPath;
-if (`gclient --version`) {
+if (commandExists('gclient')) {
$gclientPath = 'gclient';
} elsif (-e 'depot_tools/gclient') {
$gclientPath = 'depot_tools/gclient';
diff --git a/WebKitTools/Scripts/webkitdirs.pm b/WebKitTools/Scripts/webkitdirs.pm
index b4d3f60..2980750 100644
--- a/WebKitTools/Scripts/webkitdirs.pm
+++ b/WebKitTools/Scripts/webkitdirs.pm
@@ -258,6 +258,7 @@ sub jscPath($)
my ($productDir) = @_;
my $jscName = "jsc";
$jscName .= "_debug" if (isCygwin() && ($configuration eq "Debug"));
+ $jscName .= ".exe" if (isWindows() || isCygwin());
return "$productDir/$jscName" if -e "$productDir/$jscName";
return "$productDir/JavaScriptCore.framework/Resources/$jscName";
}
diff --git a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/fixChangeLogPatch.pl b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/fixChangeLogPatch.pl
index 2fd187b..ee258da 100644
--- a/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/fixChangeLogPatch.pl
+++ b/WebKitTools/Scripts/webkitperl/VCSUtils_unittest/fixChangeLogPatch.pl
@@ -30,7 +30,7 @@
# Unit tests of VCSUtils::fixChangeLogPatch().
-use Test::Simple tests => 8;
+use Test::Simple tests => 12;
use VCSUtils;
# The source ChangeLog for these tests is the following:
@@ -58,6 +58,93 @@ my $in;
my $out;
# New test
+$title = "fixChangeLogPatch: [no change] In-place change.";
+
+$in = <<'END';
+--- ChangeLog
++++ ChangeLog
+@@ -1,5 +1,5 @@
+ 2010-12-22 Bob <bob@email.address>
+
+- Reviewed by Sue.
++ Reviewed by Ray.
+
+ Changed some code on 2010-12-22.
+END
+
+ok(fixChangeLogPatch($in) eq $in, $title);
+
+# New test
+$title = "fixChangeLogPatch: [no change] Remove first entry.";
+
+$in = <<'END';
+--- ChangeLog
++++ ChangeLog
+@@ -1,11 +1,3 @@
+-2010-12-22 Bob <bob@email.address>
+-
+- Reviewed by Ray.
+-
+- Changed some code on 2010-12-22.
+-
+- * File:
+-
+ 2010-12-22 Alice <alice@email.address>
+
+ Reviewed by Ray.
+END
+
+ok(fixChangeLogPatch($in) eq $in, $title);
+
+# New test
+$title = "fixChangeLogPatch: [no change] Remove entry in the middle.";
+
+$in = <<'END';
+--- ChangeLog
++++ ChangeLog
+@@@ -7,10 +7,6 @@
+
+ * File:
+
+-2010-12-22 Bob <bob@email.address>
+-
+- Changed some code on 2010-12-22.
+-
+ 2010-12-22 Alice <alice@email.address>
+
+ Reviewed by Ray.
+END
+
+ok(fixChangeLogPatch($in) eq $in, $title);
+
+# New test
+$title = "fixChangeLogPatch: [no change] Far apart changes (i.e. more than one chunk).";
+
+$in = <<'END';
+--- ChangeLog
++++ ChangeLog
+@@ -7,7 +7,7 @@
+
+ * File:
+
+-2010-12-22 Bob <bob@email.address>
++2010-12-22 Bobby <bob@email.address>
+
+ Changed some code on 2010-12-22.
+
+@@ -21,7 +21,7 @@
+
+ * File2:
+
+-2010-12-21 Bob <bob@email.address>
++2010-12-21 Bobby <bob@email.address>
+
+ Changed some code on 2010-12-21.
+END
+
+ok(fixChangeLogPatch($in) eq $in, $title);
+
+# New test
$title = "fixChangeLogPatch: [no change] First line is new line.";
$in = <<'END';
diff --git a/WebKitTools/Scripts/webkitpy/common/config/committers.py b/WebKitTools/Scripts/webkitpy/common/config/committers.py
index 6a45cab..113131f 100644
--- a/WebKitTools/Scripts/webkitpy/common/config/committers.py
+++ b/WebKitTools/Scripts/webkitpy/common/config/committers.py
@@ -77,6 +77,7 @@ committers_unable_to_review = [
Committer("Andy Estes", "aestes@apple.com", "estes"),
Committer("Anthony Ricaud", "rik@webkit.org", "rik"),
Committer("Anton Muhin", "antonm@chromium.org", "antonm"),
+ Committer("Balazs Kelemen", "kbalazs@webkit.org", "kbalazs"),
Committer("Ben Murdoch", "benm@google.com", "benm"),
Committer("Benjamin C Meyer", ["ben@meyerhome.net", "ben@webkit.org"], "icefox"),
Committer("Benjamin Otte", ["otte@gnome.org", "otte@webkit.org"], "otte"),
@@ -92,7 +93,6 @@ committers_unable_to_review = [
Committer("Chris Rogers", "crogers@google.com", "crogers"),
Committer("Christian Dywan", ["christian@twotoasts.de", "christian@webkit.org"]),
Committer("Collin Jackson", "collinj@webkit.org"),
- Committer("Csaba Osztrogonac", "ossy@webkit.org", "ossy"),
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"),
@@ -148,6 +148,7 @@ committers_unable_to_review = [
Committer("Maxime Simon", ["simon.maxime@gmail.com", "maxime.simon@webkit.org"], "maxime.simon"),
Committer("Michael Saboff", "msaboff@apple.com"),
Committer("Michelangelo De Simone", "michelangelo@webkit.org", "michelangelo"),
+ Committer("Mihai Parparita", "mihaip@chromium.org", "mihaip"),
Committer("Mike Belshe", ["mbelshe@chromium.org", "mike@belshe.com"]),
Committer("Mike Fenton", ["mifenton@rim.com", "mike.fenton@torchmobile.com"], "mfenton"),
Committer("Mike Thole", ["mthole@mikethole.com", "mthole@apple.com"]),
@@ -156,6 +157,7 @@ committers_unable_to_review = [
Committer("Nico Weber", ["thakis@chromium.org", "thakis@google.com"], "thakis"),
Committer("Noam Rosenthal", "noam.rosenthal@nokia.com", "noamr"),
Committer("Pam Greene", "pam@chromium.org", "pamg"),
+ Committer("Patrick Gansterer", ["paroga@paroga.com", "paroga@webkit.org"], "paroga"),
Committer("Peter Kasting", ["pkasting@google.com", "pkasting@chromium.org"], "pkasting"),
Committer("Philippe Normand", ["pnormand@igalia.com", "philn@webkit.org"], "philn-tp"),
Committer("Pierre d'Herbemont", ["pdherbemont@free.fr", "pdherbemont@apple.com"], "pdherbemont"),
@@ -211,6 +213,7 @@ reviewers_list = [
Reviewer("Chris Marrin", "cmarrin@apple.com", "cmarrin"),
Reviewer("Chris Fleizach", "cfleizach@apple.com", "cfleizach"),
Reviewer("Chris Jerdonek", "cjerdonek@webkit.org", "cjerdonek"),
+ Reviewer(u"Csaba Osztrogon\u00e1c", "ossy@webkit.org", "ossy"),
Reviewer("Dan Bernstein", ["mitz@webkit.org", "mitz@apple.com"], "mitzpettel"),
Reviewer("Daniel Bates", "dbates@webkit.org", "dydz"),
Reviewer("Darin Adler", "darin@apple.com", "darin"),
diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py
index 2b2258e..cc64fac 100644
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py
@@ -175,6 +175,20 @@ class Bug(object):
def is_unassigned(self):
return self.assigned_to_email() in self.unassigned_emails
+ def status(self):
+ return self.bug_dictionary["bug_status"]
+
+ # Bugzilla has many status states we don't really use in WebKit:
+ # https://bugs.webkit.org/page.cgi?id=fields.html#status
+ _open_states = ["UNCONFIRMED", "NEW", "ASSIGNED", "REOPENED"]
+ _closed_states = ["RESOLVED", "VERIFIED", "CLOSED"]
+
+ def is_open(self):
+ return self.status() in self._open_states
+
+ def is_closed(self):
+ return not self.is_open()
+
# Rarely do we actually want obsolete attachments
def attachments(self, include_obsolete=False):
attachments = self.bug_dictionary["attachments"]
@@ -357,15 +371,14 @@ class CommitterValidator(object):
return False
return True
+ def _reject_patch_if_flags_are_invalid(self, patch):
+ return (self._validate_setter_email(
+ patch, "reviewer", self.reject_patch_from_review_queue)
+ and self._validate_setter_email(
+ patch, "committer", self.reject_patch_from_commit_queue))
+
def patches_after_rejecting_invalid_commiters_and_reviewers(self, patches):
- validated_patches = []
- for patch in patches:
- if (self._validate_setter_email(
- patch, "reviewer", self.reject_patch_from_review_queue)
- and self._validate_setter_email(
- patch, "committer", self.reject_patch_from_commit_queue)):
- validated_patches.append(patch)
- return validated_patches
+ return [patch for patch in patches if self._reject_patch_if_flags_are_invalid(patch)]
def reject_patch_from_commit_queue(self,
attachment_id,
@@ -500,6 +513,7 @@ class Bugzilla(object):
bug = {}
bug["id"] = int(soup.find("bug_id").string)
bug["title"] = self._string_contents(soup.find("short_desc"))
+ bug["bug_status"] = self._string_contents(soup.find("bug_status"))
bug["reporter_email"] = self._string_contents(soup.find("reporter"))
bug["assigned_to_email"] = self._string_contents(soup.find("assigned_to"))
bug["cc_emails"] = [self._string_contents(element)
diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py
index 280696e..32f23cd 100644
--- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/bugzilla_unittest.py
@@ -213,6 +213,7 @@ ZEZpbmlzaExvYWRXaXRoUmVhc29uOnJlYXNvbl07Cit9CisKIEBlbmQKIAogI2VuZGlmCg==
"cc_emails" : ["foo@bar.com", "example@example.com"],
"reporter_email" : "eric@webkit.org",
"assigned_to_email" : "webkit-unassigned@lists.webkit.org",
+ "bug_status": "NEW",
"attachments" : [{
"attach_date": datetime.datetime(2009, 12, 27, 23, 51),
'name': u'Patch',
diff --git a/WebKitTools/Scripts/webkitpy/common/net/statusserver.py b/WebKitTools/Scripts/webkitpy/common/net/statusserver.py
index c8fced6..57390b8 100644
--- a/WebKitTools/Scripts/webkitpy/common/net/statusserver.py
+++ b/WebKitTools/Scripts/webkitpy/common/net/statusserver.py
@@ -96,6 +96,11 @@ class StatusServer:
self.browser["work_items"] = " ".join(work_items)
return self.browser.submit().read()
+ def next_work_item(self, queue_name):
+ _log.debug("Fetching next work item for %s" % queue_name)
+ patch_status_url = "%s/next-patch/%s" % (self.url, queue_name)
+ return self._fetch_url(patch_status_url)
+
def update_work_items(self, queue_name, work_items):
_log.debug("Recording work items: %s for %s" % (work_items, queue_name))
return NetworkTransaction().run(lambda: self._post_work_items_to_server(queue_name, work_items))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py
index c543d91..51dcac8 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests.py
@@ -36,8 +36,10 @@ import os
import subprocess
import sys
import re
+import webkitpy.common.checkout.scm as scm
import webkitpy.common.system.executive as executive
import webkitpy.common.system.logutils as logutils
+import webkitpy.common.system.ospath as ospath
import webkitpy.layout_tests.port.factory as port_factory
_log = logutils.get_logger(__file__)
@@ -52,11 +54,14 @@ def port_fallbacks():
back on. All platforms fall back on 'base'.
"""
fallbacks = {_BASE_PLATFORM: []}
- for port_name in os.listdir(os.path.join('LayoutTests', 'platform')):
+ platform_dir = os.path.join(scm.find_checkout_root(), 'LayoutTests',
+ 'platform')
+ for port_name in os.listdir(platform_dir):
try:
platforms = port_factory.get(port_name).baseline_search_path()
except NotImplementedError:
- _log.error("'%s' lacks baseline_search_path(), please fix." % port_name)
+ _log.error("'%s' lacks baseline_search_path(), please fix."
+ % port_name)
fallbacks[port_name] = [_BASE_PLATFORM]
continue
fallbacks[port_name] = [os.path.basename(p) for p in platforms][1:]
@@ -102,7 +107,8 @@ def cluster_file_hashes(glob_pattern):
# Fill in the map.
cmd = ('git', 'ls-tree', '-r', 'HEAD', 'LayoutTests')
try:
- git_output = executive.Executive().run_command(cmd)
+ git_output = executive.Executive().run_command(cmd,
+ cwd=scm.find_checkout_root())
except OSError, e:
if e.errno == 2: # No such file or directory.
_log.error("Error: 'No such file' when running git.")
@@ -156,11 +162,28 @@ def has_intermediate_results(test, fallbacks, matching_platform,
return False
-def find_dups(hashes, port_fallbacks):
+def get_relative_test_path(filename, relative_to,
+ checkout_root=scm.find_checkout_root()):
+ """Constructs a relative path to |filename| from |relative_to|.
+ Args:
+ filename: The test file we're trying to get a relative path to.
+ relative_to: The absolute path we're relative to.
+ Returns:
+ A relative path to filename or None if |filename| is not below
+ |relative_to|.
+ """
+ layout_test_dir = os.path.join(checkout_root, 'LayoutTests')
+ abs_path = os.path.join(layout_test_dir, filename)
+ return ospath.relpath(abs_path, relative_to)
+
+
+def find_dups(hashes, port_fallbacks, relative_to):
"""Yields info about redundant test expectations.
Args:
hashes: a list of hashes as returned by cluster_file_hashes.
- port_fallbacks: a list of fallback information as returned by get_port_fallbacks.
+ port_fallbacks: a list of fallback information as returned by
+ get_port_fallbacks.
+ relative_to: the directory that we want the results relative to
Returns:
a tuple containing (test, platform, fallback, platforms)
"""
@@ -176,13 +199,24 @@ def find_dups(hashes, port_fallbacks):
# See if any of the platforms are redundant with each other.
for platform in platforms.keys():
for fallback in port_fallbacks[platform]:
- if fallback in platforms.keys():
- # We have to verify that there isn't an intermediate result
- # that causes this duplicate hash to exist.
- if not has_intermediate_results(test,
- port_fallbacks[platform], fallback):
- path = os.path.join('LayoutTests', platforms[platform])
- yield test, platform, fallback, path
+ if fallback not in platforms.keys():
+ continue
+ # We have to verify that there isn't an intermediate result
+ # that causes this duplicate hash to exist.
+ if has_intermediate_results(test, port_fallbacks[platform],
+ fallback):
+ continue
+ # We print the relative path so it's easy to pipe the results
+ # to xargs rm.
+ path = get_relative_test_path(platforms[platform], relative_to)
+ if not path:
+ continue
+ yield {
+ 'test': test,
+ 'platform': platform,
+ 'fallback': fallback,
+ 'path': path,
+ }
def deduplicate(glob_pattern):
@@ -194,5 +228,4 @@ def deduplicate(glob_pattern):
"""
fallbacks = port_fallbacks()
hashes = cluster_file_hashes(glob_pattern)
- return [{'test': test, 'path': path, 'platform': platform, 'fallback': fallback}
- for test, platform, fallback, path in find_dups(hashes, fallbacks)]
+ return list(find_dups(hashes, fallbacks, os.getcwd()))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py
index be2e381..bb9604f 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/deduplicate_tests_unittest.py
@@ -186,3 +186,22 @@ class ListDuplicatesTest(unittest.TestCase):
'fallback': 'chromium-win',
'platform': 'chromium-linux'},
result[0])
+
+ def test_get_relative_test_path(self):
+ checkout_root = scm.find_checkout_root()
+ layout_test_dir = os.path.join(checkout_root, 'LayoutTests')
+ test_cases = (
+ ('platform/mac/test.html',
+ ('platform/mac/test.html', layout_test_dir)),
+ ('LayoutTests/platform/mac/test.html',
+ ('platform/mac/test.html', checkout_root)),
+ (None,
+ ('platform/mac/test.html', os.path.join(checkout_root, 'WebCore'))),
+ ('test.html',
+ ('platform/mac/test.html', os.path.join(layout_test_dir, 'platform/mac'))),
+ (None,
+ ('platform/mac/test.html', os.path.join(layout_test_dir, 'platform/win'))),
+ )
+ for expected, inputs in test_cases:
+ self.assertEquals(expected,
+ deduplicate_tests.get_relative_test_path(*inputs))
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
index 9b963ca..970de60 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/layout_package/dump_render_tree_thread.py
@@ -72,20 +72,19 @@ def log_stack(stack):
_log.error(' %s' % line.strip())
-def _process_output(port, test_info, test_types, test_args, configuration,
- output_dir, crash, timeout, test_run_time, actual_checksum,
+def _process_output(port, options, test_info, test_types, test_args,
+ crash, timeout, test_run_time, actual_checksum,
output, error):
"""Receives the output from a DumpRenderTree process, subjects it to a
number of tests, and returns a list of failure types the test produced.
Args:
port: port-specific hooks
+ options: command line options argument from optparse
proc: an active DumpRenderTree process
test_info: Object containing the test filename, uri and timeout
test_types: list of test types to subject the output to
test_args: arguments to be passed to each test
- configuration: Debug or Release
- output_dir: directory to put crash stack traces into
Returns: a TestResult object
"""
@@ -106,7 +105,8 @@ def _process_output(port, test_info, test_types, test_args, configuration,
_log.debug("Stacktrace for %s:\n%s" % (test_info.filename, error))
# Strip off "file://" since RelativeTestFilename expects
# filesystem paths.
- filename = os.path.join(output_dir, port.relative_test_filename(
+ filename = os.path.join(options.results_directory,
+ port.relative_test_filename(
test_info.filename))
filename = os.path.splitext(filename)[0] + "-stack.txt"
port.maybe_make_directory(os.path.split(filename)[0])
@@ -122,7 +122,7 @@ def _process_output(port, test_info, test_types, test_args, configuration,
start_diff_time = time.time()
new_failures = test_type.compare_output(port, test_info.filename,
output, local_test_args,
- configuration)
+ options.configuration)
# Don't add any more failures if we already have a crash, so we don't
# double-report those tests. We do double-report for timeouts since
# we still want to see the text and image output.
@@ -166,25 +166,23 @@ class TestResult(object):
class SingleTestThread(threading.Thread):
"""Thread wrapper for running a single test file."""
- def __init__(self, port, image_path, shell_args, test_info,
- test_types, test_args, configuration, output_dir):
+ def __init__(self, port, options, test_info, test_types, test_args):
"""
Args:
port: object implementing port-specific hooks
+ options: command line argument object from optparse
test_info: Object containing the test filename, uri and timeout
- output_dir: Directory to put crash stacks into.
- See TestShellThread for documentation of the remaining arguments.
+ test_types: A list of TestType objects to run the test output
+ against.
+ test_args: A TestArguments object to pass to each TestType.
"""
threading.Thread.__init__(self)
self._port = port
- self._image_path = image_path
- self._shell_args = shell_args
+ self._options = options
self._test_info = test_info
self._test_types = test_types
self._test_args = test_args
- self._configuration = configuration
- self._output_dir = output_dir
self._driver = None
def run(self):
@@ -194,17 +192,17 @@ class SingleTestThread(threading.Thread):
# FIXME: this is a separate routine to work around a bug
# in coverage: see http://bitbucket.org/ned/coveragepy/issue/85.
test_info = self._test_info
- self._driver = self._port.create_driver(self._image_path,
- self._shell_args)
+ self._driver = self._port.create_driver(self._test_args.png_path,
+ self._options)
self._driver.start()
start = time.time()
crash, timeout, actual_checksum, output, error = \
self._driver.run_test(test_info.uri.strip(), test_info.timeout,
test_info.image_hash())
end = time.time()
- self._test_result = _process_output(self._port,
+ self._test_result = _process_output(self._port, self._options,
test_info, self._test_types, self._test_args,
- self._configuration, self._output_dir, crash, timeout, end - start,
+ crash, timeout, end - start,
actual_checksum, output, error)
self._driver.stop()
@@ -248,12 +246,13 @@ class WatchableThread(threading.Thread):
class TestShellThread(WatchableThread):
- def __init__(self, port, filename_list_queue, result_queue,
- test_types, test_args, image_path, shell_args, options):
+ def __init__(self, port, options, filename_list_queue, result_queue,
+ test_types, test_args):
"""Initialize all the local state for this DumpRenderTree thread.
Args:
port: interface to port-specific hooks
+ options: command line options argument from optparse
filename_list_queue: A thread safe Queue class that contains lists
of tuples of (filename, uri) pairs.
result_queue: A thread safe Queue class that will contain tuples of
@@ -261,22 +260,17 @@ class TestShellThread(WatchableThread):
test_types: A list of TestType objects to run the test output
against.
test_args: A TestArguments object to pass to each TestType.
- shell_args: Any extra arguments to be passed to DumpRenderTree.
- options: A property dictionary as produced by optparse. The
- command-line options should match those expected by
- run_webkit_tests; they are typically passed via the
- run_webkit_tests.TestRunner class."""
+
+ """
WatchableThread.__init__(self)
self._port = port
+ self._options = options
self._filename_list_queue = filename_list_queue
self._result_queue = result_queue
self._filename_list = []
self._test_types = test_types
self._test_args = test_args
self._driver = None
- self._image_path = image_path
- self._shell_args = shell_args
- self._options = options
self._directory_timing_stats = {}
self._test_results = []
self._num_tests = 0
@@ -433,13 +427,11 @@ class TestShellThread(WatchableThread):
A TestResult
"""
- worker = SingleTestThread(self._port, self._image_path,
- self._shell_args,
+ worker = SingleTestThread(self._port,
+ self._options,
test_info,
self._test_types,
- self._test_args,
- self._options.configuration,
- self._options.results_directory)
+ self._test_args)
worker.start()
@@ -503,11 +495,11 @@ class TestShellThread(WatchableThread):
self._driver.run_test(test_info.uri, test_info.timeout, image_hash)
end = time.time()
- result = _process_output(self._port, test_info, self._test_types,
- self._test_args, self._options.configuration,
- self._options.results_directory, crash,
- timeout, end - start, actual_checksum,
- output, error)
+ result = _process_output(self._port, self._options,
+ test_info, self._test_types,
+ self._test_args, crash,
+ timeout, end - start, actual_checksum,
+ output, error)
self._test_results.append(result)
return result
@@ -521,7 +513,8 @@ class TestShellThread(WatchableThread):
# poll() is not threadsafe and can throw OSError due to:
# http://bugs.python.org/issue1731717
if (not self._driver or self._driver.poll() is not None):
- self._driver = self._port.create_driver(self._image_path, self._shell_args)
+ self._driver = self._port.create_driver(self._test_args.png_path,
+ self._options)
self._driver.start()
def _kill_dump_render_tree(self):
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
index 9125f9e..70beac3 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base.py
@@ -152,6 +152,16 @@ class Port(object):
While this is a generic routine, we include it in the Port
interface so that it can be overriden for testing purposes."""
+
+ # The filenames show up in the diff output, make sure they're
+ # raw bytes and not unicode, so that they don't trigger join()
+ # trying to decode the input.
+ def to_raw_bytes(str):
+ if isinstance(str, unicode):
+ return str.encode('utf-8')
+ return str
+ expected_filename = to_raw_bytes(expected_filename)
+ actual_filename = to_raw_bytes(actual_filename)
diff = difflib.unified_diff(expected_text.splitlines(True),
actual_text.splitlines(True),
expected_filename,
@@ -364,7 +374,7 @@ class Port(object):
results_filename in a users' browser."""
raise NotImplementedError('Port.show_html_results_file')
- def create_driver(self, png_path, options):
+ def create_driver(self, image_path, options):
"""Return a newly created base.Driver subclass for starting/stopping
the test driver."""
raise NotImplementedError('Port.create_driver')
@@ -678,7 +688,7 @@ class Port(object):
class Driver:
"""Abstract interface for the DumpRenderTree interface."""
- def __init__(self, port, png_path, options):
+ def __init__(self, port, png_path, options, executive):
"""Initialize a Driver to subsequently run tests.
Typically this routine will spawn DumpRenderTree in a config
@@ -688,7 +698,10 @@ class Driver:
png_path - an absolute path for the driver to write any image
data for a test (as a PNG). If no path is provided, that
indicates that pixel test results will not be checked.
- options - any port-specific driver options."""
+ options - command line options argument from optparse
+ executive - reference to the process-wide Executive object
+
+ """
raise NotImplementedError('Driver.__init__')
def run_test(self, uri, timeout, checksum):
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py
index 1cc426f..780cd22 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/base_unittest.py
@@ -66,7 +66,7 @@ class UnitTestPort(base.Port):
def _open_configuration_file(self):
if self._configuration_contents:
return NewStringIO(self._configuration_contents)
- return base.Port._open_configuration_file(self)
+ raise IOError
class PortTest(unittest.TestCase):
@@ -163,6 +163,33 @@ class PortTest(unittest.TestCase):
self.assertFalse(base._wdiff_available)
base._wdiff_available = True
+ def test_diff_text(self):
+ port = base.Port()
+ # Make sure that we don't run into decoding exceptions when the
+ # filenames are unicode, with regular or malformed input (expected or
+ # actual input is always raw bytes, not unicode).
+ port.diff_text('exp', 'act', 'exp.txt', 'act.txt')
+ port.diff_text('exp', 'act', u'exp.txt', 'act.txt')
+ port.diff_text('exp', 'act', u'a\xac\u1234\u20ac\U00008000', 'act.txt')
+
+ port.diff_text('exp' + chr(255), 'act', 'exp.txt', 'act.txt')
+ port.diff_text('exp' + chr(255), 'act', u'exp.txt', 'act.txt')
+
+ # Though expected and actual files should always be read in with no
+ # encoding (and be stored as str objects), test unicode inputs just to
+ # be safe.
+ port.diff_text(u'exp', 'act', 'exp.txt', 'act.txt')
+ port.diff_text(
+ u'a\xac\u1234\u20ac\U00008000', 'act', 'exp.txt', 'act.txt')
+
+ # And make sure we actually get diff output.
+ diff = port.diff_text('foo', 'bar', 'exp.txt', 'act.txt')
+ self.assertTrue('foo' in diff)
+ self.assertTrue('bar' in diff)
+ self.assertTrue('exp.txt' in diff)
+ self.assertTrue('act.txt' in diff)
+ self.assertFalse('nosuchthing' in diff)
+
def test_default_configuration_notfound(self):
port = UnitTestPort()
self.assertEqual(port.default_configuration(), "Release")
@@ -223,8 +250,8 @@ class VirtualTest(unittest.TestCase):
self.assertVirtual(port._shut_down_http_server, None)
def test_virtual_driver_method(self):
- self.assertRaises(NotImplementedError, base.Driver, base.Port, "", None)
- self.assertVirtual(base.Driver, base.Port, "", None)
+ self.assertRaises(NotImplementedError, base.Driver, base.Port(),
+ "", None, None)
def test_virtual_driver_methods(self):
class VirtualDriver(base.Driver):
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
index 896eab1..3fc4613 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/chromium.py
@@ -194,13 +194,11 @@ class ChromiumPort(base.Port):
def create_driver(self, image_path, options):
"""Starts a new Driver and returns a handle to it."""
- if self._options.use_drt and sys.platform == 'darwin':
- return webkit.WebKitDriver(self, image_path, options, executive=self._executive)
- if self._options.use_drt:
- options += ['--test-shell']
- else:
- options += ['--layout-tests']
- return ChromiumDriver(self, image_path, options, executive=self._executive)
+ if options.use_drt and sys.platform == 'darwin':
+ return webkit.WebKitDriver(self, image_path, options,
+ executive=self._executive)
+ return ChromiumDriver(self, image_path, options,
+ executive=self._executive)
def start_helper(self):
helper_path = self._path_to_helper()
@@ -337,22 +335,32 @@ class ChromiumDriver(base.Driver):
def __init__(self, port, image_path, options, executive=Executive()):
self._port = port
- self._configuration = port._options.configuration
- # FIXME: _options is very confusing, because it's not an Options() element.
- # FIXME: These don't need to be passed into the constructor, but could rather
- # be passed into .start()
self._options = options
self._image_path = image_path
self._executive = executive
+ def _driver_args(self):
+ driver_args = []
+ if self._image_path:
+ driver_args.append("--pixel-tests=" + self._image_path)
+
+ if self._options.use_drt:
+ driver_args.append('--test-shell')
+ else:
+ driver_args.append('--layout-tests')
+
+ if self._options.startup_dialog:
+ driver_args.append('--testshell-startup-dialog')
+
+ if self._options.gp_fault_error_box:
+ driver_args.append('--gp-fault-error-box')
+ return driver_args
+
def start(self):
# FIXME: Should be an error to call this method twice.
- cmd = []
- # FIXME: We should not be grabbing at self._port._options.wrapper directly.
- cmd += self._command_wrapper(self._port._options.wrapper)
- cmd += [self._port._path_to_driver()]
- if self._options:
- cmd += self._options
+ cmd = self._command_wrapper(self._options.wrapper)
+ cmd.append(self._port._path_to_driver())
+ cmd += self._driver_args()
# We need to pass close_fds=True to work around Python bug #2320
# (otherwise we can hang when we kill DumpRenderTree when we are running
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py
index 1af01ad..4940e4c 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/dryrun.py
@@ -86,6 +86,7 @@ class DryRunPort(object):
port_name = port_name[len(pfx):]
else:
port_name = None
+ self._options = options
self.__delegate = factory.get(port_name, options)
def __getattr__(self, name):
@@ -116,16 +117,17 @@ class DryRunPort(object):
pass
def create_driver(self, image_path, options):
- return DryrunDriver(self, image_path, options)
+ return DryrunDriver(self, image_path, options, executive=None)
class DryrunDriver(base.Driver):
"""Dryrun implementation of the DumpRenderTree / Driver interface."""
- def __init__(self, port, image_path, test_driver_options):
+ def __init__(self, port, image_path, options, executive):
self._port = port
- self._driver_options = test_driver_options
+ self._options = options
self._image_path = image_path
+ self._executive = executive
self._layout_tests_dir = None
def poll(self):
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
index a3a16c3..2ccddb0 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/test.py
@@ -97,7 +97,7 @@ class TestPort(base.Port):
pass
def create_driver(self, image_path, options):
- return TestDriver(image_path, options, self)
+ return TestDriver(self, image_path, options, executive=None)
def start_http_server(self):
pass
@@ -139,10 +139,11 @@ class TestPort(base.Port):
class TestDriver(base.Driver):
"""Test/Dummy implementation of the DumpRenderTree interface."""
- def __init__(self, image_path, test_driver_options, port):
- self._driver_options = test_driver_options
- self._image_path = image_path
+ def __init__(self, port, image_path, options, executive):
self._port = port
+ self._image_path = image_path
+ self._options = options
+ self._executive = executive
self._image_written = False
def poll(self):
@@ -204,7 +205,7 @@ class TestDriver(base.Driver):
crash = False
timeout = False
output = basename + '-txt\n'
- if self._port.options().pixel_tests and (
+ if self._options.pixel_tests and (
'image' in basename or 'check' in basename):
checksum = basename + '-checksum\n'
with open(self._image_path, "w") as f:
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py
index b085ceb..88c9bdf 100644
--- a/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/port/webkit.py
@@ -199,7 +199,8 @@ class WebKitPort(base.Port):
webbrowser.open(uri, new=1)
def create_driver(self, image_path, options):
- return WebKitDriver(self, image_path, options, executive=self._executive)
+ return WebKitDriver(self, image_path, options,
+ executive=self._executive)
def test_base_platform_names(self):
# At the moment we don't use test platform names, but we have
@@ -405,22 +406,31 @@ class WebKitPort(base.Port):
class WebKitDriver(base.Driver):
"""WebKit implementation of the DumpRenderTree interface."""
- def __init__(self, port, image_path, driver_options, executive=Executive()):
+ def __init__(self, port, image_path, options, executive=Executive()):
self._port = port
- # FIXME: driver_options is never used.
self._image_path = image_path
+ self._options = options
+ self._executive = executive
self._driver_tempdir = tempfile.mkdtemp(prefix='DumpRenderTree-')
def __del__(self):
shutil.rmtree(self._driver_tempdir)
+ def _driver_args(self):
+ driver_args = []
+ if self._image_path:
+ driver_args.append('--pixel-tests')
+
+ # These are used by the Chromium DRT port
+ if self._options.use_drt:
+ driver_args.append('--test-shell')
+ return driver_args
+
def start(self):
- command = []
- # FIXME: We should not be grabbing at self._port._options.wrapper directly.
- command += self._command_wrapper(self._port._options.wrapper)
+ command = self._command_wrapper(self._options.wrapper)
command += [self._port._path_to_driver(), '-']
- if self._image_path:
- command.append('--pixel-tests')
+ command += self._driver_args()
+
environment = self._port.setup_environ_for_server()
environment['DYLD_FRAMEWORK_PATH'] = self._port._build_path()
environment['DUMPRENDERTREE_TEMP'] = self._driver_tempdir
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
index 2e2da6d..14d4f0e 100755
--- a/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -564,27 +564,18 @@ class TestRunner:
filename_queue.put(item)
return filename_queue
- def _get_dump_render_tree_args(self, index):
+ def _get_test_args(self, index):
"""Returns the tuple of arguments for tests and for DumpRenderTree."""
- shell_args = []
test_args = test_type_base.TestArguments()
- png_path = None
+ test_args.png_path = None
if self._options.pixel_tests:
png_path = os.path.join(self._options.results_directory,
"png_result%s.png" % index)
- shell_args.append("--pixel-tests=" + png_path)
test_args.png_path = png_path
-
test_args.new_baseline = self._options.new_baseline
test_args.reset_results = self._options.reset_results
- if self._options.startup_dialog:
- shell_args.append('--testshell-startup-dialog')
-
- if self._options.gp_fault_error_box:
- shell_args.append('--gp-fault-error-box')
-
- return test_args, png_path, shell_args
+ return test_args
def _contains_tests(self, subdir):
for test_file in self._test_files:
@@ -610,11 +601,10 @@ class TestRunner:
test_types.append(test_type(self._port,
self._options.results_directory))
- test_args, png_path, shell_args = \
- self._get_dump_render_tree_args(i)
+ test_args = self._get_test_args(i)
thread = dump_render_tree_thread.TestShellThread(self._port,
- filename_queue, self._result_queue, test_types, test_args,
- png_path, shell_args, self._options)
+ self._options, filename_queue, self._result_queue,
+ test_types, test_args)
if self._is_single_threaded():
thread.run_in_main_thread(self, result_summary)
else:
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py
new file mode 100755
index 0000000..f4c8098
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests.py
@@ -0,0 +1,160 @@
+#!/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 COMPUTER, INC. ``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 COMPUTER, INC. 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.
+
+from __future__ import with_statement
+
+import glob
+import logging
+import optparse
+import os
+import re
+import sys
+import webkitpy.common.checkout.scm as scm
+
+_log = logging.getLogger("webkitpy.layout_tests."
+ "update-webgl-conformance-tests")
+
+
+def remove_first_line_comment(text):
+ return re.compile(r'^<!--.*?-->\s*', re.DOTALL).sub('', text)
+
+
+def translate_includes(text):
+ # Mapping of single filename to relative path under WebKit root.
+ # Assumption: these filenames are globally unique.
+ include_mapping = {
+ "js-test-style.css": "../../js/resources",
+ "js-test-pre.js": "../../js/resources",
+ "js-test-post.js": "../../js/resources",
+ "desktop-gl-constants.js": "resources",
+ }
+
+ for filename, path in include_mapping.items():
+ search = r'(?:[^"\'= ]*/)?' + re.escape(filename)
+ replace = os.path.join(path, filename)
+ text = re.sub(search, replace, text)
+
+ return text
+
+
+def translate_khronos_test(text):
+ """
+ This method translates the contents of a Khronos test to a WebKit test.
+ """
+
+ translateFuncs = [
+ remove_first_line_comment,
+ translate_includes,
+ ]
+
+ for f in translateFuncs:
+ text = f(text)
+
+ return text
+
+
+def update_file(in_filename, out_dir):
+ # check in_filename exists
+ # check out_dir exists
+ out_filename = os.path.join(out_dir, os.path.basename(in_filename))
+
+ _log.debug("Processing " + in_filename)
+ with open(in_filename, 'r') as in_file:
+ with open(out_filename, 'w') as out_file:
+ out_file.write(translate_khronos_test(in_file.read()))
+
+
+def update_directory(in_dir, out_dir):
+ for filename in glob.glob(os.path.join(in_dir, '*.html')):
+ update_file(os.path.join(in_dir, filename), out_dir)
+
+
+def default_out_dir():
+ current_scm = scm.detect_scm_system(os.path.dirname(sys.argv[0]))
+ if not current_scm:
+ return os.getcwd()
+ root_dir = current_scm.checkout_root
+ if not root_dir:
+ return os.getcwd()
+ out_dir = os.path.join(root_dir, "LayoutTests/fast/canvas/webgl")
+ if os.path.isdir(out_dir):
+ return out_dir
+ return os.getcwd()
+
+
+def configure_logging(options):
+ """Configures the logging system."""
+ log_fmt = '%(levelname)s: %(message)s'
+ log_datefmt = '%y%m%d %H:%M:%S'
+ log_level = logging.INFO
+ if options.verbose:
+ log_fmt = ('%(asctime)s %(filename)s:%(lineno)-4d %(levelname)s '
+ '%(message)s')
+ log_level = logging.DEBUG
+ logging.basicConfig(level=log_level, format=log_fmt,
+ datefmt=log_datefmt)
+
+
+def option_parser():
+ usage = "usage: %prog [options] (input file or directory)"
+ parser = optparse.OptionParser(usage=usage)
+ parser.add_option('-v', '--verbose',
+ action='store_true',
+ default=False,
+ help='include debug-level logging')
+ parser.add_option('-o', '--output',
+ action='store',
+ type='string',
+ default=default_out_dir(),
+ metavar='DIR',
+ help='specify an output directory to place files '
+ 'in [default: %default]')
+ return parser
+
+
+def main():
+ parser = option_parser()
+ (options, args) = parser.parse_args()
+ configure_logging(options)
+
+ if len(args) == 0:
+ _log.error("Must specify an input directory or filename.")
+ parser.print_help()
+ return 1
+
+ in_name = args[0]
+ if os.path.isfile(in_name):
+ update_file(in_name, options.output)
+ elif os.path.isdir(in_name):
+ update_directory(in_name, options.output)
+ else:
+ _log.error("'%s' is not a directory or a file.", in_name)
+ return 2
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py
new file mode 100644
index 0000000..7393b70
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/layout_tests/update_webgl_conformance_tests_unittest.py
@@ -0,0 +1,102 @@
+#!/usr/bin/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:
+#
+# * 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.
+
+"""Unit tests for update_webgl_conformance_tests."""
+
+import unittest
+from webkitpy.layout_tests import update_webgl_conformance_tests as webgl
+
+
+def construct_script(name):
+ return "<script src=\"" + name + "\"></script>\n"
+
+
+def construct_style(name):
+ return "<link rel=\"stylesheet\" href=\"" + name + "\">"
+
+
+class TestTranslation(unittest.TestCase):
+ def assert_unchanged(self, text):
+ self.assertEqual(text, webgl.translate_khronos_test(text))
+
+ def assert_translate(self, input, output):
+ self.assertEqual(output, webgl.translate_khronos_test(input))
+
+ def test_simple_unchanged(self):
+ self.assert_unchanged("")
+ self.assert_unchanged("<html></html>")
+
+ def test_header_strip(self):
+ single_line_header = "<!-- single line header. -->"
+ multi_line_header = """<!-- this is a multi-line
+ header. it should all be removed too.
+ -->"""
+ text = "<html></html>"
+ self.assert_translate(single_line_header, "")
+ self.assert_translate(single_line_header + text, text)
+ self.assert_translate(multi_line_header + text, text)
+
+ def dont_strip_other_headers(self):
+ self.assert_unchanged("<html>\n<!-- don't remove comments on other lines. -->\n</html>")
+
+ def test_include_rewriting(self):
+ # Mappings to None are unchanged
+ styles = {
+ "../resources/js-test-style.css": "../../js/resources/js-test-style.css",
+ "fail.css": None,
+ "resources/stylesheet.css": None,
+ "../resources/style.css": None,
+ }
+ scripts = {
+ "../resources/js-test-pre.js": "../../js/resources/js-test-pre.js",
+ "../resources/js-test-post.js": "../../js/resources/js-test-post.js",
+ "../resources/desktop-gl-constants.js": "resources/desktop-gl-constants.js",
+
+ "resources/shadow-offset.js": None,
+ "../resources/js-test-post-async.js": None,
+ }
+
+ input_text = ""
+ output_text = ""
+ for input, output in styles.items():
+ input_text += construct_style(input)
+ output_text += construct_style(output if output else input)
+ for input, output in scripts.items():
+ input_text += construct_script(input)
+ output_text += construct_script(output if output else input)
+
+ head = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">\n<html>\n<head>\n'
+ foot = '</head>\n<body>\n</body>\n</html>'
+ input_text = head + input_text + foot
+ output_text = head + output_text + foot
+ self.assert_translate(input_text, output_text)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py b/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py
index 62f40ea..f8ebeff 100644
--- a/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py
+++ b/WebKitTools/Scripts/webkitpy/style/checkers/cpp.py
@@ -2519,6 +2519,7 @@ def check_identifier_name_in_declaration(filename, line_number, line, error):
and not modified_identifier.startswith('NPP_')
and not modified_identifier.startswith('NP_')
and not modified_identifier.startswith('qt_')
+ and not modified_identifier.startswith('cairo_')
and not modified_identifier.find('::qt_') >= 0
and not modified_identifier == "const_iterator"):
error(line_number, 'readability/naming', 4, identifier + " is incorrectly named. Don't use underscores in your identifier names.")
diff --git a/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py b/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py
index 16b1a3c..2f54305 100644
--- a/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/style/checkers/cpp_unittest.py
@@ -3703,6 +3703,11 @@ class WebKitStyleTest(CppStyleTestBase):
self.assert_lint('void QTFrame::qt_drt_is_awesome(int var1, int var2)', '')
self.assert_lint('void qt_drt_is_awesome(int var1, int var2);', '')
+ # Cairo forward-declarations should not be a failure.
+ self.assert_lint('typedef struct _cairo cairo_t;', '')
+ self.assert_lint('typedef struct _cairo_surface cairo_surface_t;', '')
+ self.assert_lint('typedef struct _cairo_scaled_font cairo_scaled_font_t;', '')
+
# NPAPI functions that start with NPN_, NPP_ or NP_ are allowed.
self.assert_lint('void NPN_Status(NPP, const char*)', '')
self.assert_lint('NPError NPP_SetWindow(NPP instance, NPWindow *window)', '')
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py
index 36cbc5f..289dc4a 100644
--- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py
@@ -33,7 +33,6 @@ import traceback
from datetime import datetime, timedelta
-from webkitpy.common.net.statusserver import StatusServer
from webkitpy.common.system.executive import ScriptError
from webkitpy.common.system.deprecated_logging import log, OutputTee
@@ -117,10 +116,10 @@ class QueueEngine:
message = "Unexpected failure when processing patch! Please file a bug against webkit-patch.\n%s" % e.message_with_output()
self._delegate.handle_unexpected_error(work_item, message)
except TerminateQueue, e:
- log("\nTerminateQueue exception received.")
+ self._stopping("TerminateQueue exception received.")
return 0
except KeyboardInterrupt, e:
- log("\nUser terminated queue.")
+ self._stopping("User terminated queue.")
return 1
except Exception, e:
traceback.print_exc()
@@ -129,6 +128,13 @@ class QueueEngine:
# Never reached.
self._ensure_work_log_closed()
+ def _stopping(self, message):
+ log("\n%s" % message)
+ self._delegate.stop_work_queue(message)
+ # Be careful to shut down our OutputTee or the unit tests will be unhappy.
+ self._ensure_work_log_closed()
+ self._output_tee.remove_log(self._queue_log)
+
def _begin_logging(self):
self._queue_log = self._output_tee.add_log(self._delegate.queue_log_path())
self._work_log = None
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
index ec91bdb..bfec401 100644
--- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
@@ -34,7 +34,9 @@ import threading
import unittest
from webkitpy.common.system.executive import ScriptError
-from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate, TerminateQueue
+
class LoggingDelegate(QueueEngineDelegate):
def __init__(self, test):
@@ -94,14 +96,19 @@ class LoggingDelegate(QueueEngineDelegate):
self._test.assertEquals(work_item, "work_item")
-class ThrowErrorDelegate(LoggingDelegate):
- def __init__(self, test, error_code):
+class RaisingDelegate(LoggingDelegate):
+ def __init__(self, test, exception):
LoggingDelegate.__init__(self, test)
- self.error_code = error_code
+ self._exception = exception
+ self.stop_message = None
def process_work_item(self, work_item):
self.record("process_work_item")
- raise ScriptError(exit_code=self.error_code)
+ raise self._exception
+
+ def stop_work_queue(self, message):
+ self.record("stop_work_queue")
+ self.stop_message = message
class NotSafeToProceedDelegate(LoggingDelegate):
@@ -132,7 +139,7 @@ class QueueEngineTest(unittest.TestCase):
self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "work_log_path", "work_item.log")))
def test_unexpected_error(self):
- delegate = ThrowErrorDelegate(self, 3)
+ delegate = RaisingDelegate(self, ScriptError(exit_code=3))
work_queue = QueueEngine("error-queue", delegate, threading.Event())
work_queue.run()
expected_callbacks = LoggingDelegate.expected_callbacks[:]
@@ -143,11 +150,32 @@ class QueueEngineTest(unittest.TestCase):
self.assertEquals(delegate._callbacks, expected_callbacks)
def test_handled_error(self):
- delegate = ThrowErrorDelegate(self, QueueEngine.handled_error_code)
+ delegate = RaisingDelegate(self, ScriptError(exit_code=QueueEngine.handled_error_code))
work_queue = QueueEngine("handled-error-queue", delegate, threading.Event())
work_queue.run()
self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
+ def _test_terminating_queue(self, exception, expected_message):
+ work_item_index = LoggingDelegate.expected_callbacks.index('process_work_item')
+ # The terminating error should be handled right after process_work_item.
+ # There should be no other callbacks after stop_work_queue.
+ expected_callbacks = LoggingDelegate.expected_callbacks[:work_item_index + 1]
+ expected_callbacks.append("stop_work_queue")
+
+ delegate = RaisingDelegate(self, exception)
+ work_queue = QueueEngine("terminating-queue", delegate, threading.Event())
+
+ output = OutputCapture()
+ expected_stderr = "\n%s\n" % expected_message
+ output.assert_outputs(self, work_queue.run, [], expected_stderr=expected_stderr)
+
+ self.assertEquals(delegate._callbacks, expected_callbacks)
+ self.assertEquals(delegate.stop_message, expected_message)
+
+ def test_terminating_error(self):
+ 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)
work_queue = FastQueueEngine(delegate)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download.py b/WebKitTools/Scripts/webkitpy/tool/commands/download.py
index d27ab0e..ed0e3d6 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/download.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/download.py
@@ -194,6 +194,19 @@ class BuildAttachment(AbstractPatchSequencingCommand, ProcessAttachmentsMixin):
]
+class BuildAndTestAttachment(AbstractPatchSequencingCommand, ProcessAttachmentsMixin):
+ name = "build-and-test-attachment"
+ help_text = "Apply, build, and test patches from bugzilla"
+ argument_names = "ATTACHMENT_ID [ATTACHMENT_IDS]"
+ main_steps = [
+ steps.CleanWorkingDirectory,
+ steps.Update,
+ steps.ApplyPatch,
+ steps.Build,
+ steps.RunTests,
+ ]
+
+
class PostAttachmentToRietveld(AbstractPatchSequencingCommand, ProcessAttachmentsMixin):
name = "post-attachment-to-rietveld"
help_text = "Uploads a bugzilla attachment to rietveld"
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
index 1f04923..3b0ea47 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
@@ -42,16 +42,15 @@ class EarlyWarningSytemTest(QueuesTest):
def _default_expected_stderr(self, ews):
string_replacemnts = {
"name": ews.name,
- "checkout_dir": os.getcwd(), # FIXME: Use of os.getcwd() is wrong, should be scm.checkout_root
"port": ews.port_name,
"watchers": ews.watchers,
}
expected_stderr = {
- "begin_work_queue": "CAUTION: %(name)s will discard all local changes in \"%(checkout_dir)s\"\nRunning WebKit %(name)s.\n" % string_replacemnts,
+ "begin_work_queue": self._default_begin_work_queue_stderr(ews.name, os.getcwd()), # FIXME: Use of os.getcwd() is wrong, should be scm.checkout_root
"handle_unexpected_error": "Mock error message\n",
"next_work_item": "MOCK: update_work_items: %(name)s [103]\n" % string_replacemnts,
"process_work_item": "MOCK: update_status: %(name)s Pass\n" % string_replacemnts,
- "handle_script_error": "MOCK: update_status: %(name)s ScriptError error message\nMOCK bug comment: bug_id=345, cc=%(watchers)s\n--- Begin comment ---\\Attachment 1234 did not build on %(port)s:\nBuild output: http://dummy_url\n--- End comment ---\n\n" % string_replacemnts,
+ "handle_script_error": "MOCK: update_status: %(name)s ScriptError error message\nMOCK bug comment: bug_id=142, cc=%(watchers)s\n--- Begin comment ---\\Attachment 197 did not build on %(port)s:\nBuild output: http://dummy_url\n--- End comment ---\n\n" % string_replacemnts,
}
return expected_stderr
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
index 4d2a9df..bc9ee42 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
@@ -95,6 +95,10 @@ class AbstractQueue(Command, QueueEngineDelegate):
if (response != "yes"):
error("User declined.")
log("Running WebKit %s." % self.name)
+ self.tool.status_server.update_status(self.name, "Starting Queue")
+
+ def stop_work_queue(self, reason):
+ self.tool.status_server.update_status(self.name, "Stopping Queue, reason: %s" % reason)
def should_continue_work_queue(self):
self._iteration_count += 1
@@ -115,8 +119,8 @@ class AbstractQueue(Command, QueueEngineDelegate):
# Command methods
def execute(self, options, args, tool, engine=QueueEngine):
- self.options = options
- self.tool = tool
+ self.options = options # FIXME: This code is wrong. Command.options is a list, this assumes an Options element!
+ self.tool = tool # FIXME: This code is wrong too! Command.bind_to_tool handles this!
return engine(self.name, self, self.tool.wakeup_event).run()
@classmethod
@@ -144,8 +148,16 @@ class AbstractPatchQueue(AbstractQueue):
def _update_status(self, message, patch=None, results_file=None):
self.tool.status_server.update_status(self.name, message, patch, results_file)
+ # Note, eventually this will be done by a separate "feeder" queue
+ # whose job it is to poll bugzilla and feed work items into the
+ # status server for other queues to munch on.
def _update_work_items(self, patch_ids):
self.tool.status_server.update_work_items(self.name, patch_ids)
+ if patch_ids:
+ self.log_progress(patch_ids)
+
+ def _fetch_next_work_item(self):
+ return self.tool.status_server.next_work_item(self.name)
def _did_pass(self, patch):
self._update_status(self._pass_status, patch)
@@ -189,18 +201,22 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler):
return rollout_cmp
return cmp(a.attach_date(), b.attach_date())
- def next_work_item(self):
+ def _feed_work_items_to_server(self):
+ # Grab the set of patches from bugzilla, sort them, and update the status server.
+ # Eventually this will all be done by a separate feeder queue.
patches = self._validate_patches_in_commit_queue()
patches = sorted(patches, self._patch_cmp)
self._update_work_items([patch.id() for patch in patches])
- if not patches:
- self._update_status("Empty queue")
+
+ def next_work_item(self):
+ self._feed_work_items_to_server()
+ # The grab the next patch to work on back from the status server.
+ patch_id = self._fetch_next_work_item()
+ if not patch_id:
return None
- # Only bother logging if we have patches in the queue.
- self.log_progress([patch.id() for patch in patches])
- return patches[0]
+ return self.tool.bugs.fetch_attachment(patch_id)
- def _can_build_and_test(self):
+ def _can_build_and_test_without_patch(self):
try:
self.run_webkit_patch([
"build-and-test",
@@ -211,25 +227,24 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler):
"--no-update",
"--build-style=both",
"--quiet"])
+ return True
except ScriptError, e:
failure_log = self._log_from_script_error_for_upload(e)
- self._update_status("Unable to successfully build and test", results_file=failure_log)
+ self._update_status("Unable to build and test without patch", results_file=failure_log)
return False
- return True
def should_proceed_with_work_item(self, patch):
patch_text = "rollout patch" if patch.is_rollout() else "patch"
self._update_status("Landing %s" % patch_text, patch)
return True
- def _land(self, patch, first_run=False):
+ def _build_and_test_patch(self, patch, first_run=False):
try:
args = [
- "land-attachment",
+ "build-and-test-attachment",
"--force-clean",
"--build",
"--non-interactive",
- "--ignore-builders",
"--build-style=both",
"--quiet",
patch.id()
@@ -249,27 +264,68 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler):
# built and tested.
args.append("--no-update")
self.run_webkit_patch(args)
- self._did_pass(patch)
return True
except ScriptError, e:
+ failure_log = self._log_from_script_error_for_upload(e)
+ self._update_status("Unable to build and test patch", patch=patch, results_file=failure_log)
if first_run:
return False
self._did_fail(patch)
raise
+ def _revalidate_patch(self, patch):
+ # Bugs might get closed, or patches might be obsoleted or r-'d while the
+ # commit-queue is processing. Do one last minute check before landing.
+ patch = self.tool.bugs.fetch_attachment(patch.id())
+ if patch.is_obsolete():
+ return None
+ if patch.bug().is_closed():
+ return None
+ if not patch.committer():
+ return None
+ # Reviewer is not required. Misisng reviewers will be caught during the ChangeLog check during landing.
+ return patch
+
+ def _land(self, patch):
+ try:
+ args = [
+ "land-attachment",
+ "--force-clean",
+ "--non-interactive",
+ "--ignore-builders",
+ "--quiet",
+ "--parent-command=commit-queue",
+ patch.id(),
+ ]
+ self.run_webkit_patch(args)
+ self._did_pass(patch)
+ except ScriptError, e:
+ failure_log = self._log_from_script_error_for_upload(e)
+ self._update_status("Unable to land patch", patch=patch, results_file=failure_log)
+ self._did_fail(patch)
+ raise
+
def process_work_item(self, patch):
self._cc_watchers(patch.bug_id())
- if not self._land(patch, first_run=True):
- # The patch failed to land, but the bots were green. It's possible
- # that the bots were behind. To check that case, we try to build and
- # test ourselves.
- if not self._can_build_and_test():
+ if not self._build_and_test_patch(patch, first_run=True):
+ self._update_status("Building and testing without the patch as a sanity check", patch)
+ # The patch failed to build and test. It's possible that the
+ # tree is busted. To check that case, we try to build and test
+ # without the patch.
+ if not self._can_build_and_test_without_patch():
return False
+ self._update_status("Build and test succeeded, trying again with patch", patch)
# Hum, looks like the patch is actually bad. Of course, we could
# have been bitten by a flaky test the first time around. We try
- # to land again. If it fails a second time, we're pretty sure its
- # a bad test and re can reject it outright.
- self._land(patch)
+ # to build and test again. If it fails a second time, we're pretty
+ # sure its a bad test and re can reject it outright.
+ self._build_and_test_patch(patch)
+ # Do one last check to catch any bug changes (cq-, closed, reviewer changed, etc.)
+ # This helps catch races between the bots if locks expire.
+ patch = self._revalidate_patch(patch)
+ if not patch:
+ return False
+ self._land(patch)
return True
def handle_unexpected_error(self, patch, message):
@@ -291,6 +347,8 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler):
@classmethod
def handle_checkout_needs_update(cls, tool, state, options, error):
+ message = "Tests passed, but commit failed (checkout out of date). Updating, then landing without building or re-running tests."
+ tool.status_server.update_status(cls.name, message, state["patch"])
# The only time when we find out that out checkout needs update is
# when we were ready to actually pull the trigger and land the patch.
# Rather than spinning in the master process, we retry without
@@ -377,7 +435,6 @@ class AbstractReviewQueue(AbstractPatchQueue, PersistentPatchCollectionDelegate,
patch_id = self._patches.next()
if patch_id:
return self.tool.bugs.fetch_attachment(patch_id)
- self._update_status("Empty queue")
def should_proceed_with_work_item(self, patch):
raise NotImplementedError, "subclasses must implement"
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
index fd6543c..2deee76 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
@@ -34,9 +34,9 @@ from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.thirdparty.mock import Mock
from webkitpy.tool.commands.commandtest import CommandsTest
from webkitpy.tool.commands.queues import *
-from webkitpy.tool.commands.queuestest import QueuesTest
+from webkitpy.tool.commands.queuestest import QueuesTest, MockPatch
from webkitpy.tool.commands.stepsequence import StepSequence
-from webkitpy.tool.mocktool import MockTool, MockSCM
+from webkitpy.tool.mocktool import MockTool, MockSCM, MockStatusServer
class TestQueue(AbstractPatchQueue):
@@ -47,16 +47,10 @@ class TestReviewQueue(AbstractReviewQueue):
name = "test-review-queue"
-class MockPatch(object):
+class MockRolloutPatch(MockPatch):
def is_rollout(self):
return True
- def bug_id(self):
- return 12345
-
- def id(self):
- return 76543
-
class AbstractQueueTest(CommandsTest):
def _assert_log_progress_output(self, patch_ids, progress_output):
@@ -121,6 +115,16 @@ class AbstractQueueTest(CommandsTest):
self._assert_log_message(script_error, expected_output)
+class AbstractPatchQueueTest(CommandsTest):
+ def test_fetch_next_work_item(self):
+ queue = AbstractPatchQueue()
+ tool = MockTool()
+ queue.bind_to_tool(tool)
+ self.assertEquals(queue._fetch_next_work_item(), None)
+ tool.status_server = MockStatusServer(work_items=[2, 1, 3])
+ self.assertEquals(queue._fetch_next_work_item(), 2)
+
+
class AbstractReviewQueueTest(CommandsTest):
def test_patch_collection_delegate_methods(self):
queue = TestReviewQueue()
@@ -141,25 +145,47 @@ class NeedsUpdateSequence(StepSequence):
class AlwaysCommitQueueTool(object):
+ def __init__(self):
+ self.status_server = MockStatusServer()
+
def command_by_name(self, name):
return CommitQueue
+class SecondThoughtsCommitQueue(CommitQueue):
+ def _build_and_test_patch(self, patch, first_run=True):
+ attachment_dictionary = {
+ "id": patch.id(),
+ "bug_id": patch.bug_id(),
+ "name": "Rejected",
+ "is_obsolete": True,
+ "is_patch": False,
+ "review": "-",
+ "reviewer_email": "foo@bar.com",
+ "commit-queue": "-",
+ "committer_email": "foo@bar.com",
+ "attacher_email": "Contributer1",
+ }
+ patch = Attachment(attachment_dictionary, None)
+ self.tool.bugs.set_override_patch(patch)
+ return True
+
+
class CommitQueueTest(QueuesTest):
def test_commit_queue(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root,
+ "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
"should_proceed_with_work_item": "MOCK: update_status: commit-queue Landing patch\n",
# FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time.
- "next_work_item" : """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
+ "next_work_item": """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
MOCK setting flag 'commit-queue' to '-' on attachment '128' with comment 'Rejecting patch 128 from commit-queue.' and additional comment 'non-committer@example.com does not have committer permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py.\n\n- If you do not have committer rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags.\n\n- If you have committer rights please correct the error in WebKitTools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed). The commit-queue restarts itself every 2 hours. After restart the commit-queue will correctly respect your committer rights.'
MOCK: update_work_items: commit-queue [106, 197]
2 patches in commit-queue [106, 197]
""",
- "process_work_item" : "MOCK: update_status: commit-queue Pass\n",
- "handle_unexpected_error" : "MOCK setting flag 'commit-queue' to '-' on attachment '1234' with comment 'Rejecting patch 1234 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "MOCK: update_status: commit-queue ScriptError error message\nMOCK setting flag 'commit-queue' to '-' on attachment '1234' with comment 'Rejecting patch 1234 from commit-queue.' and additional comment 'ScriptError error message'\n",
+ "process_work_item": "MOCK: update_status: commit-queue Pass\n",
+ "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n",
+ "handle_script_error": "MOCK: update_status: commit-queue ScriptError error message\nMOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'ScriptError error message'\n",
}
self.assert_queue_outputs(CommitQueue(), expected_stderr=expected_stderr)
@@ -167,10 +193,10 @@ MOCK: update_work_items: commit-queue [106, 197]
tool = MockTool(log_executive=True)
tool.buildbot.light_tree_on_fire()
expected_stderr = {
- "begin_work_queue" : "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root,
+ "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
"should_proceed_with_work_item": "MOCK: update_status: commit-queue Landing patch\n",
# FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time.
- "next_work_item" : """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
+ "next_work_item": """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
MOCK setting flag 'commit-queue' to '-' on attachment '128' with comment 'Rejecting patch 128 from commit-queue.' and additional comment 'non-committer@example.com does not have committer permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py.
@@ -180,18 +206,18 @@ MOCK setting flag 'commit-queue' to '-' on attachment '128' with comment 'Reject
MOCK: update_work_items: commit-queue [106, 197]
2 patches in commit-queue [106, 197]
""",
- "process_work_item" : "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--build', '--non-interactive', '--ignore-builders', '--build-style=both', '--quiet', 1234, '--test']\nMOCK: update_status: commit-queue Pass\n",
- "handle_unexpected_error" : "MOCK setting flag 'commit-queue' to '-' on attachment '1234' with comment 'Rejecting patch 1234 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "MOCK: update_status: commit-queue ScriptError error message\nMOCK setting flag 'commit-queue' to '-' on attachment '1234' with comment 'Rejecting patch 1234 from commit-queue.' and additional comment 'ScriptError error message'\n",
+ "process_work_item": "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test-attachment', '--force-clean', '--build', '--non-interactive', '--build-style=both', '--quiet', 197, '--test']\nMOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--non-interactive', '--ignore-builders', '--quiet', '--parent-command=commit-queue', 197]\nMOCK: update_status: commit-queue Pass\n",
+ "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n",
+ "handle_script_error": "MOCK: update_status: commit-queue ScriptError error message\nMOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'ScriptError error message'\n",
}
self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr)
def test_rollout_lands(self):
tool = MockTool(log_executive=True)
tool.buildbot.light_tree_on_fire()
- rollout_patch = MockPatch()
+ rollout_patch = MockRolloutPatch()
expected_stderr = {
- "begin_work_queue": "CAUTION: commit-queue will discard all local changes in \"%s\"\nRunning WebKit commit-queue.\n" % MockSCM.fake_checkout_root,
+ "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
"should_proceed_with_work_item": "MOCK: update_status: commit-queue Landing rollout patch\n",
# FIXME: The commit-queue warns about bad committers twice. This is due to the fact that we access Attachment.reviewer() twice and it logs each time.
"next_work_item": """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
@@ -204,9 +230,9 @@ MOCK setting flag 'commit-queue' to '-' on attachment '128' with comment 'Reject
MOCK: update_work_items: commit-queue [106, 197]
2 patches in commit-queue [106, 197]
""",
- "process_work_item": "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--build', '--non-interactive', '--ignore-builders', '--build-style=both', '--quiet', 76543]\nMOCK: update_status: commit-queue Pass\n",
- "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '76543' with comment 'Rejecting patch 76543 from commit-queue.' and additional comment 'Mock error message'\n",
- "handle_script_error": "MOCK: update_status: commit-queue ScriptError error message\nMOCK setting flag 'commit-queue' to '-' on attachment '1234' with comment 'Rejecting patch 1234 from commit-queue.' and additional comment 'ScriptError error message'\n",
+ "process_work_item": "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test-attachment', '--force-clean', '--build', '--non-interactive', '--build-style=both', '--quiet', 197]\nMOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--non-interactive', '--ignore-builders', '--quiet', '--parent-command=commit-queue', 197]\nMOCK: update_status: commit-queue Pass\n",
+ "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'Mock error message'\n",
+ "handle_script_error": "MOCK: update_status: commit-queue ScriptError error message\nMOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting patch 197 from commit-queue.' and additional comment 'ScriptError error message'\n",
}
self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr)
@@ -215,7 +241,7 @@ MOCK: update_work_items: commit-queue [106, 197]
tool = MockTool()
tool.executive = Mock()
queue.bind_to_tool(tool)
- self.assertTrue(queue._can_build_and_test())
+ self.assertTrue(queue._can_build_and_test_without_patch())
expected_run_args = ["echo", "--status-host=example.com", "build-and-test", "--force-clean", "--build", "--test", "--non-interactive", "--no-update", "--build-style=both", "--quiet"]
tool.executive.run_and_throw_if_fail.assert_called_with(expected_run_args)
@@ -245,22 +271,28 @@ MOCK: update_work_items: commit-queue [106, 197]
tool = AlwaysCommitQueueTool()
sequence = NeedsUpdateSequence(None)
- expected_stderr = "Commit failed because the checkout is out of date. Please update and try again.\n"
- OutputCapture().assert_outputs(self, sequence.run_and_handle_errors, [tool, options], expected_exception=TryAgain, expected_stderr=expected_stderr)
+ expected_stderr = "Commit failed because the checkout is out of date. Please update and try again.\nMOCK: update_status: commit-queue Tests passed, but commit failed (checkout out of date). Updating, then landing without building or re-running tests.\n"
+ state = {'patch': None}
+ OutputCapture().assert_outputs(self, sequence.run_and_handle_errors, [tool, options, state], expected_exception=TryAgain, expected_stderr=expected_stderr)
self.assertEquals(options.update, True)
self.assertEquals(options.build, False)
self.assertEquals(options.test, False)
+ def test_manual_reject_during_processing(self):
+ queue = SecondThoughtsCommitQueue()
+ queue.bind_to_tool(MockTool())
+ queue.process_work_item(MockPatch())
+
class RietveldUploadQueueTest(QueuesTest):
def test_rietveld_upload_queue(self):
expected_stderr = {
- "begin_work_queue": "CAUTION: rietveld-upload-queue will discard all local changes in \"%s\"\nRunning WebKit rietveld-upload-queue.\n" % MockSCM.fake_checkout_root,
+ "begin_work_queue": self._default_begin_work_queue_stderr("rietveld-upload-queue", MockSCM.fake_checkout_root),
"should_proceed_with_work_item": "MOCK: update_status: rietveld-upload-queue Uploading patch\n",
"process_work_item": "MOCK: update_status: rietveld-upload-queue Pass\n",
- "handle_unexpected_error": "Mock error message\nMOCK setting flag 'in-rietveld' to '-' on attachment '1234' with comment 'None' and additional comment 'None'\n",
- "handle_script_error": "ScriptError error message\nMOCK: update_status: rietveld-upload-queue ScriptError error message\nMOCK setting flag 'in-rietveld' to '-' on attachment '1234' with comment 'None' and additional comment 'None'\n",
+ "handle_unexpected_error": "Mock error message\nMOCK setting flag 'in-rietveld' to '-' on attachment '197' with comment 'None' and additional comment 'None'\n",
+ "handle_script_error": "ScriptError error message\nMOCK: update_status: rietveld-upload-queue ScriptError error message\nMOCK setting flag 'in-rietveld' to '-' on attachment '197' with comment 'None' and additional comment 'None'\n",
}
self.assert_queue_outputs(RietveldUploadQueue(), expected_stderr=expected_stderr)
@@ -268,12 +300,12 @@ class RietveldUploadQueueTest(QueuesTest):
class StyleQueueTest(QueuesTest):
def test_style_queue(self):
expected_stderr = {
- "begin_work_queue" : "CAUTION: style-queue will discard all local changes in \"%s\"\nRunning WebKit style-queue.\n" % MockSCM.fake_checkout_root,
+ "begin_work_queue": self._default_begin_work_queue_stderr("style-queue", MockSCM.fake_checkout_root),
"next_work_item": "MOCK: update_work_items: style-queue [103]\n",
"should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n",
- "process_work_item" : "MOCK: update_status: style-queue Pass\n",
- "handle_unexpected_error" : "Mock error message\n",
- "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=345, cc=[]\n--- Begin comment ---\\Attachment 1234 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",
+ "process_work_item": "MOCK: update_status: style-queue Pass\n",
+ "handle_unexpected_error": "Mock error message\n",
+ "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=142, cc=[]\n--- Begin comment ---\\Attachment 197 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,
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
index 9e17c5c..aa3cef4 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
@@ -45,16 +45,20 @@ class MockQueueEngine(object):
class MockPatch():
def id(self):
- return 1234
+ return 197
def bug_id(self):
- return 345
+ return 142
+
+ def is_rollout(self):
+ return False
class QueuesTest(unittest.TestCase):
+ # Ids match patch1 in mocktool.py
mock_work_item = Attachment({
- "id": 1234,
- "bug_id": 345,
+ "id": 197,
+ "bug_id": 142,
"name": "Patch",
"attacher_email": "adam@example.com",
}, None)
@@ -71,6 +75,10 @@ class QueuesTest(unittest.TestCase):
expected_stderr=expected_stderr.get(func_name, ""),
expected_exception=exception)
+ def _default_begin_work_queue_stderr(self, name, checkout_dir):
+ string_replacements = {"name": name, 'checkout_dir': checkout_dir}
+ return "CAUTION: %(name)s will discard all local changes in \"%(checkout_dir)s\"\nRunning WebKit %(name)s.\nMOCK: update_status: %(name)s Starting Queue\n" % string_replacements
+
def assert_queue_outputs(self, queue, args=None, work_item=None, expected_stdout=None, expected_stderr=None, expected_exceptions=None, options=Mock(), tool=MockTool()):
if not expected_stdout:
expected_stdout = {}
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
index 4b4b8b6..a63ec24 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
@@ -42,7 +42,7 @@ class SheriffBotTest(QueuesTest):
29837: [self.builder1],
}
expected_stderr = {
- "begin_work_queue": "CAUTION: sheriff-bot will discard all local changes in \"%s\"\nRunning WebKit sheriff-bot.\n" % os.getcwd(),
+ "begin_work_queue": self._default_begin_work_queue_stderr("sheriff-bot", os.getcwd()),
"next_work_item": "",
"process_work_item": "MOCK: irc.post: abarth, darin, eseidel: http://trac.webkit.org/changeset/29837 might have broken Builder1\nMOCK bug comment: bug_id=42, cc=['abarth@webkit.org', 'eric@webkit.org']\n--- Begin comment ---\\http://trac.webkit.org/changeset/29837 might have broken Builder1\n--- End comment ---\n\n",
"handle_unexpected_error": "Mock error message\n"
diff --git a/WebKitTools/Scripts/webkitpy/tool/mocktool.py b/WebKitTools/Scripts/webkitpy/tool/mocktool.py
index e3d36ce..8a6188a 100644
--- a/WebKitTools/Scripts/webkitpy/tool/mocktool.py
+++ b/WebKitTools/Scripts/webkitpy/tool/mocktool.py
@@ -161,6 +161,7 @@ _bug1 = {
"invalid commit-queue setter.",
"assigned_to_email": _unassigned_email,
"attachments": [_patch1, _patch2],
+ "bug_status": "UNCONFIRMED",
}
@@ -169,6 +170,7 @@ _bug2 = {
"title": "Bug with a patch needing review.",
"assigned_to_email": "foo@foo.com",
"attachments": [_patch3],
+ "bug_status": "ASSIGNED",
}
@@ -177,6 +179,7 @@ _bug3 = {
"title": "The third bug",
"assigned_to_email": _unassigned_email,
"attachments": [_patch7],
+ "bug_status": "NEW",
}
@@ -185,6 +188,7 @@ _bug4 = {
"title": "The fourth bug",
"assigned_to_email": "foo@foo.com",
"attachments": [_patch4, _patch5, _patch6],
+ "bug_status": "REOPENED",
}
@@ -254,8 +258,8 @@ class MockBugzilla(Mock):
def __init__(self):
Mock.__init__(self)
self.queries = MockBugzillaQueries(self)
- self.committers = CommitterList(reviewers=[Reviewer("Foo Bar",
- "foo@bar.com")])
+ self.committers = CommitterList(reviewers=[Reviewer("Foo Bar", "foo@bar.com")])
+ self._override_patch = None
def create_bug(self,
bug_title,
@@ -277,7 +281,13 @@ class MockBugzilla(Mock):
def fetch_bug(self, bug_id):
return Bug(self.bug_cache.get(bug_id), self)
+ def set_override_patch(self, patch):
+ self._override_patch = patch
+
def fetch_attachment(self, attachment_id):
+ if self._override_patch:
+ return self._override_patch
+
# This could be changed to .get() if we wish to allow failed lookups.
attachment_dictionary = self.attachment_cache[attachment_id]
bug = self.fetch_bug(attachment_dictionary["bug_id"])
@@ -497,8 +507,9 @@ class MockIRC(object):
class MockStatusServer(object):
- def __init__(self):
+ def __init__(self, work_items=None):
self.host = "example.com"
+ self._work_items = work_items or []
def patch_status(self, queue_name, patch_id):
return None
@@ -506,7 +517,13 @@ class MockStatusServer(object):
def svn_revision(self, svn_revision):
return None
+ def next_work_item(self, queue_name):
+ if not self._work_items:
+ return None
+ return self._work_items[0]
+
def update_work_items(self, queue_name, work_items):
+ self._work_items = work_items
log("MOCK: update_work_items: %s %s" % (queue_name, work_items))
def update_status(self, queue_name, status, patch=None, results_file=None):
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py b/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py
index 6cded27..327ac09 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/applypatch.py
@@ -35,8 +35,9 @@ class ApplyPatch(AbstractStep):
def options(cls):
return AbstractStep.options() + [
Options.non_interactive,
+ Options.force_patch,
]
def run(self, state):
log("Processing patch %s from bug %s." % (state["patch"].id(), state["patch"].bug_id()))
- self._tool.checkout().apply_patch(state["patch"], force=self._options.non_interactive)
+ self._tool.checkout().apply_patch(state["patch"], force=self._options.non_interactive or self._options.force_patch)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/options.py b/WebKitTools/Scripts/webkitpy/tool/steps/options.py
index e7e3855..3dc1963 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/options.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/options.py
@@ -43,6 +43,7 @@ class Options(object):
description = make_option("-m", "--description", action="store", type="string", dest="description", help="Description string for the attachment (default: \"patch\")")
email = make_option("--email", action="store", type="string", dest="email", help="Email address to use in ChangeLogs.")
force_clean = make_option("--force-clean", action="store_true", dest="force_clean", default=False, help="Clean working directory before applying patches (removes local changes and commits)")
+ force_patch = make_option("--force-patch", action="store_true", dest="force_patch", default=False, help="Forcefully applies the patch, continuing past errors.")
git_commit = make_option("-g", "--git-commit", action="store", dest="git_commit", help="Operate on a local commit. If a range, the commits are squashed into one. HEAD.. operates on working copy changes only.")
local_commit = make_option("--local-commit", action="store_true", dest="local_commit", default=False, help="Make a local commit for each applied patch")
non_interactive = make_option("--non-interactive", action="store_true", dest="non_interactive", default=False, help="Never prompt the user, fail as fast as possible.")