c18a8467f006ac197e54be5f0e58710b46107227
[WebKit-https.git] / Tools / Scripts / webkitpy / common / checkout / changelog_unittest.py
1 # Copyright (C) 2009 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 unittest2 as unittest
30
31 from StringIO import StringIO
32
33 from webkitpy.common.system.filesystem_mock import MockFileSystem
34 from webkitpy.common.checkout.changelog import *
35
36
37 class ChangeLogTest(unittest.TestCase):
38
39     _changelog_path = 'Tools/ChangeLog'
40
41     _example_entry = u'''2009-08-17  Peter Kasting  <pkasting@google.com>
42
43         Reviewed by Tor Arne Vestb\xf8.
44
45         https://bugs.webkit.org/show_bug.cgi?id=27323
46         Only add Cygwin to the path when it isn't already there.  This avoids
47         causing problems for people who purposefully have non-Cygwin versions of
48         executables like svn in front of the Cygwin ones in their paths.
49
50         * DumpRenderTree/win/DumpRenderTree.vcproj:
51         * DumpRenderTree/win/ImageDiff.vcproj:
52         * DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj:
53 '''
54
55     _rolled_over_footer = '== Rolled over to ChangeLog-2009-06-16 =='
56
57     # More example text than we need.  Eventually we need to support parsing this all and write tests for the parsing.
58     _example_changelog = u"""2009-08-17  Tor Arne Vestb\xf8  <vestbo@webkit.org>
59
60         <http://webkit.org/b/28393> check-webkit-style: add check for use of std::max()/std::min() instead of MAX()/MIN()
61
62         Reviewed by David Levin.
63
64         * Scripts/modules/cpp_style.py:
65         (_ERROR_CATEGORIES): Added 'runtime/max_min_macros'.
66         (check_max_min_macros): Added.  Returns level 4 error when MAX()
67         and MIN() macros are used in header files and C++ source files.
68         (check_style): Added call to check_max_min_macros().
69         * Scripts/modules/cpp_style_unittest.py: Added unit tests.
70         (test_max_macro): Added.
71         (test_min_macro): Added.
72
73 2009-08-16  David Kilzer  <ddkilzer@apple.com>
74
75         Backed out r47343 which was mistakenly committed
76
77         * Scripts/bugzilla-tool:
78         * Scripts/modules/scm.py:
79
80 2009-06-18  Darin Adler  <darin@apple.com>
81
82         Rubber stamped by Mark Rowe.
83
84         * DumpRenderTree/mac/DumpRenderTreeWindow.mm:
85         (-[DumpRenderTreeWindow close]): Resolved crashes seen during regression
86         tests. The close method can be called on a window that's already closed
87         so we can't assert here.
88
89 2011-11-04  Benjamin Poulain  <bpoulain@apple.com>
90
91         [Mac] ResourceRequest's nsURLRequest() does not differentiate null and empty URLs with CFNetwork
92         https://bugs.webkit.org/show_bug.cgi?id=71539
93
94         Reviewed by David Kilzer.
95
96         In order to have CFURL and NSURL to be consistent when both are used on Mac,
97         KURL::createCFURL() is changed to support empty URL values.
98
99         * This change log entry is made up to test _parse_entry:
100             * a list of things
101
102         * platform/cf/KURLCFNet.cpp:
103         (WebCore::createCFURLFromBuffer):
104         (WebCore::KURL::createCFURL):
105         * platform/mac/KURLMac.mm :
106         (WebCore::KURL::operator NSURL *):
107         (WebCore::KURL::createCFURL):
108         * WebCoreSupport/ChromeClientEfl.cpp:
109         (WebCore::ChromeClientEfl::closeWindowSoon): call new function and moves its
110         previous functionality there.
111         * ewk/ewk_private.h:
112         * ewk/ewk_view.cpp:
113
114 2011-03-02  Carol Szabo  <carol.szabo@nokia.com>
115
116         Reviewed by David Hyatt  <hyatt@apple.com>
117
118         content property doesn't support quotes
119         https://bugs.webkit.org/show_bug.cgi?id=6503
120
121         Added full support for quotes as defined by CSS 2.1.
122
123         Tests: fast/css/content/content-quotes-01.html
124                fast/css/content/content-quotes-02.html
125                fast/css/content/content-quotes-03.html
126                fast/css/content/content-quotes-04.html
127                fast/css/content/content-quotes-05.html
128                fast/css/content/content-quotes-06.html
129
130 2011-03-31  Brent Fulgham  <bfulgham@webkit.org>
131
132        Reviewed Adam Roben.
133
134        [WinCairo] Implement Missing drawWindowsBitmap method.
135        https://bugs.webkit.org/show_bug.cgi?id=57409
136
137 2011-03-28  Dirk Pranke  <dpranke@chromium.org>
138
139        RS=Tony Chang.
140
141        r81977 moved FontPlatformData.h from
142        WebCore/platform/graphics/cocoa to platform/graphics. This
143        change updates the chromium build accordingly.
144
145        https://bugs.webkit.org/show_bug.cgi?id=57281
146
147        * platform/graphics/chromium/CrossProcessFontLoading.mm:
148
149 2011-05-04  Alexis Menard  <alexis.menard@openbossa.org>
150
151        Unreviewed warning fix.
152
153        The variable is just used in the ASSERT macro. Let's use ASSERT_UNUSED to avoid
154        a warning in Release build.
155
156        * accessibility/AccessibilityRenderObject.cpp:
157        (WebCore::lastChildConsideringContinuation):
158
159 2011-10-11  Antti Koivisto  <antti@apple.com>
160
161        Resolve regular and visited link style in a single pass
162        https://bugs.webkit.org/show_bug.cgi?id=69838
163
164        Reviewed by Darin Adler
165
166        We can simplify and speed up selector matching by removing the recursive matching done
167        to generate the style for the :visited pseudo selector. Both regular and visited link style
168        can be generated in a single pass through the style selector.
169
170 == Rolled over to ChangeLog-2009-06-16 ==
171 """
172
173     def test_parse_bug_id_from_changelog(self):
174         commit_text = '''
175 2011-03-23  Ojan Vafai  <ojan@chromium.org>
176
177         Add failing result for WebKit2. All tests that require
178         focus fail on WebKit2. See https://bugs.webkit.org/show_bug.cgi?id=56988.
179
180         * platform/mac-wk2/fast/css/pseudo-any-expected.txt: Added.
181
182         '''
183
184         self.assertEqual(56988, parse_bug_id_from_changelog(commit_text))
185
186         commit_text = '''
187 2011-03-23  Ojan Vafai  <ojan@chromium.org>
188
189         Add failing result for WebKit2. All tests that require
190         focus fail on WebKit2. See https://bugs.webkit.org/show_bug.cgi?id=56988.
191         https://bugs.webkit.org/show_bug.cgi?id=12345
192
193         * platform/mac-wk2/fast/css/pseudo-any-expected.txt: Added.
194
195         '''
196
197         self.assertEqual(12345, parse_bug_id_from_changelog(commit_text))
198
199         commit_text = '''
200 2011-03-31  Adam Roben  <aroben@apple.com>
201
202         Quote the executable path we pass to ::CreateProcessW
203
204         This will ensure that spaces in the path will be interpreted correctly.
205
206         Fixes <http://webkit.org/b/57569> Web process sometimes fails to launch when there are
207         spaces in its path
208
209         Reviewed by Steve Falkenburg.
210
211         * UIProcess/Launcher/win/ProcessLauncherWin.cpp:
212         (WebKit::ProcessLauncher::launchProcess): Surround the executable path in quotes.
213
214         '''
215
216         self.assertEqual(57569, parse_bug_id_from_changelog(commit_text))
217
218         commit_text = '''
219 2011-03-29  Timothy Hatcher  <timothy@apple.com>
220
221         Update WebCore Localizable.strings to contain WebCore, WebKit/mac and WebKit2 strings.
222
223         https://webkit.org/b/57354
224
225         Reviewed by Sam Weinig.
226
227         * English.lproj/Localizable.strings: Updated.
228         * StringsNotToBeLocalized.txt: Removed. To hard to maintain in WebCore.
229         * platform/network/cf/LoaderRunLoopCF.h: Remove a single quote in an #error so
230         extract-localizable-strings does not complain about unbalanced single quotes.
231         '''
232
233         self.assertEqual(57354, parse_bug_id_from_changelog(commit_text))
234
235     def test_parse_log_entries_from_changelog(self):
236         changelog_file = StringIO(self._example_changelog)
237         parsed_entries = list(ChangeLog.parse_entries_from_file(changelog_file))
238         self.assertEqual(len(parsed_entries), 9)
239         self.assertEqual(parsed_entries[0].date_line(), u"2009-08-17  Tor Arne Vestb\xf8  <vestbo@webkit.org>")
240         self.assertEqual(parsed_entries[0].reviewer_text(), "David Levin")
241         self.assertEqual(parsed_entries[0].is_touched_files_text_clean(), False)
242         self.assertEqual(parsed_entries[1].author_email(), "ddkilzer@apple.com")
243         self.assertEqual(parsed_entries[1].touched_files_text(), "        * Scripts/bugzilla-tool:\n        * Scripts/modules/scm.py:\n")
244         self.assertEqual(parsed_entries[1].is_touched_files_text_clean(), True)
245         self.assertEqual(parsed_entries[2].reviewer_text(), "Mark Rowe")
246         self.assertEqual(parsed_entries[2].touched_files(), ["DumpRenderTree/mac/DumpRenderTreeWindow.mm"])
247         self.assertEqual(parsed_entries[2].touched_functions(), {"DumpRenderTree/mac/DumpRenderTreeWindow.mm": ["-[DumpRenderTreeWindow close]"]})
248         self.assertEqual(parsed_entries[2].is_touched_files_text_clean(), False)
249         self.assertEqual(parsed_entries[3].author_name(), "Benjamin Poulain")
250         self.assertEqual(parsed_entries[3].touched_files(), ["platform/cf/KURLCFNet.cpp", "platform/mac/KURLMac.mm",
251             "WebCoreSupport/ChromeClientEfl.cpp", "ewk/ewk_private.h", "ewk/ewk_view.cpp"])
252         self.assertEqual(parsed_entries[3].touched_functions(), {"platform/cf/KURLCFNet.cpp": ["WebCore::createCFURLFromBuffer", "WebCore::KURL::createCFURL"],
253             "platform/mac/KURLMac.mm": ["WebCore::KURL::operator NSURL *", "WebCore::KURL::createCFURL"],
254             "WebCoreSupport/ChromeClientEfl.cpp": ["WebCore::ChromeClientEfl::closeWindowSoon"], "ewk/ewk_private.h": [], "ewk/ewk_view.cpp": []})
255         self.assertEqual(parsed_entries[3].bug_description(), "[Mac] ResourceRequest's nsURLRequest() does not differentiate null and empty URLs with CFNetwork")
256         self.assertEqual(parsed_entries[4].reviewer_text(), "David Hyatt")
257         self.assertIsNone(parsed_entries[4].bug_description())
258         self.assertEqual(parsed_entries[5].reviewer_text(), "Adam Roben")
259         self.assertEqual(parsed_entries[6].reviewer_text(), "Tony Chang")
260         self.assertIsNone(parsed_entries[7].reviewer_text())
261         self.assertEqual(parsed_entries[8].reviewer_text(), 'Darin Adler')
262
263     def test_parse_log_entries_from_annotated_file(self):
264         # Note that there are trailing spaces on some of the lines intentionally.
265         changelog_file = StringIO(u"100000 ossy@webkit.org 2011-11-11  Csaba Osztrogon\u00e1c  <ossy@webkit.org>\n"
266             u"100000 ossy@webkit.org\n"
267             u"100000 ossy@webkit.org         100,000 !!!\n"
268             u"100000 ossy@webkit.org \n"
269             u"100000 ossy@webkit.org         Reviewed by Zoltan Herczeg.\n"
270             u"100000 ossy@webkit.org \n"
271             u"100000 ossy@webkit.org         * ChangeLog: Point out revision 100,000.\n"
272             u"100000 ossy@webkit.org \n"
273             u"93798 ap@apple.com 2011-08-25  Alexey Proskuryakov  <ap@apple.com>\n"
274             u"93798 ap@apple.com \n"
275             u"93798 ap@apple.com         Fix build when GCC 4.2 is not installed.\n"
276             u"93798 ap@apple.com \n"
277             u"93798 ap@apple.com         * gtest/xcode/Config/CompilerVersion.xcconfig: Copied from Source/WebCore/Configurations/CompilerVersion.xcconfig.\n"
278             u"93798 ap@apple.com         * gtest/xcode/Config/General.xcconfig:\n"
279             u"93798 ap@apple.com         Use the same compiler version as other projects do.\n"
280             u"93798 ap@apple.com\n"
281             u"99491 andreas.kling@nokia.com 2011-11-03  Andreas Kling  <kling@webkit.org>\n"
282             u"99491 andreas.kling@nokia.com \n"
283             u"99190 andreas.kling@nokia.com         Unreviewed build fix, sigh.\n"
284             u"99190 andreas.kling@nokia.com \n"
285             u"99190 andreas.kling@nokia.com         * css/CSSFontFaceRule.h:\n"
286             u"99190 andreas.kling@nokia.com         * css/CSSMutableStyleDeclaration.h:\n"
287             u"99190 andreas.kling@nokia.com\n"
288             u"99190 andreas.kling@nokia.com 2011-11-03  Andreas Kling  <kling@webkit.org>\n"
289             u"99190 andreas.kling@nokia.com \n"
290             u"99187 andreas.kling@nokia.com         Unreviewed build fix, out-of-line StyleSheet::parentStyleSheet()\n"
291             u"99187 andreas.kling@nokia.com         again since there's a cycle in the includes between CSSRule/StyleSheet.\n"
292             u"99187 andreas.kling@nokia.com \n"
293             u"99187 andreas.kling@nokia.com         * css/StyleSheet.cpp:\n"
294             u"99187 andreas.kling@nokia.com         (WebCore::StyleSheet::parentStyleSheet):\n"
295             u"99187 andreas.kling@nokia.com         * css/StyleSheet.h:\n"
296             u"99187 andreas.kling@nokia.com \n")
297
298         parsed_entries = list(ChangeLog.parse_entries_from_file(changelog_file))
299         self.assertEqual(parsed_entries[0].revision(), 100000)
300         self.assertEqual(parsed_entries[0].reviewer_text(), "Zoltan Herczeg")
301         self.assertEqual(parsed_entries[0].author_name(), u"Csaba Osztrogon\u00e1c")
302         self.assertEqual(parsed_entries[0].author_email(), "ossy@webkit.org")
303         self.assertEqual(parsed_entries[1].revision(), 93798)
304         self.assertEqual(parsed_entries[1].author_name(), "Alexey Proskuryakov")
305         self.assertEqual(parsed_entries[2].revision(), 99190)
306         self.assertEqual(parsed_entries[2].author_name(), "Andreas Kling")
307         self.assertEqual(parsed_entries[3].revision(), 99187)
308         self.assertEqual(parsed_entries[3].author_name(), "Andreas Kling")
309
310     def _assert_parse_reviewer_text_and_list(self, text, expected_reviewer_text, expected_reviewer_text_list=None):
311         reviewer_text, reviewer_text_list = ChangeLogEntry._parse_reviewer_text(text)
312         self.assertEqual(reviewer_text, expected_reviewer_text)
313         if expected_reviewer_text_list:
314             self.assertEqual(reviewer_text_list, expected_reviewer_text_list)
315         else:
316             self.assertEqual(reviewer_text_list, [expected_reviewer_text])
317
318     def _assert_parse_reviewer_text_list(self, text, expected_reviewer_text_list):
319         reviewer_text, reviewer_text_list = ChangeLogEntry._parse_reviewer_text(text)
320         self.assertEqual(reviewer_text_list, expected_reviewer_text_list)
321
322     def test_parse_reviewer_text(self):
323         self._assert_parse_reviewer_text_and_list('  reviewed  by Ryosuke Niwa,   Oliver Hunt, and  Dimitri Glazkov',
324             'Ryosuke Niwa, Oliver Hunt, and Dimitri Glazkov', ['Ryosuke Niwa', 'Oliver Hunt', 'Dimitri Glazkov'])
325         self._assert_parse_reviewer_text_and_list('Reviewed by Brady Eidson and David Levin, landed by Brady Eidson',
326             'Brady Eidson and David Levin', ['Brady Eidson', 'David Levin'])
327
328         self._assert_parse_reviewer_text_and_list('Reviewed by Simon Fraser. Committed by Beth Dakin.', 'Simon Fraser')
329         self._assert_parse_reviewer_text_and_list('Reviewed by Geoff Garen. V8 fixes courtesy of Dmitry Titov.', 'Geoff Garen')
330         self._assert_parse_reviewer_text_and_list('Reviewed by Adam Roben&Dirk Schulze', 'Adam Roben&Dirk Schulze', ['Adam Roben', 'Dirk Schulze'])
331         self._assert_parse_reviewer_text_and_list('Rubber stamps by Darin Adler & Sam Weinig.', 'Darin Adler & Sam Weinig', ['Darin Adler', 'Sam Weinig'])
332
333         self._assert_parse_reviewer_text_and_list('Reviewed by adam,andy and andy adam, andy smith',
334             'adam,andy and andy adam, andy smith', ['adam', 'andy', 'andy adam', 'andy smith'])
335
336         self._assert_parse_reviewer_text_and_list('rubber stamped by Oliver Hunt (oliver@apple.com) and Darin Adler (darin@apple.com)',
337             'Oliver Hunt and Darin Adler', ['Oliver Hunt', 'Darin Adler'])
338
339         self._assert_parse_reviewer_text_and_list('rubber  Stamped by David Hyatt  <hyatt@apple.com>', 'David Hyatt')
340         self._assert_parse_reviewer_text_and_list('Rubber-stamped by Antti Koivisto.', 'Antti Koivisto')
341         self._assert_parse_reviewer_text_and_list('Rubberstamped by Dan Bernstein.', 'Dan Bernstein')
342         self._assert_parse_reviewer_text_and_list('Reviews by Ryosuke Niwa', 'Ryosuke Niwa')
343         self._assert_parse_reviewer_text_and_list('Reviews Ryosuke Niwa', 'Ryosuke Niwa')
344         self._assert_parse_reviewer_text_and_list('Rubberstamp Ryosuke Niwa', 'Ryosuke Niwa')
345         self._assert_parse_reviewer_text_and_list('Typed and reviewed by Alexey Proskuryakov.', 'Alexey Proskuryakov')
346         self._assert_parse_reviewer_text_and_list('Reviewed and landed by Brady Eidson', 'Brady Eidson')
347         self._assert_parse_reviewer_text_and_list('Reviewed by rniwa@webkit.org.', 'rniwa@webkit.org')
348         self._assert_parse_reviewer_text_and_list('Reviewed by Dirk Schulze / Darin Adler.', 'Dirk Schulze / Darin Adler', ['Dirk Schulze', 'Darin Adler'])
349         self._assert_parse_reviewer_text_and_list('Reviewed by Sam Weinig + Oliver Hunt.', 'Sam Weinig + Oliver Hunt', ['Sam Weinig', 'Oliver Hunt'])
350
351         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig, and given a good once-over by Jeff Miller.', ['Sam Weinig', 'Jeff Miller'])
352         self._assert_parse_reviewer_text_list(' Reviewed by Sam Weinig, even though this is just a...', ['Sam Weinig'])
353         self._assert_parse_reviewer_text_list('Rubber stamped by by Gustavo Noronha Silva', ['Gustavo Noronha Silva'])
354         self._assert_parse_reviewer_text_list('Rubberstamped by Noam Rosenthal, who wrote the original code.', ['Noam Rosenthal'])
355         self._assert_parse_reviewer_text_list('Reviewed by Dan Bernstein (relanding of r47157)', ['Dan Bernstein'])
356         self._assert_parse_reviewer_text_list('Reviewed by Geoffrey "Sean/Shawn/Shaun" Garen', ['Geoffrey Garen'])
357         self._assert_parse_reviewer_text_list('Reviewed by Dave "Messy" Hyatt.', ['Dave Hyatt'])
358         self._assert_parse_reviewer_text_list('Reviewed by Sam \'The Belly\' Weinig', ['Sam Weinig'])
359         self._assert_parse_reviewer_text_list('Rubber-stamped by David "I\'d prefer not" Hyatt.', ['David Hyatt'])
360         self._assert_parse_reviewer_text_list('Reviewed by Mr. Geoffrey Garen.', ['Geoffrey Garen'])
361         self._assert_parse_reviewer_text_list('Reviewed by Darin (ages ago)', ['Darin'])
362         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig (except for a few comment and header tweaks).', ['Sam Weinig'])
363         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig (all but the FormDataListItem rename)', ['Sam Weinig'])
364         self._assert_parse_reviewer_text_list('Reviewed by Darin Adler, tweaked and landed by Beth.', ['Darin Adler'])
365         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig with no hesitation', ['Sam Weinig'])
366         self._assert_parse_reviewer_text_list('Reviewed by Oliver Hunt, okayed by Darin Adler.', ['Oliver Hunt'])
367         self._assert_parse_reviewer_text_list('Reviewed by Darin Adler).', ['Darin Adler'])
368
369         # For now, we let unofficial reviewers recognized as reviewers
370         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig, Anders Carlsson, and (unofficially) Adam Barth.',
371             ['Sam Weinig', 'Anders Carlsson', 'Adam Barth'])
372
373         self._assert_parse_reviewer_text_list('Reviewed by NOBODY.', None)
374         self._assert_parse_reviewer_text_list('Reviewed by NOBODY - Build Fix.', None)
375         self._assert_parse_reviewer_text_list('Reviewed by NOBODY, layout tests fix.', None)
376         self._assert_parse_reviewer_text_list('Reviewed by NOBODY (Qt build fix pt 2).', None)
377         self._assert_parse_reviewer_text_list('Reviewed by NOBODY(rollout)', None)
378         self._assert_parse_reviewer_text_list('Reviewed by NOBODY (Build fix, forgot to svn add this file)', None)
379         self._assert_parse_reviewer_text_list('Reviewed by nobody (trivial follow up fix), Joseph Pecoraro LGTM-ed.', None)
380
381     def _entry_with_author(self, author_text):
382         return ChangeLogEntry('''2009-08-19  AUTHOR_TEXT
383
384             Reviewed by Ryosuke Niwa
385
386             * Scripts/bugzilla-tool:
387 '''.replace("AUTHOR_TEXT", author_text))
388
389     def _entry_with_reviewer(self, reviewer_line):
390         return ChangeLogEntry('''2009-08-19  Eric Seidel  <eric@webkit.org>
391
392             REVIEW_LINE
393
394             * Scripts/bugzilla-tool:
395 '''.replace("REVIEW_LINE", reviewer_line))
396
397     def _contributors(self, names):
398         return [CommitterList().contributor_by_name(name) for name in names]
399
400     def _assert_fuzzy_reviewer_match(self, reviewer_text, expected_text_list, expected_contributors):
401         unused, reviewer_text_list = ChangeLogEntry._parse_reviewer_text(reviewer_text)
402         self.assertEqual(reviewer_text_list, expected_text_list)
403         self.assertEqual(self._entry_with_reviewer(reviewer_text).reviewers(), self._contributors(expected_contributors))
404
405     def test_fuzzy_reviewer_match__none(self):
406         self._assert_fuzzy_reviewer_match('Reviewed by BUILD FIX', ['BUILD FIX'], [])
407         self._assert_fuzzy_reviewer_match('Reviewed by Mac build fix', ['Mac build fix'], [])
408
409     def test_fuzzy_reviewer_match_adam_barth(self):
410         self._assert_fuzzy_reviewer_match('Reviewed by Adam Barth.:w', ['Adam Barth.:w'], ['Adam Barth'])
411
412     def test_fuzzy_reviewer_match_darin_adler_et_al(self):
413         self._assert_fuzzy_reviewer_match('Reviewed by Darin Adler in <https://bugs.webkit.org/show_bug.cgi?id=47736>.', ['Darin Adler in'], ['Darin Adler'])
414         self._assert_fuzzy_reviewer_match('Reviewed by Darin Adler, Dan Bernstein, Adele Peterson, and others.',
415             ['Darin Adler', 'Dan Bernstein', 'Adele Peterson', 'others'], ['Darin Adler', 'Dan Bernstein', 'Adele Peterson'])
416
417     def test_fuzzy_reviewer_match_dimitri_glazkov(self):
418         self._assert_fuzzy_reviewer_match('Reviewed by Dimitri Glazkov, build fix', ['Dimitri Glazkov', 'build fix'], ['Dimitri Glazkov'])
419
420     def test_fuzzy_reviewer_match_george_staikos(self):
421         self._assert_fuzzy_reviewer_match('Reviewed by George Staikos (and others)', ['George Staikos', 'others'], ['George Staikos'])
422
423     def test_fuzzy_reviewer_match_mark_rowe(self):
424         self._assert_fuzzy_reviewer_match('Reviewed by Mark Rowe, but Dan Bernstein also reviewed and asked thoughtful questions.',
425             ['Mark Rowe', 'but Dan Bernstein also reviewed', 'asked thoughtful questions'], ['Mark Rowe'])
426
427     def test_fuzzy_reviewer_match_initial(self):
428         self._assert_fuzzy_reviewer_match('Reviewed by Alejandro G. Castro.',
429             ['Alejandro G. Castro'], ['Alejandro G. Castro'])
430         self._assert_fuzzy_reviewer_match('Reviewed by G. Alejandro G. Castro and others.',
431             ['G. Alejandro G. Castro', 'others'], ['Alejandro G. Castro'])
432
433         # If a reviewer has a name that ended with an initial, the regular expression
434         # will incorrectly trim the last period, but it will still match fuzzily to
435         # the full reviewer name.
436         self._assert_fuzzy_reviewer_match('Reviewed by G. Alejandro G. G. Castro G.',
437             ['G. Alejandro G. G. Castro G'], ['Alejandro G. Castro'])
438
439     def _assert_parse_authors(self, author_text, expected_contributors):
440         parsed_authors = [(author['name'], author['email']) for author in self._entry_with_author(author_text).authors()]
441         self.assertEqual(parsed_authors, expected_contributors)
442
443     def test_parse_authors(self):
444         self._assert_parse_authors(u'Aaron Colwell  <acolwell@chromium.org>', [(u'Aaron Colwell', u'acolwell@chromium.org')])
445         self._assert_parse_authors('Eric Seidel  <eric@webkit.org>, Ryosuke Niwa  <rniwa@webkit.org>',
446             [('Eric Seidel', 'eric@webkit.org'), ('Ryosuke Niwa', 'rniwa@webkit.org')])
447         self._assert_parse_authors('Zan Dobersek  <zandobersek@gmail.com> and Philippe Normand  <pnormand@igalia.com>',
448             [('Zan Dobersek', 'zandobersek@gmail.com'), ('Philippe Normand', 'pnormand@igalia.com')])
449         self._assert_parse_authors('New Contributor  <new@webkit.org> and Noob  <noob@webkit.org>',
450             [('New Contributor', 'new@webkit.org'), ('Noob', 'noob@webkit.org')])
451         self._assert_parse_authors('Adam Barth  <abarth@webkit.org> && Benjamin Poulain  <bpoulain@apple.com>',
452             [('Adam Barth', 'abarth@webkit.org'), ('Benjamin Poulain', 'bpoulain@apple.com')])
453
454     def _assert_has_valid_reviewer(self, reviewer_line, expected):
455         self.assertEqual(self._entry_with_reviewer(reviewer_line).has_valid_reviewer(), expected)
456
457     def test_has_valid_reviewer(self):
458         self._assert_has_valid_reviewer("Reviewed by Eric Seidel.", True)
459         self._assert_has_valid_reviewer("Reviewed by Eric Seidel", True)  # Not picky about the '.'
460         self._assert_has_valid_reviewer("Reviewed by Eric.", False)
461         self._assert_has_valid_reviewer("Reviewed by Eric C Seidel.", False)
462         self._assert_has_valid_reviewer("Rubber-stamped by Eric.", False)
463         self._assert_has_valid_reviewer("Rubber-stamped by Eric Seidel.", True)
464         self._assert_has_valid_reviewer("Rubber stamped by Eric.", False)
465         self._assert_has_valid_reviewer("Rubber stamped by Eric Seidel.", True)
466         self._assert_has_valid_reviewer("Unreviewed build fix.", True)
467
468     def test_is_touched_files_text_clean(self):
469         tests = [
470         ('''2013-01-30  Timothy Loh  <timloh@chromium.com>
471
472         Make ChangeLogEntry detect annotations by prepare-ChangeLog (Added/Removed/Copied from/Renamed from) as clean.
473         https://bugs.webkit.org/show_bug.cgi?id=108433
474
475         * Scripts/webkitpy/common/checkout/changelog.py:
476         (ChangeLogEntry.is_touched_files_text_clean):
477         * Scripts/webkitpy/common/checkout/changelog_unittest.py:
478         (test_is_touched_files_text_clean):
479 ''', True),
480         ('''2013-01-10  Alan Cutter  <alancutter@chromium.org>
481
482         Perform some file operations (automatically added comments).
483
484         * QueueStatusServer/config/charts.py: Copied from Tools/QueueStatusServer/model/queuelog.py.
485         (get_time_unit):
486         * QueueStatusServer/handlers/queuecharts.py: Added.
487         (QueueCharts):
488         * Scripts/webkitpy/tool/bot/testdata/webkit_sheriff_0.js: Removed.
489         * EWSTools/build-vm.sh: Renamed from Tools/EWSTools/cold-boot.sh.
490 ''', True),
491         ('''2013-01-30  Timothy Loh  <timloh@chromium.com>
492
493         Add unit test (manually added comment).
494
495         * Scripts/webkitpy/common/checkout/changelog_unittest.py:
496         (test_is_touched_files_text_clean): Added.
497 ''', False),
498         ('''2013-01-30  Timothy Loh  <timloh@chromium.com>
499
500         Add file (manually added comment).
501
502         * Scripts/webkitpy/common/checkout/super_changelog.py: Copied from the internet.
503 ''', False),
504         ]
505
506         for contents, expected_result in tests:
507             entry = ChangeLogEntry(contents)
508             self.assertEqual(entry.is_touched_files_text_clean(), expected_result)
509
510     def test_latest_entry_parse(self):
511         changelog_contents = u"%s\n%s" % (self._example_entry, self._example_changelog)
512         changelog_file = StringIO(changelog_contents)
513         latest_entry = ChangeLog.parse_latest_entry_from_file(changelog_file)
514         self.assertEqual(latest_entry.contents(), self._example_entry)
515         self.assertEqual(latest_entry.author_name(), "Peter Kasting")
516         self.assertEqual(latest_entry.author_email(), "pkasting@google.com")
517         self.assertEqual(latest_entry.reviewer_text(), u"Tor Arne Vestb\xf8")
518         touched_files = ["DumpRenderTree/win/DumpRenderTree.vcproj", "DumpRenderTree/win/ImageDiff.vcproj", "DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj"]
519         self.assertEqual(latest_entry.touched_files(), touched_files)
520         self.assertEqual(latest_entry.touched_functions(), dict((f, []) for f in touched_files))
521
522         self.assertTrue(latest_entry.reviewer())  # Make sure that our UTF8-based lookup of Tor works.
523
524     def test_latest_entry_parse_single_entry(self):
525         changelog_contents = u"%s\n%s" % (self._example_entry, self._rolled_over_footer)
526         changelog_file = StringIO(changelog_contents)
527         latest_entry = ChangeLog.parse_latest_entry_from_file(changelog_file)
528         self.assertEqual(latest_entry.contents(), self._example_entry)
529         self.assertEqual(latest_entry.author_name(), "Peter Kasting")
530
531     # FIXME: We really should be getting this from prepare-ChangeLog itself.
532     _new_entry_boilerplate = '''2009-08-19  Eric Seidel  <eric@webkit.org>
533
534         Need a short description (OOPS!).
535         Need the bug URL (OOPS!).
536
537         Reviewed by NOBODY (OOPS!).
538
539         * Scripts/bugzilla-tool:
540 '''
541
542     _new_entry_boilerplate_with_bugurl = '''2009-08-19  Eric Seidel  <eric@webkit.org>
543
544         Need a short description (OOPS!).
545         https://bugs.webkit.org/show_bug.cgi?id=12345
546
547         Reviewed by NOBODY (OOPS!).
548
549         * Scripts/bugzilla-tool:
550 '''
551
552     _new_entry_boilerplate_with_multiple_bugurl = '''2009-08-19  Eric Seidel  <eric@webkit.org>
553
554         Need a short description (OOPS!).
555         https://bugs.webkit.org/show_bug.cgi?id=12345
556         http://webkit.org/b/12345
557
558         Reviewed by NOBODY (OOPS!).
559
560         * Scripts/bugzilla-tool:
561 '''
562
563     _new_entry_boilerplate_without_reviewer_line = '''2009-08-19  Eric Seidel  <eric@webkit.org>
564
565         Need a short description (OOPS!).
566         https://bugs.webkit.org/show_bug.cgi?id=12345
567
568         * Scripts/bugzilla-tool:
569 '''
570
571     _new_entry_boilerplate_without_reviewer_multiple_bugurl = '''2009-08-19  Eric Seidel  <eric@webkit.org>
572
573         Need a short description (OOPS!).
574         https://bugs.webkit.org/show_bug.cgi?id=12345
575         http://webkit.org/b/12345
576
577         * Scripts/bugzilla-tool:
578 '''
579
580     def test_set_reviewer(self):
581         fs = MockFileSystem()
582
583         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate_with_bugurl, self._example_changelog)
584         reviewer_name = 'Test Reviewer'
585         fs.write_text_file(self._changelog_path, changelog_contents)
586         ChangeLog(self._changelog_path, fs).set_reviewer(reviewer_name)
587         actual_contents = fs.read_text_file(self._changelog_path)
588         expected_contents = changelog_contents.replace('NOBODY (OOPS!)', reviewer_name)
589         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
590
591         changelog_contents_without_reviewer_line = u"%s\n%s" % (self._new_entry_boilerplate_without_reviewer_line, self._example_changelog)
592         fs.write_text_file(self._changelog_path, changelog_contents_without_reviewer_line)
593         ChangeLog(self._changelog_path, fs).set_reviewer(reviewer_name)
594         actual_contents = fs.read_text_file(self._changelog_path)
595         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
596
597         changelog_contents_without_reviewer_line = u"%s\n%s" % (self._new_entry_boilerplate_without_reviewer_multiple_bugurl, self._example_changelog)
598         fs.write_text_file(self._changelog_path, changelog_contents_without_reviewer_line)
599         ChangeLog(self._changelog_path, fs).set_reviewer(reviewer_name)
600         actual_contents = fs.read_text_file(self._changelog_path)
601         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate_with_multiple_bugurl, self._example_changelog)
602         expected_contents = changelog_contents.replace('NOBODY (OOPS!)', reviewer_name)
603         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
604
605     def test_set_short_description_and_bug_url(self):
606         fs = MockFileSystem()
607
608         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate_with_bugurl, self._example_changelog)
609         fs.write_text_file(self._changelog_path, changelog_contents)
610         short_description = "A short description"
611         bug_url = "http://example.com/b/2344"
612         ChangeLog(self._changelog_path, fs).set_short_description_and_bug_url(short_description, bug_url)
613         actual_contents = fs.read_text_file(self._changelog_path)
614         expected_message = "%s\n        %s" % (short_description, bug_url)
615         expected_contents = changelog_contents.replace("Need a short description (OOPS!).", expected_message)
616         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
617
618         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
619         fs.write_text_file(self._changelog_path, changelog_contents)
620         short_description = "A short description 2"
621         bug_url = "http://example.com/b/2345"
622         ChangeLog(self._changelog_path, fs).set_short_description_and_bug_url(short_description, bug_url)
623         actual_contents = fs.read_text_file(self._changelog_path)
624         expected_message = "%s\n        %s" % (short_description, bug_url)
625         expected_contents = changelog_contents.replace("Need a short description (OOPS!).\n        Need the bug URL (OOPS!).", expected_message)
626         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
627
628     def test_delete_entries(self):
629         fs = MockFileSystem()
630         fs.write_text_file(self._changelog_path, self._example_changelog)
631         ChangeLog(self._changelog_path, fs).delete_entries(8)
632         actual_contents = fs.read_text_file(self._changelog_path)
633         expected_contents = """2011-10-11  Antti Koivisto  <antti@apple.com>
634
635        Resolve regular and visited link style in a single pass
636        https://bugs.webkit.org/show_bug.cgi?id=69838
637
638        Reviewed by Darin Adler
639
640        We can simplify and speed up selector matching by removing the recursive matching done
641        to generate the style for the :visited pseudo selector. Both regular and visited link style
642        can be generated in a single pass through the style selector.
643
644 == Rolled over to ChangeLog-2009-06-16 ==
645 """
646         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
647
648         ChangeLog(self._changelog_path, fs).delete_entries(2)
649         actual_contents = fs.read_text_file(self._changelog_path)
650         expected_contents = "== Rolled over to ChangeLog-2009-06-16 ==\n"
651         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
652
653
654     def test_prepend_text(self):
655         fs = MockFileSystem()
656         fs.write_text_file(self._changelog_path, self._example_changelog)
657         ChangeLog(self._changelog_path, fs).prepend_text(self._example_entry + "\n")
658         actual_contents = fs.read_text_file(self._changelog_path)
659         expected_contents = self._example_entry + "\n" + self._example_changelog
660         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())