blob: d921ebfb8ca172ae2fc3ee34892b01fc0bb4cce5 [file] [log] [blame]
James E. Blairb0fcae42012-07-17 11:12:10 -07001#!/usr/bin/env python
2
3# Copyright 2012 Hewlett-Packard Development Company, L.P.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17import unittest
18import ConfigParser
19import os
20import Queue
21import logging
22import json
23import threading
24import time
25import pprint
26import re
27
28import zuul
29import zuul.scheduler
30import zuul.launcher.jenkins
31import zuul.trigger.gerrit
32
33FIXTURE_DIR = os.path.join(os.path.dirname(__file__),
34 'fixtures')
35CONFIG = ConfigParser.ConfigParser()
36CONFIG.read(os.path.join(FIXTURE_DIR, "zuul.conf"))
37
38CONFIG.set('zuul', 'layout_config',
39 os.path.join(FIXTURE_DIR, "layout.yaml"))
40
41logging.basicConfig(level=logging.DEBUG)
42
43
44class FakeChange(object):
45 categories = {'APRV': 'Approved',
46 'CRVW': 'Code-Review',
47 'VRFY': 'Verified'}
48
49 def __init__(self, number, project, branch, subject, status='NEW'):
James E. Blaird466dc42012-07-31 10:42:56 -070050 self.reported = 0
James E. Blairb0fcae42012-07-17 11:12:10 -070051 self.patchsets = []
52 self.submit_records = []
53 self.number = number
54 self.project = project
55 self.branch = branch
56 self.subject = subject
57 self.latest_patchset = 0
58 self.data = {
59 'branch': branch,
60 'comments': [],
61 'commitMessage': subject,
62 'createdOn': time.time(),
63 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
64 'lastUpdated': time.time(),
65 'number': str(number),
66 'open': True,
67 'owner': {'email': 'user@example.com',
68 'name': 'User Name',
69 'username': 'username'},
70 'patchSets': self.patchsets,
71 'project': project,
72 'status': status,
73 'subject': subject,
74 'submitRecords': self.submit_records,
75 'url': 'https://hostname/%s' % number}
76
77 self.addPatchset()
78
79 def addPatchset(self, files=None):
80 self.latest_patchset += 1
81 d = {'approvals': [],
82 'createdOn': time.time(),
83 'files': [{'file': '/COMMIT_MSG',
84 'type': 'ADDED'},
85 {'file': 'README',
86 'type': 'MODIFIED'}],
87 'number': self.latest_patchset,
88 'ref': 'refs/changes/1/%s/%s' % (self.number,
89 self.latest_patchset),
90 'revision':
91 'aa69c46accf97d0598111724a38250ae76a22c87',
92 'uploader': {'email': 'user@example.com',
93 'name': 'User name',
94 'username': 'user'}}
95 self.data['currentPatchSet'] = d
96 self.patchsets.append(d)
97
98 def addApproval(self, category, value):
99 event = {'approvals': [{'description': self.categories[category],
100 'type': category,
101 'value': str(value)}],
102 'author': {'email': 'user@example.com',
103 'name': 'User Name',
104 'username': 'username'},
105 'change': {'branch': self.branch,
106 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
107 'number': str(self.number),
108 'owner': {'email': 'user@example.com',
109 'name': 'User Name',
110 'username': 'username'},
111 'project': self.project,
112 'subject': self.subject,
113 'topic': 'master',
114 'url': 'https://hostname/459'},
115 'comment': '',
116 'patchSet': self.patchsets[-1],
117 'type': 'comment-added'}
118 return json.loads(json.dumps(event))
119
120 def query(self):
121 return json.loads(json.dumps(self.data))
122
123 def setMerged(self):
124 self.data['status'] = 'MERGED'
125 self.open = False
126
James E. Blaird466dc42012-07-31 10:42:56 -0700127 def setReported(self):
128 self.reported += 1
129
James E. Blairb0fcae42012-07-17 11:12:10 -0700130
131class FakeGerrit(object):
132 def __init__(self, *args, **kw):
133 self.event_queue = Queue.Queue()
134 self.fixture_dir = os.path.join(FIXTURE_DIR, 'gerrit')
135 self.change_number = 0
136 self.changes = {}
137
138 def addFakeChange(self, project, branch, subject):
139 self.change_number += 1
140 c = FakeChange(self.change_number, project, branch, subject)
141 self.changes[self.change_number] = c
142 return c
143
144 def addEvent(self, data):
145 return self.event_queue.put(data)
146
147 def getEvent(self):
148 return self.event_queue.get()
149
150 def eventDone(self):
151 self.event_queue.task_done()
152
153 def review(self, project, changeid, message, action):
James E. Blaird466dc42012-07-31 10:42:56 -0700154 number, ps = changeid.split(',')
155 change = self.changes[int(number)]
James E. Blairb0fcae42012-07-17 11:12:10 -0700156 if 'submit' in action:
James E. Blairb0fcae42012-07-17 11:12:10 -0700157 change.setMerged()
James E. Blaird466dc42012-07-31 10:42:56 -0700158 if message:
159 change.setReported()
James E. Blairb0fcae42012-07-17 11:12:10 -0700160
161 def query(self, number):
162 change = self.changes[int(number)]
163 return change.query()
164
165 def startWatching(self, *args, **kw):
166 pass
167
168
169class FakeJenkinsEvent(object):
170 def __init__(self, name, number, parameters, phase, status=None):
171 data = {'build':
172 {'full_url': 'https://server/job/%s/%s/' % (name, number),
173 'number': number,
174 'parameters': parameters,
175 'phase': phase,
176 'url': 'job/%s/%s/' % (name, number)},
177 'name': name,
178 'url': 'job/%s/' % name}
179 if status:
180 data['build']['status'] = status
181 self.body = json.dumps(data)
182
183
184class FakeJenkinsJob(threading.Thread):
185 log = logging.getLogger("zuul.test")
186
187 def __init__(self, jenkins, callback, name, number, parameters):
188 threading.Thread.__init__(self)
189 self.jenkins = jenkins
190 self.callback = callback
191 self.name = name
192 self.number = number
193 self.parameters = parameters
194 self.wait_condition = threading.Condition()
195 self.waiting = False
James E. Blaird466dc42012-07-31 10:42:56 -0700196 self.aborted = False
197 self.canceled = False
198 self.created = time.time()
James E. Blairb0fcae42012-07-17 11:12:10 -0700199
200 def release(self):
201 self.wait_condition.acquire()
202 self.wait_condition.notify()
203 self.waiting = False
204 self.log.debug("Job %s released" % (self.parameters['UUID']))
205 self.wait_condition.release()
206
207 def isWaiting(self):
208 self.wait_condition.acquire()
209 if self.waiting:
210 ret = True
211 else:
212 ret = False
213 self.wait_condition.release()
214 return ret
215
216 def _wait(self):
217 self.wait_condition.acquire()
218 self.waiting = True
219 self.log.debug("Job %s waiting" % (self.parameters['UUID']))
220 self.wait_condition.wait()
221 self.wait_condition.release()
222
223 def run(self):
224 self.jenkins.fakeEnqueue(self)
225 if self.jenkins.hold_jobs_in_queue:
226 self._wait()
227 self.jenkins.fakeDequeue(self)
James E. Blaird466dc42012-07-31 10:42:56 -0700228 if self.canceled:
229 self.jenkins.all_jobs.remove(self)
230 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700231 self.callback.jenkins_endpoint(FakeJenkinsEvent(
232 self.name, self.number, self.parameters,
233 'STARTED'))
234 if self.jenkins.hold_jobs_in_build:
235 self._wait()
236 self.log.debug("Job %s continuing" % (self.parameters['UUID']))
James E. Blairb02a3bb2012-07-30 17:49:55 -0700237
238 result = 'SUCCESS'
239 if self.jenkins.fakeShouldFailTest(
240 self.name,
241 self.parameters['GERRIT_CHANGES']):
242 result = 'FAILURE'
James E. Blaird466dc42012-07-31 10:42:56 -0700243 if self.aborted:
244 result = 'ABORTED'
James E. Blairb02a3bb2012-07-30 17:49:55 -0700245
James E. Blairb0fcae42012-07-17 11:12:10 -0700246 self.jenkins.fakeAddHistory(name=self.name, number=self.number,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700247 result=result)
James E. Blairb0fcae42012-07-17 11:12:10 -0700248 self.callback.jenkins_endpoint(FakeJenkinsEvent(
249 self.name, self.number, self.parameters,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700250 'COMPLETED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700251 self.callback.jenkins_endpoint(FakeJenkinsEvent(
252 self.name, self.number, self.parameters,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700253 'FINISHED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700254 self.jenkins.all_jobs.remove(self)
255
256
257class FakeJenkins(object):
258 log = logging.getLogger("zuul.test")
259
260 def __init__(self, *args, **kw):
261 self.queue = []
262 self.all_jobs = []
263 self.job_counter = {}
James E. Blaird466dc42012-07-31 10:42:56 -0700264 self.queue_counter = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700265 self.job_history = []
266 self.hold_jobs_in_queue = False
267 self.hold_jobs_in_build = False
James E. Blairb02a3bb2012-07-30 17:49:55 -0700268 self.fail_tests = {}
James E. Blairb0fcae42012-07-17 11:12:10 -0700269
270 def fakeEnqueue(self, job):
271 self.queue.append(job)
272
273 def fakeDequeue(self, job):
274 self.queue.remove(job)
275
276 def fakeAddHistory(self, **kw):
277 self.job_history.append(kw)
278
279 def fakeRelease(self, regex=None):
280 all_jobs = self.all_jobs[:]
281 self.log.debug("releasing jobs %s (%s)" % (regex, len(self.all_jobs)))
282 for job in all_jobs:
283 if not regex or re.match(regex, job.name):
284 self.log.debug("releasing job %s" % (job.parameters['UUID']))
285 job.release()
286 else:
287 self.log.debug("not releasing job %s" % (
288 job.parameters['UUID']))
289 self.log.debug("done releasing jobs %s (%s)" % (regex,
290 len(self.all_jobs)))
291
292 def fakeAllWaiting(self, regex=None):
293 all_jobs = self.all_jobs[:]
294 for job in all_jobs:
295 self.log.debug("job %s %s" % (job.parameters['UUID'],
296 job.isWaiting()))
297 if not job.isWaiting():
298 return False
299 return True
300
James E. Blairb02a3bb2012-07-30 17:49:55 -0700301 def fakeAddFailTest(self, name, change):
302 l = self.fail_tests.get(name, [])
303 l.append(change)
304 self.fail_tests[name] = l
305
306 def fakeShouldFailTest(self, name, changes):
307 l = self.fail_tests.get(name, [])
308 for change in l:
309 if change in changes:
310 return True
311 return False
312
James E. Blairb0fcae42012-07-17 11:12:10 -0700313 def build_job(self, name, parameters):
314 count = self.job_counter.get(name, 0)
315 count += 1
316 self.job_counter[name] = count
James E. Blaird466dc42012-07-31 10:42:56 -0700317
318 queue_count = self.queue_counter
319 self.queue_counter += 1
James E. Blairb0fcae42012-07-17 11:12:10 -0700320 job = FakeJenkinsJob(self, self.callback, name, count, parameters)
James E. Blaird466dc42012-07-31 10:42:56 -0700321 job.queue_id = queue_count
322
James E. Blairb0fcae42012-07-17 11:12:10 -0700323 self.all_jobs.append(job)
324 job.start()
325
James E. Blaird466dc42012-07-31 10:42:56 -0700326 def stop_build(self, name, number):
327 for job in self.all_jobs:
328 if job.name == name and job.number == number:
329 job.aborted = True
330 job.release()
331 return
332
333 def cancel_queue(self, id):
334 for job in self.queue:
335 if job.queue_id == id:
336 job.canceled = True
337 job.release()
338 return
339
340 def get_queue_info(self):
341 items = []
342 for job in self.queue:
343 paramstr = ''
344 paramlst = []
345 d = {'actions': [{'parameters': paramlst},
346 {'causes': [{'shortDescription':
347 'Started by user Jenkins',
348 'userId': 'jenkins',
349 'userName': 'Jenkins'}]}],
350 'blocked': False,
351 'buildable': True,
352 'buildableStartMilliseconds': (job.created * 1000) + 5,
353 'id': job.queue_id,
354 'inQueueSince': (job.created * 1000),
355 'params': paramstr,
356 'stuck': False,
357 'task': {'color': 'blue',
358 'name': job.name,
359 'url': 'https://server/job/%s/' % job.name},
360 'why': 'Waiting for next available executor'}
361 for k, v in job.parameters.items():
362 paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
363 pd = {'name': k, 'value': v}
364 paramlst.append(pd)
365 items.append(d)
366 return items
367
James E. Blairb0fcae42012-07-17 11:12:10 -0700368 def set_build_description(self, *args, **kw):
369 pass
370
371
372class FakeJenkinsCallback(zuul.launcher.jenkins.JenkinsCallback):
373 def start(self):
374 pass
375
376
377class testScheduler(unittest.TestCase):
378 log = logging.getLogger("zuul.test")
379
380 def setUp(self):
381 self.config = CONFIG
382 self.sched = zuul.scheduler.Scheduler()
383
384 def jenkinsFactory(*args, **kw):
385 self.fake_jenkins = FakeJenkins()
386 return self.fake_jenkins
387
388 def jenkinsCallbackFactory(*args, **kw):
389 self.fake_jenkins_callback = FakeJenkinsCallback(*args, **kw)
390 return self.fake_jenkins_callback
391
392 zuul.launcher.jenkins.ExtendedJenkins = jenkinsFactory
393 zuul.launcher.jenkins.JenkinsCallback = jenkinsCallbackFactory
394 self.jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
395 self.fake_jenkins.callback = self.fake_jenkins_callback
396
397 zuul.lib.gerrit.Gerrit = FakeGerrit
398
399 self.gerrit = zuul.trigger.gerrit.Gerrit(self.config, self.sched)
400 self.fake_gerrit = self.gerrit.gerrit
401
402 self.sched.setLauncher(self.jenkins)
403 self.sched.setTrigger(self.gerrit)
404
405 self.sched.start()
406 self.sched.reconfigure(self.config)
407 self.sched.resume()
408
409 def tearDown(self):
410 self.jenkins.stop()
411 self.gerrit.stop()
412 self.sched.stop()
413 self.sched.join()
414
415 def waitUntilSettled(self):
416 self.log.debug("Waiting until settled...")
417 start = time.time()
418 while True:
419 if time.time() - start > 10:
420 print 'queue status:',
421 print self.sched.trigger_event_queue.empty(),
422 print self.sched.result_event_queue.empty(),
423 print self.fake_gerrit.event_queue.empty(),
424 raise Exception("Timeout waiting for Zuul to settle")
425 self.fake_gerrit.event_queue.join()
426 self.sched.queue_lock.acquire()
427 if (self.sched.trigger_event_queue.empty() and
428 self.sched.result_event_queue.empty() and
429 self.fake_gerrit.event_queue.empty() and
430 self.fake_jenkins.fakeAllWaiting()):
431 self.sched.queue_lock.release()
432 self.log.debug("...settled.")
433 return
434 self.sched.queue_lock.release()
435 self.sched.wake_event.wait(0.1)
436
James E. Blaird466dc42012-07-31 10:42:56 -0700437 def countJobResults(self, jobs, result):
438 jobs = filter(lambda x: x['result'] == result, jobs)
439 return len(jobs)
440
James E. Blairb0fcae42012-07-17 11:12:10 -0700441 def test_jobs_launched(self):
442 "Test that jobs are launched and a change is merged"
443 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
444 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
445 self.waitUntilSettled()
446 jobs = self.fake_jenkins.job_history
447 job_names = [x['name'] for x in jobs]
448 assert 'project-merge' in job_names
449 assert 'project-test1' in job_names
450 assert 'project-test2' in job_names
451 assert jobs[0]['result'] == 'SUCCESS'
452 assert jobs[1]['result'] == 'SUCCESS'
453 assert jobs[2]['result'] == 'SUCCESS'
454 assert A.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700455 assert A.reported == 2
James E. Blairb0fcae42012-07-17 11:12:10 -0700456
457 def test_parallel_changes(self):
458 "Test that changes are tested in parallel and merged in series"
459 self.fake_jenkins.hold_jobs_in_build = True
460 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
461 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
462 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
463
464 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
465 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
466 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
467
468 self.waitUntilSettled()
469 jobs = self.fake_jenkins.all_jobs
470 assert len(jobs) == 1
471 assert jobs[0].name == 'project-merge'
472 assert (jobs[0].parameters['GERRIT_CHANGES'] ==
473 'org/project:master:refs/changes/1/1/1')
474
475 self.fake_jenkins.fakeRelease('.*-merge')
476 self.waitUntilSettled()
477 assert len(jobs) == 3
478 assert jobs[0].name == 'project-test1'
479 assert (jobs[0].parameters['GERRIT_CHANGES'] ==
480 'org/project:master:refs/changes/1/1/1')
481 assert jobs[1].name == 'project-test2'
482 assert (jobs[1].parameters['GERRIT_CHANGES'] ==
483 'org/project:master:refs/changes/1/1/1')
484 assert jobs[2].name == 'project-merge'
485 assert (jobs[2].parameters['GERRIT_CHANGES'] ==
486 'org/project:master:refs/changes/1/1/1^'
487 'org/project:master:refs/changes/1/2/1')
488
489 self.fake_jenkins.fakeRelease('.*-merge')
490 self.waitUntilSettled()
491 assert len(jobs) == 5
492 assert jobs[0].name == 'project-test1'
493 assert (jobs[0].parameters['GERRIT_CHANGES'] ==
494 'org/project:master:refs/changes/1/1/1')
495 assert jobs[1].name == 'project-test2'
496 assert (jobs[1].parameters['GERRIT_CHANGES'] ==
497 'org/project:master:refs/changes/1/1/1')
498
499 assert jobs[2].name == 'project-test1'
500 assert (jobs[2].parameters['GERRIT_CHANGES'] ==
501 'org/project:master:refs/changes/1/1/1^'
502 'org/project:master:refs/changes/1/2/1')
503 assert jobs[3].name == 'project-test2'
504 assert (jobs[3].parameters['GERRIT_CHANGES'] ==
505 'org/project:master:refs/changes/1/1/1^'
506 'org/project:master:refs/changes/1/2/1')
507
508 assert jobs[4].name == 'project-merge'
509 assert (jobs[4].parameters['GERRIT_CHANGES'] ==
510 'org/project:master:refs/changes/1/1/1^'
511 'org/project:master:refs/changes/1/2/1^'
512 'org/project:master:refs/changes/1/3/1')
513
514 self.fake_jenkins.fakeRelease('.*-merge')
515 self.waitUntilSettled()
516 assert len(jobs) == 6
517 assert jobs[0].name == 'project-test1'
518 assert (jobs[0].parameters['GERRIT_CHANGES'] ==
519 'org/project:master:refs/changes/1/1/1')
520 assert jobs[1].name == 'project-test2'
521 assert (jobs[1].parameters['GERRIT_CHANGES'] ==
522 'org/project:master:refs/changes/1/1/1')
523
524 assert jobs[2].name == 'project-test1'
525 assert (jobs[2].parameters['GERRIT_CHANGES'] ==
526 'org/project:master:refs/changes/1/1/1^'
527 'org/project:master:refs/changes/1/2/1')
528 assert jobs[3].name == 'project-test2'
529 assert (jobs[3].parameters['GERRIT_CHANGES'] ==
530 'org/project:master:refs/changes/1/1/1^'
531 'org/project:master:refs/changes/1/2/1')
532
533 assert jobs[4].name == 'project-test1'
534 assert (jobs[4].parameters['GERRIT_CHANGES'] ==
535 'org/project:master:refs/changes/1/1/1^'
536 'org/project:master:refs/changes/1/2/1^'
537 'org/project:master:refs/changes/1/3/1')
538 assert jobs[5].name == 'project-test2'
539 assert (jobs[5].parameters['GERRIT_CHANGES'] ==
540 'org/project:master:refs/changes/1/1/1^'
541 'org/project:master:refs/changes/1/2/1^'
542 'org/project:master:refs/changes/1/3/1')
543
544 self.fake_jenkins.hold_jobs_in_build = False
545 self.fake_jenkins.fakeRelease()
546 self.waitUntilSettled()
547 assert len(jobs) == 0
548
549 jobs = self.fake_jenkins.job_history
550 assert len(jobs) == 9
551 assert A.data['status'] == 'MERGED'
552 assert B.data['status'] == 'MERGED'
553 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700554 assert A.reported == 2
555 assert B.reported == 2
556 assert C.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700557
558 def test_failed_changes(self):
559 "Test that a change behind a failed change is retested"
560 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
561 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
562
563 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
564 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
565
566 self.fake_jenkins.fakeAddFailTest(
567 'project-test1',
568 'org/project:master:refs/changes/1/1/1')
569
570 self.waitUntilSettled()
571 jobs = self.fake_jenkins.job_history
572 assert len(jobs) > 6
573 assert A.data['status'] == 'NEW'
574 assert B.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700575 assert A.reported == 2
576 assert B.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700577
578 def test_independent_queues(self):
579 "Test that changes end up in the right queues"
580 self.fake_jenkins.hold_jobs_in_build = True
581 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
582 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
583 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
584
585 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
586 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
587 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
588
589 jobs = self.fake_jenkins.all_jobs
590 self.waitUntilSettled()
591
592 # There should be one merge job at the head of each queue running
593 assert len(jobs) == 2
594 assert jobs[0].name == 'project-merge'
595 assert (jobs[0].parameters['GERRIT_CHANGES'] ==
596 'org/project:master:refs/changes/1/1/1')
597 assert jobs[1].name == 'project1-merge'
598 assert (jobs[1].parameters['GERRIT_CHANGES'] ==
599 'org/project1:master:refs/changes/1/2/1')
600
601 # Release the current merge jobs
602 self.fake_jenkins.fakeRelease('.*-merge')
603 self.waitUntilSettled()
604 # Release the merge job for project2 which is behind project1
605 self.fake_jenkins.fakeRelease('.*-merge')
606 self.waitUntilSettled()
607
608 # All the test jobs should be running:
609 # project1 (3) + project2 (3) + project (2) = 8
610 assert len(jobs) == 8
611
612 self.fake_jenkins.fakeRelease()
613 self.waitUntilSettled()
614 assert len(jobs) == 0
615
616 jobs = self.fake_jenkins.job_history
617 assert len(jobs) == 11
618 assert A.data['status'] == 'MERGED'
619 assert B.data['status'] == 'MERGED'
620 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700621 assert A.reported == 2
622 assert B.reported == 2
623 assert C.reported == 2
624
625 def test_failed_change_at_head(self):
626 "Test that if a change at the head fails, jobs behind it are canceled"
627 self.fake_jenkins.hold_jobs_in_build = True
628
629 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
630 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
631 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
632
633 self.fake_jenkins.fakeAddFailTest(
634 'project-test1',
635 'org/project:master:refs/changes/1/1/1')
636
637 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
638 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
639 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
640
641 self.waitUntilSettled()
642 jobs = self.fake_jenkins.all_jobs
643 finished_jobs = self.fake_jenkins.job_history
644
645 assert len(jobs) == 1
646 assert jobs[0].name == 'project-merge'
647 assert (jobs[0].parameters['GERRIT_CHANGES'] ==
648 'org/project:master:refs/changes/1/1/1')
649
650 self.fake_jenkins.fakeRelease('.*-merge')
651 self.waitUntilSettled()
652 self.fake_jenkins.fakeRelease('.*-merge')
653 self.waitUntilSettled()
654 self.fake_jenkins.fakeRelease('.*-merge')
655 self.waitUntilSettled()
656
657 assert len(jobs) == 6
658 assert jobs[0].name == 'project-test1'
659 assert jobs[1].name == 'project-test2'
660 assert jobs[2].name == 'project-test1'
661 assert jobs[3].name == 'project-test2'
662 assert jobs[4].name == 'project-test1'
663 assert jobs[5].name == 'project-test2'
664
665 jobs[0].release()
666 self.waitUntilSettled()
667
668 assert len(jobs) == 1 # project-test2
669 assert self.countJobResults(finished_jobs, 'ABORTED') == 4
670
671 self.fake_jenkins.hold_jobs_in_build = False
672 self.fake_jenkins.fakeRelease()
673 self.waitUntilSettled()
674
675 assert len(jobs) == 0
676 assert len(finished_jobs) == 15
677 assert A.data['status'] == 'NEW'
678 assert B.data['status'] == 'MERGED'
679 assert C.data['status'] == 'MERGED'
680 assert A.reported == 2
681 assert B.reported == 2
682 assert C.reported == 2
683
684 def test_failed_change_at_head_with_queue(self):
685 "Test that if a change at the head fails, queued jobs are canceled"
686 self.fake_jenkins.hold_jobs_in_queue = True
687
688 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
689 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
690 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
691
692 self.fake_jenkins.fakeAddFailTest(
693 'project-test1',
694 'org/project:master:refs/changes/1/1/1')
695
696 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
697 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
698 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
699
700 self.waitUntilSettled()
701 jobs = self.fake_jenkins.all_jobs
702 finished_jobs = self.fake_jenkins.job_history
703 queue = self.fake_jenkins.queue
704
705 assert len(jobs) == 1
706 assert len(queue) == 1
707 assert jobs[0].name == 'project-merge'
708 assert (jobs[0].parameters['GERRIT_CHANGES'] ==
709 'org/project:master:refs/changes/1/1/1')
710
711 self.fake_jenkins.fakeRelease('.*-merge')
712 self.waitUntilSettled()
713 self.fake_jenkins.fakeRelease('.*-merge')
714 self.waitUntilSettled()
715 self.fake_jenkins.fakeRelease('.*-merge')
716 self.waitUntilSettled()
717
718 assert len(jobs) == 6
719 assert len(queue) == 6
720 assert jobs[0].name == 'project-test1'
721 assert jobs[1].name == 'project-test2'
722 assert jobs[2].name == 'project-test1'
723 assert jobs[3].name == 'project-test2'
724 assert jobs[4].name == 'project-test1'
725 assert jobs[5].name == 'project-test2'
726
727 jobs[0].release()
728 self.waitUntilSettled()
729
730 assert len(jobs) == 1 # project-test2
731 assert len(queue) == 1
732 assert self.countJobResults(finished_jobs, 'ABORTED') == 0
733
734 self.fake_jenkins.hold_jobs_in_queue = False
735 self.fake_jenkins.fakeRelease()
736 self.waitUntilSettled()
737
738 assert len(jobs) == 0
739 assert len(finished_jobs) == 11
740 assert A.data['status'] == 'NEW'
741 assert B.data['status'] == 'MERGED'
742 assert C.data['status'] == 'MERGED'
743 assert A.reported == 2
744 assert B.reported == 2
745 assert C.reported == 2