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 29import unittest 30import datetime 31import StringIO 32 33from .bugzilla import Bugzilla, BugzillaQueries, parse_bug_id, parse_bug_id_from_changelog 34 35from webkitpy.common.system.outputcapture import OutputCapture 36from webkitpy.tool.mocktool import MockBrowser 37from webkitpy.thirdparty.mock import Mock 38from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup 39 40 41class BugzillaTest(unittest.TestCase): 42 _example_attachment = ''' 43 <attachment 44 isobsolete="1" 45 ispatch="1" 46 isprivate="0" 47 > 48 <attachid>33721</attachid> 49 <date>2009-07-29 10:23 PDT</date> 50 <desc>Fixed whitespace issue</desc> 51 <filename>patch</filename> 52 <type>text/plain</type> 53 <size>9719</size> 54 <attacher>christian.plesner.hansen@gmail.com</attacher> 55 <flag name="review" 56 id="17931" 57 status="+" 58 setter="one@test.com" 59 /> 60 <flag name="commit-queue" 61 id="17932" 62 status="+" 63 setter="two@test.com" 64 /> 65 </attachment> 66''' 67 _expected_example_attachment_parsing = { 68 'attach_date': datetime.datetime(2009, 07, 29, 10, 23), 69 'bug_id' : 100, 70 'is_obsolete' : True, 71 'is_patch' : True, 72 'id' : 33721, 73 'url' : "https://bugs.webkit.org/attachment.cgi?id=33721", 74 'name' : "Fixed whitespace issue", 75 'type' : "text/plain", 76 'review' : '+', 77 'reviewer_email' : 'one@test.com', 78 'commit-queue' : '+', 79 'committer_email' : 'two@test.com', 80 'attacher_email' : 'christian.plesner.hansen@gmail.com', 81 } 82 83 def test_url_creation(self): 84 # FIXME: These would be all better as doctests 85 bugs = Bugzilla() 86 self.assertEquals(None, bugs.bug_url_for_bug_id(None)) 87 self.assertEquals(None, bugs.short_bug_url_for_bug_id(None)) 88 self.assertEquals(None, bugs.attachment_url_for_id(None)) 89 90 def test_parse_bug_id(self): 91 # FIXME: These would be all better as doctests 92 bugs = Bugzilla() 93 self.assertEquals(12345, parse_bug_id("http://webkit.org/b/12345")) 94 self.assertEquals(12345, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?id=12345")) 95 self.assertEquals(12345, parse_bug_id(bugs.short_bug_url_for_bug_id(12345))) 96 self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345))) 97 self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345, xml=True))) 98 99 # Our bug parser is super-fragile, but at least we're testing it. 100 self.assertEquals(None, parse_bug_id("http://www.webkit.org/b/12345")) 101 self.assertEquals(None, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?ctype=xml&id=12345")) 102 103 _bug_xml = """ 104 <bug> 105 <bug_id>32585</bug_id> 106 <creation_ts>2009-12-15 15:17 PST</creation_ts> 107 <short_desc>bug to test webkit-patch's and commit-queue's failures</short_desc> 108 <delta_ts>2009-12-27 21:04:50 PST</delta_ts> 109 <reporter_accessible>1</reporter_accessible> 110 <cclist_accessible>1</cclist_accessible> 111 <classification_id>1</classification_id> 112 <classification>Unclassified</classification> 113 <product>WebKit</product> 114 <component>Tools / Tests</component> 115 <version>528+ (Nightly build)</version> 116 <rep_platform>PC</rep_platform> 117 <op_sys>Mac OS X 10.5</op_sys> 118 <bug_status>NEW</bug_status> 119 <priority>P2</priority> 120 <bug_severity>Normal</bug_severity> 121 <target_milestone>---</target_milestone> 122 <everconfirmed>1</everconfirmed> 123 <reporter name="Eric Seidel">eric@webkit.org</reporter> 124 <assigned_to name="Nobody">webkit-unassigned@lists.webkit.org</assigned_to> 125 <cc>foo@bar.com</cc> 126 <cc>example@example.com</cc> 127 <long_desc isprivate="0"> 128 <who name="Eric Seidel">eric@webkit.org</who> 129 <bug_when>2009-12-15 15:17:28 PST</bug_when> 130 <thetext>bug to test webkit-patch and commit-queue failures 131 132Ignore this bug. Just for testing failure modes of webkit-patch and the commit-queue.</thetext> 133 </long_desc> 134 <attachment 135 isobsolete="0" 136 ispatch="1" 137 isprivate="0" 138 > 139 <attachid>45548</attachid> 140 <date>2009-12-27 23:51 PST</date> 141 <desc>Patch</desc> 142 <filename>bug-32585-20091228005112.patch</filename> 143 <type>text/plain</type> 144 <size>10882</size> 145 <attacher>mjs@apple.com</attacher> 146 147 <token>1261988248-dc51409e9c421a4358f365fa8bec8357</token> 148 <data encoding="base64">SW5kZXg6IFdlYktpdC9tYWMvQ2hhbmdlTG9nCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09 149removed-because-it-was-really-long 150ZEZpbmlzaExvYWRXaXRoUmVhc29uOnJlYXNvbl07Cit9CisKIEBlbmQKIAogI2VuZGlmCg== 151</data> 152 153 <flag name="review" 154 id="27602" 155 status="?" 156 setter="mjs@apple.com" 157 /> 158 </attachment> 159 </bug> 160""" 161 162 _single_bug_xml = """ 163<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 164<!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/bugzilla.dtd"> 165<bugzilla version="3.2.3" 166 urlbase="https://bugs.webkit.org/" 167 maintainer="admin@webkit.org" 168 exporter="eric@webkit.org" 169> 170%s 171</bugzilla> 172""" % _bug_xml 173 174 _expected_example_bug_parsing = { 175 "id" : 32585, 176 "title" : u"bug to test webkit-patch's and commit-queue's failures", 177 "cc_emails" : ["foo@bar.com", "example@example.com"], 178 "reporter_email" : "eric@webkit.org", 179 "assigned_to_email" : "webkit-unassigned@lists.webkit.org", 180 "bug_status": "NEW", 181 "attachments" : [{ 182 "attach_date": datetime.datetime(2009, 12, 27, 23, 51), 183 'name': u'Patch', 184 'url' : "https://bugs.webkit.org/attachment.cgi?id=45548", 185 'is_obsolete': False, 186 'review': '?', 187 'is_patch': True, 188 'attacher_email': 'mjs@apple.com', 189 'bug_id': 32585, 190 'type': 'text/plain', 191 'id': 45548 192 }], 193 } 194 195 def test_parse_bug_id_from_changelog(self): 196 commit_text = ''' 1972011-03-23 Ojan Vafai <ojan@chromium.org> 198 199 Add failing result for WebKit2. All tests that require 200 focus fail on WebKit2. See https://bugs.webkit.org/show_bug.cgi?id=56988. 201 202 * platform/mac-wk2/fast/css/pseudo-any-expected.txt: Added. 203 204 ''' 205 206 self.assertEquals(56988, parse_bug_id_from_changelog(commit_text)) 207 208 commit_text = ''' 2092011-03-23 Ojan Vafai <ojan@chromium.org> 210 211 Add failing result for WebKit2. All tests that require 212 focus fail on WebKit2. See https://bugs.webkit.org/show_bug.cgi?id=56988. 213 https://bugs.webkit.org/show_bug.cgi?id=12345 214 215 * platform/mac-wk2/fast/css/pseudo-any-expected.txt: Added. 216 217 ''' 218 219 self.assertEquals(12345, parse_bug_id_from_changelog(commit_text)) 220 221 commit_text = ''' 2222011-03-31 Adam Roben <aroben@apple.com> 223 224 Quote the executable path we pass to ::CreateProcessW 225 226 This will ensure that spaces in the path will be interpreted correctly. 227 228 Fixes <http://webkit.org/b/57569> Web process sometimes fails to launch when there are 229 spaces in its path 230 231 Reviewed by Steve Falkenburg. 232 233 * UIProcess/Launcher/win/ProcessLauncherWin.cpp: 234 (WebKit::ProcessLauncher::launchProcess): Surround the executable path in quotes. 235 236 ''' 237 238 self.assertEquals(57569, parse_bug_id_from_changelog(commit_text)) 239 240 241 # FIXME: This should move to a central location and be shared by more unit tests. 242 def _assert_dictionaries_equal(self, actual, expected): 243 # Make sure we aren't parsing more or less than we expect 244 self.assertEquals(sorted(actual.keys()), sorted(expected.keys())) 245 246 for key, expected_value in expected.items(): 247 self.assertEquals(actual[key], expected_value, ("Failure for key: %s: Actual='%s' Expected='%s'" % (key, actual[key], expected_value))) 248 249 def test_parse_bug_dictionary_from_xml(self): 250 bug = Bugzilla()._parse_bug_dictionary_from_xml(self._single_bug_xml) 251 self._assert_dictionaries_equal(bug, self._expected_example_bug_parsing) 252 253 _sample_multi_bug_xml = """ 254<bugzilla version="3.2.3" urlbase="https://bugs.webkit.org/" maintainer="admin@webkit.org" exporter="eric@webkit.org"> 255 %s 256 %s 257</bugzilla> 258""" % (_bug_xml, _bug_xml) 259 260 def test_parse_bugs_from_xml(self): 261 bugzilla = Bugzilla() 262 bugs = bugzilla._parse_bugs_from_xml(self._sample_multi_bug_xml) 263 self.assertEquals(len(bugs), 2) 264 self.assertEquals(bugs[0].id(), self._expected_example_bug_parsing['id']) 265 bugs = bugzilla._parse_bugs_from_xml("") 266 self.assertEquals(len(bugs), 0) 267 268 # This could be combined into test_bug_parsing later if desired. 269 def test_attachment_parsing(self): 270 bugzilla = Bugzilla() 271 soup = BeautifulSoup(self._example_attachment) 272 attachment_element = soup.find("attachment") 273 attachment = bugzilla._parse_attachment_element(attachment_element, self._expected_example_attachment_parsing['bug_id']) 274 self.assertTrue(attachment) 275 self._assert_dictionaries_equal(attachment, self._expected_example_attachment_parsing) 276 277 _sample_attachment_detail_page = """ 278<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 279 "http://www.w3.org/TR/html4/loose.dtd"> 280<html> 281 <head> 282 <title> 283 Attachment 41073 Details for Bug 27314</title> 284<link rel="Top" href="https://bugs.webkit.org/"> 285 <link rel="Up" href="show_bug.cgi?id=27314"> 286""" 287 288 def test_attachment_detail_bug_parsing(self): 289 bugzilla = Bugzilla() 290 self.assertEquals(27314, bugzilla._parse_bug_id_from_attachment_page(self._sample_attachment_detail_page)) 291 292 def test_add_cc_to_bug(self): 293 bugzilla = Bugzilla() 294 bugzilla.browser = MockBrowser() 295 bugzilla.authenticate = lambda: None 296 expected_stderr = "Adding ['adam@example.com'] to the CC list for bug 42\n" 297 OutputCapture().assert_outputs(self, bugzilla.add_cc_to_bug, [42, ["adam@example.com"]], expected_stderr=expected_stderr) 298 299 def _mock_control_item(self, name): 300 mock_item = Mock() 301 mock_item.name = name 302 return mock_item 303 304 def _mock_find_control(self, item_names=[], selected_index=0): 305 mock_control = Mock() 306 mock_control.items = [self._mock_control_item(name) for name in item_names] 307 mock_control.value = [item_names[selected_index]] if item_names else None 308 return lambda name, type: mock_control 309 310 def _assert_reopen(self, item_names=None, selected_index=None, extra_stderr=None): 311 bugzilla = Bugzilla() 312 bugzilla.browser = MockBrowser() 313 bugzilla.authenticate = lambda: None 314 315 mock_find_control = self._mock_find_control(item_names, selected_index) 316 bugzilla.browser.find_control = mock_find_control 317 expected_stderr = "Re-opening bug 42\n['comment']\n" 318 if extra_stderr: 319 expected_stderr += extra_stderr 320 OutputCapture().assert_outputs(self, bugzilla.reopen_bug, [42, ["comment"]], expected_stderr=expected_stderr) 321 322 def test_reopen_bug(self): 323 self._assert_reopen(item_names=["REOPENED", "RESOLVED", "CLOSED"], selected_index=1) 324 self._assert_reopen(item_names=["UNCONFIRMED", "RESOLVED", "CLOSED"], selected_index=1) 325 extra_stderr = "Did not reopen bug 42, it appears to already be open with status ['NEW'].\n" 326 self._assert_reopen(item_names=["NEW", "RESOLVED"], selected_index=0, extra_stderr=extra_stderr) 327 328 def test_file_object_for_upload(self): 329 bugzilla = Bugzilla() 330 file_object = StringIO.StringIO() 331 unicode_tor = u"WebKit \u2661 Tor Arne Vestb\u00F8!" 332 utf8_tor = unicode_tor.encode("utf-8") 333 self.assertEqual(bugzilla._file_object_for_upload(file_object), file_object) 334 self.assertEqual(bugzilla._file_object_for_upload(utf8_tor).read(), utf8_tor) 335 self.assertEqual(bugzilla._file_object_for_upload(unicode_tor).read(), utf8_tor) 336 337 def test_filename_for_upload(self): 338 bugzilla = Bugzilla() 339 mock_file = Mock() 340 mock_file.name = "foo" 341 self.assertEqual(bugzilla._filename_for_upload(mock_file, 1234), 'foo') 342 mock_timestamp = lambda: "now" 343 filename = bugzilla._filename_for_upload(StringIO.StringIO(), 1234, extension="patch", timestamp=mock_timestamp) 344 self.assertEqual(filename, "bug-1234-now.patch") 345 346 347class BugzillaQueriesTest(unittest.TestCase): 348 _sample_request_page = """ 349<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 350 "http://www.w3.org/TR/html4/loose.dtd"> 351<html> 352 <head> 353 <title>Request Queue</title> 354 </head> 355<body> 356 357<h3>Flag: review</h3> 358 <table class="requests" cellspacing="0" cellpadding="4" border="1"> 359 <tr> 360 <th>Requester</th> 361 <th>Requestee</th> 362 <th>Bug</th> 363 <th>Attachment</th> 364 <th>Created</th> 365 </tr> 366 <tr> 367 <td>Shinichiro Hamaji <hamaji@chromium.org></td> 368 <td></td> 369 <td><a href="show_bug.cgi?id=30015">30015: text-transform:capitalize is failing in CSS2.1 test suite</a></td> 370 <td><a href="attachment.cgi?id=40511&action=review"> 37140511: Patch v0</a></td> 372 <td>2009-10-02 04:58 PST</td> 373 </tr> 374 <tr> 375 <td>Zan Dobersek <zandobersek@gmail.com></td> 376 <td></td> 377 <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td> 378 <td><a href="attachment.cgi?id=40722&action=review"> 37940722: Media controls, the simple approach</a></td> 380 <td>2009-10-06 09:13 PST</td> 381 </tr> 382 <tr> 383 <td>Zan Dobersek <zandobersek@gmail.com></td> 384 <td></td> 385 <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td> 386 <td><a href="attachment.cgi?id=40723&action=review"> 38740723: Adjust the media slider thumb size</a></td> 388 <td>2009-10-06 09:15 PST</td> 389 </tr> 390 </table> 391</body> 392</html> 393""" 394 _sample_quip_page = u""" 395<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 396 "http://www.w3.org/TR/html4/loose.dtd"> 397<html> 398 <head> 399 <title>Bugzilla Quip System</title> 400 </head> 401 <body> 402 <h2> 403 404 Existing quips: 405 </h2> 406 <ul> 407 <li>Everything should be made as simple as possible, but not simpler. - Albert Einstein</li> 408 <li>Good artists copy. Great artists steal. - Pablo Picasso</li> 409 <li>\u00e7gua mole em pedra dura, tanto bate at\u008e que fura.</li> 410 411 </ul> 412 </body> 413</html> 414""" 415 416 def _assert_result_count(self, queries, html, count): 417 self.assertEquals(queries._parse_result_count(html), count) 418 419 def test_parse_result_count(self): 420 queries = BugzillaQueries(None) 421 # Pages with results, always list the count at least twice. 422 self._assert_result_count(queries, '<span class="bz_result_count">314 bugs found.</span><span class="bz_result_count">314 bugs found.</span>', 314) 423 self._assert_result_count(queries, '<span class="bz_result_count">Zarro Boogs found.</span>', 0) 424 self._assert_result_count(queries, '<span class="bz_result_count">\n \nOne bug found.</span>', 1) 425 self.assertRaises(Exception, queries._parse_result_count, ['Invalid']) 426 427 def test_request_page_parsing(self): 428 queries = BugzillaQueries(None) 429 self.assertEquals([40511, 40722, 40723], queries._parse_attachment_ids_request_query(self._sample_request_page)) 430 431 def test_quip_page_parsing(self): 432 queries = BugzillaQueries(None) 433 expected_quips = ["Everything should be made as simple as possible, but not simpler. - Albert Einstein", "Good artists copy. Great artists steal. - Pablo Picasso", u"\u00e7gua mole em pedra dura, tanto bate at\u008e que fura."] 434 self.assertEquals(expected_quips, queries._parse_quips(self._sample_quip_page)) 435 436 def test_load_query(self): 437 queries = BugzillaQueries(Mock()) 438 queries._load_query("request.cgi?action=queue&type=review&group=type") 439