Home | History | Annotate | Download | only in bugzilla
      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 unittest
     30 import datetime
     31 import StringIO
     32 
     33 from .bugzilla import Bugzilla, BugzillaQueries, parse_bug_id, parse_bug_id_from_changelog
     34 
     35 from webkitpy.common.system.outputcapture import OutputCapture
     36 from webkitpy.tool.mocktool import MockBrowser
     37 from webkitpy.thirdparty.mock import Mock
     38 from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup
     39 
     40 
     41 class 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 (at] gmail.com</attacher>
     55           <flag name="review"
     56                 id="17931"
     57                 status="+"
     58                 setter="one (at] test.com"
     59            />
     60           <flag name="commit-queue"
     61                 id="17932"
     62                 status="+"
     63                 setter="two (at] 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 (at] test.com',
     78         'commit-queue' : '+',
     79         'committer_email' : 'two (at] test.com',
     80         'attacher_email' : 'christian.plesner.hansen (at] 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&apos;s and commit-queue&apos;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 (at] webkit.org</reporter>
    124           <assigned_to name="Nobody">webkit-unassigned (at] lists.webkit.org</assigned_to>
    125           <cc>foo (at] bar.com</cc>
    126     <cc>example (at] example.com</cc>
    127           <long_desc isprivate="0">
    128             <who name="Eric Seidel">eric (at] 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 
    132 Ignore 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 (at] apple.com</attacher> 
    146 
    147               <token>1261988248-dc51409e9c421a4358f365fa8bec8357</token> 
    148               <data encoding="base64">SW5kZXg6IFdlYktpdC9tYWMvQ2hhbmdlTG9nCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09
    149 removed-because-it-was-really-long
    150 ZEZpbmlzaExvYWRXaXRoUmVhc29uOnJlYXNvbl07Cit9CisKIEBlbmQKIAogI2VuZGlmCg==
    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 (at] bar.com", "example (at] example.com"],
    178         "reporter_email" : "eric (at] webkit.org",
    179         "assigned_to_email" : "webkit-unassigned (at] 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 (at] 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 = '''
    197 2011-03-23  Ojan Vafai  <ojan (at] 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 = '''
    209 2011-03-23  Ojan Vafai  <ojan (at] 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 = '''
    222 2011-03-31  Adam Roben  <aroben (at] 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 (at] example.com'] to the CC list for bug 42\n"
    297         OutputCapture().assert_outputs(self, bugzilla.add_cc_to_bug, [42, ["adam (at] 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 
    347 class 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 &lt;hamaji&#64;chromium.org&gt;</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&amp;action=review">
    371 40511: Patch v0</a></td>
    372         <td>2009-10-02 04:58 PST</td>
    373     </tr>
    374     <tr>
    375         <td>Zan Dobersek &lt;zandobersek&#64;gmail.com&gt;</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&amp;action=review">
    379 40722: Media controls, the simple approach</a></td>
    380         <td>2009-10-06 09:13 PST</td>
    381     </tr>
    382     <tr>
    383         <td>Zan Dobersek &lt;zandobersek&#64;gmail.com&gt;</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&amp;action=review">
    387 40723: 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