diff options
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/tool/bot')
13 files changed, 0 insertions, 1441 deletions
diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py b/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py deleted file mode 100644 index ef65bee..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Required for Python to search this directory for module files diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py deleted file mode 100644 index ea12702..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask.py +++ /dev/null @@ -1,196 +0,0 @@ -# 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. - -from webkitpy.common.system.executive import ScriptError -from webkitpy.common.net.layouttestresults import LayoutTestResults - - -class CommitQueueTaskDelegate(object): - def run_command(self, command): - raise NotImplementedError("subclasses must implement") - - def command_passed(self, message, patch): - raise NotImplementedError("subclasses must implement") - - def command_failed(self, message, script_error, patch): - raise NotImplementedError("subclasses must implement") - - def refetch_patch(self, patch): - raise NotImplementedError("subclasses must implement") - - def layout_test_results(self): - raise NotImplementedError("subclasses must implement") - - def report_flaky_tests(self, patch, flaky_tests): - raise NotImplementedError("subclasses must implement") - - -class CommitQueueTask(object): - def __init__(self, delegate, patch): - self._delegate = delegate - self._patch = patch - self._script_error = None - - def _validate(self): - # Bugs might get closed, or patches might be obsoleted or r-'d while the - # commit-queue is processing. - self._patch = self._delegate.refetch_patch(self._patch) - if self._patch.is_obsolete(): - return False - if self._patch.bug().is_closed(): - return False - if not self._patch.committer(): - return False - # Reviewer is not required. Missing reviewers will be caught during - # the ChangeLog check during landing. - return True - - def _run_command(self, command, success_message, failure_message): - try: - self._delegate.run_command(command) - self._delegate.command_passed(success_message, patch=self._patch) - return True - except ScriptError, e: - self._script_error = e - self.failure_status_id = self._delegate.command_failed(failure_message, script_error=self._script_error, patch=self._patch) - return False - - def _apply(self): - return self._run_command([ - "apply-attachment", - "--force-clean", - "--non-interactive", - self._patch.id(), - ], - "Applied patch", - "Patch does not apply") - - def _build(self): - return self._run_command([ - "build", - "--no-clean", - "--no-update", - "--build-style=both", - ], - "Built patch", - "Patch does not build") - - def _build_without_patch(self): - return self._run_command([ - "build", - "--force-clean", - "--no-update", - "--build-style=both", - ], - "Able to build without patch", - "Unable to build without patch") - - def _test(self): - return self._run_command([ - "build-and-test", - "--no-clean", - "--no-update", - # Notice that we don't pass --build, which means we won't build! - "--test", - "--non-interactive", - ], - "Passed tests", - "Patch does not pass tests") - - def _build_and_test_without_patch(self): - return self._run_command([ - "build-and-test", - "--force-clean", - "--no-update", - "--build", - "--test", - "--non-interactive", - ], - "Able to pass tests without patch", - "Unable to pass tests without patch (tree is red?)") - - def _failing_tests_from_last_run(self): - results = self._delegate.layout_test_results() - if not results: - return None - 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", - "--non-interactive", - "--parent-command=commit-queue", - self._patch.id(), - ], - "Landed patch", - "Unable to land patch") - - def _report_flaky_tests(self, flaky_tests): - self._delegate.report_flaky_tests(self._patch, flaky_tests) - - def _test_patch(self): - if self._patch.is_rollout(): - return True - if self._test(): - return True - - first_failing_tests = self._failing_tests_from_last_run() - if self._test(): - self._report_flaky_tests(first_failing_tests) - return True - - second_failing_tests = self._failing_tests_from_last_run() - if first_failing_tests != second_failing_tests: - self._report_flaky_tests(first_failing_tests + second_failing_tests) - return False - - if self._build_and_test_without_patch(): - raise self._script_error # The error from the previous ._test() run is real, report it. - return False # Tree must be red, just retry later. - - def run(self): - if not self._validate(): - return False - if not self._apply(): - raise self._script_error - if not self._build(): - if not self._build_without_patch(): - return False - raise self._script_error - if not self._test_patch(): - return False - # Make sure the patch is still valid before landing (e.g., make sure - # no one has set commit-queue- since we started working on the patch.) - if not self._validate(): - return False - if not self._land(): - raise self._script_error - return True diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py deleted file mode 100644 index 15a4a6b..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py +++ /dev/null @@ -1,204 +0,0 @@ -# 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. - -from datetime import datetime -import unittest - -from webkitpy.common.system.deprecated_logging import error, log -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.bot.commitqueuetask import * -from webkitpy.tool.mocktool import MockTool - - -class MockCommitQueue(CommitQueueTaskDelegate): - def __init__(self, error_plan): - self._error_plan = error_plan - - def run_command(self, command): - log("run_webkit_patch: %s" % command) - if self._error_plan: - error = self._error_plan.pop(0) - if error: - raise error - - def command_passed(self, success_message, patch): - log("command_passed: success_message='%s' patch='%s'" % ( - success_message, patch.id())) - - def command_failed(self, failure_message, script_error, patch): - log("command_failed: failure_message='%s' script_error='%s' patch='%s'" % ( - failure_message, script_error, patch.id())) - return 3947 - - def refetch_patch(self, patch): - return patch - - def layout_test_results(self): - return None - - def report_flaky_tests(self, patch, flaky_tests): - log("report_flaky_tests: patch='%s' flaky_tests='%s'" % (patch.id(), flaky_tests)) - - -class CommitQueueTaskTest(unittest.TestCase): - def _run_through_task(self, commit_queue, expected_stderr, expected_exception=None): - tool = MockTool(log_executive=True) - patch = tool.bugs.fetch_attachment(197) - task = CommitQueueTask(commit_queue, patch) - OutputCapture().assert_outputs(self, task.run, expected_stderr=expected_stderr, expected_exception=expected_exception) - - def test_success_case(self): - commit_queue = MockCommitQueue([]) - 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'] -command_passed: success_message='Built patch' patch='197' -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', '--non-interactive', '--parent-command=commit-queue', 197] -command_passed: success_message='Landed patch' patch='197' -""" - self._run_through_task(commit_queue, expected_stderr) - - def test_apply_failure(self): - commit_queue = MockCommitQueue([ - ScriptError("MOCK apply failure"), - ]) - 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) - - def test_build_failure(self): - commit_queue = MockCommitQueue([ - None, - ScriptError("MOCK build failure"), - ]) - 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'] -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'] -command_passed: success_message='Able to build without patch' patch='197' -""" - self._run_through_task(commit_queue, expected_stderr, ScriptError) - - def test_red_build_failure(self): - commit_queue = MockCommitQueue([ - None, - ScriptError("MOCK build failure"), - ScriptError("MOCK clean build failure"), - ]) - 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'] -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'] -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) - - def test_flaky_test_failure(self): - commit_queue = MockCommitQueue([ - None, - None, - ScriptError("MOCK tests failure"), - ]) - 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'] -command_passed: success_message='Built patch' patch='197' -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', '--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', '--non-interactive', '--parent-command=commit-queue', 197] -command_passed: success_message='Landed patch' patch='197' -""" - self._run_through_task(commit_queue, expected_stderr) - - def test_test_failure(self): - commit_queue = MockCommitQueue([ - None, - None, - ScriptError("MOCK test failure"), - ScriptError("MOCK test failure again"), - ]) - 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'] -command_passed: success_message='Built patch' patch='197' -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', '--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', '--non-interactive'] -command_passed: success_message='Able to pass tests without patch' patch='197' -""" - self._run_through_task(commit_queue, expected_stderr, ScriptError) - - def test_red_test_failure(self): - commit_queue = MockCommitQueue([ - None, - None, - ScriptError("MOCK test failure"), - ScriptError("MOCK test failure again"), - ScriptError("MOCK clean test failure"), - ]) - 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'] -command_passed: success_message='Built patch' patch='197' -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', '--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', '--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) - - def test_land_failure(self): - commit_queue = MockCommitQueue([ - None, - None, - None, - ScriptError("MOCK land failure"), - ]) - 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'] -command_passed: success_message='Built patch' patch='197' -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', '--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/bot/feeders.py b/WebKitTools/Scripts/webkitpy/tool/bot/feeders.py deleted file mode 100644 index 046c4c1..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/feeders.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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. - -from webkitpy.common.config.committervalidator import CommitterValidator -from webkitpy.common.system.deprecated_logging import log -from webkitpy.tool.grammar import pluralize - - -class AbstractFeeder(object): - def __init__(self, tool): - self._tool = tool - - def feed(self): - raise NotImplementedError("subclasses must implement") - - -class CommitQueueFeeder(AbstractFeeder): - queue_name = "commit-queue" - - def __init__(self, tool): - AbstractFeeder.__init__(self, tool) - self.committer_validator = CommitterValidator(self._tool.bugs) - - def _update_work_items(self, item_ids): - # FIXME: This is the last use of update_work_items, the commit-queue - # should move to feeding patches one at a time like the EWS does. - self._tool.status_server.update_work_items(self.queue_name, item_ids) - log("Feeding %s items %s" % (self.queue_name, item_ids)) - - def feed(self): - patches = self._validate_patches() - patches = sorted(patches, self._patch_cmp) - patch_ids = [patch.id() for patch in patches] - self._update_work_items(patch_ids) - - def _patches_for_bug(self, bug_id): - return self._tool.bugs.fetch_bug(bug_id).commit_queued_patches(include_invalid=True) - - def _validate_patches(self): - # Not using BugzillaQueries.fetch_patches_from_commit_queue() so we can reject patches with invalid committers/reviewers. - bug_ids = self._tool.bugs.queries.fetch_bug_ids_from_commit_queue() - all_patches = sum([self._patches_for_bug(bug_id) for bug_id in bug_ids], []) - return self.committer_validator.patches_after_rejecting_invalid_commiters_and_reviewers(all_patches) - - def _patch_cmp(self, a, b): - # Sort first by is_rollout, then by attach_date. - # Reversing the order so that is_rollout is first. - rollout_cmp = cmp(b.is_rollout(), a.is_rollout()) - if rollout_cmp != 0: - return rollout_cmp - return cmp(a.attach_date(), b.attach_date()) - - -class EWSFeeder(AbstractFeeder): - def __init__(self, tool): - self._ids_sent_to_server = set() - AbstractFeeder.__init__(self, tool) - - def feed(self): - ids_needing_review = set(self._tool.bugs.queries.fetch_attachment_ids_from_review_queue()) - new_ids = ids_needing_review.difference(self._ids_sent_to_server) - log("Feeding EWS (%s, %s new)" % (pluralize("r? patch", len(ids_needing_review)), len(new_ids))) - for attachment_id in new_ids: # Order doesn't really matter for the EWS. - self._tool.status_server.submit_to_ews(attachment_id) - self._ids_sent_to_server.add(attachment_id) diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/feeders_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/feeders_unittest.py deleted file mode 100644 index 5ce00b4..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/feeders_unittest.py +++ /dev/null @@ -1,70 +0,0 @@ -# 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. - -from datetime import datetime -import unittest - -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.bot.feeders import * -from webkitpy.tool.mocktool import MockTool - - -class FeedersTest(unittest.TestCase): - def test_commit_queue_feeder(self): - feeder = CommitQueueFeeder(MockTool()) - expected_stderr = u"""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. - -- If you do not have committer rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags. - -- 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] -Feeding commit-queue items [106, 197] -""" - OutputCapture().assert_outputs(self, feeder.feed, expected_stderr=expected_stderr) - - def _mock_attachment(self, is_rollout, attach_date): - attachment = Mock() - attachment.is_rollout = lambda: is_rollout - attachment.attach_date = lambda: attach_date - return attachment - - def test_patch_cmp(self): - long_ago_date = datetime(1900, 1, 21) - recent_date = datetime(2010, 1, 21) - attachment1 = self._mock_attachment(is_rollout=False, attach_date=recent_date) - attachment2 = self._mock_attachment(is_rollout=False, attach_date=long_ago_date) - attachment3 = self._mock_attachment(is_rollout=True, attach_date=recent_date) - attachment4 = self._mock_attachment(is_rollout=True, attach_date=long_ago_date) - attachments = [attachment1, attachment2, attachment3, attachment4] - expected_sort = [attachment4, attachment3, attachment2, attachment1] - queue = CommitQueueFeeder(MockTool()) - attachments.sort(queue._patch_cmp) - self.assertEqual(attachments, expected_sort) diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py b/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py deleted file mode 100644 index a848472..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command.py +++ /dev/null @@ -1,109 +0,0 @@ -# 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. - -import random -import webkitpy.common.config.irc as config_irc - -from webkitpy.common.checkout.changelog import view_source_url -from webkitpy.tool.bot.queueengine import TerminateQueue -from webkitpy.common.net.bugzilla import parse_bug_id -from webkitpy.common.system.executive import ScriptError - -# FIXME: Merge with Command? -class IRCCommand(object): - def execute(self, nick, args, tool, sheriff): - raise NotImplementedError, "subclasses must implement" - - -class LastGreenRevision(IRCCommand): - def execute(self, nick, args, tool, sheriff): - return "%s: %s" % (nick, - view_source_url(tool.buildbot.last_green_revision())) - - -class Restart(IRCCommand): - def execute(self, nick, args, tool, sheriff): - tool.irc().post("Restarting...") - raise TerminateQueue() - - -class Rollout(IRCCommand): - def execute(self, nick, args, tool, sheriff): - if len(args) < 2: - tool.irc().post("%s: Usage: SVN_REVISION REASON" % nick) - return - svn_revision = args[0].lstrip("r") - rollout_reason = " ".join(args[1:]) - tool.irc().post("Preparing rollout for r%s..." % svn_revision) - try: - complete_reason = "%s (Requested by %s on %s)." % ( - rollout_reason, nick, config_irc.channel) - bug_id = sheriff.post_rollout_patch(svn_revision, complete_reason) - bug_url = tool.bugs.bug_url_for_bug_id(bug_id) - tool.irc().post("%s: Created rollout: %s" % (nick, bug_url)) - except ScriptError, e: - tool.irc().post("%s: Failed to create rollout patch:" % nick) - tool.irc().post("%s" % e) - bug_id = parse_bug_id(e.output) - if bug_id: - tool.irc().post("Ugg... Might have created %s" % - tool.bugs.bug_url_for_bug_id(bug_id)) - - -class Help(IRCCommand): - def execute(self, nick, args, tool, sheriff): - return "%s: Available commands: %s" % (nick, ", ".join(commands.keys())) - - -class Hi(IRCCommand): - def execute(self, nick, args, tool, sheriff): - quips = tool.bugs.quips() - quips.append('"Only you can prevent forest fires." -- Smokey the Bear') - return random.choice(quips) - - -class Eliza(IRCCommand): - therapist = None - - def __init__(self): - if not self.therapist: - import webkitpy.thirdparty.autoinstalled.eliza as eliza - Eliza.therapist = eliza.eliza() - - def execute(self, nick, args, tool, sheriff): - return "%s: %s" % (nick, self.therapist.respond(" ".join(args))) - - -# FIXME: Lame. We should have an auto-registering CommandCenter. -commands = { - "last-green-revision": LastGreenRevision, - "restart": Restart, - "rollout": Rollout, - "help": Help, - "hi": Hi, -} diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/irc_command_unittest.py deleted file mode 100644 index 7aeb6a0..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/irc_command_unittest.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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. - -import unittest - -from webkitpy.tool.bot.irc_command import * - - -class IRCCommandTest(unittest.TestCase): - def test_eliza(self): - eliza = Eliza() - eliza.execute("tom", "hi", None, None) - eliza.execute("tom", "bye", None, None) diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py deleted file mode 100644 index 8b016e8..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine.py +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright (c) 2009 Google Inc. All rights reserved. -# Copyright (c) 2009 Apple 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 os -import time -import traceback - -from datetime import datetime, timedelta - -from webkitpy.common.system.executive import ScriptError -from webkitpy.common.system.deprecated_logging import log, OutputTee - - -class TerminateQueue(Exception): - pass - - -class QueueEngineDelegate: - def queue_log_path(self): - raise NotImplementedError, "subclasses must implement" - - def work_item_log_path(self, work_item): - raise NotImplementedError, "subclasses must implement" - - def begin_work_queue(self): - raise NotImplementedError, "subclasses must implement" - - def should_continue_work_queue(self): - raise NotImplementedError, "subclasses must implement" - - def next_work_item(self): - raise NotImplementedError, "subclasses must implement" - - def should_proceed_with_work_item(self, work_item): - # returns (safe_to_proceed, waiting_message, patch) - raise NotImplementedError, "subclasses must implement" - - def process_work_item(self, work_item): - raise NotImplementedError, "subclasses must implement" - - def handle_unexpected_error(self, work_item, message): - raise NotImplementedError, "subclasses must implement" - - -class QueueEngine: - def __init__(self, name, delegate, wakeup_event): - self._name = name - self._delegate = delegate - self._wakeup_event = wakeup_event - self._output_tee = OutputTee() - - log_date_format = "%Y-%m-%d %H:%M:%S" - sleep_duration_text = "2 mins" # This could be generated from seconds_to_sleep - seconds_to_sleep = 120 - handled_error_code = 2 - - # Child processes exit with a special code to the parent queue process can detect the error was handled. - @classmethod - def exit_after_handled_error(cls, error): - log(error) - exit(cls.handled_error_code) - - def run(self): - self._begin_logging() - - self._delegate.begin_work_queue() - while (self._delegate.should_continue_work_queue()): - try: - self._ensure_work_log_closed() - work_item = self._delegate.next_work_item() - if not work_item: - self._sleep("No work item.") - continue - if not self._delegate.should_proceed_with_work_item(work_item): - self._sleep("Not proceeding with work item.") - continue - - # FIXME: Work logs should not depend on bug_id specificaly. - # This looks fixed, no? - self._open_work_log(work_item) - try: - if not self._delegate.process_work_item(work_item): - log("Unable to process work item.") - continue - except ScriptError, e: - # Use a special exit code to indicate that the error was already - # handled in the child process and we should just keep looping. - if e.exit_code == self.handled_error_code: - continue - 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: - self._stopping("TerminateQueue exception received.") - return 0 - except KeyboardInterrupt, e: - self._stopping("User terminated queue.") - return 1 - except Exception, e: - traceback.print_exc() - # Don't try tell the status bot, in case telling it causes an exception. - self._sleep("Exception while preparing queue") - self._stopping("Delegate terminated queue.") - return 0 - - 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 - - def _open_work_log(self, work_item): - work_item_log_path = self._delegate.work_item_log_path(work_item) - if not work_item_log_path: - return - self._work_log = self._output_tee.add_log(work_item_log_path) - - def _ensure_work_log_closed(self): - # If we still have a bug log open, close it. - if self._work_log: - self._output_tee.remove_log(self._work_log) - self._work_log = None - - def _now(self): - """Overriden by the unit tests to allow testing _sleep_message""" - return datetime.now() - - def _sleep_message(self, message): - wake_time = self._now() + timedelta(seconds=self.seconds_to_sleep) - return "%s Sleeping until %s (%s)." % (message, wake_time.strftime(self.log_date_format), self.sleep_duration_text) - - def _sleep(self, message): - log(self._sleep_message(message)) - self._wakeup_event.wait(self.seconds_to_sleep) - self._wakeup_event.clear() diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py deleted file mode 100644 index 37d8502..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py +++ /dev/null @@ -1,209 +0,0 @@ -# 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 datetime -import os -import shutil -import tempfile -import threading -import unittest - -from webkitpy.common.system.executive import ScriptError -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate, TerminateQueue - - -class LoggingDelegate(QueueEngineDelegate): - def __init__(self, test): - self._test = test - self._callbacks = [] - self._run_before = False - self.stop_message = None - - expected_callbacks = [ - 'queue_log_path', - 'begin_work_queue', - 'should_continue_work_queue', - 'next_work_item', - 'should_proceed_with_work_item', - 'work_item_log_path', - 'process_work_item', - 'should_continue_work_queue', - 'stop_work_queue', - ] - - def record(self, method_name): - self._callbacks.append(method_name) - - def queue_log_path(self): - self.record("queue_log_path") - return os.path.join(self._test.temp_dir, "queue_log_path") - - def work_item_log_path(self, work_item): - self.record("work_item_log_path") - return os.path.join(self._test.temp_dir, "work_log_path", "%s.log" % work_item) - - def begin_work_queue(self): - self.record("begin_work_queue") - - def should_continue_work_queue(self): - self.record("should_continue_work_queue") - if not self._run_before: - self._run_before = True - return True - return False - - def next_work_item(self): - self.record("next_work_item") - return "work_item" - - def should_proceed_with_work_item(self, work_item): - self.record("should_proceed_with_work_item") - self._test.assertEquals(work_item, "work_item") - fake_patch = { 'bug_id' : 42 } - return (True, "waiting_message", fake_patch) - - def process_work_item(self, work_item): - self.record("process_work_item") - self._test.assertEquals(work_item, "work_item") - return True - - def handle_unexpected_error(self, work_item, message): - self.record("handle_unexpected_error") - self._test.assertEquals(work_item, "work_item") - - def stop_work_queue(self, message): - self.record("stop_work_queue") - self.stop_message = message - - -class RaisingDelegate(LoggingDelegate): - def __init__(self, test, exception): - LoggingDelegate.__init__(self, test) - self._exception = exception - - def process_work_item(self, work_item): - self.record("process_work_item") - raise self._exception - - -class NotSafeToProceedDelegate(LoggingDelegate): - def should_proceed_with_work_item(self, work_item): - self.record("should_proceed_with_work_item") - self._test.assertEquals(work_item, "work_item") - return False - - -class FastQueueEngine(QueueEngine): - def __init__(self, delegate): - QueueEngine.__init__(self, "fast-queue", delegate, threading.Event()) - - # No sleep for the wicked. - seconds_to_sleep = 0 - - def _sleep(self, message): - pass - - -class QueueEngineTest(unittest.TestCase): - def test_trivial(self): - delegate = LoggingDelegate(self) - self._run_engine(delegate) - self.assertEquals(delegate.stop_message, "Delegate terminated queue.") - self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks) - self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "queue_log_path"))) - self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "work_log_path", "work_item.log"))) - - def test_unexpected_error(self): - delegate = RaisingDelegate(self, ScriptError(exit_code=3)) - self._run_engine(delegate) - expected_callbacks = LoggingDelegate.expected_callbacks[:] - work_item_index = expected_callbacks.index('process_work_item') - # The unexpected error should be handled right after process_work_item starts - # but before any other callback. Otherwise callbacks should be normal. - expected_callbacks.insert(work_item_index + 1, 'handle_unexpected_error') - self.assertEquals(delegate._callbacks, expected_callbacks) - - def test_handled_error(self): - delegate = RaisingDelegate(self, ScriptError(exit_code=QueueEngine.handled_error_code)) - self._run_engine(delegate) - self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks) - - def _run_engine(self, delegate, engine=None, termination_message=None): - if not engine: - engine = QueueEngine("test-queue", delegate, threading.Event()) - if not termination_message: - termination_message = "Delegate terminated queue." - expected_stderr = "\n%s\n" % termination_message - OutputCapture().assert_outputs(self, engine.run, [], expected_stderr=expected_stderr) - - def _test_terminating_queue(self, exception, termination_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) - self._run_engine(delegate, termination_message=termination_message) - - self.assertEquals(delegate._callbacks, expected_callbacks) - self.assertEquals(delegate.stop_message, termination_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) - self._run_engine(delegate, engine=FastQueueEngine(delegate)) - expected_callbacks = LoggingDelegate.expected_callbacks[:] - expected_callbacks.remove('work_item_log_path') - expected_callbacks.remove('process_work_item') - self.assertEquals(delegate._callbacks, expected_callbacks) - - def test_now(self): - """Make sure there are no typos in the QueueEngine.now() method.""" - engine = QueueEngine("test", None, None) - self.assertTrue(isinstance(engine._now(), datetime.datetime)) - - def test_sleep_message(self): - engine = QueueEngine("test", None, None) - engine._now = lambda: datetime.datetime(2010, 1, 1) - expected_sleep_message = "MESSAGE Sleeping until 2010-01-01 00:02:00 (2 mins)." - self.assertEqual(engine._sleep_message("MESSAGE"), expected_sleep_message) - - def setUp(self): - self.temp_dir = tempfile.mkdtemp(suffix="work_queue_test_logs") - - def tearDown(self): - shutil.rmtree(self.temp_dir) - - -if __name__ == '__main__': - unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py deleted file mode 100644 index da506bc..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff.py +++ /dev/null @@ -1,91 +0,0 @@ -# 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. - -from webkitpy.common.checkout.changelog import view_source_url -from webkitpy.common.net.bugzilla import parse_bug_id -from webkitpy.common.system.deprecated_logging import log -from webkitpy.common.system.executive import ScriptError -from webkitpy.tool.grammar import join_with_separators - - -class Sheriff(object): - def __init__(self, tool, sheriffbot): - self._tool = tool - self._sheriffbot = sheriffbot - - def post_irc_warning(self, commit_info, builders): - irc_nicknames = sorted([party.irc_nickname for - party in commit_info.responsible_parties() - if party.irc_nickname]) - irc_prefix = ": " if irc_nicknames else "" - irc_message = "%s%s%s might have broken %s" % ( - ", ".join(irc_nicknames), - irc_prefix, - view_source_url(commit_info.revision()), - join_with_separators([builder.name() for builder in builders])) - - self._tool.irc().post(irc_message) - - def post_rollout_patch(self, svn_revision, rollout_reason): - # Ensure that svn_revision is a number (and not an option to - # create-rollout). - try: - svn_revision = int(svn_revision) - except: - raise ScriptError(message="Invalid svn revision number \"%s\"." - % svn_revision) - - if rollout_reason.startswith("-"): - raise ScriptError(message="The rollout reason may not begin " - "with - (\"%s\")." % rollout_reason) - - output = self._sheriffbot.run_webkit_patch([ - "create-rollout", - "--force-clean", - # In principle, we should pass --non-interactive here, but it - # turns out that create-rollout doesn't need it yet. We can't - # pass it prophylactically because we reject unrecognized command - # line switches. - "--parent-command=sheriff-bot", - svn_revision, - rollout_reason, - ]) - return parse_bug_id(output) - - def post_blame_comment_on_bug(self, commit_info, builders, tests): - if not commit_info.bug_id(): - return - comment = "%s might have broken %s" % ( - view_source_url(commit_info.revision()), - join_with_separators([builder.name() for builder in builders])) - if tests: - comment += "\nThe following tests are not passing:\n" - comment += "\n".join(tests) - self._tool.bugs.post_comment_to_bug(commit_info.bug_id(), - comment, - cc=self._sheriffbot.watchers) diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py deleted file mode 100644 index 690af1f..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriff_unittest.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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. - -import os -import unittest - -from webkitpy.common.net.buildbot import Builder -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.thirdparty.mock import Mock -from webkitpy.tool.bot.sheriff import Sheriff -from webkitpy.tool.mocktool import MockTool - - -class MockSheriffBot(object): - name = "mock-sheriff-bot" - watchers = [ - "watcher@example.com", - ] - - def run_webkit_patch(self, args): - return "Created bug https://bugs.webkit.org/show_bug.cgi?id=36936\n" - - -class SheriffTest(unittest.TestCase): - def test_post_blame_comment_on_bug(self): - def run(): - sheriff = Sheriff(MockTool(), MockSheriffBot()) - builders = [ - Builder("Foo", None), - Builder("Bar", None), - ] - commit_info = Mock() - commit_info.bug_id = lambda: None - commit_info.revision = lambda: 4321 - # Should do nothing with no bug_id - sheriff.post_blame_comment_on_bug(commit_info, builders, []) - sheriff.post_blame_comment_on_bug(commit_info, builders, ["mock-test-1", "mock-test-2"]) - # Should try to post a comment to the bug, but MockTool.bugs does nothing. - commit_info.bug_id = lambda: 1234 - sheriff.post_blame_comment_on_bug(commit_info, builders, []) - sheriff.post_blame_comment_on_bug(commit_info, builders, ["mock-test-1"]) - sheriff.post_blame_comment_on_bug(commit_info, builders, ["mock-test-1", "mock-test-2"]) - - expected_stderr = u"""MOCK bug comment: bug_id=1234, cc=['watcher@example.com'] ---- Begin comment --- -http://trac.webkit.org/changeset/4321 might have broken Foo and Bar ---- End comment --- - -MOCK bug comment: bug_id=1234, cc=['watcher@example.com'] ---- Begin comment --- -http://trac.webkit.org/changeset/4321 might have broken Foo and Bar -The following tests are not passing: -mock-test-1 ---- End comment --- - -MOCK bug comment: bug_id=1234, cc=['watcher@example.com'] ---- Begin comment --- -http://trac.webkit.org/changeset/4321 might have broken Foo and Bar -The following tests are not passing: -mock-test-1 -mock-test-2 ---- End comment --- - -""" - OutputCapture().assert_outputs(self, run, expected_stderr=expected_stderr) diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py deleted file mode 100644 index de77222..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot.py +++ /dev/null @@ -1,83 +0,0 @@ -# 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. - -import webkitpy.tool.bot.irc_command as irc_command - -from webkitpy.common.net.irc.ircbot import IRCBotDelegate -from webkitpy.common.thread.threadedmessagequeue import ThreadedMessageQueue - - -class _IRCThreadTearoff(IRCBotDelegate): - def __init__(self, password, message_queue, wakeup_event): - self._password = password - self._message_queue = message_queue - self._wakeup_event = wakeup_event - - # IRCBotDelegate methods - - def irc_message_received(self, nick, message): - self._message_queue.post([nick, message]) - self._wakeup_event.set() - - def irc_nickname(self): - return "sheriffbot" - - def irc_password(self): - return self._password - - -class SheriffIRCBot(object): - def __init__(self, tool, sheriff): - self._tool = tool - self._sheriff = sheriff - self._message_queue = ThreadedMessageQueue() - - def irc_delegate(self): - return _IRCThreadTearoff(self._tool.irc_password, - self._message_queue, - self._tool.wakeup_event) - - def process_message(self, message): - (nick, request) = message - tokenized_request = request.strip().split(" ") - if not tokenized_request: - return - command = irc_command.commands.get(tokenized_request[0]) - args = tokenized_request[1:] - if not command: - # Give the peoples someone to talk with. - command = irc_command.Eliza - args = tokenized_request - response = command().execute(nick, args, self._tool, self._sheriff) - if response: - self._tool.irc().post(response) - - def process_pending_messages(self): - (messages, is_running) = self._message_queue.take_all() - for message in messages: - self.process_message(message) diff --git a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py b/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py deleted file mode 100644 index 08023bd..0000000 --- a/WebKitTools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py +++ /dev/null @@ -1,95 +0,0 @@ -# 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. - -import unittest -import random - -from webkitpy.common.system.outputcapture import OutputCapture -from webkitpy.tool.bot.sheriff import Sheriff -from webkitpy.tool.bot.sheriffircbot import SheriffIRCBot -from webkitpy.tool.bot.sheriff_unittest import MockSheriffBot -from webkitpy.tool.mocktool import MockTool - - -def run(message): - tool = MockTool() - tool.ensure_irc_connected(None) - bot = SheriffIRCBot(tool, Sheriff(tool, MockSheriffBot())) - bot._message_queue.post(["mock_nick", message]) - bot.process_pending_messages() - - -class SheriffIRCBotTest(unittest.TestCase): - def test_hi(self): - random.seed(23324) - expected_stderr = 'MOCK: irc.post: "Only you can prevent forest fires." -- Smokey the Bear\n' - OutputCapture().assert_outputs(self, run, args=["hi"], expected_stderr=expected_stderr) - - def test_help(self): - expected_stderr = "MOCK: irc.post: mock_nick: Available commands: rollout, hi, help, restart, last-green-revision\n" - OutputCapture().assert_outputs(self, run, args=["help"], expected_stderr=expected_stderr) - - def test_lgr(self): - expected_stderr = "MOCK: irc.post: mock_nick: http://trac.webkit.org/changeset/9479\n" - OutputCapture().assert_outputs(self, run, args=["last-green-revision"], expected_stderr=expected_stderr) - - def test_rollout(self): - expected_stderr = "MOCK: irc.post: Preparing rollout for r21654...\nMOCK: irc.post: mock_nick: Created rollout: http://example.com/36936\n" - OutputCapture().assert_outputs(self, run, args=["rollout 21654 This patch broke the world"], expected_stderr=expected_stderr) - - def test_rollout_with_r_in_svn_revision(self): - expected_stderr = "MOCK: irc.post: Preparing rollout for r21654...\nMOCK: irc.post: mock_nick: Created rollout: http://example.com/36936\n" - OutputCapture().assert_outputs(self, run, args=["rollout r21654 This patch broke the world"], expected_stderr=expected_stderr) - - def test_rollout_bananas(self): - expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION REASON\n" - OutputCapture().assert_outputs(self, run, args=["rollout bananas"], expected_stderr=expected_stderr) - - def test_rollout_invalidate_revision(self): - expected_stderr = ("MOCK: irc.post: Preparing rollout for r--component=Tools...\n" - "MOCK: irc.post: mock_nick: Failed to create rollout patch:\n" - "MOCK: irc.post: Invalid svn revision number \"--component=Tools\".\n") - OutputCapture().assert_outputs(self, run, - args=["rollout " - "--component=Tools 21654"], - expected_stderr=expected_stderr) - - def test_rollout_invalidate_reason(self): - expected_stderr = ("MOCK: irc.post: Preparing rollout for " - "r21654...\nMOCK: irc.post: mock_nick: Failed to " - "create rollout patch:\nMOCK: irc.post: The rollout" - " reason may not begin with - (\"-bad (Requested " - "by mock_nick on #webkit).\").\n") - OutputCapture().assert_outputs(self, run, - args=["rollout " - "21654 -bad"], - expected_stderr=expected_stderr) - - def test_rollout_no_reason(self): - expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION REASON\n" - OutputCapture().assert_outputs(self, run, args=["rollout 21654"], expected_stderr=expected_stderr) |