1# Copyright (C) 2010 Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above 10# copyright notice, this list of conditions and the following disclaimer 11# in the documentation and/or other materials provided with the 12# distribution. 13# * Neither the name of Google Inc. nor the names of its 14# contributors may be used to endorse or promote products derived from 15# this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29from __future__ import with_statement 30 31import codecs 32import os 33import shutil 34import tempfile 35import unittest 36 37from webkitpy.common.checkout.api import Checkout 38from webkitpy.common.checkout.changelog import ChangeLogEntry 39from webkitpy.common.checkout.scm import detect_scm_system, CommitMessage 40from webkitpy.common.system.outputcapture import OutputCapture 41from webkitpy.common.system.executive import ScriptError 42from webkitpy.thirdparty.mock import Mock 43 44 45# FIXME: Copied from scm_unittest.py 46def write_into_file_at_path(file_path, contents, encoding="utf-8"): 47 with codecs.open(file_path, "w", encoding) as file: 48 file.write(contents) 49 50 51_changelog1entry1 = u"""2010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org> 52 53 Unreviewed build fix to un-break webkit-patch land. 54 55 Move commit_message_for_this_commit from scm to checkout 56 https://bugs.webkit.org/show_bug.cgi?id=36629 57 58 * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage 59""" 60_changelog1entry2 = u"""2010-03-25 Adam Barth <abarth@webkit.org> 61 62 Reviewed by Eric Seidel. 63 64 Move commit_message_for_this_commit from scm to checkout 65 https://bugs.webkit.org/show_bug.cgi?id=36629 66 67 * Scripts/webkitpy/common/checkout/api.py: 68""" 69_changelog1 = u"\n".join([_changelog1entry1, _changelog1entry2]) 70_changelog2 = u"""2010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org> 71 72 Unreviewed build fix to un-break webkit-patch land. 73 74 Second part of this complicated change. 75 76 * Path/To/Complicated/File: Added. 77 782010-03-25 Adam Barth <abarth@webkit.org> 79 80 Reviewed by Eric Seidel. 81 82 Filler change. 83""" 84 85class CommitMessageForThisCommitTest(unittest.TestCase): 86 expected_commit_message = u"""2010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org> 87 88 Unreviewed build fix to un-break webkit-patch land. 89 90 Move commit_message_for_this_commit from scm to checkout 91 https://bugs.webkit.org/show_bug.cgi?id=36629 92 93 * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage 942010-03-25 Tor Arne Vestb\u00f8 <vestbo@webkit.org> 95 96 Unreviewed build fix to un-break webkit-patch land. 97 98 Second part of this complicated change. 99 100 * Path/To/Complicated/File: Added. 101""" 102 103 def setUp(self): 104 self.temp_dir = tempfile.mkdtemp(suffix="changelogs") 105 self.old_cwd = os.getcwd() 106 os.chdir(self.temp_dir) 107 write_into_file_at_path("ChangeLog1", _changelog1) 108 write_into_file_at_path("ChangeLog2", _changelog2) 109 110 def tearDown(self): 111 shutil.rmtree(self.temp_dir, ignore_errors=True) 112 os.chdir(self.old_cwd) 113 114 # FIXME: This should not need to touch the file system, however 115 # ChangeLog is difficult to mock at current. 116 def test_commit_message_for_this_commit(self): 117 checkout = Checkout(None) 118 checkout.modified_changelogs = lambda git_commit, changed_files=None: ["ChangeLog1", "ChangeLog2"] 119 output = OutputCapture() 120 expected_stderr = "Parsing ChangeLog: ChangeLog1\nParsing ChangeLog: ChangeLog2\n" 121 commit_message = output.assert_outputs(self, checkout.commit_message_for_this_commit, 122 kwargs={"git_commit": None}, expected_stderr=expected_stderr) 123 self.assertEqual(commit_message.message(), self.expected_commit_message) 124 125 126class CheckoutTest(unittest.TestCase): 127 def test_latest_entry_for_changelog_at_revision(self): 128 scm = Mock() 129 def mock_contents_at_revision(changelog_path, revision): 130 self.assertEqual(changelog_path, "foo") 131 self.assertEqual(revision, "bar") 132 # contents_at_revision is expected to return a byte array (str) 133 # so we encode our unicode ChangeLog down to a utf-8 stream. 134 # The ChangeLog utf-8 decoding should ignore invalid codepoints. 135 invalid_utf8 = "\255" 136 return _changelog1.encode("utf-8") + invalid_utf8 137 scm.contents_at_revision = mock_contents_at_revision 138 checkout = Checkout(scm) 139 entry = checkout._latest_entry_for_changelog_at_revision("foo", "bar") 140 self.assertEqual(entry.contents(), _changelog1entry1) 141 142 # FIXME: This tests a hack around our current changed_files handling. 143 # Right now changelog_entries_for_revision tries to fetch deleted files 144 # from revisions, resulting in a ScriptError exception. Test that we 145 # recover from those and still return the other ChangeLog entries. 146 def test_changelog_entries_for_revision(self): 147 scm = Mock() 148 scm.changed_files_for_revision = lambda revision: ['foo/ChangeLog', 'bar/ChangeLog'] 149 checkout = Checkout(scm) 150 151 def mock_latest_entry_for_changelog_at_revision(path, revision): 152 if path == "foo/ChangeLog": 153 return 'foo' 154 raise ScriptError() 155 156 checkout._latest_entry_for_changelog_at_revision = mock_latest_entry_for_changelog_at_revision 157 158 # Even though fetching one of the entries failed, the other should succeed. 159 entries = checkout.changelog_entries_for_revision(1) 160 self.assertEqual(len(entries), 1) 161 self.assertEqual(entries[0], 'foo') 162 163 def test_commit_info_for_revision(self): 164 scm = Mock() 165 scm.committer_email_for_revision = lambda revision: "committer@example.com" 166 checkout = Checkout(scm) 167 checkout.changelog_entries_for_revision = lambda revision: [ChangeLogEntry(_changelog1entry1)] 168 commitinfo = checkout.commit_info_for_revision(4) 169 self.assertEqual(commitinfo.bug_id(), 36629) 170 self.assertEqual(commitinfo.author_name(), u"Tor Arne Vestb\u00f8") 171 self.assertEqual(commitinfo.author_email(), "vestbo@webkit.org") 172 self.assertEqual(commitinfo.reviewer_text(), None) 173 self.assertEqual(commitinfo.reviewer(), None) 174 self.assertEqual(commitinfo.committer_email(), "committer@example.com") 175 self.assertEqual(commitinfo.committer(), None) 176 177 checkout.changelog_entries_for_revision = lambda revision: [] 178 self.assertEqual(checkout.commit_info_for_revision(1), None) 179 180 def test_bug_id_for_revision(self): 181 scm = Mock() 182 scm.committer_email_for_revision = lambda revision: "committer@example.com" 183 checkout = Checkout(scm) 184 checkout.changelog_entries_for_revision = lambda revision: [ChangeLogEntry(_changelog1entry1)] 185 self.assertEqual(checkout.bug_id_for_revision(4), 36629) 186 187 def test_bug_id_for_this_commit(self): 188 scm = Mock() 189 checkout = Checkout(scm) 190 checkout.commit_message_for_this_commit = lambda git_commit, changed_files=None: CommitMessage(ChangeLogEntry(_changelog1entry1).contents().splitlines()) 191 self.assertEqual(checkout.bug_id_for_this_commit(git_commit=None), 36629) 192 193 def test_modified_changelogs(self): 194 scm = Mock() 195 scm.checkout_root = "/foo/bar" 196 scm.changed_files = lambda git_commit: ["file1", "ChangeLog", "relative/path/ChangeLog"] 197 checkout = Checkout(scm) 198 expected_changlogs = ["/foo/bar/ChangeLog", "/foo/bar/relative/path/ChangeLog"] 199 self.assertEqual(checkout.modified_changelogs(git_commit=None), expected_changlogs) 200 201 def test_suggested_reviewers(self): 202 def mock_changelog_entries_for_revision(revision): 203 if revision % 2 == 0: 204 return [ChangeLogEntry(_changelog1entry1)] 205 return [ChangeLogEntry(_changelog1entry2)] 206 207 def mock_revisions_changing_file(path, limit=5): 208 if path.endswith("ChangeLog"): 209 return [3] 210 return [4, 8] 211 212 scm = Mock() 213 scm.checkout_root = "/foo/bar" 214 scm.changed_files = lambda git_commit: ["file1", "file2", "relative/path/ChangeLog"] 215 scm.revisions_changing_file = mock_revisions_changing_file 216 checkout = Checkout(scm) 217 checkout.changelog_entries_for_revision = mock_changelog_entries_for_revision 218 reviewers = checkout.suggested_reviewers(git_commit=None) 219 reviewer_names = [reviewer.full_name for reviewer in reviewers] 220 self.assertEqual(reviewer_names, [u'Tor Arne Vestb\xf8']) 221