Changeset 140491 in webkit
- Timestamp:
- Jan 22, 2013 5:36:37 PM (11 years ago)
- Location:
- trunk/Tools
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r140476 r140491 1 2013-01-22 Timothy Loh <timloh@chromium.com> 2 3 Add functions to ChangeLog - parse bug desc/changed functions, delete/prepend entries 4 https://bugs.webkit.org/show_bug.cgi?id=107478 5 6 Reviewed by Eric Seidel. 7 8 On the road to resolving Bug 74358, we need a few more functions in 9 changelog.py. 10 To make things easier to mock, change @staticmethods to @classmethods. 11 12 * Scripts/webkitpy/common/checkout/changelog.py: 13 (ChangeLogEntry): 14 (ChangeLogEntry._parse_reviewer_text): 15 (ChangeLogEntry._split_contributor_names): 16 (ChangeLogEntry._parse_author_name_and_email): 17 (ChangeLogEntry._parse_author_text): 18 (ChangeLogEntry._parse_touched_functions): 19 (ChangeLogEntry._parse_bug_description): 20 (ChangeLogEntry._parse_entry): 21 (ChangeLogEntry.date_line): 22 (ChangeLogEntry.bug_description): 23 (ChangeLogEntry.touched_functions): 24 (ChangeLogEntry.touched_files_text): 25 (ChangeLogEntry.is_touched_files_text_clean): 26 (ChangeLog): 27 (ChangeLog.parse_latest_entry_from_file): 28 (ChangeLog._separate_revision_and_line): 29 (ChangeLog.parse_entries_from_file): 30 (ChangeLog.set_short_description_and_bug_url): 31 (ChangeLog.delete_entries): 32 (ChangeLog.prepend_text): 33 * Scripts/webkitpy/common/checkout/changelog_unittest.py: 34 (test_parse_log_entries_from_changelog): 35 (test_latest_entry_parse): 36 (test_set_short_description_and_bug_url): 37 (test_delete_entries): 38 (test_prepend_text): 39 1 40 2013-01-22 Lucas Forschler <lforschler@apple.com> 2 41 -
trunk/Tools/Scripts/webkitpy/common/checkout/changelog.py
r135912 r140491 65 65 # e.g. * Source/WebCore/page/EventHandler.cpp: Implement FooBarQuux. 66 66 touched_files_regexp = r'^\s*\*\s*(?P<file>[A-Za-z0-9_\-\./\\]+)\s*\:' 67 # e.g. (ChangeLogEntry.touched_functions): Added. 68 touched_functions_regexp = r'^\s*\((?P<function>[^)]*)\):' 67 69 68 70 # e.g. Reviewed by Darin Adler. … … 109 111 self._parse_entry() 110 112 111 @ staticmethod112 def _parse_reviewer_text( text):113 @classmethod 114 def _parse_reviewer_text(cls, text): 113 115 match = re.search(ChangeLogEntry.reviewed_by_regexp, text, re.MULTILINE | re.IGNORECASE) 114 116 if not match: … … 138 140 return reviewer_text, reviewer_list 139 141 140 @ staticmethod141 def _split_contributor_names( text):142 @classmethod 143 def _split_contributor_names(cls, text): 142 144 return re.split(r'\s*(?:,(?:\s+and\s+|&)?|(?:^|\s+)and\s+|&&|[/+&])\s*', text) 143 145 … … 149 151 return [reviewers[0] for reviewers in list_of_reviewers if len(reviewers) == 1] 150 152 151 @ staticmethod152 def _parse_author_name_and_email( author_name_and_email):153 @classmethod 154 def _parse_author_name_and_email(cls, author_name_and_email): 153 155 match = re.match(r'(?P<name>.+?)\s+<(?P<email>[^>]+)>', author_name_and_email) 154 156 return {'name': match.group("name"), 'email': match.group("email")} 155 157 156 @ staticmethod157 def _parse_author_text( text):158 @classmethod 159 def _parse_author_text(cls, text): 158 160 if not text: 159 161 return [] 160 authors = ChangeLogEntry._split_contributor_names(text)162 authors = cls._split_contributor_names(text) 161 163 assert(authors and len(authors) >= 1) 162 return [ChangeLogEntry._parse_author_name_and_email(author) for author in authors] 164 return [cls._parse_author_name_and_email(author) for author in authors] 165 166 @classmethod 167 def _parse_touched_functions(cls, text): 168 result = {} 169 cur_file = None 170 for line in text.splitlines(): 171 file_match = re.match(cls.touched_files_regexp, line) 172 if file_match: 173 cur_file = file_match.group("file") 174 result[cur_file] = [] 175 func_match = re.match(cls.touched_functions_regexp, line) 176 if func_match and cur_file: 177 result[cur_file].append(func_match.group("function")) 178 return result 179 180 @classmethod 181 def _parse_bug_description(cls, text): 182 # If line 4 is a bug url, line 3 is the bug description. 183 # It's too hard to guess in other cases, so we return None. 184 lines = text.splitlines() 185 if len(lines) < 4: 186 return None 187 for bug_url in (config_urls.bug_url_short, config_urls.bug_url_long): 188 if re.match("^\s*" + bug_url + "$", lines[3]): 189 return lines[2].strip() 190 return None 163 191 164 192 def _parse_entry(self): … … 167 195 _log.warning("Creating invalid ChangeLogEntry:\n%s" % self._contents) 168 196 197 self._date_line = match.group() 198 self._bug_description = self._parse_bug_description(self._contents) 199 169 200 # FIXME: group("name") does not seem to be Unicode? Probably due to self._contents not being unicode. 170 201 self._author_text = match.group("authors") if match else None … … 176 207 177 208 self._touched_files = re.findall(self.touched_files_regexp, self._contents, re.MULTILINE) 209 self._touched_functions = self._parse_touched_functions(self._contents) 210 211 def date_line(self): 212 return self._date_line 178 213 179 214 def author_text(self): … … 221 256 return parse_bug_id_from_changelog(self._contents) 222 257 258 def bug_description(self): 259 return self._bug_description 260 223 261 def touched_files(self): 224 262 return self._touched_files 225 263 264 # Returns a dict from file name to lists of function names. 265 def touched_functions(self): 266 return self._touched_functions 267 268 def touched_files_text(self): 269 match = re.search(self.touched_files_regexp, self._contents, re.MULTILINE) 270 return self._contents[match.start():].lstrip("\n\r") if match else "" 271 272 # Determine if any text has been added to the section on touched files 273 def is_touched_files_text_clean(self): 274 for line in self.touched_files_text().splitlines(): 275 if re.match(self.touched_files_regexp + "$", line): 276 continue 277 if re.match(self.touched_functions_regexp + "$", line): 278 continue 279 return False 280 return True 226 281 227 282 # FIXME: Various methods on ChangeLog should move into ChangeLogEntry instead. … … 233 288 _changelog_indent = " " * 8 234 289 235 @ staticmethod236 def parse_latest_entry_from_file(c hangelog_file):290 @classmethod 291 def parse_latest_entry_from_file(cls, changelog_file): 237 292 """changelog_file must be a file-like object which returns 238 293 unicode strings. Use codecs.open or StringIO(unicode()) … … 258 313 svn_blame_regexp = re.compile(r'^(\s*(?P<revision>\d+) [^ ]+)\s*(?P<line>.*?\n)') 259 314 260 @ staticmethod261 def _separate_revision_and_line( line):262 match = ChangeLog.svn_blame_regexp.match(line)315 @classmethod 316 def _separate_revision_and_line(cls, line): 317 match = cls.svn_blame_regexp.match(line) 263 318 if not match: 264 319 return None, line 265 320 return int(match.group('revision')), match.group('line') 266 321 267 @ staticmethod268 def parse_entries_from_file(c hangelog_file):322 @classmethod 323 def parse_entries_from_file(cls, changelog_file): 269 324 """changelog_file must be a file-like object which returns 270 325 unicode strings. Use codecs.open or StringIO(unicode()) … … 274 329 275 330 # The first line should be a date line. 276 revision, first_line = ChangeLog._separate_revision_and_line(changelog_file.readline())331 revision, first_line = cls._separate_revision_and_line(changelog_file.readline()) 277 332 assert(isinstance(first_line, unicode)) 278 if not date_line_regexp.match( ChangeLog.svn_blame_regexp.sub('', first_line)):333 if not date_line_regexp.match(cls.svn_blame_regexp.sub('', first_line)): 279 334 raise StopIteration 280 335 … … 283 338 for line in changelog_file: 284 339 if revisions_in_entry: 285 revision, line = ChangeLog._separate_revision_and_line(line)340 revision, line = cls._separate_revision_and_line(line) 286 341 287 342 if rolled_over_regexp.match(line): … … 378 433 if line != bug_boilerplate: 379 434 print line, 435 436 def delete_entries(self, num_entries): 437 date_line_regexp = re.compile(ChangeLogEntry.date_line_regexp) 438 rolled_over_regexp = re.compile(ChangeLogEntry.rolled_over_regexp) 439 entries = 0 440 for line in fileinput.FileInput(self.path, inplace=1): 441 if date_line_regexp.match(line): 442 entries += 1 443 elif rolled_over_regexp.match(line): 444 entries = num_entries + 1 445 if entries > num_entries: 446 print line, 447 448 def prepend_text(self, text): 449 data = codecs.open(self.path, "r", "utf-8").read() 450 codecs.open(self.path, "w", "utf-8").write(text + data) -
trunk/Tools/Scripts/webkitpy/common/checkout/changelog_unittest.py
r134636 r140491 237 237 parsed_entries = list(ChangeLog.parse_entries_from_file(changelog_file)) 238 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>") 239 240 self.assertEqual(parsed_entries[0].reviewer_text(), "David Levin") 241 self.assertEqual(parsed_entries[0].is_touched_files_text_clean(), False) 240 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) 241 245 self.assertEqual(parsed_entries[2].reviewer_text(), "Mark Rowe") 242 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) 243 249 self.assertEqual(parsed_entries[3].author_name(), "Benjamin Poulain") 244 250 self.assertEqual(parsed_entries[3].touched_files(), ["platform/cf/KURLCFNet.cpp", "platform/mac/KURLMac.mm", 245 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") 246 256 self.assertEqual(parsed_entries[4].reviewer_text(), "David Hyatt") 257 self.assertEqual(parsed_entries[4].bug_description(), None) 247 258 self.assertEqual(parsed_entries[5].reviewer_text(), "Adam Roben") 248 259 self.assertEqual(parsed_entries[6].reviewer_text(), "Tony Chang") … … 463 474 self.assertEqual(latest_entry.author_email(), "pkasting@google.com") 464 475 self.assertEqual(latest_entry.reviewer_text(), u"Tor Arne Vestb\xf8") 465 self.assertEqual(latest_entry.touched_files(), ["DumpRenderTree/win/DumpRenderTree.vcproj", "DumpRenderTree/win/ImageDiff.vcproj", "DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj"]) 476 touched_files = ["DumpRenderTree/win/DumpRenderTree.vcproj", "DumpRenderTree/win/ImageDiff.vcproj", "DumpRenderTree/win/TestNetscapePlugin/TestNetscapePlugin.vcproj"] 477 self.assertEqual(latest_entry.touched_files(), touched_files) 478 self.assertEqual(latest_entry.touched_functions(), dict((f, []) for f in touched_files)) 466 479 467 480 self.assertTrue(latest_entry.reviewer()) # Make sure that our UTF8-based lookup of Tor works. … … 584 597 os.remove(changelog_path) 585 598 self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines()) 599 600 def test_delete_entries(self): 601 changelog_path = self._write_tmp_file_with_contents(self._example_changelog.encode("utf-8")) 602 ChangeLog(changelog_path).delete_entries(8) 603 actual_contents = self._read_file_contents(changelog_path, "utf-8") 604 expected_contents = """2011-10-11 Antti Koivisto <antti@apple.com> 605 606 Resolve regular and visited link style in a single pass 607 https://bugs.webkit.org/show_bug.cgi?id=69838 608 609 Reviewed by Darin Adler 610 611 We can simplify and speed up selector matching by removing the recursive matching done 612 to generate the style for the :visited pseudo selector. Both regular and visited link style 613 can be generated in a single pass through the style selector. 614 615 == Rolled over to ChangeLog-2009-06-16 == 616 """ 617 self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines()) 618 619 ChangeLog(changelog_path).delete_entries(2) 620 actual_contents = self._read_file_contents(changelog_path, "utf-8") 621 expected_contents = "== Rolled over to ChangeLog-2009-06-16 ==\n" 622 self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines()) 623 624 os.remove(changelog_path) 625 626 def test_prepend_text(self): 627 changelog_path = self._write_tmp_file_with_contents(self._example_changelog.encode("utf-8")) 628 ChangeLog(changelog_path).prepend_text(self._example_entry + "\n") 629 actual_contents = self._read_file_contents(changelog_path, "utf-8") 630 expected_contents = self._example_entry + "\n" + self._example_changelog 631 self.assertEqual(actual_contents.splitlines(), expected_contents.splitlines()) 632 os.remove(changelog_path)
Note: See TracChangeset
for help on using the changeset viewer.