OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """Unit tests for pending_manager.py.""" | |
7 | |
8 import logging | |
9 import os | |
10 import re | |
11 import sys | |
12 import time | |
13 import traceback | |
14 import unittest | |
15 import urllib2 | |
16 | |
17 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
18 sys.path.insert(0, os.path.join(ROOT_DIR, '..')) | |
19 | |
20 import find_depot_tools # pylint: disable=W0611 | |
21 import breakpad | |
22 | |
23 import context | |
24 import pending_manager | |
25 from verification import base | |
26 from verification import fake | |
27 from verification import project_base | |
28 from verification import reviewer_lgtm | |
29 | |
30 # In tests/ | |
31 import mocks | |
32 | |
33 | |
34 def read(filename): | |
35 f = open(filename, 'rb') | |
36 content = f.read() | |
37 f.close() | |
38 return content | |
39 | |
40 | |
41 def write(filename, content): | |
42 f = open(filename, 'wb') | |
43 f.write(content) | |
44 f.close() | |
45 | |
46 | |
47 def trim(x): | |
48 return x.replace(' ', '').replace('\n', '') | |
49 | |
50 | |
51 def _try_comment(issue=31337): | |
52 return ( | |
53 "add_comment(%d, u'%shttp://localhost/author@example.com/%d/1\\n')" % | |
54 (issue, pending_manager.PendingManager.TRYING_PATCH.replace('\n', '\\n'), | |
55 issue)) | |
56 | |
57 | |
58 class TestPendingManager(mocks.TestCase): | |
59 def setUp(self): | |
60 super(TestPendingManager, self).setUp() | |
61 self.root_dir = ROOT_DIR | |
62 | |
63 def testLoadSave(self): | |
64 pc = pending_manager.PendingManager( | |
65 context.Context(None, None, mocks.AsyncPushMock(self)), | |
66 [fake.FakeVerifier(base.SUCCEEDED)], | |
67 []) | |
68 filename = os.path.join(self.root_dir, 'foo.json') | |
69 empty = """{ | |
70 "__persistent_type__": "PendingQueue", | |
71 "pending_commits": {} | |
72 } | |
73 """ | |
74 write(filename, empty) | |
75 try: | |
76 pc.load(filename) | |
77 self.assertEqual(pc.queue.pending_commits, {}) | |
78 pc.save(filename) | |
79 self.assertEqual(trim(empty), trim(read(filename))) | |
80 finally: | |
81 os.remove(filename) | |
82 if os.path.exists(filename + '.old'): | |
83 os.remove(filename + '.old') | |
84 | |
85 def _get_pc(self, verifiers_no_patch, verifiers): | |
86 return pending_manager.PendingManager( | |
87 self.context, verifiers_no_patch, verifiers) | |
88 | |
89 def _check_standard_verification(self, pc, success, defered): | |
90 """Verifies the checkout and rietveld calls.""" | |
91 pc.scan_results() | |
92 self.assertEqual(len(pc.queue.iterate()), 0) | |
93 issue = 31337 | |
94 if pc.verifiers: | |
95 if success: | |
96 self.context.checkout.check_calls( | |
97 [ 'prepare(None)', | |
98 'apply_patch(%r)' % (self.context.rietveld.patchsets[0],), | |
99 'prepare(None)', # Will sync to HEAD/124. | |
100 'apply_patch(%r)' % (self.context.rietveld.patchsets[1],), | |
101 ( | |
102 "commit(u'foo\\n\\n" | |
103 "Review URL: http://nowhere/%d', " | |
104 "u'author@example.com')") % issue]) | |
105 self.context.rietveld.check_calls( | |
106 [ _try_comment(), | |
107 'close_issue(%d)' % issue, | |
108 "update_description(%d, u'foo')" % issue, | |
109 "add_comment(%d, 'Change committed as 125')" % issue]) | |
110 else: | |
111 self.context.checkout.check_calls( | |
112 [ 'prepare(None)', | |
113 'apply_patch(%r)' % (self.context.rietveld.patchsets[0],)]) | |
114 self.context.rietveld.check_calls( | |
115 [ _try_comment(), | |
116 "set_flag(%d, 1, 'commit', False)" % issue, | |
117 "add_comment(%d, %r)" % (issue, pc.FAILED_NO_MESSAGE)]) | |
118 else: | |
119 if success: | |
120 self.context.checkout.check_calls( | |
121 self._prepare_apply_commit(0, issue)) | |
122 self.context.rietveld.check_calls( | |
123 [ _try_comment(), | |
124 'close_issue(%d)' % issue, | |
125 "update_description(%d, u'foo')" % issue, | |
126 "add_comment(%d, 'Change committed as 125')" % issue]) | |
127 else: | |
128 # checkout is never touched in that case. | |
129 self.context.checkout.check_calls([]) | |
130 if defered: | |
131 self.context.rietveld.check_calls( | |
132 [ _try_comment(), | |
133 "set_flag(%d, 1, 'commit', False)" % issue, | |
134 "add_comment(%d, %r)" % (issue, pc.FAILED_NO_MESSAGE)]) | |
135 else: | |
136 self.context.rietveld.check_calls( | |
137 [ "set_flag(%d, 1, 'commit', False)" % issue, | |
138 "add_comment(%d, %r)" % (issue, pc.FAILED_NO_MESSAGE)]) | |
139 | |
140 def _prepare_apply_commit(self, index, issue, server_hooks_missing=False): | |
141 """Returns a frequent sequence of action happening on the Checkout object. | |
142 | |
143 The list returned by this function should be used as an argument to | |
144 self.context.checkout.check_calls(). | |
145 """ | |
146 seq = [ | |
147 # Reverts any previous modification or checkout the tree if it was not | |
148 # present. | |
149 'prepare(None)', | |
150 # Applies the requested PatchSet. | |
151 'apply_patch(%r)' % self.context.rietveld.patchsets[index], | |
152 ] | |
153 # Commits the patch. | |
154 author_and_reviewer = '' | |
155 if server_hooks_missing: | |
156 author_and_reviewer = ( | |
157 '\\n\\nR=rev@example.com\\n\\nAuthor: author@example.com') | |
158 commit_message = ( | |
159 "commit(u'foo%s\\n\\n" | |
160 "Review URL: http://nowhere/%d', " | |
161 "u'author@example.com')") % (author_and_reviewer, issue) | |
162 seq.append(commit_message) | |
163 | |
164 return seq | |
165 | |
166 def testNoVerification(self): | |
167 try: | |
168 self._get_pc([], []) | |
169 except ValueError: | |
170 pass | |
171 else: | |
172 self.fail(msg='A PendingManager must require at least one verifier.') | |
173 | |
174 try: | |
175 # Cannot have the same verifier two times. | |
176 self._get_pc( | |
177 [fake.FakeVerifier(base.SUCCEEDED)], | |
178 [fake.FakeVerifier(base.SUCCEEDED)]) | |
179 except AssertionError: | |
180 pass | |
181 else: | |
182 self.fail(msg='A PendingManager should not accept the same verifier' | |
183 ' two times.') | |
184 | |
185 def _check_1(self, pc, result): | |
186 issue = 31337 | |
187 # 'initial' won't be sent if the pre-patch verification fails, this is to | |
188 # not add noise for ignored CLs. | |
189 send_initial_packet = (result == base.SUCCEEDED or pc.verifiers) | |
190 self.assertEqual(len(pc.queue.iterate()), 0) | |
191 pc.look_for_new_pending_commit() | |
192 self.assertEqual(len(pc.queue.iterate()), 1) | |
193 commit = pc.queue.get(issue) | |
194 self.assertEqual(len(commit.verifications), 0) | |
195 pc.process_new_pending_commit() | |
196 if result == base.FAILED: | |
197 self.assertEqual([], pc.queue.iterate()) | |
198 else: | |
199 commit = pc.queue.get(issue) | |
200 self.assertEqual(commit.verifications['fake'].get_state(), result) | |
201 self.assertEqual(len(commit.verifications), 1) | |
202 pc.update_status() | |
203 if result == base.FAILED: | |
204 self.assertEqual([], pc.queue.iterate()) | |
205 else: | |
206 commit = pc.queue.get(issue) | |
207 self.assertEqual(commit.verifications['fake'].get_state(), result) | |
208 self.assertEqual('', commit.relpath) | |
209 self.assertEqual(len(commit.verifications), 1) | |
210 self._check_standard_verification(pc, result == base.SUCCEEDED, False) | |
211 | |
212 if result == base.SUCCEEDED: | |
213 self.context.status.check_names(['initial', 'why not', 'commit']) | |
214 elif send_initial_packet: | |
215 self.context.status.check_names(['initial', 'abort']) | |
216 else: | |
217 # Only happens when there is no verifier that requires a patch. | |
218 self.context.status.check_names(['abort']) | |
219 | |
220 def testNoPatchVerification(self): | |
221 pc = self._get_pc([fake.FakeVerifier(base.SUCCEEDED)], []) | |
222 self._check_1(pc, base.SUCCEEDED) | |
223 | |
224 def testPatchVerification(self): | |
225 pc = self._get_pc([], [fake.FakeVerifier(base.SUCCEEDED)]) | |
226 self._check_1(pc, base.SUCCEEDED) | |
227 | |
228 def testNoPatchVerificationFail(self): | |
229 pc = self._get_pc([fake.FakeVerifier(base.FAILED)], []) | |
230 self._check_1(pc, base.FAILED) | |
231 | |
232 def testPatchVerificationFail(self): | |
233 pc = self._get_pc([], [fake.FakeVerifier(base.FAILED)]) | |
234 self._check_1(pc, base.FAILED) | |
235 | |
236 def testPatchDiscardThrows(self): | |
237 # Handle HTTPError correctly. | |
238 result = [] | |
239 issue = 31337 | |
240 pc = self._get_pc([], [fake.FakeVerifier(base.FAILED)]) | |
241 | |
242 def set_flag_throw(_issue, _patchset, _flag, _value): | |
243 raise urllib2.HTTPError(None, None, None, None, None) | |
244 | |
245 def send_stack(*_args, **_kwargs): | |
246 result.append(True) | |
247 | |
248 self.mock(breakpad, 'SendStack', send_stack) | |
249 self.mock(traceback, 'print_stack', lambda: None) | |
250 self.mock(logging, 'error', lambda _: None) | |
251 pc.context.rietveld.set_flag = set_flag_throw | |
252 | |
253 self.assertEqual(len(pc.queue.iterate()), 0) | |
254 pc.look_for_new_pending_commit() | |
255 self.assertEqual(len(pc.queue.iterate()), 1) | |
256 commit = pc.queue.get(issue) | |
257 self.assertEqual(len(commit.verifications), 0) | |
258 pc.process_new_pending_commit() | |
259 self.assertEqual([], pc.queue.iterate()) | |
260 pc.update_status() | |
261 self.assertEqual([], pc.queue.iterate()) | |
262 self.context.checkout.check_calls( | |
263 [ 'prepare(None)', | |
264 'apply_patch(%r)' % (self.context.rietveld.patchsets[0],), | |
265 ]) | |
266 self.context.rietveld.check_calls( | |
267 [ _try_comment(), | |
268 "add_comment(%d, %r)" % (issue, pc.FAILED_NO_MESSAGE), | |
269 ]) | |
270 self.context.status.check_names(['initial', 'abort']) | |
271 | |
272 def _check_defer_1(self, pc, result): | |
273 issue = 31337 | |
274 self.assertEqual(len(pc.queue.iterate()), 0) | |
275 pc.look_for_new_pending_commit() | |
276 self.assertEqual(len(pc.queue.iterate()), 1) | |
277 commit = pc.queue.get(issue) | |
278 self.assertEqual(len(commit.verifications), 0) | |
279 pc.process_new_pending_commit() | |
280 commit = pc.queue.get(issue) | |
281 self.assertEqual('', commit.relpath) | |
282 self.assertEqual(commit.verifications['fake'].get_state(), base.PROCESSING) | |
283 self.assertEqual(len(commit.verifications), 1) | |
284 pc.update_status() | |
285 commit = pc.queue.get(issue) | |
286 self.assertEqual('', commit.relpath) | |
287 self.assertEqual(commit.verifications['fake'].get_state(), result) | |
288 self.assertEqual(len(commit.verifications), 1) | |
289 self._check_standard_verification(pc, result == base.SUCCEEDED, True) | |
290 if result == base.SUCCEEDED: | |
291 self.context.status.check_names(['initial', 'why not', 'why not', | |
292 'why not', 'commit']) | |
293 else: | |
294 self.context.status.check_names(['initial', 'why not', 'why not', | |
295 'abort']) | |
296 | |
297 def testDeferNoPatchVerification(self): | |
298 pc = self._get_pc([fake.DeferredFakeVerifier(base.SUCCEEDED, 0)], []) | |
299 self._check_defer_1(pc, base.SUCCEEDED) | |
300 | |
301 def testDeferPatchVerification(self): | |
302 pc = self._get_pc([], [fake.DeferredFakeVerifier(base.SUCCEEDED, 0)]) | |
303 self._check_defer_1(pc, base.SUCCEEDED) | |
304 | |
305 def testDeferNoPatchVerificationFail(self): | |
306 pc = self._get_pc([fake.DeferredFakeVerifier(base.FAILED, 0)], []) | |
307 self._check_defer_1(pc, base.FAILED) | |
308 | |
309 def testDeferPatchVerificationFail(self): | |
310 pc = self._get_pc([], [fake.DeferredFakeVerifier(base.FAILED, 0)]) | |
311 self._check_defer_1(pc, base.FAILED) | |
312 | |
313 def _check_4(self, f1, f2, f3, f4): | |
314 issue = 31337 | |
315 fake1 = fake.FakeVerifier(f1) | |
316 fake1.name = 'fake1' | |
317 fake2 = fake.FakeVerifier(f2) | |
318 fake2.name = 'fake2' | |
319 fake3 = fake.FakeVerifier(f3) | |
320 fake3.name = 'fake3' | |
321 fake4 = fake.FakeVerifier(f4) | |
322 fake4.name = 'fake4' | |
323 nb = 1 | |
324 if f1 is base.SUCCEEDED: | |
325 nb = 2 | |
326 if f2 is base.SUCCEEDED: | |
327 nb = 3 | |
328 if f3 is base.SUCCEEDED: | |
329 nb = 4 | |
330 pc = self._get_pc([fake1, fake2], [fake3, fake4]) | |
331 self.assertEqual(len(pc.queue.iterate()), 0) | |
332 pc.look_for_new_pending_commit() | |
333 self.assertEqual(len(pc.queue.iterate()), 1) | |
334 commit = pc.queue.get(issue) | |
335 self.assertEqual(len(commit.verifications), 0) | |
336 pc.process_new_pending_commit() | |
337 if not all(f == base.SUCCEEDED for f in (f1, f2, f3, f4)): | |
338 self.assertEqual([], pc.queue.iterate()) | |
339 else: | |
340 commit = pc.queue.get(issue) | |
341 self.assertEqual(commit.verifications['fake1'].get_state(), f1) | |
342 self.assertEqual(commit.verifications['fake2'].get_state(), f2) | |
343 self.assertEqual(commit.verifications['fake3'].get_state(), f3) | |
344 self.assertEqual(commit.verifications['fake4'].get_state(), f4) | |
345 self.assertEqual(len(commit.verifications), nb) | |
346 pc.update_status() | |
347 if not all(f == base.SUCCEEDED for f in (f1, f2, f3, f4)): | |
348 self.assertEqual([], pc.queue.iterate()) | |
349 else: | |
350 commit = pc.queue.get(issue) | |
351 self.assertEqual(commit.verifications['fake1'].get_state(), f1) | |
352 self.assertEqual(commit.verifications['fake2'].get_state(), f2) | |
353 self.assertEqual(commit.verifications['fake3'].get_state(), f3) | |
354 self.assertEqual(commit.verifications['fake4'].get_state(), f4) | |
355 self.assertEqual(len(commit.verifications), nb) | |
356 self._check_standard_verification( | |
357 pc, all(x == base.SUCCEEDED for x in (f1, f2, f3, f4)), False) | |
358 if all(x == base.SUCCEEDED for x in (f1, f2, f3, f4)): | |
359 self.context.status.check_names(['initial', 'why not', 'commit']) | |
360 else: | |
361 self.context.status.check_names(['initial', 'abort']) | |
362 | |
363 def test4thVerificationFail(self): | |
364 self._check_4(base.SUCCEEDED, base.SUCCEEDED, base.SUCCEEDED, base.FAILED) | |
365 | |
366 def test4Verification(self): | |
367 self._check_4( | |
368 base.SUCCEEDED, base.SUCCEEDED, base.SUCCEEDED, base.SUCCEEDED) | |
369 | |
370 def test4Verification3rdFail(self): | |
371 self._check_4(base.SUCCEEDED, base.SUCCEEDED, base.FAILED, base.SUCCEEDED) | |
372 | |
373 def _check_defer_4(self, f1, f2, f3, f4): | |
374 issue = 31337 | |
375 fake1 = fake.DeferredFakeVerifier(f1, 0) | |
376 fake1.name = 'fake1' | |
377 fake2 = fake.DeferredFakeVerifier(f2, 0) | |
378 fake2.name = 'fake2' | |
379 fake3 = fake.DeferredFakeVerifier(f3, 0) | |
380 fake3.name = 'fake3' | |
381 fake4 = fake.DeferredFakeVerifier(f4, 0) | |
382 fake4.name = 'fake4' | |
383 pc = self._get_pc([fake1, fake2], [fake3, fake4]) | |
384 self.assertEqual(len(pc.queue.iterate()), 0) | |
385 pc.look_for_new_pending_commit() | |
386 self.assertEqual(len(pc.queue.iterate()), 1) | |
387 commit = pc.queue.get(issue) | |
388 self.assertEqual(len(commit.verifications), 0) | |
389 pc.process_new_pending_commit() | |
390 commit = pc.queue.get(issue) | |
391 self.assertEqual( | |
392 commit.verifications['fake1'].get_state(), base.PROCESSING) | |
393 self.assertEqual( | |
394 commit.verifications['fake2'].get_state(), base.PROCESSING) | |
395 self.assertEqual( | |
396 commit.verifications['fake3'].get_state(), base.PROCESSING) | |
397 self.assertEqual( | |
398 commit.verifications['fake4'].get_state(), base.PROCESSING) | |
399 self.assertEqual(len(commit.verifications), 4) | |
400 pc.update_status() | |
401 self.assertEqual(commit.verifications['fake1'].get_state(), f1) | |
402 self.assertEqual(commit.verifications['fake2'].get_state(), f2) | |
403 self.assertEqual(commit.verifications['fake3'].get_state(), f3) | |
404 self.assertEqual(commit.verifications['fake4'].get_state(), f4) | |
405 self.assertEqual('', commit.relpath) | |
406 self._check_standard_verification( | |
407 pc, all(x == base.SUCCEEDED for x in (f1, f2, f3, f4)), False) | |
408 if all(x == base.SUCCEEDED for x in (f1, f2, f3, f4)): | |
409 self.context.status.check_names(['initial', 'why not', 'why not', | |
410 'why not', 'commit']) | |
411 else: | |
412 self.context.status.check_names(['initial', 'why not', 'why not', | |
413 'abort']) | |
414 | |
415 def testDefer4thVerificationFail(self): | |
416 self._check_defer_4( | |
417 base.SUCCEEDED, base.SUCCEEDED, base.SUCCEEDED, base.FAILED) | |
418 | |
419 def testDefer4Verification(self): | |
420 self._check_defer_4( | |
421 base.SUCCEEDED, base.SUCCEEDED, base.SUCCEEDED, base.SUCCEEDED) | |
422 | |
423 def testDefer4Verification3rdFail(self): | |
424 self._check_defer_4( | |
425 base.SUCCEEDED, base.SUCCEEDED, base.FAILED, base.SUCCEEDED) | |
426 | |
427 def testRelPath(self): | |
428 issue = 31337 | |
429 verifiers = [ | |
430 project_base.ProjectBaseUrlVerifier( | |
431 [r'^%s(.*)$' % re.escape(r'http://example.com/')]), | |
432 ] | |
433 pc = self._get_pc([], verifiers) | |
434 pc.context.rietveld.issues[issue]['base_url'] = 'http://example.com/sub/dir' | |
435 pc.look_for_new_pending_commit() | |
436 self.assertEqual(1, len(pc.queue.iterate())) | |
437 pc.process_new_pending_commit() | |
438 self.assertEqual('sub/dir', pc.queue.get(issue).relpath) | |
439 self.context.checkout.check_calls( | |
440 [ 'prepare(None)', | |
441 'apply_patch(%r)' % (self.context.rietveld.patchsets[0],)]) | |
442 pc.update_status() | |
443 self.context.checkout.check_calls([]) | |
444 pc.scan_results() | |
445 self.context.checkout.check_calls( | |
446 # Will sync to HEAD, 124. | |
447 self._prepare_apply_commit(1, issue)) | |
448 self.context.rietveld.check_calls( | |
449 [ _try_comment(), | |
450 'close_issue(%d)' % issue, | |
451 "update_description(%d, u'foo')" % issue, | |
452 "add_comment(%d, 'Change committed as 125')" % issue]) | |
453 self.context.status.check_names(['initial', 'why not', 'commit']) | |
454 | |
455 def testCommitBurst(self): | |
456 issue = 31337 | |
457 pc = self._get_pc([fake.FakeVerifier(base.SUCCEEDED)], []) | |
458 self.assertEqual(4, pc.MAX_COMMIT_BURST) | |
459 timestamp = [1] | |
460 self.mock(time, 'time', lambda: timestamp[-1]) | |
461 for i in range(pc.MAX_COMMIT_BURST + 2): | |
462 self.context.rietveld.issues[i] = ( | |
463 self.context.rietveld.issues[issue].copy()) | |
464 self.context.rietveld.issues[i]['issue'] = i | |
465 pc.look_for_new_pending_commit() | |
466 self.assertEqual(len(pc.queue.iterate()), pc.MAX_COMMIT_BURST + 3) | |
467 pc.process_new_pending_commit() | |
468 pc.update_status() | |
469 pc.scan_results() | |
470 self.context.checkout.check_calls( | |
471 self._prepare_apply_commit(0, 0) + | |
472 self._prepare_apply_commit(1, 1) + | |
473 self._prepare_apply_commit(2, 2) + | |
474 self._prepare_apply_commit(3, 3)) | |
475 self.context.rietveld.check_calls( | |
476 [ _try_comment(0), | |
477 _try_comment(1), | |
478 _try_comment(2), | |
479 _try_comment(3), | |
480 _try_comment(4), | |
481 _try_comment(5), | |
482 _try_comment(), | |
483 'close_issue(0)', | |
484 "update_description(0, u'foo')", | |
485 "add_comment(0, 'Change committed as 125')", | |
486 'close_issue(1)', | |
487 "update_description(1, u'foo')", | |
488 "add_comment(1, 'Change committed as 125')", | |
489 'close_issue(2)', | |
490 "update_description(2, u'foo')", | |
491 "add_comment(2, 'Change committed as 125')", | |
492 'close_issue(3)', | |
493 "update_description(3, u'foo')", | |
494 "add_comment(3, 'Change committed as 125')", | |
495 ]) | |
496 self.assertEqual(3, len(pc.queue.iterate())) | |
497 total = pc.MAX_COMMIT_BURST + 3 | |
498 self.context.status.check_names(['initial'] * total + | |
499 (['why not', 'commit'] * | |
500 pc.MAX_COMMIT_BURST) + | |
501 ['why not'] * 3) | |
502 | |
503 # Dry run. | |
504 pc.scan_results() | |
505 self.context.checkout.check_calls([]) | |
506 self.context.rietveld.check_calls([]) | |
507 self.context.status.check_names(['why not'] * 3) | |
508 | |
509 # Remove one item from the burst. | |
510 pc.recent_commit_timestamps.pop() | |
511 pc.scan_results() | |
512 next_item = pc.MAX_COMMIT_BURST | |
513 self.context.checkout.check_calls( | |
514 self._prepare_apply_commit(next_item, next_item)) | |
515 self.context.rietveld.check_calls( | |
516 [ 'close_issue(%d)' % next_item, | |
517 "update_description(%d, u'foo')" % next_item, | |
518 "add_comment(%d, 'Change committed as 125')" % next_item, | |
519 ]) | |
520 self.context.status.check_names(['why not', 'commit'] + ['why not'] * 2) | |
521 # After a delay, must flush the queue. | |
522 timestamp.append(timestamp[-1] + pc.COMMIT_BURST_DELAY + 1) | |
523 pc.scan_results() | |
524 self.context.checkout.check_calls( | |
525 self._prepare_apply_commit(next_item + 1, next_item + 1) + | |
526 self._prepare_apply_commit(next_item + 2, issue)) | |
527 self.context.rietveld.check_calls( | |
528 [ 'close_issue(%d)' % (next_item + 1), | |
529 "update_description(%d, u'foo')" % (next_item + 1), | |
530 "add_comment(%d, 'Change committed as 125')" % (next_item + 1), | |
531 'close_issue(%d)' % issue, | |
532 "update_description(%d, u'foo')" % issue, | |
533 "add_comment(%d, 'Change committed as 125')" % issue]) | |
534 self.context.status.check_names(['why not', 'commit'] * 2) | |
535 | |
536 def testIgnored(self): | |
537 issue = 31337 | |
538 verifiers = [ | |
539 project_base.ProjectBaseUrlVerifier( | |
540 [r'^%s(.*)$' % re.escape(r'http://example.com/')]), | |
541 ] | |
542 pc = self._get_pc(verifiers, []) | |
543 pc.context.rietveld.issues[issue]['base_url'] = 'http://unrelated.com/sub' | |
544 pc.look_for_new_pending_commit() | |
545 pc.process_new_pending_commit() | |
546 pc.update_status() | |
547 pc.scan_results() | |
548 self.assertEqual(1, len(pc.queue.iterate())) | |
549 self.assertEqual('', pc.queue.get(issue).relpath) | |
550 self.assertEqual(base.IGNORED, pc.queue.get(issue).get_state()) | |
551 | |
552 def testServerHooksMissing(self): | |
553 issue = 31337 | |
554 verifiers = [ | |
555 project_base.ProjectBaseUrlVerifier( | |
556 [r'^%s(.*)$' % re.escape(r'http://example.com/')]), | |
557 ] | |
558 self.context.server_hooks_missing = True | |
559 pc = self._get_pc(verifiers, []) | |
560 pc.context.rietveld.issues[issue]['base_url'] = 'http://example.com/' | |
561 pc.look_for_new_pending_commit() | |
562 pc.process_new_pending_commit() | |
563 pc.update_status() | |
564 pc.scan_results() | |
565 self.context.rietveld.check_calls( | |
566 [ _try_comment(), | |
567 'close_issue(%d)' % issue, | |
568 "update_description(%d, u'foo')" % issue, | |
569 "add_comment(%d, 'Change committed as 125')" % issue]) | |
570 self.context.status.check_names(['initial', 'why not', 'commit']) | |
571 self.context.checkout.check_calls( | |
572 self._prepare_apply_commit(0, issue, server_hooks_missing=True)) | |
573 | |
574 def testDisapeared(self): | |
575 issue = 31337 | |
576 verifiers = [ | |
577 project_base.ProjectBaseUrlVerifier( | |
578 [r'^%s(.*)$' % re.escape(r'http://example.com/')]), | |
579 ] | |
580 pc = self._get_pc(verifiers, []) | |
581 pc.context.rietveld.issues[issue]['base_url'] = 'http://unrelated.com/sub' | |
582 pc.look_for_new_pending_commit() | |
583 pc.process_new_pending_commit() | |
584 pc.update_status() | |
585 pc.scan_results() | |
586 self.assertEqual(1, len(pc.queue.iterate())) | |
587 del pc.context.rietveld.issues[issue] | |
588 pc.look_for_new_pending_commit() | |
589 pc.process_new_pending_commit() | |
590 pc.update_status() | |
591 pc.scan_results() | |
592 self.assertEqual(0, len(pc.queue.iterate())) | |
593 self.context.status.check_names(['abort']) | |
594 | |
595 def _get_pc_reviewer(self): | |
596 verifiers = [ | |
597 reviewer_lgtm.ReviewerLgtmVerifier( | |
598 ['.*'], [re.escape('commit-bot@example.com')]) | |
599 ] | |
600 pc = self._get_pc(verifiers, []) | |
601 return pc | |
602 | |
603 def _approve(self, sender=None): | |
604 issue = 31337 | |
605 if not sender: | |
606 sender = self.context.rietveld.issues[issue]['reviewers'][0] | |
607 self.context.rietveld.issues[issue]['messages'].append( | |
608 { | |
609 'approval': True, | |
610 'sender': sender, | |
611 }) | |
612 | |
613 def testVerifyDefaultMock(self): | |
614 # Verify mock expectation for the default settings. | |
615 issue = 31337 | |
616 pc = self._get_pc_reviewer() | |
617 self.assertEqual(0, len(pc.queue.iterate())) | |
618 pc.look_for_new_pending_commit() | |
619 self.assertEqual(1, len(pc.queue.iterate())) | |
620 # Pop the LGTM. | |
621 pc.queue.iterate()[0].messages.pop(1) | |
622 pc.process_new_pending_commit() | |
623 self.assertEqual(0, len(pc.queue.iterate())) | |
624 pc.update_status() | |
625 self.assertEqual(0, len(pc.queue.iterate())) | |
626 self.context.rietveld.check_calls( | |
627 [ "set_flag(%d, 1, 'commit', False)" % issue, | |
628 "add_comment(%d, %r)" % (issue, reviewer_lgtm.LgtmStatus.NO_LGTM)]) | |
629 self.context.status.check_names(['abort']) | |
630 | |
631 def testVerifyDefaultMockPlusLGTM(self): | |
632 # Verify mock expectation with a single approval message. | |
633 issue = 31337 | |
634 pc = self._get_pc_reviewer() | |
635 self._approve() | |
636 self.assertEqual(0, len(pc.queue.iterate())) | |
637 pc.look_for_new_pending_commit() | |
638 self.assertEqual(1, len(pc.queue.iterate())) | |
639 pc.process_new_pending_commit() | |
640 self.assertEqual(1, len(pc.queue.iterate())) | |
641 pc.update_status() | |
642 self.assertEqual(1, len(pc.queue.iterate())) | |
643 pc.scan_results() | |
644 self.assertEqual(0, len(pc.queue.iterate())) | |
645 self.context.rietveld.check_calls( | |
646 [ _try_comment(), | |
647 'close_issue(%d)' % issue, | |
648 "update_description(%d, u'foo')" % issue, | |
649 "add_comment(%d, 'Change committed as 125')" % issue]) | |
650 self.context.status.check_names(['initial', 'why not', 'commit']) | |
651 self.context.checkout.check_calls( | |
652 self._prepare_apply_commit(0, issue)) | |
653 | |
654 def testDriveBy(self): | |
655 issue = 31337 | |
656 pc = self._get_pc_reviewer() | |
657 self._approve() | |
658 pc.look_for_new_pending_commit() | |
659 pc.process_new_pending_commit() | |
660 pc.update_status() | |
661 # A new reviewer prevents the commit. | |
662 i = self.context.rietveld.issues[issue] | |
663 i['reviewers'] = i['reviewers'] + ['annoying@dude.org'] | |
664 pc.scan_results() | |
665 self.context.rietveld.check_calls( | |
666 [ _try_comment(), | |
667 "set_flag(%d, 1, 'commit', False)" % issue, | |
668 ('add_comment(%d, "List of reviewers changed. annoying@dude.org ' | |
669 'did a drive-by without LGTM\'ing!")') % issue]) | |
670 self.context.status.check_names(['initial', 'abort']) | |
671 | |
672 def testDriveByLGTM(self): | |
673 issue = 31337 | |
674 pc = self._get_pc_reviewer() | |
675 self._approve() | |
676 pc.look_for_new_pending_commit() | |
677 pc.process_new_pending_commit() | |
678 pc.update_status() | |
679 # He's nice, he left a LGTM. | |
680 i = self.context.rietveld.issues[issue] | |
681 i['reviewers'] = i['reviewers'] + ['nice@dude.org'] | |
682 self._approve('nice@dude.org') | |
683 pc.scan_results() | |
684 self.assertEqual(0, len(pc.queue.iterate())) | |
685 self.context.rietveld.check_calls( | |
686 [ _try_comment(), | |
687 'close_issue(%d)' % issue, | |
688 "update_description(%d, u'foo')" % issue, | |
689 "add_comment(%d, 'Change committed as 125')" % issue]) | |
690 self.context.status.check_names(['initial', 'why not', 'commit']) | |
691 self.context.checkout.check_calls( | |
692 self._prepare_apply_commit(0, issue)) | |
693 | |
694 def testWhyNotUpdates(self): | |
695 issue = 31337 | |
696 fake1_change = 1 | |
697 fake1 = fake.DeferredFakeVerifier(base.SUCCEEDED, fake1_change) | |
698 fake1.name = 'fake1' | |
699 | |
700 fake2_change = 3 | |
701 fake2 = fake.DeferredFakeVerifier(base.SUCCEEDED, fake2_change) | |
702 fake2.name = 'fake2' | |
703 pc = self._get_pc([fake1, fake2], []) | |
704 pc.look_for_new_pending_commit() | |
705 pc.process_new_pending_commit() | |
706 | |
707 self.context.status.check_names(['initial', 'why not']) | |
708 | |
709 # Make sure the 'why not' is only pushed when it changes. | |
710 for i in range(fake2_change * 2): | |
711 expected = ['why not'] if fake1_change == i or fake2_change == i else [] | |
712 pc.update_status() | |
713 self.context.status.check_names(expected) | |
714 | |
715 pc.scan_results() | |
716 | |
717 self.context.rietveld.check_calls( | |
718 [_try_comment(), | |
719 'close_issue(%d)' % issue, | |
720 "update_description(%d, u'foo')" % issue, | |
721 "add_comment(%d, 'Change committed as 125')" % issue]) | |
722 self.context.status.check_names(['why not', 'commit']) | |
723 self.context.checkout.check_calls( | |
724 self._prepare_apply_commit(0, issue)) | |
725 | |
726 | |
727 if __name__ == '__main__': | |
728 logging.basicConfig( | |
729 level=[logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG][ | |
730 min(sys.argv.count('-v'), 3)], | |
731 format='%(levelname)5s %(module)15s(%(lineno)3d): %(message)s') | |
732 unittest.main() | |
OLD | NEW |