Unreviewed, reverting r263195, r263252, and r265394.
[WebKit-https.git] / Tools / Scripts / webkitpy / common / checkout / changelog_unittest.py
1 # Copyright (C) 2009 Google Inc. All rights reserved.
2 # Copyright (C) 2013 Apple Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #    * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #    * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #    * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 import unittest
31
32 from webkitpy.common.system.filesystem_mock import MockFileSystem
33 from webkitpy.common.unicode_compatibility import StringIO
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 Fr\u00e9d\u00e9ric Wang.
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  Fr\u00e9d\u00e9ric Wang  <fred.wang@free.fr>
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  Fr\u00e9d\u00e9ric Wang  <fred.wang@free.fr>")
240         self.assertEqual(parsed_entries[0].date(), "2009-08-17")
241         self.assertEqual(parsed_entries[0].reviewer_text(), "David Levin")
242         self.assertEqual(parsed_entries[0].is_touched_files_text_clean(), False)
243         self.assertIsNone(parsed_entries[0].bug_description())
244         self.assertEqual(parsed_entries[1].date_line(), "2009-08-16  David Kilzer  <ddkilzer@apple.com>")
245         self.assertEqual(parsed_entries[1].date(), "2009-08-16")
246         self.assertEqual(parsed_entries[1].author_email(), "ddkilzer@apple.com")
247         self.assertEqual(parsed_entries[1].bug_description(), "Backed out r47343 which was mistakenly committed")
248         self.assertEqual(parsed_entries[1].touched_files_text(), "        * Scripts/bugzilla-tool:\n        * Scripts/modules/scm.py:\n")
249         self.assertEqual(parsed_entries[1].is_touched_files_text_clean(), True)
250         self.assertEqual(parsed_entries[2].reviewer_text(), "Mark Rowe")
251         self.assertEqual(parsed_entries[2].touched_files(), ["DumpRenderTree/mac/DumpRenderTreeWindow.mm"])
252         self.assertEqual(parsed_entries[2].touched_functions(), {"DumpRenderTree/mac/DumpRenderTreeWindow.mm": ["-[DumpRenderTreeWindow close]"]})
253         self.assertEqual(parsed_entries[2].is_touched_files_text_clean(), False)
254         self.assertIsNone(parsed_entries[2].bug_description())
255         self.assertEqual(parsed_entries[3].author_name(), "Benjamin Poulain")
256         self.assertEqual(parsed_entries[3].touched_files(), ["platform/cf/KURLCFNet.cpp", "platform/mac/KURLMac.mm",
257             "WebCoreSupport/ChromeClientEfl.cpp", "ewk/ewk_private.h", "ewk/ewk_view.cpp"])
258         self.assertEqual(parsed_entries[3].touched_functions(), {"platform/cf/KURLCFNet.cpp": ["WebCore::createCFURLFromBuffer", "WebCore::KURL::createCFURL"],
259             "platform/mac/KURLMac.mm": ["WebCore::KURL::operator NSURL *", "WebCore::KURL::createCFURL"],
260             "WebCoreSupport/ChromeClientEfl.cpp": ["WebCore::ChromeClientEfl::closeWindowSoon"], "ewk/ewk_private.h": [], "ewk/ewk_view.cpp": []})
261         self.assertEqual(parsed_entries[3].bug_description(), "[Mac] ResourceRequest's nsURLRequest() does not differentiate null and empty URLs with CFNetwork")
262         self.assertEqual(parsed_entries[4].reviewer_text(), "David Hyatt")
263         self.assertIsNone(parsed_entries[4].bug_description())
264         self.assertEqual(parsed_entries[5].reviewer_text(), "Adam Roben")
265         self.assertIsNone(parsed_entries[5].bug_description())
266         self.assertEqual(parsed_entries[6].reviewer_text(), "Tony Chang")
267         self.assertIsNone(parsed_entries[6].bug_description())
268         self.assertIsNone(parsed_entries[7].reviewer_text())
269         self.assertEqual(parsed_entries[7].bug_description(), "Unreviewed warning fix.")
270         self.assertEqual(parsed_entries[8].reviewer_text(), 'Darin Adler')
271         self.assertEqual(parsed_entries[8].bug_description(), 'Resolve regular and visited link style in a single pass')
272
273     def test_parse_log_entries_from_annotated_file(self):
274         # Note that there are trailing spaces on some of the lines intentionally.
275         changelog_file = StringIO(u"100000 ossy@webkit.org 2011-11-11  Csaba Osztrogon\u00e1c  <ossy@webkit.org>\n"
276             u"100000 ossy@webkit.org\n"
277             u"100000 ossy@webkit.org         100,000 !!!\n"
278             u"100000 ossy@webkit.org \n"
279             u"100000 ossy@webkit.org         Reviewed by Zoltan Herczeg.\n"
280             u"100000 ossy@webkit.org \n"
281             u"100000 ossy@webkit.org         * ChangeLog: Point out revision 100,000.\n"
282             u"100000 ossy@webkit.org \n"
283             u"93798 ap@apple.com 2011-08-25  Alexey Proskuryakov  <ap@apple.com>\n"
284             u"93798 ap@apple.com \n"
285             u"93798 ap@apple.com         Fix build when GCC 4.2 is not installed.\n"
286             u"93798 ap@apple.com \n"
287             u"93798 ap@apple.com         * gtest/xcode/Config/CompilerVersion.xcconfig: Copied from Source/WebCore/Configurations/CompilerVersion.xcconfig.\n"
288             u"93798 ap@apple.com         * gtest/xcode/Config/General.xcconfig:\n"
289             u"93798 ap@apple.com         Use the same compiler version as other projects do.\n"
290             u"93798 ap@apple.com\n"
291             u"99491 andreas.kling@nokia.com 2011-11-03  Andreas Kling  <kling@webkit.org>\n"
292             u"99491 andreas.kling@nokia.com \n"
293             u"99190 andreas.kling@nokia.com         Unreviewed build fix, sigh.\n"
294             u"99190 andreas.kling@nokia.com \n"
295             u"99190 andreas.kling@nokia.com         * css/CSSFontFaceRule.h:\n"
296             u"99190 andreas.kling@nokia.com         * css/CSSMutableStyleDeclaration.h:\n"
297             u"99190 andreas.kling@nokia.com\n"
298             u"99190 andreas.kling@nokia.com 2011-11-03  Andreas Kling  <kling@webkit.org>\n"
299             u"99190 andreas.kling@nokia.com \n"
300             u"99187 andreas.kling@nokia.com         Unreviewed build fix, out-of-line StyleSheet::parentStyleSheet()\n"
301             u"99187 andreas.kling@nokia.com         again since there's a cycle in the includes between CSSRule/StyleSheet.\n"
302             u"99187 andreas.kling@nokia.com \n"
303             u"99187 andreas.kling@nokia.com         * css/StyleSheet.cpp:\n"
304             u"99187 andreas.kling@nokia.com         (WebCore::StyleSheet::parentStyleSheet):\n"
305             u"99187 andreas.kling@nokia.com         * css/StyleSheet.h:\n"
306             u"99187 andreas.kling@nokia.com \n")
307
308         parsed_entries = list(ChangeLog.parse_entries_from_file(changelog_file))
309         self.assertEqual(parsed_entries[0].revision(), 100000)
310         self.assertEqual(parsed_entries[0].reviewer_text(), "Zoltan Herczeg")
311         self.assertEqual(parsed_entries[0].author_name(), u"Csaba Osztrogon\u00e1c")
312         self.assertEqual(parsed_entries[0].author_email(), "ossy@webkit.org")
313         self.assertEqual(parsed_entries[1].revision(), 93798)
314         self.assertEqual(parsed_entries[1].author_name(), "Alexey Proskuryakov")
315         self.assertEqual(parsed_entries[2].revision(), 99190)
316         self.assertEqual(parsed_entries[2].author_name(), "Andreas Kling")
317         self.assertEqual(parsed_entries[3].revision(), 99187)
318         self.assertEqual(parsed_entries[3].author_name(), "Andreas Kling")
319
320     def _assert_parse_reviewer_text_and_list(self, text, expected_reviewer_text, expected_reviewer_text_list=None):
321         reviewer_text, reviewer_text_list = ChangeLogEntry._parse_reviewer_text(text)
322         self.assertEqual(reviewer_text, expected_reviewer_text)
323         if expected_reviewer_text_list:
324             self.assertEqual(reviewer_text_list, expected_reviewer_text_list)
325         else:
326             self.assertEqual(reviewer_text_list, [expected_reviewer_text])
327
328     def _assert_parse_reviewer_text_list(self, text, expected_reviewer_text_list):
329         reviewer_text, reviewer_text_list = ChangeLogEntry._parse_reviewer_text(text)
330         self.assertEqual(reviewer_text_list, expected_reviewer_text_list)
331
332     def test_parse_reviewer_text(self):
333         self._assert_parse_reviewer_text_and_list('  reviewed  by Ryosuke Niwa,   Oliver Hunt, and  Dimitri Glazkov',
334             'Ryosuke Niwa, Oliver Hunt, and Dimitri Glazkov', ['Ryosuke Niwa', 'Oliver Hunt', 'Dimitri Glazkov'])
335         self._assert_parse_reviewer_text_and_list('Reviewed by Brady Eidson and David Levin, landed by Brady Eidson',
336             'Brady Eidson and David Levin', ['Brady Eidson', 'David Levin'])
337
338         self._assert_parse_reviewer_text_and_list('Reviewed by Simon Fraser. Committed by Beth Dakin.', 'Simon Fraser')
339         self._assert_parse_reviewer_text_and_list('Reviewed by Geoff Garen. V8 fixes courtesy of Dmitry Titov.', 'Geoff Garen')
340         self._assert_parse_reviewer_text_and_list('Reviewed by Adam Roben&Dirk Schulze', 'Adam Roben&Dirk Schulze', ['Adam Roben', 'Dirk Schulze'])
341         self._assert_parse_reviewer_text_and_list('Rubber stamps by Darin Adler & Sam Weinig.', 'Darin Adler & Sam Weinig', ['Darin Adler', 'Sam Weinig'])
342
343         self._assert_parse_reviewer_text_and_list('Reviewed by adam,andy and andy adam, andy smith',
344             'adam,andy and andy adam, andy smith', ['adam', 'andy', 'andy adam', 'andy smith'])
345
346         self._assert_parse_reviewer_text_and_list('rubber stamped by Oliver Hunt (oliver@apple.com) and Darin Adler (darin@apple.com)',
347             'Oliver Hunt and Darin Adler', ['Oliver Hunt', 'Darin Adler'])
348
349         self._assert_parse_reviewer_text_and_list('rubber  Stamped by David Hyatt  <hyatt@apple.com>', 'David Hyatt')
350         self._assert_parse_reviewer_text_and_list('Rubber-stamped by Antti Koivisto.', 'Antti Koivisto')
351         self._assert_parse_reviewer_text_and_list('Rubberstamped by Dan Bernstein.', 'Dan Bernstein')
352         self._assert_parse_reviewer_text_and_list('Reviews by Ryosuke Niwa', 'Ryosuke Niwa')
353         self._assert_parse_reviewer_text_and_list('Reviews Ryosuke Niwa', 'Ryosuke Niwa')
354         self._assert_parse_reviewer_text_and_list('Rubberstamp Ryosuke Niwa', 'Ryosuke Niwa')
355         self._assert_parse_reviewer_text_and_list('Typed and reviewed by Alexey Proskuryakov.', 'Alexey Proskuryakov')
356         self._assert_parse_reviewer_text_and_list('Reviewed and landed by Brady Eidson', 'Brady Eidson')
357         self._assert_parse_reviewer_text_and_list('Reviewed by rniwa@webkit.org.', 'rniwa@webkit.org')
358         self._assert_parse_reviewer_text_and_list('Reviewed by Dirk Schulze / Darin Adler.', 'Dirk Schulze / Darin Adler', ['Dirk Schulze', 'Darin Adler'])
359         self._assert_parse_reviewer_text_and_list('Reviewed by Sam Weinig + Oliver Hunt.', 'Sam Weinig + Oliver Hunt', ['Sam Weinig', 'Oliver Hunt'])
360
361         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig, and given a good once-over by Jeff Miller.', ['Sam Weinig', 'Jeff Miller'])
362         self._assert_parse_reviewer_text_list(' Reviewed by Sam Weinig, even though this is just a...', ['Sam Weinig'])
363         self._assert_parse_reviewer_text_list('Rubber stamped by by Gustavo Noronha Silva', ['Gustavo Noronha Silva'])
364         self._assert_parse_reviewer_text_list('Rubberstamped by Noam Rosenthal, who wrote the original code.', ['Noam Rosenthal'])
365         self._assert_parse_reviewer_text_list('Reviewed by Dan Bernstein (relanding of r47157)', ['Dan Bernstein'])
366         self._assert_parse_reviewer_text_list('Reviewed by Geoffrey "Sean/Shawn/Shaun" Garen', ['Geoffrey Garen'])
367         self._assert_parse_reviewer_text_list('Reviewed by Dave "Messy" Hyatt.', ['Dave Hyatt'])
368         self._assert_parse_reviewer_text_list('Reviewed by Sam \'The Belly\' Weinig', ['Sam Weinig'])
369         self._assert_parse_reviewer_text_list('Rubber-stamped by David "I\'d prefer not" Hyatt.', ['David Hyatt'])
370         self._assert_parse_reviewer_text_list('Reviewed by Mr. Geoffrey Garen.', ['Geoffrey Garen'])
371         self._assert_parse_reviewer_text_list('Reviewed by Darin (ages ago)', ['Darin'])
372         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig (except for a few comment and header tweaks).', ['Sam Weinig'])
373         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig (all but the FormDataListItem rename)', ['Sam Weinig'])
374         self._assert_parse_reviewer_text_list('Reviewed by Darin Adler, tweaked and landed by Beth.', ['Darin Adler'])
375         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig with no hesitation', ['Sam Weinig'])
376         self._assert_parse_reviewer_text_list('Reviewed by Oliver Hunt, okayed by Darin Adler.', ['Oliver Hunt'])
377         self._assert_parse_reviewer_text_list('Reviewed by Darin Adler).', ['Darin Adler'])
378
379         # For now, we let unofficial reviewers recognized as reviewers
380         self._assert_parse_reviewer_text_list('Reviewed by Sam Weinig, Anders Carlsson, and (unofficially) Adam Barth.',
381             ['Sam Weinig', 'Anders Carlsson', 'Adam Barth'])
382
383         self._assert_parse_reviewer_text_list('Reviewed by NOBODY.', None)
384         self._assert_parse_reviewer_text_list('Reviewed by NOBODY - Build Fix.', None)
385         self._assert_parse_reviewer_text_list('Reviewed by NOBODY, layout tests fix.', None)
386         self._assert_parse_reviewer_text_list('Reviewed by NOBODY(revert)', None)
387         self._assert_parse_reviewer_text_list('Reviewed by NOBODY (Build fix, forgot to svn add this file)', None)
388         self._assert_parse_reviewer_text_list('Reviewed by nobody (trivial follow up fix), Joseph Pecoraro LGTM-ed.', None)
389
390     def _entry_with_author(self, author_text):
391         return ChangeLogEntry('''2009-08-19  AUTHOR_TEXT
392
393             Reviewed by Ryosuke Niwa
394
395             * Scripts/bugzilla-tool:
396 '''.replace("AUTHOR_TEXT", author_text))
397
398     def _entry_with_reviewer(self, reviewer_line):
399         return ChangeLogEntry('''2009-08-19  Eric Seidel  <eric@webkit.org>
400
401             REVIEW_LINE
402
403             * Scripts/bugzilla-tool:
404 '''.replace("REVIEW_LINE", reviewer_line))
405
406     def _contributors(self, names):
407         return [CommitterList().contributor_by_name(name) for name in names]
408
409     def _assert_fuzzy_radar_match(self, radar_text, expected_radar_id):
410         parsed_radar_id = ChangeLogEntry._parse_radar_id(radar_text)
411         self.assertEqual(parsed_radar_id, expected_radar_id)
412
413     def test_fuzzy_radar_match__none(self):
414         self._assert_fuzzy_radar_match(None, None)
415         self._assert_fuzzy_radar_match('', None)
416
417         self._assert_fuzzy_radar_match('rdar://1', None)
418         self._assert_fuzzy_radar_match('rdar://12', None)
419         self._assert_fuzzy_radar_match('rdar://123', None)
420         self._assert_fuzzy_radar_match('rdar://1234', None)
421         self._assert_fuzzy_radar_match('rdar://12345', None)
422         self._assert_fuzzy_radar_match('rdar://123456', None)
423         self._assert_fuzzy_radar_match('rdar://1234567', None)
424         self._assert_fuzzy_radar_match('rdar://12345678', None)
425
426         self._assert_fuzzy_radar_match('<rdar://1>', None)
427         self._assert_fuzzy_radar_match('<rdar://12>', None)
428         self._assert_fuzzy_radar_match('<rdar://123>', None)
429         self._assert_fuzzy_radar_match('<rdar://1234>', None)
430         self._assert_fuzzy_radar_match('<rdar://12345>', None)
431         self._assert_fuzzy_radar_match('<rdar://123456>', None)
432         self._assert_fuzzy_radar_match('<rdar://1234567>', None)
433         self._assert_fuzzy_radar_match('<rdar://12345678>', None)
434
435         self._assert_fuzzy_radar_match('<rdar://problem/1>', None)
436         self._assert_fuzzy_radar_match('<rdar://problem/12>', None)
437         self._assert_fuzzy_radar_match('<rdar://problem/123>', None)
438         self._assert_fuzzy_radar_match('<rdar://problem/1234>', None)
439         self._assert_fuzzy_radar_match('<rdar://problem/12345>', None)
440         self._assert_fuzzy_radar_match('<rdar://problem/123456>', None)
441
442         self._assert_fuzzy_radar_match('<rdar://problems/1>', None)
443         self._assert_fuzzy_radar_match('<rdar://problems/12>', None)
444         self._assert_fuzzy_radar_match('<rdar://problems/123>', None)
445         self._assert_fuzzy_radar_match('<rdar://problems/1234>', None)
446         self._assert_fuzzy_radar_match('<rdar://problems/12345>', None)
447         self._assert_fuzzy_radar_match('<rdar://problems/123456>', None)
448
449         self._assert_fuzzy_radar_match('rdar://problem/1', None)
450         self._assert_fuzzy_radar_match('rdar://problem/12', None)
451         self._assert_fuzzy_radar_match('rdar://problem/123', None)
452         self._assert_fuzzy_radar_match('rdar://problem/1234', None)
453         self._assert_fuzzy_radar_match('rdar://problem/12345', None)
454         self._assert_fuzzy_radar_match('rdar://problem/123456', None)
455
456         self._assert_fuzzy_radar_match('rdar://problems/1', None)
457         self._assert_fuzzy_radar_match('rdar://problems/12', None)
458         self._assert_fuzzy_radar_match('rdar://problems/123', None)
459         self._assert_fuzzy_radar_match('rdar://problems/1234', None)
460         self._assert_fuzzy_radar_match('rdar://problems/12345', None)
461         self._assert_fuzzy_radar_match('rdar://problems/123456', None)
462
463         self._assert_fuzzy_radar_match('There is no rdar link here', None)
464         self._assert_fuzzy_radar_match('There is no rdar:// link here', None)
465         self._assert_fuzzy_radar_match('There is no malformed <rdar://abcd link here', None)
466         self._assert_fuzzy_radar_match('There is no malformed <rdar://problem> link here', None)
467         self._assert_fuzzy_radar_match('There is no malformed <rdar://problem/abcdefgh> link here', None)
468         self._assert_fuzzy_radar_match('There is no malformed <rdar://problem/1234> link here', None)
469         self._assert_fuzzy_radar_match('                fixed in <rdar://problem/2345678>', None)
470         self._assert_fuzzy_radar_match('                whitespace here <rdar://problem/12345678>', None)
471
472     def test_fuzzy_radar_match_format_1(self):
473         self._assert_fuzzy_radar_match('<rdar://problem/1234567>', 1234567)
474         self._assert_fuzzy_radar_match('<rdar://problem/12345678>', 12345678)
475
476         self._assert_fuzzy_radar_match('<rdar://problems/1234567>', 1234567)
477         self._assert_fuzzy_radar_match('<rdar://problems/12345678>', 12345678)
478
479     def test_fuzzy_radar_match_format_2(self):
480         self._assert_fuzzy_radar_match('rdar://problem/1234567', 1234567)
481         self._assert_fuzzy_radar_match('rdar://problem/12345678', 12345678)
482
483         self._assert_fuzzy_radar_match('rdar://problems/1234567', 1234567)
484         self._assert_fuzzy_radar_match('rdar://problems/12345678', 12345678)
485
486     def test_fuzzy_radar_match_format_3(self):
487         contents = """
488         2011-03-23  Ojan Vafai  <ojan@chromium.org>
489
490                 Add failing result for WebKit2. All tests that require
491                 focus fail on WebKit2. See https://bugs.webkit.org/show_bug.cgi?id=56988.
492                 <rdar://problem/42824228>
493
494                 * platform/mac-wk2/fast/css/pseudo-any-expected.txt: Added.
495
496                '''"""
497         self._assert_fuzzy_radar_match(contents, 42824228)
498
499         contents = """
500         2018-08-02  Wenson Hsieh  <wenson_hsieh@apple.com>
501
502                 [iOS] Keyboard becomes unresponsive after pressing delete while pressing down on a character key with accents
503                 https://bugs.webkit.org/show_bug.cgi?id=188251
504                 <rdar://problem/37842108>
505         """
506         self._assert_fuzzy_radar_match(contents, 37842108)
507
508         contents = """
509         2017-06-26  Wenson Hsieh  <wenson_hsieh@apple.com>
510
511                 Refactor drag start codepaths to plumb a DragItem to client layers
512                 https://bugs.webkit.org/show_bug.cgi?id=173832
513                 Work towards <rdar://problem/32236827>
514
515                 Reviewed by Ryosuke Niwa and Tim Horton.
516         """
517         self._assert_fuzzy_radar_match(contents, None)
518
519     def _assert_fuzzy_reviewer_match(self, reviewer_text, expected_text_list, expected_contributors):
520         unused, reviewer_text_list = ChangeLogEntry._parse_reviewer_text(reviewer_text)
521         self.assertEqual(reviewer_text_list, expected_text_list)
522         self.assertEqual(self._entry_with_reviewer(reviewer_text).reviewers(), self._contributors(expected_contributors))
523
524     def test_fuzzy_reviewer_match__none(self):
525         self._assert_fuzzy_reviewer_match('Reviewed by BUILD FIX', ['BUILD FIX'], [])
526         self._assert_fuzzy_reviewer_match('Reviewed by Mac build fix', ['Mac build fix'], [])
527
528     def test_fuzzy_reviewer_match_adam_barth(self):
529         self._assert_fuzzy_reviewer_match('Reviewed by Adam Barth.:w', ['Adam Barth.:w'], ['Adam Barth'])
530
531     def test_fuzzy_reviewer_match_darin_adler_et_al(self):
532         self._assert_fuzzy_reviewer_match('Reviewed by Darin Adler in <https://bugs.webkit.org/show_bug.cgi?id=47736>.', ['Darin Adler in'], ['Darin Adler'])
533         self._assert_fuzzy_reviewer_match('Reviewed by Darin Adler, Dan Bernstein, Adele Peterson, and others.',
534             ['Darin Adler', 'Dan Bernstein', 'Adele Peterson', 'others'], ['Darin Adler', 'Dan Bernstein', 'Adele Peterson'])
535
536     def test_fuzzy_reviewer_match_dimitri_glazkov(self):
537         self._assert_fuzzy_reviewer_match('Reviewed by Dimitri Glazkov, build fix', ['Dimitri Glazkov', 'build fix'], ['Dimitri Glazkov'])
538
539     def test_fuzzy_reviewer_match_george_staikos(self):
540         self._assert_fuzzy_reviewer_match('Reviewed by George Staikos (and others)', ['George Staikos', 'others'], ['George Staikos'])
541
542     def test_fuzzy_reviewer_match_mark_rowe(self):
543         self._assert_fuzzy_reviewer_match('Reviewed by Mark Rowe, but Dan Bernstein also reviewed and asked thoughtful questions.',
544             ['Mark Rowe', 'but Dan Bernstein also reviewed', 'asked thoughtful questions'], ['Mark Rowe'])
545
546     def test_fuzzy_reviewer_match_initial(self):
547         self._assert_fuzzy_reviewer_match('Reviewed by Alejandro G. Castro.',
548             ['Alejandro G. Castro'], ['Alejandro G. Castro'])
549         self._assert_fuzzy_reviewer_match('Reviewed by G. Alejandro G. Castro and others.',
550             ['G. Alejandro G. Castro', 'others'], ['Alejandro G. Castro'])
551
552         # If a reviewer has a name that ended with an initial, the regular expression
553         # will incorrectly trim the last period, but it will still match fuzzily to
554         # the full reviewer name.
555         self._assert_fuzzy_reviewer_match('Reviewed by G. Alejandro G. G. Castro G.',
556             ['G. Alejandro G. G. Castro G'], ['Alejandro G. Castro'])
557
558     def _assert_parse_authors(self, author_text, expected_contributors):
559         parsed_authors = [(author['name'], author['email']) for author in self._entry_with_author(author_text).authors()]
560         self.assertEqual(parsed_authors, expected_contributors)
561
562     def test_parse_authors(self):
563         self._assert_parse_authors(u'Aaron Colwell  <acolwell@chromium.org>', [(u'Aaron Colwell', u'acolwell@chromium.org')])
564         self._assert_parse_authors('Eric Seidel  <eric@webkit.org>, Ryosuke Niwa  <rniwa@webkit.org>',
565             [('Eric Seidel', 'eric@webkit.org'), ('Ryosuke Niwa', 'rniwa@webkit.org')])
566         self._assert_parse_authors('Zan Dobersek  <zandobersek@gmail.com> and Philippe Normand  <pnormand@igalia.com>',
567             [('Zan Dobersek', 'zandobersek@gmail.com'), ('Philippe Normand', 'pnormand@igalia.com')])
568         self._assert_parse_authors('New Contributor  <new@webkit.org> and Noob  <noob@webkit.org>',
569             [('New Contributor', 'new@webkit.org'), ('Noob', 'noob@webkit.org')])
570         self._assert_parse_authors('Adam Barth  <abarth@webkit.org> && Benjamin Poulain  <bpoulain@apple.com>',
571             [('Adam Barth', 'abarth@webkit.org'), ('Benjamin Poulain', 'bpoulain@apple.com')])
572         self._assert_parse_authors(u'Pawe\u0142 Hajdan, Jr.  <phajdan.jr@chromium.org>',
573             [(u'Pawe\u0142 Hajdan, Jr.', u'phajdan.jr@chromium.org')])
574         self._assert_parse_authors(u'Pawe\u0142 Hajdan, Jr.  <phajdan.jr@chromium.org>, Adam Barth  <abarth@webkit.org>',
575             [(u'Pawe\u0142 Hajdan, Jr.', u'phajdan.jr@chromium.org'), (u'Adam Barth', u'abarth@webkit.org')])
576
577     def _assert_has_valid_reviewer(self, reviewer_line, expected):
578         self.assertEqual(self._entry_with_reviewer(reviewer_line).has_valid_reviewer(), expected)
579
580     def test_has_valid_reviewer(self):
581         self._assert_has_valid_reviewer("Reviewed by Darin Adler.", True)
582         self._assert_has_valid_reviewer("Reviewed by Darin Adler", True)  # Not picky about the '.'
583         self._assert_has_valid_reviewer("Reviewed by Darin.", False)
584         self._assert_has_valid_reviewer("Reviewed by Darin B Adler.", False)
585         self._assert_has_valid_reviewer("Rubber-stamped by Darin.", False)
586         self._assert_has_valid_reviewer("Rubber-stamped by Darin Adler.", True)
587         self._assert_has_valid_reviewer("Rubber stamped by Darin.", False)
588         self._assert_has_valid_reviewer("Rubber stamped by Darin Adler.", True)
589         self._assert_has_valid_reviewer("Unreviewed build fix.", True)
590         self._assert_has_valid_reviewer("Reviewed by Gabor Rapcsanyi.", False)
591         self._assert_has_valid_reviewer("Reviewed by Myles Maxfield", True)
592         self._assert_has_valid_reviewer("Reviewed by Myles C. Maxfield", True)
593
594     def test_is_touched_files_text_clean(self):
595         tests = [
596         ('''2013-01-30  Timothy Loh  <timloh@chromium.com>
597
598         Make ChangeLogEntry detect annotations by prepare-ChangeLog (Added/Removed/Copied from/Renamed from) as clean.
599         https://bugs.webkit.org/show_bug.cgi?id=108433
600
601         * Scripts/webkitpy/common/checkout/changelog.py:
602         (ChangeLogEntry.is_touched_files_text_clean):
603         * Scripts/webkitpy/common/checkout/changelog_unittest.py:
604         (test_is_touched_files_text_clean):
605 ''', True),
606         ('''2013-01-10  Alan Cutter  <alancutter@chromium.org>
607
608         Perform some file operations (automatically added comments).
609
610         * Scripts/webkitpy/tool/bot/testdata/webkit_sheriff_0.js: Removed.
611         * EWSTools/build-vm.sh: Renamed from Tools/EWSTools/cold-boot.sh.
612 ''', True),
613         ('''2013-01-30  Timothy Loh  <timloh@chromium.com>
614
615         Add unit test (manually added comment).
616
617         * Scripts/webkitpy/common/checkout/changelog_unittest.py:
618         (test_is_touched_files_text_clean): Added.
619 ''', False),
620         ('''2013-01-30  Timothy Loh  <timloh@chromium.com>
621
622         Add file (manually added comment).
623
624         * Scripts/webkitpy/common/checkout/super_changelog.py: Copied from the internet.
625 ''', False),
626         ]
627
628         for contents, expected_result in tests:
629             entry = ChangeLogEntry(contents)
630             self.assertEqual(entry.is_touched_files_text_clean(), expected_result)
631
632     def test_latest_entry_parse(self):
633         changelog_contents = u"%s\n%s" % (self._example_entry, self._example_changelog)
634         changelog_file = StringIO(changelog_contents)
635         latest_entry = ChangeLog.parse_latest_entry_from_file(changelog_file)
636         self.assertEqual(latest_entry.contents(), self._example_entry)
637         self.assertEqual(latest_entry.author_name(), "Peter Kasting")
638         self.assertEqual(latest_entry.author_email(), "pkasting@google.com")
639         self.assertEqual(latest_entry.reviewer_text(), u"Fr\u00e9d\u00e9ric Wang")
640         touched_files = ["DumpRenderTree/win/DumpRenderTree.vcproj", "DumpRenderTree/win/ImageDiff.vcproj", "DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj"]
641         self.assertEqual(latest_entry.touched_files(), touched_files)
642         self.assertEqual(latest_entry.touched_functions(), dict((f, []) for f in touched_files))
643
644         self.assertTrue(latest_entry.reviewer())  # Make sure that our UTF8-based lookup of Tor works.
645
646     def test_latest_entry_parse_single_entry(self):
647         changelog_contents = u"%s\n%s" % (self._example_entry, self._rolled_over_footer)
648         changelog_file = StringIO(changelog_contents)
649         latest_entry = ChangeLog.parse_latest_entry_from_file(changelog_file)
650         self.assertEqual(latest_entry.contents(), self._example_entry)
651         self.assertEqual(latest_entry.author_name(), "Peter Kasting")
652
653     # FIXME: We really should be getting this from prepare-ChangeLog itself.
654     _new_entry_boilerplate = '''2009-08-19  Eric Seidel  <eric@webkit.org>
655
656         Need a short description (OOPS!).
657         Need the bug URL (OOPS!).
658
659         Reviewed by NOBODY (OOPS!).
660
661         * Scripts/bugzilla-tool:
662 '''
663
664     _new_entry_boilerplate_with_bugurl = '''2009-08-19  Eric Seidel  <eric@webkit.org>
665
666         Need a short description (OOPS!).
667         https://bugs.webkit.org/show_bug.cgi?id=12345
668
669         Reviewed by NOBODY (OOPS!).
670
671         * Scripts/bugzilla-tool:
672 '''
673
674     _new_entry_boilerplate_with_unreviewed = '''2009-08-19  Eric Seidel  <eric@webkit.org>
675
676         Need a short description (OOPS!).
677         https://bugs.webkit.org/show_bug.cgi?id=12345
678
679         Unreviewed.
680
681         * Scripts/bugzilla-tool:
682 '''
683
684     _new_entry_boilerplate_with_multiple_bugurl = '''2009-08-19  Eric Seidel  <eric@webkit.org>
685
686         Need a short description (OOPS!).
687         https://bugs.webkit.org/show_bug.cgi?id=12345
688         http://webkit.org/b/12345
689
690         Reviewed by NOBODY (OOPS!).
691
692         * Scripts/bugzilla-tool:
693 '''
694
695     _new_entry_boilerplate_without_reviewer_line = '''2009-08-19  Eric Seidel  <eric@webkit.org>
696
697         Need a short description (OOPS!).
698         https://bugs.webkit.org/show_bug.cgi?id=12345
699
700         * Scripts/bugzilla-tool:
701 '''
702
703     _new_entry_boilerplate_without_reviewer_multiple_bugurl = '''2009-08-19  Eric Seidel  <eric@webkit.org>
704
705         Need a short description (OOPS!).
706         https://bugs.webkit.org/show_bug.cgi?id=12345
707         http://webkit.org/b/12345
708
709         * Scripts/bugzilla-tool:
710 '''
711
712     def test_set_reviewer(self):
713         fs = MockFileSystem()
714
715         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate_with_bugurl, self._example_changelog)
716         reviewer_name = 'Test Reviewer'
717         fs.write_text_file(self._changelog_path, changelog_contents)
718         ChangeLog(self._changelog_path, fs).set_reviewer(reviewer_name)
719         actual_contents = fs.read_text_file(self._changelog_path)
720         expected_contents = changelog_contents.replace('NOBODY (OOPS!)', reviewer_name)
721         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
722
723         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate_with_unreviewed, self._example_changelog)
724         fs.write_text_file(self._changelog_path, changelog_contents)
725         ChangeLog(self._changelog_path, fs).set_reviewer(reviewer_name)
726         actual_contents = fs.read_text_file(self._changelog_path)
727         self.assertEqual(actual_contents.splitlines(), changelog_contents.splitlines())
728
729         changelog_contents_without_reviewer_line = u"%s\n%s" % (self._new_entry_boilerplate_without_reviewer_line, self._example_changelog)
730         fs.write_text_file(self._changelog_path, changelog_contents_without_reviewer_line)
731         ChangeLog(self._changelog_path, fs).set_reviewer(reviewer_name)
732         actual_contents = fs.read_text_file(self._changelog_path)
733         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
734
735         changelog_contents_without_reviewer_line = u"%s\n%s" % (self._new_entry_boilerplate_without_reviewer_multiple_bugurl, self._example_changelog)
736         fs.write_text_file(self._changelog_path, changelog_contents_without_reviewer_line)
737         ChangeLog(self._changelog_path, fs).set_reviewer(reviewer_name)
738         actual_contents = fs.read_text_file(self._changelog_path)
739         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate_with_multiple_bugurl, self._example_changelog)
740         expected_contents = changelog_contents.replace('NOBODY (OOPS!)', reviewer_name)
741         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
742
743     def test_set_short_description_and_bug_url(self):
744         fs = MockFileSystem()
745
746         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate_with_bugurl, self._example_changelog)
747         fs.write_text_file(self._changelog_path, changelog_contents)
748         short_description = "A short description"
749         bug_url = "http://example.com/b/2344"
750         ChangeLog(self._changelog_path, fs).set_short_description_and_bug_url(short_description, bug_url)
751         actual_contents = fs.read_text_file(self._changelog_path)
752         expected_contents = changelog_contents.replace("Need a short description (OOPS!).", short_description)
753         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
754
755         changelog_contents = u"%s\n%s" % (self._new_entry_boilerplate, self._example_changelog)
756         fs.write_text_file(self._changelog_path, changelog_contents)
757         short_description = "A short description 2"
758         bug_url = "http://example.com/b/2345"
759         ChangeLog(self._changelog_path, fs).set_short_description_and_bug_url(short_description, bug_url)
760         actual_contents = fs.read_text_file(self._changelog_path)
761         expected_message = "%s\n        %s" % (short_description, bug_url)
762         expected_contents = changelog_contents.replace("Need a short description (OOPS!).\n        Need the bug URL (OOPS!).", expected_message)
763         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
764
765     def test_delete_entries(self):
766         fs = MockFileSystem()
767         fs.write_text_file(self._changelog_path, self._example_changelog)
768         ChangeLog(self._changelog_path, fs).delete_entries(8)
769         actual_contents = fs.read_text_file(self._changelog_path)
770         expected_contents = """2011-10-11  Antti Koivisto  <antti@apple.com>
771
772        Resolve regular and visited link style in a single pass
773        https://bugs.webkit.org/show_bug.cgi?id=69838
774
775        Reviewed by Darin Adler
776
777        We can simplify and speed up selector matching by removing the recursive matching done
778        to generate the style for the :visited pseudo selector. Both regular and visited link style
779        can be generated in a single pass through the style selector.
780
781 == Rolled over to ChangeLog-2009-06-16 ==
782 """
783         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
784
785         ChangeLog(self._changelog_path, fs).delete_entries(2)
786         actual_contents = fs.read_text_file(self._changelog_path)
787         expected_contents = "== Rolled over to ChangeLog-2009-06-16 ==\n"
788         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())
789
790     def test_prepend_text(self):
791         fs = MockFileSystem()
792         fs.write_text_file(self._changelog_path, self._example_changelog)
793         ChangeLog(self._changelog_path, fs).prepend_text(self._example_entry + "\n")
794         actual_contents = fs.read_text_file(self._changelog_path)
795         expected_contents = self._example_entry + "\n" + self._example_changelog
796         self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines())