diff options
Diffstat (limited to 'Tools/Scripts/webkitpy/tool/steps')
31 files changed, 831 insertions, 230 deletions
diff --git a/Tools/Scripts/webkitpy/tool/steps/__init__.py b/Tools/Scripts/webkitpy/tool/steps/__init__.py index 56429e8fe..655f7d50a 100644 --- a/Tools/Scripts/webkitpy/tool/steps/__init__.py +++ b/Tools/Scripts/webkitpy/tool/steps/__init__.py @@ -35,23 +35,23 @@ from webkitpy.tool.steps.attachtobug import AttachToBug from webkitpy.tool.steps.build import Build from webkitpy.tool.steps.checkstyle import CheckStyle from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory -from webkitpy.tool.steps.cleanworkingdirectorywithlocalcommits import CleanWorkingDirectoryWithLocalCommits from webkitpy.tool.steps.closebug import CloseBug from webkitpy.tool.steps.closebugforlanddiff import CloseBugForLandDiff from webkitpy.tool.steps.closepatch import ClosePatch from webkitpy.tool.steps.commit import Commit from webkitpy.tool.steps.confirmdiff import ConfirmDiff from webkitpy.tool.steps.createbug import CreateBug +from webkitpy.tool.steps.discardlocalchanges import DiscardLocalChanges from webkitpy.tool.steps.editchangelog import EditChangeLog from webkitpy.tool.steps.ensurebugisopenandassigned import EnsureBugIsOpenAndAssigned from webkitpy.tool.steps.ensurelocalcommitifneeded import EnsureLocalCommitIfNeeded +from webkitpy.tool.steps.haslanded import HasLanded from webkitpy.tool.steps.obsoletepatches import ObsoletePatches from webkitpy.tool.steps.options import Options from webkitpy.tool.steps.postdiff import PostDiff from webkitpy.tool.steps.postdiffforcommit import PostDiffForCommit from webkitpy.tool.steps.postdiffforrevert import PostDiffForRevert from webkitpy.tool.steps.preparechangelog import PrepareChangeLog -from webkitpy.tool.steps.preparechangelogfordepsroll import PrepareChangeLogForDEPSRoll from webkitpy.tool.steps.preparechangelogforrevert import PrepareChangeLogForRevert from webkitpy.tool.steps.promptforbugortitle import PromptForBugOrTitle from webkitpy.tool.steps.reopenbugafterrollout import ReopenBugAfterRollout @@ -60,6 +60,5 @@ from webkitpy.tool.steps.runtests import RunTests from webkitpy.tool.steps.suggestreviewers import SuggestReviewers from webkitpy.tool.steps.update import Update from webkitpy.tool.steps.updatechangelogswithreviewer import UpdateChangeLogsWithReviewer -from webkitpy.tool.steps.updatechromiumdeps import UpdateChromiumDEPS from webkitpy.tool.steps.validatechangelogs import ValidateChangeLogs from webkitpy.tool.steps.validatereviewer import ValidateReviewer diff --git a/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py b/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py index 9fab6f438..12be0bee2 100644 --- a/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/addsvnmimetypeforpng_unittest.py @@ -21,7 +21,7 @@ # (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 unittest2 as unittest from webkitpy.tool.steps.addsvnmimetypeforpng import AddSvnMimetypeForPng from webkitpy.common.system.filesystem_mock import MockFileSystem diff --git a/Tools/Scripts/webkitpy/tool/steps/applywatchlist_unittest.py b/Tools/Scripts/webkitpy/tool/steps/applywatchlist_unittest.py index a978f4164..a740c3d3c 100644 --- a/Tools/Scripts/webkitpy/tool/steps/applywatchlist_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/applywatchlist_unittest.py @@ -26,7 +26,7 @@ # (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 unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.tool.mocktool import MockOptions, MockTool diff --git a/Tools/Scripts/webkitpy/tool/steps/build.py b/Tools/Scripts/webkitpy/tool/steps/build.py index a2a627229..b02830ca2 100644 --- a/Tools/Scripts/webkitpy/tool/steps/build.py +++ b/Tools/Scripts/webkitpy/tool/steps/build.py @@ -48,7 +48,7 @@ class Build(AbstractStep): environment.disable_gcc_smartquotes() env = environment.to_dictionary() - build_webkit_command = self._tool.port().build_webkit_command(build_style=build_style) + build_webkit_command = self._tool.deprecated_port().build_webkit_command(build_style=build_style) self._tool.executive.run_and_throw_if_fail(build_webkit_command, self._options.quiet, cwd=self._tool.scm().checkout_root, env=env) diff --git a/Tools/Scripts/webkitpy/tool/steps/checkstyle.py b/Tools/Scripts/webkitpy/tool/steps/checkstyle.py index 0cb15f4c1..cec8a8132 100644 --- a/Tools/Scripts/webkitpy/tool/steps/checkstyle.py +++ b/Tools/Scripts/webkitpy/tool/steps/checkstyle.py @@ -57,7 +57,7 @@ class CheckStyle(AbstractStep): args.append(self._options.check_style_filter) try: - self._tool.executive.run_and_throw_if_fail(self._tool.port().check_webkit_style_command() + args, cwd=self._tool.scm().checkout_root) + self._tool.executive.run_and_throw_if_fail(self._tool.deprecated_port().check_webkit_style_command() + args, cwd=self._tool.scm().checkout_root) except ScriptError, e: if self._options.non_interactive: # We need to re-raise the exception here to have the diff --git a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py index 191352440..a4cbe82c5 100644 --- a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py +++ b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory.py @@ -28,12 +28,10 @@ from webkitpy.tool.steps.abstractstep import AbstractStep from webkitpy.tool.steps.options import Options +from webkitpy.common.system.executive import ScriptError class CleanWorkingDirectory(AbstractStep): - def __init__(self, tool, options, allow_local_commits=False): - AbstractStep.__init__(self, tool, options) - self._allow_local_commits = allow_local_commits @classmethod def options(cls): @@ -45,6 +43,8 @@ class CleanWorkingDirectory(AbstractStep): def run(self, state): if not self._options.clean: return - if not self._allow_local_commits: - self._tool.scm().ensure_no_local_commits(self._options.force_clean) - self._tool.scm().ensure_clean_working_directory(force_clean=self._options.force_clean) + + if self._tool.scm().has_working_directory_changes() and not self._options.force_clean: + raise ScriptError("Working directory has changes, pass --force-clean to continue.") + + self._tool.scm().discard_working_directory_changes() diff --git a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py index 15a8850a5..7e31a9bd8 100644 --- a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectory_unittest.py @@ -26,27 +26,43 @@ # (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 unittest2 as unittest from webkitpy.thirdparty.mock import Mock from webkitpy.tool.mocktool import MockOptions, MockTool from webkitpy.tool.steps.cleanworkingdirectory import CleanWorkingDirectory +from webkitpy.common.system.executive import ScriptError class CleanWorkingDirectoryTest(unittest.TestCase): - def test_run(self): + def test_run_working_directory_changes_no_force(self): tool = MockTool() tool._scm = Mock() - tool._scm.checkout_root = '/mock-checkout' step = CleanWorkingDirectory(tool, MockOptions(clean=True, force_clean=False)) + tool._scm.has_working_directory_changes = lambda: True + self.assertRaises(ScriptError, step.run, {}) + self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 0) + + def test_run_working_directory_changes_force(self): + tool = MockTool() + tool._scm = Mock() + step = CleanWorkingDirectory(tool, MockOptions(clean=True, force_clean=True)) + tool._scm.has_working_directory_changes = lambda: True + step.run({}) + self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 1) + + def test_run_no_local_changes(self): + tool = MockTool() + tool._scm = Mock() + step = CleanWorkingDirectory(tool, MockOptions(clean=True, force_clean=False)) + tool._scm.has_working_directory_changes = lambda: False + tool._scm.has_local_commits = lambda: False step.run({}) - self.assertEqual(tool._scm.ensure_no_local_commits.call_count, 1) - self.assertEqual(tool._scm.ensure_clean_working_directory.call_count, 1) + self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 1) def test_no_clean(self): tool = MockTool() tool._scm = Mock() step = CleanWorkingDirectory(tool, MockOptions(clean=False)) step.run({}) - self.assertEqual(tool._scm.ensure_no_local_commits.call_count, 0) - self.assertEqual(tool._scm.ensure_clean_working_directory.call_count, 0) + self.assertEqual(tool._scm.discard_working_directory_changes.call_count, 0) diff --git a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py b/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py deleted file mode 100644 index f06f94ef4..000000000 --- a/Tools/Scripts/webkitpy/tool/steps/cleanworkingdirectorywithlocalcommits.py +++ /dev/null @@ -1,34 +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.tool.steps.cleanworkingdirectory import CleanWorkingDirectory - -class CleanWorkingDirectoryWithLocalCommits(CleanWorkingDirectory): - def __init__(self, tool, options): - # FIXME: This a bit of a hack. Consider doing this more cleanly. - CleanWorkingDirectory.__init__(self, tool, options, allow_local_commits=True) diff --git a/Tools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py b/Tools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py index 6969c4e9a..b042d4258 100644 --- a/Tools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/closebugforlanddiff_unittest.py @@ -26,7 +26,7 @@ # (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 unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.tool.mocktool import MockOptions, MockTool diff --git a/Tools/Scripts/webkitpy/tool/steps/commit.py b/Tools/Scripts/webkitpy/tool/steps/commit.py index 2bffa4c2a..1d5109a00 100644 --- a/Tools/Scripts/webkitpy/tool/steps/commit.py +++ b/Tools/Scripts/webkitpy/tool/steps/commit.py @@ -40,20 +40,17 @@ _log = logging.getLogger(__name__) class Commit(AbstractStep): - # FIXME: This option exists only to make sure we don't break scripts which include --ignore-builders - # You can safely delete this option any time after 11/01/11. @classmethod def options(cls): return AbstractStep.options() + [ - Options.check_builders, Options.non_interactive, ] 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. ' + return ('There are %s local commits (and possibly changes in the working directory. ' + 'Everything will be committed as a single commit. ' 'To avoid this prompt, set "git config webkit-patch.commit-should-always-squash true".' % ( - error.num_local_commits, working_directory_message)) + error.num_local_commits)) def _check_test_expectations(self, changed_files): test_expectations_files = [filename for filename in changed_files if filename.endswith('TestExpectations')] @@ -63,7 +60,7 @@ class Commit(AbstractStep): args = ["--diff-files"] args.extend(test_expectations_files) try: - self._tool.executive.run_and_throw_if_fail(self._tool.port().check_webkit_style_command() + args, cwd=self._tool.scm().checkout_root) + self._tool.executive.run_and_throw_if_fail(self._tool.deprecated_port().check_webkit_style_command() + args, cwd=self._tool.scm().checkout_root) except ScriptError, e: if self._options.non_interactive: raise @@ -76,12 +73,11 @@ class Commit(AbstractStep): raise Exception("Attempted to commit with a commit message shorter than 10 characters. Either your patch is missing a ChangeLog or webkit-patch may have a bug.") self._check_test_expectations(self._changed_files(state)) - self._state = state username = None password = None - force_squash = False + force_squash = self._options.non_interactive num_tries = 0 while num_tries < 3: @@ -95,7 +91,7 @@ class Commit(AbstractStep): self._state["commit_text"] = commit_text break; except AmbiguousCommitError, e: - if self._options.non_interactive or self._tool.user.confirm(self._commit_warning(e)): + if self._tool.user.confirm(self._commit_warning(e)): force_squash = True else: # This will correctly interrupt the rest of the commit process. diff --git a/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py b/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py index 936e3ebab..c6b76b428 100644 --- a/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/commit_unittest.py @@ -26,7 +26,7 @@ # (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 unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.common.system.executive import ScriptError diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelogfordepsroll.py b/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges.py index 4bbd383ae..8a84cc702 100644 --- a/Tools/Scripts/webkitpy/tool/steps/preparechangelogfordepsroll.py +++ b/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges.py @@ -1,4 +1,4 @@ -# Copyright (C) 2011 Google Inc. All rights reserved. +# 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 @@ -26,13 +26,27 @@ # (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 ChangeLog from webkitpy.tool.steps.abstractstep import AbstractStep +from webkitpy.tool.steps.options import Options +from webkitpy.common.system.executive import ScriptError -class PrepareChangeLogForDEPSRoll(AbstractStep): +class DiscardLocalChanges(AbstractStep): + + @classmethod + def options(cls): + return AbstractStep.options() + [ + Options.clean, + Options.force_clean, + ] + def run(self, state): - self._tool.executive.run_and_throw_if_fail(self._tool.port().prepare_changelog_command()) - changelog_paths = self._tool.checkout().modified_changelogs(git_commit=None) - for changelog_path in changelog_paths: - ChangeLog(changelog_path).update_with_unreviewed_message("Unreviewed. Rolled DEPS.\n\n") + if not self._options.clean: + return + + if not self._options.force_clean: + if self._tool.scm().has_working_directory_changes(): + raise ScriptError("Working directory has changes, pass --force-clean to continue.") + if self._tool.scm().has_local_commits(): + raise ScriptError("Repository has local commits, pass --force-clean to continue.") + self._tool.scm().discard_local_changes() diff --git a/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges_unittest.py b/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges_unittest.py new file mode 100644 index 000000000..d38fc926c --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/steps/discardlocalchanges_unittest.py @@ -0,0 +1,97 @@ +# 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 unittest2 as unittest + +from webkitpy.thirdparty.mock import Mock +from webkitpy.tool.mocktool import MockOptions, MockTool +from webkitpy.tool.steps.discardlocalchanges import DiscardLocalChanges +from webkitpy.common.system.executive import ScriptError + + +class DiscardLocalChangesTest(unittest.TestCase): + def test_skip_on_clean(self): + tool = MockTool() + tool._scm = Mock() + step = DiscardLocalChanges(tool, MockOptions(clean=False)) + step.run({}) + self.assertEqual(tool._scm.discard_local_changes.call_count, 0) + + def test_working_changes_exist_with_force(self): + tool = MockTool() + tool._scm = Mock() + tool._scm.has_working_directory_changes = lambda: True + tool._scm.has_local_commits = lambda: False + step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True)) + step.run({}) + self.assertEqual(tool._scm.discard_local_changes.call_count, 1) + + def test_local_commits_exist_with_force(self): + tool = MockTool() + tool._scm = Mock() + tool._scm.has_working_directory_changes = lambda: False + tool._scm.has_local_commits = lambda: True + step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True)) + step.run({}) + self.assertEqual(tool._scm.discard_local_changes.call_count, 1) + + def test_local_commits_and_working_changes_exist_with_force(self): + tool = MockTool() + tool._scm = Mock() + tool._scm.has_working_directory_changes = lambda: True + tool._scm.has_local_commits = lambda: True + step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True)) + step.run({}) + self.assertEqual(tool._scm.discard_local_changes.call_count, 1) + + def test_no_changes_exist_with_force(self): + tool = MockTool() + tool._scm = Mock() + tool._scm.has_working_directory_changes = lambda: False + tool._scm.has_local_commits = lambda: False + step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=True)) + step.run({}) + self.assertEqual(tool._scm.discard_local_changes.call_count, 1) + + def test_error_working_changes_exist_without_force(self): + tool = MockTool() + tool._scm = Mock() + tool._scm.has_working_directory_changes = lambda: True + tool._scm.has_local_commits = lambda: False + step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=False)) + self.assertRaises(ScriptError, step.run, {}) + self.assertEqual(tool._scm.discard_local_changes.call_count, 0) + + def test_error_local_commits_exist_without_force(self): + tool = MockTool() + tool._scm = Mock() + tool._scm.has_working_directory_changes = lambda: False + tool._scm.has_local_commits = lambda: True + step = DiscardLocalChanges(tool, MockOptions(clean=True, force_clean=False)) + self.assertRaises(ScriptError, step.run, {}) + self.assertEqual(tool._scm.discard_local_changes.call_count, 0) diff --git a/Tools/Scripts/webkitpy/tool/steps/haslanded.py b/Tools/Scripts/webkitpy/tool/steps/haslanded.py new file mode 100644 index 000000000..b0692b32b --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/steps/haslanded.py @@ -0,0 +1,120 @@ +# 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 cStringIO as StringIO +import logging +import sys +import re +import tempfile + +from webkitpy.tool.steps.abstractstep import AbstractStep +from webkitpy.common.system.executive import Executive, ScriptError +from webkitpy.common.checkout import diff_parser + +from webkitpy.tool.steps import confirmdiff + +_log = logging.getLogger(__name__) + + +class HasLanded(confirmdiff.ConfirmDiff): + + @classmethod + def convert_to_svn(cls, diff): + lines = StringIO.StringIO(diff).readlines() + convert = diff_parser.get_diff_converter(lines) + return "".join(convert(x) for x in lines) + + @classmethod + def strip_change_log(cls, diff): + output = [] + skipping = False + for line in StringIO.StringIO(diff).readlines(): + indexline = re.match("^Index: ([^\\n]*/)?([^/\\n]*)$", line) + if skipping and indexline: + skipping = False + if indexline and indexline.group(2) == "ChangeLog": + skipping = True + if not skipping: + output.append(line) + return "".join(output) + + @classmethod + def diff_diff(cls, diff1, diff2, diff1_suffix, diff2_suffix, executive=None): + # Now this is where it gets complicated, we need to compare our diff to the diff at landed_revision. + diff1_patch = tempfile.NamedTemporaryFile(suffix=diff1_suffix + '.patch') + diff1_patch.write(diff1) + diff1_patch.flush() + + # Check if there are any differences in the patch that don't happen + diff2_patch = tempfile.NamedTemporaryFile(suffix=diff2_suffix + '.patch') + diff2_patch.write(diff2) + diff2_patch.flush() + + # Diff the two diff's together... + if not executive: + executive = Executive() + + try: + return executive.run_command( + ["interdiff", diff1_patch.name, diff2_patch.name], decode_output=False) + except ScriptError, e: + _log.warning("Unable to find interdiff util (part of GNU difftools package) which is required.") + raise + + def run(self, state): + # Check if there are changes first + if not self._tool.scm().local_changes_exist(): + _log.warn("No local changes found, exiting.") + return True + + # Check if there is a SVN revision in the bug from the commit queue + landed_revision = self.cached_lookup(state, "bug").commit_revision() + if not landed_revision: + raise ScriptError("Unable to find landed message in associated bug.") + + # Now this is there it gets complicated, we need to compare our diff to the diff at landed_revision. + landed_diff_bin = self._tool.scm().diff_for_revision(landed_revision) + landed_diff_trimmed = self.strip_change_log(self.convert_to_svn(landed_diff_bin)) + + # Check if there are any differences in the patch that don't happen + local_diff_bin = self._tool.scm().create_patch() + local_diff_trimmed = self.strip_change_log(self.convert_to_svn(local_diff_bin)) + + # Diff the two diff's together... + diff_diff = self.diff_diff(landed_diff_trimmed, local_diff_trimmed, + '-landed', '-local', + executive=self._tool.executive) + + with self._show_pretty_diff(diff_diff) as pretty_diff_file: + if not pretty_diff_file: + self._tool.user.page(diff_diff) + + if self._tool.user.confirm("May I discard local changes?"): + # Discard changes if the user confirmed we should + _log.warn("Discarding changes as requested.") + self._tool.scm().discard_local_changes() diff --git a/Tools/Scripts/webkitpy/tool/steps/haslanded_unittest.py b/Tools/Scripts/webkitpy/tool/steps/haslanded_unittest.py new file mode 100644 index 000000000..3a67029a8 --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/steps/haslanded_unittest.py @@ -0,0 +1,299 @@ +# 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 unittest2 as unittest +import subprocess + +from webkitpy.tool.steps.haslanded import HasLanded + + +class HasLandedTest(unittest.TestCase): + maxDiff = None + + @unittest.skipUnless(subprocess.call('which interdiff', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0, "requires interdiff") + def test_run(self): + # These patches require trailing whitespace to remain valid patches. + diff1 = """\ +Index: a.py +=================================================================== +--- a.py ++++ a.py +@@ -1,3 +1,5 @@ + A + B + C ++D ++E +Index: b.py +=================================================================== +--- b.py 2013-01-21 15:20:59.693887185 +1100 ++++ b.py 2013-01-21 15:22:24.382555711 +1100 +@@ -1,3 +1,5 @@ + 1 + 2 + 3 ++4 ++5 +""" + + diff1_add_line = """\ +Index: a.py +=================================================================== +--- a.py ++++ a.py +@@ -1,3 +1,6 @@ + A + B + C ++D ++E ++F +Index: b.py +=================================================================== +--- b.py ++++ b.py +@@ -1,3 +1,5 @@ + 1 + 2 + 3 ++4 ++5 +""" + + diff1_remove_line = """\ +Index: a.py +=================================================================== +--- a.py ++++ a.py +@@ -1,3 +1,4 @@ + A + B + C ++D +Index: b.py +=================================================================== +--- b.py ++++ b.py +@@ -1,3 +1,5 @@ + 1 + 2 + 3 ++4 ++5 +""" + + diff1_add_file = diff1 + """\ +Index: c.py +=================================================================== +--- c.py ++++ c.py +@@ -1,3 +1,5 @@ + 1 + 2 + 3 ++4 ++5 +""" + + diff1_remove_file = """\ +Index: a.py +=================================================================== +--- a.py ++++ a.py +@@ -1,3 +1,5 @@ + A + B + C ++D ++E +""" + self.assertMultiLineEqual( + HasLanded.diff_diff(diff1, diff1_add_line, '', 'add-line'), + """\ +diff -u a.py a.py +--- a.py ++++ a.py +@@ -5,0 +6 @@ ++F +""") + + self.assertMultiLineEqual( + HasLanded.diff_diff(diff1, diff1_remove_line, '', 'remove-line'), + """\ +diff -u a.py a.py +--- a.py ++++ a.py +@@ -5 +4,0 @@ +-E +""") + self.assertMultiLineEqual( + HasLanded.diff_diff(diff1, diff1_add_file, '', 'add-file'), + """\ +only in patch2: +unchanged: +--- c.py ++++ c.py +@@ -1,3 +1,5 @@ + 1 + 2 + 3 ++4 ++5 +""") + self.assertMultiLineEqual( + HasLanded.diff_diff(diff1, diff1_remove_file, '', 'remove-file'), + """\ +reverted: +--- b.py 2013-01-21 15:22:24.382555711 +1100 ++++ b.py 2013-01-21 15:20:59.693887185 +1100 +@@ -1,5 +1,3 @@ + 1 + 2 + 3 +-4 +-5 +""") + + def test_convert_to_svn_and_strip_change_log(self): + # These patches require trailing whitespace to remain valid patches. + testbefore1 = HasLanded.convert_to_svn("""\ +diff --git a/Tools/ChangeLog b/Tools/ChangeLog +index 219ba72..0390b73 100644 +--- a/Tools/ChangeLog ++++ b/Tools/ChangeLog +@@ -1,3 +1,32 @@ ++2013-01-17 Tim 'mithro' Ansell <mithro@mithis.com> ++ ++ Adding "has-landed" command to webkit-patch which allows a person to ++ Reviewed by NOBODY (OOPS!). ++ + 2013-01-20 Tim 'mithro' Ansell <mithro@mithis.com> + + Extend diff_parser to support the --full-index output. +diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +index 4bf8ec6..3a128cb 100644 +--- a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py ++++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +@@ -28,6 +28,8 @@ ++import re ++ + from .attachment import Attachment + +""") + testafter1 = HasLanded.convert_to_svn("""\ +diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +index 4bf8ec6..3a128cb 100644 +--- a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py ++++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +@@ -28,6 +28,8 @@ ++import re ++ + from .attachment import Attachment + +diff --git a/Tools/ChangeLog b/Tools/ChangeLog +index 219ba72..0390b73 100644 +--- a/Tools/ChangeLog ++++ b/Tools/ChangeLog +@@ -1,3 +1,32 @@ ++2013-01-17 Tim 'mithro' Ansell <mithro@mithis.com> ++ ++ Adding "has-landed" command to webkit-patch which allows a person to ++ Reviewed by NOBODY (OOPS!). ++ + 2013-01-20 Tim 'mithro' Ansell <mithro@mithis.com> + + Extend diff_parser to support the --full-index output. +""") + testexpected1 = """\ +Index: Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +=================================================================== +--- Tools/Scripts/webkitpy/common/net/bugzilla/bug.py ++++ Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +@@ -28,6 +28,8 @@ ++import re ++ + from .attachment import Attachment + +""" + testmiddle1 = HasLanded.convert_to_svn("""\ +diff --git a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +index 4bf8ec6..3a128cb 100644 +--- a/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py ++++ b/Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +@@ -28,6 +28,8 @@ ++import re ++ + from .attachment import Attachment + +diff --git a/ChangeLog b/ChangeLog +index 219ba72..0390b73 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,3 +1,32 @@ ++2013-01-17 Tim 'mithro' Ansell <mithro@mithis.com> ++ ++ Adding "has-landed" command to webkit-patch which allows a person to ++ Reviewed by NOBODY (OOPS!). ++ + 2013-01-20 Tim 'mithro' Ansell <mithro@mithis.com> + + Extend diff_parser to support the --full-index output. +diff --git a/Tools/Scripts/webkitpy/common/other.py b/Tools/Scripts/webkitpy/common/other.py +index 4bf8ec6..3a128cb 100644 +--- a/Tools/Scripts/webkitpy/common/other.py ++++ b/Tools/Scripts/webkitpy/common/other.py +@@ -28,6 +28,8 @@ ++import re ++ + from .attachment import Attachment + +""") + testexpected2 = """\ +Index: Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +=================================================================== +--- Tools/Scripts/webkitpy/common/net/bugzilla/bug.py ++++ Tools/Scripts/webkitpy/common/net/bugzilla/bug.py +@@ -28,6 +28,8 @@ ++import re ++ + from .attachment import Attachment + +Index: Tools/Scripts/webkitpy/common/other.py +=================================================================== +--- Tools/Scripts/webkitpy/common/other.py ++++ Tools/Scripts/webkitpy/common/other.py +@@ -28,6 +28,8 @@ ++import re ++ + from .attachment import Attachment + +""" + + self.assertMultiLineEqual(testexpected1, HasLanded.strip_change_log(testbefore1)) + self.assertMultiLineEqual(testexpected1, HasLanded.strip_change_log(testafter1)) + self.assertMultiLineEqual(testexpected2, HasLanded.strip_change_log(testmiddle1)) diff --git a/Tools/Scripts/webkitpy/tool/steps/options.py b/Tools/Scripts/webkitpy/tool/steps/options.py index c29e59d9c..7eda61459 100644 --- a/Tools/Scripts/webkitpy/tool/steps/options.py +++ b/Tools/Scripts/webkitpy/tool/steps/options.py @@ -33,7 +33,6 @@ class Options(object): build = make_option("--build", action="store_true", dest="build", default=False, help="Build and run run-webkit-tests before committing.") build_style = make_option("--build-style", action="store", dest="build_style", default=None, help="Whether to build debug, release, or both.") cc = make_option("--cc", action="store", type="string", dest="cc", help="Comma-separated list of email addresses to carbon-copy.") - check_builders = make_option("--ignore-builders", action="store_false", dest="check_builders", default=True, help="DEPRECATED: Will be removed any time after 11/01/11.") check_style = make_option("--ignore-style", action="store_false", dest="check_style", default=True, help="Don't check to see if the patch has proper style before uploading.") check_style_filter = make_option("--check-style-filter", action="store", type="string", dest="check_style_filter", default=None, help="Filter style-checker rules (see check-webkit-style --help).") clean = make_option("--no-clean", action="store_false", dest="clean", default=True, help="Don't check if the working directory is clean before applying patches") @@ -57,4 +56,5 @@ class Options(object): suggest_reviewers = make_option("--suggest-reviewers", action="store_true", default=False, help="Offer to CC appropriate reviewers.") test = make_option("--test", action="store_true", dest="test", default=False, help="Run run-webkit-tests before committing.") update = make_option("--no-update", action="store_false", dest="update", default=True, help="Don't update the working directory.") + update_changelogs = make_option("--update-changelogs", action="store_true", dest="update_changelogs", default=False, help="Update existing ChangeLog entries with new date, bug description, and touched files/functions.") changelog_count = make_option("--changelog-count", action="store", type="int", dest="changelog_count", help="Number of changelogs to parse.") diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py index 4d80ab61f..716ab826d 100644 --- a/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py +++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelog.py @@ -27,6 +27,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import logging +import re import sys from webkitpy.common.checkout.changelog import ChangeLog @@ -44,6 +45,7 @@ class PrepareChangeLog(AbstractStep): Options.quiet, Options.email, Options.git_commit, + Options.update_changelogs, ] def _ensure_bug_url(self, state): @@ -52,17 +54,60 @@ class PrepareChangeLog(AbstractStep): bug_id = state.get("bug_id") changelogs = self.cached_lookup(state, "changelogs") for changelog_path in changelogs: - changelog = ChangeLog(changelog_path) + changelog = ChangeLog(changelog_path, self._tool.filesystem) if not changelog.latest_entry().bug_id(): changelog.set_short_description_and_bug_url( self.cached_lookup(state, "bug_title"), self._tool.bugs.bug_url_for_bug_id(bug_id)) + def _resolve_existing_entry(self, changelog_path): + # When this is called, the top entry in the ChangeLog was just created + # by prepare-ChangeLog, as an clean updated version of the one below it. + with self._tool.filesystem.open_text_file_for_reading(changelog_path) as changelog_file: + entries_gen = ChangeLog.parse_entries_from_file(changelog_file) + entries = zip(entries_gen, range(2)) + + if not len(entries): + raise Exception("Expected to find at least two ChangeLog entries in %s but found none." % changelog_path) + if len(entries) == 1: + # If we get here, it probably means we've just rolled over to a + # new CL file, so we don't have anything to resolve. + return + + (new_entry, _), (old_entry, _) = entries + final_entry = self._merge_entries(old_entry, new_entry) + + changelog = ChangeLog(changelog_path, self._tool.filesystem) + changelog.delete_entries(2) + changelog.prepend_text(final_entry) + + def _merge_entries(self, old_entry, new_entry): + final_entry = old_entry.contents() + + final_entry = final_entry.replace(old_entry.date(), new_entry.date(), 1) + + new_bug_desc = new_entry.bug_description() + old_bug_desc = old_entry.bug_description() + if new_bug_desc and old_bug_desc and new_bug_desc != old_bug_desc: + final_entry = final_entry.replace(old_bug_desc, new_bug_desc) + + new_touched = new_entry.touched_functions() + old_touched = old_entry.touched_functions() + if new_touched != old_touched: + if old_entry.is_touched_files_text_clean(): + final_entry = final_entry.replace(old_entry.touched_files_text(), new_entry.touched_files_text()) + else: + final_entry += "\n" + new_entry.touched_files_text() + + return final_entry + "\n" + def run(self, state): if self.cached_lookup(state, "changelogs"): self._ensure_bug_url(state) - return - args = self._tool.port().prepare_changelog_command() + if not self._options.update_changelogs: + return + + args = self._tool.deprecated_port().prepare_changelog_command() if state.get("bug_id"): args.append("--bug=%s" % state["bug_id"]) args.append("--description=%s" % self.cached_lookup(state, 'bug_title')) @@ -75,8 +120,15 @@ class PrepareChangeLog(AbstractStep): args.extend(self._changed_files(state)) try: - self._tool.executive.run_and_throw_if_fail(args, self._options.quiet, cwd=self._tool.scm().checkout_root) + output = self._tool.executive.run_and_throw_if_fail(args, self._options.quiet, cwd=self._tool.scm().checkout_root) except ScriptError, e: _log.error("Unable to prepare ChangeLogs.") sys.exit(1) + + # These are the ChangeLog entries added by prepare-Changelog + changelogs = re.findall(r'Editing the (\S*/ChangeLog) file.', output) + changelogs = set(self._tool.filesystem.join(self._tool.scm().checkout_root, f) for f in changelogs) + for changelog in changelogs & set(self.cached_lookup(state, "changelogs")): + self._resolve_existing_entry(changelog) + self.did_modify_checkout(state) diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py index fc31d1fa9..803f072a3 100644 --- a/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelog_unittest.py @@ -26,32 +26,108 @@ # (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 +import unittest2 as unittest # Do not import changelog_unittest.ChangeLogTest directly as that will cause it to be run again. from webkitpy.common.checkout import changelog_unittest +from webkitpy.common.system.filesystem_mock import MockFileSystem from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.tool.mocktool import MockOptions, MockTool from webkitpy.tool.steps.preparechangelog import PrepareChangeLog - class PrepareChangeLogTest(changelog_unittest.ChangeLogTest): + def test_resolve_existing_entry(self): + step = PrepareChangeLog(MockTool(), MockOptions()) + + headers = ["2013-01-18 Timothy Loh <timloh@chromium.com>\n\n", + "2013-01-20 Timothy Loh <timloh@chromium.com>\n\n", + u"2009-08-17 Tor Arne Vestb\xf8 <vestbo@webkit.org>\n\n", + u"2009-08-18 Tor Arne Vestb\xf8 <vestbo@webkit.org>\n\n", + "2013-01-18 Eric Seidel <eric@webkit.org>\n\n", + "2013-01-20 Eric Seidel <eric@webkit.org>\n\n", + ] + + bug_descs = [" prepare-Changelog should support updating the list of changed files\n", + " webkit-patch upload should support updating the list of changed files\n"] + + bug_url = " https://bugs.webkit.org/show_bug.cgi?id=74358\n\n" + + descriptions = ["", " A description of the changes.\n\n", + " A description.\n\n With some\n line breaks\n\n"] + + changes = [ +""" * Scripts/webkitpy/tool/steps/preparechangelog.py: + (PrepareChangeLog): + (PrepareChangeLog.run):\n\n""", +""" * Scripts/webkitpy/tool/steps/preparechangelog.py: + (PrepareChangeLog._resolve_existing_entry): + (PrepareChangeLog): + (PrepareChangeLog.run):\n\n""", +""" * Scripts/webkitpy/tool/steps/preparechangelog.py: + (PrepareChangeLog): Some annotations + (PrepareChangeLog.run): + More annotations\n\n""", +""" * Scripts/webkitpy/tool/steps/preparechangelog.py: + (PrepareChangeLog): Some annotations + (PrepareChangeLog.run): + More annotations + + * Scripts/webkitpy/tool/steps/preparechangelog.py: + (PrepareChangeLog._resolve_existing_entry): + (PrepareChangeLog): + (PrepareChangeLog.run):\n\n""", + ] + + def make_entry(indices): + a, b, c, d = indices + return headers[a] + bug_descs[b] + bug_url + descriptions[c] + changes[d] + + test_cases = [((0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)), + ((0, 0, 0, 0), (0, 0, 1, 0), (0, 0, 1, 0)), + ((1, 0, 0, 0), (0, 0, 2, 0), (1, 0, 2, 0)), + ((0, 1, 0, 0), (0, 0, 1, 0), (0, 1, 1, 0)), + ((0, 0, 0, 1), (0, 0, 0, 0), (0, 0, 0, 1)), + ((0, 0, 0, 0), (0, 0, 1, 1), (0, 0, 1, 0)), + ((0, 0, 0, 0), (0, 0, 2, 2), (0, 0, 2, 2)), + ((0, 0, 0, 1), (0, 0, 1, 2), (0, 0, 1, 3)), + ((1, 1, 0, 1), (0, 0, 0, 2), (1, 1, 0, 3)), + ((3, 0, 0, 0), (2, 0, 1, 0), (3, 0, 1, 0)), + ((4, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)), + ((5, 0, 0, 0), (0, 0, 0, 0), (1, 0, 0, 0)), + ((0, 0, 0, 0), (4, 0, 0, 0), (4, 0, 0, 0)), + ((1, 0, 0, 0), (4, 0, 0, 0), (5, 0, 0, 0)), + ] + + for new, old, final in test_cases: + new_entry = make_entry(new) + old_entry = make_entry(old) + start_file = new_entry + old_entry + self._rolled_over_footer + + final_entry = make_entry(final) + end_file = final_entry + self._rolled_over_footer + + path = "ChangeLog" + step._tool.filesystem = MockFileSystem() + step._tool.filesystem.write_text_file(path, start_file) + step._resolve_existing_entry(path) + actual_output = step._tool.filesystem.read_text_file(path) + self.assertEquals(actual_output, end_file) + def test_ensure_bug_url(self): - # FIXME: This should use a MockFileSystem instead of a real FileSystem. capture = OutputCapture() step = PrepareChangeLog(MockTool(), MockOptions()) changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog) - changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8")) + changelog_path = "ChangeLog" state = { "bug_title": "Example title", "bug_id": 1234, "changelogs": [changelog_path], } - capture.assert_outputs(self, step.run, [state]) - actual_contents = self._read_file_contents(changelog_path, "utf-8") + step._tool.filesystem = MockFileSystem() + step._tool.filesystem.write_text_file(changelog_path, changelog_contents) + capture.assert_outputs(self, step._ensure_bug_url, [state]) + actual_contents = step._tool.filesystem.read_text_file(changelog_path) expected_message = "Example title\n http://example.com/1234" expected_contents = changelog_contents.replace("Need a short description (OOPS!).\n Need the bug URL (OOPS!).", expected_message) - os.remove(changelog_path) - self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines()) + self.assertEqual(actual_contents, expected_contents) diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py index 95a99c320..82e7b0252 100644 --- a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py +++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert.py @@ -48,7 +48,7 @@ class PrepareChangeLogForRevert(AbstractStep): def run(self, state): # This could move to prepare-ChangeLog by adding a --revert= option. - self._tool.executive.run_and_throw_if_fail(self._tool.port().prepare_changelog_command(), cwd=self._tool.scm().checkout_root) + self._tool.executive.run_and_throw_if_fail(self._tool.deprecated_port().prepare_changelog_command(), cwd=self._tool.scm().checkout_root) changelog_paths = self._tool.checkout().modified_changelogs(git_commit=None) bug_url = self._tool.bugs.bug_url_for_bug_id(state["bug_id"]) if state["bug_id"] else None message = self._message_for_revert(state["revision_list"], state["reason"], bug_url) diff --git a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py index b82cb4aa2..3ec6e9a60 100644 --- a/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/preparechangelogforrevert_unittest.py @@ -26,27 +26,17 @@ # (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 codecs -import os -import tempfile -import unittest +import unittest2 as unittest # Do not import changelog_unittest.ChangeLogTest directly as that will cause it to be run again. from webkitpy.common.checkout import changelog_unittest from webkitpy.common.checkout.changelog import ChangeLog +from webkitpy.common.system.filesystem_mock import MockFileSystem from webkitpy.tool.steps.preparechangelogforrevert import * class UpdateChangeLogsForRevertTest(unittest.TestCase): - @staticmethod - def _write_tmp_file_with_contents(byte_array): - assert(isinstance(byte_array, str)) - (file_descriptor, file_path) = tempfile.mkstemp() # NamedTemporaryFile always deletes the file on close in python < 2.6 - with os.fdopen(file_descriptor, "w") as file: - file.write(byte_array) - return file_path - _revert_entry_with_bug_url = '''2009-08-19 Eric Seidel <eric@webkit.org> Unreviewed, rolling out r12345. @@ -110,13 +100,13 @@ class UpdateChangeLogsForRevertTest(unittest.TestCase): def _assert_message_for_revert_output(self, args, expected_entry): changelog_contents = u"%s\n%s" % (changelog_unittest.ChangeLogTest._new_entry_boilerplate, changelog_unittest.ChangeLogTest._example_changelog) - changelog_path = self._write_tmp_file_with_contents(changelog_contents.encode("utf-8")) - changelog = ChangeLog(changelog_path) + changelog_path = "ChangeLog" + fs = MockFileSystem({changelog_path: changelog_contents.encode("utf-8")}) + changelog = ChangeLog(changelog_path, fs) changelog.update_with_unreviewed_message(PrepareChangeLogForRevert._message_for_revert(*args)) actual_entry = changelog.latest_entry() - os.remove(changelog_path) - self.assertEqual(actual_entry.contents(), expected_entry) - self.assertEqual(actual_entry.reviewer_text(), None) + self.assertMultiLineEqual(actual_entry.contents(), expected_entry) + self.assertIsNone(actual_entry.reviewer_text()) # These checks could be removed to allow this to work on other entries: self.assertEqual(actual_entry.author_name(), "Eric Seidel") self.assertEqual(actual_entry.author_email(), "eric@webkit.org") diff --git a/Tools/Scripts/webkitpy/tool/steps/runtests.py b/Tools/Scripts/webkitpy/tool/steps/runtests.py index 6dc90f92c..a45628b2d 100644 --- a/Tools/Scripts/webkitpy/tool/steps/runtests.py +++ b/Tools/Scripts/webkitpy/tool/steps/runtests.py @@ -1,9 +1,9 @@ # 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 @@ -13,7 +13,7 @@ # * 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 @@ -27,7 +27,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import logging - +import os +import platform +import sys from webkitpy.tool.steps.abstractstep import AbstractStep from webkitpy.tool.steps.options import Options from webkitpy.common.system.executive import ScriptError @@ -41,6 +43,7 @@ class RunTests(AbstractStep): @classmethod def options(cls): return AbstractStep.options() + [ + Options.build_style, Options.test, Options.non_interactive, Options.quiet, @@ -53,44 +56,59 @@ class RunTests(AbstractStep): if not self._options.non_interactive: # FIXME: We should teach the commit-queue and the EWS how to run these tests. - python_unittests_command = self._tool.port().run_python_unittests_command() + python_unittests_command = self._tool.deprecated_port().run_python_unittests_command() if python_unittests_command: _log.info("Running Python unit tests") self._tool.executive.run_and_throw_if_fail(python_unittests_command, cwd=self._tool.scm().checkout_root) - perl_unittests_command = self._tool.port().run_perl_unittests_command() + perl_unittests_command = self._tool.deprecated_port().run_perl_unittests_command() if perl_unittests_command: _log.info("Running Perl unit tests") self._tool.executive.run_and_throw_if_fail(perl_unittests_command, cwd=self._tool.scm().checkout_root) - javascriptcore_tests_command = self._tool.port().run_javascriptcore_tests_command() + javascriptcore_tests_command = self._tool.deprecated_port().run_javascriptcore_tests_command() if javascriptcore_tests_command: _log.info("Running JavaScriptCore tests") self._tool.executive.run_and_throw_if_fail(javascriptcore_tests_command, quiet=True, cwd=self._tool.scm().checkout_root) - webkit_unit_tests_command = self._tool.port().run_webkit_unit_tests_command() + bindings_tests_command = self._tool.deprecated_port().run_bindings_tests_command() + if bindings_tests_command: + _log.info("Running bindings generation tests") + args = bindings_tests_command + try: + self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root) + except ScriptError, e: + _log.info("Error running run-bindings-tests: %s" % e.message_with_output()) + + webkit_unit_tests_command = self._tool.deprecated_port().run_webkit_unit_tests_command() if webkit_unit_tests_command: _log.info("Running WebKit unit tests") args = webkit_unit_tests_command - if self._options.non_interactive: - args.append("--gtest_output=xml:%s/webkit_unit_tests_output.xml" % self._tool.port().results_directory) try: self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root) except ScriptError, e: _log.info("Error running webkit_unit_tests: %s" % e.message_with_output()) + _log.info("Running run-webkit-tests") - args = self._tool.port().run_webkit_tests_command() + args = self._tool.deprecated_port().run_webkit_tests_command() if self._options.non_interactive: args.extend([ "--no-new-test-results", - "--no-launch-safari", - "--skip-failing-tests", + "--no-show-results", "--exit-after-n-failures=%s" % self.NON_INTERACTIVE_FAILURE_LIMIT_COUNT, - "--results-directory=%s" % self._tool.port().results_directory, - "--quiet", ]) + # old-run-webkit-tests does not support --skip-failing-tests + # Using --quiet one Windows fails when we try to use /dev/null, disabling for now until we find a fix + if sys.platform != "cygwin": + args.append("--quiet") + args.append("--skip-failing-tests") + else: + args.append("--no-build"); + if self._options.quiet: args.append("--quiet") + self._tool.executive.run_and_throw_if_fail(args, cwd=self._tool.scm().checkout_root) + diff --git a/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py b/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py index 78a867b36..ef8920e9b 100644 --- a/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/runtests_unittest.py @@ -26,7 +26,9 @@ # (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 platform +import sys +import unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.tool.mocktool import MockOptions, MockTool @@ -38,9 +40,22 @@ class RunTestsTest(unittest.TestCase): tool._deprecated_port.run_python_unittests_command = lambda: None tool._deprecated_port.run_perl_unittests_command = lambda: None step = RunTests(tool, MockOptions(test=True, non_interactive=True, quiet=False)) - expected_logs = """Running WebKit unit tests -MOCK run_and_throw_if_fail: ['mock-run-webkit-unit-tests', '--gtest_output=xml:/mock-results/webkit_unit_tests_output.xml'], cwd=/mock-checkout + + if sys.platform != "cygwin": + expected_logs = """Running bindings generation tests +MOCK run_and_throw_if_fail: ['mock-run-bindings-tests'], cwd=/mock-checkout +Running WebKit unit tests +MOCK run_and_throw_if_fail: ['mock-run-webkit-unit-tests'], cwd=/mock-checkout +Running run-webkit-tests +MOCK run_and_throw_if_fail: ['mock-run-webkit-tests', '--no-new-test-results', '--no-show-results', '--exit-after-n-failures=30', '--quiet', '--skip-failing-tests'], cwd=/mock-checkout +""" + else: + expected_logs = """Running bindings generation tests +MOCK run_and_throw_if_fail: ['mock-run-bindings-tests'], cwd=/mock-checkout +Running WebKit unit tests +MOCK run_and_throw_if_fail: ['mock-run-webkit-unit-tests'], cwd=/mock-checkout Running run-webkit-tests -MOCK run_and_throw_if_fail: ['mock-run-webkit-tests', '--no-new-test-results', '--no-launch-safari', '--skip-failing-tests', '--exit-after-n-failures=30', '--results-directory=/mock-results', '--quiet'], cwd=/mock-checkout +MOCK run_and_throw_if_fail: ['mock-run-webkit-tests', '--no-new-test-results', '--no-show-results', '--exit-after-n-failures=30', '--no-build'], cwd=/mock-checkout """ + OutputCapture().assert_outputs(self, step.run, [{}], expected_logs=expected_logs) diff --git a/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py b/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py index c4ea47b4d..7172ba7f5 100644 --- a/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/steps_unittest.py @@ -26,7 +26,7 @@ # (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 unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.common.config.ports import DeprecatedPort @@ -99,10 +99,9 @@ class StepsTest(unittest.TestCase): mock_options = self._step_options() mock_options.non_interactive = False step = steps.RunTests(MockTool(log_executive=True), mock_options) - # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment. - mock_port = DeprecatedPort() tool = MockTool(log_executive=True) - tool.port = lambda: mock_port + # FIXME: We shouldn't use a real port-object here, but there is too much to mock at the moment. + tool._deprecated_port = DeprecatedPort() step = steps.RunTests(tool, mock_options) expected_logs = """Running Python unit tests MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitpy'], cwd=/mock-checkout @@ -110,6 +109,8 @@ Running Perl unit tests MOCK run_and_throw_if_fail: ['Tools/Scripts/test-webkitperl'], cwd=/mock-checkout Running JavaScriptCore tests MOCK run_and_throw_if_fail: ['Tools/Scripts/run-javascriptcore-tests'], cwd=/mock-checkout +Running bindings generation tests +MOCK run_and_throw_if_fail: ['Tools/Scripts/run-bindings-tests'], cwd=/mock-checkout Running run-webkit-tests MOCK run_and_throw_if_fail: ['Tools/Scripts/run-webkit-tests', '--quiet'], cwd=/mock-checkout """ diff --git a/Tools/Scripts/webkitpy/tool/steps/suggestreviewers.py b/Tools/Scripts/webkitpy/tool/steps/suggestreviewers.py index 76bef35ac..40a24829b 100644 --- a/Tools/Scripts/webkitpy/tool/steps/suggestreviewers.py +++ b/Tools/Scripts/webkitpy/tool/steps/suggestreviewers.py @@ -42,9 +42,12 @@ class SuggestReviewers(AbstractStep): if not self._options.suggest_reviewers: return - reviewers = self._tool.checkout().suggested_reviewers(self._options.git_commit, self._changed_files(state)) + reviewers = self._tool.checkout().suggested_reviewers(self._options.git_commit, self._changed_files(state))[:5] print "The following reviewers have recently modified files in your patch:" - print "\n".join([reviewer.full_name for reviewer in reviewers]) + print ", ".join([reviewer.full_name for reviewer in reviewers]) + + if not state.get('bug_id'): + return if not self._tool.user.confirm("Would you like to CC them?"): return reviewer_emails = [reviewer.bugzilla_email() for reviewer in reviewers] diff --git a/Tools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py b/Tools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py index 42254c86b..fc096f118 100644 --- a/Tools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/suggestreviewers_unittest.py @@ -26,7 +26,7 @@ # (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 unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.tool.mocktool import MockOptions, MockTool diff --git a/Tools/Scripts/webkitpy/tool/steps/update.py b/Tools/Scripts/webkitpy/tool/steps/update.py index 0737ebcd0..f70354078 100644 --- a/Tools/Scripts/webkitpy/tool/steps/update.py +++ b/Tools/Scripts/webkitpy/tool/steps/update.py @@ -50,5 +50,5 @@ class Update(AbstractStep): self._tool.executive.run_and_throw_if_fail(self._update_command(), quiet=self._options.quiet, cwd=self._tool.scm().checkout_root) def _update_command(self): - update_command = self._tool.port().update_webkit_command(self._options.non_interactive) + update_command = self._tool.deprecated_port().update_webkit_command(self._options.non_interactive) return update_command diff --git a/Tools/Scripts/webkitpy/tool/steps/update_unittest.py b/Tools/Scripts/webkitpy/tool/steps/update_unittest.py index c1a934db5..49d6b4098 100644 --- a/Tools/Scripts/webkitpy/tool/steps/update_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/update_unittest.py @@ -26,9 +26,9 @@ # (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 unittest2 as unittest -from webkitpy.common.config.ports import ChromiumPort, ChromiumAndroidPort, ChromiumXVFBPort +from webkitpy.common.config.ports import MacPort, MacWK2Port from webkitpy.tool.mocktool import MockOptions, MockTool from webkitpy.tool.steps.update import Update @@ -41,14 +41,11 @@ class UpdateTest(unittest.TestCase): step = Update(tool, options) self.assertEqual(["mock-update-webkit"], step._update_command()) - tool._deprecated_port = ChromiumPort() - self.assertEqual(["Tools/Scripts/update-webkit", "--chromium", "--force-update"], step._update_command()) + tool._deprecated_port = MacPort() + self.assertEqual(["Tools/Scripts/update-webkit"], step._update_command()) - tool._deprecated_port = ChromiumXVFBPort() - self.assertEqual(["Tools/Scripts/update-webkit", "--chromium", "--force-update"], step._update_command()) - - tool._deprecated_port = ChromiumAndroidPort() - self.assertEqual(["Tools/Scripts/update-webkit", "--chromium", "--force-update", "--chromium-android"], step._update_command()) + tool._deprecated_port = MacWK2Port() + self.assertEqual(["Tools/Scripts/update-webkit"], step._update_command()) def test_update_command_interactive(self): tool = MockTool() @@ -56,11 +53,8 @@ class UpdateTest(unittest.TestCase): step = Update(tool, options) self.assertEqual(["mock-update-webkit"], step._update_command()) - tool._deprecated_port = ChromiumPort() - self.assertEqual(["Tools/Scripts/update-webkit", "--chromium"], step._update_command()) - - tool._deprecated_port = ChromiumXVFBPort() - self.assertEqual(["Tools/Scripts/update-webkit", "--chromium"], step._update_command()) + tool._deprecated_port = MacPort() + self.assertEqual(["Tools/Scripts/update-webkit"], step._update_command()) - tool._deprecated_port = ChromiumAndroidPort() - self.assertEqual(["Tools/Scripts/update-webkit", "--chromium", "--chromium-android"], step._update_command()) + tool._deprecated_port = MacWK2Port() + self.assertEqual(["Tools/Scripts/update-webkit"], step._update_command()) diff --git a/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py b/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py index 3182cf3ab..d433e3f21 100644 --- a/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/updatechangelogswithreview_unittest.py @@ -26,7 +26,7 @@ # (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 unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.tool.mocktool import MockOptions, MockTool diff --git a/Tools/Scripts/webkitpy/tool/steps/updatechromiumdeps.py b/Tools/Scripts/webkitpy/tool/steps/updatechromiumdeps.py deleted file mode 100644 index 23d861bfc..000000000 --- a/Tools/Scripts/webkitpy/tool/steps/updatechromiumdeps.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) 2011 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 logging -import sys -import urllib2 - -from webkitpy.tool.steps.abstractstep import AbstractStep -from webkitpy.tool.steps.options import Options -from webkitpy.common.config import urls - -_log = logging.getLogger(__name__) - - -class UpdateChromiumDEPS(AbstractStep): - @classmethod - def options(cls): - return AbstractStep.options() + [ - Options.non_interactive, - ] - - # Notice that this method throws lots of exciting exceptions! - def _fetch_last_known_good_revision(self): - return int(urllib2.urlopen(urls.chromium_lkgr_url).read()) - - def _validate_revisions(self, current_chromium_revision, new_chromium_revision): - if new_chromium_revision < current_chromium_revision: - message = "Current Chromium DEPS revision %s is newer than %s." % (current_chromium_revision, new_chromium_revision) - if self._options.non_interactive: - _log.error(message) - sys.exit(1) - _log.info(message) - new_chromium_revision = self._tool.user.prompt("Enter new chromium revision (enter nothing to cancel):\n") - try: - new_chromium_revision = int(new_chromium_revision) - except ValueError, TypeError: - new_chromium_revision = None - if not new_chromium_revision: - _log.error("Unable to update Chromium DEPS") - sys.exit(1) - - def run(self, state): - # Note that state["chromium_revision"] must be defined, but can be None. - new_chromium_revision = state["chromium_revision"] - if not new_chromium_revision: - new_chromium_revision = self._fetch_last_known_good_revision() - - deps = self._tool.checkout().chromium_deps() - current_chromium_revision = deps.read_variable("chromium_rev") - self._validate_revisions(current_chromium_revision, new_chromium_revision) - _log.info("Updating Chromium DEPS to %s" % new_chromium_revision) - deps.write_variable("chromium_rev", new_chromium_revision) diff --git a/Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py index 061baa5ec..e77e5c01e 100644 --- a/Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py +++ b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs.py @@ -29,6 +29,7 @@ import logging import sys +from optparse import make_option from webkitpy.tool.steps.abstractstep import AbstractStep from webkitpy.tool.steps.options import Options from webkitpy.common.checkout.diff_parser import DiffParser @@ -42,12 +43,11 @@ class ValidateChangeLogs(AbstractStep): @classmethod def options(cls): return AbstractStep.options() + [ + make_option("--check-oops", action="store_true", default=False, help="Check there are no OOPS left in change log"), Options.non_interactive, ] 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 @@ -64,6 +64,12 @@ class ValidateChangeLogs(AbstractStep): return True return False + def _changelog_contains_oops(self, diff_file): + for diff_line in diff_file.lines: + if 'OOPS!' in diff_line[2]: + return True + return False + def run(self, state): changed_files = self.cached_lookup(state, "changed_files") for filename in changed_files: @@ -76,6 +82,11 @@ class ValidateChangeLogs(AbstractStep): diff = self._tool.scm().diff_for_file(filename) parsed_diff = DiffParser(diff.splitlines()) for filename, diff_file in parsed_diff.files.items(): + if not self._tool.checkout().is_path_to_changelog(diff_file.filename): + continue if not self._check_changelog_diff(diff_file): _log.error("ChangeLog entry in %s is not at the top of the file." % diff_file.filename) sys.exit(1) + if self._options.check_oops and self._changelog_contains_oops(diff_file): + _log.error("ChangeLog entry in %s contains OOPS!." % diff_file.filename) + sys.exit(1) diff --git a/Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py index c3b723ed1..50ecc4646 100644 --- a/Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py +++ b/Tools/Scripts/webkitpy/tool/steps/validatechangelogs_unittest.py @@ -26,7 +26,7 @@ # (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 unittest2 as unittest from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.thirdparty.mock import Mock @@ -38,7 +38,6 @@ class ValidateChangeLogsTest(unittest.TestCase): def _assert_start_line_produces_output(self, start_line, should_fail=False, non_interactive=False): tool = MockTool() - tool._checkout.is_path_to_changelog = lambda path: True step = ValidateChangeLogs(tool, MockOptions(git_commit=None, non_interactive=non_interactive)) diff_file = Mock() diff_file.filename = "mock/ChangeLog" @@ -56,3 +55,15 @@ class ValidateChangeLogsTest(unittest.TestCase): self._assert_start_line_produces_output(1, non_interactive=False) self._assert_start_line_produces_output(8, non_interactive=True, should_fail=True) + + def test_changelog_contains_oops(self): + tool = MockTool() + tool._checkout.is_path_to_changelog = lambda path: True + step = ValidateChangeLogs(tool, MockOptions(git_commit=None, non_interactive=True, check_oops=True)) + diff_file = Mock() + diff_file.filename = "mock/ChangeLog" + diff_file.lines = [(1, 1, "foo"), (2, 2, "bar OOPS! bar"), (3, 3, "foo")] + self.assertTrue(OutputCapture().assert_outputs(self, step._changelog_contains_oops, [diff_file], expected_logs='')) + + diff_file.lines = [(1, 1, "foo"), (2, 2, "bar OOPS bar"), (3, 3, "foo")] + self.assertFalse(OutputCapture().assert_outputs(self, step._changelog_contains_oops, [diff_file], expected_logs='')) |
