OLD | NEW |
| (Empty) |
1 # Copyright (C) 2011 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 datetime | |
30 import logging | |
31 | |
32 from .bug import Bug | |
33 from .attachment import Attachment | |
34 from webkitpy.common.config.committers import CommitterList, Reviewer | |
35 | |
36 _log = logging.getLogger(__name__) | |
37 | |
38 | |
39 def _id_to_object_dictionary(*objects): | |
40 dictionary = {} | |
41 for thing in objects: | |
42 dictionary[thing["id"]] = thing | |
43 return dictionary | |
44 | |
45 # Testing | |
46 | |
47 | |
48 _patch1 = { | |
49 "id": 10000, | |
50 "bug_id": 50000, | |
51 "url": "http://example.com/10000", | |
52 "name": "Patch1", | |
53 "is_obsolete": False, | |
54 "is_patch": True, | |
55 "review": "+", | |
56 "reviewer_email": "foo@bar.com", | |
57 "commit-queue": "+", | |
58 "committer_email": "foo@bar.com", | |
59 "attacher_email": "Contributer1", | |
60 } | |
61 | |
62 | |
63 _patch2 = { | |
64 "id": 10001, | |
65 "bug_id": 50000, | |
66 "url": "http://example.com/10001", | |
67 "name": "Patch2", | |
68 "is_obsolete": False, | |
69 "is_patch": True, | |
70 "review": "+", | |
71 "reviewer_email": "reviewer2@webkit.org", | |
72 "commit-queue": "+", | |
73 "committer_email": "non-committer@example.com", | |
74 "attacher_email": "eric@webkit.org", | |
75 } | |
76 | |
77 | |
78 _patch3 = { | |
79 "id": 10002, | |
80 "bug_id": 50001, | |
81 "url": "http://example.com/10002", | |
82 "name": "Patch3", | |
83 "is_obsolete": False, | |
84 "is_patch": True, | |
85 "review": "?", | |
86 "commit-queue": "-", | |
87 "attacher_email": "eric@webkit.org", | |
88 "attach_date": datetime.datetime.today(), | |
89 } | |
90 | |
91 | |
92 _patch4 = { | |
93 "id": 10003, | |
94 "bug_id": 50003, | |
95 "url": "http://example.com/10002", | |
96 "name": "Patch3", | |
97 "is_obsolete": False, | |
98 "is_patch": True, | |
99 "review": "+", | |
100 "commit-queue": "?", | |
101 "reviewer_email": "foo@bar.com", | |
102 "attacher_email": "Contributer2", | |
103 } | |
104 | |
105 | |
106 _patch5 = { | |
107 "id": 10004, | |
108 "bug_id": 50003, | |
109 "url": "http://example.com/10002", | |
110 "name": "Patch5", | |
111 "is_obsolete": False, | |
112 "is_patch": True, | |
113 "review": "+", | |
114 "reviewer_email": "foo@bar.com", | |
115 "attacher_email": "eric@webkit.org", | |
116 } | |
117 | |
118 | |
119 _patch6 = { # Valid committer, but no reviewer. | |
120 "id": 10005, | |
121 "bug_id": 50003, | |
122 "url": "http://example.com/10002", | |
123 "name": "ROLLOUT of r3489", | |
124 "is_obsolete": False, | |
125 "is_patch": True, | |
126 "commit-queue": "+", | |
127 "committer_email": "foo@bar.com", | |
128 "attacher_email": "eric@webkit.org", | |
129 } | |
130 | |
131 | |
132 _patch7 = { # Valid review, patch is marked obsolete. | |
133 "id": 10006, | |
134 "bug_id": 50002, | |
135 "url": "http://example.com/10002", | |
136 "name": "Patch7", | |
137 "is_obsolete": True, | |
138 "is_patch": True, | |
139 "review": "+", | |
140 "reviewer_email": "foo@bar.com", | |
141 "attacher_email": "eric@webkit.org", | |
142 } | |
143 | |
144 | |
145 # This matches one of Bug.unassigned_emails | |
146 _unassigned_email = "webkit-unassigned@lists.webkit.org" | |
147 # This is needed for the FlakyTestReporter to believe the bug | |
148 # was filed by one of the webkitpy bots. | |
149 _commit_queue_email = "commit-queue@webkit.org" | |
150 | |
151 | |
152 _bug1 = { | |
153 "id": 50000, | |
154 "title": "Bug with two r+'d and cq+'d patches, one of which has an " | |
155 "invalid commit-queue setter.", | |
156 "reporter_email": "foo@foo.com", | |
157 "assigned_to_email": _unassigned_email, | |
158 "cc_emails": [], | |
159 "attachments": [_patch1, _patch2], | |
160 "bug_status": "UNCONFIRMED", | |
161 "comments": [], | |
162 } | |
163 | |
164 | |
165 _bug2 = { | |
166 "id": 50001, | |
167 "title": "Bug with a patch needing review.", | |
168 "reporter_email": "eric@webkit.org", | |
169 "assigned_to_email": "foo@foo.com", | |
170 "cc_emails": ["abarth@webkit.org", ], | |
171 "attachments": [_patch3], | |
172 "bug_status": "ASSIGNED", | |
173 "comments": [{"comment_date": datetime.datetime(2011, 6, 11, 9, 4, 3), | |
174 "comment_email": "bar@foo.com", | |
175 "text": "Message1.\nCommitted r35: <http://trac.webkit.org/cha
ngeset/35>", | |
176 }, | |
177 ], | |
178 } | |
179 | |
180 | |
181 _bug3 = { | |
182 "id": 50002, | |
183 "title": "The third bug", | |
184 "reporter_email": "foo@foo.com", | |
185 "assigned_to_email": _unassigned_email, | |
186 "cc_emails": [], | |
187 "attachments": [_patch7], | |
188 "bug_status": "NEW", | |
189 "comments": [{"comment_date": datetime.datetime(2011, 6, 11, 9, 4, 3), | |
190 "comment_email": "bar@foo.com", | |
191 "text": "Committed r30: <http://trac.webkit.org/changeset/30>
", | |
192 }, | |
193 {"comment_date": datetime.datetime(2011, 6, 11, 9, 4, 3), | |
194 "comment_email": "bar@foo.com", | |
195 "text": "Committed r31: <http://trac.webkit.org/changeset/31>
", | |
196 }, | |
197 ], | |
198 } | |
199 | |
200 | |
201 _bug4 = { | |
202 "id": 50003, | |
203 "title": "The fourth bug", | |
204 "reporter_email": "foo@foo.com", | |
205 "assigned_to_email": "foo@foo.com", | |
206 "cc_emails": [], | |
207 "attachments": [_patch4, _patch5, _patch6], | |
208 "bug_status": "REOPENED", | |
209 "comments": [{"comment_date": datetime.datetime(2011, 6, 11, 9, 4, 3), | |
210 "comment_email": "bar@foo.com", | |
211 "text": "Committed r25: <http://trac.webkit.org/changeset/30>"
, | |
212 }, | |
213 {"comment_date": datetime.datetime(2011, 6, 11, 9, 4, 3), | |
214 "comment_email": "bar@foo.com", | |
215 "text": "Rolled out in <http://trac.webkit.org/changeset/26", | |
216 }, | |
217 ], | |
218 } | |
219 | |
220 | |
221 _bug5 = { | |
222 "id": 50004, | |
223 "title": "The fifth bug", | |
224 "reporter_email": _commit_queue_email, | |
225 "assigned_to_email": "foo@foo.com", | |
226 "cc_emails": [], | |
227 "attachments": [], | |
228 "bug_status": "RESOLVED", | |
229 "dup_id": 50002, | |
230 "comments": [{"comment_date": datetime.datetime(2011, 6, 11, 9, 4, 3), | |
231 "comment_email": "bar@foo.com", | |
232 "text": "Committed r15: <http://trac.webkit.org/changeset/15>"
, | |
233 }, | |
234 ], | |
235 | |
236 } | |
237 | |
238 | |
239 class MockBugzillaQueries(object): | |
240 | |
241 def __init__(self, bugzilla): | |
242 self._bugzilla = bugzilla | |
243 | |
244 def _all_bugs(self): | |
245 return map(lambda bug_dictionary: Bug(bug_dictionary, self._bugzilla), | |
246 self._bugzilla.bug_cache.values()) | |
247 | |
248 def fetch_bug_ids_from_commit_queue(self): | |
249 bugs_with_commit_queued_patches = filter( | |
250 lambda bug: bug.commit_queued_patches(), | |
251 self._all_bugs()) | |
252 return map(lambda bug: bug.id(), bugs_with_commit_queued_patches) | |
253 | |
254 def fetch_attachment_ids_from_review_queue(self): | |
255 unreviewed_patches = sum([bug.unreviewed_patches() | |
256 for bug in self._all_bugs()], []) | |
257 return map(lambda patch: patch.id(), unreviewed_patches) | |
258 | |
259 def fetch_patches_from_commit_queue(self): | |
260 return sum([bug.commit_queued_patches() | |
261 for bug in self._all_bugs()], []) | |
262 | |
263 def fetch_bug_ids_from_pending_commit_list(self): | |
264 bugs_with_reviewed_patches = filter(lambda bug: bug.reviewed_patches(), | |
265 self._all_bugs()) | |
266 bug_ids = map(lambda bug: bug.id(), bugs_with_reviewed_patches) | |
267 # NOTE: This manual hack here is to allow testing logging in | |
268 # test_assign_to_committer the real pending-commit query on bugzilla | |
269 # will return bugs with patches which have r+, but are also obsolete. | |
270 return bug_ids + [50002] | |
271 | |
272 def fetch_bugs_from_review_queue(self, cc_email=None): | |
273 unreviewed_bugs = [bug for bug in self._all_bugs() if bug.unreviewed_pat
ches()] | |
274 | |
275 if cc_email: | |
276 return [bug for bug in unreviewed_bugs if cc_email in bug.cc_emails(
)] | |
277 | |
278 return unreviewed_bugs | |
279 | |
280 def fetch_patches_from_pending_commit_list(self): | |
281 return sum([bug.reviewed_patches() for bug in self._all_bugs()], []) | |
282 | |
283 def fetch_bugs_matching_search(self, search_string): | |
284 return [self._bugzilla.fetch_bug(50004), self._bugzilla.fetch_bug(50003)
] | |
285 | |
286 def fetch_bugs_matching_quicksearch(self, search_string): | |
287 return [self._bugzilla.fetch_bug(50001), self._bugzilla.fetch_bug(50002)
, | |
288 self._bugzilla.fetch_bug(50003), self._bugzilla.fetch_bug(50004)
] | |
289 | |
290 | |
291 _mock_reviewers = [Reviewer("Foo Bar", "foo@bar.com"), | |
292 Reviewer("Reviewer2", "reviewer2@webkit.org")] | |
293 | |
294 | |
295 # FIXME: Bugzilla is the wrong Mock-point. Once we have a BugzillaNetwork | |
296 # class we should mock that instead. | |
297 # Most of this class is just copy/paste from Bugzilla. | |
298 class MockBugzilla(object): | |
299 | |
300 bug_server_url = "http://example.com" | |
301 | |
302 bug_cache = _id_to_object_dictionary(_bug1, _bug2, _bug3, _bug4, _bug5) | |
303 | |
304 attachment_cache = _id_to_object_dictionary(_patch1, | |
305 _patch2, | |
306 _patch3, | |
307 _patch4, | |
308 _patch5, | |
309 _patch6, | |
310 _patch7) | |
311 | |
312 def __init__(self): | |
313 self.queries = MockBugzillaQueries(self) | |
314 # FIXME: This should move onto the Host object, and we should use a Mock
CommitterList | |
315 self.committers = CommitterList(reviewers=_mock_reviewers) | |
316 self.username = None | |
317 self._override_patch = None | |
318 | |
319 def authenticate(self): | |
320 self.username = "username@webkit.org" | |
321 | |
322 def create_bug(self, | |
323 bug_title, | |
324 bug_description, | |
325 component=None, | |
326 diff=None, | |
327 patch_description=None, | |
328 cc=None, | |
329 blocked=None, | |
330 mark_for_review=False, | |
331 mark_for_commit_queue=False): | |
332 _log.info("MOCK create_bug") | |
333 _log.info("bug_title: %s" % bug_title) | |
334 _log.info("bug_description: %s" % bug_description) | |
335 if component: | |
336 _log.info("component: %s" % component) | |
337 if cc: | |
338 _log.info("cc: %s" % cc) | |
339 if blocked: | |
340 _log.info("blocked: %s" % blocked) | |
341 return 60001 | |
342 | |
343 def quips(self): | |
344 return ["Good artists copy. Great artists steal. - Pablo Picasso"] | |
345 | |
346 def fetch_bug(self, bug_id): | |
347 return Bug(self.bug_cache.get(int(bug_id)), self) | |
348 | |
349 def set_override_patch(self, patch): | |
350 self._override_patch = patch | |
351 | |
352 def fetch_attachment(self, attachment_id): | |
353 if self._override_patch: | |
354 return self._override_patch | |
355 | |
356 attachment_dictionary = self.attachment_cache.get(attachment_id) | |
357 if not attachment_dictionary: | |
358 print "MOCK: fetch_attachment: %s is not a known attachment id" % at
tachment_id | |
359 return None | |
360 bug = self.fetch_bug(attachment_dictionary["bug_id"]) | |
361 for attachment in bug.attachments(include_obsolete=True): | |
362 if attachment.id() == int(attachment_id): | |
363 return attachment | |
364 | |
365 def bug_url_for_bug_id(self, bug_id): | |
366 return "%s/%s" % (self.bug_server_url, bug_id) | |
367 | |
368 def fetch_bug_dictionary(self, bug_id): | |
369 return self.bug_cache.get(bug_id) | |
370 | |
371 def attachment_url_for_id(self, attachment_id, action="view"): | |
372 action_param = "" | |
373 if action and action != "view": | |
374 action_param = "&action=%s" % action | |
375 return "%s/%s%s" % (self.bug_server_url, attachment_id, action_param) | |
376 | |
377 def reassign_bug(self, bug_id, assignee=None, comment_text=None): | |
378 _log.info("MOCK reassign_bug: bug_id=%s, assignee=%s" % (bug_id, assigne
e)) | |
379 if comment_text: | |
380 _log.info("-- Begin comment --") | |
381 _log.info(comment_text) | |
382 _log.info("-- End comment --") | |
383 | |
384 def set_flag_on_attachment(self, | |
385 attachment_id, | |
386 flag_name, | |
387 flag_value, | |
388 comment_text=None): | |
389 _log.info("MOCK setting flag '%s' to '%s' on attachment '%s' with commen
t '%s'" % ( | |
390 flag_name, flag_value, attachment_id, comment_text)) | |
391 | |
392 def post_comment_to_bug(self, bug_id, comment_text, cc=None): | |
393 _log.info("MOCK bug comment: bug_id=%s, cc=%s\n--- Begin comment ---\n%s
\n--- End comment ---\n" % ( | |
394 bug_id, cc, comment_text)) | |
395 | |
396 def add_attachment_to_bug(self, bug_id, file_or_string, description, filenam
e=None, comment_text=None, mimetype=None): | |
397 _log.info("MOCK add_attachment_to_bug: bug_id=%s, description=%s filenam
e=%s mimetype=%s" % | |
398 (bug_id, description, filename, mimetype)) | |
399 if comment_text: | |
400 _log.info("-- Begin comment --") | |
401 _log.info(comment_text) | |
402 _log.info("-- End comment --") | |
403 | |
404 def add_patch_to_bug(self, | |
405 bug_id, | |
406 diff, | |
407 description, | |
408 comment_text=None, | |
409 mark_for_review=False, | |
410 mark_for_commit_queue=False, | |
411 mark_for_landing=False): | |
412 _log.info("MOCK add_patch_to_bug: bug_id=%s, description=%s, mark_for_re
view=%s, mark_for_commit_queue=%s, mark_for_landing=%s" % | |
413 (bug_id, description, mark_for_review, mark_for_commit_queue,
mark_for_landing)) | |
414 if comment_text: | |
415 _log.info("-- Begin comment --") | |
416 _log.info(comment_text) | |
417 _log.info("-- End comment --") | |
418 | |
419 def add_cc_to_bug(self, bug_id, ccs): | |
420 pass | |
421 | |
422 def obsolete_attachment(self, attachment_id, message=None): | |
423 pass | |
424 | |
425 def reopen_bug(self, bug_id, message): | |
426 _log.info("MOCK reopen_bug %s with comment '%s'" % (bug_id, message)) | |
427 | |
428 def close_bug_as_fixed(self, bug_id, message): | |
429 pass | |
430 | |
431 def clear_attachment_flags(self, attachment_id, message): | |
432 pass | |
OLD | NEW |