blob: dbed89b77e2c1ff168832bd738a3a0b073ecf782 [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
James E. Blair8cc15a82012-08-01 11:17:57 -070021import hashlib
James E. Blairb0fcae42012-07-17 11:12:10 -070022import logging
James E. Blair8cc15a82012-08-01 11:17:57 -070023import random
James E. Blairb0fcae42012-07-17 11:12:10 -070024import json
25import threading
26import time
27import pprint
28import re
James E. Blair8cc15a82012-08-01 11:17:57 -070029import urllib2
30import urlparse
James E. Blair4886cc12012-07-18 15:39:41 -070031import shutil
32import git
James E. Blairb0fcae42012-07-17 11:12:10 -070033
34import zuul
35import zuul.scheduler
36import zuul.launcher.jenkins
37import zuul.trigger.gerrit
38
39FIXTURE_DIR = os.path.join(os.path.dirname(__file__),
40 'fixtures')
41CONFIG = ConfigParser.ConfigParser()
42CONFIG.read(os.path.join(FIXTURE_DIR, "zuul.conf"))
43
44CONFIG.set('zuul', 'layout_config',
45 os.path.join(FIXTURE_DIR, "layout.yaml"))
46
47logging.basicConfig(level=logging.DEBUG)
48
49
James E. Blair8cc15a82012-08-01 11:17:57 -070050def random_sha1():
51 return hashlib.sha1(str(random.random())).hexdigest()
52
53
James E. Blair4886cc12012-07-18 15:39:41 -070054class ChangeReference(git.Reference):
55 _common_path_default = "refs/changes"
56 _points_to_commits_only = True
57
58
59def init_repo(project):
60 parts = project.split('/')
61 path = os.path.join("/tmp/zuul-test/upstream", *parts[:-1])
62 if not os.path.exists(path):
63 os.makedirs(path)
64 path = os.path.join("/tmp/zuul-test/upstream", project)
65 repo = git.Repo.init(path)
66
67 fn = os.path.join(path, 'README')
68 f = open(fn, 'w')
69 f.write("test\n")
70 f.close()
71 repo.index.add([fn])
72 repo.index.commit('initial commit')
James E. Blairc6294a52012-08-17 10:19:48 -070073 master = repo.create_head('master')
James E. Blair4886cc12012-07-18 15:39:41 -070074 repo.create_tag('init')
75
James E. Blairc6294a52012-08-17 10:19:48 -070076 mp = repo.create_head('mp')
77 repo.head.reference = mp
78 f = open(fn, 'a')
79 f.write("test mp\n")
80 f.close()
81 repo.index.add([fn])
82 repo.index.commit('mp commit')
83
84 repo.head.reference = master
85 repo.head.reset(index=True, working_tree=True)
86 repo.git.clean('-x', '-f', '-d')
87
James E. Blair4886cc12012-07-18 15:39:41 -070088
James E. Blair973721f2012-08-15 10:19:43 -070089def add_fake_change_to_repo(project, branch, change_num, patchset, msg, fn):
James E. Blair4886cc12012-07-18 15:39:41 -070090 path = os.path.join("/tmp/zuul-test/upstream", project)
91 repo = git.Repo(path)
92 ref = ChangeReference.create(repo, '1/%s/%s' % (change_num,
93 patchset),
94 'refs/tags/init')
95 repo.head.reference = ref
96 repo.head.reset(index=True, working_tree=True)
97 repo.git.clean('-x', '-f', '-d')
98
99 path = os.path.join("/tmp/zuul-test/upstream", project)
James E. Blair973721f2012-08-15 10:19:43 -0700100 fn = os.path.join(path, fn)
James E. Blair4886cc12012-07-18 15:39:41 -0700101 f = open(fn, 'w')
James E. Blair973721f2012-08-15 10:19:43 -0700102 f.write("test %s %s %s\n" % (branch, change_num, patchset))
James E. Blair4886cc12012-07-18 15:39:41 -0700103 f.close()
104 repo.index.add([fn])
James E. Blairdaabed22012-08-15 15:38:57 -0700105 return repo.index.commit(msg)
James E. Blair4886cc12012-07-18 15:39:41 -0700106
107
108def ref_has_change(ref, change):
109 path = os.path.join("/tmp/zuul-test/git", change.project)
110 repo = git.Repo(path)
111 for commit in repo.iter_commits(ref):
112 if commit.message.strip() == ('%s-1' % change.subject):
113 return True
114 return False
115
116
117def job_has_changes(*args):
118 job = args[0]
119 commits = args[1:]
120 project = job.parameters['ZUUL_PROJECT']
121 path = os.path.join("/tmp/zuul-test/git", project)
122 repo = git.Repo(path)
123 ref = job.parameters['ZUUL_REF']
124 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
125 commit_messages = ['%s-1' % commit.subject for commit in commits]
James E. Blair4886cc12012-07-18 15:39:41 -0700126 for msg in commit_messages:
127 if msg not in repo_messages:
128 return False
129 return True
130
131
James E. Blairb0fcae42012-07-17 11:12:10 -0700132class FakeChange(object):
James E. Blair8c803f82012-07-31 16:25:42 -0700133 categories = {'APRV': ('Approved', -1, 1),
134 'CRVW': ('Code-Review', -2, 2),
135 'VRFY': ('Verified', -2, 2)}
James E. Blairb0fcae42012-07-17 11:12:10 -0700136
James E. Blair8cc15a82012-08-01 11:17:57 -0700137 def __init__(self, gerrit, number, project, branch, subject, status='NEW'):
138 self.gerrit = gerrit
James E. Blaird466dc42012-07-31 10:42:56 -0700139 self.reported = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700140 self.queried = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700141 self.patchsets = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700142 self.number = number
143 self.project = project
144 self.branch = branch
145 self.subject = subject
146 self.latest_patchset = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700147 self.depends_on_change = None
148 self.needed_by_changes = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700149 self.data = {
150 'branch': branch,
151 'comments': [],
152 'commitMessage': subject,
153 'createdOn': time.time(),
James E. Blair8cc15a82012-08-01 11:17:57 -0700154 'id': 'I' + random_sha1(),
James E. Blairb0fcae42012-07-17 11:12:10 -0700155 'lastUpdated': time.time(),
156 'number': str(number),
157 'open': True,
158 'owner': {'email': 'user@example.com',
159 'name': 'User Name',
160 'username': 'username'},
161 'patchSets': self.patchsets,
162 'project': project,
163 'status': status,
164 'subject': subject,
James E. Blair8c803f82012-07-31 16:25:42 -0700165 'submitRecords': [],
James E. Blairb0fcae42012-07-17 11:12:10 -0700166 'url': 'https://hostname/%s' % number}
167
168 self.addPatchset()
James E. Blair8c803f82012-07-31 16:25:42 -0700169 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700170
171 def addPatchset(self, files=None):
172 self.latest_patchset += 1
James E. Blairdaabed22012-08-15 15:38:57 -0700173 if files:
174 fn = files[0]
175 else:
176 fn = '%s-%s' % (self.branch, self.number)
177 msg = self.subject + '-' + str(self.latest_patchset)
178 c = add_fake_change_to_repo(self.project, self.branch,
179 self.number, self.latest_patchset,
180 msg, fn)
James E. Blairb0fcae42012-07-17 11:12:10 -0700181 d = {'approvals': [],
182 'createdOn': time.time(),
183 'files': [{'file': '/COMMIT_MSG',
184 'type': 'ADDED'},
185 {'file': 'README',
186 'type': 'MODIFIED'}],
James E. Blair8c803f82012-07-31 16:25:42 -0700187 'number': str(self.latest_patchset),
James E. Blairb0fcae42012-07-17 11:12:10 -0700188 'ref': 'refs/changes/1/%s/%s' % (self.number,
189 self.latest_patchset),
James E. Blairdaabed22012-08-15 15:38:57 -0700190 'revision': c.hexsha,
James E. Blairb0fcae42012-07-17 11:12:10 -0700191 'uploader': {'email': 'user@example.com',
192 'name': 'User name',
193 'username': 'user'}}
194 self.data['currentPatchSet'] = d
195 self.patchsets.append(d)
James E. Blair8c803f82012-07-31 16:25:42 -0700196 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700197
198 def addApproval(self, category, value):
James E. Blair8c803f82012-07-31 16:25:42 -0700199 approval = {'description': self.categories[category][0],
200 'type': category,
201 'value': str(value)}
202 self.patchsets[-1]['approvals'].append(approval)
203 event = {'approvals': [approval],
James E. Blairb0fcae42012-07-17 11:12:10 -0700204 'author': {'email': 'user@example.com',
205 'name': 'User Name',
206 'username': 'username'},
207 'change': {'branch': self.branch,
208 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
209 'number': str(self.number),
210 'owner': {'email': 'user@example.com',
211 'name': 'User Name',
212 'username': 'username'},
213 'project': self.project,
214 'subject': self.subject,
215 'topic': 'master',
216 'url': 'https://hostname/459'},
217 'comment': '',
218 'patchSet': self.patchsets[-1],
219 'type': 'comment-added'}
James E. Blair8c803f82012-07-31 16:25:42 -0700220 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700221 return json.loads(json.dumps(event))
222
James E. Blair8c803f82012-07-31 16:25:42 -0700223 def getSubmitRecords(self):
224 status = {}
225 for cat in self.categories.keys():
226 status[cat] = 0
227
228 for a in self.patchsets[-1]['approvals']:
229 cur = status[a['type']]
230 cat_min, cat_max = self.categories[a['type']][1:]
231 new = int(a['value'])
232 if new == cat_min:
233 cur = new
234 elif abs(new) > abs(cur):
235 cur = new
236 status[a['type']] = cur
237
238 labels = []
239 ok = True
240 for typ, cat in self.categories.items():
241 cur = status[typ]
242 cat_min, cat_max = cat[1:]
243 if cur == cat_min:
244 value = 'REJECT'
245 ok = False
246 elif cur == cat_max:
247 value = 'OK'
248 else:
249 value = 'NEED'
250 ok = False
251 labels.append({'label': cat[0], 'status': value})
252 if ok:
253 return [{'status': 'OK'}]
254 return [{'status': 'NOT_READY',
255 'labels': labels}]
256
257 def setDependsOn(self, other, patchset):
258 self.depends_on_change = other
259 d = {'id': other.data['id'],
260 'number': other.data['number'],
261 'ref': other.patchsets[patchset - 1]['ref']
262 }
263 self.data['dependsOn'] = [d]
264
265 other.needed_by_changes.append(self)
266 needed = other.data.get('neededBy', [])
267 d = {'id': self.data['id'],
268 'number': self.data['number'],
269 'ref': self.patchsets[patchset - 1]['ref'],
270 'revision': self.patchsets[patchset - 1]['revision']
271 }
272 needed.append(d)
273 other.data['neededBy'] = needed
274
James E. Blairb0fcae42012-07-17 11:12:10 -0700275 def query(self):
James E. Blair8c803f82012-07-31 16:25:42 -0700276 self.queried += 1
277 d = self.data.get('dependsOn')
278 if d:
279 d = d[0]
280 if (self.depends_on_change.patchsets[-1]['ref'] == d['ref']):
281 d['isCurrentPatchSet'] = True
282 else:
283 d['isCurrentPatchSet'] = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700284 return json.loads(json.dumps(self.data))
285
286 def setMerged(self):
James E. Blaircaec0c52012-08-22 14:52:22 -0700287 if (self.depends_on_change
288 and self.depends_on_change.data['status'] != 'MERGED'):
289 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700290 self.data['status'] = 'MERGED'
291 self.open = False
James E. Blairdaabed22012-08-15 15:38:57 -0700292
293 path = os.path.join("/tmp/zuul-test/upstream", self.project)
294 repo = git.Repo(path)
295 repo.heads[self.branch].commit = \
296 repo.commit(self.patchsets[-1]['revision'])
James E. Blairb0fcae42012-07-17 11:12:10 -0700297
James E. Blaird466dc42012-07-31 10:42:56 -0700298 def setReported(self):
299 self.reported += 1
300
James E. Blairb0fcae42012-07-17 11:12:10 -0700301
302class FakeGerrit(object):
303 def __init__(self, *args, **kw):
304 self.event_queue = Queue.Queue()
305 self.fixture_dir = os.path.join(FIXTURE_DIR, 'gerrit')
306 self.change_number = 0
307 self.changes = {}
308
309 def addFakeChange(self, project, branch, subject):
310 self.change_number += 1
James E. Blair8cc15a82012-08-01 11:17:57 -0700311 c = FakeChange(self, self.change_number, project, branch, subject)
James E. Blairb0fcae42012-07-17 11:12:10 -0700312 self.changes[self.change_number] = c
313 return c
314
315 def addEvent(self, data):
316 return self.event_queue.put(data)
317
318 def getEvent(self):
319 return self.event_queue.get()
320
321 def eventDone(self):
322 self.event_queue.task_done()
323
324 def review(self, project, changeid, message, action):
James E. Blaird466dc42012-07-31 10:42:56 -0700325 number, ps = changeid.split(',')
326 change = self.changes[int(number)]
James E. Blairb0fcae42012-07-17 11:12:10 -0700327 if 'submit' in action:
James E. Blairb0fcae42012-07-17 11:12:10 -0700328 change.setMerged()
James E. Blaird466dc42012-07-31 10:42:56 -0700329 if message:
330 change.setReported()
James E. Blairb0fcae42012-07-17 11:12:10 -0700331
332 def query(self, number):
333 change = self.changes[int(number)]
334 return change.query()
335
336 def startWatching(self, *args, **kw):
337 pass
338
339
340class FakeJenkinsEvent(object):
341 def __init__(self, name, number, parameters, phase, status=None):
342 data = {'build':
343 {'full_url': 'https://server/job/%s/%s/' % (name, number),
344 'number': number,
345 'parameters': parameters,
346 'phase': phase,
347 'url': 'job/%s/%s/' % (name, number)},
348 'name': name,
349 'url': 'job/%s/' % name}
350 if status:
351 data['build']['status'] = status
352 self.body = json.dumps(data)
353
354
355class FakeJenkinsJob(threading.Thread):
356 log = logging.getLogger("zuul.test")
357
358 def __init__(self, jenkins, callback, name, number, parameters):
359 threading.Thread.__init__(self)
360 self.jenkins = jenkins
361 self.callback = callback
362 self.name = name
363 self.number = number
364 self.parameters = parameters
365 self.wait_condition = threading.Condition()
366 self.waiting = False
James E. Blaird466dc42012-07-31 10:42:56 -0700367 self.aborted = False
368 self.canceled = False
369 self.created = time.time()
James E. Blairb0fcae42012-07-17 11:12:10 -0700370
371 def release(self):
372 self.wait_condition.acquire()
373 self.wait_condition.notify()
374 self.waiting = False
375 self.log.debug("Job %s released" % (self.parameters['UUID']))
376 self.wait_condition.release()
377
378 def isWaiting(self):
379 self.wait_condition.acquire()
380 if self.waiting:
381 ret = True
382 else:
383 ret = False
384 self.wait_condition.release()
385 return ret
386
387 def _wait(self):
388 self.wait_condition.acquire()
389 self.waiting = True
390 self.log.debug("Job %s waiting" % (self.parameters['UUID']))
391 self.wait_condition.wait()
392 self.wait_condition.release()
393
394 def run(self):
395 self.jenkins.fakeEnqueue(self)
396 if self.jenkins.hold_jobs_in_queue:
397 self._wait()
398 self.jenkins.fakeDequeue(self)
James E. Blaird466dc42012-07-31 10:42:56 -0700399 if self.canceled:
400 self.jenkins.all_jobs.remove(self)
401 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700402 self.callback.jenkins_endpoint(FakeJenkinsEvent(
403 self.name, self.number, self.parameters,
404 'STARTED'))
405 if self.jenkins.hold_jobs_in_build:
406 self._wait()
407 self.log.debug("Job %s continuing" % (self.parameters['UUID']))
James E. Blairb02a3bb2012-07-30 17:49:55 -0700408
409 result = 'SUCCESS'
James E. Blairdaabed22012-08-15 15:38:57 -0700410 if ('ZUUL_REF' in self.parameters) and self.jenkins.fakeShouldFailTest(
James E. Blairb02a3bb2012-07-30 17:49:55 -0700411 self.name,
James E. Blair4886cc12012-07-18 15:39:41 -0700412 self.parameters['ZUUL_REF']):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700413 result = 'FAILURE'
James E. Blaird466dc42012-07-31 10:42:56 -0700414 if self.aborted:
415 result = 'ABORTED'
James E. Blairb02a3bb2012-07-30 17:49:55 -0700416
James E. Blairb0fcae42012-07-17 11:12:10 -0700417 self.jenkins.fakeAddHistory(name=self.name, number=self.number,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700418 result=result)
James E. Blairb0fcae42012-07-17 11:12:10 -0700419 self.callback.jenkins_endpoint(FakeJenkinsEvent(
420 self.name, self.number, self.parameters,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700421 'COMPLETED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700422 self.callback.jenkins_endpoint(FakeJenkinsEvent(
423 self.name, self.number, self.parameters,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700424 'FINISHED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700425 self.jenkins.all_jobs.remove(self)
426
427
428class FakeJenkins(object):
429 log = logging.getLogger("zuul.test")
430
431 def __init__(self, *args, **kw):
432 self.queue = []
433 self.all_jobs = []
434 self.job_counter = {}
James E. Blaird466dc42012-07-31 10:42:56 -0700435 self.queue_counter = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700436 self.job_history = []
437 self.hold_jobs_in_queue = False
438 self.hold_jobs_in_build = False
James E. Blairb02a3bb2012-07-30 17:49:55 -0700439 self.fail_tests = {}
James E. Blairb0fcae42012-07-17 11:12:10 -0700440
441 def fakeEnqueue(self, job):
442 self.queue.append(job)
443
444 def fakeDequeue(self, job):
445 self.queue.remove(job)
446
447 def fakeAddHistory(self, **kw):
448 self.job_history.append(kw)
449
450 def fakeRelease(self, regex=None):
451 all_jobs = self.all_jobs[:]
452 self.log.debug("releasing jobs %s (%s)" % (regex, len(self.all_jobs)))
453 for job in all_jobs:
454 if not regex or re.match(regex, job.name):
455 self.log.debug("releasing job %s" % (job.parameters['UUID']))
456 job.release()
457 else:
458 self.log.debug("not releasing job %s" % (
459 job.parameters['UUID']))
460 self.log.debug("done releasing jobs %s (%s)" % (regex,
461 len(self.all_jobs)))
462
463 def fakeAllWaiting(self, regex=None):
464 all_jobs = self.all_jobs[:]
465 for job in all_jobs:
466 self.log.debug("job %s %s" % (job.parameters['UUID'],
467 job.isWaiting()))
468 if not job.isWaiting():
469 return False
470 return True
471
James E. Blairb02a3bb2012-07-30 17:49:55 -0700472 def fakeAddFailTest(self, name, change):
473 l = self.fail_tests.get(name, [])
474 l.append(change)
475 self.fail_tests[name] = l
476
James E. Blair4886cc12012-07-18 15:39:41 -0700477 def fakeShouldFailTest(self, name, ref):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700478 l = self.fail_tests.get(name, [])
479 for change in l:
James E. Blair4886cc12012-07-18 15:39:41 -0700480 if ref_has_change(ref, change):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700481 return True
482 return False
483
James E. Blairb0fcae42012-07-17 11:12:10 -0700484 def build_job(self, name, parameters):
485 count = self.job_counter.get(name, 0)
486 count += 1
487 self.job_counter[name] = count
James E. Blaird466dc42012-07-31 10:42:56 -0700488
489 queue_count = self.queue_counter
490 self.queue_counter += 1
James E. Blairb0fcae42012-07-17 11:12:10 -0700491 job = FakeJenkinsJob(self, self.callback, name, count, parameters)
James E. Blaird466dc42012-07-31 10:42:56 -0700492 job.queue_id = queue_count
493
James E. Blairb0fcae42012-07-17 11:12:10 -0700494 self.all_jobs.append(job)
495 job.start()
496
James E. Blaird466dc42012-07-31 10:42:56 -0700497 def stop_build(self, name, number):
498 for job in self.all_jobs:
499 if job.name == name and job.number == number:
500 job.aborted = True
501 job.release()
502 return
503
504 def cancel_queue(self, id):
505 for job in self.queue:
506 if job.queue_id == id:
507 job.canceled = True
508 job.release()
509 return
510
511 def get_queue_info(self):
512 items = []
513 for job in self.queue:
514 paramstr = ''
515 paramlst = []
516 d = {'actions': [{'parameters': paramlst},
517 {'causes': [{'shortDescription':
518 'Started by user Jenkins',
519 'userId': 'jenkins',
520 'userName': 'Jenkins'}]}],
521 'blocked': False,
522 'buildable': True,
523 'buildableStartMilliseconds': (job.created * 1000) + 5,
524 'id': job.queue_id,
525 'inQueueSince': (job.created * 1000),
526 'params': paramstr,
527 'stuck': False,
528 'task': {'color': 'blue',
529 'name': job.name,
530 'url': 'https://server/job/%s/' % job.name},
531 'why': 'Waiting for next available executor'}
532 for k, v in job.parameters.items():
533 paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
534 pd = {'name': k, 'value': v}
535 paramlst.append(pd)
536 items.append(d)
537 return items
538
James E. Blairb0fcae42012-07-17 11:12:10 -0700539 def set_build_description(self, *args, **kw):
540 pass
541
542
543class FakeJenkinsCallback(zuul.launcher.jenkins.JenkinsCallback):
544 def start(self):
545 pass
546
547
James E. Blair8cc15a82012-08-01 11:17:57 -0700548class FakeURLOpener(object):
549 def __init__(self, fake_gerrit, url):
550 self.fake_gerrit = fake_gerrit
551 self.url = url
552
553 def read(self):
554 res = urlparse.urlparse(self.url)
555 path = res.path
556 project = '/'.join(path.split('/')[2:-2])
James E. Blair8cc15a82012-08-01 11:17:57 -0700557 ret = ''
James E. Blairdaabed22012-08-15 15:38:57 -0700558 path = os.path.join("/tmp/zuul-test/upstream", project)
559 repo = git.Repo(path)
560 for ref in repo.refs:
561 ret += ref.object.hexsha + '\t' + ref.path + '\n'
James E. Blair8cc15a82012-08-01 11:17:57 -0700562 return ret
563
564
James E. Blair4886cc12012-07-18 15:39:41 -0700565class FakeGerritTrigger(zuul.trigger.gerrit.Gerrit):
566 def getGitUrl(self, project):
567 return "/tmp/zuul-test/upstream/%s" % project
568
569
James E. Blairb0fcae42012-07-17 11:12:10 -0700570class testScheduler(unittest.TestCase):
571 log = logging.getLogger("zuul.test")
572
573 def setUp(self):
James E. Blair4886cc12012-07-18 15:39:41 -0700574 if os.path.exists("/tmp/zuul-test"):
575 shutil.rmtree("/tmp/zuul-test")
576 os.makedirs("/tmp/zuul-test")
577 os.makedirs("/tmp/zuul-test/upstream")
578 os.makedirs("/tmp/zuul-test/git")
579
580 # For each project in config:
581 init_repo("org/project")
582 init_repo("org/project1")
583 init_repo("org/project2")
James E. Blair7f71c802012-08-22 13:04:32 -0700584 init_repo("org/one-job-project")
James E. Blairb0fcae42012-07-17 11:12:10 -0700585 self.config = CONFIG
586 self.sched = zuul.scheduler.Scheduler()
587
588 def jenkinsFactory(*args, **kw):
589 self.fake_jenkins = FakeJenkins()
590 return self.fake_jenkins
591
592 def jenkinsCallbackFactory(*args, **kw):
593 self.fake_jenkins_callback = FakeJenkinsCallback(*args, **kw)
594 return self.fake_jenkins_callback
595
James E. Blair8cc15a82012-08-01 11:17:57 -0700596 def URLOpenerFactory(*args, **kw):
597 args = [self.fake_gerrit] + list(args)
598 return FakeURLOpener(*args, **kw)
599
James E. Blairb0fcae42012-07-17 11:12:10 -0700600 zuul.launcher.jenkins.ExtendedJenkins = jenkinsFactory
601 zuul.launcher.jenkins.JenkinsCallback = jenkinsCallbackFactory
James E. Blair8cc15a82012-08-01 11:17:57 -0700602 urllib2.urlopen = URLOpenerFactory
James E. Blairb0fcae42012-07-17 11:12:10 -0700603 self.jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
604 self.fake_jenkins.callback = self.fake_jenkins_callback
605
606 zuul.lib.gerrit.Gerrit = FakeGerrit
607
James E. Blair4886cc12012-07-18 15:39:41 -0700608 self.gerrit = FakeGerritTrigger(self.config, self.sched)
James E. Blair8cc15a82012-08-01 11:17:57 -0700609 self.gerrit.replication_timeout = 1.5
610 self.gerrit.replication_retry_interval = 0.5
James E. Blairb0fcae42012-07-17 11:12:10 -0700611 self.fake_gerrit = self.gerrit.gerrit
612
613 self.sched.setLauncher(self.jenkins)
614 self.sched.setTrigger(self.gerrit)
615
616 self.sched.start()
617 self.sched.reconfigure(self.config)
618 self.sched.resume()
619
620 def tearDown(self):
621 self.jenkins.stop()
622 self.gerrit.stop()
623 self.sched.stop()
624 self.sched.join()
James E. Blair4886cc12012-07-18 15:39:41 -0700625 #shutil.rmtree("/tmp/zuul-test")
James E. Blairb0fcae42012-07-17 11:12:10 -0700626
627 def waitUntilSettled(self):
628 self.log.debug("Waiting until settled...")
629 start = time.time()
630 while True:
631 if time.time() - start > 10:
632 print 'queue status:',
633 print self.sched.trigger_event_queue.empty(),
634 print self.sched.result_event_queue.empty(),
635 print self.fake_gerrit.event_queue.empty(),
636 raise Exception("Timeout waiting for Zuul to settle")
637 self.fake_gerrit.event_queue.join()
638 self.sched.queue_lock.acquire()
639 if (self.sched.trigger_event_queue.empty() and
640 self.sched.result_event_queue.empty() and
641 self.fake_gerrit.event_queue.empty() and
642 self.fake_jenkins.fakeAllWaiting()):
643 self.sched.queue_lock.release()
644 self.log.debug("...settled.")
645 return
646 self.sched.queue_lock.release()
647 self.sched.wake_event.wait(0.1)
648
James E. Blaird466dc42012-07-31 10:42:56 -0700649 def countJobResults(self, jobs, result):
650 jobs = filter(lambda x: x['result'] == result, jobs)
651 return len(jobs)
652
James E. Blairb0fcae42012-07-17 11:12:10 -0700653 def test_jobs_launched(self):
654 "Test that jobs are launched and a change is merged"
655 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair8c803f82012-07-31 16:25:42 -0700656 A.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700657 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
658 self.waitUntilSettled()
659 jobs = self.fake_jenkins.job_history
660 job_names = [x['name'] for x in jobs]
661 assert 'project-merge' in job_names
662 assert 'project-test1' in job_names
663 assert 'project-test2' in job_names
664 assert jobs[0]['result'] == 'SUCCESS'
665 assert jobs[1]['result'] == 'SUCCESS'
666 assert jobs[2]['result'] == 'SUCCESS'
667 assert A.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700668 assert A.reported == 2
James E. Blairb0fcae42012-07-17 11:12:10 -0700669
670 def test_parallel_changes(self):
671 "Test that changes are tested in parallel and merged in series"
672 self.fake_jenkins.hold_jobs_in_build = True
673 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
674 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
675 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700676 A.addApproval('CRVW', 2)
677 B.addApproval('CRVW', 2)
678 C.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700679
680 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
681 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
682 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
683
684 self.waitUntilSettled()
685 jobs = self.fake_jenkins.all_jobs
686 assert len(jobs) == 1
687 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700688 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700689
690 self.fake_jenkins.fakeRelease('.*-merge')
691 self.waitUntilSettled()
692 assert len(jobs) == 3
693 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700694 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700695 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700696 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700697 assert jobs[2].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700698 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700699
700 self.fake_jenkins.fakeRelease('.*-merge')
701 self.waitUntilSettled()
702 assert len(jobs) == 5
703 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700704 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700705 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700706 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700707
708 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700709 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700710 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700711 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700712
713 assert jobs[4].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700714 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700715
716 self.fake_jenkins.fakeRelease('.*-merge')
717 self.waitUntilSettled()
718 assert len(jobs) == 6
719 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700720 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700721 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700722 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700723
724 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700725 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700726 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700727 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700728
729 assert jobs[4].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700730 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700731 assert jobs[5].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700732 assert job_has_changes(jobs[5], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700733
734 self.fake_jenkins.hold_jobs_in_build = False
735 self.fake_jenkins.fakeRelease()
736 self.waitUntilSettled()
737 assert len(jobs) == 0
738
739 jobs = self.fake_jenkins.job_history
740 assert len(jobs) == 9
741 assert A.data['status'] == 'MERGED'
742 assert B.data['status'] == 'MERGED'
743 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700744 assert A.reported == 2
745 assert B.reported == 2
746 assert C.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700747
748 def test_failed_changes(self):
749 "Test that a change behind a failed change is retested"
750 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
751 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
James E. Blair8c803f82012-07-31 16:25:42 -0700752 A.addApproval('CRVW', 2)
753 B.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700754
755 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
756 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
757
James E. Blair4886cc12012-07-18 15:39:41 -0700758 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700759
760 self.waitUntilSettled()
761 jobs = self.fake_jenkins.job_history
762 assert len(jobs) > 6
763 assert A.data['status'] == 'NEW'
764 assert B.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700765 assert A.reported == 2
766 assert B.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700767
768 def test_independent_queues(self):
769 "Test that changes end up in the right queues"
770 self.fake_jenkins.hold_jobs_in_build = True
771 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
772 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
773 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700774 A.addApproval('CRVW', 2)
775 B.addApproval('CRVW', 2)
776 C.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700777
778 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
779 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
780 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
781
782 jobs = self.fake_jenkins.all_jobs
783 self.waitUntilSettled()
784
785 # There should be one merge job at the head of each queue running
786 assert len(jobs) == 2
787 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700788 assert job_has_changes(jobs[0], A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700789 assert jobs[1].name == 'project1-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700790 assert job_has_changes(jobs[1], B)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700791
792 # Release the current merge jobs
793 self.fake_jenkins.fakeRelease('.*-merge')
794 self.waitUntilSettled()
795 # Release the merge job for project2 which is behind project1
796 self.fake_jenkins.fakeRelease('.*-merge')
797 self.waitUntilSettled()
798
799 # All the test jobs should be running:
800 # project1 (3) + project2 (3) + project (2) = 8
801 assert len(jobs) == 8
802
803 self.fake_jenkins.fakeRelease()
804 self.waitUntilSettled()
805 assert len(jobs) == 0
806
807 jobs = self.fake_jenkins.job_history
808 assert len(jobs) == 11
809 assert A.data['status'] == 'MERGED'
810 assert B.data['status'] == 'MERGED'
811 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700812 assert A.reported == 2
813 assert B.reported == 2
814 assert C.reported == 2
815
816 def test_failed_change_at_head(self):
817 "Test that if a change at the head fails, jobs behind it are canceled"
818 self.fake_jenkins.hold_jobs_in_build = True
819
820 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
821 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
822 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700823 A.addApproval('CRVW', 2)
824 B.addApproval('CRVW', 2)
825 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700826
James E. Blair4886cc12012-07-18 15:39:41 -0700827 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700828
829 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
830 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
831 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
832
833 self.waitUntilSettled()
834 jobs = self.fake_jenkins.all_jobs
835 finished_jobs = self.fake_jenkins.job_history
836
837 assert len(jobs) == 1
838 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700839 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700840
841 self.fake_jenkins.fakeRelease('.*-merge')
842 self.waitUntilSettled()
843 self.fake_jenkins.fakeRelease('.*-merge')
844 self.waitUntilSettled()
845 self.fake_jenkins.fakeRelease('.*-merge')
846 self.waitUntilSettled()
847
848 assert len(jobs) == 6
849 assert jobs[0].name == 'project-test1'
850 assert jobs[1].name == 'project-test2'
851 assert jobs[2].name == 'project-test1'
852 assert jobs[3].name == 'project-test2'
853 assert jobs[4].name == 'project-test1'
854 assert jobs[5].name == 'project-test2'
855
856 jobs[0].release()
857 self.waitUntilSettled()
858
James E. Blairec590122012-08-22 15:19:31 -0700859 assert len(jobs) == 2 # project-test2, project-merge for B
James E. Blaird466dc42012-07-31 10:42:56 -0700860 assert self.countJobResults(finished_jobs, 'ABORTED') == 4
861
862 self.fake_jenkins.hold_jobs_in_build = False
863 self.fake_jenkins.fakeRelease()
864 self.waitUntilSettled()
865
866 assert len(jobs) == 0
867 assert len(finished_jobs) == 15
868 assert A.data['status'] == 'NEW'
869 assert B.data['status'] == 'MERGED'
870 assert C.data['status'] == 'MERGED'
871 assert A.reported == 2
872 assert B.reported == 2
873 assert C.reported == 2
874
875 def test_failed_change_at_head_with_queue(self):
876 "Test that if a change at the head fails, queued jobs are canceled"
877 self.fake_jenkins.hold_jobs_in_queue = True
878
879 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
880 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
881 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700882 A.addApproval('CRVW', 2)
883 B.addApproval('CRVW', 2)
884 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700885
James E. Blair4886cc12012-07-18 15:39:41 -0700886 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700887
888 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
889 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
890 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
891
892 self.waitUntilSettled()
893 jobs = self.fake_jenkins.all_jobs
894 finished_jobs = self.fake_jenkins.job_history
895 queue = self.fake_jenkins.queue
896
897 assert len(jobs) == 1
898 assert len(queue) == 1
899 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700900 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700901
902 self.fake_jenkins.fakeRelease('.*-merge')
903 self.waitUntilSettled()
904 self.fake_jenkins.fakeRelease('.*-merge')
905 self.waitUntilSettled()
906 self.fake_jenkins.fakeRelease('.*-merge')
907 self.waitUntilSettled()
908
909 assert len(jobs) == 6
910 assert len(queue) == 6
911 assert jobs[0].name == 'project-test1'
912 assert jobs[1].name == 'project-test2'
913 assert jobs[2].name == 'project-test1'
914 assert jobs[3].name == 'project-test2'
915 assert jobs[4].name == 'project-test1'
916 assert jobs[5].name == 'project-test2'
917
918 jobs[0].release()
919 self.waitUntilSettled()
920
James E. Blairec590122012-08-22 15:19:31 -0700921 assert len(jobs) == 2 # project-test2, project-merge for B
922 assert len(queue) == 2
James E. Blaird466dc42012-07-31 10:42:56 -0700923 assert self.countJobResults(finished_jobs, 'ABORTED') == 0
924
925 self.fake_jenkins.hold_jobs_in_queue = False
926 self.fake_jenkins.fakeRelease()
927 self.waitUntilSettled()
928
929 assert len(jobs) == 0
930 assert len(finished_jobs) == 11
931 assert A.data['status'] == 'NEW'
932 assert B.data['status'] == 'MERGED'
933 assert C.data['status'] == 'MERGED'
934 assert A.reported == 2
935 assert B.reported == 2
936 assert C.reported == 2
James E. Blair8c803f82012-07-31 16:25:42 -0700937
938 def test_patch_order(self):
939 "Test that dependent patches are tested in the right order"
940 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
941 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
942 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
943 A.addApproval('CRVW', 2)
944 B.addApproval('CRVW', 2)
945 C.addApproval('CRVW', 2)
946
947 M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
948 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
949 M2.setMerged()
950 M1.setMerged()
951
952 # C -> B -> A -> M1 -> M2
953 # M2 is here to make sure it is never queried. If it is, it
954 # means zuul is walking down the entire history of merged
955 # changes.
956
957 C.setDependsOn(B, 1)
958 B.setDependsOn(A, 1)
959 A.setDependsOn(M1, 1)
960 M1.setDependsOn(M2, 1)
961
962 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
963
964 self.waitUntilSettled()
965
966 assert A.data['status'] == 'NEW'
967 assert B.data['status'] == 'NEW'
968 assert C.data['status'] == 'NEW'
969
970 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
971 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
972
973 self.waitUntilSettled()
974 assert M2.queried == 0
975 assert A.data['status'] == 'MERGED'
976 assert B.data['status'] == 'MERGED'
977 assert C.data['status'] == 'MERGED'
978 assert A.reported == 2
979 assert B.reported == 2
980 assert C.reported == 2
981
982 def test_can_merge(self):
James E. Blair4886cc12012-07-18 15:39:41 -0700983 "Test whether a change is ready to merge"
James E. Blair8c803f82012-07-31 16:25:42 -0700984 # TODO: move to test_gerrit (this is a unit test!)
985 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair4aea70c2012-07-26 14:23:24 -0700986 a = self.sched.trigger.getChange(1, 2)
987 mgr = self.sched.pipelines['gate'].manager
988 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -0700989
990 A.addApproval('CRVW', 2)
James E. Blair4aea70c2012-07-26 14:23:24 -0700991 a = self.sched.trigger.getChange(1, 2)
992 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -0700993
994 A.addApproval('APRV', 1)
James E. Blair4aea70c2012-07-26 14:23:24 -0700995 a = self.sched.trigger.getChange(1, 2)
996 assert self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -0700997
998 return True
James E. Blair4886cc12012-07-18 15:39:41 -0700999
1000 def test_build_configuration(self):
1001 "Test that zuul merges the right commits for testing"
1002 self.fake_jenkins.hold_jobs_in_queue = True
1003 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1004 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1005 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1006 A.addApproval('CRVW', 2)
1007 B.addApproval('CRVW', 2)
1008 C.addApproval('CRVW', 2)
1009 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1010 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1011 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1012 self.waitUntilSettled()
1013
1014 jobs = self.fake_jenkins.all_jobs
1015
1016 self.fake_jenkins.fakeRelease('.*-merge')
1017 self.waitUntilSettled()
1018 self.fake_jenkins.fakeRelease('.*-merge')
1019 self.waitUntilSettled()
1020 self.fake_jenkins.fakeRelease('.*-merge')
1021 self.waitUntilSettled()
1022 ref = jobs[-1].parameters['ZUUL_REF']
1023 self.fake_jenkins.hold_jobs_in_queue = False
1024 self.fake_jenkins.fakeRelease()
James E. Blair973721f2012-08-15 10:19:43 -07001025 self.waitUntilSettled()
James E. Blair4886cc12012-07-18 15:39:41 -07001026
1027 path = os.path.join("/tmp/zuul-test/git/org/project")
1028 repo = git.Repo(path)
1029 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1030 repo_messages.reverse()
James E. Blair4886cc12012-07-18 15:39:41 -07001031 correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
1032 assert repo_messages == correct_messages
James E. Blair973721f2012-08-15 10:19:43 -07001033
1034 def test_build_configuration_conflict(self):
1035 "Test that merge conflicts are handled"
1036 self.fake_jenkins.hold_jobs_in_queue = True
1037 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1038 A.addPatchset(['conflict'])
1039 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1040 B.addPatchset(['conflict'])
1041 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1042 A.addApproval('CRVW', 2)
1043 B.addApproval('CRVW', 2)
1044 C.addApproval('CRVW', 2)
1045 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1046 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1047 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1048 self.waitUntilSettled()
1049
1050 jobs = self.fake_jenkins.all_jobs
1051
1052 self.fake_jenkins.fakeRelease('.*-merge')
1053 self.waitUntilSettled()
1054 self.fake_jenkins.fakeRelease('.*-merge')
1055 self.waitUntilSettled()
1056 self.fake_jenkins.fakeRelease('.*-merge')
1057 self.waitUntilSettled()
1058 ref = jobs[-1].parameters['ZUUL_REF']
1059 self.fake_jenkins.hold_jobs_in_queue = False
1060 self.fake_jenkins.fakeRelease()
1061 self.waitUntilSettled()
1062
1063 assert A.data['status'] == 'MERGED'
1064 assert B.data['status'] == 'NEW'
1065 assert C.data['status'] == 'MERGED'
1066 assert A.reported == 2
1067 assert B.reported == 2
1068 assert C.reported == 2
James E. Blairdaabed22012-08-15 15:38:57 -07001069
1070 def test_post(self):
1071 "Test that post jobs run"
1072 e = {"type": "ref-updated",
1073 "submitter": {"name": "User Name"},
1074 "refUpdate": {"oldRev":
1075 "90f173846e3af9154517b88543ffbd1691f31366",
1076 "newRev":
1077 "d479a0bfcb34da57a31adb2a595c0cf687812543",
1078 "refName": "master", "project": "org/project"}}
1079 self.fake_gerrit.addEvent(e)
1080 self.waitUntilSettled()
1081
1082 jobs = self.fake_jenkins.job_history
1083 job_names = [x['name'] for x in jobs]
1084 assert len(jobs) == 1
1085 assert 'project-post' in job_names
James E. Blairc6294a52012-08-17 10:19:48 -07001086
1087 def test_build_configuration_branch(self):
1088 "Test that the right commits are on alternate branches"
1089 self.fake_jenkins.hold_jobs_in_queue = True
1090 A = self.fake_gerrit.addFakeChange('org/project', 'mp', 'A')
1091 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1092 C = self.fake_gerrit.addFakeChange('org/project', 'mp', 'C')
1093 A.addApproval('CRVW', 2)
1094 B.addApproval('CRVW', 2)
1095 C.addApproval('CRVW', 2)
1096 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1097 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1098 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1099 self.waitUntilSettled()
1100
1101 jobs = self.fake_jenkins.all_jobs
1102
1103 self.fake_jenkins.fakeRelease('.*-merge')
1104 self.waitUntilSettled()
1105 self.fake_jenkins.fakeRelease('.*-merge')
1106 self.waitUntilSettled()
1107 self.fake_jenkins.fakeRelease('.*-merge')
1108 self.waitUntilSettled()
1109 ref = jobs[-1].parameters['ZUUL_REF']
1110 self.fake_jenkins.hold_jobs_in_queue = False
1111 self.fake_jenkins.fakeRelease()
1112 self.waitUntilSettled()
1113
1114 path = os.path.join("/tmp/zuul-test/git/org/project")
1115 repo = git.Repo(path)
1116 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1117 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001118 correct_messages = ['initial commit', 'mp commit', 'A-1', 'B-1', 'C-1']
1119 assert repo_messages == correct_messages
1120
1121 def test_build_configuration_branch_interaction(self):
1122 "Test that switching between branches works"
1123 self.test_build_configuration()
1124 self.test_build_configuration_branch()
1125 # C has been merged, undo that
1126 path = os.path.join("/tmp/zuul-test/upstream", "org/project")
1127 repo = git.Repo(path)
1128 repo.heads.master.commit = repo.commit('init')
1129 self.test_build_configuration()
1130
1131 def test_build_configuration_multi_branch(self):
1132 "Test that dependent changes on multiple branches are merged"
1133 self.fake_jenkins.hold_jobs_in_queue = True
1134 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1135 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1136 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1137 A.addApproval('CRVW', 2)
1138 B.addApproval('CRVW', 2)
1139 C.addApproval('CRVW', 2)
1140 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1141 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1142 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1143 self.waitUntilSettled()
1144
1145 jobs = self.fake_jenkins.all_jobs
1146
1147 self.fake_jenkins.fakeRelease('.*-merge')
1148 self.waitUntilSettled()
1149 ref_mp = jobs[-1].parameters['ZUUL_REF']
1150 self.fake_jenkins.fakeRelease('.*-merge')
1151 self.waitUntilSettled()
1152 self.fake_jenkins.fakeRelease('.*-merge')
1153 self.waitUntilSettled()
1154 ref_master = jobs[-1].parameters['ZUUL_REF']
1155 self.fake_jenkins.hold_jobs_in_queue = False
1156 self.fake_jenkins.fakeRelease()
1157 self.waitUntilSettled()
1158
1159 path = os.path.join("/tmp/zuul-test/git/org/project")
1160 repo = git.Repo(path)
1161
1162 repo_messages = [c.message.strip()
1163 for c in repo.iter_commits(ref_master)]
1164 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001165 correct_messages = ['initial commit', 'A-1', 'C-1']
1166 assert repo_messages == correct_messages
1167
1168 repo_messages = [c.message.strip()
1169 for c in repo.iter_commits(ref_mp)]
1170 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001171 correct_messages = ['initial commit', 'mp commit', 'B-1']
1172 assert repo_messages == correct_messages
James E. Blair7f71c802012-08-22 13:04:32 -07001173
1174 def test_one_job_project(self):
1175 "Test that queueing works with one job"
1176 A = self.fake_gerrit.addFakeChange('org/one-job-project',
1177 'master', 'A')
1178 B = self.fake_gerrit.addFakeChange('org/one-job-project',
1179 'master', 'B')
1180 A.addApproval('CRVW', 2)
1181 B.addApproval('CRVW', 2)
1182 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1183 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1184 self.waitUntilSettled()
1185
1186 jobs = self.fake_jenkins.all_jobs
1187 finished_jobs = self.fake_jenkins.job_history
James E. Blair7f71c802012-08-22 13:04:32 -07001188
1189 assert A.data['status'] == 'MERGED'
1190 assert A.reported == 2
1191 assert B.data['status'] == 'MERGED'
1192 assert B.reported == 2
James E. Blaircaec0c52012-08-22 14:52:22 -07001193
1194 def test_dependent_changes_dequeue(self):
1195 "Test that dependent patches are not needlessly tested"
1196 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1197 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1198 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1199 A.addApproval('CRVW', 2)
1200 B.addApproval('CRVW', 2)
1201 C.addApproval('CRVW', 2)
1202
1203 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1204 M1.setMerged()
1205
1206 # C -> B -> A -> M1
1207
1208 C.setDependsOn(B, 1)
1209 B.setDependsOn(A, 1)
1210 A.setDependsOn(M1, 1)
1211
1212 self.fake_jenkins.fakeAddFailTest('project-merge', A)
1213
1214 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1215 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1216 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1217
1218 self.waitUntilSettled()
1219
1220 jobs = self.fake_jenkins.all_jobs
1221 finished_jobs = self.fake_jenkins.job_history
1222
James E. Blairec590122012-08-22 15:19:31 -07001223 assert A.data['status'] == 'NEW'
1224 assert A.reported == 2
1225 assert B.data['status'] == 'NEW'
1226 assert B.reported == 2
1227 assert C.data['status'] == 'NEW'
1228 assert C.reported == 2
1229 assert len(finished_jobs) == 1
1230
1231 def test_head_is_dequeued_once(self):
1232 "Test that if a change at the head fails it is dequeud only once"
1233 # If it's dequeued more than once, we should see extra
1234 # aborted jobs.
1235 self.fake_jenkins.hold_jobs_in_build = True
1236
1237 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1238 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1239 C = self.fake_gerrit.addFakeChange('org/project1', 'master', 'C')
1240 A.addApproval('CRVW', 2)
1241 B.addApproval('CRVW', 2)
1242 C.addApproval('CRVW', 2)
1243
1244 self.fake_jenkins.fakeAddFailTest('project1-test1', A)
1245 self.fake_jenkins.fakeAddFailTest('project1-test2', A)
1246 self.fake_jenkins.fakeAddFailTest('project1-project2-integration', A)
1247
1248 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1249 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1250 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1251
1252 self.waitUntilSettled()
1253 jobs = self.fake_jenkins.all_jobs
1254 finished_jobs = self.fake_jenkins.job_history
1255
1256 assert len(jobs) == 1
1257 assert jobs[0].name == 'project1-merge'
1258 assert job_has_changes(jobs[0], A)
1259
1260 self.fake_jenkins.fakeRelease('.*-merge')
1261 self.waitUntilSettled()
1262 self.fake_jenkins.fakeRelease('.*-merge')
1263 self.waitUntilSettled()
1264 self.fake_jenkins.fakeRelease('.*-merge')
1265 self.waitUntilSettled()
1266
1267 assert len(jobs) == 9
1268 assert jobs[0].name == 'project1-test1'
1269 assert jobs[1].name == 'project1-test2'
1270 assert jobs[2].name == 'project1-project2-integration'
1271 assert jobs[3].name == 'project1-test1'
1272 assert jobs[4].name == 'project1-test2'
1273 assert jobs[5].name == 'project1-project2-integration'
1274 assert jobs[6].name == 'project1-test1'
1275 assert jobs[7].name == 'project1-test2'
1276 assert jobs[8].name == 'project1-project2-integration'
1277
1278 jobs[0].release()
1279 self.waitUntilSettled()
1280
1281 assert len(jobs) == 3 # test2, integration, merge for B
1282 assert self.countJobResults(finished_jobs, 'ABORTED') == 6
1283
1284 self.fake_jenkins.hold_jobs_in_build = False
1285 self.fake_jenkins.fakeRelease()
1286 self.waitUntilSettled()
1287
1288 assert len(jobs) == 0
1289 assert len(finished_jobs) == 20
James E. Blaircaec0c52012-08-22 14:52:22 -07001290
1291 assert A.data['status'] == 'NEW'
James E. Blairec590122012-08-22 15:19:31 -07001292 assert B.data['status'] == 'MERGED'
1293 assert C.data['status'] == 'MERGED'
1294 assert A.reported == 2
1295 assert B.reported == 2
1296 assert C.reported == 2