blob: c9ba1d5f1f1422a022c9620e0d5e3db4c6da06aa [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]
126 print 'checking that job %s has changes:' % ref
127 print ' commit messages:', commit_messages
128 print ' repo messages :', repo_messages
129 for msg in commit_messages:
130 if msg not in repo_messages:
131 return False
132 return True
133
134
James E. Blairb0fcae42012-07-17 11:12:10 -0700135class FakeChange(object):
James E. Blair8c803f82012-07-31 16:25:42 -0700136 categories = {'APRV': ('Approved', -1, 1),
137 'CRVW': ('Code-Review', -2, 2),
138 'VRFY': ('Verified', -2, 2)}
James E. Blairb0fcae42012-07-17 11:12:10 -0700139
James E. Blair8cc15a82012-08-01 11:17:57 -0700140 def __init__(self, gerrit, number, project, branch, subject, status='NEW'):
141 self.gerrit = gerrit
James E. Blaird466dc42012-07-31 10:42:56 -0700142 self.reported = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700143 self.queried = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700144 self.patchsets = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700145 self.number = number
146 self.project = project
147 self.branch = branch
148 self.subject = subject
149 self.latest_patchset = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700150 self.depends_on_change = None
151 self.needed_by_changes = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700152 self.data = {
153 'branch': branch,
154 'comments': [],
155 'commitMessage': subject,
156 'createdOn': time.time(),
James E. Blair8cc15a82012-08-01 11:17:57 -0700157 'id': 'I' + random_sha1(),
James E. Blairb0fcae42012-07-17 11:12:10 -0700158 'lastUpdated': time.time(),
159 'number': str(number),
160 'open': True,
161 'owner': {'email': 'user@example.com',
162 'name': 'User Name',
163 'username': 'username'},
164 'patchSets': self.patchsets,
165 'project': project,
166 'status': status,
167 'subject': subject,
James E. Blair8c803f82012-07-31 16:25:42 -0700168 'submitRecords': [],
James E. Blairb0fcae42012-07-17 11:12:10 -0700169 'url': 'https://hostname/%s' % number}
170
171 self.addPatchset()
James E. Blair8c803f82012-07-31 16:25:42 -0700172 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700173
174 def addPatchset(self, files=None):
175 self.latest_patchset += 1
James E. Blairdaabed22012-08-15 15:38:57 -0700176 if files:
177 fn = files[0]
178 else:
179 fn = '%s-%s' % (self.branch, self.number)
180 msg = self.subject + '-' + str(self.latest_patchset)
181 c = add_fake_change_to_repo(self.project, self.branch,
182 self.number, self.latest_patchset,
183 msg, fn)
James E. Blairb0fcae42012-07-17 11:12:10 -0700184 d = {'approvals': [],
185 'createdOn': time.time(),
186 'files': [{'file': '/COMMIT_MSG',
187 'type': 'ADDED'},
188 {'file': 'README',
189 'type': 'MODIFIED'}],
James E. Blair8c803f82012-07-31 16:25:42 -0700190 'number': str(self.latest_patchset),
James E. Blairb0fcae42012-07-17 11:12:10 -0700191 'ref': 'refs/changes/1/%s/%s' % (self.number,
192 self.latest_patchset),
James E. Blairdaabed22012-08-15 15:38:57 -0700193 'revision': c.hexsha,
James E. Blairb0fcae42012-07-17 11:12:10 -0700194 'uploader': {'email': 'user@example.com',
195 'name': 'User name',
196 'username': 'user'}}
197 self.data['currentPatchSet'] = d
198 self.patchsets.append(d)
James E. Blair8c803f82012-07-31 16:25:42 -0700199 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700200
201 def addApproval(self, category, value):
James E. Blair8c803f82012-07-31 16:25:42 -0700202 approval = {'description': self.categories[category][0],
203 'type': category,
204 'value': str(value)}
205 self.patchsets[-1]['approvals'].append(approval)
206 event = {'approvals': [approval],
James E. Blairb0fcae42012-07-17 11:12:10 -0700207 'author': {'email': 'user@example.com',
208 'name': 'User Name',
209 'username': 'username'},
210 'change': {'branch': self.branch,
211 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
212 'number': str(self.number),
213 'owner': {'email': 'user@example.com',
214 'name': 'User Name',
215 'username': 'username'},
216 'project': self.project,
217 'subject': self.subject,
218 'topic': 'master',
219 'url': 'https://hostname/459'},
220 'comment': '',
221 'patchSet': self.patchsets[-1],
222 'type': 'comment-added'}
James E. Blair8c803f82012-07-31 16:25:42 -0700223 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700224 return json.loads(json.dumps(event))
225
James E. Blair8c803f82012-07-31 16:25:42 -0700226 def getSubmitRecords(self):
227 status = {}
228 for cat in self.categories.keys():
229 status[cat] = 0
230
231 for a in self.patchsets[-1]['approvals']:
232 cur = status[a['type']]
233 cat_min, cat_max = self.categories[a['type']][1:]
234 new = int(a['value'])
235 if new == cat_min:
236 cur = new
237 elif abs(new) > abs(cur):
238 cur = new
239 status[a['type']] = cur
240
241 labels = []
242 ok = True
243 for typ, cat in self.categories.items():
244 cur = status[typ]
245 cat_min, cat_max = cat[1:]
246 if cur == cat_min:
247 value = 'REJECT'
248 ok = False
249 elif cur == cat_max:
250 value = 'OK'
251 else:
252 value = 'NEED'
253 ok = False
254 labels.append({'label': cat[0], 'status': value})
255 if ok:
256 return [{'status': 'OK'}]
257 return [{'status': 'NOT_READY',
258 'labels': labels}]
259
260 def setDependsOn(self, other, patchset):
261 self.depends_on_change = other
262 d = {'id': other.data['id'],
263 'number': other.data['number'],
264 'ref': other.patchsets[patchset - 1]['ref']
265 }
266 self.data['dependsOn'] = [d]
267
268 other.needed_by_changes.append(self)
269 needed = other.data.get('neededBy', [])
270 d = {'id': self.data['id'],
271 'number': self.data['number'],
272 'ref': self.patchsets[patchset - 1]['ref'],
273 'revision': self.patchsets[patchset - 1]['revision']
274 }
275 needed.append(d)
276 other.data['neededBy'] = needed
277
James E. Blairb0fcae42012-07-17 11:12:10 -0700278 def query(self):
James E. Blair8c803f82012-07-31 16:25:42 -0700279 self.queried += 1
280 d = self.data.get('dependsOn')
281 if d:
282 d = d[0]
283 if (self.depends_on_change.patchsets[-1]['ref'] == d['ref']):
284 d['isCurrentPatchSet'] = True
285 else:
286 d['isCurrentPatchSet'] = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700287 return json.loads(json.dumps(self.data))
288
289 def setMerged(self):
James E. Blaircaec0c52012-08-22 14:52:22 -0700290 if (self.depends_on_change
291 and self.depends_on_change.data['status'] != 'MERGED'):
292 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700293 self.data['status'] = 'MERGED'
294 self.open = False
James E. Blairdaabed22012-08-15 15:38:57 -0700295
296 path = os.path.join("/tmp/zuul-test/upstream", self.project)
297 repo = git.Repo(path)
298 repo.heads[self.branch].commit = \
299 repo.commit(self.patchsets[-1]['revision'])
James E. Blairb0fcae42012-07-17 11:12:10 -0700300
James E. Blaird466dc42012-07-31 10:42:56 -0700301 def setReported(self):
302 self.reported += 1
303
James E. Blairb0fcae42012-07-17 11:12:10 -0700304
305class FakeGerrit(object):
306 def __init__(self, *args, **kw):
307 self.event_queue = Queue.Queue()
308 self.fixture_dir = os.path.join(FIXTURE_DIR, 'gerrit')
309 self.change_number = 0
310 self.changes = {}
311
312 def addFakeChange(self, project, branch, subject):
313 self.change_number += 1
James E. Blair8cc15a82012-08-01 11:17:57 -0700314 c = FakeChange(self, self.change_number, project, branch, subject)
James E. Blairb0fcae42012-07-17 11:12:10 -0700315 self.changes[self.change_number] = c
316 return c
317
318 def addEvent(self, data):
319 return self.event_queue.put(data)
320
321 def getEvent(self):
322 return self.event_queue.get()
323
324 def eventDone(self):
325 self.event_queue.task_done()
326
327 def review(self, project, changeid, message, action):
James E. Blaird466dc42012-07-31 10:42:56 -0700328 number, ps = changeid.split(',')
329 change = self.changes[int(number)]
James E. Blairb0fcae42012-07-17 11:12:10 -0700330 if 'submit' in action:
James E. Blairb0fcae42012-07-17 11:12:10 -0700331 change.setMerged()
James E. Blaird466dc42012-07-31 10:42:56 -0700332 if message:
333 change.setReported()
James E. Blairb0fcae42012-07-17 11:12:10 -0700334
335 def query(self, number):
336 change = self.changes[int(number)]
337 return change.query()
338
339 def startWatching(self, *args, **kw):
340 pass
341
342
343class FakeJenkinsEvent(object):
344 def __init__(self, name, number, parameters, phase, status=None):
345 data = {'build':
346 {'full_url': 'https://server/job/%s/%s/' % (name, number),
347 'number': number,
348 'parameters': parameters,
349 'phase': phase,
350 'url': 'job/%s/%s/' % (name, number)},
351 'name': name,
352 'url': 'job/%s/' % name}
353 if status:
354 data['build']['status'] = status
355 self.body = json.dumps(data)
356
357
358class FakeJenkinsJob(threading.Thread):
359 log = logging.getLogger("zuul.test")
360
361 def __init__(self, jenkins, callback, name, number, parameters):
362 threading.Thread.__init__(self)
363 self.jenkins = jenkins
364 self.callback = callback
365 self.name = name
366 self.number = number
367 self.parameters = parameters
368 self.wait_condition = threading.Condition()
369 self.waiting = False
James E. Blaird466dc42012-07-31 10:42:56 -0700370 self.aborted = False
371 self.canceled = False
372 self.created = time.time()
James E. Blairb0fcae42012-07-17 11:12:10 -0700373
374 def release(self):
375 self.wait_condition.acquire()
376 self.wait_condition.notify()
377 self.waiting = False
378 self.log.debug("Job %s released" % (self.parameters['UUID']))
379 self.wait_condition.release()
380
381 def isWaiting(self):
382 self.wait_condition.acquire()
383 if self.waiting:
384 ret = True
385 else:
386 ret = False
387 self.wait_condition.release()
388 return ret
389
390 def _wait(self):
391 self.wait_condition.acquire()
392 self.waiting = True
393 self.log.debug("Job %s waiting" % (self.parameters['UUID']))
394 self.wait_condition.wait()
395 self.wait_condition.release()
396
397 def run(self):
398 self.jenkins.fakeEnqueue(self)
399 if self.jenkins.hold_jobs_in_queue:
400 self._wait()
401 self.jenkins.fakeDequeue(self)
James E. Blaird466dc42012-07-31 10:42:56 -0700402 if self.canceled:
403 self.jenkins.all_jobs.remove(self)
404 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700405 self.callback.jenkins_endpoint(FakeJenkinsEvent(
406 self.name, self.number, self.parameters,
407 'STARTED'))
408 if self.jenkins.hold_jobs_in_build:
409 self._wait()
410 self.log.debug("Job %s continuing" % (self.parameters['UUID']))
James E. Blairb02a3bb2012-07-30 17:49:55 -0700411
412 result = 'SUCCESS'
James E. Blairdaabed22012-08-15 15:38:57 -0700413 if ('ZUUL_REF' in self.parameters) and self.jenkins.fakeShouldFailTest(
James E. Blairb02a3bb2012-07-30 17:49:55 -0700414 self.name,
James E. Blair4886cc12012-07-18 15:39:41 -0700415 self.parameters['ZUUL_REF']):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700416 result = 'FAILURE'
James E. Blaird466dc42012-07-31 10:42:56 -0700417 if self.aborted:
418 result = 'ABORTED'
James E. Blairb02a3bb2012-07-30 17:49:55 -0700419
James E. Blairb0fcae42012-07-17 11:12:10 -0700420 self.jenkins.fakeAddHistory(name=self.name, number=self.number,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700421 result=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 'COMPLETED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700425 self.callback.jenkins_endpoint(FakeJenkinsEvent(
426 self.name, self.number, self.parameters,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700427 'FINISHED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700428 self.jenkins.all_jobs.remove(self)
429
430
431class FakeJenkins(object):
432 log = logging.getLogger("zuul.test")
433
434 def __init__(self, *args, **kw):
435 self.queue = []
436 self.all_jobs = []
437 self.job_counter = {}
James E. Blaird466dc42012-07-31 10:42:56 -0700438 self.queue_counter = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700439 self.job_history = []
440 self.hold_jobs_in_queue = False
441 self.hold_jobs_in_build = False
James E. Blairb02a3bb2012-07-30 17:49:55 -0700442 self.fail_tests = {}
James E. Blairb0fcae42012-07-17 11:12:10 -0700443
444 def fakeEnqueue(self, job):
445 self.queue.append(job)
446
447 def fakeDequeue(self, job):
448 self.queue.remove(job)
449
450 def fakeAddHistory(self, **kw):
451 self.job_history.append(kw)
452
453 def fakeRelease(self, regex=None):
454 all_jobs = self.all_jobs[:]
455 self.log.debug("releasing jobs %s (%s)" % (regex, len(self.all_jobs)))
456 for job in all_jobs:
457 if not regex or re.match(regex, job.name):
458 self.log.debug("releasing job %s" % (job.parameters['UUID']))
459 job.release()
460 else:
461 self.log.debug("not releasing job %s" % (
462 job.parameters['UUID']))
463 self.log.debug("done releasing jobs %s (%s)" % (regex,
464 len(self.all_jobs)))
465
466 def fakeAllWaiting(self, regex=None):
467 all_jobs = self.all_jobs[:]
468 for job in all_jobs:
469 self.log.debug("job %s %s" % (job.parameters['UUID'],
470 job.isWaiting()))
471 if not job.isWaiting():
472 return False
473 return True
474
James E. Blairb02a3bb2012-07-30 17:49:55 -0700475 def fakeAddFailTest(self, name, change):
476 l = self.fail_tests.get(name, [])
477 l.append(change)
478 self.fail_tests[name] = l
479
James E. Blair4886cc12012-07-18 15:39:41 -0700480 def fakeShouldFailTest(self, name, ref):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700481 l = self.fail_tests.get(name, [])
482 for change in l:
James E. Blair4886cc12012-07-18 15:39:41 -0700483 if ref_has_change(ref, change):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700484 return True
485 return False
486
James E. Blairb0fcae42012-07-17 11:12:10 -0700487 def build_job(self, name, parameters):
488 count = self.job_counter.get(name, 0)
489 count += 1
490 self.job_counter[name] = count
James E. Blaird466dc42012-07-31 10:42:56 -0700491
492 queue_count = self.queue_counter
493 self.queue_counter += 1
James E. Blairb0fcae42012-07-17 11:12:10 -0700494 job = FakeJenkinsJob(self, self.callback, name, count, parameters)
James E. Blaird466dc42012-07-31 10:42:56 -0700495 job.queue_id = queue_count
496
James E. Blairb0fcae42012-07-17 11:12:10 -0700497 self.all_jobs.append(job)
498 job.start()
499
James E. Blaird466dc42012-07-31 10:42:56 -0700500 def stop_build(self, name, number):
501 for job in self.all_jobs:
502 if job.name == name and job.number == number:
503 job.aborted = True
504 job.release()
505 return
506
507 def cancel_queue(self, id):
508 for job in self.queue:
509 if job.queue_id == id:
510 job.canceled = True
511 job.release()
512 return
513
514 def get_queue_info(self):
515 items = []
516 for job in self.queue:
517 paramstr = ''
518 paramlst = []
519 d = {'actions': [{'parameters': paramlst},
520 {'causes': [{'shortDescription':
521 'Started by user Jenkins',
522 'userId': 'jenkins',
523 'userName': 'Jenkins'}]}],
524 'blocked': False,
525 'buildable': True,
526 'buildableStartMilliseconds': (job.created * 1000) + 5,
527 'id': job.queue_id,
528 'inQueueSince': (job.created * 1000),
529 'params': paramstr,
530 'stuck': False,
531 'task': {'color': 'blue',
532 'name': job.name,
533 'url': 'https://server/job/%s/' % job.name},
534 'why': 'Waiting for next available executor'}
535 for k, v in job.parameters.items():
536 paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
537 pd = {'name': k, 'value': v}
538 paramlst.append(pd)
539 items.append(d)
540 return items
541
James E. Blairb0fcae42012-07-17 11:12:10 -0700542 def set_build_description(self, *args, **kw):
543 pass
544
545
546class FakeJenkinsCallback(zuul.launcher.jenkins.JenkinsCallback):
547 def start(self):
548 pass
549
550
James E. Blair8cc15a82012-08-01 11:17:57 -0700551class FakeURLOpener(object):
552 def __init__(self, fake_gerrit, url):
553 self.fake_gerrit = fake_gerrit
554 self.url = url
555
556 def read(self):
557 res = urlparse.urlparse(self.url)
558 path = res.path
559 project = '/'.join(path.split('/')[2:-2])
James E. Blair8cc15a82012-08-01 11:17:57 -0700560 ret = ''
James E. Blairdaabed22012-08-15 15:38:57 -0700561 path = os.path.join("/tmp/zuul-test/upstream", project)
562 repo = git.Repo(path)
563 for ref in repo.refs:
564 ret += ref.object.hexsha + '\t' + ref.path + '\n'
James E. Blair8cc15a82012-08-01 11:17:57 -0700565 return ret
566
567
James E. Blair4886cc12012-07-18 15:39:41 -0700568class FakeGerritTrigger(zuul.trigger.gerrit.Gerrit):
569 def getGitUrl(self, project):
570 return "/tmp/zuul-test/upstream/%s" % project
571
572
James E. Blairb0fcae42012-07-17 11:12:10 -0700573class testScheduler(unittest.TestCase):
574 log = logging.getLogger("zuul.test")
575
576 def setUp(self):
James E. Blair4886cc12012-07-18 15:39:41 -0700577 if os.path.exists("/tmp/zuul-test"):
578 shutil.rmtree("/tmp/zuul-test")
579 os.makedirs("/tmp/zuul-test")
580 os.makedirs("/tmp/zuul-test/upstream")
581 os.makedirs("/tmp/zuul-test/git")
582
583 # For each project in config:
584 init_repo("org/project")
585 init_repo("org/project1")
586 init_repo("org/project2")
James E. Blair7f71c802012-08-22 13:04:32 -0700587 init_repo("org/one-job-project")
James E. Blairb0fcae42012-07-17 11:12:10 -0700588 self.config = CONFIG
589 self.sched = zuul.scheduler.Scheduler()
590
591 def jenkinsFactory(*args, **kw):
592 self.fake_jenkins = FakeJenkins()
593 return self.fake_jenkins
594
595 def jenkinsCallbackFactory(*args, **kw):
596 self.fake_jenkins_callback = FakeJenkinsCallback(*args, **kw)
597 return self.fake_jenkins_callback
598
James E. Blair8cc15a82012-08-01 11:17:57 -0700599 def URLOpenerFactory(*args, **kw):
600 args = [self.fake_gerrit] + list(args)
601 return FakeURLOpener(*args, **kw)
602
James E. Blairb0fcae42012-07-17 11:12:10 -0700603 zuul.launcher.jenkins.ExtendedJenkins = jenkinsFactory
604 zuul.launcher.jenkins.JenkinsCallback = jenkinsCallbackFactory
James E. Blair8cc15a82012-08-01 11:17:57 -0700605 urllib2.urlopen = URLOpenerFactory
James E. Blairb0fcae42012-07-17 11:12:10 -0700606 self.jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
607 self.fake_jenkins.callback = self.fake_jenkins_callback
608
609 zuul.lib.gerrit.Gerrit = FakeGerrit
610
James E. Blair4886cc12012-07-18 15:39:41 -0700611 self.gerrit = FakeGerritTrigger(self.config, self.sched)
James E. Blair8cc15a82012-08-01 11:17:57 -0700612 self.gerrit.replication_timeout = 1.5
613 self.gerrit.replication_retry_interval = 0.5
James E. Blairb0fcae42012-07-17 11:12:10 -0700614 self.fake_gerrit = self.gerrit.gerrit
615
616 self.sched.setLauncher(self.jenkins)
617 self.sched.setTrigger(self.gerrit)
618
619 self.sched.start()
620 self.sched.reconfigure(self.config)
621 self.sched.resume()
622
623 def tearDown(self):
624 self.jenkins.stop()
625 self.gerrit.stop()
626 self.sched.stop()
627 self.sched.join()
James E. Blair4886cc12012-07-18 15:39:41 -0700628 #shutil.rmtree("/tmp/zuul-test")
James E. Blairb0fcae42012-07-17 11:12:10 -0700629
630 def waitUntilSettled(self):
631 self.log.debug("Waiting until settled...")
632 start = time.time()
633 while True:
634 if time.time() - start > 10:
635 print 'queue status:',
636 print self.sched.trigger_event_queue.empty(),
637 print self.sched.result_event_queue.empty(),
638 print self.fake_gerrit.event_queue.empty(),
639 raise Exception("Timeout waiting for Zuul to settle")
640 self.fake_gerrit.event_queue.join()
641 self.sched.queue_lock.acquire()
642 if (self.sched.trigger_event_queue.empty() and
643 self.sched.result_event_queue.empty() and
644 self.fake_gerrit.event_queue.empty() and
645 self.fake_jenkins.fakeAllWaiting()):
646 self.sched.queue_lock.release()
647 self.log.debug("...settled.")
648 return
649 self.sched.queue_lock.release()
650 self.sched.wake_event.wait(0.1)
651
James E. Blaird466dc42012-07-31 10:42:56 -0700652 def countJobResults(self, jobs, result):
653 jobs = filter(lambda x: x['result'] == result, jobs)
654 return len(jobs)
655
James E. Blairb0fcae42012-07-17 11:12:10 -0700656 def test_jobs_launched(self):
657 "Test that jobs are launched and a change is merged"
658 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair8c803f82012-07-31 16:25:42 -0700659 A.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700660 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
661 self.waitUntilSettled()
662 jobs = self.fake_jenkins.job_history
663 job_names = [x['name'] for x in jobs]
664 assert 'project-merge' in job_names
665 assert 'project-test1' in job_names
666 assert 'project-test2' in job_names
667 assert jobs[0]['result'] == 'SUCCESS'
668 assert jobs[1]['result'] == 'SUCCESS'
669 assert jobs[2]['result'] == 'SUCCESS'
670 assert A.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700671 assert A.reported == 2
James E. Blairb0fcae42012-07-17 11:12:10 -0700672
673 def test_parallel_changes(self):
674 "Test that changes are tested in parallel and merged in series"
675 self.fake_jenkins.hold_jobs_in_build = True
676 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
677 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
678 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700679 A.addApproval('CRVW', 2)
680 B.addApproval('CRVW', 2)
681 C.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700682
683 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
684 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
685 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
686
687 self.waitUntilSettled()
688 jobs = self.fake_jenkins.all_jobs
689 assert len(jobs) == 1
690 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700691 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700692
693 self.fake_jenkins.fakeRelease('.*-merge')
694 self.waitUntilSettled()
695 assert len(jobs) == 3
696 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700697 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700698 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700699 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700700 assert jobs[2].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700701 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700702
703 self.fake_jenkins.fakeRelease('.*-merge')
704 self.waitUntilSettled()
705 assert len(jobs) == 5
706 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700707 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700708 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700709 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700710
711 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700712 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700713 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700714 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700715
716 assert jobs[4].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700717 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700718
719 self.fake_jenkins.fakeRelease('.*-merge')
720 self.waitUntilSettled()
721 assert len(jobs) == 6
722 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700723 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700724 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700725 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700726
727 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700728 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700729 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700730 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700731
732 assert jobs[4].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700733 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700734 assert jobs[5].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700735 assert job_has_changes(jobs[5], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700736
737 self.fake_jenkins.hold_jobs_in_build = False
738 self.fake_jenkins.fakeRelease()
739 self.waitUntilSettled()
740 assert len(jobs) == 0
741
742 jobs = self.fake_jenkins.job_history
743 assert len(jobs) == 9
744 assert A.data['status'] == 'MERGED'
745 assert B.data['status'] == 'MERGED'
746 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700747 assert A.reported == 2
748 assert B.reported == 2
749 assert C.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700750
751 def test_failed_changes(self):
752 "Test that a change behind a failed change is retested"
753 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
754 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
James E. Blair8c803f82012-07-31 16:25:42 -0700755 A.addApproval('CRVW', 2)
756 B.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700757
758 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
759 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
760
James E. Blair4886cc12012-07-18 15:39:41 -0700761 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700762
763 self.waitUntilSettled()
764 jobs = self.fake_jenkins.job_history
765 assert len(jobs) > 6
766 assert A.data['status'] == 'NEW'
767 assert B.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700768 assert A.reported == 2
769 assert B.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700770
771 def test_independent_queues(self):
772 "Test that changes end up in the right queues"
773 self.fake_jenkins.hold_jobs_in_build = True
774 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
775 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
776 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700777 A.addApproval('CRVW', 2)
778 B.addApproval('CRVW', 2)
779 C.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700780
781 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
782 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
783 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
784
785 jobs = self.fake_jenkins.all_jobs
786 self.waitUntilSettled()
787
788 # There should be one merge job at the head of each queue running
789 assert len(jobs) == 2
790 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700791 assert job_has_changes(jobs[0], A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700792 assert jobs[1].name == 'project1-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700793 assert job_has_changes(jobs[1], B)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700794
795 # Release the current merge jobs
796 self.fake_jenkins.fakeRelease('.*-merge')
797 self.waitUntilSettled()
798 # Release the merge job for project2 which is behind project1
799 self.fake_jenkins.fakeRelease('.*-merge')
800 self.waitUntilSettled()
801
802 # All the test jobs should be running:
803 # project1 (3) + project2 (3) + project (2) = 8
804 assert len(jobs) == 8
805
806 self.fake_jenkins.fakeRelease()
807 self.waitUntilSettled()
808 assert len(jobs) == 0
809
810 jobs = self.fake_jenkins.job_history
811 assert len(jobs) == 11
812 assert A.data['status'] == 'MERGED'
813 assert B.data['status'] == 'MERGED'
814 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700815 assert A.reported == 2
816 assert B.reported == 2
817 assert C.reported == 2
818
819 def test_failed_change_at_head(self):
820 "Test that if a change at the head fails, jobs behind it are canceled"
821 self.fake_jenkins.hold_jobs_in_build = True
822
823 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
824 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
825 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700826 A.addApproval('CRVW', 2)
827 B.addApproval('CRVW', 2)
828 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700829
James E. Blair4886cc12012-07-18 15:39:41 -0700830 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700831
832 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
833 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
834 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
835
836 self.waitUntilSettled()
837 jobs = self.fake_jenkins.all_jobs
838 finished_jobs = self.fake_jenkins.job_history
839
840 assert len(jobs) == 1
841 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700842 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700843
844 self.fake_jenkins.fakeRelease('.*-merge')
845 self.waitUntilSettled()
846 self.fake_jenkins.fakeRelease('.*-merge')
847 self.waitUntilSettled()
848 self.fake_jenkins.fakeRelease('.*-merge')
849 self.waitUntilSettled()
850
851 assert len(jobs) == 6
852 assert jobs[0].name == 'project-test1'
853 assert jobs[1].name == 'project-test2'
854 assert jobs[2].name == 'project-test1'
855 assert jobs[3].name == 'project-test2'
856 assert jobs[4].name == 'project-test1'
857 assert jobs[5].name == 'project-test2'
858
859 jobs[0].release()
860 self.waitUntilSettled()
861
862 assert len(jobs) == 1 # project-test2
863 assert self.countJobResults(finished_jobs, 'ABORTED') == 4
864
865 self.fake_jenkins.hold_jobs_in_build = False
866 self.fake_jenkins.fakeRelease()
867 self.waitUntilSettled()
868
869 assert len(jobs) == 0
870 assert len(finished_jobs) == 15
871 assert A.data['status'] == 'NEW'
872 assert B.data['status'] == 'MERGED'
873 assert C.data['status'] == 'MERGED'
874 assert A.reported == 2
875 assert B.reported == 2
876 assert C.reported == 2
877
878 def test_failed_change_at_head_with_queue(self):
879 "Test that if a change at the head fails, queued jobs are canceled"
880 self.fake_jenkins.hold_jobs_in_queue = True
881
882 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
883 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
884 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700885 A.addApproval('CRVW', 2)
886 B.addApproval('CRVW', 2)
887 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700888
James E. Blair4886cc12012-07-18 15:39:41 -0700889 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700890
891 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
892 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
893 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
894
895 self.waitUntilSettled()
896 jobs = self.fake_jenkins.all_jobs
897 finished_jobs = self.fake_jenkins.job_history
898 queue = self.fake_jenkins.queue
899
900 assert len(jobs) == 1
901 assert len(queue) == 1
902 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700903 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700904
905 self.fake_jenkins.fakeRelease('.*-merge')
906 self.waitUntilSettled()
907 self.fake_jenkins.fakeRelease('.*-merge')
908 self.waitUntilSettled()
909 self.fake_jenkins.fakeRelease('.*-merge')
910 self.waitUntilSettled()
911
912 assert len(jobs) == 6
913 assert len(queue) == 6
914 assert jobs[0].name == 'project-test1'
915 assert jobs[1].name == 'project-test2'
916 assert jobs[2].name == 'project-test1'
917 assert jobs[3].name == 'project-test2'
918 assert jobs[4].name == 'project-test1'
919 assert jobs[5].name == 'project-test2'
920
921 jobs[0].release()
922 self.waitUntilSettled()
923
924 assert len(jobs) == 1 # project-test2
925 assert len(queue) == 1
926 assert self.countJobResults(finished_jobs, 'ABORTED') == 0
927
928 self.fake_jenkins.hold_jobs_in_queue = False
929 self.fake_jenkins.fakeRelease()
930 self.waitUntilSettled()
931
932 assert len(jobs) == 0
933 assert len(finished_jobs) == 11
934 assert A.data['status'] == 'NEW'
935 assert B.data['status'] == 'MERGED'
936 assert C.data['status'] == 'MERGED'
937 assert A.reported == 2
938 assert B.reported == 2
939 assert C.reported == 2
James E. Blair8c803f82012-07-31 16:25:42 -0700940
941 def test_patch_order(self):
942 "Test that dependent patches are tested in the right order"
943 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
944 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
945 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
946 A.addApproval('CRVW', 2)
947 B.addApproval('CRVW', 2)
948 C.addApproval('CRVW', 2)
949
950 M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
951 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
952 M2.setMerged()
953 M1.setMerged()
954
955 # C -> B -> A -> M1 -> M2
956 # M2 is here to make sure it is never queried. If it is, it
957 # means zuul is walking down the entire history of merged
958 # changes.
959
960 C.setDependsOn(B, 1)
961 B.setDependsOn(A, 1)
962 A.setDependsOn(M1, 1)
963 M1.setDependsOn(M2, 1)
964
965 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
966
967 self.waitUntilSettled()
968
969 assert A.data['status'] == 'NEW'
970 assert B.data['status'] == 'NEW'
971 assert C.data['status'] == 'NEW'
972
973 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
974 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
975
976 self.waitUntilSettled()
977 assert M2.queried == 0
978 assert A.data['status'] == 'MERGED'
979 assert B.data['status'] == 'MERGED'
980 assert C.data['status'] == 'MERGED'
981 assert A.reported == 2
982 assert B.reported == 2
983 assert C.reported == 2
984
985 def test_can_merge(self):
James E. Blair4886cc12012-07-18 15:39:41 -0700986 "Test whether a change is ready to merge"
James E. Blair8c803f82012-07-31 16:25:42 -0700987 # TODO: move to test_gerrit (this is a unit test!)
988 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair4aea70c2012-07-26 14:23:24 -0700989 a = self.sched.trigger.getChange(1, 2)
990 mgr = self.sched.pipelines['gate'].manager
991 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -0700992
993 A.addApproval('CRVW', 2)
James E. Blair4aea70c2012-07-26 14:23:24 -0700994 a = self.sched.trigger.getChange(1, 2)
995 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -0700996
997 A.addApproval('APRV', 1)
James E. Blair4aea70c2012-07-26 14:23:24 -0700998 a = self.sched.trigger.getChange(1, 2)
999 assert self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001000
1001 return True
James E. Blair4886cc12012-07-18 15:39:41 -07001002
1003 def test_build_configuration(self):
1004 "Test that zuul merges the right commits for testing"
1005 self.fake_jenkins.hold_jobs_in_queue = True
1006 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1007 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1008 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1009 A.addApproval('CRVW', 2)
1010 B.addApproval('CRVW', 2)
1011 C.addApproval('CRVW', 2)
1012 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1013 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1014 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1015 self.waitUntilSettled()
1016
1017 jobs = self.fake_jenkins.all_jobs
1018
1019 self.fake_jenkins.fakeRelease('.*-merge')
1020 self.waitUntilSettled()
1021 self.fake_jenkins.fakeRelease('.*-merge')
1022 self.waitUntilSettled()
1023 self.fake_jenkins.fakeRelease('.*-merge')
1024 self.waitUntilSettled()
1025 ref = jobs[-1].parameters['ZUUL_REF']
1026 self.fake_jenkins.hold_jobs_in_queue = False
1027 self.fake_jenkins.fakeRelease()
James E. Blair973721f2012-08-15 10:19:43 -07001028 self.waitUntilSettled()
James E. Blair4886cc12012-07-18 15:39:41 -07001029
1030 path = os.path.join("/tmp/zuul-test/git/org/project")
1031 repo = git.Repo(path)
1032 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1033 repo_messages.reverse()
1034 print ' repo messages :', repo_messages
1035 correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
1036 assert repo_messages == correct_messages
James E. Blair973721f2012-08-15 10:19:43 -07001037
1038 def test_build_configuration_conflict(self):
1039 "Test that merge conflicts are handled"
1040 self.fake_jenkins.hold_jobs_in_queue = True
1041 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1042 A.addPatchset(['conflict'])
1043 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1044 B.addPatchset(['conflict'])
1045 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1046 A.addApproval('CRVW', 2)
1047 B.addApproval('CRVW', 2)
1048 C.addApproval('CRVW', 2)
1049 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1050 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1051 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1052 self.waitUntilSettled()
1053
1054 jobs = self.fake_jenkins.all_jobs
1055
1056 self.fake_jenkins.fakeRelease('.*-merge')
1057 self.waitUntilSettled()
1058 self.fake_jenkins.fakeRelease('.*-merge')
1059 self.waitUntilSettled()
1060 self.fake_jenkins.fakeRelease('.*-merge')
1061 self.waitUntilSettled()
1062 ref = jobs[-1].parameters['ZUUL_REF']
1063 self.fake_jenkins.hold_jobs_in_queue = False
1064 self.fake_jenkins.fakeRelease()
1065 self.waitUntilSettled()
1066
1067 assert A.data['status'] == 'MERGED'
1068 assert B.data['status'] == 'NEW'
1069 assert C.data['status'] == 'MERGED'
1070 assert A.reported == 2
1071 assert B.reported == 2
1072 assert C.reported == 2
James E. Blairdaabed22012-08-15 15:38:57 -07001073
1074 def test_post(self):
1075 "Test that post jobs run"
1076 e = {"type": "ref-updated",
1077 "submitter": {"name": "User Name"},
1078 "refUpdate": {"oldRev":
1079 "90f173846e3af9154517b88543ffbd1691f31366",
1080 "newRev":
1081 "d479a0bfcb34da57a31adb2a595c0cf687812543",
1082 "refName": "master", "project": "org/project"}}
1083 self.fake_gerrit.addEvent(e)
1084 self.waitUntilSettled()
1085
1086 jobs = self.fake_jenkins.job_history
1087 job_names = [x['name'] for x in jobs]
1088 assert len(jobs) == 1
1089 assert 'project-post' in job_names
James E. Blairc6294a52012-08-17 10:19:48 -07001090
1091 def test_build_configuration_branch(self):
1092 "Test that the right commits are on alternate branches"
1093 self.fake_jenkins.hold_jobs_in_queue = True
1094 A = self.fake_gerrit.addFakeChange('org/project', 'mp', 'A')
1095 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1096 C = self.fake_gerrit.addFakeChange('org/project', 'mp', 'C')
1097 A.addApproval('CRVW', 2)
1098 B.addApproval('CRVW', 2)
1099 C.addApproval('CRVW', 2)
1100 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1101 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1102 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1103 self.waitUntilSettled()
1104
1105 jobs = self.fake_jenkins.all_jobs
1106
1107 self.fake_jenkins.fakeRelease('.*-merge')
1108 self.waitUntilSettled()
1109 self.fake_jenkins.fakeRelease('.*-merge')
1110 self.waitUntilSettled()
1111 self.fake_jenkins.fakeRelease('.*-merge')
1112 self.waitUntilSettled()
1113 ref = jobs[-1].parameters['ZUUL_REF']
1114 self.fake_jenkins.hold_jobs_in_queue = False
1115 self.fake_jenkins.fakeRelease()
1116 self.waitUntilSettled()
1117
1118 path = os.path.join("/tmp/zuul-test/git/org/project")
1119 repo = git.Repo(path)
1120 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1121 repo_messages.reverse()
1122 print ' repo messages :', repo_messages
1123 correct_messages = ['initial commit', 'mp commit', 'A-1', 'B-1', 'C-1']
1124 assert repo_messages == correct_messages
1125
1126 def test_build_configuration_branch_interaction(self):
1127 "Test that switching between branches works"
1128 self.test_build_configuration()
1129 self.test_build_configuration_branch()
1130 # C has been merged, undo that
1131 path = os.path.join("/tmp/zuul-test/upstream", "org/project")
1132 repo = git.Repo(path)
1133 repo.heads.master.commit = repo.commit('init')
1134 self.test_build_configuration()
1135
1136 def test_build_configuration_multi_branch(self):
1137 "Test that dependent changes on multiple branches are merged"
1138 self.fake_jenkins.hold_jobs_in_queue = True
1139 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1140 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1141 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1142 A.addApproval('CRVW', 2)
1143 B.addApproval('CRVW', 2)
1144 C.addApproval('CRVW', 2)
1145 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1146 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1147 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1148 self.waitUntilSettled()
1149
1150 jobs = self.fake_jenkins.all_jobs
1151
1152 self.fake_jenkins.fakeRelease('.*-merge')
1153 self.waitUntilSettled()
1154 ref_mp = jobs[-1].parameters['ZUUL_REF']
1155 self.fake_jenkins.fakeRelease('.*-merge')
1156 self.waitUntilSettled()
1157 self.fake_jenkins.fakeRelease('.*-merge')
1158 self.waitUntilSettled()
1159 ref_master = jobs[-1].parameters['ZUUL_REF']
1160 self.fake_jenkins.hold_jobs_in_queue = False
1161 self.fake_jenkins.fakeRelease()
1162 self.waitUntilSettled()
1163
1164 path = os.path.join("/tmp/zuul-test/git/org/project")
1165 repo = git.Repo(path)
1166
1167 repo_messages = [c.message.strip()
1168 for c in repo.iter_commits(ref_master)]
1169 repo_messages.reverse()
1170 print ' repo messages :', repo_messages
1171 correct_messages = ['initial commit', 'A-1', 'C-1']
1172 assert repo_messages == correct_messages
1173
1174 repo_messages = [c.message.strip()
1175 for c in repo.iter_commits(ref_mp)]
1176 repo_messages.reverse()
1177 print ' repo messages :', repo_messages
1178 correct_messages = ['initial commit', 'mp commit', 'B-1']
1179 assert repo_messages == correct_messages
James E. Blair7f71c802012-08-22 13:04:32 -07001180
1181 def test_one_job_project(self):
1182 "Test that queueing works with one job"
1183 A = self.fake_gerrit.addFakeChange('org/one-job-project',
1184 'master', 'A')
1185 B = self.fake_gerrit.addFakeChange('org/one-job-project',
1186 'master', 'B')
1187 A.addApproval('CRVW', 2)
1188 B.addApproval('CRVW', 2)
1189 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1190 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1191 self.waitUntilSettled()
1192
1193 jobs = self.fake_jenkins.all_jobs
1194 finished_jobs = self.fake_jenkins.job_history
1195 print jobs
1196 print finished_jobs
1197
1198 assert A.data['status'] == 'MERGED'
1199 assert A.reported == 2
1200 assert B.data['status'] == 'MERGED'
1201 assert B.reported == 2
James E. Blaircaec0c52012-08-22 14:52:22 -07001202
1203 def test_dependent_changes_dequeue(self):
1204 "Test that dependent patches are not needlessly tested"
1205 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1206 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1207 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1208 A.addApproval('CRVW', 2)
1209 B.addApproval('CRVW', 2)
1210 C.addApproval('CRVW', 2)
1211
1212 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1213 M1.setMerged()
1214
1215 # C -> B -> A -> M1
1216
1217 C.setDependsOn(B, 1)
1218 B.setDependsOn(A, 1)
1219 A.setDependsOn(M1, 1)
1220
1221 self.fake_jenkins.fakeAddFailTest('project-merge', A)
1222
1223 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1224 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1225 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1226
1227 self.waitUntilSettled()
1228
1229 jobs = self.fake_jenkins.all_jobs
1230 finished_jobs = self.fake_jenkins.job_history
1231
1232 pprint.pprint(jobs)
1233 pprint.pprint(finished_jobs)
1234
1235 assert A.data['status'] == 'NEW'
1236 assert B.data['status'] == 'NEW'
1237 assert C.data['status'] == 'NEW'
1238
1239 assert len(finished_jobs) == 1