Update tools and configurations after renaming Source/WebKit to Source/WebKitLegacy.
[WebKit-https.git] / Tools / Scripts / webkitpy / common / checkout / checkout_unittest.py
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
29 import codecs
30 import os
31 import shutil
32 import tempfile
33 import unittest
34
35 from .checkout import Checkout
36 from .changelog import ChangeLogEntry
37 from .scm import CommitMessage, SCMDetector
38 from .scm.scm_mock import MockSCM
39 from webkitpy.common.webkit_finder import WebKitFinder
40 from webkitpy.common.system.executive import Executive, ScriptError
41 from webkitpy.common.system.filesystem import FileSystem  # FIXME: This should not be needed.
42 from webkitpy.common.system.filesystem_mock import MockFileSystem
43 from webkitpy.common.system.executive_mock import MockExecutive
44 from webkitpy.common.system.outputcapture import OutputCapture
45 from webkitpy.thirdparty.mock import Mock
46
47
48 _changelog1entry1 = u"""2010-03-25  Tor Arne Vestb\u00f8  <vestbo@webkit.org>
49
50         Unreviewed build fix to un-break webkit-patch land.
51
52         Move commit_message_for_this_commit from scm to checkout
53         https://bugs.webkit.org/show_bug.cgi?id=36629
54
55         * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage
56 """
57 _changelog1entry2 = u"""2010-03-25  Adam Barth  <abarth@webkit.org>
58
59         Reviewed by Eric Seidel.
60
61         Move commit_message_for_this_commit from scm to checkout
62         https://bugs.webkit.org/show_bug.cgi?id=36629
63
64         * Scripts/webkitpy/common/checkout/api.py:
65 """
66 _changelog1 = u"\n".join([_changelog1entry1, _changelog1entry2])
67 _changelog2 = u"""2010-03-25  Tor Arne Vestb\u00f8  <vestbo@webkit.org>
68
69         Unreviewed build fix to un-break webkit-patch land.
70
71         Second part of this complicated change by me, Tor Arne Vestb\u00f8!
72
73         * Path/To/Complicated/File: Added.
74
75 2010-03-25  Adam Barth  <abarth@webkit.org>
76
77         Reviewed by Eric Seidel.
78
79         Filler change.
80 """
81
82 _changelog3 = u"""2014-07-17  David Kilzer  <ddkilzer@apple.com>
83
84         SECTORDER_FLAGS should be defined in target's xcconfig file, not Base.xcconfig
85         <http://webkit.org/b/135006>
86
87         Reviewed by Darin Adler.
88
89         * WebKit.xcodeproj/project.pbxproj: Remove references to unused
90         WebKit.xcconfig file.
91 """
92
93 _changelog4 = u"""2014-07-17  David Kilzer  <ddkilzer@apple.com>
94
95         SECTORDER_FLAGS should be defined in target's xcconfig file, not Base.xcconfig
96         <http://webkit.org/b/135006>
97
98         Reviewed by Darin Adler.
99
100         * Path/To/Complicated/File: Added.
101 """
102
103 _changelog5 = u"""2014-07-17  David Kilzer  <ddkilzer@apple.com>
104
105         SECTORDER_FLAGS should be defined in target's xcconfig file, not Base.xcconfig
106         <http://webkit.org/b/135006>
107
108         Reviewed by Darin Adler.
109
110         Filler change.
111
112         * Path/To/Complicated/File: Added.
113 """
114
115 _changelog6 = u"""2014-06-23  Daniel Bates  <dabates@apple.com>
116
117         Rolling out r170340 and r170339.
118
119         Changeset r170339 broke the Apple Windows Debug and Release builds.
120
121         Reverted changesets:
122
123         "[Win] Build fix after r134209"
124         http://trac.webkit.org/changeset/170340
125
126         "[Win] Clean up and refactor WinLauncher"
127         https://bugs.webkit.org/show_bug.cgi?id=134209
128         http://trac.webkit.org/changeset/170339
129 """
130
131
132 class CommitMessageForThisCommitTest(unittest.TestCase):
133     def setUp(self):
134         # FIXME: This should not need to touch the filesystem, however
135         # ChangeLog is difficult to mock at current.
136         self.filesystem = FileSystem()
137         self.temp_dir = str(self.filesystem.mkdtemp(suffix="changelogs"))
138         self.old_cwd = self.filesystem.getcwd()
139         self.filesystem.chdir(self.temp_dir)
140         self.webkit_base = WebKitFinder(self.filesystem).webkit_base()
141
142         # Trick commit-log-editor into thinking we're in a Subversion working copy so it won't
143         # complain about not being able to figure out what SCM is in use.
144         # FIXME: VCSTools.pm is no longer so easily fooled.  It logs because "svn info" doesn't
145         # treat a bare .svn directory being part of an svn checkout.
146         self.filesystem.maybe_make_directory(".svn")
147
148     def mock_changelog(self, changelogs):
149         for path, contents in zip(self.changelog_paths, changelogs):
150             self.filesystem.maybe_make_directory(self.filesystem.dirname(path))
151             self.filesystem.write_text_file(path, contents)
152
153     def tearDown(self):
154         self.filesystem.rmtree(self.temp_dir)
155         self.filesystem.chdir(self.old_cwd)
156
157     def mock_checkout_for_test(self):
158         executive = Executive()
159
160         def mock_run(*args, **kwargs):
161             # Note that we use a real Executive here, not a MockExecutive, so we can test that we're
162             # invoking commit-log-editor correctly.
163             env = os.environ.copy()
164             env['CHANGE_LOG_EMAIL_ADDRESS'] = 'vestbo@webkit.org'
165             kwargs['env'] = env
166             return executive.run_command(*args, **kwargs)
167
168         detector = SCMDetector(self.filesystem, executive)
169         real_scm = detector.detect_scm_system(self.webkit_base)
170
171         mock_scm = MockSCM()
172         mock_scm.run = mock_run
173
174         real_checkout = Checkout(real_scm)
175         checkout = Checkout(mock_scm)
176         checkout.script_path = real_checkout.script_path
177         checkout.modified_changelogs = lambda git_commit, changed_files=None: self.changelog_paths
178
179         return checkout
180
181     def test_commit_message_for_unreviewed_changelogs_with_different_messages(self):
182         expected_commit_message = u"""Unreviewed build fix to un-break webkit-patch land.
183
184 Tools:
185
186 Move commit_message_for_this_commit from scm to checkout
187 https://bugs.webkit.org/show_bug.cgi?id=36629
188
189 * Scripts/webkitpy/common/checkout/api.py: import scm.CommitMessage
190
191 LayoutTests:
192
193 Second part of this complicated change by me, Tor Arne Vestb\u00f8!
194
195 * Path/To/Complicated/File: Added.
196 """
197
198         self.changelog_paths = map(self.filesystem.abspath, (self.filesystem.join("Tools", "ChangeLog"), self.filesystem.join("LayoutTests", "ChangeLog")))
199
200         self.mock_changelog((_changelog1, _changelog2))
201         checkout = self.mock_checkout_for_test()
202         commit_message = checkout.commit_message_for_this_commit(git_commit=None, return_stderr=True)
203         # Throw away the first line - a warning about unknown VCS root.
204         commit_message.message_lines = commit_message.message_lines[1:]
205         self.assertMultiLineEqual(commit_message.message(), expected_commit_message)
206
207     def test_commit_message_for_one_reviewed_changelog(self):
208         expected_commit_message = u"""SECTORDER_FLAGS should be defined in target's xcconfig file, not Base.xcconfig
209 <http://webkit.org/b/135006>
210
211 Patch by David Kilzer <ddkilzer@apple.com> on 2014-07-17
212 Reviewed by Darin Adler.
213
214 * WebKit.xcodeproj/project.pbxproj: Remove references to unused
215 WebKit.xcconfig file.
216 """
217
218         self.changelog_paths = map(self.filesystem.abspath, [self.filesystem.join("Source/WebKitLegacy", "ChangeLog")])
219
220         self.mock_changelog([_changelog3])
221         checkout = self.mock_checkout_for_test()
222         commit_message = checkout.commit_message_for_this_commit(git_commit=None, return_stderr=True)
223         # Throw away the first line - a warning about unknown VCS root.
224         commit_message.message_lines = commit_message.message_lines[1:]
225         self.assertMultiLineEqual(commit_message.message(), expected_commit_message)
226
227     def test_commit_message_for_changelogs_with_same_messages(self):
228         expected_commit_message = u"""SECTORDER_FLAGS should be defined in target's xcconfig file, not Base.xcconfig
229 <http://webkit.org/b/135006>
230
231 Patch by David Kilzer <ddkilzer@apple.com> on 2014-07-17
232 Reviewed by Darin Adler.
233
234 Source/WebKitLegacy:
235
236 * WebKit.xcodeproj/project.pbxproj: Remove references to unused
237 WebKit.xcconfig file.
238
239 LayoutTests:
240
241 * Path/To/Complicated/File: Added.
242 """
243
244         self.changelog_paths = map(self.filesystem.abspath, (self.filesystem.join("Source/WebKitLegacy", "ChangeLog"), self.filesystem.join("LayoutTests", "ChangeLog")))
245
246         self.mock_changelog((_changelog3, _changelog4))
247         checkout = self.mock_checkout_for_test()
248         commit_message = checkout.commit_message_for_this_commit(git_commit=None, return_stderr=True)
249         # Throw away the first line - a warning about unknown VCS root.
250         commit_message.message_lines = commit_message.message_lines[1:]
251         self.assertMultiLineEqual(commit_message.message(), expected_commit_message)
252
253     def test_commit_message_for_changelogs_with_different_messages(self):
254         expected_commit_message = u"""SECTORDER_FLAGS should be defined in target's xcconfig file, not Base.xcconfig
255 <http://webkit.org/b/135006>
256
257 Patch by David Kilzer <ddkilzer@apple.com> on 2014-07-17
258 Reviewed by Darin Adler.
259
260 Source/WebKitLegacy:
261
262 * WebKit.xcodeproj/project.pbxproj: Remove references to unused
263 WebKit.xcconfig file.
264
265 LayoutTests:
266
267 Filler change.
268
269 * Path/To/Complicated/File: Added.
270 """
271
272         self.changelog_paths = map(self.filesystem.abspath, (self.filesystem.join("Source/WebKitLegacy", "ChangeLog"), self.filesystem.join("LayoutTests", "ChangeLog")))
273
274         self.mock_changelog((_changelog3, _changelog5))
275         checkout = self.mock_checkout_for_test()
276         commit_message = checkout.commit_message_for_this_commit(git_commit=None, return_stderr=True)
277         # Throw away the first line - a warning about unknown VCS root.
278         commit_message.message_lines = commit_message.message_lines[1:]
279         self.assertMultiLineEqual(commit_message.message(), expected_commit_message)
280
281     def test_commit_message_for_one_rollout_changelog(self):
282         expected_commit_message = u"""Rolling out r170340 and r170339.
283
284 Changeset r170339 broke the Apple Windows Debug and Release builds.
285
286 Reverted changesets:
287
288 "[Win] Build fix after r134209"
289 http://trac.webkit.org/changeset/170340
290
291 "[Win] Clean up and refactor WinLauncher"
292 https://bugs.webkit.org/show_bug.cgi?id=134209
293 http://trac.webkit.org/changeset/170339
294
295 Patch by Daniel Bates <dabates@apple.com> on 2014-06-23
296 """
297
298         self.changelog_paths = map(self.filesystem.abspath, [self.filesystem.join("Tools", "ChangeLog")])
299
300         self.mock_changelog([_changelog6])
301         checkout = self.mock_checkout_for_test()
302         commit_message = checkout.commit_message_for_this_commit(git_commit=None, return_stderr=True)
303         # Throw away the first line - a warning about unknown VCS root.
304         commit_message.message_lines = commit_message.message_lines[1:]
305         self.assertMultiLineEqual(commit_message.message(), expected_commit_message)
306
307     def test_commit_message_for_rollout_changelogs_with_different_directories(self):
308         expected_commit_message = u"""Rolling out r170340 and r170339.
309
310 Changeset r170339 broke the Apple Windows Debug and Release builds.
311
312 Reverted changesets:
313
314 "[Win] Build fix after r134209"
315 http://trac.webkit.org/changeset/170340
316
317 "[Win] Clean up and refactor WinLauncher"
318 https://bugs.webkit.org/show_bug.cgi?id=134209
319 http://trac.webkit.org/changeset/170339
320
321 Patch by Daniel Bates <dabates@apple.com> on 2014-06-23
322 """
323
324         self.changelog_paths = map(self.filesystem.abspath, (self.filesystem.join("Tools", "ChangeLog"), self.filesystem.join("Source/WebCore", "ChangeLog")))
325
326         self.mock_changelog((_changelog6, _changelog6))
327         checkout = self.mock_checkout_for_test()
328         commit_message = checkout.commit_message_for_this_commit(git_commit=None, return_stderr=True)
329         # Throw away the first line - a warning about unknown VCS root.
330         commit_message.message_lines = commit_message.message_lines[1:]
331         self.assertMultiLineEqual(commit_message.message(), expected_commit_message)
332
333
334 class CheckoutTest(unittest.TestCase):
335     def _make_checkout(self):
336         return Checkout(scm=MockSCM(), filesystem=MockFileSystem(), executive=MockExecutive())
337
338     def test_latest_entry_for_changelog_at_revision(self):
339         def mock_contents_at_revision(changelog_path, revision):
340             self.assertEqual(changelog_path, "foo")
341             self.assertEqual(revision, "bar")
342             # contents_at_revision is expected to return a byte array (str)
343             # so we encode our unicode ChangeLog down to a utf-8 stream.
344             # The ChangeLog utf-8 decoding should ignore invalid codepoints.
345             invalid_utf8 = "\255"
346             return _changelog1.encode("utf-8") + invalid_utf8
347         checkout = self._make_checkout()
348         checkout._scm.contents_at_revision = mock_contents_at_revision
349         entry = checkout._latest_entry_for_changelog_at_revision("foo", "bar")
350         self.assertMultiLineEqual(entry.contents(), _changelog1entry1)  # Pylint is confused about this line, pylint: disable=E1101
351
352     # FIXME: This tests a hack around our current changed_files handling.
353     # Right now changelog_entries_for_revision tries to fetch deleted files
354     # from revisions, resulting in a ScriptError exception.  Test that we
355     # recover from those and still return the other ChangeLog entries.
356     def test_changelog_entries_for_revision(self):
357         checkout = self._make_checkout()
358         checkout._scm.changed_files_for_revision = lambda revision: ['foo/ChangeLog', 'bar/ChangeLog']
359
360         def mock_latest_entry_for_changelog_at_revision(path, revision):
361             if path == "foo/ChangeLog":
362                 return 'foo'
363             raise ScriptError()
364
365         checkout._latest_entry_for_changelog_at_revision = mock_latest_entry_for_changelog_at_revision
366
367         # Even though fetching one of the entries failed, the other should succeed.
368         entries = checkout.changelog_entries_for_revision(1)
369         self.assertEqual(len(entries), 1)
370         self.assertEqual(entries[0], 'foo')
371
372     def test_commit_info_for_revision(self):
373         checkout = self._make_checkout()
374         checkout._scm.changed_files_for_revision = lambda revision: ['path/to/file', 'another/file']
375         checkout._scm.committer_email_for_revision = lambda revision, changed_files=None: "committer@example.com"
376         checkout.changelog_entries_for_revision = lambda revision, changed_files=None: [ChangeLogEntry(_changelog1entry1)]
377         commitinfo = checkout.commit_info_for_revision(4)
378         self.assertEqual(commitinfo.bug_id(), 36629)
379         self.assertEqual(commitinfo.bug_description(), "Unreviewed build fix to un-break webkit-patch land.")
380         self.assertEqual(commitinfo.author_name(), u"Tor Arne Vestb\u00f8")
381         self.assertEqual(commitinfo.author_email(), "vestbo@webkit.org")
382         self.assertIsNone(commitinfo.reviewer_text())
383         self.assertIsNone(commitinfo.reviewer())
384         self.assertEqual(commitinfo.committer_email(), "committer@example.com")
385         self.assertIsNone(commitinfo.committer())
386         self.assertEqual(commitinfo.to_json(), {
387             'bug_id': 36629,
388             'author_email': 'vestbo@webkit.org',
389             'changed_files': [
390                 'path/to/file',
391                 'another/file',
392             ],
393             'reviewer_text': None,
394             'author_name': u'Tor Arne Vestb\xf8',
395             'bug_description': 'Unreviewed build fix to un-break webkit-patch land.',
396         })
397
398         checkout.changelog_entries_for_revision = lambda revision, changed_files=None: []
399         self.assertIsNone(checkout.commit_info_for_revision(1))
400
401     def test_bug_id_for_revision(self):
402         checkout = self._make_checkout()
403         checkout._scm.committer_email_for_revision = lambda revision: "committer@example.com"
404         checkout.changelog_entries_for_revision = lambda revision, changed_files=None: [ChangeLogEntry(_changelog1entry1)]
405         self.assertEqual(checkout.bug_id_for_revision(4), 36629)
406
407     def test_bug_id_for_this_commit(self):
408         checkout = self._make_checkout()
409         checkout.commit_message_for_this_commit = lambda git_commit, changed_files=None, return_stderr=False: CommitMessage(ChangeLogEntry(_changelog1entry1).contents().splitlines())
410         self.assertEqual(checkout.bug_id_for_this_commit(git_commit=None), 36629)
411
412     def test_modified_changelogs(self):
413         checkout = self._make_checkout()
414         checkout._scm.checkout_root = "/foo/bar"
415         checkout._scm.changed_files = lambda git_commit: ["file1", "ChangeLog", "relative/path/ChangeLog"]
416         expected_changlogs = ["/foo/bar/ChangeLog", "/foo/bar/relative/path/ChangeLog"]
417         self.assertEqual(checkout.modified_changelogs(git_commit=None), expected_changlogs)
418
419     def test_suggested_reviewers(self):
420         def mock_changelog_entries_for_revision(revision, changed_files=None):
421             if revision % 2 == 0:
422                 return [ChangeLogEntry(_changelog1entry1)]
423             return [ChangeLogEntry(_changelog1entry2)]
424
425         def mock_revisions_changing_file(path, limit=5):
426             if path.endswith("ChangeLog"):
427                 return [3]
428             return [4, 8]
429
430         checkout = self._make_checkout()
431         checkout._scm.checkout_root = "/foo/bar"
432         checkout._scm.changed_files = lambda git_commit: ["file1", "file2", "relative/path/ChangeLog"]
433         checkout._scm.revisions_changing_file = mock_revisions_changing_file
434         checkout.changelog_entries_for_revision = mock_changelog_entries_for_revision
435         reviewers = checkout.suggested_reviewers(git_commit=None)
436         reviewer_names = [reviewer.full_name for reviewer in reviewers]
437         self.assertEqual(reviewer_names, [u'Tor Arne Vestb\xf8'])
438
439     def test_apply_patch(self):
440         checkout = self._make_checkout()
441         checkout._executive = MockExecutive(should_log=True)
442         checkout.script_path = lambda script: script
443         mock_patch = Mock()
444         mock_patch.contents = lambda: "foo"
445         mock_patch.reviewer = lambda: None
446         expected_logs = "MOCK run_command: ['svn-apply', '--force'], cwd=/mock-checkout, input=foo\n"
447         OutputCapture().assert_outputs(self, checkout.apply_patch, [mock_patch], expected_logs=expected_logs)