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 verification/try_server.py.""" | |
7 | |
8 import json | |
9 import logging | |
10 import optparse | |
11 import os | |
12 import re | |
13 import StringIO | |
14 import sys | |
15 import time | |
16 import unittest | |
17 import urllib | |
18 | |
19 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
20 sys.path.insert(0, os.path.join(ROOT_DIR, '..')) | |
21 | |
22 # In tests/ | |
23 import mocks # pylint: disable=W0403 | |
24 | |
25 from testing_support import auto_stub | |
26 | |
27 # In root | |
28 import buildbot_json | |
29 from verification import base | |
30 from verification import try_server | |
31 | |
32 # pylint: disable=W0212 | |
33 SUCCESS = buildbot_json.SUCCESS | |
34 WARNINGS = buildbot_json.WARNINGS | |
35 FAILURE = buildbot_json.FAILURE | |
36 SKIPPED = buildbot_json.SKIPPED | |
37 EXCEPTION = buildbot_json.EXCEPTION | |
38 | |
39 | |
40 class FakeTryServer(auto_stub.SimpleMock): | |
41 """Stateful try server mock. | |
42 | |
43 Includes calls to send try jobs TryChange() and visible results from HTTP | |
44 requests. | |
45 """ | |
46 def __init__(self, unit_test): | |
47 super(FakeTryServer, self).__init__(unit_test) | |
48 | |
49 # Try server immutable properties. | |
50 self.steps = ['update', 'compile', 'test1', 'test2'] | |
51 self.server_url = 'http://foo/bar' | |
52 | |
53 # State of the try server. | |
54 self._builds = { 'linux': [], 'mac': [] } | |
55 self.pending_builds = { | |
56 'linux': [], | |
57 'mac': [] | |
58 } | |
59 # Default mocks. | |
60 self.unit_test.mock(urllib, 'urlopen', self._mockurlopen) | |
61 self.unit_test.mock(try_server.trychange, 'TryChange', self.TryChangeMock) | |
62 | |
63 def TryChangeMock(self, cmd, _change, swallow_exception): | |
64 """Mocks trychange.py.""" | |
65 self.assertEqual(swallow_exception, True) | |
66 parser = optparse.OptionParser() | |
67 parser.add_option('--bot', action='append') | |
68 parser.add_option('--clobber', action='store_true', default=False) | |
69 parser.add_option('--email') | |
70 parser.add_option('--issue', type='int') | |
71 parser.add_option('--name') | |
72 parser.add_option('--no_search', action='store_true') | |
73 parser.add_option('--patchset', type='int') | |
74 parser.add_option('--revision') | |
75 parser.add_option('--rietveld_url') | |
76 parser.add_option('--user') | |
77 options, args = parser.parse_args(cmd) | |
78 self.assertEqual(options.email, 'user1@example.com') | |
79 self.assertEqual(options.issue, 42) | |
80 self.assertEqual(options.no_search, True) | |
81 self.assertEqual(options.patchset, 23) | |
82 self.assertEqual( | |
83 options.rietveld_url, | |
84 '%s/download/issue42_23.diff' % self.unit_test.context.rietveld.url) | |
85 self.assertEqual(options.user, 'user1') | |
86 self.assertEqual(args, ['extra_flags']) | |
87 bot = ', '.join(options.bot) | |
88 call = 'trychange b={%s} c=%s r=%s' % ( | |
89 bot, options.clobber, options.revision) | |
90 if options.name != '42-23': | |
91 call += ' n=%s' % options.name | |
92 self.calls.append(call) | |
93 logging.debug(self.calls[-1]) | |
94 | |
95 def _mockurlopen_internal(self, sub_url): | |
96 """Returns data to be encoded before returning.""" | |
97 # sub_url is str on python <= 2.6 and unicode for >= 2.7 | |
98 self.unit_test.assertTrue(isinstance(sub_url, basestring)) | |
99 expected_url = self.server_url + '/json/' | |
100 self.unit_test.assertTrue(sub_url.startswith(expected_url)) | |
101 self.calls.append(sub_url[len(expected_url):]) | |
102 baseurl = '^%s/json/' % re.escape(self.server_url) | |
103 match = re.match(baseurl + r'builders/(\w+)/builds/\?(.+)$', sub_url) | |
104 if match: | |
105 data = {} | |
106 for query in match.group(2).split('&'): | |
107 m = re.match(r'select=(\w+)', query) | |
108 self.unit_test.assertTrue(m, (match.group(2), query)) | |
109 build = int(m.group(1)) | |
110 data[str(build)] = self._builds[match.group(1)][build] | |
111 return data | |
112 | |
113 match = re.match(baseurl + r'builders/(\w+)/builds/_all$', sub_url) | |
114 if match: | |
115 # Data is not stored exactly as the try server serves it. | |
116 data = {} | |
117 for i, build in enumerate(self._builds[match.group(1)]): | |
118 data[str(i)] = build | |
119 return data | |
120 | |
121 match = re.match(baseurl + r'builders/\?(.+)$', sub_url) | |
122 if match: | |
123 data = {} | |
124 for query in match.group(1).split('&'): | |
125 m = re.match(r'select=(\w+)', query) | |
126 self.unit_test.assertTrue(m, (match.group(1), query)) | |
127 builder = m.group(1) | |
128 data[builder] = { | |
129 'cachedBuilds': range(len(self._builds[builder])), | |
130 'pendingBuilds': len(self.pending_builds.get(builder, [])), | |
131 } | |
132 return data | |
133 | |
134 match = re.match(baseurl + r'builders/(\w+)/pendingBuilds$', sub_url) | |
135 if match: | |
136 return self.pending_builds[match.group(1)] | |
137 | |
138 def _mockurlopen(self, sub_url): | |
139 """Mocks urllib.urlopen() and JSON encode + StringIO buffers.""" | |
140 sub_url = re.match(r'^(.+)[\&\?]filter=1$', sub_url).group(1) | |
141 data = self._mockurlopen_internal(sub_url) | |
142 self.unit_test.assertNotEquals(None, data, sub_url) | |
143 #logging.debug('_mockurlopen(%s) -> %s' % (sub_url, data)) | |
144 return StringIO.StringIO(json.dumps(data)) | |
145 | |
146 def add_build(self, builder, revision, reason, step_results): | |
147 """Add a build to a builder.""" | |
148 self.assertEqual(len(self.steps), len(step_results)) | |
149 assert isinstance(revision, (str, int)) | |
150 data = { | |
151 'reason': reason or self.unit_test.pending.pending_name(), | |
152 'sourceStamp': { | |
153 'revision': 'sol@%s' % revision, | |
154 'hasPatch': True, | |
155 }, | |
156 'steps': [], | |
157 'blame': ['user1@example.com'], | |
158 'number': 0, | |
159 'slave': 'foo', | |
160 } | |
161 result = max(step_results) | |
162 if result in (SUCCESS, WARNINGS) and step_results[-1] is None: | |
163 result = None | |
164 for i, step in enumerate(self.steps): | |
165 data['steps'].append({ | |
166 'name': step, | |
167 'results': [step_results[i]], | |
168 }) | |
169 data['results'] = [result] | |
170 self._builds[builder].append(data) | |
171 | |
172 def set_build_result(self, builder, result): | |
173 """Override the build result for every steps to |result|.""" | |
174 for step in self._builds[builder][-1]['steps']: | |
175 step['results'] = [result] | |
176 self._builds[builder][-1]['results'] = [result] | |
177 | |
178 | |
179 class TryServerSvnTest(mocks.TestCase): | |
180 def setUp(self): | |
181 super(TryServerSvnTest, self).setUp() | |
182 self.email = 'user1@example.com' | |
183 self.user = 'user1' | |
184 self.timestamp = [1.] | |
185 # Mocks http://chromium-status.appspot.com/lkgr | |
186 self.lkgr = 123 | |
187 self.try_server = FakeTryServer(self) | |
188 self.mock(time, 'time', lambda: self.timestamp[-1]) | |
189 | |
190 try_server.TryRunnerSvn.update_latency = 0 | |
191 self.builders_and_tests = { | |
192 'linux': ['test1', 'test2'], | |
193 'mac': ['test1', 'test2'], | |
194 } | |
195 self.try_runner = try_server.TryRunnerSvn( | |
196 self.context, | |
197 self.try_server.server_url, | |
198 self.email, | |
199 self.builders_and_tests, | |
200 ['ignored_step'], | |
201 'sol', | |
202 ['extra_flags',], | |
203 lambda: self.lkgr, | |
204 ) | |
205 self.pending.revision = 123 | |
206 | |
207 def tearDown(self): | |
208 try: | |
209 if not self.has_failed(): | |
210 self.try_server.check_calls([]) | |
211 finally: | |
212 super(TryServerSvnTest, self).tearDown() | |
213 | |
214 def get_verif(self): | |
215 return self.pending.verifications[self.try_runner.name] | |
216 | |
217 def assertPending( | |
218 self, state, nb_jobs, error_message, | |
219 linux_build=1, | |
220 mac_build=1, | |
221 linux_state=None, | |
222 mac_state=None, | |
223 linux_clobber=False, | |
224 mac_clobber=False, | |
225 linux_rev=123, | |
226 mac_rev=123, | |
227 linux_sent=1, | |
228 mac_sent=1, | |
229 linux_name='42-23', | |
230 mac_name='42-23'): | |
231 if linux_state is None: | |
232 linux_state = state | |
233 if mac_state is None: | |
234 mac_state = state | |
235 self.assertEqual([self.try_runner.name], self.pending.verifications.keys()) | |
236 self.assertEqual(error_message, self.get_verif().error_message) | |
237 self.assertEqual(nb_jobs, len(self.get_verif().try_jobs)) | |
238 self.assertEqual( | |
239 len(self.builders_and_tests), len(self.get_verif().try_jobs)) | |
240 self.assertEqual(linux_name, self.get_verif().try_jobs[0].name) | |
241 self.assertEqual('linux', self.get_verif().try_jobs[0].builder) | |
242 self.assertEqual(linux_rev, self.get_verif().try_jobs[0].revision) | |
243 self.assertEqual(linux_sent, self.get_verif().try_jobs[0].sent) | |
244 self.assertEqual(linux_clobber, self.get_verif().try_jobs[0].clobber) | |
245 self.assertEqual(linux_build, self.get_verif().try_jobs[0].build) | |
246 self.assertEqual(linux_state, self.get_verif().try_jobs[0].get_state()) | |
247 if len(self.builders_and_tests) > 1: | |
248 self.assertEqual(mac_name, self.get_verif().try_jobs[1].name) | |
249 self.assertEqual('mac', self.get_verif().try_jobs[1].builder) | |
250 self.assertEqual(mac_rev, self.get_verif().try_jobs[1].revision) | |
251 self.assertEqual(mac_sent, self.get_verif().try_jobs[1].sent) | |
252 self.assertEqual(mac_clobber, self.get_verif().try_jobs[1].clobber) | |
253 self.assertEqual(mac_build, self.get_verif().try_jobs[1].build) | |
254 self.assertEqual(mac_state, self.get_verif().try_jobs[1].get_state()) | |
255 self.assertEqual(state, self.get_verif().get_state()) | |
256 | |
257 def testVoid(self): | |
258 self.assertEqual(self.pending.verifications.keys(), []) | |
259 | |
260 def testVerificationVoid(self): | |
261 self.try_runner.verify(self.pending) | |
262 self.assertPending(base.PROCESSING, 2, None, linux_build=None, | |
263 mac_build=None) | |
264 self.try_server.check_calls( | |
265 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
266 self.context.status.check_names(['try server'] * 2) | |
267 | |
268 def testVoidUpdate(self): | |
269 self.try_runner.update_status([]) | |
270 | |
271 def test_steps_quality(self): | |
272 self.assertEqual(None, try_server.steps_quality([])) | |
273 self.assertEqual(True, try_server.steps_quality([True, None])) | |
274 self.assertEqual(False, try_server.steps_quality([True, None, False])) | |
275 | |
276 def testStepQualityNone(self): | |
277 self.try_runner.status.builders['linux'].builds.cache() | |
278 self.assertEqual( | |
279 (None, 0), | |
280 self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) | |
281 self.try_server.check_calls(['builders/linux/builds/_all']) | |
282 | |
283 def testStepQualityGood(self): | |
284 self.try_server.add_build( | |
285 'linux', 123, None, [SUCCESS, None, None, None]) | |
286 self.try_runner.status.builders['linux'].builds.cache() | |
287 self.try_server.check_calls(['builders/linux/builds/_all']) | |
288 self.assertEqual( | |
289 ([True, None, None, None], 1), | |
290 self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) | |
291 self.try_server.set_build_result('linux', SUCCESS) | |
292 self.try_runner.status.builders['linux'].builds.refresh() | |
293 self.assertEqual( | |
294 ([True] * 4, 1), | |
295 self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) | |
296 self.try_server.check_calls(['builders/linux/builds/_all']) | |
297 | |
298 def testStepQualityBad(self): | |
299 self.try_server.add_build( | |
300 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, SUCCESS]) | |
301 self.try_runner.status.builders['linux'].builds.cache() | |
302 # Also test that FakeTryServer.add_build() is implemented correctly. | |
303 self.assertEqual( | |
304 ([True, True, False, True], 1), | |
305 self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) | |
306 self.try_server.check_calls(['builders/linux/builds/_all']) | |
307 | |
308 def testStepQualityBadIncomplete(self): | |
309 self.try_server.add_build( | |
310 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, None]) | |
311 self.try_runner.status.builders['linux'].builds.cache() | |
312 # Also test that FakeTryServer.add_build() is implemented correctly. | |
313 self.assertEqual( | |
314 ([True, True, False, None], 1), | |
315 self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) | |
316 self.try_server.check_calls(['builders/linux/builds/_all']) | |
317 | |
318 def testStepQualityGoodAndBad(self): | |
319 self.try_server.add_build( | |
320 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
321 self.try_server.add_build('linux', 123, None, [FAILURE, None, None, None]) | |
322 self.try_runner.status.builders['linux'].builds.cache() | |
323 self.try_server.check_calls(['builders/linux/builds/_all']) | |
324 self.assertEqual( | |
325 ([True] * 4, 2), | |
326 self.try_runner.step_db.revision_quality_builder_steps('linux', 123)) | |
327 | |
328 def testQualityAutomatic(self): | |
329 self.try_runner.verify(self.pending) | |
330 self.assertEqual( | |
331 (None, 0), | |
332 self.try_runner.step_db.revision_quality_builder_steps( | |
333 'linux', 123)) | |
334 self.try_server.add_build( | |
335 'linux', 123, 'georges tried stuff', | |
336 [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
337 self.try_runner.update_status([self.pending]) | |
338 self.assertEqual( | |
339 ([True] * 4, 1), | |
340 self.try_runner.step_db.revision_quality_builder_steps( | |
341 'linux', 123)) | |
342 self.try_server.check_calls( | |
343 [ 'trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123', | |
344 'builders/?select=linux&select=mac', | |
345 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
346 self.context.status.check_names(['try server'] * 2) | |
347 | |
348 def testQualityManual(self): | |
349 self.try_server.add_build( | |
350 'linux', 123, 'georges tried stuff', | |
351 [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
352 self.try_runner.status.builders['linux'].builds.cache() | |
353 self.assertEqual( | |
354 ([True] * 4, 1), | |
355 self.try_runner.step_db.revision_quality_builder_steps( | |
356 'linux', 123)) | |
357 self.try_server.check_calls(['builders/linux/builds/_all']) | |
358 | |
359 def _simple(self, status_linux, status_mac=None, error_msg=None): | |
360 """status_linux affects test1, status_mac affects test2.""" | |
361 def is_failure(status): | |
362 return status in (FAILURE, EXCEPTION) | |
363 | |
364 self.assertEqual( | |
365 bool(is_failure(status_linux) or is_failure(status_mac)), | |
366 bool(error_msg)) | |
367 if status_mac is None: | |
368 status_mac = status_linux | |
369 self.lkgr = 12 | |
370 self.try_server.add_build( | |
371 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
372 self.try_server.add_build( | |
373 'mac', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
374 | |
375 self.try_runner.verify(self.pending) | |
376 self.try_server.check_calls( | |
377 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
378 self.try_server.add_build( | |
379 'linux', 123, None, [SUCCESS, SUCCESS, status_linux, SUCCESS]) | |
380 self.try_server.add_build( | |
381 'mac', 123, None, [SUCCESS, SUCCESS, SUCCESS, status_mac]) | |
382 self.try_runner.update_status([self.pending]) | |
383 | |
384 if is_failure(status_linux): | |
385 self.assertEqual(123, self.try_runner.get_lkgr('linux')) | |
386 self.assertEqual( | |
387 123, self.try_runner.step_db.last_good_revision_builder('linux')) | |
388 else: | |
389 self.assertEqual(123, self.try_runner.get_lkgr('linux')) | |
390 self.assertEqual( | |
391 123, self.try_runner.step_db.last_good_revision_builder('linux')) | |
392 if is_failure(status_mac): | |
393 self.assertEqual(123, self.try_runner.get_lkgr('mac')) | |
394 self.assertEqual( | |
395 123, self.try_runner.step_db.last_good_revision_builder('mac')) | |
396 else: | |
397 self.assertEqual(123, self.try_runner.get_lkgr('mac')) | |
398 self.assertEqual( | |
399 123, self.try_runner.step_db.last_good_revision_builder('mac')) | |
400 | |
401 if error_msg: | |
402 # Can't test failure without testing automatic retry mechanism. | |
403 expected = ( | |
404 [ 'builders/?select=linux&select=mac', | |
405 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
406 | |
407 if is_failure(status_linux): | |
408 expected.append( | |
409 'trychange b={linux:test1} c=False r=sol@123 n=42-23 (retry)') | |
410 linux_build = None | |
411 linux_state = base.PROCESSING | |
412 linux_name = '42-23 (retry)' | |
413 else: | |
414 linux_build = 1 | |
415 linux_state = base.SUCCEEDED | |
416 linux_name = '42-23' | |
417 | |
418 if is_failure(status_mac): | |
419 expected.append( | |
420 'trychange b={mac:test2} c=False r=sol@123 n=42-23 (retry)') | |
421 mac_build = None | |
422 mac_state = base.PROCESSING | |
423 mac_name = '42-23 (retry)' | |
424 else: | |
425 mac_build = 1 | |
426 mac_state = base.SUCCEEDED | |
427 mac_name = '42-23' | |
428 | |
429 self.assertPending( | |
430 base.PROCESSING, 2, None, linux_build=linux_build, | |
431 mac_build=mac_build, linux_state=linux_state, mac_state=mac_state, | |
432 linux_name=linux_name, mac_name=mac_name) | |
433 self.try_server.check_calls(expected) | |
434 | |
435 if is_failure(status_linux): | |
436 self.try_server.add_build( | |
437 'linux', 123, linux_name, [SUCCESS, SUCCESS, status_linux, SUCCESS]) | |
438 if is_failure(status_mac): | |
439 self.try_server.add_build( | |
440 'mac', 123, mac_name, [SUCCESS, SUCCESS, SUCCESS, status_mac]) | |
441 | |
442 self.try_runner.update_status([self.pending]) | |
443 if is_failure(status_linux): | |
444 linux_state = base.FAILED | |
445 linux_build = 2 | |
446 else: | |
447 linux_build = 1 | |
448 if is_failure(status_mac): | |
449 mac_state = base.FAILED | |
450 mac_build = 2 | |
451 else: | |
452 mac_build = 1 | |
453 self.assertPending( | |
454 base.FAILED, 2, error_msg, | |
455 linux_build=linux_build, mac_build=mac_build, | |
456 linux_state=linux_state, mac_state=mac_state, | |
457 linux_name=linux_name, mac_name=mac_name) | |
458 | |
459 if is_failure(status_linux) and is_failure(status_mac): | |
460 self.try_server.check_calls( | |
461 [ 'builders/?select=linux&select=mac', | |
462 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
463 self.context.checkout.check_calls( | |
464 [ 'prepare(123)', | |
465 'apply_patch(%r)' % self.context.rietveld.patchsets[-2], | |
466 'prepare(123)', | |
467 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) | |
468 elif is_failure(status_linux): | |
469 self.try_server.check_calls( | |
470 ['builders/?select=linux', 'builders/linux/builds/_all']) | |
471 self.context.checkout.check_calls( | |
472 [ 'prepare(123)', | |
473 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) | |
474 else: | |
475 self.try_server.check_calls( | |
476 ['builders/?select=mac', 'builders/mac/builds/_all']) | |
477 self.context.checkout.check_calls( | |
478 [ 'prepare(123)', | |
479 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) | |
480 else: | |
481 self.assertPending(base.SUCCEEDED, 2, None) | |
482 self.try_server.check_calls( | |
483 [ 'builders/?select=linux&select=mac', | |
484 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
485 count = 6 + 3 * ( | |
486 int(is_failure(status_linux)) + int(is_failure(status_mac))) | |
487 self.context.status.check_names(['try server'] * count) | |
488 | |
489 def testImmediateSuccess(self): | |
490 self._simple(SUCCESS) | |
491 | |
492 def testImmediateWarnings(self): | |
493 self._simple(WARNINGS) | |
494 | |
495 def testImmediateSkipped(self): | |
496 self._simple(SKIPPED) | |
497 | |
498 def second_fail_msg( | |
499 self, clname, step2, step1, builder, number, is_clobber=False): | |
500 extra = '' | |
501 if is_clobber: | |
502 extra = ' (clobber build)' | |
503 return ( | |
504 u'Try job failure for %s on %s for step "%s"%s.\n' | |
505 u'It\'s a second try, previously, step "%s" failed.\n' | |
506 u'%s/buildstatus?builder=%s&number=%s\n') % ( | |
507 clname, builder, step2, extra, step1, self.try_server.server_url, | |
508 builder, number) | |
509 def testImmediateFailureLinux(self): | |
510 self._simple( | |
511 FAILURE, SUCCESS, | |
512 self.second_fail_msg('42-23 (retry)', 'test1', 'test1', 'linux', 2)) | |
513 | |
514 def testImmediateFailureMac(self): | |
515 self._simple( | |
516 SUCCESS, FAILURE, | |
517 self.second_fail_msg('42-23 (retry)', 'test2', 'test2', 'mac', 2)) | |
518 | |
519 def testImmediateDoubleFailure(self): | |
520 self._simple( | |
521 FAILURE, FAILURE, | |
522 self.second_fail_msg('42-23 (retry)', 'test2', 'test2', 'mac', 2)) | |
523 | |
524 def testImmediateException(self): | |
525 self._simple( | |
526 SUCCESS, EXCEPTION, | |
527 self.second_fail_msg('42-23 (retry)', 'test2', 'test2', 'mac', 2)) | |
528 | |
529 def testSuccess(self): | |
530 self.lkgr = 2 | |
531 # Normal workflow with incremental success. | |
532 self.try_runner.verify(self.pending) | |
533 self.try_server.check_calls( | |
534 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
535 | |
536 self.try_runner.update_status([self.pending]) | |
537 self.assertPending( | |
538 base.PROCESSING, 2, None, linux_build=None, mac_build=None) | |
539 self.try_server.check_calls( | |
540 ['builders/?select=linux&select=mac', | |
541 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
542 | |
543 self.try_server.add_build( | |
544 'linux', 123, None, [SUCCESS, None, None, None]) | |
545 self.try_runner.update_status([self.pending]) | |
546 self.assertPending(base.PROCESSING, 2, None, linux_build=0, mac_build=None) | |
547 self.try_server.check_calls( | |
548 ['builders/?select=linux&select=mac', | |
549 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
550 | |
551 self.try_server.add_build( | |
552 'mac', 123, None, [SUCCESS, None, None, None]) | |
553 self.try_runner.update_status([self.pending]) | |
554 self.assertPending(base.PROCESSING, 2, None, linux_build=0, mac_build=0) | |
555 self.try_server.check_calls( | |
556 ['builders/?select=mac', | |
557 'builders/mac/builds/_all', 'builders/linux/builds/?select=0']) | |
558 | |
559 # This one will be cached since it's now immutable. | |
560 self.try_server.set_build_result('mac', SUCCESS) | |
561 self.try_runner.update_status([self.pending]) | |
562 self.assertPending( | |
563 base.PROCESSING, 2, None, linux_build=0, mac_build=0, | |
564 mac_state=base.SUCCEEDED) | |
565 self.try_server.check_calls( | |
566 ['builders/linux/builds/?select=0', 'builders/mac/builds/?select=0']) | |
567 | |
568 self.try_server.set_build_result('linux', SUCCESS) | |
569 self.try_runner.update_status([self.pending]) | |
570 self.assertPending(base.SUCCEEDED, 2, None, linux_build=0, mac_build=0) | |
571 self.assertEqual( | |
572 123, self.try_runner.step_db.last_good_revision_builder('linux')) | |
573 self.assertEqual( | |
574 123, self.try_runner.step_db.last_good_revision_builder('mac')) | |
575 self.assertEqual( | |
576 ([True] * 4, 1), | |
577 self.try_runner.step_db.revision_quality_builder_steps( | |
578 'linux', 123)) | |
579 self.assertEqual( | |
580 ([True] * 4, 1), | |
581 self.try_runner.step_db.revision_quality_builder_steps( | |
582 'mac', 123)) | |
583 self.try_server.check_calls(['builders/linux/builds/?select=0']) | |
584 self.context.status.check_names(['try server'] * 6) | |
585 | |
586 def testIgnorePreviousJobs(self): | |
587 self.try_runner.verify(self.pending) | |
588 self.try_server.check_calls( | |
589 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
590 | |
591 self.try_runner.update_status([self.pending]) | |
592 self.try_server.check_calls( | |
593 [ 'builders/?select=linux&select=mac', | |
594 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
595 | |
596 self.try_server.add_build('linux', 12, None, [None, None, None, None]) | |
597 self.try_server.add_build('mac', 12, None, [None, None, None, None]) | |
598 self.try_runner.update_status([self.pending]) | |
599 self.assertPending( | |
600 base.PROCESSING, 2, None, linux_build=None, | |
601 mac_build=None) | |
602 self.try_server.check_calls( | |
603 ['builders/?select=linux&select=mac', | |
604 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
605 | |
606 self.try_server.add_build( | |
607 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
608 self.try_server.add_build( | |
609 'mac', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
610 self.try_runner.update_status([self.pending]) | |
611 self.assertPending(base.SUCCEEDED, 2, None) | |
612 self.assertEqual( | |
613 123, self.try_runner.step_db.last_good_revision_builder('linux')) | |
614 self.assertEqual( | |
615 123, self.try_runner.step_db.last_good_revision_builder('mac')) | |
616 self.assertEqual( | |
617 ([True] * 4, 1), | |
618 self.try_runner.step_db.revision_quality_builder_steps( | |
619 'linux', 123)) | |
620 self.assertEqual( | |
621 ([True] * 4, 1), | |
622 self.try_runner.step_db.revision_quality_builder_steps( | |
623 'mac', 123)) | |
624 self.try_server.check_calls( | |
625 ['builders/?select=linux&select=mac', | |
626 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
627 self.context.status.check_names(['try server'] * 6) | |
628 | |
629 def testNames(self): | |
630 job = try_server.TryJob( | |
631 builder='builder', revision=123, tests=['test1'], clobber=False) | |
632 self.assertEqual(None, job.name) | |
633 | |
634 def testLostJob(self): | |
635 # Test that a job is automatically retried if it was never started up. It | |
636 # does happen. | |
637 self.try_runner.verify(self.pending) | |
638 self.try_server.check_calls( | |
639 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
640 | |
641 # Keep a copy of the try jobs to compare later. | |
642 self.try_runner.update_status([self.pending]) | |
643 self.try_server.check_calls( | |
644 [ 'builders/?select=linux&select=mac', | |
645 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
646 self.assertPending( | |
647 base.PROCESSING, 2, None, mac_sent=self.timestamp[-1], | |
648 linux_build=None, mac_build=None) | |
649 | |
650 # lost_try_job_delay + 2 seconds later. | |
651 # linux is pending, mac is lost. | |
652 self.try_server.pending_builds['linux'] = [ | |
653 { | |
654 'reason': '42-23', | |
655 } | |
656 ] | |
657 self.timestamp.append(self.try_runner.lost_try_job_delay + 2) | |
658 self.try_runner.update_status([self.pending]) | |
659 self.assertPending( | |
660 base.PROCESSING, 2, None, mac_sent=self.timestamp[-1], | |
661 linux_build=None, mac_build=None, | |
662 mac_name='42-23 (previous was lost)') | |
663 self.try_server.check_calls( | |
664 # Look if there is pending build on each builder. | |
665 [ 'builders/?select=linux&select=mac', | |
666 'builders/linux/builds/_all', 'builders/mac/builds/_all', | |
667 'builders/linux/pendingBuilds', | |
668 # Retry only mac. | |
669 'trychange b={mac:test1,test2} c=False r=sol@123 n=42-23 (previous ' | |
670 'was lost)']) | |
671 | |
672 # linux job was completed. | |
673 self.try_server.pending_builds['linux'] = [] | |
674 self.try_server.add_build( | |
675 'linux', 123, None, [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
676 self.try_runner.update_status([self.pending]) | |
677 self.assertPending( | |
678 base.PROCESSING, 2, None, mac_sent=self.timestamp[1], | |
679 linux_build=0, mac_build=None, | |
680 linux_state=base.SUCCEEDED, | |
681 mac_name='42-23 (previous was lost)') | |
682 self.try_server.check_calls( | |
683 [ 'builders/?select=linux&select=mac', | |
684 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
685 | |
686 # 2 * (lost_try_job_delay + 2) seconds later, mac job started and completed. | |
687 self.timestamp.append(2 * (self.try_runner.lost_try_job_delay + 2)) | |
688 self.try_server.add_build( | |
689 'mac', 123, '42-23 (previous was lost)', | |
690 [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
691 self.try_runner.update_status([self.pending]) | |
692 self.assertPending( | |
693 base.SUCCEEDED, 2, None, mac_sent=self.timestamp[1], | |
694 linux_build=0, mac_build=0, | |
695 mac_name='42-23 (previous was lost)') | |
696 self.try_server.check_calls( | |
697 ['builders/?select=mac', 'builders/mac/builds/_all']) | |
698 | |
699 self.try_runner.update_status([self.pending]) | |
700 self.context.checkout.check_calls( | |
701 [ 'prepare(123)', | |
702 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) | |
703 self.context.status.check_names(['try server'] * 7) | |
704 | |
705 def testFailedStepRetryLkgr(self): | |
706 self.try_runner.verify(self.pending) | |
707 self.try_server.check_calls( | |
708 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
709 self.try_server.add_build( | |
710 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, SUCCESS]) | |
711 | |
712 self.lkgr = 122 | |
713 self.try_runner.update_status([self.pending]) | |
714 self.try_server.check_calls( | |
715 [ 'builders/?select=linux&select=mac', | |
716 'builders/linux/builds/_all', 'builders/mac/builds/_all', | |
717 # Only the failed test is retried, on lkgr revision. | |
718 'trychange b={linux:test1} c=False r=sol@122 n=42-23 (retry)']) | |
719 self.assertEqual(['test1'], self.get_verif().try_jobs[0].failed_steps) | |
720 self.context.checkout.check_calls( | |
721 [ 'prepare(122)', | |
722 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) | |
723 self.context.status.check_names(['try server'] * 5) | |
724 | |
725 def testFailedUpdate(self): | |
726 # It must not retry a failed update. | |
727 # Add succeededing builds, this sets quality to True, which disable retry | |
728 # mechanism. | |
729 self.try_server.add_build( | |
730 'linux', 123, 'georges tried stuff', | |
731 [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
732 self.try_server.add_build( | |
733 'mac', 123, 'georges tried stuff', [SUCCESS, SUCCESS, SUCCESS, SUCCESS]) | |
734 self.lkgr = 123 | |
735 | |
736 self.try_runner.verify(self.pending) | |
737 self.try_server.check_calls( | |
738 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
739 self.try_server.add_build( | |
740 'linux', 123, None, [FAILURE, None, None, None]) | |
741 self.try_runner.update_status([self.pending]) | |
742 self.try_server.check_calls( | |
743 [ 'builders/?select=linux&select=mac', | |
744 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
745 self.assertEqual('linux', self.get_verif().try_jobs[0].builder) | |
746 self.assertEqual(['update'], self.get_verif().try_jobs[0].failed_steps) | |
747 self.assertPending( | |
748 base.FAILED, 2, | |
749 (u'Try job failure for 42-23 on linux for step ' | |
750 u'"update".\n%s/buildstatus?builder=linux&number=1\n\n' | |
751 u'Step "update" is always a major failure.\n' | |
752 u'Look at the try server FAQ for more details.') % | |
753 self.try_server.server_url, | |
754 mac_build=None, | |
755 mac_state=base.PROCESSING) | |
756 self.context.status.check_names(['try server'] * 4) | |
757 | |
758 def testFailedCompileRetryClobber(self): | |
759 # It must retry once a non-clobber compile. | |
760 self.try_runner.verify(self.pending) | |
761 self.try_server.check_calls( | |
762 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
763 self.try_server.add_build( | |
764 'linux', 123, None, [SUCCESS, FAILURE, None, None]) | |
765 self.lkgr = 122 | |
766 self.try_runner.update_status([self.pending]) | |
767 self.context.checkout.check_calls( | |
768 [ 'prepare(122)', | |
769 'apply_patch(%r)' % self.context.rietveld.patchsets[-1]]) | |
770 self.try_server.check_calls( | |
771 [ 'builders/?select=linux&select=mac', | |
772 'builders/linux/builds/_all', 'builders/mac/builds/_all', | |
773 # Retries at lkgr. | |
774 'trychange b={linux:test1,test2} c=True r=sol@122 n=42-23 (retry)']) | |
775 self.assertEqual(['compile'], self.get_verif().try_jobs[0].failed_steps) | |
776 self.assertPending( | |
777 base.PROCESSING, 2, None, linux_rev=122, | |
778 linux_clobber=True, linux_build=None, mac_build=None, | |
779 linux_name='42-23 (retry)') | |
780 | |
781 self.try_server.add_build( | |
782 'linux', 122, '42-23 (retry)', [SUCCESS, FAILURE, None, None]) | |
783 self.try_runner.update_status([self.pending]) | |
784 self.try_server.check_calls( | |
785 [ 'builders/?select=linux&select=mac', | |
786 'builders/linux/builds/_all', 'builders/mac/builds/_all']) | |
787 self.assertEqual(['compile'], self.get_verif().try_jobs[0].failed_steps) | |
788 self.assertPending( | |
789 base.FAILED, 2, | |
790 self.second_fail_msg('42-23 (retry)', 'compile', 'compile', 'linux', 1, | |
791 True), | |
792 linux_rev=122, | |
793 linux_clobber=True, | |
794 mac_build=None, | |
795 mac_state=base.PROCESSING, | |
796 linux_name='42-23 (retry)') | |
797 self.context.status.check_names(['try server'] * 7) | |
798 | |
799 def testTooManyRetries(self): | |
800 self.try_runner.verify(self.pending) | |
801 self.try_server.check_calls( | |
802 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
803 job = self.pending.verifications[self.try_runner.name].try_jobs[0] | |
804 self.try_runner._send_jobs( | |
805 self.pending, [job], False, {job.builder:job.tests}, 'foo') | |
806 self.try_server.check_calls( | |
807 [ 'trychange b={linux:test1,test2} c=False r=sol@123 n=foo']) | |
808 job = self.pending.verifications[self.try_runner.name].try_jobs[0] | |
809 self.try_runner._send_jobs( | |
810 self.pending, [job], False, {job.builder:job.tests}, 'foo') | |
811 self.try_server.check_calls( | |
812 [ 'trychange b={linux:test1,test2} c=False r=sol@123 n=foo']) | |
813 job = self.pending.verifications[self.try_runner.name].try_jobs[0] | |
814 self.try_runner._send_jobs( | |
815 self.pending, [job], False, {job.builder:job.tests}, 'foo') | |
816 self.try_server.check_calls( | |
817 [ 'trychange b={linux:test1,test2} c=False r=sol@123 n=foo']) | |
818 job = self.pending.verifications[self.try_runner.name].try_jobs[0] | |
819 try: | |
820 self.try_runner._send_jobs( | |
821 self.pending, [job], False, {job.builder:job.tests}, 'foo') | |
822 self.fail() | |
823 except base.DiscardPending: | |
824 pass | |
825 self.context.status.check_names(['try server'] * 5) | |
826 | |
827 def testNoTry(self): | |
828 self.pending.description += '\nNOTRY=true' | |
829 self.try_runner.verify(self.pending) | |
830 self.assertEqual( | |
831 base.SUCCEEDED, | |
832 self.pending.verifications[self.try_runner.name].get_state()) | |
833 | |
834 def testNoTryWrong(self): | |
835 self.pending.description += '\nNOTRY=true2' | |
836 self.try_runner.verify(self.pending) | |
837 self.try_server.check_calls( | |
838 ['trychange b={linux:test1,test2, mac:test1,test2} c=False r=sol@123']) | |
839 self.context.status.check_names(['try server'] * 2) | |
840 self.assertEqual( | |
841 base.PROCESSING, | |
842 self.pending.verifications[self.try_runner.name].get_state()) | |
843 | |
844 def testSuccessIgnoredFailure(self): | |
845 # Simplify testing code by removing mac. | |
846 del self.try_runner.builders_and_tests['mac'] | |
847 # Add fake failing ignored step. | |
848 self.try_server.steps = [ | |
849 'update', 'compile', 'ignored_step', 'test1', 'test2'] | |
850 | |
851 self.try_runner.verify(self.pending) | |
852 self.try_server.check_calls( | |
853 ['trychange b={linux:test1,test2} c=False r=sol@123']) | |
854 | |
855 self.try_runner.update_status([self.pending]) | |
856 self.assertPending( | |
857 base.PROCESSING, 1, None, linux_build=None, mac_build=None) | |
858 self.try_server.check_calls( | |
859 ['builders/?select=linux', 'builders/linux/builds/_all']) | |
860 | |
861 self.try_server.add_build( | |
862 'linux', 123, None, [SUCCESS, SUCCESS, FAILURE, SUCCESS, SUCCESS]) | |
863 self.try_runner.update_status([self.pending]) | |
864 self.assertPending(base.SUCCEEDED, 1, None, linux_build=0, mac_build=None) | |
865 self.try_server.check_calls( | |
866 ['builders/?select=linux', 'builders/linux/builds/_all']) | |
867 | |
868 self.try_server.set_build_result('linux', SUCCESS) | |
869 self.try_runner.update_status([self.pending]) | |
870 self.assertPending(base.SUCCEEDED, 1, None, linux_build=0, mac_build=0) | |
871 # TODO(maruel): Fix, since StepDb doesn't know about ignored steps. | |
872 self.assertEqual( | |
873 None, self.try_runner.step_db.last_good_revision_builder('linux')) | |
874 self.assertEqual( | |
875 ([True, True, False, True, True], 1), | |
876 self.try_runner.step_db.revision_quality_builder_steps( | |
877 'linux', 123)) | |
878 self.context.status.check_names(['try server'] * 3) | |
879 | |
880 | |
881 if __name__ == '__main__': | |
882 logging.basicConfig( | |
883 level=[logging.WARNING, logging.INFO, logging.DEBUG][ | |
884 min(sys.argv.count('-v'), 2)], | |
885 format='%(levelname)5s %(module)15s(%(lineno)3d): %(message)s') | |
886 unittest.main() | |
OLD | NEW |