• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&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@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 &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">
37140511: 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">
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 &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">
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