blob: 1c76e8bf4a40566a52802617211bcc603847da60 [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
James E. Blair1dbd5082012-08-23 15:12:15 -070047TMP_ROOT = os.environ.get("ZUUL_TEST_ROOT", "/tmp")
48TEST_ROOT = os.path.join(TMP_ROOT, "zuul-test")
49UPSTREAM_ROOT = os.path.join(TEST_ROOT, "upstream")
50GIT_ROOT = os.path.join(TEST_ROOT, "git")
51
52CONFIG.set('zuul', 'git_dir', GIT_ROOT)
53
James E. Blairb0fcae42012-07-17 11:12:10 -070054logging.basicConfig(level=logging.DEBUG)
55
56
James E. Blair8cc15a82012-08-01 11:17:57 -070057def random_sha1():
58 return hashlib.sha1(str(random.random())).hexdigest()
59
60
James E. Blair4886cc12012-07-18 15:39:41 -070061class ChangeReference(git.Reference):
62 _common_path_default = "refs/changes"
63 _points_to_commits_only = True
64
65
66def init_repo(project):
67 parts = project.split('/')
James E. Blair1dbd5082012-08-23 15:12:15 -070068 path = os.path.join(UPSTREAM_ROOT, *parts[:-1])
James E. Blair4886cc12012-07-18 15:39:41 -070069 if not os.path.exists(path):
70 os.makedirs(path)
James E. Blair1dbd5082012-08-23 15:12:15 -070071 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blair4886cc12012-07-18 15:39:41 -070072 repo = git.Repo.init(path)
73
74 fn = os.path.join(path, 'README')
75 f = open(fn, 'w')
76 f.write("test\n")
77 f.close()
78 repo.index.add([fn])
79 repo.index.commit('initial commit')
James E. Blairc6294a52012-08-17 10:19:48 -070080 master = repo.create_head('master')
James E. Blair4886cc12012-07-18 15:39:41 -070081 repo.create_tag('init')
82
James E. Blairc6294a52012-08-17 10:19:48 -070083 mp = repo.create_head('mp')
84 repo.head.reference = mp
85 f = open(fn, 'a')
86 f.write("test mp\n")
87 f.close()
88 repo.index.add([fn])
89 repo.index.commit('mp commit')
90
91 repo.head.reference = master
92 repo.head.reset(index=True, working_tree=True)
93 repo.git.clean('-x', '-f', '-d')
94
James E. Blair4886cc12012-07-18 15:39:41 -070095
James E. Blair973721f2012-08-15 10:19:43 -070096def add_fake_change_to_repo(project, branch, change_num, patchset, msg, fn):
James E. Blair1dbd5082012-08-23 15:12:15 -070097 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blair4886cc12012-07-18 15:39:41 -070098 repo = git.Repo(path)
99 ref = ChangeReference.create(repo, '1/%s/%s' % (change_num,
100 patchset),
101 'refs/tags/init')
102 repo.head.reference = ref
103 repo.head.reset(index=True, working_tree=True)
104 repo.git.clean('-x', '-f', '-d')
105
James E. Blair1dbd5082012-08-23 15:12:15 -0700106 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blair973721f2012-08-15 10:19:43 -0700107 fn = os.path.join(path, fn)
James E. Blair4886cc12012-07-18 15:39:41 -0700108 f = open(fn, 'w')
James E. Blair973721f2012-08-15 10:19:43 -0700109 f.write("test %s %s %s\n" % (branch, change_num, patchset))
James E. Blair4886cc12012-07-18 15:39:41 -0700110 f.close()
111 repo.index.add([fn])
James E. Blairdaabed22012-08-15 15:38:57 -0700112 return repo.index.commit(msg)
James E. Blair4886cc12012-07-18 15:39:41 -0700113
114
115def ref_has_change(ref, change):
James E. Blair1dbd5082012-08-23 15:12:15 -0700116 path = os.path.join(GIT_ROOT, change.project)
James E. Blair4886cc12012-07-18 15:39:41 -0700117 repo = git.Repo(path)
118 for commit in repo.iter_commits(ref):
119 if commit.message.strip() == ('%s-1' % change.subject):
120 return True
121 return False
122
123
124def job_has_changes(*args):
125 job = args[0]
126 commits = args[1:]
127 project = job.parameters['ZUUL_PROJECT']
James E. Blair1dbd5082012-08-23 15:12:15 -0700128 path = os.path.join(GIT_ROOT, project)
James E. Blair4886cc12012-07-18 15:39:41 -0700129 repo = git.Repo(path)
130 ref = job.parameters['ZUUL_REF']
131 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
132 commit_messages = ['%s-1' % commit.subject for commit in commits]
James E. Blair4886cc12012-07-18 15:39:41 -0700133 for msg in commit_messages:
134 if msg not in repo_messages:
135 return False
136 return True
137
138
James E. Blairb0fcae42012-07-17 11:12:10 -0700139class FakeChange(object):
James E. Blair8c803f82012-07-31 16:25:42 -0700140 categories = {'APRV': ('Approved', -1, 1),
141 'CRVW': ('Code-Review', -2, 2),
142 'VRFY': ('Verified', -2, 2)}
James E. Blairb0fcae42012-07-17 11:12:10 -0700143
James E. Blair8cc15a82012-08-01 11:17:57 -0700144 def __init__(self, gerrit, number, project, branch, subject, status='NEW'):
145 self.gerrit = gerrit
James E. Blaird466dc42012-07-31 10:42:56 -0700146 self.reported = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700147 self.queried = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700148 self.patchsets = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700149 self.number = number
150 self.project = project
151 self.branch = branch
152 self.subject = subject
153 self.latest_patchset = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700154 self.depends_on_change = None
155 self.needed_by_changes = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700156 self.data = {
157 'branch': branch,
158 'comments': [],
159 'commitMessage': subject,
160 'createdOn': time.time(),
James E. Blair8cc15a82012-08-01 11:17:57 -0700161 'id': 'I' + random_sha1(),
James E. Blairb0fcae42012-07-17 11:12:10 -0700162 'lastUpdated': time.time(),
163 'number': str(number),
164 'open': True,
165 'owner': {'email': 'user@example.com',
166 'name': 'User Name',
167 'username': 'username'},
168 'patchSets': self.patchsets,
169 'project': project,
170 'status': status,
171 'subject': subject,
James E. Blair8c803f82012-07-31 16:25:42 -0700172 'submitRecords': [],
James E. Blairb0fcae42012-07-17 11:12:10 -0700173 'url': 'https://hostname/%s' % number}
174
175 self.addPatchset()
James E. Blair8c803f82012-07-31 16:25:42 -0700176 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700177
178 def addPatchset(self, files=None):
179 self.latest_patchset += 1
James E. Blairdaabed22012-08-15 15:38:57 -0700180 if files:
181 fn = files[0]
182 else:
183 fn = '%s-%s' % (self.branch, self.number)
184 msg = self.subject + '-' + str(self.latest_patchset)
185 c = add_fake_change_to_repo(self.project, self.branch,
186 self.number, self.latest_patchset,
187 msg, fn)
James E. Blairb0fcae42012-07-17 11:12:10 -0700188 d = {'approvals': [],
189 'createdOn': time.time(),
190 'files': [{'file': '/COMMIT_MSG',
191 'type': 'ADDED'},
192 {'file': 'README',
193 'type': 'MODIFIED'}],
James E. Blair8c803f82012-07-31 16:25:42 -0700194 'number': str(self.latest_patchset),
James E. Blairb0fcae42012-07-17 11:12:10 -0700195 'ref': 'refs/changes/1/%s/%s' % (self.number,
196 self.latest_patchset),
James E. Blairdaabed22012-08-15 15:38:57 -0700197 'revision': c.hexsha,
James E. Blairb0fcae42012-07-17 11:12:10 -0700198 'uploader': {'email': 'user@example.com',
199 'name': 'User name',
200 'username': 'user'}}
201 self.data['currentPatchSet'] = d
202 self.patchsets.append(d)
James E. Blair8c803f82012-07-31 16:25:42 -0700203 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700204
205 def addApproval(self, category, value):
James E. Blair8c803f82012-07-31 16:25:42 -0700206 approval = {'description': self.categories[category][0],
207 'type': category,
208 'value': str(value)}
209 self.patchsets[-1]['approvals'].append(approval)
210 event = {'approvals': [approval],
James E. Blairb0fcae42012-07-17 11:12:10 -0700211 'author': {'email': 'user@example.com',
212 'name': 'User Name',
213 'username': 'username'},
214 'change': {'branch': self.branch,
215 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
216 'number': str(self.number),
217 'owner': {'email': 'user@example.com',
218 'name': 'User Name',
219 'username': 'username'},
220 'project': self.project,
221 'subject': self.subject,
222 'topic': 'master',
223 'url': 'https://hostname/459'},
224 'comment': '',
225 'patchSet': self.patchsets[-1],
226 'type': 'comment-added'}
James E. Blair8c803f82012-07-31 16:25:42 -0700227 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700228 return json.loads(json.dumps(event))
229
James E. Blair8c803f82012-07-31 16:25:42 -0700230 def getSubmitRecords(self):
231 status = {}
232 for cat in self.categories.keys():
233 status[cat] = 0
234
235 for a in self.patchsets[-1]['approvals']:
236 cur = status[a['type']]
237 cat_min, cat_max = self.categories[a['type']][1:]
238 new = int(a['value'])
239 if new == cat_min:
240 cur = new
241 elif abs(new) > abs(cur):
242 cur = new
243 status[a['type']] = cur
244
245 labels = []
246 ok = True
247 for typ, cat in self.categories.items():
248 cur = status[typ]
249 cat_min, cat_max = cat[1:]
250 if cur == cat_min:
251 value = 'REJECT'
252 ok = False
253 elif cur == cat_max:
254 value = 'OK'
255 else:
256 value = 'NEED'
257 ok = False
258 labels.append({'label': cat[0], 'status': value})
259 if ok:
260 return [{'status': 'OK'}]
261 return [{'status': 'NOT_READY',
262 'labels': labels}]
263
264 def setDependsOn(self, other, patchset):
265 self.depends_on_change = other
266 d = {'id': other.data['id'],
267 'number': other.data['number'],
268 'ref': other.patchsets[patchset - 1]['ref']
269 }
270 self.data['dependsOn'] = [d]
271
272 other.needed_by_changes.append(self)
273 needed = other.data.get('neededBy', [])
274 d = {'id': self.data['id'],
275 'number': self.data['number'],
276 'ref': self.patchsets[patchset - 1]['ref'],
277 'revision': self.patchsets[patchset - 1]['revision']
278 }
279 needed.append(d)
280 other.data['neededBy'] = needed
281
James E. Blairb0fcae42012-07-17 11:12:10 -0700282 def query(self):
James E. Blair8c803f82012-07-31 16:25:42 -0700283 self.queried += 1
284 d = self.data.get('dependsOn')
285 if d:
286 d = d[0]
287 if (self.depends_on_change.patchsets[-1]['ref'] == d['ref']):
288 d['isCurrentPatchSet'] = True
289 else:
290 d['isCurrentPatchSet'] = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700291 return json.loads(json.dumps(self.data))
292
293 def setMerged(self):
James E. Blaircaec0c52012-08-22 14:52:22 -0700294 if (self.depends_on_change
295 and self.depends_on_change.data['status'] != 'MERGED'):
296 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700297 self.data['status'] = 'MERGED'
298 self.open = False
James E. Blairdaabed22012-08-15 15:38:57 -0700299
James E. Blair1dbd5082012-08-23 15:12:15 -0700300 path = os.path.join(UPSTREAM_ROOT, self.project)
James E. Blairdaabed22012-08-15 15:38:57 -0700301 repo = git.Repo(path)
302 repo.heads[self.branch].commit = \
303 repo.commit(self.patchsets[-1]['revision'])
James E. Blairb0fcae42012-07-17 11:12:10 -0700304
James E. Blaird466dc42012-07-31 10:42:56 -0700305 def setReported(self):
306 self.reported += 1
307
James E. Blairb0fcae42012-07-17 11:12:10 -0700308
309class FakeGerrit(object):
310 def __init__(self, *args, **kw):
311 self.event_queue = Queue.Queue()
312 self.fixture_dir = os.path.join(FIXTURE_DIR, 'gerrit')
313 self.change_number = 0
314 self.changes = {}
315
316 def addFakeChange(self, project, branch, subject):
317 self.change_number += 1
James E. Blair8cc15a82012-08-01 11:17:57 -0700318 c = FakeChange(self, self.change_number, project, branch, subject)
James E. Blairb0fcae42012-07-17 11:12:10 -0700319 self.changes[self.change_number] = c
320 return c
321
322 def addEvent(self, data):
323 return self.event_queue.put(data)
324
325 def getEvent(self):
326 return self.event_queue.get()
327
328 def eventDone(self):
329 self.event_queue.task_done()
330
331 def review(self, project, changeid, message, action):
James E. Blaird466dc42012-07-31 10:42:56 -0700332 number, ps = changeid.split(',')
333 change = self.changes[int(number)]
James E. Blairb0fcae42012-07-17 11:12:10 -0700334 if 'submit' in action:
James E. Blairb0fcae42012-07-17 11:12:10 -0700335 change.setMerged()
James E. Blaird466dc42012-07-31 10:42:56 -0700336 if message:
337 change.setReported()
James E. Blairb0fcae42012-07-17 11:12:10 -0700338
339 def query(self, number):
340 change = self.changes[int(number)]
341 return change.query()
342
343 def startWatching(self, *args, **kw):
344 pass
345
346
347class FakeJenkinsEvent(object):
348 def __init__(self, name, number, parameters, phase, status=None):
349 data = {'build':
350 {'full_url': 'https://server/job/%s/%s/' % (name, number),
351 'number': number,
352 'parameters': parameters,
353 'phase': phase,
354 'url': 'job/%s/%s/' % (name, number)},
355 'name': name,
356 'url': 'job/%s/' % name}
357 if status:
358 data['build']['status'] = status
359 self.body = json.dumps(data)
360
361
362class FakeJenkinsJob(threading.Thread):
363 log = logging.getLogger("zuul.test")
364
365 def __init__(self, jenkins, callback, name, number, parameters):
366 threading.Thread.__init__(self)
367 self.jenkins = jenkins
368 self.callback = callback
369 self.name = name
370 self.number = number
371 self.parameters = parameters
372 self.wait_condition = threading.Condition()
373 self.waiting = False
James E. Blaird466dc42012-07-31 10:42:56 -0700374 self.aborted = False
375 self.canceled = False
376 self.created = time.time()
James E. Blairb0fcae42012-07-17 11:12:10 -0700377
378 def release(self):
379 self.wait_condition.acquire()
380 self.wait_condition.notify()
381 self.waiting = False
382 self.log.debug("Job %s released" % (self.parameters['UUID']))
383 self.wait_condition.release()
384
385 def isWaiting(self):
386 self.wait_condition.acquire()
387 if self.waiting:
388 ret = True
389 else:
390 ret = False
391 self.wait_condition.release()
392 return ret
393
394 def _wait(self):
395 self.wait_condition.acquire()
396 self.waiting = True
397 self.log.debug("Job %s waiting" % (self.parameters['UUID']))
398 self.wait_condition.wait()
399 self.wait_condition.release()
400
401 def run(self):
402 self.jenkins.fakeEnqueue(self)
403 if self.jenkins.hold_jobs_in_queue:
404 self._wait()
405 self.jenkins.fakeDequeue(self)
James E. Blaird466dc42012-07-31 10:42:56 -0700406 if self.canceled:
407 self.jenkins.all_jobs.remove(self)
408 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700409 self.callback.jenkins_endpoint(FakeJenkinsEvent(
410 self.name, self.number, self.parameters,
411 'STARTED'))
412 if self.jenkins.hold_jobs_in_build:
413 self._wait()
414 self.log.debug("Job %s continuing" % (self.parameters['UUID']))
James E. Blairb02a3bb2012-07-30 17:49:55 -0700415
416 result = 'SUCCESS'
James E. Blairdaabed22012-08-15 15:38:57 -0700417 if ('ZUUL_REF' in self.parameters) and self.jenkins.fakeShouldFailTest(
James E. Blairb02a3bb2012-07-30 17:49:55 -0700418 self.name,
James E. Blair4886cc12012-07-18 15:39:41 -0700419 self.parameters['ZUUL_REF']):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700420 result = 'FAILURE'
James E. Blaird466dc42012-07-31 10:42:56 -0700421 if self.aborted:
422 result = 'ABORTED'
James E. Blairb02a3bb2012-07-30 17:49:55 -0700423
James E. Blairb0fcae42012-07-17 11:12:10 -0700424 self.jenkins.fakeAddHistory(name=self.name, number=self.number,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700425 result=result)
James E. Blairb0fcae42012-07-17 11:12:10 -0700426 self.callback.jenkins_endpoint(FakeJenkinsEvent(
427 self.name, self.number, self.parameters,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700428 'COMPLETED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700429 self.callback.jenkins_endpoint(FakeJenkinsEvent(
430 self.name, self.number, self.parameters,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700431 'FINISHED', result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700432 self.jenkins.all_jobs.remove(self)
433
434
435class FakeJenkins(object):
436 log = logging.getLogger("zuul.test")
437
438 def __init__(self, *args, **kw):
439 self.queue = []
440 self.all_jobs = []
441 self.job_counter = {}
James E. Blaird466dc42012-07-31 10:42:56 -0700442 self.queue_counter = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700443 self.job_history = []
444 self.hold_jobs_in_queue = False
445 self.hold_jobs_in_build = False
James E. Blairb02a3bb2012-07-30 17:49:55 -0700446 self.fail_tests = {}
James E. Blairb0fcae42012-07-17 11:12:10 -0700447
448 def fakeEnqueue(self, job):
449 self.queue.append(job)
450
451 def fakeDequeue(self, job):
452 self.queue.remove(job)
453
454 def fakeAddHistory(self, **kw):
455 self.job_history.append(kw)
456
457 def fakeRelease(self, regex=None):
458 all_jobs = self.all_jobs[:]
459 self.log.debug("releasing jobs %s (%s)" % (regex, len(self.all_jobs)))
460 for job in all_jobs:
461 if not regex or re.match(regex, job.name):
462 self.log.debug("releasing job %s" % (job.parameters['UUID']))
463 job.release()
464 else:
465 self.log.debug("not releasing job %s" % (
466 job.parameters['UUID']))
467 self.log.debug("done releasing jobs %s (%s)" % (regex,
468 len(self.all_jobs)))
469
470 def fakeAllWaiting(self, regex=None):
471 all_jobs = self.all_jobs[:]
472 for job in all_jobs:
473 self.log.debug("job %s %s" % (job.parameters['UUID'],
474 job.isWaiting()))
475 if not job.isWaiting():
476 return False
477 return True
478
James E. Blairb02a3bb2012-07-30 17:49:55 -0700479 def fakeAddFailTest(self, name, change):
480 l = self.fail_tests.get(name, [])
481 l.append(change)
482 self.fail_tests[name] = l
483
James E. Blair4886cc12012-07-18 15:39:41 -0700484 def fakeShouldFailTest(self, name, ref):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700485 l = self.fail_tests.get(name, [])
486 for change in l:
James E. Blair4886cc12012-07-18 15:39:41 -0700487 if ref_has_change(ref, change):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700488 return True
489 return False
490
James E. Blairb0fcae42012-07-17 11:12:10 -0700491 def build_job(self, name, parameters):
492 count = self.job_counter.get(name, 0)
493 count += 1
494 self.job_counter[name] = count
James E. Blaird466dc42012-07-31 10:42:56 -0700495
496 queue_count = self.queue_counter
497 self.queue_counter += 1
James E. Blairb0fcae42012-07-17 11:12:10 -0700498 job = FakeJenkinsJob(self, self.callback, name, count, parameters)
James E. Blaird466dc42012-07-31 10:42:56 -0700499 job.queue_id = queue_count
500
James E. Blairb0fcae42012-07-17 11:12:10 -0700501 self.all_jobs.append(job)
502 job.start()
503
James E. Blaird466dc42012-07-31 10:42:56 -0700504 def stop_build(self, name, number):
505 for job in self.all_jobs:
506 if job.name == name and job.number == number:
507 job.aborted = True
508 job.release()
509 return
510
511 def cancel_queue(self, id):
512 for job in self.queue:
513 if job.queue_id == id:
514 job.canceled = True
515 job.release()
516 return
517
518 def get_queue_info(self):
519 items = []
520 for job in self.queue:
521 paramstr = ''
522 paramlst = []
523 d = {'actions': [{'parameters': paramlst},
524 {'causes': [{'shortDescription':
525 'Started by user Jenkins',
526 'userId': 'jenkins',
527 'userName': 'Jenkins'}]}],
528 'blocked': False,
529 'buildable': True,
530 'buildableStartMilliseconds': (job.created * 1000) + 5,
531 'id': job.queue_id,
532 'inQueueSince': (job.created * 1000),
533 'params': paramstr,
534 'stuck': False,
535 'task': {'color': 'blue',
536 'name': job.name,
537 'url': 'https://server/job/%s/' % job.name},
538 'why': 'Waiting for next available executor'}
539 for k, v in job.parameters.items():
540 paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
541 pd = {'name': k, 'value': v}
542 paramlst.append(pd)
543 items.append(d)
544 return items
545
James E. Blairb0fcae42012-07-17 11:12:10 -0700546 def set_build_description(self, *args, **kw):
547 pass
548
549
550class FakeJenkinsCallback(zuul.launcher.jenkins.JenkinsCallback):
551 def start(self):
552 pass
553
554
James E. Blair8cc15a82012-08-01 11:17:57 -0700555class FakeURLOpener(object):
556 def __init__(self, fake_gerrit, url):
557 self.fake_gerrit = fake_gerrit
558 self.url = url
559
560 def read(self):
561 res = urlparse.urlparse(self.url)
562 path = res.path
563 project = '/'.join(path.split('/')[2:-2])
James E. Blair8cc15a82012-08-01 11:17:57 -0700564 ret = ''
James E. Blair1dbd5082012-08-23 15:12:15 -0700565 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blairdaabed22012-08-15 15:38:57 -0700566 repo = git.Repo(path)
567 for ref in repo.refs:
568 ret += ref.object.hexsha + '\t' + ref.path + '\n'
James E. Blair8cc15a82012-08-01 11:17:57 -0700569 return ret
570
571
James E. Blair4886cc12012-07-18 15:39:41 -0700572class FakeGerritTrigger(zuul.trigger.gerrit.Gerrit):
573 def getGitUrl(self, project):
James E. Blair1dbd5082012-08-23 15:12:15 -0700574 return os.path.join(UPSTREAM_ROOT, project.name)
James E. Blair4886cc12012-07-18 15:39:41 -0700575
576
James E. Blairb0fcae42012-07-17 11:12:10 -0700577class testScheduler(unittest.TestCase):
578 log = logging.getLogger("zuul.test")
579
580 def setUp(self):
James E. Blair1dbd5082012-08-23 15:12:15 -0700581 if os.path.exists(TEST_ROOT):
582 shutil.rmtree(TEST_ROOT)
583 os.makedirs(TEST_ROOT)
584 os.makedirs(UPSTREAM_ROOT)
585 os.makedirs(GIT_ROOT)
James E. Blair4886cc12012-07-18 15:39:41 -0700586
587 # For each project in config:
588 init_repo("org/project")
589 init_repo("org/project1")
590 init_repo("org/project2")
James E. Blair7f71c802012-08-22 13:04:32 -0700591 init_repo("org/one-job-project")
James E. Blairb0fcae42012-07-17 11:12:10 -0700592 self.config = CONFIG
593 self.sched = zuul.scheduler.Scheduler()
594
595 def jenkinsFactory(*args, **kw):
596 self.fake_jenkins = FakeJenkins()
597 return self.fake_jenkins
598
599 def jenkinsCallbackFactory(*args, **kw):
600 self.fake_jenkins_callback = FakeJenkinsCallback(*args, **kw)
601 return self.fake_jenkins_callback
602
James E. Blair8cc15a82012-08-01 11:17:57 -0700603 def URLOpenerFactory(*args, **kw):
604 args = [self.fake_gerrit] + list(args)
605 return FakeURLOpener(*args, **kw)
606
James E. Blairb0fcae42012-07-17 11:12:10 -0700607 zuul.launcher.jenkins.ExtendedJenkins = jenkinsFactory
608 zuul.launcher.jenkins.JenkinsCallback = jenkinsCallbackFactory
James E. Blair8cc15a82012-08-01 11:17:57 -0700609 urllib2.urlopen = URLOpenerFactory
James E. Blairb0fcae42012-07-17 11:12:10 -0700610 self.jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
611 self.fake_jenkins.callback = self.fake_jenkins_callback
612
613 zuul.lib.gerrit.Gerrit = FakeGerrit
614
James E. Blair4886cc12012-07-18 15:39:41 -0700615 self.gerrit = FakeGerritTrigger(self.config, self.sched)
James E. Blair8cc15a82012-08-01 11:17:57 -0700616 self.gerrit.replication_timeout = 1.5
617 self.gerrit.replication_retry_interval = 0.5
James E. Blairb0fcae42012-07-17 11:12:10 -0700618 self.fake_gerrit = self.gerrit.gerrit
619
620 self.sched.setLauncher(self.jenkins)
621 self.sched.setTrigger(self.gerrit)
622
623 self.sched.start()
624 self.sched.reconfigure(self.config)
625 self.sched.resume()
626
627 def tearDown(self):
628 self.jenkins.stop()
629 self.gerrit.stop()
630 self.sched.stop()
631 self.sched.join()
James E. Blair1dbd5082012-08-23 15:12:15 -0700632 #shutil.rmtree(TEST_ROOT)
James E. Blairb0fcae42012-07-17 11:12:10 -0700633
634 def waitUntilSettled(self):
635 self.log.debug("Waiting until settled...")
636 start = time.time()
637 while True:
638 if time.time() - start > 10:
639 print 'queue status:',
640 print self.sched.trigger_event_queue.empty(),
641 print self.sched.result_event_queue.empty(),
642 print self.fake_gerrit.event_queue.empty(),
643 raise Exception("Timeout waiting for Zuul to settle")
644 self.fake_gerrit.event_queue.join()
645 self.sched.queue_lock.acquire()
646 if (self.sched.trigger_event_queue.empty() and
647 self.sched.result_event_queue.empty() and
648 self.fake_gerrit.event_queue.empty() and
649 self.fake_jenkins.fakeAllWaiting()):
650 self.sched.queue_lock.release()
651 self.log.debug("...settled.")
652 return
653 self.sched.queue_lock.release()
654 self.sched.wake_event.wait(0.1)
655
James E. Blaird466dc42012-07-31 10:42:56 -0700656 def countJobResults(self, jobs, result):
657 jobs = filter(lambda x: x['result'] == result, jobs)
658 return len(jobs)
659
James E. Blairb0fcae42012-07-17 11:12:10 -0700660 def test_jobs_launched(self):
661 "Test that jobs are launched and a change is merged"
662 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair8c803f82012-07-31 16:25:42 -0700663 A.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700664 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
665 self.waitUntilSettled()
666 jobs = self.fake_jenkins.job_history
667 job_names = [x['name'] for x in jobs]
668 assert 'project-merge' in job_names
669 assert 'project-test1' in job_names
670 assert 'project-test2' in job_names
671 assert jobs[0]['result'] == 'SUCCESS'
672 assert jobs[1]['result'] == 'SUCCESS'
673 assert jobs[2]['result'] == 'SUCCESS'
674 assert A.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700675 assert A.reported == 2
James E. Blairb0fcae42012-07-17 11:12:10 -0700676
677 def test_parallel_changes(self):
678 "Test that changes are tested in parallel and merged in series"
679 self.fake_jenkins.hold_jobs_in_build = True
680 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
681 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
682 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700683 A.addApproval('CRVW', 2)
684 B.addApproval('CRVW', 2)
685 C.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700686
687 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
688 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
689 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
690
691 self.waitUntilSettled()
692 jobs = self.fake_jenkins.all_jobs
693 assert len(jobs) == 1
694 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700695 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700696
697 self.fake_jenkins.fakeRelease('.*-merge')
698 self.waitUntilSettled()
699 assert len(jobs) == 3
700 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700701 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700702 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700703 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700704 assert jobs[2].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700705 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700706
707 self.fake_jenkins.fakeRelease('.*-merge')
708 self.waitUntilSettled()
709 assert len(jobs) == 5
710 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700711 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700712 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700713 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700714
715 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700716 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700717 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700718 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700719
720 assert jobs[4].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700721 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700722
723 self.fake_jenkins.fakeRelease('.*-merge')
724 self.waitUntilSettled()
725 assert len(jobs) == 6
726 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700727 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700728 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700729 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700730
731 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700732 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700733 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700734 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700735
736 assert jobs[4].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700737 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700738 assert jobs[5].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700739 assert job_has_changes(jobs[5], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700740
741 self.fake_jenkins.hold_jobs_in_build = False
742 self.fake_jenkins.fakeRelease()
743 self.waitUntilSettled()
744 assert len(jobs) == 0
745
746 jobs = self.fake_jenkins.job_history
747 assert len(jobs) == 9
748 assert A.data['status'] == 'MERGED'
749 assert B.data['status'] == 'MERGED'
750 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700751 assert A.reported == 2
752 assert B.reported == 2
753 assert C.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700754
755 def test_failed_changes(self):
756 "Test that a change behind a failed change is retested"
757 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
758 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
James E. Blair8c803f82012-07-31 16:25:42 -0700759 A.addApproval('CRVW', 2)
760 B.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700761
762 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
763 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
764
James E. Blair4886cc12012-07-18 15:39:41 -0700765 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700766
767 self.waitUntilSettled()
768 jobs = self.fake_jenkins.job_history
769 assert len(jobs) > 6
770 assert A.data['status'] == 'NEW'
771 assert B.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700772 assert A.reported == 2
773 assert B.reported == 2
James E. Blairb02a3bb2012-07-30 17:49:55 -0700774
775 def test_independent_queues(self):
776 "Test that changes end up in the right queues"
777 self.fake_jenkins.hold_jobs_in_build = True
778 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
779 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
780 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700781 A.addApproval('CRVW', 2)
782 B.addApproval('CRVW', 2)
783 C.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700784
785 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
786 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
787 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
788
789 jobs = self.fake_jenkins.all_jobs
790 self.waitUntilSettled()
791
792 # There should be one merge job at the head of each queue running
793 assert len(jobs) == 2
794 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700795 assert job_has_changes(jobs[0], A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700796 assert jobs[1].name == 'project1-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700797 assert job_has_changes(jobs[1], B)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700798
799 # Release the current merge jobs
800 self.fake_jenkins.fakeRelease('.*-merge')
801 self.waitUntilSettled()
802 # Release the merge job for project2 which is behind project1
803 self.fake_jenkins.fakeRelease('.*-merge')
804 self.waitUntilSettled()
805
806 # All the test jobs should be running:
807 # project1 (3) + project2 (3) + project (2) = 8
808 assert len(jobs) == 8
809
810 self.fake_jenkins.fakeRelease()
811 self.waitUntilSettled()
812 assert len(jobs) == 0
813
814 jobs = self.fake_jenkins.job_history
815 assert len(jobs) == 11
816 assert A.data['status'] == 'MERGED'
817 assert B.data['status'] == 'MERGED'
818 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700819 assert A.reported == 2
820 assert B.reported == 2
821 assert C.reported == 2
822
823 def test_failed_change_at_head(self):
824 "Test that if a change at the head fails, jobs behind it are canceled"
825 self.fake_jenkins.hold_jobs_in_build = True
826
827 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
828 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
829 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700830 A.addApproval('CRVW', 2)
831 B.addApproval('CRVW', 2)
832 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700833
James E. Blair4886cc12012-07-18 15:39:41 -0700834 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700835
836 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
837 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
838 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
839
840 self.waitUntilSettled()
841 jobs = self.fake_jenkins.all_jobs
842 finished_jobs = self.fake_jenkins.job_history
843
844 assert len(jobs) == 1
845 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700846 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700847
848 self.fake_jenkins.fakeRelease('.*-merge')
849 self.waitUntilSettled()
850 self.fake_jenkins.fakeRelease('.*-merge')
851 self.waitUntilSettled()
852 self.fake_jenkins.fakeRelease('.*-merge')
853 self.waitUntilSettled()
854
855 assert len(jobs) == 6
856 assert jobs[0].name == 'project-test1'
857 assert jobs[1].name == 'project-test2'
858 assert jobs[2].name == 'project-test1'
859 assert jobs[3].name == 'project-test2'
860 assert jobs[4].name == 'project-test1'
861 assert jobs[5].name == 'project-test2'
862
863 jobs[0].release()
864 self.waitUntilSettled()
865
James E. Blairec590122012-08-22 15:19:31 -0700866 assert len(jobs) == 2 # project-test2, project-merge for B
James E. Blaird466dc42012-07-31 10:42:56 -0700867 assert self.countJobResults(finished_jobs, 'ABORTED') == 4
868
869 self.fake_jenkins.hold_jobs_in_build = False
870 self.fake_jenkins.fakeRelease()
871 self.waitUntilSettled()
872
873 assert len(jobs) == 0
874 assert len(finished_jobs) == 15
875 assert A.data['status'] == 'NEW'
876 assert B.data['status'] == 'MERGED'
877 assert C.data['status'] == 'MERGED'
878 assert A.reported == 2
879 assert B.reported == 2
880 assert C.reported == 2
881
882 def test_failed_change_at_head_with_queue(self):
883 "Test that if a change at the head fails, queued jobs are canceled"
884 self.fake_jenkins.hold_jobs_in_queue = True
885
886 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
887 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
888 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700889 A.addApproval('CRVW', 2)
890 B.addApproval('CRVW', 2)
891 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700892
James E. Blair4886cc12012-07-18 15:39:41 -0700893 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700894
895 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
896 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
897 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
898
899 self.waitUntilSettled()
900 jobs = self.fake_jenkins.all_jobs
901 finished_jobs = self.fake_jenkins.job_history
902 queue = self.fake_jenkins.queue
903
904 assert len(jobs) == 1
905 assert len(queue) == 1
906 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700907 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700908
909 self.fake_jenkins.fakeRelease('.*-merge')
910 self.waitUntilSettled()
911 self.fake_jenkins.fakeRelease('.*-merge')
912 self.waitUntilSettled()
913 self.fake_jenkins.fakeRelease('.*-merge')
914 self.waitUntilSettled()
915
916 assert len(jobs) == 6
917 assert len(queue) == 6
918 assert jobs[0].name == 'project-test1'
919 assert jobs[1].name == 'project-test2'
920 assert jobs[2].name == 'project-test1'
921 assert jobs[3].name == 'project-test2'
922 assert jobs[4].name == 'project-test1'
923 assert jobs[5].name == 'project-test2'
924
925 jobs[0].release()
926 self.waitUntilSettled()
927
James E. Blairec590122012-08-22 15:19:31 -0700928 assert len(jobs) == 2 # project-test2, project-merge for B
929 assert len(queue) == 2
James E. Blaird466dc42012-07-31 10:42:56 -0700930 assert self.countJobResults(finished_jobs, 'ABORTED') == 0
931
932 self.fake_jenkins.hold_jobs_in_queue = False
933 self.fake_jenkins.fakeRelease()
934 self.waitUntilSettled()
935
936 assert len(jobs) == 0
937 assert len(finished_jobs) == 11
938 assert A.data['status'] == 'NEW'
939 assert B.data['status'] == 'MERGED'
940 assert C.data['status'] == 'MERGED'
941 assert A.reported == 2
942 assert B.reported == 2
943 assert C.reported == 2
James E. Blair8c803f82012-07-31 16:25:42 -0700944
945 def test_patch_order(self):
946 "Test that dependent patches are tested in the right order"
947 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
948 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
949 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
950 A.addApproval('CRVW', 2)
951 B.addApproval('CRVW', 2)
952 C.addApproval('CRVW', 2)
953
954 M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
955 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
956 M2.setMerged()
957 M1.setMerged()
958
959 # C -> B -> A -> M1 -> M2
960 # M2 is here to make sure it is never queried. If it is, it
961 # means zuul is walking down the entire history of merged
962 # changes.
963
964 C.setDependsOn(B, 1)
965 B.setDependsOn(A, 1)
966 A.setDependsOn(M1, 1)
967 M1.setDependsOn(M2, 1)
968
969 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
970
971 self.waitUntilSettled()
972
973 assert A.data['status'] == 'NEW'
974 assert B.data['status'] == 'NEW'
975 assert C.data['status'] == 'NEW'
976
977 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
978 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
979
980 self.waitUntilSettled()
981 assert M2.queried == 0
982 assert A.data['status'] == 'MERGED'
983 assert B.data['status'] == 'MERGED'
984 assert C.data['status'] == 'MERGED'
985 assert A.reported == 2
986 assert B.reported == 2
987 assert C.reported == 2
988
989 def test_can_merge(self):
James E. Blair4886cc12012-07-18 15:39:41 -0700990 "Test whether a change is ready to merge"
James E. Blair8c803f82012-07-31 16:25:42 -0700991 # TODO: move to test_gerrit (this is a unit test!)
992 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair4aea70c2012-07-26 14:23:24 -0700993 a = self.sched.trigger.getChange(1, 2)
994 mgr = self.sched.pipelines['gate'].manager
995 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -0700996
997 A.addApproval('CRVW', 2)
James E. Blair4aea70c2012-07-26 14:23:24 -0700998 a = self.sched.trigger.getChange(1, 2)
999 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001000
1001 A.addApproval('APRV', 1)
James E. Blair4aea70c2012-07-26 14:23:24 -07001002 a = self.sched.trigger.getChange(1, 2)
1003 assert self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001004
1005 return True
James E. Blair4886cc12012-07-18 15:39:41 -07001006
1007 def test_build_configuration(self):
1008 "Test that zuul merges the right commits for testing"
1009 self.fake_jenkins.hold_jobs_in_queue = True
1010 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1011 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1012 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1013 A.addApproval('CRVW', 2)
1014 B.addApproval('CRVW', 2)
1015 C.addApproval('CRVW', 2)
1016 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1017 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1018 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1019 self.waitUntilSettled()
1020
1021 jobs = self.fake_jenkins.all_jobs
1022
1023 self.fake_jenkins.fakeRelease('.*-merge')
1024 self.waitUntilSettled()
1025 self.fake_jenkins.fakeRelease('.*-merge')
1026 self.waitUntilSettled()
1027 self.fake_jenkins.fakeRelease('.*-merge')
1028 self.waitUntilSettled()
James E. Blair1dbd5082012-08-23 15:12:15 -07001029
1030 pprint.pprint(jobs)
1031
James E. Blair4886cc12012-07-18 15:39:41 -07001032 ref = jobs[-1].parameters['ZUUL_REF']
1033 self.fake_jenkins.hold_jobs_in_queue = False
1034 self.fake_jenkins.fakeRelease()
James E. Blair973721f2012-08-15 10:19:43 -07001035 self.waitUntilSettled()
James E. Blair4886cc12012-07-18 15:39:41 -07001036
James E. Blair1dbd5082012-08-23 15:12:15 -07001037 path = os.path.join(GIT_ROOT, "org/project")
James E. Blair4886cc12012-07-18 15:39:41 -07001038 repo = git.Repo(path)
1039 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1040 repo_messages.reverse()
James E. Blair4886cc12012-07-18 15:39:41 -07001041 correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
1042 assert repo_messages == correct_messages
James E. Blair973721f2012-08-15 10:19:43 -07001043
1044 def test_build_configuration_conflict(self):
1045 "Test that merge conflicts are handled"
1046 self.fake_jenkins.hold_jobs_in_queue = True
1047 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1048 A.addPatchset(['conflict'])
1049 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1050 B.addPatchset(['conflict'])
1051 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1052 A.addApproval('CRVW', 2)
1053 B.addApproval('CRVW', 2)
1054 C.addApproval('CRVW', 2)
1055 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1056 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1057 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1058 self.waitUntilSettled()
1059
1060 jobs = self.fake_jenkins.all_jobs
1061
1062 self.fake_jenkins.fakeRelease('.*-merge')
1063 self.waitUntilSettled()
1064 self.fake_jenkins.fakeRelease('.*-merge')
1065 self.waitUntilSettled()
1066 self.fake_jenkins.fakeRelease('.*-merge')
1067 self.waitUntilSettled()
1068 ref = jobs[-1].parameters['ZUUL_REF']
1069 self.fake_jenkins.hold_jobs_in_queue = False
1070 self.fake_jenkins.fakeRelease()
1071 self.waitUntilSettled()
1072
1073 assert A.data['status'] == 'MERGED'
1074 assert B.data['status'] == 'NEW'
1075 assert C.data['status'] == 'MERGED'
1076 assert A.reported == 2
1077 assert B.reported == 2
1078 assert C.reported == 2
James E. Blairdaabed22012-08-15 15:38:57 -07001079
1080 def test_post(self):
1081 "Test that post jobs run"
1082 e = {"type": "ref-updated",
1083 "submitter": {"name": "User Name"},
1084 "refUpdate": {"oldRev":
1085 "90f173846e3af9154517b88543ffbd1691f31366",
1086 "newRev":
1087 "d479a0bfcb34da57a31adb2a595c0cf687812543",
1088 "refName": "master", "project": "org/project"}}
1089 self.fake_gerrit.addEvent(e)
1090 self.waitUntilSettled()
1091
1092 jobs = self.fake_jenkins.job_history
1093 job_names = [x['name'] for x in jobs]
1094 assert len(jobs) == 1
1095 assert 'project-post' in job_names
James E. Blairc6294a52012-08-17 10:19:48 -07001096
1097 def test_build_configuration_branch(self):
1098 "Test that the right commits are on alternate branches"
1099 self.fake_jenkins.hold_jobs_in_queue = True
1100 A = self.fake_gerrit.addFakeChange('org/project', 'mp', 'A')
1101 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1102 C = self.fake_gerrit.addFakeChange('org/project', 'mp', 'C')
1103 A.addApproval('CRVW', 2)
1104 B.addApproval('CRVW', 2)
1105 C.addApproval('CRVW', 2)
1106 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1107 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1108 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1109 self.waitUntilSettled()
1110
1111 jobs = self.fake_jenkins.all_jobs
1112
1113 self.fake_jenkins.fakeRelease('.*-merge')
1114 self.waitUntilSettled()
1115 self.fake_jenkins.fakeRelease('.*-merge')
1116 self.waitUntilSettled()
1117 self.fake_jenkins.fakeRelease('.*-merge')
1118 self.waitUntilSettled()
1119 ref = jobs[-1].parameters['ZUUL_REF']
1120 self.fake_jenkins.hold_jobs_in_queue = False
1121 self.fake_jenkins.fakeRelease()
1122 self.waitUntilSettled()
1123
James E. Blair1dbd5082012-08-23 15:12:15 -07001124 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001125 repo = git.Repo(path)
1126 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1127 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001128 correct_messages = ['initial commit', 'mp commit', 'A-1', 'B-1', 'C-1']
1129 assert repo_messages == correct_messages
1130
1131 def test_build_configuration_branch_interaction(self):
1132 "Test that switching between branches works"
1133 self.test_build_configuration()
1134 self.test_build_configuration_branch()
1135 # C has been merged, undo that
James E. Blair1dbd5082012-08-23 15:12:15 -07001136 path = os.path.join(UPSTREAM_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001137 repo = git.Repo(path)
1138 repo.heads.master.commit = repo.commit('init')
1139 self.test_build_configuration()
1140
1141 def test_build_configuration_multi_branch(self):
1142 "Test that dependent changes on multiple branches are merged"
1143 self.fake_jenkins.hold_jobs_in_queue = True
1144 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1145 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1146 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1147 A.addApproval('CRVW', 2)
1148 B.addApproval('CRVW', 2)
1149 C.addApproval('CRVW', 2)
1150 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1151 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1152 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1153 self.waitUntilSettled()
1154
1155 jobs = self.fake_jenkins.all_jobs
1156
1157 self.fake_jenkins.fakeRelease('.*-merge')
1158 self.waitUntilSettled()
1159 ref_mp = jobs[-1].parameters['ZUUL_REF']
1160 self.fake_jenkins.fakeRelease('.*-merge')
1161 self.waitUntilSettled()
1162 self.fake_jenkins.fakeRelease('.*-merge')
1163 self.waitUntilSettled()
1164 ref_master = jobs[-1].parameters['ZUUL_REF']
1165 self.fake_jenkins.hold_jobs_in_queue = False
1166 self.fake_jenkins.fakeRelease()
1167 self.waitUntilSettled()
1168
James E. Blair1dbd5082012-08-23 15:12:15 -07001169 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001170 repo = git.Repo(path)
1171
1172 repo_messages = [c.message.strip()
1173 for c in repo.iter_commits(ref_master)]
1174 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001175 correct_messages = ['initial commit', 'A-1', 'C-1']
1176 assert repo_messages == correct_messages
1177
1178 repo_messages = [c.message.strip()
1179 for c in repo.iter_commits(ref_mp)]
1180 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001181 correct_messages = ['initial commit', 'mp commit', 'B-1']
1182 assert repo_messages == correct_messages
James E. Blair7f71c802012-08-22 13:04:32 -07001183
1184 def test_one_job_project(self):
1185 "Test that queueing works with one job"
1186 A = self.fake_gerrit.addFakeChange('org/one-job-project',
1187 'master', 'A')
1188 B = self.fake_gerrit.addFakeChange('org/one-job-project',
1189 'master', 'B')
1190 A.addApproval('CRVW', 2)
1191 B.addApproval('CRVW', 2)
1192 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1193 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1194 self.waitUntilSettled()
1195
1196 jobs = self.fake_jenkins.all_jobs
1197 finished_jobs = self.fake_jenkins.job_history
James E. Blair7f71c802012-08-22 13:04:32 -07001198
1199 assert A.data['status'] == 'MERGED'
1200 assert A.reported == 2
1201 assert B.data['status'] == 'MERGED'
1202 assert B.reported == 2
James E. Blaircaec0c52012-08-22 14:52:22 -07001203
1204 def test_dependent_changes_dequeue(self):
1205 "Test that dependent patches are not needlessly tested"
1206 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1207 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1208 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1209 A.addApproval('CRVW', 2)
1210 B.addApproval('CRVW', 2)
1211 C.addApproval('CRVW', 2)
1212
1213 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1214 M1.setMerged()
1215
1216 # C -> B -> A -> M1
1217
1218 C.setDependsOn(B, 1)
1219 B.setDependsOn(A, 1)
1220 A.setDependsOn(M1, 1)
1221
1222 self.fake_jenkins.fakeAddFailTest('project-merge', A)
1223
1224 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1225 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1226 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1227
1228 self.waitUntilSettled()
1229
1230 jobs = self.fake_jenkins.all_jobs
1231 finished_jobs = self.fake_jenkins.job_history
1232
James E. Blairec590122012-08-22 15:19:31 -07001233 assert A.data['status'] == 'NEW'
1234 assert A.reported == 2
1235 assert B.data['status'] == 'NEW'
1236 assert B.reported == 2
1237 assert C.data['status'] == 'NEW'
1238 assert C.reported == 2
1239 assert len(finished_jobs) == 1
1240
1241 def test_head_is_dequeued_once(self):
1242 "Test that if a change at the head fails it is dequeud only once"
1243 # If it's dequeued more than once, we should see extra
1244 # aborted jobs.
1245 self.fake_jenkins.hold_jobs_in_build = True
1246
1247 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1248 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1249 C = self.fake_gerrit.addFakeChange('org/project1', 'master', 'C')
1250 A.addApproval('CRVW', 2)
1251 B.addApproval('CRVW', 2)
1252 C.addApproval('CRVW', 2)
1253
1254 self.fake_jenkins.fakeAddFailTest('project1-test1', A)
1255 self.fake_jenkins.fakeAddFailTest('project1-test2', A)
1256 self.fake_jenkins.fakeAddFailTest('project1-project2-integration', A)
1257
1258 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1259 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1260 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1261
1262 self.waitUntilSettled()
1263 jobs = self.fake_jenkins.all_jobs
1264 finished_jobs = self.fake_jenkins.job_history
1265
1266 assert len(jobs) == 1
1267 assert jobs[0].name == 'project1-merge'
1268 assert job_has_changes(jobs[0], A)
1269
1270 self.fake_jenkins.fakeRelease('.*-merge')
1271 self.waitUntilSettled()
1272 self.fake_jenkins.fakeRelease('.*-merge')
1273 self.waitUntilSettled()
1274 self.fake_jenkins.fakeRelease('.*-merge')
1275 self.waitUntilSettled()
1276
1277 assert len(jobs) == 9
1278 assert jobs[0].name == 'project1-test1'
1279 assert jobs[1].name == 'project1-test2'
1280 assert jobs[2].name == 'project1-project2-integration'
1281 assert jobs[3].name == 'project1-test1'
1282 assert jobs[4].name == 'project1-test2'
1283 assert jobs[5].name == 'project1-project2-integration'
1284 assert jobs[6].name == 'project1-test1'
1285 assert jobs[7].name == 'project1-test2'
1286 assert jobs[8].name == 'project1-project2-integration'
1287
1288 jobs[0].release()
1289 self.waitUntilSettled()
1290
1291 assert len(jobs) == 3 # test2, integration, merge for B
1292 assert self.countJobResults(finished_jobs, 'ABORTED') == 6
1293
1294 self.fake_jenkins.hold_jobs_in_build = False
1295 self.fake_jenkins.fakeRelease()
1296 self.waitUntilSettled()
1297
1298 assert len(jobs) == 0
1299 assert len(finished_jobs) == 20
James E. Blaircaec0c52012-08-22 14:52:22 -07001300
1301 assert A.data['status'] == 'NEW'
James E. Blairec590122012-08-22 15:19:31 -07001302 assert B.data['status'] == 'MERGED'
1303 assert C.data['status'] == 'MERGED'
1304 assert A.reported == 2
1305 assert B.reported == 2
1306 assert C.reported == 2