diff options
| author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
|---|---|---|
| committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
| commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
| tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Tools/Scripts/webkitpy/tool | |
| parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
| download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 | |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Tools/Scripts/webkitpy/tool')
25 files changed, 282 insertions, 75 deletions
diff --git a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py index 1d82ea8..4bdc79b 100644 --- a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py +++ b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py @@ -149,11 +149,11 @@ class CommitQueueTask(object): "Able to pass tests without patch", "Unable to pass tests without patch (tree is red?)") - def _failing_tests_from_last_run(self): + def _failing_results_from_last_run(self): results = self._delegate.layout_test_results() if not results: - return None - return results.failing_tests() + return [] # Makes callers slighty cleaner to not have to deal with None + return results.failing_test_results() def _land(self): # Unclear if this should pass --quiet or not. If --parent-command always does the reporting, then it should. @@ -168,8 +168,8 @@ class CommitQueueTask(object): "Landed patch", "Unable to land patch") - def _report_flaky_tests(self, flaky_tests): - self._delegate.report_flaky_tests(self._patch, flaky_tests) + def _report_flaky_tests(self, flaky_test_results): + self._delegate.report_flaky_tests(self._patch, flaky_test_results) def _test_patch(self): if self._patch.is_rollout(): @@ -177,12 +177,14 @@ class CommitQueueTask(object): if self._test(): return True - first_failing_tests = self._failing_tests_from_last_run() + first_failing_results = self._failing_results_from_last_run() + first_failing_tests = [result.filename for result in first_failing_results] if self._test(): - self._report_flaky_tests(first_failing_tests) + self._report_flaky_tests(first_failing_results) return True - second_failing_tests = self._failing_tests_from_last_run() + second_failing_results = self._failing_results_from_last_run() + second_failing_tests = [result.filename for result in second_failing_results] if first_failing_tests != second_failing_tests: # We could report flaky tests here, but since run-webkit-tests # is run with --exit-after-N-failures=1, we would need to @@ -192,9 +194,14 @@ class CommitQueueTask(object): 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 self.report_failure() # The error from the previous ._test() run is real, report it. return False # Tree must be red, just retry later. + def report_failure(self): + if not self._validate(): + return False + raise self._script_error + def run(self): if not self._validate(): return False @@ -203,11 +210,11 @@ class CommitQueueTask(object): if not self._update(): return False if not self._apply(): - raise self._script_error + return self.report_failure() if not self._build(): if not self._build_without_patch(): return False - raise self._script_error + return self.report_failure() if not self._test_patch(): return False # Make sure the patch is still valid before landing (e.g., make sure @@ -216,5 +223,5 @@ class CommitQueueTask(object): return False # FIXME: We should understand why the land failure occured and retry if possible. if not self._land(): - raise self._script_error + return self.report_failure() return True diff --git a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py index 376f407..f279cac 100644 --- a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py +++ b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py @@ -31,6 +31,8 @@ import unittest from webkitpy.common.system.deprecated_logging import error, log from webkitpy.common.system.outputcapture import OutputCapture +from webkitpy.layout_tests.layout_package import test_results +from webkitpy.layout_tests.layout_package import test_failures from webkitpy.thirdparty.mock import Mock from webkitpy.tool.bot.commitqueuetask import * from webkitpy.tool.mocktool import MockTool @@ -62,11 +64,15 @@ class MockCommitQueue(CommitQueueTaskDelegate): def layout_test_results(self): return None - def report_flaky_tests(self, patch, flaky_tests): + def report_flaky_tests(self, patch, flaky_results): + flaky_tests = [result.filename for result in flaky_results] log("report_flaky_tests: patch='%s' flaky_tests='%s'" % (patch.id(), flaky_tests)) class CommitQueueTaskTest(unittest.TestCase): + def _mock_test_result(self, testname): + return test_results.TestResult(testname, [test_failures.FailureTextMismatch()]) + def _run_through_task(self, commit_queue, expected_stderr, expected_exception=None, expect_retry=False): tool = MockTool(log_executive=True) patch = tool.bugs.fetch_attachment(197) @@ -189,7 +195,7 @@ run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--n 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' +report_flaky_tests: patch='197' flaky_tests='[]' run_webkit_patch: ['land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197] command_passed: success_message='Landed patch' patch='197' """ @@ -227,13 +233,13 @@ command_failed: failure_message='Patch does not pass tests' script_error='MOCK t task = CommitQueueTask(commit_queue, patch) self._double_flaky_test_counter = 0 - def mock_failing_tests_from_last_run(): + def mock_failing_results_from_last_run(): CommitQueueTaskTest._double_flaky_test_counter += 1 if CommitQueueTaskTest._double_flaky_test_counter % 2: - return ['foo.html'] - return ['bar.html'] + return [self._mock_test_result('foo.html')] + return [self._mock_test_result('bar.html')] - task._failing_tests_from_last_run = mock_failing_tests_from_last_run + task._failing_results_from_last_run = mock_failing_results_from_last_run success = OutputCapture().assert_outputs(self, task.run, expected_stderr=expected_stderr) self.assertEqual(success, False) diff --git a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py index 01cbf39..91fcb85 100644 --- a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py +++ b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py @@ -102,7 +102,9 @@ class FlakyTestReporter(object): The bots will update this with information from each new failure. -If you would like to track this test fix with another bug, please close this bug as a duplicate. +If you believe this bug to be fixed or invalid, feel free to close. The bots will re-open if the flake re-occurs. + +If you would like to track this test fix with another bug, please close this bug as a duplicate. The bots will follow the duplicate chain when making future comments. """ % format_values master_flake_bug = 50856 # MASTER: Flaky tests found by the commit-queue @@ -124,8 +126,9 @@ If you would like to track this test fix with another bug, please close this bug bot_id_string = "Bot: %s " % (bot_id) if bot_id else "" return "%sPort: %s Platform: %s" % (bot_id_string, self._tool.port().name(), self._tool.platform.display_name()) - def _latest_flake_message(self, flaky_test, patch): - flake_message = "The %s just saw %s flake while processing attachment %s on bug %s." % (self._bot_name, flaky_test, patch.id(), patch.bug_id()) + def _latest_flake_message(self, flaky_result, patch): + failure_messages = [failure.message() for failure in flaky_result.failures] + flake_message = "The %s just saw %s flake (%s) while processing attachment %s on bug %s." % (self._bot_name, flaky_result.filename, ", ".join(failure_messages), patch.id(), patch.bug_id()) return "%s\n%s" % (flake_message, self._bot_information()) def _results_diff_path_for_test(self, flaky_test): @@ -150,11 +153,12 @@ If you would like to track this test fix with another bug, please close this bug else: self._tool.bugs.post_comment_to_bug(bug.id(), latest_flake_message) - def report_flaky_tests(self, flaky_tests, patch): + def report_flaky_tests(self, flaky_test_results, patch): message = "The %s encountered the following flaky tests while processing attachment %s:\n\n" % (self._bot_name, patch.id()) - for flaky_test in flaky_tests: + for flaky_result in flaky_test_results: + flaky_test = flaky_result.filename bug = self._lookup_bug_for_flaky_test(flaky_test) - latest_flake_message = self._latest_flake_message(flaky_test, patch) + latest_flake_message = self._latest_flake_message(flaky_result, patch) author_emails = self._author_emails_for_test(flaky_test) if not bug: _log.info("Bug does not already exist for %s, creating." % flaky_test) diff --git a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py index f72fb28..631f8d1 100644 --- a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py +++ b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py @@ -31,6 +31,8 @@ import unittest from webkitpy.common.config.committers import Committer from webkitpy.common.system.filesystem_mock import MockFileSystem from webkitpy.common.system.outputcapture import OutputCapture +from webkitpy.layout_tests.layout_package import test_results +from webkitpy.layout_tests.layout_package import test_failures from webkitpy.tool.bot.flakytestreporter import FlakyTestReporter from webkitpy.tool.mocktool import MockTool, MockStatusServer @@ -49,6 +51,9 @@ class MockCommitInfo(object): class FlakyTestReporterTest(unittest.TestCase): + def _mock_test_result(self, testname): + return test_results.TestResult(testname, [test_failures.FailureTextMismatch()]) + def _assert_emails_for_test(self, emails): tool = MockTool() reporter = FlakyTestReporter(tool, 'dummy-queue') @@ -75,7 +80,9 @@ FLAKE_MESSAGE The bots will update this with information from each new failure. -If you would like to track this test fix with another bug, please close this bug as a duplicate. +If you believe this bug to be fixed or invalid, feel free to close. The bots will re-open if the flake re-occurs. + +If you would like to track this test fix with another bug, please close this bug as a duplicate. The bots will follow the duplicate chain when making future comments. component: Tools / Tests cc: test@test.com @@ -110,12 +117,14 @@ foo/bar.html has been flaky on the dummy-queue. foo/bar.html was authored by abarth@webkit.org. http://trac.webkit.org/browser/trunk/LayoutTests/foo/bar.html -The dummy-queue just saw foo/bar.html flake while processing attachment 197 on bug 42. +The dummy-queue just saw foo/bar.html flake (Text diff mismatch) while processing attachment 197 on bug 42. Bot: mock-bot-id Port: MockPort Platform: MockPlatform 1.0 The bots will update this with information from each new failure. -If you would like to track this test fix with another bug, please close this bug as a duplicate. +If you believe this bug to be fixed or invalid, feel free to close. The bots will re-open if the flake re-occurs. + +If you would like to track this test fix with another bug, please close this bug as a duplicate. The bots will follow the duplicate chain when making future comments. component: Tools / Tests cc: abarth@webkit.org @@ -130,7 +139,8 @@ The dummy-queue is continuing to process your patch. --- End comment --- """ - OutputCapture().assert_outputs(self, reporter.report_flaky_tests, [['foo/bar.html'], patch], expected_stderr=expected_stderr) + test_results = [self._mock_test_result('foo/bar.html')] + OutputCapture().assert_outputs(self, reporter.report_flaky_tests, [test_results, patch], expected_stderr=expected_stderr) def test_optional_author_string(self): reporter = FlakyTestReporter(MockTool(), 'dummy-queue') diff --git a/Tools/Scripts/webkitpy/tool/bot/irc_command.py b/Tools/Scripts/webkitpy/tool/bot/irc_command.py index 0c17c9f..265974e 100644 --- a/Tools/Scripts/webkitpy/tool/bot/irc_command.py +++ b/Tools/Scripts/webkitpy/tool/bot/irc_command.py @@ -30,9 +30,10 @@ import random import webkitpy.common.config.irc as config_irc from webkitpy.common.config import urls -from webkitpy.tool.bot.queueengine import TerminateQueue from webkitpy.common.net.bugzilla import parse_bug_id from webkitpy.common.system.executive import ScriptError +from webkitpy.tool.bot.queueengine import TerminateQueue +from webkitpy.tool.grammar import join_with_separators # FIXME: Merge with Command? class IRCCommand(object): @@ -53,17 +54,38 @@ class Restart(IRCCommand): class Rollout(IRCCommand): + def _parse_args(self, args): + read_revision = True + rollout_reason = [] + # the first argument must be a revision number + svn_revision_list = [args[0].lstrip("r")] + if not svn_revision_list[0].isdigit(): + read_revision = False + + for arg in args[1:]: + if arg.lstrip("r").isdigit() and read_revision: + svn_revision_list.append(arg.lstrip("r")) + else: + read_revision = False + rollout_reason.append(arg) + + return svn_revision_list, rollout_reason + def execute(self, nick, args, tool, sheriff): - if len(args) < 2: - tool.irc().post("%s: Usage: SVN_REVISION REASON" % nick) + svn_revision_list, rollout_reason = self._parse_args(args) + + if (len(svn_revision_list) == 0) or (len(rollout_reason) == 0): + tool.irc().post("%s: Usage: SVN_REVISION [SVN_REVISIONS] REASON" % nick) return - svn_revision = args[0].lstrip("r") - rollout_reason = " ".join(args[1:]) - tool.irc().post("Preparing rollout for r%s..." % svn_revision) + + rollout_reason = " ".join(rollout_reason) + + tool.irc().post("Preparing rollout for %s..." % + join_with_separators(["r" + str(revision) for revision in svn_revision_list])) 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_id = sheriff.post_rollout_patch(svn_revision_list, 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: diff --git a/Tools/Scripts/webkitpy/tool/bot/sheriff.py b/Tools/Scripts/webkitpy/tool/bot/sheriff.py index 43f3221..a5edceb 100644 --- a/Tools/Scripts/webkitpy/tool/bot/sheriff.py +++ b/Tools/Scripts/webkitpy/tool/bot/sheriff.py @@ -51,14 +51,14 @@ class Sheriff(object): 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 + def post_rollout_patch(self, svn_revision_list, rollout_reason): + # Ensure that svn revisions are numbers (and not options to # create-rollout). try: - svn_revision = int(svn_revision) + svn_revisions = " ".join([str(int(revision)) for revision in svn_revision_list]) except: raise ScriptError(message="Invalid svn revision number \"%s\"." - % svn_revision) + % " ".join(svn_revision_list)) if rollout_reason.startswith("-"): raise ScriptError(message="The rollout reason may not begin " @@ -72,7 +72,7 @@ class Sheriff(object): # pass it prophylactically because we reject unrecognized command # line switches. "--parent-command=sheriff-bot", - svn_revision, + svn_revisions, rollout_reason, ]) return parse_bug_id(output) diff --git a/Tools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py b/Tools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py index 08023bd..ccb8ac2 100644 --- a/Tools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py +++ b/Tools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py @@ -62,12 +62,20 @@ class SheriffIRCBotTest(unittest.TestCase): 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_multi_rollout(self): + expected_stderr = "MOCK: irc.post: Preparing rollout for r21654, r21655, and r21656...\nMOCK: irc.post: mock_nick: Created rollout: http://example.com/36936\n" + OutputCapture().assert_outputs(self, run, args=["rollout 21654 21655 21656 This 21654 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_multi_rollout_with_r_in_svn_revision(self): + expected_stderr = "MOCK: irc.post: Preparing rollout for r21654, r21655, and r21656...\nMOCK: irc.post: mock_nick: Created rollout: http://example.com/36936\n" + OutputCapture().assert_outputs(self, run, args=["rollout r21654 21655 r21656 This r21654 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" + expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION [SVN_REVISIONS] REASON\n" OutputCapture().assert_outputs(self, run, args=["rollout bananas"], expected_stderr=expected_stderr) def test_rollout_invalidate_revision(self): @@ -90,6 +98,21 @@ class SheriffIRCBotTest(unittest.TestCase): "21654 -bad"], expected_stderr=expected_stderr) + def test_multi_rollout_invalidate_reason(self): + expected_stderr = ("MOCK: irc.post: Preparing rollout for " + "r21654, r21655, and r21656...\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 21655 r21656 -bad"], + expected_stderr=expected_stderr) + def test_rollout_no_reason(self): - expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION REASON\n" + expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION [SVN_REVISIONS] REASON\n" OutputCapture().assert_outputs(self, run, args=["rollout 21654"], expected_stderr=expected_stderr) + + def test_multi_rollout_no_reason(self): + expected_stderr = "MOCK: irc.post: mock_nick: Usage: SVN_REVISION [SVN_REVISIONS] REASON\n" + OutputCapture().assert_outputs(self, run, args=["rollout 21654 21655 r21656"], expected_stderr=expected_stderr) diff --git a/Tools/Scripts/webkitpy/tool/commands/download.py b/Tools/Scripts/webkitpy/tool/commands/download.py index 020f339..1b478bf 100644 --- a/Tools/Scripts/webkitpy/tool/commands/download.py +++ b/Tools/Scripts/webkitpy/tool/commands/download.py @@ -95,6 +95,7 @@ class Land(AbstractSequencedCommand): steps.EnsureBuildersAreGreen, steps.UpdateChangeLogsWithReviewer, steps.ValidateReviewer, + steps.ValidateChangeLogs, # We do this after UpdateChangeLogsWithReviewer to avoid not having to cache the diff twice. steps.Build, steps.RunTests, steps.Commit, @@ -257,6 +258,7 @@ class AbstractPatchLandingCommand(AbstractPatchSequencingCommand): steps.CleanWorkingDirectory, steps.Update, steps.ApplyPatch, + steps.ValidateChangeLogs, steps.ValidateReviewer, steps.Build, steps.RunTests, diff --git a/Tools/Scripts/webkitpy/tool/commands/download_unittest.py b/Tools/Scripts/webkitpy/tool/commands/download_unittest.py index 3748a8f..ba23ab9 100644 --- a/Tools/Scripts/webkitpy/tool/commands/download_unittest.py +++ b/Tools/Scripts/webkitpy/tool/commands/download_unittest.py @@ -109,11 +109,11 @@ class DownloadCommandsTest(CommandsTest): def test_land_diff(self): expected_stderr = "Building WebKit\nRunning Python unit tests\nRunning Perl unit tests\nRunning JavaScriptCore tests\nRunning run-webkit-tests\nCommitted r49824: <http://trac.webkit.org/changeset/49824>\nUpdating bug 42\n" mock_tool = MockTool() - mock_tool.scm().create_patch = Mock() + mock_tool.scm().create_patch = Mock(return_value="Patch1\nMockPatch\n") mock_tool.checkout().modified_changelogs = Mock(return_value=[]) self.assert_execute_outputs(Land(), [42], options=self._default_options(), expected_stderr=expected_stderr, tool=mock_tool) # Make sure we're not calling expensive calls too often. - self.assertEqual(mock_tool.scm().create_patch.call_count, 0) + self.assertEqual(mock_tool.scm().create_patch.call_count, 1) self.assertEqual(mock_tool.checkout().modified_changelogs.call_count, 1) def test_land_red_builders(self): diff --git a/Tools/Scripts/webkitpy/tool/commands/queries.py b/Tools/Scripts/webkitpy/tool/commands/queries.py index f04f384..733751e 100644 --- a/Tools/Scripts/webkitpy/tool/commands/queries.py +++ b/Tools/Scripts/webkitpy/tool/commands/queries.py @@ -272,7 +272,7 @@ class FailureReason(AbstractDeclarativeCommand): print "%s failing" % (pluralize("builder", len(red_statuses))) builder_choices = [status["name"] for status in red_statuses] # We could offer an "All" choice here. - chosen_name = User.prompt_with_list("Which builder to diagnose:", builder_choices) + chosen_name = self._tool.user.prompt_with_list("Which builder to diagnose:", builder_choices) # FIXME: prompt_with_list should really take a set of objects and a set of names and then return the object. for status in red_statuses: if status["name"] == chosen_name: @@ -345,7 +345,7 @@ class FindFlakyTests(AbstractDeclarativeCommand): def _builder_to_analyze(self): statuses = self._tool.buildbot.builder_statuses() choices = [status["name"] for status in statuses] - chosen_name = User.prompt_with_list("Which builder to analyze:", choices) + chosen_name = self._tool.user.prompt_with_list("Which builder to analyze:", choices) for status in statuses: if status["name"] == chosen_name: return (self._tool.buildbot.builder_with_name(chosen_name), status["built_revision"]) diff --git a/Tools/Scripts/webkitpy/tool/commands/queues.py b/Tools/Scripts/webkitpy/tool/commands/queues.py index e15555f..5628543 100644 --- a/Tools/Scripts/webkitpy/tool/commands/queues.py +++ b/Tools/Scripts/webkitpy/tool/commands/queues.py @@ -96,7 +96,7 @@ class AbstractQueue(Command, QueueEngineDelegate): return self._tool.executive.run_and_throw_if_fail(webkit_patch_args) def _log_directory(self): - return "%s-logs" % self.name + return os.path.join("..", "%s-logs" % self.name) # QueueEngineDelegate methods @@ -312,9 +312,9 @@ class CommitQueue(AbstractPatchQueue, StepSequenceErrorHandler, CommitQueueTaskD def refetch_patch(self, patch): return self._tool.bugs.fetch_attachment(patch.id()) - def report_flaky_tests(self, patch, flaky_tests): + def report_flaky_tests(self, patch, flaky_test_results): reporter = FlakyTestReporter(self._tool, self.name) - reporter.report_flaky_tests(flaky_tests, patch) + reporter.report_flaky_tests(flaky_test_results, patch) # StepSequenceErrorHandler methods diff --git a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py index d793213..34a6a64 100644 --- a/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py +++ b/Tools/Scripts/webkitpy/tool/commands/queues_unittest.py @@ -31,6 +31,8 @@ import os from webkitpy.common.checkout.scm import CheckoutNeedsUpdate from webkitpy.common.net.bugzilla import Attachment from webkitpy.common.system.outputcapture import OutputCapture +from webkitpy.layout_tests.layout_package import test_results +from webkitpy.layout_tests.layout_package import test_failures from webkitpy.thirdparty.mock import Mock from webkitpy.tool.commands.commandtest import CommandsTest from webkitpy.tool.commands.queues import * @@ -53,7 +55,7 @@ class TestFeederQueue(FeederQueue): class AbstractQueueTest(CommandsTest): def test_log_directory(self): - self.assertEquals(TestQueue()._log_directory(), "test-queue-logs") + self.assertEquals(TestQueue()._log_directory(), os.path.join("..", "test-queue-logs")) def _assert_run_webkit_patch(self, run_args, port=None): queue = TestQueue() @@ -198,6 +200,9 @@ class SecondThoughtsCommitQueue(CommitQueue): class CommitQueueTest(QueuesTest): + def _mock_test_result(self, testname): + return test_results.TestResult(testname, [test_failures.FailureTextMismatch()]) + def test_commit_queue(self): expected_stderr = { "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root), @@ -333,13 +338,13 @@ MOCK: release_work_item: commit-queue 197 queue.bind_to_tool(MockTool()) expected_stderr = """MOCK bug comment: bug_id=76, cc=None --- Begin comment --- -The commit-queue just saw foo/bar.html flake while processing attachment 197 on bug 42. +The commit-queue just saw foo/bar.html flake (Text diff mismatch) while processing attachment 197 on bug 42. Port: MockPort Platform: MockPlatform 1.0 --- End comment --- MOCK bug comment: bug_id=76, cc=None --- Begin comment --- -The commit-queue just saw bar/baz.html flake while processing attachment 197 on bug 42. +The commit-queue just saw bar/baz.html flake (Text diff mismatch) while processing attachment 197 on bug 42. Port: MockPort Platform: MockPlatform 1.0 --- End comment --- @@ -353,7 +358,9 @@ The commit-queue is continuing to process your patch. --- End comment --- """ - OutputCapture().assert_outputs(self, queue.report_flaky_tests, [QueuesTest.mock_work_item, ["foo/bar.html", "bar/baz.html"]], expected_stderr=expected_stderr) + test_names = ["foo/bar.html", "bar/baz.html"] + test_results = [self._mock_test_result(name) for name in test_names] + OutputCapture().assert_outputs(self, queue.report_flaky_tests, [QueuesTest.mock_work_item, test_results], expected_stderr=expected_stderr) def test_layout_test_results(self): queue = CommitQueue() diff --git a/Tools/Scripts/webkitpy/tool/commands/rebaseline.py b/Tools/Scripts/webkitpy/tool/commands/rebaseline.py index 8c4b997..34a398a 100644 --- a/Tools/Scripts/webkitpy/tool/commands/rebaseline.py +++ b/Tools/Scripts/webkitpy/tool/commands/rebaseline.py @@ -34,6 +34,7 @@ import urllib from webkitpy.common.net.buildbot import BuildBot from webkitpy.common.net.layouttestresults import LayoutTestResults from webkitpy.common.system.user import User +from webkitpy.layout_tests.layout_package import test_failures from webkitpy.layout_tests.port import factory from webkitpy.tool.grammar import pluralize from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand @@ -88,7 +89,7 @@ class Rebaseline(AbstractDeclarativeCommand): shutil.move(downloaded_file, local_file) def _tests_to_update(self, build): - failing_tests = build.layout_test_results().results_matching_keys([LayoutTestResults.fail_key]) + failing_tests = build.layout_test_results().tests_matching_failure_types([test_failures.FailureTextMismatch]) return self._tool.user.prompt_with_list("Which test(s) to rebaseline:", failing_tests, can_choose_multiple=True) def _results_url_for_test(self, build, test): diff --git a/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py b/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py index d6582a7..79e4cf4 100644 --- a/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py +++ b/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py @@ -28,7 +28,19 @@ import unittest -from webkitpy.tool.commands.rebaseline import BuilderToPort +from webkitpy.common.system.outputcapture import OutputCapture +from webkitpy.thirdparty.mock import Mock +from webkitpy.tool.commands.rebaseline import BuilderToPort, Rebaseline +from webkitpy.tool.mocktool import MockTool + + +class RebaselineTest(unittest.TestCase): + # This just makes sure the code runs without exceptions. + def test_tests_to_update(self): + command = Rebaseline() + command.bind_to_tool(MockTool()) + build = Mock() + OutputCapture().assert_outputs(self, command._tests_to_update, [build]) class BuilderToPortTest(unittest.TestCase): diff --git a/Tools/Scripts/webkitpy/tool/commands/upload.py b/Tools/Scripts/webkitpy/tool/commands/upload.py index e12c8e2..6617b4f 100644 --- a/Tools/Scripts/webkitpy/tool/commands/upload.py +++ b/Tools/Scripts/webkitpy/tool/commands/upload.py @@ -196,6 +196,7 @@ class Post(AbstractPatchUploadingCommand): help_text = "Attach the current working directory diff to a bug as a patch file" argument_names = "[BUGID]" steps = [ + steps.ValidateChangeLogs, steps.CheckStyle, steps.ConfirmDiff, steps.ObsoletePatches, @@ -215,6 +216,7 @@ class LandSafely(AbstractPatchUploadingCommand): show_in_main_help = True steps = [ steps.UpdateChangeLogsWithReviewer, + steps.ValidateChangeLogs, steps.ObsoletePatches, steps.PostDiffForCommit, ] @@ -241,6 +243,7 @@ class Upload(AbstractPatchUploadingCommand): argument_names = "[BUGID]" show_in_main_help = True steps = [ + steps.ValidateChangeLogs, steps.CheckStyle, steps.PromptForBugOrTitle, steps.CreateBug, diff --git a/Tools/Scripts/webkitpy/tool/mocktool.py b/Tools/Scripts/webkitpy/tool/mocktool.py index 30a4bc3..eb7c248 100644 --- a/Tools/Scripts/webkitpy/tool/mocktool.py +++ b/Tools/Scripts/webkitpy/tool/mocktool.py @@ -540,10 +540,14 @@ class MockCheckout(object): class MockUser(object): - @staticmethod - def prompt(message, repeat=1, raw_input=raw_input): + @classmethod + def prompt(cls, message, repeat=1, raw_input=raw_input): return "Mock user response" + @classmethod + def prompt_with_list(cls, list_title, list_items, can_choose_multiple=False, raw_input=raw_input): + pass + def edit(self, files): pass diff --git a/Tools/Scripts/webkitpy/tool/steps/__init__.py b/Tools/Scripts/webkitpy/tool/steps/__init__.py index 64d9d05..f426f17 100644 --- a/Tools/Scripts/webkitpy/tool/steps/__init__.py +++ b/Tools/Scripts/webkitpy/tool/steps/__init__.py @@ -56,4 +56,5 @@ 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.validatechangelogs import ValidateChangeLogs from webkitpy.tool.steps.validatereviewer import ValidateReviewer diff --git a/Tools/Scripts/webkitpy/tool/steps/abstractstep.py b/Tools/Scripts/webkitpy/tool/steps/abstractstep.py index 5525ea0..1c93d5b 100644 --- a/Tools/Scripts/webkitpy/tool/steps/abstractstep.py +++ b/Tools/Scripts/webkitpy/tool/steps/abstractstep.py @@ -52,6 +52,7 @@ class AbstractStep(object): "bug_title": lambda self, state: self._tool.bugs.fetch_bug(state["bug_id"]).title(), "changed_files": lambda self, state: self._tool.scm().changed_files(self._options.git_commit), "diff": lambda self, state: self._tool.scm().create_patch(self._options.git_commit, changed_files=self._changed_files(state)), + # Absolute path to ChangeLog files. "changelogs": lambda self, state: self._tool.checkout().modified_changelogs(self._options.git_commit, changed_files=self._changed_files(state)), } diff --git a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py index 9c16242..a165dd2 100644 --- a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py +++ b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py @@ -47,7 +47,9 @@ class CleanWorkingDirectory(AbstractStep): def run(self, state): if not self._options.clean: return - # FIXME: This chdir should not be necessary. + # FIXME: This chdir should not be necessary and can be removed as + # soon as ensure_no_local_commits and ensure_clean_working_directory + # are known to set the CWD to checkout_root when calling run_command. os.chdir(self._tool.scm().checkout_root) if not self._allow_local_commits: self._tool.scm().ensure_no_local_commits(self._options.force_clean) diff --git a/Tools/Scripts/webkitpy/tool/steps/commit.py b/Tools/Scripts/webkitpy/tool/steps/commit.py index 5aa6b51..859acbf 100644 --- a/Tools/Scripts/webkitpy/tool/steps/commit.py +++ b/Tools/Scripts/webkitpy/tool/steps/commit.py @@ -36,12 +36,6 @@ from webkitpy.tool.steps.options import Options class Commit(AbstractStep): - @classmethod - def options(cls): - return AbstractStep.options() + [ - Options.git_commit, - ] - def _commit_warning(self, error): working_directory_message = "" if error.working_directory_is_clean else " and working copy changes" return ('There are %s local commits%s. Everything will be committed as a single commit. ' diff --git a/Tools/Scripts/webkitpy/tool/steps/update.py b/Tools/Scripts/webkitpy/tool/steps/update.py index cd1d4d8..036c46d 100644 --- a/Tools/Scripts/webkitpy/tool/steps/update.py +++ b/Tools/Scripts/webkitpy/tool/steps/update.py @@ -36,10 +36,11 @@ class Update(AbstractStep): def options(cls): return AbstractStep.options() + [ Options.update, + Options.quiet, ] def run(self, state): if not self._options.update: return log("Updating working directory") - self._tool.executive.run_and_throw_if_fail(self._tool.port().update_webkit_command(), quiet=True) + self._tool.executive.run_and_throw_if_fail(self._tool.port().update_webkit_command(), quiet=self._options.quiet) diff --git a/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py b/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py index e46b790..2bf41b3 100644 --- a/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py +++ b/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py @@ -67,6 +67,9 @@ class UpdateChangeLogsWithReviewer(AbstractStep): log("Failed to guess reviewer from bug %s and --reviewer= not provided. Not updating ChangeLogs with reviewer." % bug_id) return - os.chdir(self._tool.scm().checkout_root) + # cached_lookup("changelogs") is always absolute paths. for changelog_path in self.cached_lookup(state, "changelogs"): ChangeLog(changelog_path).set_reviewer(reviewer) + + # Tell the world that we just changed something on disk so that the cached diff is invalidated. + self.did_modify_checkout(state) diff --git a/Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py new file mode 100644 index 0000000..e812f94 --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py @@ -0,0 +1,58 @@ +# 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.tool.steps.abstractstep import AbstractStep +from webkitpy.tool.steps.options import Options +from webkitpy.common.checkout.diff_parser import DiffParser +from webkitpy.common.system.deprecated_logging import error, log + + +# This is closely related to the ValidateReviewer step and the CommitterValidator class. +# We may want to unify more of this code in one place. +class ValidateChangeLogs(AbstractStep): + def _check_changelog_diff(self, diff_file): + if not self._tool.checkout().is_path_to_changelog(diff_file.filename): + return True + # Each line is a tuple, the first value is the deleted line number + # Date, reviewer, bug title, bug url, and empty lines could all be + # identical in the most recent entries. If the diff starts any + # later than that, assume that the entry is wrong. + if diff_file.lines[0][0] < 8: + return True + log("The diff to %s looks wrong. Are you sure your ChangeLog entry is at the top of the file?" % (diff_file.filename)) + # FIXME: Do we need to make the file path absolute? + self._tool.scm().diff_for_file(diff_file.filename) + if self._tool.user.confirm("OK to continue?", default='n'): + return True + return False + + def run(self, state): + parsed_diff = DiffParser(self.cached_lookup(state, "diff").splitlines()) + for filename, diff_file in parsed_diff.files.items(): + if not self._check_changelog_diff(diff_file): + error("ChangeLog entry in %s is not at the top of the file." % diff_file.filename) diff --git a/Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py new file mode 100644 index 0000000..66db793 --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py @@ -0,0 +1,55 @@ +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * 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.thirdparty.mock import Mock +from webkitpy.tool.mocktool import MockOptions, MockTool +from webkitpy.tool.steps.validatechangelogs import ValidateChangeLogs + + +class ValidateChangeLogsTest(unittest.TestCase): + + def _assert_start_line_produces_output(self, start_line, should_prompt_user=False): + tool = MockTool() + tool._checkout.is_path_to_changelog = lambda path: True + step = ValidateChangeLogs(tool, MockOptions(git_commit=None)) + diff_file = Mock() + diff_file.filename = "mock/ChangeLog" + diff_file.lines = [(start_line, start_line, "foo")] + expected_stdout = expected_stderr = "" + if should_prompt_user: + expected_stdout = "OK to continue?\n" + expected_stderr = "The diff to mock/ChangeLog looks wrong. Are you sure your ChangeLog entry is at the top of the file?\n" + OutputCapture().assert_outputs(self, step._check_changelog_diff, [diff_file], expected_stdout=expected_stdout, expected_stderr=expected_stderr) + + def test_check_changelog_diff(self): + self._assert_start_line_produces_output(1, should_prompt_user=False) + self._assert_start_line_produces_output(7, should_prompt_user=False) + self._assert_start_line_produces_output(8, should_prompt_user=True) diff --git a/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py b/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py index bdf729e..c2a27b9 100644 --- a/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py +++ b/Tools/Scripts/webkitpy/tool/steps/validatereviewer.py @@ -37,12 +37,6 @@ from webkitpy.common.system.deprecated_logging import error, log # FIXME: Some of this logic should probably be unified with CommitterValidator? class ValidateReviewer(AbstractStep): - @classmethod - def options(cls): - return AbstractStep.options() + [ - Options.git_commit, - ] - # FIXME: This should probably move onto ChangeLogEntry def _has_valid_reviewer(self, changelog_entry): if changelog_entry.reviewer(): @@ -58,9 +52,6 @@ class ValidateReviewer(AbstractStep): # this check is too draconian (and too poorly tested) to foist upon users. if not self._options.non_interactive: return - # FIXME: We should figure out how to handle the current working - # directory issue more globally. - os.chdir(self._tool.scm().checkout_root) for changelog_path in self.cached_lookup(state, "changelogs"): changelog_entry = ChangeLog(changelog_path).latest_entry() if self._has_valid_reviewer(changelog_entry): |
