|
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 |
|
31 import datetime |
|
32 |
|
33 from webkitpy.common.config.committers import CommitterList, Reviewer, Committer |
|
34 from webkitpy.common.net.bugzilla import Bugzilla, BugzillaQueries, parse_bug_id, CommitterValidator, Bug |
|
35 from webkitpy.common.system.outputcapture import OutputCapture |
|
36 from webkitpy.thirdparty.mock import Mock |
|
37 from webkitpy.thirdparty.BeautifulSoup import BeautifulSoup |
|
38 |
|
39 |
|
40 class MockBrowser(object): |
|
41 def open(self, url): |
|
42 pass |
|
43 |
|
44 def select_form(self, name): |
|
45 pass |
|
46 |
|
47 def __setitem__(self, key, value): |
|
48 pass |
|
49 |
|
50 def submit(self): |
|
51 pass |
|
52 |
|
53 |
|
54 class BugTest(unittest.TestCase): |
|
55 def test_is_unassigned(self): |
|
56 for email in Bug.unassigned_emails: |
|
57 bug = Bug({"assigned_to_email" : email}, bugzilla=None) |
|
58 self.assertTrue(bug.is_unassigned()) |
|
59 bug = Bug({"assigned_to_email" : "test@test.com"}, bugzilla=None) |
|
60 self.assertFalse(bug.is_unassigned()) |
|
61 |
|
62 |
|
63 class CommitterValidatorTest(unittest.TestCase): |
|
64 def test_flag_permission_rejection_message(self): |
|
65 validator = CommitterValidator(bugzilla=None) |
|
66 self.assertEqual(validator._committers_py_path(), "WebKitTools/Scripts/webkitpy/common/config/committers.py") |
|
67 expected_messsage="""foo@foo.com does not have review permissions according to http://trac.webkit.org/browser/trunk/WebKitTools/Scripts/webkitpy/common/config/committers.py. |
|
68 |
|
69 - If you do not have review rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags. |
|
70 |
|
71 - If you have review rights please correct the error in WebKitTools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed). Due to bug 30084 the commit-queue will require a restart after your change. Please contact eseidel@chromium.org to request a commit-queue restart. After restart the commit-queue will correctly respect your review rights.""" |
|
72 self.assertEqual(validator._flag_permission_rejection_message("foo@foo.com", "review"), expected_messsage) |
|
73 |
|
74 |
|
75 class BugzillaTest(unittest.TestCase): |
|
76 _example_attachment = ''' |
|
77 <attachment |
|
78 isobsolete="1" |
|
79 ispatch="1" |
|
80 isprivate="0" |
|
81 > |
|
82 <attachid>33721</attachid> |
|
83 <date>2009-07-29 10:23 PDT</date> |
|
84 <desc>Fixed whitespace issue</desc> |
|
85 <filename>patch</filename> |
|
86 <type>text/plain</type> |
|
87 <size>9719</size> |
|
88 <attacher>christian.plesner.hansen@gmail.com</attacher> |
|
89 <flag name="review" |
|
90 id="17931" |
|
91 status="+" |
|
92 setter="one@test.com" |
|
93 /> |
|
94 <flag name="commit-queue" |
|
95 id="17932" |
|
96 status="+" |
|
97 setter="two@test.com" |
|
98 /> |
|
99 <flag name="in-rietveld" |
|
100 id="17933" |
|
101 status="+" |
|
102 setter="three@test.com" |
|
103 /> |
|
104 </attachment> |
|
105 ''' |
|
106 _expected_example_attachment_parsing = { |
|
107 'attach_date': datetime.datetime(2009, 07, 29, 10, 23), |
|
108 'bug_id' : 100, |
|
109 'is_obsolete' : True, |
|
110 'is_patch' : True, |
|
111 'id' : 33721, |
|
112 'url' : "https://bugs.webkit.org/attachment.cgi?id=33721", |
|
113 'name' : "Fixed whitespace issue", |
|
114 'type' : "text/plain", |
|
115 'review' : '+', |
|
116 'reviewer_email' : 'one@test.com', |
|
117 'commit-queue' : '+', |
|
118 'committer_email' : 'two@test.com', |
|
119 'in-rietveld': '+', |
|
120 'rietveld_uploader_email': 'three@test.com', |
|
121 'attacher_email' : 'christian.plesner.hansen@gmail.com', |
|
122 } |
|
123 |
|
124 def test_url_creation(self): |
|
125 # FIXME: These would be all better as doctests |
|
126 bugs = Bugzilla() |
|
127 self.assertEquals(None, bugs.bug_url_for_bug_id(None)) |
|
128 self.assertEquals(None, bugs.short_bug_url_for_bug_id(None)) |
|
129 self.assertEquals(None, bugs.attachment_url_for_id(None)) |
|
130 |
|
131 def test_parse_bug_id(self): |
|
132 # FIXME: These would be all better as doctests |
|
133 bugs = Bugzilla() |
|
134 self.assertEquals(12345, parse_bug_id("http://webkit.org/b/12345")) |
|
135 self.assertEquals(12345, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?id=12345")) |
|
136 self.assertEquals(12345, parse_bug_id(bugs.short_bug_url_for_bug_id(12345))) |
|
137 self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345))) |
|
138 self.assertEquals(12345, parse_bug_id(bugs.bug_url_for_bug_id(12345, xml=True))) |
|
139 |
|
140 # Our bug parser is super-fragile, but at least we're testing it. |
|
141 self.assertEquals(None, parse_bug_id("http://www.webkit.org/b/12345")) |
|
142 self.assertEquals(None, parse_bug_id("http://bugs.webkit.org/show_bug.cgi?ctype=xml&id=12345")) |
|
143 |
|
144 _example_bug = """ |
|
145 <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> |
|
146 <!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/bugzilla.dtd"> |
|
147 <bugzilla version="3.2.3" |
|
148 urlbase="https://bugs.webkit.org/" |
|
149 maintainer="admin@webkit.org" |
|
150 exporter="eric@webkit.org" |
|
151 > |
|
152 <bug> |
|
153 <bug_id>32585</bug_id> |
|
154 <creation_ts>2009-12-15 15:17 PST</creation_ts> |
|
155 <short_desc>bug to test webkit-patch and commit-queue failures</short_desc> |
|
156 <delta_ts>2009-12-27 21:04:50 PST</delta_ts> |
|
157 <reporter_accessible>1</reporter_accessible> |
|
158 <cclist_accessible>1</cclist_accessible> |
|
159 <classification_id>1</classification_id> |
|
160 <classification>Unclassified</classification> |
|
161 <product>WebKit</product> |
|
162 <component>Tools / Tests</component> |
|
163 <version>528+ (Nightly build)</version> |
|
164 <rep_platform>PC</rep_platform> |
|
165 <op_sys>Mac OS X 10.5</op_sys> |
|
166 <bug_status>NEW</bug_status> |
|
167 <priority>P2</priority> |
|
168 <bug_severity>Normal</bug_severity> |
|
169 <target_milestone>---</target_milestone> |
|
170 <everconfirmed>1</everconfirmed> |
|
171 <reporter name="Eric Seidel">eric@webkit.org</reporter> |
|
172 <assigned_to name="Nobody">webkit-unassigned@lists.webkit.org</assigned_to> |
|
173 <cc>foo@bar.com</cc> |
|
174 <cc>example@example.com</cc> |
|
175 <long_desc isprivate="0"> |
|
176 <who name="Eric Seidel">eric@webkit.org</who> |
|
177 <bug_when>2009-12-15 15:17:28 PST</bug_when> |
|
178 <thetext>bug to test webkit-patch and commit-queue failures |
|
179 |
|
180 Ignore this bug. Just for testing failure modes of webkit-patch and the commit-queue.</thetext> |
|
181 </long_desc> |
|
182 <attachment |
|
183 isobsolete="0" |
|
184 ispatch="1" |
|
185 isprivate="0" |
|
186 > |
|
187 <attachid>45548</attachid> |
|
188 <date>2009-12-27 23:51 PST</date> |
|
189 <desc>Patch</desc> |
|
190 <filename>bug-32585-20091228005112.patch</filename> |
|
191 <type>text/plain</type> |
|
192 <size>10882</size> |
|
193 <attacher>mjs@apple.com</attacher> |
|
194 |
|
195 <token>1261988248-dc51409e9c421a4358f365fa8bec8357</token> |
|
196 <data encoding="base64">SW5kZXg6IFdlYktpdC9tYWMvQ2hhbmdlTG9nCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09 |
|
197 removed-because-it-was-really-long |
|
198 ZEZpbmlzaExvYWRXaXRoUmVhc29uOnJlYXNvbl07Cit9CisKIEBlbmQKIAogI2VuZGlmCg== |
|
199 </data> |
|
200 |
|
201 <flag name="review" |
|
202 id="27602" |
|
203 status="?" |
|
204 setter="mjs@apple.com" |
|
205 /> |
|
206 </attachment> |
|
207 </bug> |
|
208 </bugzilla> |
|
209 """ |
|
210 _expected_example_bug_parsing = { |
|
211 "id" : 32585, |
|
212 "title" : u"bug to test webkit-patch and commit-queue failures", |
|
213 "cc_emails" : ["foo@bar.com", "example@example.com"], |
|
214 "reporter_email" : "eric@webkit.org", |
|
215 "assigned_to_email" : "webkit-unassigned@lists.webkit.org", |
|
216 "attachments" : [{ |
|
217 "attach_date": datetime.datetime(2009, 12, 27, 23, 51), |
|
218 'name': u'Patch', |
|
219 'url' : "https://bugs.webkit.org/attachment.cgi?id=45548", |
|
220 'is_obsolete': False, |
|
221 'review': '?', |
|
222 'is_patch': True, |
|
223 'attacher_email': 'mjs@apple.com', |
|
224 'bug_id': 32585, |
|
225 'type': 'text/plain', |
|
226 'id': 45548 |
|
227 }], |
|
228 } |
|
229 |
|
230 # FIXME: This should move to a central location and be shared by more unit tests. |
|
231 def _assert_dictionaries_equal(self, actual, expected): |
|
232 # Make sure we aren't parsing more or less than we expect |
|
233 self.assertEquals(sorted(actual.keys()), sorted(expected.keys())) |
|
234 |
|
235 for key, expected_value in expected.items(): |
|
236 self.assertEquals(actual[key], expected_value, ("Failure for key: %s: Actual='%s' Expected='%s'" % (key, actual[key], expected_value))) |
|
237 |
|
238 def test_bug_parsing(self): |
|
239 bug = Bugzilla()._parse_bug_page(self._example_bug) |
|
240 self._assert_dictionaries_equal(bug, self._expected_example_bug_parsing) |
|
241 |
|
242 # This could be combined into test_bug_parsing later if desired. |
|
243 def test_attachment_parsing(self): |
|
244 bugzilla = Bugzilla() |
|
245 soup = BeautifulSoup(self._example_attachment) |
|
246 attachment_element = soup.find("attachment") |
|
247 attachment = bugzilla._parse_attachment_element(attachment_element, self._expected_example_attachment_parsing['bug_id']) |
|
248 self.assertTrue(attachment) |
|
249 self._assert_dictionaries_equal(attachment, self._expected_example_attachment_parsing) |
|
250 |
|
251 _sample_attachment_detail_page = """ |
|
252 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
|
253 "http://www.w3.org/TR/html4/loose.dtd"> |
|
254 <html> |
|
255 <head> |
|
256 <title> |
|
257 Attachment 41073 Details for Bug 27314</title> |
|
258 <link rel="Top" href="https://bugs.webkit.org/"> |
|
259 <link rel="Up" href="show_bug.cgi?id=27314"> |
|
260 """ |
|
261 |
|
262 def test_attachment_detail_bug_parsing(self): |
|
263 bugzilla = Bugzilla() |
|
264 self.assertEquals(27314, bugzilla._parse_bug_id_from_attachment_page(self._sample_attachment_detail_page)) |
|
265 |
|
266 def test_add_cc_to_bug(self): |
|
267 bugzilla = Bugzilla() |
|
268 bugzilla.browser = MockBrowser() |
|
269 bugzilla.authenticate = lambda: None |
|
270 expected_stderr = "Adding ['adam@example.com'] to the CC list for bug 42\n" |
|
271 OutputCapture().assert_outputs(self, bugzilla.add_cc_to_bug, [42, ["adam@example.com"]], expected_stderr=expected_stderr) |
|
272 |
|
273 def _mock_control_item(self, name): |
|
274 mock_item = Mock() |
|
275 mock_item.name = name |
|
276 return mock_item |
|
277 |
|
278 def _mock_find_control(self, item_names=[], selected_index=0): |
|
279 mock_control = Mock() |
|
280 mock_control.items = [self._mock_control_item(name) for name in item_names] |
|
281 mock_control.value = [item_names[selected_index]] if item_names else None |
|
282 return lambda name, type: mock_control |
|
283 |
|
284 def _assert_reopen(self, item_names=None, selected_index=None, extra_stderr=None): |
|
285 bugzilla = Bugzilla() |
|
286 bugzilla.browser = MockBrowser() |
|
287 bugzilla.authenticate = lambda: None |
|
288 |
|
289 mock_find_control = self._mock_find_control(item_names, selected_index) |
|
290 bugzilla.browser.find_control = mock_find_control |
|
291 expected_stderr = "Re-opening bug 42\n['comment']\n" |
|
292 if extra_stderr: |
|
293 expected_stderr += extra_stderr |
|
294 OutputCapture().assert_outputs(self, bugzilla.reopen_bug, [42, ["comment"]], expected_stderr=expected_stderr) |
|
295 |
|
296 def test_reopen_bug(self): |
|
297 self._assert_reopen(item_names=["REOPENED", "RESOLVED", "CLOSED"], selected_index=1) |
|
298 self._assert_reopen(item_names=["UNCONFIRMED", "RESOLVED", "CLOSED"], selected_index=1) |
|
299 extra_stderr = "Did not reopen bug 42, it appears to already be open with status ['NEW'].\n" |
|
300 self._assert_reopen(item_names=["NEW", "RESOLVED"], selected_index=0, extra_stderr=extra_stderr) |
|
301 |
|
302 |
|
303 class BugzillaQueriesTest(unittest.TestCase): |
|
304 _sample_request_page = """ |
|
305 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
|
306 "http://www.w3.org/TR/html4/loose.dtd"> |
|
307 <html> |
|
308 <head> |
|
309 <title>Request Queue</title> |
|
310 </head> |
|
311 <body> |
|
312 |
|
313 <h3>Flag: review</h3> |
|
314 <table class="requests" cellspacing="0" cellpadding="4" border="1"> |
|
315 <tr> |
|
316 <th>Requester</th> |
|
317 <th>Requestee</th> |
|
318 <th>Bug</th> |
|
319 <th>Attachment</th> |
|
320 <th>Created</th> |
|
321 </tr> |
|
322 <tr> |
|
323 <td>Shinichiro Hamaji <hamaji@chromium.org></td> |
|
324 <td></td> |
|
325 <td><a href="show_bug.cgi?id=30015">30015: text-transform:capitalize is failing in CSS2.1 test suite</a></td> |
|
326 <td><a href="attachment.cgi?id=40511&action=review"> |
|
327 40511: Patch v0</a></td> |
|
328 <td>2009-10-02 04:58 PST</td> |
|
329 </tr> |
|
330 <tr> |
|
331 <td>Zan Dobersek <zandobersek@gmail.com></td> |
|
332 <td></td> |
|
333 <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td> |
|
334 <td><a href="attachment.cgi?id=40722&action=review"> |
|
335 40722: Media controls, the simple approach</a></td> |
|
336 <td>2009-10-06 09:13 PST</td> |
|
337 </tr> |
|
338 <tr> |
|
339 <td>Zan Dobersek <zandobersek@gmail.com></td> |
|
340 <td></td> |
|
341 <td><a href="show_bug.cgi?id=26304">26304: [GTK] Add controls for playing html5 video.</a></td> |
|
342 <td><a href="attachment.cgi?id=40723&action=review"> |
|
343 40723: Adjust the media slider thumb size</a></td> |
|
344 <td>2009-10-06 09:15 PST</td> |
|
345 </tr> |
|
346 </table> |
|
347 </body> |
|
348 </html> |
|
349 """ |
|
350 _sample_quip_page = u""" |
|
351 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
|
352 "http://www.w3.org/TR/html4/loose.dtd"> |
|
353 <html> |
|
354 <head> |
|
355 <title>Bugzilla Quip System</title> |
|
356 </head> |
|
357 <body> |
|
358 <h2> |
|
359 |
|
360 Existing quips: |
|
361 </h2> |
|
362 <ul> |
|
363 <li>Everything should be made as simple as possible, but not simpler. - Albert Einstein</li> |
|
364 <li>Good artists copy. Great artists steal. - Pablo Picasso</li> |
|
365 <li>\u00e7gua mole em pedra dura, tanto bate at\u008e que fura.</li> |
|
366 |
|
367 </ul> |
|
368 </body> |
|
369 </html> |
|
370 """ |
|
371 |
|
372 def test_request_page_parsing(self): |
|
373 queries = BugzillaQueries(None) |
|
374 self.assertEquals([40511, 40722, 40723], queries._parse_attachment_ids_request_query(self._sample_request_page)) |
|
375 |
|
376 def test_quip_page_parsing(self): |
|
377 queries = BugzillaQueries(None) |
|
378 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."] |
|
379 self.assertEquals(expected_quips, queries._parse_quips(self._sample_quip_page)) |
|
380 |
|
381 def test_load_query(self): |
|
382 queries = BugzillaQueries(Mock()) |
|
383 queries._load_query("request.cgi?action=queue&type=review&group=type") |