summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/tool
diff options
context:
space:
mode:
authorJohn Reck <jreck@google.com>2010-11-04 12:00:17 -0700
committerJohn Reck <jreck@google.com>2010-11-09 11:35:04 -0800
commite14391e94c850b8bd03680c23b38978db68687a8 (patch)
tree3fed87e6620fecaf3edc7259ae58a11662bedcb2 /WebKitTools/Scripts/webkitpy/tool
parent1bd705833a68f07850cf7e204b26f8d328d16951 (diff)
downloadexternal_webkit-e14391e94c850b8bd03680c23b38978db68687a8.zip
external_webkit-e14391e94c850b8bd03680c23b38978db68687a8.tar.gz
external_webkit-e14391e94c850b8bd03680c23b38978db68687a8.tar.bz2
Merge Webkit at r70949: Initial merge by git.
Change-Id: I77b8645c083b5d0da8dba73ed01d4014aab9848e
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/tool')
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py7
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py60
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/download.py16
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py7
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py11
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py70
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues.py106
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py91
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py22
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py5
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/upload.py6
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py11
-rwxr-xr-xWebKitTools/Scripts/webkitpy/tool/main.py3
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/mocktool.py58
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/__init__.py2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/options.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/runtests.py1
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py2
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers.py (renamed from WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py)43
-rw-r--r--WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py45
21 files changed, 297 insertions, 271 deletions
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py
index 02e203c..ea12702 100644
--- a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py
@@ -85,7 +85,6 @@ class CommitQueueTask(object):
"apply-attachment",
"--force-clean",
"--non-interactive",
- "--quiet",
self._patch.id(),
],
"Applied patch",
@@ -97,7 +96,6 @@ class CommitQueueTask(object):
"--no-clean",
"--no-update",
"--build-style=both",
- "--quiet",
],
"Built patch",
"Patch does not build")
@@ -108,7 +106,6 @@ class CommitQueueTask(object):
"--force-clean",
"--no-update",
"--build-style=both",
- "--quiet",
],
"Able to build without patch",
"Unable to build without patch")
@@ -120,7 +117,6 @@ class CommitQueueTask(object):
"--no-update",
# Notice that we don't pass --build, which means we won't build!
"--test",
- "--quiet",
"--non-interactive",
],
"Passed tests",
@@ -133,7 +129,6 @@ class CommitQueueTask(object):
"--no-update",
"--build",
"--test",
- "--quiet",
"--non-interactive",
],
"Able to pass tests without patch",
@@ -146,11 +141,11 @@ class CommitQueueTask(object):
return results.failing_tests()
def _land(self):
+ # Unclear if this should pass --quiet or not. If --parent-command always does the reporting, then it should.
return self._run_command([
"land-attachment",
"--force-clean",
"--ignore-builders",
- "--quiet",
"--non-interactive",
"--parent-command=commit-queue",
self._patch.id(),
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py
index 6fa0667..15a4a6b 100644
--- a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py
@@ -75,13 +75,13 @@ class CommitQueueTaskTest(unittest.TestCase):
def test_success_case(self):
commit_queue = MockCommitQueue([])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_passed: success_message='Passed tests' patch='197'
-run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--quiet', '--non-interactive', '--parent-command=commit-queue', 197]
+run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
command_passed: success_message='Landed patch' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr)
@@ -90,7 +90,7 @@ command_passed: success_message='Landed patch' patch='197'
commit_queue = MockCommitQueue([
ScriptError("MOCK apply failure"),
])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_failed: failure_message='Patch does not apply' script_error='MOCK apply failure' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr, ScriptError)
@@ -100,11 +100,11 @@ command_failed: failure_message='Patch does not apply' script_error='MOCK apply
None,
ScriptError("MOCK build failure"),
])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
command_failed: failure_message='Patch does not build' script_error='MOCK build failure' patch='197'
-run_webkit_patch: ['build', '--force-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--force-clean', '--no-update', '--build-style=both']
command_passed: success_message='Able to build without patch' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr, ScriptError)
@@ -115,11 +115,11 @@ command_passed: success_message='Able to build without patch' patch='197'
ScriptError("MOCK build failure"),
ScriptError("MOCK clean build failure"),
])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
command_failed: failure_message='Patch does not build' script_error='MOCK build failure' patch='197'
-run_webkit_patch: ['build', '--force-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--force-clean', '--no-update', '--build-style=both']
command_failed: failure_message='Unable to build without patch' script_error='MOCK clean build failure' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr)
@@ -130,16 +130,16 @@ command_failed: failure_message='Unable to build without patch' script_error='MO
None,
ScriptError("MOCK tests failure"),
])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_failed: failure_message='Patch does not pass tests' script_error='MOCK tests failure' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_passed: success_message='Passed tests' patch='197'
report_flaky_tests: patch='197' flaky_tests='None'
-run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--quiet', '--non-interactive', '--parent-command=commit-queue', 197]
+run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
command_passed: success_message='Landed patch' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr)
@@ -151,15 +151,15 @@ command_passed: success_message='Landed patch' patch='197'
ScriptError("MOCK test failure"),
ScriptError("MOCK test failure again"),
])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure again' patch='197'
-run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--non-interactive']
command_passed: success_message='Able to pass tests without patch' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr, ScriptError)
@@ -172,15 +172,15 @@ command_passed: success_message='Able to pass tests without patch' patch='197'
ScriptError("MOCK test failure again"),
ScriptError("MOCK clean test failure"),
])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure again' patch='197'
-run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--non-interactive']
command_failed: failure_message='Unable to pass tests without patch (tree is red?)' script_error='MOCK clean test failure' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr)
@@ -192,13 +192,13 @@ command_failed: failure_message='Unable to pass tests without patch (tree is red
None,
ScriptError("MOCK land failure"),
])
- expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ expected_stderr = """run_webkit_patch: ['apply-attachment', '--force-clean', '--non-interactive', 197]
command_passed: success_message='Applied patch' patch='197'
-run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+run_webkit_patch: ['build', '--no-clean', '--no-update', '--build-style=both']
command_passed: success_message='Built patch' patch='197'
-run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
command_passed: success_message='Passed tests' patch='197'
-run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--quiet', '--non-interactive', '--parent-command=commit-queue', 197]
+run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
command_failed: failure_message='Unable to land patch' script_error='MOCK land failure' patch='197'
"""
self._run_through_task(commit_queue, expected_stderr, ScriptError)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download.py b/WebKitTools/Scripts/webkitpy/tool/commands/download.py
index ed5c604..541c9c4 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/download.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/download.py
@@ -106,8 +106,10 @@ land will NOT build and run the tests before committing, but you can use the --b
If a bug id is provided, or one can be found in the ChangeLog land will update the bug after committing."""
def _prepare_state(self, options, args, tool):
+ changed_files = self._tool.scm().changed_files(options.git_commit)
return {
- "bug_id": (args and args[0]) or tool.checkout().bug_id_for_this_commit(options.git_commit),
+ "changed_files": changed_files,
+ "bug_id": (args and args[0]) or tool.checkout().bug_id_for_this_commit(options.git_commit, changed_files),
}
@@ -221,18 +223,6 @@ class BuildAndTestAttachment(AbstractPatchSequencingCommand, ProcessAttachmentsM
]
-class PostAttachmentToRietveld(AbstractPatchSequencingCommand, ProcessAttachmentsMixin):
- name = "post-attachment-to-rietveld"
- help_text = "Uploads a bugzilla attachment to rietveld"
- arguments_names = "ATTACHMENTID"
- main_steps = [
- steps.CleanWorkingDirectory,
- steps.Update,
- steps.ApplyPatch,
- steps.PostCodeReview,
- ]
-
-
class AbstractPatchApplyingCommand(AbstractPatchSequencingCommand):
prepare_steps = [
steps.EnsureLocalCommitIfNeeded,
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py
index 6af1f64..bfca139 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/download_unittest.py
@@ -118,10 +118,6 @@ class DownloadCommandsTest(CommandsTest):
expected_stderr = "Processing 1 patch from 1 bug.\nUpdating working directory\nProcessing patch 197 from bug 42.\nBuilding WebKit\n"
self.assert_execute_outputs(BuildAttachment(), [197], options=self._default_options(), expected_stderr=expected_stderr)
- def test_post_attachment_to_rietveld(self):
- expected_stderr = "Processing 1 patch from 1 bug.\nUpdating working directory\nProcessing patch 197 from bug 42.\nMOCK: Uploading patch to rietveld\nMOCK setting flag 'in-rietveld' to '+' on attachment '197' with comment 'None' and additional comment 'None'\n"
- self.assert_execute_outputs(PostAttachmentToRietveld(), [197], options=self._default_options(), expected_stderr=expected_stderr)
-
def test_land_attachment(self):
# FIXME: This expected result is imperfect, notice how it's seeing the same patch as still there after it thought it would have cleared the flags.
expected_stderr = """Processing 1 patch from 1 bug.
@@ -186,5 +182,6 @@ where ATTACHMENT_ID is the ID of this attachment.
def test_rollout(self):
expected_stderr = "Preparing rollout for bug 42.\nUpdating working directory\nRunning prepare-ChangeLog\nMOCK: user.open_url: file://...\nBuilding WebKit\n"
- self.assert_execute_outputs(Rollout(), [852, "Reason"], options=self._default_options(), expected_stderr=expected_stderr)
+ expected_stdout = "Was that diff correct?\n"
+ self.assert_execute_outputs(Rollout(), [852, "Reason"], options=self._default_options(), expected_stdout=expected_stdout, expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
index 5ec468e..3b53d1a 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem.py
@@ -50,8 +50,7 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue):
self.port.flag(),
"--build-style=%s" % self._build_style,
"--force-clean",
- "--no-update",
- "--quiet"])
+ "--no-update"])
return True
except ScriptError, e:
failure_log = self._log_from_script_error_for_upload(e)
@@ -81,6 +80,14 @@ class AbstractEarlyWarningSystem(AbstractReviewQueue):
raise
def review_patch(self, patch):
+ if patch.is_obsolete():
+ self._did_error(patch, "%s does not process obsolete patches." % self.name)
+ return False
+
+ if patch.bug().is_closed():
+ self._did_error(patch, "%s does not process patches on closed bugs." % self.name)
+ return False
+
if not self._build(patch, first_run=True):
if not self._can_build():
return False
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
index c400f81..830e11c 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/earlywarningsystem_unittest.py
@@ -29,15 +29,54 @@
import os
from webkitpy.thirdparty.mock import Mock
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.tool.bot.queueengine import QueueEngine
from webkitpy.tool.commands.earlywarningsystem import *
from webkitpy.tool.commands.queuestest import QueuesTest
+from webkitpy.tool.mocktool import MockTool, MockOptions
+
+
+class AbstractEarlyWarningSystemTest(QueuesTest):
+ def test_can_build(self):
+ # Needed to define port_name, used in AbstractEarlyWarningSystem.__init__
+ class TestEWS(AbstractEarlyWarningSystem):
+ port_name = "win" # Needs to be a port which port/factory understands.
+
+ queue = TestEWS()
+ queue.bind_to_tool(MockTool(log_executive=True))
+ queue._options = MockOptions(port=None)
+ expected_stderr = "MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--port=win', '--build-style=release', '--force-clean', '--no-update']\n"
+ OutputCapture().assert_outputs(self, queue._can_build, [], expected_stderr=expected_stderr)
+
+ def mock_run_webkit_patch(args):
+ raise ScriptError("MOCK script error")
+
+ queue.run_webkit_patch = mock_run_webkit_patch
+ expected_stderr = "MOCK: update_status: None Unable to perform a build\n"
+ OutputCapture().assert_outputs(self, queue._can_build, [], expected_stderr=expected_stderr)
+
+ # FIXME: This belongs on an AbstractReviewQueueTest object in queues_unittest.py
+ def test_subprocess_handled_error(self):
+ queue = AbstractReviewQueue()
+ queue.bind_to_tool(MockTool())
+
+ def mock_review_patch(patch):
+ raise ScriptError('MOCK script error', exit_code=QueueEngine.handled_error_code)
+
+ queue.review_patch = mock_review_patch
+ mock_patch = queue._tool.bugs.fetch_attachment(197)
+ expected_stderr = "MOCK: release_work_item: None 197\n"
+ OutputCapture().assert_outputs(self, queue.process_work_item, [mock_patch], expected_stderr=expected_stderr, expected_exception=ScriptError)
+
class EarlyWarningSytemTest(QueuesTest):
def test_failed_builds(self):
ews = ChromiumLinuxEWS()
+ ews.bind_to_tool(MockTool())
ews._build = lambda patch, first_run=False: False
ews._can_build = lambda: True
- ews.review_patch(Mock())
+ mock_patch = ews._tool.bugs.fetch_attachment(197)
+ ews.review_patch(mock_patch)
def _default_expected_stderr(self, ews):
string_replacemnts = {
@@ -46,20 +85,29 @@ class EarlyWarningSytemTest(QueuesTest):
"watchers": ews.watchers,
}
expected_stderr = {
- "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
+ "begin_work_queue": self._default_begin_work_queue_stderr(ews.name, ews._tool.scm().checkout_root),
"handle_unexpected_error": "Mock error message\n",
"next_work_item": "",
"process_work_item": "MOCK: update_status: %(name)s Pass\nMOCK: release_work_item: %(name)s 197\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 ---\nAttachment 197 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=42, cc=%(watchers)s\n--- Begin comment ---\nAttachment 197 did not build on %(port)s:\nBuild output: http://dummy_url\n--- End comment ---\n\n" % string_replacemnts,
}
return expected_stderr
def _test_ews(self, ews):
+ ews.bind_to_tool(MockTool())
expected_exceptions = {
"handle_script_error": SystemExit,
}
self.assert_queue_outputs(ews, expected_stderr=self._default_expected_stderr(ews), expected_exceptions=expected_exceptions)
+ def _test_committer_only_ews(self, ews):
+ ews.bind_to_tool(MockTool())
+ expected_stderr = self._default_expected_stderr(ews)
+ string_replacemnts = {"name": ews.name}
+ expected_stderr["process_work_item"] = "MOCK: update_status: %(name)s Error: %(name)s cannot process patches from non-committers :(\nMOCK: release_work_item: %(name)s 197\n" % string_replacemnts
+ expected_exceptions = {"handle_script_error": SystemExit}
+ self.assert_queue_outputs(ews, expected_stderr=expected_stderr, expected_exceptions=expected_exceptions)
+
# FIXME: If all EWSes are going to output the same text, we
# could test them all in one method with a for loop over an array.
def test_chromium_linux_ews(self):
@@ -78,19 +126,7 @@ class EarlyWarningSytemTest(QueuesTest):
self._test_ews(EflEWS())
def test_mac_ews(self):
- ews = MacEWS()
- expected_stderr = self._default_expected_stderr(ews)
- expected_stderr["process_work_item"] = "MOCK: update_status: mac-ews Error: mac-ews cannot process patches from non-committers :(\n"
- expected_exceptions = {
- "handle_script_error": SystemExit,
- }
- self.assert_queue_outputs(ews, expected_stderr=expected_stderr, expected_exceptions=expected_exceptions)
+ self._test_committer_only_ews(MacEWS())
def test_chromium_mac_ews(self):
- ews = ChromiumMacEWS()
- expected_stderr = self._default_expected_stderr(ews)
- expected_stderr["process_work_item"] = "MOCK: update_status: cr-mac-ews Error: cr-mac-ews cannot process patches from non-committers :(\n"
- expected_exceptions = {
- "handle_script_error": SystemExit,
- }
- self.assert_queue_outputs(ews, expected_stderr=expected_stderr, expected_exceptions=expected_exceptions)
+ self._test_committer_only_ews(ChromiumMacEWS())
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
index 7b3002a..6b4213b 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues.py
@@ -38,7 +38,7 @@ from datetime import datetime
from optparse import make_option
from StringIO import StringIO
-from webkitpy.common.net.bugzilla import CommitterValidator
+from webkitpy.common.net.bugzilla import CommitterValidator, Attachment
from webkitpy.common.net.layouttestresults import path_for_layout_test, LayoutTestResults
from webkitpy.common.net.statusserver import StatusServer
from webkitpy.common.system.executive import ScriptError
@@ -47,7 +47,7 @@ from webkitpy.tool.commands.stepsequence import StepSequenceErrorHandler
from webkitpy.tool.bot.commitqueuetask import CommitQueueTask, CommitQueueTaskDelegate
from webkitpy.tool.bot.feeders import CommitQueueFeeder, EWSFeeder
from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate
-from webkitpy.tool.grammar import pluralize
+from webkitpy.tool.grammar import pluralize, join_with_separators
from webkitpy.tool.multicommandtool import Command, TryAgain
@@ -87,6 +87,11 @@ class AbstractQueue(Command, QueueEngineDelegate):
if self._options.port:
webkit_patch_args += ["--port=%s" % self._options.port]
webkit_patch_args.extend(args)
+ # FIXME: There is probably no reason to use run_and_throw_if_fail anymore.
+ # run_and_throw_if_fail was invented to support tee'd output
+ # (where we write both to a log file and to the console at once),
+ # but the queues don't need live-progress, a dump-of-output at the
+ # end should be sufficient.
return self._tool.executive.run_and_throw_if_fail(webkit_patch_args)
def _log_directory(self):
@@ -196,24 +201,40 @@ class AbstractPatchQueue(AbstractQueue):
def _update_status(self, message, patch=None, results_file=None):
return self._tool.status_server.update_status(self.name, message, patch, results_file)
- def _fetch_next_work_item(self):
- return self._tool.status_server.next_work_item(self.name)
+ def _next_patch(self):
+ patch_id = self._tool.status_server.next_work_item(self.name)
+ if not patch_id:
+ return None
+ patch = self._tool.bugs.fetch_attachment(patch_id)
+ if not patch:
+ # FIXME: Using a fake patch because release_work_item has the wrong API.
+ # We also don't really need to release the lock (although that's fine),
+ # mostly we just need to remove this bogus patch from our queue.
+ # If for some reason bugzilla is just down, then it will be re-fed later.
+ patch = Attachment({'id': patch_id}, None)
+ self._release_work_item(patch)
+ return None
+ return patch
def _release_work_item(self, patch):
self._tool.status_server.release_work_item(self.name, patch)
def _did_pass(self, patch):
self._update_status(self._pass_status, patch)
+ self._release_work_item(patch)
def _did_fail(self, patch):
self._update_status(self._fail_status, patch)
+ self._release_work_item(patch)
def _did_retry(self, patch):
self._update_status(self._retry_status, patch)
+ self._release_work_item(patch)
def _did_error(self, patch, reason):
message = "%s: %s" % (self._error_status, reason)
self._update_status(message, patch)
+ self._release_work_item(patch)
def work_item_log_path(self, patch):
return os.path.join(self._log_directory(), "%s.log" % patch.bug_id())
@@ -229,10 +250,7 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskD
self.committer_validator = CommitterValidator(self._tool.bugs)
def next_work_item(self):
- patch_id = self._fetch_next_work_item()
- if not patch_id:
- return None
- return self._tool.bugs.fetch_attachment(patch_id)
+ return self._next_patch()
def should_proceed_with_work_item(self, patch):
patch_text = "rollout patch" if patch.is_rollout() else "patch"
@@ -251,7 +269,6 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskD
validator = CommitterValidator(self._tool.bugs)
validator.reject_patch_from_commit_queue(patch.id(), self._error_message_for_bug(task.failure_status_id, e))
self._did_fail(patch)
- self._release_work_item(patch)
def _error_message_for_bug(self, status_id, script_error):
if not script_error.output:
@@ -297,13 +314,17 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskD
def _author_emails_for_tests(self, flaky_tests):
test_paths = map(path_for_layout_test, flaky_tests)
commit_infos = self._tool.checkout().recent_commit_infos_for_files(test_paths)
- return [commit_info.author().bugzilla_email() for commit_info in commit_infos if commit_info.author()]
+ return set([commit_info.author().bugzilla_email() for commit_info in commit_infos if commit_info.author()])
def report_flaky_tests(self, patch, flaky_tests):
- authors = self._author_emails_for_tests(flaky_tests)
- cc_explaination = " The author(s) of the test(s) have been CCed on this bug." if authors else ""
- message = "The %s encountered the following flaky tests while processing attachment %s:\n\n%s\n\nPlease file bugs against the tests.%s The commit-queue is continuing to process your patch." % (self.name, patch.id(), "\n".join(flaky_tests), cc_explaination)
- self._tool.bugs.post_comment_to_bug(patch.bug_id(), message, cc=authors)
+ message = "The %s encountered the following flaky tests while processing attachment %s:" % (self.name, patch.id())
+ message += "\n\n%s\n\n" % ("\n".join(flaky_tests))
+ message += "Please file bugs against the tests. "
+ author_emails = self._author_emails_for_tests(flaky_tests)
+ if author_emails:
+ message += "These tests were authored by %s. " % (join_with_separators(sorted(author_emails)))
+ message += "The commit-queue is continuing to process your patch."
+ self._tool.bugs.post_comment_to_bug(patch.bug_id(), message)
# StepSequenceErrorHandler methods
@@ -327,52 +348,6 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskD
raise TryAgain()
-# FIXME: All the Rietveld code is no longer used and should be deleted.
-class RietveldUploadQueue(AbstractPatchQueue, StepSequenceErrorHandler):
- name = "rietveld-upload-queue"
-
- def __init__(self):
- AbstractPatchQueue.__init__(self)
-
- # AbstractPatchQueue methods
-
- def next_work_item(self):
- patch_id = self._tool.bugs.queries.fetch_first_patch_from_rietveld_queue()
- if patch_id:
- return patch_id
- self._update_status("Empty queue")
-
- def should_proceed_with_work_item(self, patch):
- self._update_status("Uploading patch", patch)
- return True
-
- def process_work_item(self, patch):
- try:
- self.run_webkit_patch(["post-attachment-to-rietveld", "--force-clean", "--non-interactive", "--parent-command=rietveld-upload-queue", patch.id()])
- self._did_pass(patch)
- return True
- except ScriptError, e:
- if e.exit_code != QueueEngine.handled_error_code:
- self._did_fail(patch)
- raise e
-
- @classmethod
- def _reject_patch(cls, tool, patch_id):
- tool.bugs.set_flag_on_attachment(patch_id, "in-rietveld", "-")
-
- def handle_unexpected_error(self, patch, message):
- log(message)
- self._reject_patch(self._tool, patch.id())
-
- # StepSequenceErrorHandler methods
-
- @classmethod
- def handle_script_error(cls, tool, state, script_error):
- log(script_error.message_with_output())
- cls._update_status_for_script_error(tool, state, script_error)
- cls._reject_patch(tool, state["patch"].id())
-
-
class AbstractReviewQueue(AbstractPatchQueue, StepSequenceErrorHandler):
"""This is the base-class for the EWS queues and the style-queue."""
def __init__(self, options=None):
@@ -387,10 +362,7 @@ class AbstractReviewQueue(AbstractPatchQueue, StepSequenceErrorHandler):
AbstractPatchQueue.begin_work_queue(self)
def next_work_item(self):
- patch_id = self._fetch_next_work_item()
- if not patch_id:
- return None
- return self._tool.bugs.fetch_attachment(patch_id)
+ return self._next_patch()
def should_proceed_with_work_item(self, patch):
raise NotImplementedError("subclasses must implement")
@@ -404,9 +376,11 @@ class AbstractReviewQueue(AbstractPatchQueue, StepSequenceErrorHandler):
except ScriptError, e:
if e.exit_code != QueueEngine.handled_error_code:
self._did_fail(patch)
+ else:
+ # The subprocess handled the error, but won't have released the patch, so we do.
+ # FIXME: We need to simplify the rules by which _release_work_item is called.
+ self._release_work_item(patch)
raise e
- finally:
- self._release_work_item(patch)
def handle_unexpected_error(self, patch, message):
log(message)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
index b37b5dc..b45db7b 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queues_unittest.py
@@ -29,12 +29,13 @@
import os
from webkitpy.common.checkout.scm import CheckoutNeedsUpdate
+from webkitpy.common.config.committers import Committer
from webkitpy.common.net.bugzilla import Attachment
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, MockPatch
+from webkitpy.tool.commands.queuestest import QueuesTest
from webkitpy.tool.commands.stepsequence import StepSequence
from webkitpy.tool.mocktool import MockTool, MockSCM, MockStatusServer
@@ -51,11 +52,6 @@ class TestFeederQueue(FeederQueue):
_sleep_duration = 0
-class MockRolloutPatch(MockPatch):
- def is_rollout(self):
- return True
-
-
class AbstractQueueTest(CommandsTest):
def test_log_directory(self):
self.assertEquals(TestQueue()._log_directory(), "test-queue-logs")
@@ -144,15 +140,19 @@ MOCK: submit_to_ews: 103
class AbstractPatchQueueTest(CommandsTest):
- def test_fetch_next_work_item(self):
+ def test_next_patch(self):
queue = AbstractPatchQueue()
tool = MockTool()
queue.bind_to_tool(tool)
queue._options = Mock()
queue._options.port = None
- 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)
+ self.assertEquals(queue._next_patch(), None)
+ tool.status_server = MockStatusServer(work_items=[2, 197])
+ expected_stdout = "MOCK: fetch_attachment: 2 is not a known attachment id\n" # A mock-only message to prevent us from making mistakes.
+ expected_stderr = "MOCK: release_work_item: None 2\n"
+ patch_id = OutputCapture().assert_outputs(self, queue._next_patch, [], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
+ self.assertEquals(patch_id, None) # 2 is an invalid patch id
+ self.assertEquals(queue._next_patch().id(), 197)
class NeedsUpdateSequence(StepSequence):
@@ -198,6 +198,19 @@ class SecondThoughtsCommitQueue(CommitQueue):
return Attachment(attachment_dictionary, None)
+# Creating fake CommitInfos is a pain, so we use a mock one here.
+class MockCommitInfo(object):
+ def __init__(self, author_email):
+ self._author_email = author_email
+
+ def author(self):
+ # It's definitely possible to have commits with authors who
+ # are not in our committers.py list.
+ if not self._author_email:
+ return None
+ return Committer("Mock Committer", self._author_email)
+
+
class CommitQueueTest(QueuesTest):
def test_commit_queue(self):
expected_stderr = {
@@ -209,6 +222,7 @@ MOCK: update_status: commit-queue Built patch
MOCK: update_status: commit-queue Passed tests
MOCK: update_status: commit-queue Landed patch
MOCK: update_status: commit-queue Pass
+MOCK: release_work_item: commit-queue 197
""",
"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": "ScriptError error message\n",
@@ -243,15 +257,16 @@ MOCK: release_work_item: commit-queue 197
"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 Processing patch\n",
"next_work_item": "",
- "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', 197]
MOCK: update_status: commit-queue Applied patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both']
MOCK: update_status: commit-queue Built patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
MOCK: update_status: commit-queue Passed tests
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--quiet', '--non-interactive', '--parent-command=commit-queue', 197]
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
MOCK: update_status: commit-queue Landed patch
MOCK: update_status: commit-queue Pass
+MOCK: release_work_item: commit-queue 197
""",
"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": "ScriptError error message\n",
@@ -261,22 +276,22 @@ MOCK: update_status: commit-queue Pass
def test_rollout_lands(self):
tool = MockTool(log_executive=True)
tool.buildbot.light_tree_on_fire()
- rollout_patch = MockRolloutPatch()
+ rollout_patch = tool.bugs.fetch_attachment(106) # _patch6, a rollout patch.
+ assert(rollout_patch.is_rollout())
expected_stderr = {
"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 Processing rollout patch\n",
"next_work_item": "",
- "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', '--quiet', 197]
+ "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--force-clean', '--non-interactive', 106]
MOCK: update_status: commit-queue Applied patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both', '--quiet']
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both']
MOCK: update_status: commit-queue Built patch
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test', '--no-clean', '--no-update', '--test', '--quiet', '--non-interactive']
-MOCK: update_status: commit-queue Passed tests
-MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--quiet', '--non-interactive', '--parent-command=commit-queue', 197]
+MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 106]
MOCK: update_status: commit-queue Landed patch
MOCK: update_status: commit-queue Pass
+MOCK: release_work_item: commit-queue 106
""",
- "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_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '106' with comment 'Rejecting patch 106 from commit-queue.' and additional comment 'Mock error message'\n",
"handle_script_error": "ScriptError error message\n",
}
self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr)
@@ -307,23 +322,36 @@ MOCK: update_status: commit-queue Passed tests
MOCK: update_status: commit-queue Retry
MOCK: release_work_item: commit-queue 197
"""
- OutputCapture().assert_outputs(self, queue.process_work_item, [MockPatch()], expected_stderr=expected_stderr)
+ OutputCapture().assert_outputs(self, queue.process_work_item, [QueuesTest.mock_work_item], expected_stderr=expected_stderr)
+
+ def _assert_emails_for_tests(self, emails):
+ queue = CommitQueue()
+ tool = MockTool()
+ queue.bind_to_tool(tool)
+ commit_infos = [MockCommitInfo(email) for email in emails]
+ tool.checkout().recent_commit_infos_for_files = lambda paths: set(commit_infos)
+ self.assertEqual(queue._author_emails_for_tests([]), set(emails))
+
+ def test_author_emails_for_tests(self):
+ self._assert_emails_for_tests([])
+ self._assert_emails_for_tests(["test1@test.com", "test1@test.com"])
+ self._assert_emails_for_tests(["test1@test.com", "test2@test.com"])
def test_report_flaky_tests(self):
queue = CommitQueue()
queue.bind_to_tool(MockTool())
- expected_stderr = """MOCK bug comment: bug_id=142, cc=['abarth@webkit.org']
+ expected_stderr = """MOCK bug comment: bug_id=42, cc=None
--- Begin comment ---
The commit-queue encountered the following flaky tests while processing attachment 197:
foo/bar.html
bar/baz.html
-Please file bugs against the tests. The author(s) of the test(s) have been CCed on this bug. The commit-queue is continuing to process your patch.
+Please file bugs against the tests. These tests were authored by abarth@webkit.org. The commit-queue is continuing to process your patch.
--- End comment ---
"""
- OutputCapture().assert_outputs(self, queue.report_flaky_tests, [MockPatch(), ["foo/bar.html", "bar/baz.html"]], expected_stderr=expected_stderr)
+ OutputCapture().assert_outputs(self, queue.report_flaky_tests, [QueuesTest.mock_work_item, ["foo/bar.html", "bar/baz.html"]], expected_stderr=expected_stderr)
def test_layout_test_results(self):
queue = CommitQueue()
@@ -333,17 +361,6 @@ Please file bugs against the tests. The author(s) of the test(s) have been CCed
queue._read_file_contents = lambda path: ""
self.assertEquals(queue.layout_test_results(), None)
-class RietveldUploadQueueTest(QueuesTest):
- def test_rietveld_upload_queue(self):
- expected_stderr = {
- "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 '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)
-
class StyleQueueTest(QueuesTest):
def test_style_queue(self):
@@ -353,7 +370,7 @@ class StyleQueueTest(QueuesTest):
"should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n",
"process_work_item": "MOCK: update_status: style-queue Pass\nMOCK: release_work_item: style-queue 197\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 ---\nAttachment 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",
+ "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=42, cc=[]\n--- Begin comment ---\nAttachment 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 379d380..6455617 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/queuestest.py
@@ -44,25 +44,9 @@ class MockQueueEngine(object):
pass
-class MockPatch():
- def id(self):
- return 197
-
- def bug_id(self):
- return 142
-
- def is_rollout(self):
- return False
-
-
class QueuesTest(unittest.TestCase):
- # Ids match patch1 in mocktool.py
- mock_work_item = Attachment({
- "id": 197,
- "bug_id": 142,
- "name": "Patch",
- "attacher_email": "adam@example.com",
- }, None)
+ # This is _patch1 in mocktool.py
+ mock_work_item = MockTool().bugs.fetch_attachment(197)
def assert_outputs(self, func, func_name, args, expected_stdout, expected_stderr, expected_exceptions):
exception = None
@@ -108,4 +92,4 @@ class QueuesTest(unittest.TestCase):
self.assert_outputs(queue.handle_unexpected_error, "handle_unexpected_error", [work_item, "Mock error message"], expected_stdout, expected_stderr, expected_exceptions)
# Should we have a different function for testing StepSequenceErrorHandlers?
if isinstance(queue, StepSequenceErrorHandler):
- self.assert_outputs(queue.handle_script_error, "handle_script_error", [tool, {"patch": MockPatch()}, ScriptError(message="ScriptError error message", script_args="MockErrorCommand")], expected_stdout, expected_stderr, expected_exceptions)
+ self.assert_outputs(queue.handle_script_error, "handle_script_error", [tool, {"patch": self.mock_work_item}, ScriptError(message="ScriptError error message", script_args="MockErrorCommand")], expected_stdout, expected_stderr, expected_exceptions)
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
index 32eb016..4db463e 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/sheriffbot_unittest.py
@@ -38,9 +38,10 @@ class SheriffBotTest(QueuesTest):
builder2 = MockBuilder("Builder2")
def test_sheriff_bot(self):
- mock_work_item = MockFailureMap(MockTool().buildbot)
+ tool = MockTool()
+ mock_work_item = MockFailureMap(tool.buildbot)
expected_stderr = {
- "begin_work_queue": self._default_begin_work_queue_stderr("sheriff-bot", os.getcwd()),
+ "begin_work_queue": self._default_begin_work_queue_stderr("sheriff-bot", tool.scm().checkout_root),
"next_work_item": "",
"process_work_item": """MOCK: irc.post: abarth, darin, eseidel: http://trac.webkit.org/changeset/29837 might have broken Builder1
MOCK bug comment: bug_id=42, cc=['abarth@webkit.org', 'eric@webkit.org']
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
index 107d8db..ed91f5a 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/upload.py
@@ -152,7 +152,9 @@ class AbstractPatchUploadingCommand(AbstractSequencedCommand):
# Perfer a bug id passed as an argument over a bug url in the diff (i.e. ChangeLogs).
bug_id = args and args[0]
if not bug_id:
- bug_id = tool.checkout().bug_id_for_this_commit(options.git_commit)
+ changed_files = self._tool.scm().changed_files(options.git_commit)
+ state["changed_files"] = changed_files
+ bug_id = tool.checkout().bug_id_for_this_commit(options.git_commit, changed_files)
return bug_id
def _prepare_state(self, options, args, tool):
@@ -171,6 +173,7 @@ class Post(AbstractPatchUploadingCommand):
steps.CheckStyle,
steps.ConfirmDiff,
steps.ObsoletePatches,
+ steps.SuggestReviewers,
steps.PostDiff,
]
@@ -219,6 +222,7 @@ class Upload(AbstractPatchUploadingCommand):
steps.EditChangeLog,
steps.ConfirmDiff,
steps.ObsoletePatches,
+ steps.SuggestReviewers,
steps.PostDiff,
]
long_help = """upload uploads the current diff to bugs.webkit.org.
diff --git a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py
index 0d096b6..bd1fbd6 100644
--- a/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/commands/upload_unittest.py
@@ -58,6 +58,7 @@ class UploadCommandsTest(CommandsTest):
options.description = "MOCK description"
options.request_commit = False
options.review = True
+ options.suggest_reviewers = False
expected_stderr = """Running check-webkit-style
MOCK: user.open_url: file://...
Obsoleting 2 old patches on bug 42
@@ -67,7 +68,8 @@ None
-- End comment --
MOCK: user.open_url: http://example.com/42
"""
- self.assert_execute_outputs(Post(), [42], options=options, expected_stderr=expected_stderr)
+ expected_stdout = "Was that diff correct?\n"
+ self.assert_execute_outputs(Post(), [42], options=options, expected_stdout=expected_stdout, expected_stderr=expected_stderr)
def test_land_safely(self):
expected_stderr = "Obsoleting 2 old patches on bug 42\nMOCK add_patch_to_bug: bug_id=42, description=Patch for landing, mark_for_review=False, mark_for_commit_queue=False, mark_for_landing=True\n-- Begin comment --\nNone\n-- End comment --\n"
@@ -88,6 +90,7 @@ MOCK: user.open_url: http://example.com/42
options.description = "MOCK description"
options.request_commit = False
options.review = True
+ options.suggest_reviewers = False
expected_stderr = """Running check-webkit-style
MOCK: user.open_url: file://...
Obsoleting 2 old patches on bug 42
@@ -97,7 +100,8 @@ None
-- End comment --
MOCK: user.open_url: http://example.com/42
"""
- self.assert_execute_outputs(Upload(), [42], options=options, expected_stderr=expected_stderr)
+ expected_stdout = "Was that diff correct?\n"
+ self.assert_execute_outputs(Upload(), [42], options=options, expected_stdout=expected_stdout, expected_stderr=expected_stderr)
def test_mark_bug_fixed(self):
tool = MockTool()
@@ -106,7 +110,8 @@ MOCK: user.open_url: http://example.com/42
options.bug_id = 42
options.comment = "MOCK comment"
expected_stderr = "Bug: <http://example.com/42> Bug with two r+'d and cq+'d patches, one of which has an invalid commit-queue setter.\nRevision: 9876\nMOCK: user.open_url: http://example.com/42\nAdding comment to Bug 42.\nMOCK bug comment: bug_id=42, cc=None\n--- Begin comment ---\nMOCK comment\n\nCommitted r9876: <http://trac.webkit.org/changeset/9876>\n--- End comment ---\n\n"
- self.assert_execute_outputs(MarkBugFixed(), [], expected_stderr=expected_stderr, tool=tool, options=options)
+ expected_stdout = "Is this correct?\n"
+ self.assert_execute_outputs(MarkBugFixed(), [], expected_stdout=expected_stdout, expected_stderr=expected_stderr, tool=tool, options=options)
def test_edit_changelog(self):
self.assert_execute_outputs(EditChangeLogs(), [])
diff --git a/WebKitTools/Scripts/webkitpy/tool/main.py b/WebKitTools/Scripts/webkitpy/tool/main.py
index ce6666e..e0862c5 100755
--- a/WebKitTools/Scripts/webkitpy/tool/main.py
+++ b/WebKitTools/Scripts/webkitpy/tool/main.py
@@ -37,7 +37,6 @@ from webkitpy.common.checkout.scm import default_scm
from webkitpy.common.config.ports import WebKitPort
from webkitpy.common.net.bugzilla import Bugzilla
from webkitpy.common.net.buildbot import BuildBot
-from webkitpy.common.net.rietveld import Rietveld
from webkitpy.common.net.irc.ircproxy import IRCProxy
from webkitpy.common.system.executive import Executive
from webkitpy.common.system.user import User
@@ -79,7 +78,6 @@ class WebKitPatch(MultiCommandTool):
self._scm = None
self._checkout = None
self.status_server = StatusServer()
- self.codereview = Rietveld(self.executive)
self.port_factory = port.factory
def scm(self):
@@ -126,7 +124,6 @@ class WebKitPatch(MultiCommandTool):
if options.dry_run:
self.scm().dryrun = True
self.bugs.dryrun = True
- self.codereview.dryrun = True
if options.status_host:
self.status_server.set_host(options.status_host)
if options.bot_id:
diff --git a/WebKitTools/Scripts/webkitpy/tool/mocktool.py b/WebKitTools/Scripts/webkitpy/tool/mocktool.py
index 05b30dd..af232d9 100644
--- a/WebKitTools/Scripts/webkitpy/tool/mocktool.py
+++ b/WebKitTools/Scripts/webkitpy/tool/mocktool.py
@@ -33,7 +33,6 @@ from webkitpy.common.config.committers import CommitterList, Reviewer
from webkitpy.common.checkout.commitinfo import CommitInfo
from webkitpy.common.checkout.scm import CommitMessage
from webkitpy.common.net.bugzilla import Bug, Attachment
-from webkitpy.common.net.rietveld import Rietveld
from webkitpy.thirdparty.mock import Mock
from webkitpy.common.system.deprecated_logging import log
@@ -86,7 +85,6 @@ _patch3 = {
"name": "Patch3",
"is_obsolete": False,
"is_patch": True,
- "in-rietveld": "?",
"review": "?",
"attacher_email": "eric@webkit.org",
}
@@ -113,7 +111,6 @@ _patch5 = {
"name": "Patch5",
"is_obsolete": False,
"is_patch": True,
- "in-rietveld": "?",
"review": "+",
"reviewer_email": "foo@bar.com",
"attacher_email": "eric@webkit.org",
@@ -127,7 +124,6 @@ _patch6 = { # Valid committer, but no reviewer.
"name": "ROLLOUT of r3489",
"is_obsolete": False,
"is_patch": True,
- "in-rietveld": "-",
"commit-queue": "+",
"committer_email": "foo@bar.com",
"attacher_email": "eric@webkit.org",
@@ -141,7 +137,6 @@ _patch7 = { # Valid review, patch is marked obsolete.
"name": "Patch7",
"is_obsolete": True,
"is_patch": True,
- "in-rietveld": "+",
"review": "+",
"reviewer_email": "foo@bar.com",
"attacher_email": "eric@webkit.org",
@@ -192,6 +187,7 @@ _bug4 = {
}
+# FIXME: This should not inherit from Mock
class MockBugzillaQueries(Mock):
def __init__(self, bugzilla):
@@ -229,18 +225,14 @@ class MockBugzillaQueries(Mock):
def fetch_patches_from_pending_commit_list(self):
return sum([bug.reviewed_patches() for bug in self._all_bugs()], [])
- def fetch_first_patch_from_rietveld_queue(self):
- for bug in self._all_bugs():
- patches = bug.in_rietveld_queue_patches()
- if len(patches):
- return patches[0]
- raise Exception('No patches in the rietveld queue')
+
+_mock_reviewer = Reviewer("Foo Bar", "foo@bar.com")
+
# FIXME: Bugzilla is the wrong Mock-point. Once we have a BugzillaNetwork
# class we should mock that instead.
# Most of this class is just copy/paste from Bugzilla.
-
-
+# FIXME: This should not inherit from Mock
class MockBugzilla(Mock):
bug_server_url = "http://example.com"
@@ -258,7 +250,7 @@ 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=[_mock_reviewer])
self._override_patch = None
def create_bug(self,
@@ -288,8 +280,10 @@ class MockBugzilla(Mock):
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]
+ attachment_dictionary = self.attachment_cache.get(attachment_id)
+ if not attachment_dictionary:
+ print "MOCK: fetch_attachment: %s is not a known attachment id" % attachment_id
+ return None
bug = self.fetch_bug(attachment_dictionary["bug_id"])
for attachment in bug.attachments(include_obsolete=True):
if attachment.id() == int(attachment_id):
@@ -418,6 +412,7 @@ class MockBuildBot(object):
return MockFailureMap(self)
+# FIXME: This should not inherit from Mock
class MockSCM(Mock):
fake_checkout_root = os.path.realpath("/tmp") # realpath is needed to allow for Mac OS X's /private/tmp
@@ -480,7 +475,7 @@ class MockCheckout(object):
# that LandDiff will try to actually read the patch from disk!
return []
- def commit_message_for_this_commit(self, git_commit):
+ def commit_message_for_this_commit(self, git_commit, changed_files=None):
commit_message = Mock()
commit_message.message = lambda:"This is a fake commit message that is at least 50 characters."
return commit_message
@@ -491,6 +486,9 @@ class MockCheckout(object):
def apply_reverse_diff(self, revision):
pass
+ def suggested_reviewers(self, git_commit, changed_files=None):
+ return [_mock_reviewer]
+
class MockUser(object):
@@ -508,6 +506,7 @@ class MockUser(object):
pass
def confirm(self, message=None, default='y'):
+ print message
return default == 'y'
def can_open_url(self):
@@ -545,7 +544,7 @@ class MockStatusServer(object):
def next_work_item(self, queue_name):
if not self._work_items:
return None
- return self._work_items[0]
+ return self._work_items.pop(0)
def release_work_item(self, queue_name, patch):
log("MOCK: release_work_item: %s %s" % (queue_name, patch.id()))
@@ -568,7 +567,8 @@ class MockStatusServer(object):
return "http://dummy_url"
-class MockExecute(Mock):
+# FIXME: This should not inherit from Mock
+class MockExecutive(Mock):
def __init__(self, should_log):
self._should_log = should_log
@@ -603,47 +603,37 @@ class MockOptions(object):
self.__dict__[key] = value
-class MockRietveld():
-
- def __init__(self, executive, dryrun=False):
- pass
-
- def post(self, diff, patch_id, codereview_issue, message=None, cc=None):
- log("MOCK: Uploading patch to rietveld")
-
-
-class MockTestPort1():
+class MockTestPort1(object):
def skips_layout_test(self, test_name):
return test_name in ["media/foo/bar.html", "foo"]
-class MockTestPort2():
+class MockTestPort2(object):
def skips_layout_test(self, test_name):
return test_name == "media/foo/bar.html"
-class MockPortFactory():
+class MockPortFactory(object):
def get_all(self, options=None):
return {"test_port1": MockTestPort1(), "test_port2": MockTestPort2()}
-class MockTool():
+class MockTool(object):
def __init__(self, log_executive=False):
self.wakeup_event = threading.Event()
self.bugs = MockBugzilla()
self.buildbot = MockBuildBot()
- self.executive = MockExecute(should_log=log_executive)
+ self.executive = MockExecutive(should_log=log_executive)
self._irc = None
self.user = MockUser()
self._scm = MockSCM()
self._checkout = MockCheckout()
self.status_server = MockStatusServer()
self.irc_password = "MOCK irc password"
- self.codereview = MockRietveld(self.executive)
self.port_factory = MockPortFactory()
def scm(self):
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py b/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py
index d59cdc5..64d9d05 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/__init__.py
@@ -44,7 +44,6 @@ from webkitpy.tool.steps.ensurebuildersaregreen import EnsureBuildersAreGreen
from webkitpy.tool.steps.ensurelocalcommitifneeded import EnsureLocalCommitIfNeeded
from webkitpy.tool.steps.obsoletepatches import ObsoletePatches
from webkitpy.tool.steps.options import Options
-from webkitpy.tool.steps.postcodereview import PostCodeReview
from webkitpy.tool.steps.postdiff import PostDiff
from webkitpy.tool.steps.postdiffforcommit import PostDiffForCommit
from webkitpy.tool.steps.postdiffforrevert import PostDiffForRevert
@@ -54,6 +53,7 @@ from webkitpy.tool.steps.promptforbugortitle import PromptForBugOrTitle
from webkitpy.tool.steps.reopenbugafterrollout import ReopenBugAfterRollout
from webkitpy.tool.steps.revertrevision import RevertRevision
from webkitpy.tool.steps.runtests import RunTests
+from webkitpy.tool.steps.suggestreviewers import SuggestReviewers
from webkitpy.tool.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer
from webkitpy.tool.steps.update import Update
from webkitpy.tool.steps.validatereviewer import ValidateReviewer
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/options.py b/WebKitTools/Scripts/webkitpy/tool/steps/options.py
index 835fdba..4f17dd3 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/options.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/options.py
@@ -54,5 +54,6 @@ class Options(object):
request_commit = make_option("--request-commit", action="store_true", dest="request_commit", default=False, help="Mark the patch as needing auto-commit after review.")
review = make_option("--no-review", action="store_false", dest="review", default=True, help="Do not mark the patch for review.")
reviewer = make_option("-r", "--reviewer", action="store", type="string", dest="reviewer", help="Update ChangeLogs to say Reviewed by REVIEWER.")
+ suggest_reviewers = make_option("--suggest-reviewers", action="store_true", default=False, help="Offer to CC appropriate reviewers.")
test = make_option("--test", action="store_true", dest="test", default=False, help="Run run-webkit-tests before committing.")
update = make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory.")
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py b/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py
index 81b6bcb..bbb794c 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/revertrevision.py
@@ -32,3 +32,4 @@ from webkitpy.tool.steps.abstractstep import AbstractStep
class RevertRevision(AbstractStep):
def run(self, state):
self._tool.checkout().apply_reverse_diff(state["revision"])
+ self.did_modify_checkout(state)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py b/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py
index dcbfc44..282e381 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/runtests.py
@@ -57,6 +57,7 @@ class RunTests(AbstractStep):
log("Running run-webkit-tests")
args = self._tool.port().run_webkit_tests_command()
if self._options.non_interactive:
+ args.append("--no-new-test-results")
args.append("--no-launch-safari")
args.append("--exit-after-n-failures=1")
args.append("--wait-for-httpd")
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py
index 7eb8e3a..eabb656 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/steps_unittest.py
@@ -87,6 +87,6 @@ MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/test-webkitperl']
Running JavaScriptCore tests
MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/run-javascriptcore-tests']
Running run-webkit-tests
-MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/run-webkit-tests', '--no-launch-safari', '--exit-after-n-failures=1', '--wait-for-httpd', '--ignore-tests', 'compositing,media', '--quiet']
+MOCK run_and_throw_if_fail: ['WebKitTools/Scripts/run-webkit-tests', '--no-new-test-results', '--no-launch-safari', '--exit-after-n-failures=1', '--wait-for-httpd', '--ignore-tests', 'compositing,media', '--quiet']
"""
OutputCapture().assert_outputs(self, step.run, [{}], expected_stderr=expected_stderr)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py b/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers.py
index d2f79f3..76bef35 100644
--- a/WebKitTools/Scripts/webkitpy/tool/steps/postcodereview.py
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers.py
@@ -30,41 +30,22 @@ from webkitpy.tool.steps.abstractstep import AbstractStep
from webkitpy.tool.steps.options import Options
-class PostCodeReview(AbstractStep):
+class SuggestReviewers(AbstractStep):
@classmethod
def options(cls):
return AbstractStep.options() + [
- Options.cc,
- Options.description,
+ Options.git_commit,
+ Options.suggest_reviewers,
]
def run(self, state):
- patch = state.get("patch")
- bug_id = patch.bug_id()
- title = patch.name()
+ if not self._options.suggest_reviewers:
+ return
- # If the issue already exists, then the message becomes the label
- # of the new patch. Otherwise, it becomes the title of the whole
- # issue.
- if title:
- # This is the common case for the the first "upload" command.
- message = title
- elif bug_id:
- # This is the common case for the "post" command and
- # subsequent runs of the "upload" command.
- message = "Code review for %s" % self._tool.bugs.bug_url_for_bug_id(bug_id)
- else:
- # Unreachable with our current commands, but we might hit
- # this case if we support bug-less code reviews.
- message = "Code review"
-
- # Use the bug ID as the rietveld issue number. This means rietveld code reviews
- # when there are multiple different patches on a bug will be a bit wonky, but
- # webkit-patch assumes one-patch-per-bug.
- created_issue = self._tool.codereview.post(diff=self.cached_lookup(state, "diff"),
- message=message,
- codereview_issue=bug_id,
- cc=self._options.cc,
- patch_id=patch.id())
-
- self._tool.bugs.set_flag_on_attachment(patch.id(), 'in-rietveld', '+')
+ reviewers = self._tool.checkout().suggested_reviewers(self._options.git_commit, self._changed_files(state))
+ print "The following reviewers have recently modified files in your patch:"
+ print "\n".join([reviewer.full_name for reviewer in reviewers])
+ if not self._tool.user.confirm("Would you like to CC them?"):
+ return
+ reviewer_emails = [reviewer.bugzilla_email() for reviewer in reviewers]
+ self._tool.bugs.add_cc_to_bug(state['bug_id'], reviewer_emails)
diff --git a/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py b/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py
new file mode 100644
index 0000000..0c86535
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2009 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+
+from webkitpy.common.system.outputcapture import OutputCapture
+from webkitpy.tool.mocktool import MockOptions, MockTool
+from webkitpy.tool.steps.suggestreviewers import SuggestReviewers
+
+
+class SuggestReviewersTest(unittest.TestCase):
+ def test_disabled(self):
+ step = SuggestReviewers(MockTool(), MockOptions(suggest_reviewers=False))
+ OutputCapture().assert_outputs(self, step.run, [{}])
+
+ def test_basic(self):
+ capture = OutputCapture()
+ step = SuggestReviewers(MockTool(), MockOptions(suggest_reviewers=True, git_commit=None))
+ expected_stdout = "The following reviewers have recently modified files in your patch:\nFoo Bar\nWould you like to CC them?\n"
+ capture.assert_outputs(self, step.run, [{"bug_id": "123"}], expected_stdout=expected_stdout)