summaryrefslogtreecommitdiffstats
path: root/Tools/Scripts/webkitpy/tool
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Tools/Scripts/webkitpy/tool
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_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')
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py31
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py18
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py16
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py18
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/irc_command.py36
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/sheriff.py10
-rw-r--r--Tools/Scripts/webkitpy/tool/bot/sheriffircbot_unittest.py27
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/download.py2
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/download_unittest.py4
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/queries.py4
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/queues.py6
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/queues_unittest.py15
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/rebaseline.py3
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py14
-rw-r--r--Tools/Scripts/webkitpy/tool/commands/upload.py3
-rw-r--r--Tools/Scripts/webkitpy/tool/mocktool.py8
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/__init__.py1
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/abstractstep.py1
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py4
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/commit.py6
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/update.py3
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreviewer.py5
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py58
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py55
-rw-r--r--Tools/Scripts/webkitpy/tool/steps/validatereviewer.py9
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):