| 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 |