blob: d59a66abd95afd6abe1ee4fa5e3b6c0ad18fe466 [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. Blair127bc182012-08-28 15:55:15 -0700156 self.fail_merge = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700157 self.data = {
158 'branch': branch,
159 'comments': [],
160 'commitMessage': subject,
161 'createdOn': time.time(),
James E. Blair8cc15a82012-08-01 11:17:57 -0700162 'id': 'I' + random_sha1(),
James E. Blairb0fcae42012-07-17 11:12:10 -0700163 'lastUpdated': time.time(),
164 'number': str(number),
165 'open': True,
166 'owner': {'email': 'user@example.com',
167 'name': 'User Name',
168 'username': 'username'},
169 'patchSets': self.patchsets,
170 'project': project,
171 'status': status,
172 'subject': subject,
James E. Blair8c803f82012-07-31 16:25:42 -0700173 'submitRecords': [],
James E. Blairb0fcae42012-07-17 11:12:10 -0700174 'url': 'https://hostname/%s' % number}
175
176 self.addPatchset()
James E. Blair8c803f82012-07-31 16:25:42 -0700177 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700178
179 def addPatchset(self, files=None):
180 self.latest_patchset += 1
James E. Blairdaabed22012-08-15 15:38:57 -0700181 if files:
182 fn = files[0]
183 else:
184 fn = '%s-%s' % (self.branch, self.number)
185 msg = self.subject + '-' + str(self.latest_patchset)
186 c = add_fake_change_to_repo(self.project, self.branch,
187 self.number, self.latest_patchset,
188 msg, fn)
James E. Blairb0fcae42012-07-17 11:12:10 -0700189 d = {'approvals': [],
190 'createdOn': time.time(),
191 'files': [{'file': '/COMMIT_MSG',
192 'type': 'ADDED'},
193 {'file': 'README',
194 'type': 'MODIFIED'}],
James E. Blair8c803f82012-07-31 16:25:42 -0700195 'number': str(self.latest_patchset),
James E. Blairb0fcae42012-07-17 11:12:10 -0700196 'ref': 'refs/changes/1/%s/%s' % (self.number,
197 self.latest_patchset),
James E. Blairdaabed22012-08-15 15:38:57 -0700198 'revision': c.hexsha,
James E. Blairb0fcae42012-07-17 11:12:10 -0700199 'uploader': {'email': 'user@example.com',
200 'name': 'User name',
201 'username': 'user'}}
202 self.data['currentPatchSet'] = d
203 self.patchsets.append(d)
James E. Blair8c803f82012-07-31 16:25:42 -0700204 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700205
James E. Blaire0487072012-08-29 17:38:31 -0700206 def getPatchsetCreatedEvent(self, patchset):
207 event = {"type": "patchset-created",
208 "change": {"project": self.project,
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800209 "branch": self.branch,
210 "id": "I5459869c07352a31bfb1e7a8cac379cabfcb25af",
211 "number": str(self.number),
212 "subject": self.subject,
213 "owner": {"name": "User Name"},
214 "url": "https://hostname/3"},
James E. Blaire0487072012-08-29 17:38:31 -0700215 "patchSet": self.patchsets[patchset - 1],
216 "uploader": {"name": "User Name"}}
217 return event
218
James E. Blairb0fcae42012-07-17 11:12:10 -0700219 def addApproval(self, category, value):
James E. Blair8c803f82012-07-31 16:25:42 -0700220 approval = {'description': self.categories[category][0],
221 'type': category,
222 'value': str(value)}
223 self.patchsets[-1]['approvals'].append(approval)
224 event = {'approvals': [approval],
James E. Blairb0fcae42012-07-17 11:12:10 -0700225 'author': {'email': 'user@example.com',
226 'name': 'User Name',
227 'username': 'username'},
228 'change': {'branch': self.branch,
229 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
230 'number': str(self.number),
231 'owner': {'email': 'user@example.com',
232 'name': 'User Name',
233 'username': 'username'},
234 'project': self.project,
235 'subject': self.subject,
236 'topic': 'master',
237 'url': 'https://hostname/459'},
238 'comment': '',
239 'patchSet': self.patchsets[-1],
240 'type': 'comment-added'}
James E. Blair8c803f82012-07-31 16:25:42 -0700241 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700242 return json.loads(json.dumps(event))
243
James E. Blair8c803f82012-07-31 16:25:42 -0700244 def getSubmitRecords(self):
245 status = {}
246 for cat in self.categories.keys():
247 status[cat] = 0
248
249 for a in self.patchsets[-1]['approvals']:
250 cur = status[a['type']]
251 cat_min, cat_max = self.categories[a['type']][1:]
252 new = int(a['value'])
253 if new == cat_min:
254 cur = new
255 elif abs(new) > abs(cur):
256 cur = new
257 status[a['type']] = cur
258
259 labels = []
260 ok = True
261 for typ, cat in self.categories.items():
262 cur = status[typ]
263 cat_min, cat_max = cat[1:]
264 if cur == cat_min:
265 value = 'REJECT'
266 ok = False
267 elif cur == cat_max:
268 value = 'OK'
269 else:
270 value = 'NEED'
271 ok = False
272 labels.append({'label': cat[0], 'status': value})
273 if ok:
274 return [{'status': 'OK'}]
275 return [{'status': 'NOT_READY',
276 'labels': labels}]
277
278 def setDependsOn(self, other, patchset):
279 self.depends_on_change = other
280 d = {'id': other.data['id'],
281 'number': other.data['number'],
282 'ref': other.patchsets[patchset - 1]['ref']
283 }
284 self.data['dependsOn'] = [d]
285
286 other.needed_by_changes.append(self)
287 needed = other.data.get('neededBy', [])
288 d = {'id': self.data['id'],
289 'number': self.data['number'],
290 'ref': self.patchsets[patchset - 1]['ref'],
291 'revision': self.patchsets[patchset - 1]['revision']
292 }
293 needed.append(d)
294 other.data['neededBy'] = needed
295
James E. Blairb0fcae42012-07-17 11:12:10 -0700296 def query(self):
James E. Blair8c803f82012-07-31 16:25:42 -0700297 self.queried += 1
298 d = self.data.get('dependsOn')
299 if d:
300 d = d[0]
301 if (self.depends_on_change.patchsets[-1]['ref'] == d['ref']):
302 d['isCurrentPatchSet'] = True
303 else:
304 d['isCurrentPatchSet'] = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700305 return json.loads(json.dumps(self.data))
306
307 def setMerged(self):
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800308 if (self.depends_on_change and
309 self.depends_on_change.data['status'] != 'MERGED'):
James E. Blaircaec0c52012-08-22 14:52:22 -0700310 return
James E. Blair127bc182012-08-28 15:55:15 -0700311 if self.fail_merge:
312 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700313 self.data['status'] = 'MERGED'
314 self.open = False
James E. Blairdaabed22012-08-15 15:38:57 -0700315
James E. Blair1dbd5082012-08-23 15:12:15 -0700316 path = os.path.join(UPSTREAM_ROOT, self.project)
James E. Blairdaabed22012-08-15 15:38:57 -0700317 repo = git.Repo(path)
318 repo.heads[self.branch].commit = \
319 repo.commit(self.patchsets[-1]['revision'])
James E. Blairb0fcae42012-07-17 11:12:10 -0700320
James E. Blaird466dc42012-07-31 10:42:56 -0700321 def setReported(self):
322 self.reported += 1
323
James E. Blairb0fcae42012-07-17 11:12:10 -0700324
325class FakeGerrit(object):
326 def __init__(self, *args, **kw):
327 self.event_queue = Queue.Queue()
328 self.fixture_dir = os.path.join(FIXTURE_DIR, 'gerrit')
329 self.change_number = 0
330 self.changes = {}
331
332 def addFakeChange(self, project, branch, subject):
333 self.change_number += 1
James E. Blair8cc15a82012-08-01 11:17:57 -0700334 c = FakeChange(self, self.change_number, project, branch, subject)
James E. Blairb0fcae42012-07-17 11:12:10 -0700335 self.changes[self.change_number] = c
336 return c
337
338 def addEvent(self, data):
339 return self.event_queue.put(data)
340
341 def getEvent(self):
342 return self.event_queue.get()
343
344 def eventDone(self):
345 self.event_queue.task_done()
346
347 def review(self, project, changeid, message, action):
James E. Blaird466dc42012-07-31 10:42:56 -0700348 number, ps = changeid.split(',')
349 change = self.changes[int(number)]
James E. Blairb0fcae42012-07-17 11:12:10 -0700350 if 'submit' in action:
James E. Blairb0fcae42012-07-17 11:12:10 -0700351 change.setMerged()
James E. Blaird466dc42012-07-31 10:42:56 -0700352 if message:
353 change.setReported()
James E. Blairb0fcae42012-07-17 11:12:10 -0700354
355 def query(self, number):
356 change = self.changes[int(number)]
357 return change.query()
358
359 def startWatching(self, *args, **kw):
360 pass
361
362
363class FakeJenkinsEvent(object):
364 def __init__(self, name, number, parameters, phase, status=None):
Zhongyue Luo5d556072012-09-21 02:00:47 +0900365 data = {
366 'build': {
367 'full_url': 'https://server/job/%s/%s/' % (name, number),
368 'number': number,
369 'parameters': parameters,
370 'phase': phase,
371 'url': 'job/%s/%s/' % (name, number),
372 },
373 'name': name,
374 'url': 'job/%s/' % name,
375 }
James E. Blairb0fcae42012-07-17 11:12:10 -0700376 if status:
377 data['build']['status'] = status
378 self.body = json.dumps(data)
379
380
381class FakeJenkinsJob(threading.Thread):
382 log = logging.getLogger("zuul.test")
383
384 def __init__(self, jenkins, callback, name, number, parameters):
385 threading.Thread.__init__(self)
386 self.jenkins = jenkins
387 self.callback = callback
388 self.name = name
389 self.number = number
390 self.parameters = parameters
391 self.wait_condition = threading.Condition()
392 self.waiting = False
James E. Blaird466dc42012-07-31 10:42:56 -0700393 self.aborted = False
394 self.canceled = False
395 self.created = time.time()
James E. Blairb0fcae42012-07-17 11:12:10 -0700396
397 def release(self):
398 self.wait_condition.acquire()
399 self.wait_condition.notify()
400 self.waiting = False
401 self.log.debug("Job %s released" % (self.parameters['UUID']))
402 self.wait_condition.release()
403
404 def isWaiting(self):
405 self.wait_condition.acquire()
406 if self.waiting:
407 ret = True
408 else:
409 ret = False
410 self.wait_condition.release()
411 return ret
412
413 def _wait(self):
414 self.wait_condition.acquire()
415 self.waiting = True
416 self.log.debug("Job %s waiting" % (self.parameters['UUID']))
417 self.wait_condition.wait()
418 self.wait_condition.release()
419
420 def run(self):
421 self.jenkins.fakeEnqueue(self)
422 if self.jenkins.hold_jobs_in_queue:
423 self._wait()
424 self.jenkins.fakeDequeue(self)
James E. Blaird466dc42012-07-31 10:42:56 -0700425 if self.canceled:
426 self.jenkins.all_jobs.remove(self)
427 return
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800428 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
429 self.number,
430 self.parameters,
431 'STARTED'))
James E. Blairb0fcae42012-07-17 11:12:10 -0700432 if self.jenkins.hold_jobs_in_build:
433 self._wait()
434 self.log.debug("Job %s continuing" % (self.parameters['UUID']))
James E. Blairb02a3bb2012-07-30 17:49:55 -0700435
436 result = 'SUCCESS'
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800437 if (('ZUUL_REF' in self.parameters) and
438 self.jenkins.fakeShouldFailTest(self.name,
439 self.parameters['ZUUL_REF'])):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700440 result = 'FAILURE'
James E. Blaird466dc42012-07-31 10:42:56 -0700441 if self.aborted:
442 result = 'ABORTED'
James E. Blairb02a3bb2012-07-30 17:49:55 -0700443
James E. Blairb0fcae42012-07-17 11:12:10 -0700444 self.jenkins.fakeAddHistory(name=self.name, number=self.number,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700445 result=result)
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800446 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
447 self.number,
448 self.parameters,
449 'COMPLETED',
450 result))
451 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
452 self.number,
453 self.parameters,
454 'FINISHED',
455 result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700456 self.jenkins.all_jobs.remove(self)
457
458
459class FakeJenkins(object):
460 log = logging.getLogger("zuul.test")
461
462 def __init__(self, *args, **kw):
463 self.queue = []
464 self.all_jobs = []
465 self.job_counter = {}
James E. Blaird466dc42012-07-31 10:42:56 -0700466 self.queue_counter = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700467 self.job_history = []
468 self.hold_jobs_in_queue = False
469 self.hold_jobs_in_build = False
James E. Blairb02a3bb2012-07-30 17:49:55 -0700470 self.fail_tests = {}
James E. Blair7ee88a22012-09-12 18:59:31 +0200471 self.nonexistent_jobs = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700472
473 def fakeEnqueue(self, job):
474 self.queue.append(job)
475
476 def fakeDequeue(self, job):
477 self.queue.remove(job)
478
479 def fakeAddHistory(self, **kw):
480 self.job_history.append(kw)
481
482 def fakeRelease(self, regex=None):
483 all_jobs = self.all_jobs[:]
484 self.log.debug("releasing jobs %s (%s)" % (regex, len(self.all_jobs)))
485 for job in all_jobs:
486 if not regex or re.match(regex, job.name):
487 self.log.debug("releasing job %s" % (job.parameters['UUID']))
488 job.release()
489 else:
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800490 self.log.debug("not releasing job %s" %
491 (job.parameters['UUID']))
James E. Blairb0fcae42012-07-17 11:12:10 -0700492 self.log.debug("done releasing jobs %s (%s)" % (regex,
493 len(self.all_jobs)))
494
495 def fakeAllWaiting(self, regex=None):
James E. Blair4aa1ad62012-10-05 12:39:26 -0700496 all_jobs = self.all_jobs[:] + self.queue[:]
James E. Blairb0fcae42012-07-17 11:12:10 -0700497 for job in all_jobs:
498 self.log.debug("job %s %s" % (job.parameters['UUID'],
499 job.isWaiting()))
500 if not job.isWaiting():
501 return False
502 return True
503
James E. Blairb02a3bb2012-07-30 17:49:55 -0700504 def fakeAddFailTest(self, name, change):
505 l = self.fail_tests.get(name, [])
506 l.append(change)
507 self.fail_tests[name] = l
508
James E. Blair4886cc12012-07-18 15:39:41 -0700509 def fakeShouldFailTest(self, name, ref):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700510 l = self.fail_tests.get(name, [])
511 for change in l:
James E. Blair4886cc12012-07-18 15:39:41 -0700512 if ref_has_change(ref, change):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700513 return True
514 return False
515
James E. Blairb0fcae42012-07-17 11:12:10 -0700516 def build_job(self, name, parameters):
James E. Blair7ee88a22012-09-12 18:59:31 +0200517 if name in self.nonexistent_jobs:
518 raise Exception("Job does not exist")
James E. Blairb0fcae42012-07-17 11:12:10 -0700519 count = self.job_counter.get(name, 0)
520 count += 1
521 self.job_counter[name] = count
James E. Blaird466dc42012-07-31 10:42:56 -0700522
523 queue_count = self.queue_counter
524 self.queue_counter += 1
James E. Blairb0fcae42012-07-17 11:12:10 -0700525 job = FakeJenkinsJob(self, self.callback, name, count, parameters)
James E. Blaird466dc42012-07-31 10:42:56 -0700526 job.queue_id = queue_count
527
James E. Blairb0fcae42012-07-17 11:12:10 -0700528 self.all_jobs.append(job)
529 job.start()
530
James E. Blaird466dc42012-07-31 10:42:56 -0700531 def stop_build(self, name, number):
532 for job in self.all_jobs:
533 if job.name == name and job.number == number:
534 job.aborted = True
535 job.release()
536 return
537
538 def cancel_queue(self, id):
539 for job in self.queue:
540 if job.queue_id == id:
541 job.canceled = True
542 job.release()
543 return
544
545 def get_queue_info(self):
546 items = []
547 for job in self.queue:
548 paramstr = ''
549 paramlst = []
550 d = {'actions': [{'parameters': paramlst},
551 {'causes': [{'shortDescription':
552 'Started by user Jenkins',
553 'userId': 'jenkins',
554 'userName': 'Jenkins'}]}],
555 'blocked': False,
556 'buildable': True,
557 'buildableStartMilliseconds': (job.created * 1000) + 5,
558 'id': job.queue_id,
559 'inQueueSince': (job.created * 1000),
560 'params': paramstr,
561 'stuck': False,
562 'task': {'color': 'blue',
563 'name': job.name,
564 'url': 'https://server/job/%s/' % job.name},
565 'why': 'Waiting for next available executor'}
566 for k, v in job.parameters.items():
567 paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
568 pd = {'name': k, 'value': v}
569 paramlst.append(pd)
570 items.append(d)
571 return items
572
James E. Blairb0fcae42012-07-17 11:12:10 -0700573 def set_build_description(self, *args, **kw):
574 pass
575
576
577class FakeJenkinsCallback(zuul.launcher.jenkins.JenkinsCallback):
578 def start(self):
579 pass
580
581
James E. Blair8cc15a82012-08-01 11:17:57 -0700582class FakeURLOpener(object):
583 def __init__(self, fake_gerrit, url):
584 self.fake_gerrit = fake_gerrit
585 self.url = url
586
587 def read(self):
588 res = urlparse.urlparse(self.url)
589 path = res.path
590 project = '/'.join(path.split('/')[2:-2])
James E. Blair35956c52012-09-17 22:13:36 +0200591 ret = '001e# service=git-upload-pack\n'
592 ret += ('000000a31270149696713ba7e06f1beb760f20d359c4abed HEAD\x00'
593 'multi_ack thin-pack side-band side-band-64k ofs-delta '
594 'shallow no-progress include-tag multi_ack_detailed no-done\n')
James E. Blair1dbd5082012-08-23 15:12:15 -0700595 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blairdaabed22012-08-15 15:38:57 -0700596 repo = git.Repo(path)
597 for ref in repo.refs:
James E. Blair35956c52012-09-17 22:13:36 +0200598 r = ref.object.hexsha + ' ' + ref.path + '\n'
599 ret += '%04x%s' % (len(r) + 4, r)
600 ret += '0000'
James E. Blair8cc15a82012-08-01 11:17:57 -0700601 return ret
602
603
James E. Blair4886cc12012-07-18 15:39:41 -0700604class FakeGerritTrigger(zuul.trigger.gerrit.Gerrit):
605 def getGitUrl(self, project):
James E. Blair1dbd5082012-08-23 15:12:15 -0700606 return os.path.join(UPSTREAM_ROOT, project.name)
James E. Blair4886cc12012-07-18 15:39:41 -0700607
608
James E. Blairb0fcae42012-07-17 11:12:10 -0700609class testScheduler(unittest.TestCase):
610 log = logging.getLogger("zuul.test")
611
612 def setUp(self):
James E. Blair1dbd5082012-08-23 15:12:15 -0700613 if os.path.exists(TEST_ROOT):
614 shutil.rmtree(TEST_ROOT)
615 os.makedirs(TEST_ROOT)
616 os.makedirs(UPSTREAM_ROOT)
617 os.makedirs(GIT_ROOT)
James E. Blair4886cc12012-07-18 15:39:41 -0700618
619 # For each project in config:
620 init_repo("org/project")
621 init_repo("org/project1")
622 init_repo("org/project2")
James E. Blair127bc182012-08-28 15:55:15 -0700623 init_repo("org/project3")
James E. Blair7f71c802012-08-22 13:04:32 -0700624 init_repo("org/one-job-project")
James E. Blair4ec821f2012-08-23 15:28:28 -0700625 init_repo("org/nonvoting-project")
James E. Blairb0fcae42012-07-17 11:12:10 -0700626 self.config = CONFIG
627 self.sched = zuul.scheduler.Scheduler()
628
629 def jenkinsFactory(*args, **kw):
630 self.fake_jenkins = FakeJenkins()
631 return self.fake_jenkins
632
633 def jenkinsCallbackFactory(*args, **kw):
634 self.fake_jenkins_callback = FakeJenkinsCallback(*args, **kw)
635 return self.fake_jenkins_callback
636
James E. Blair8cc15a82012-08-01 11:17:57 -0700637 def URLOpenerFactory(*args, **kw):
638 args = [self.fake_gerrit] + list(args)
639 return FakeURLOpener(*args, **kw)
640
James E. Blairb0fcae42012-07-17 11:12:10 -0700641 zuul.launcher.jenkins.ExtendedJenkins = jenkinsFactory
642 zuul.launcher.jenkins.JenkinsCallback = jenkinsCallbackFactory
James E. Blair8cc15a82012-08-01 11:17:57 -0700643 urllib2.urlopen = URLOpenerFactory
James E. Blairb0fcae42012-07-17 11:12:10 -0700644 self.jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
645 self.fake_jenkins.callback = self.fake_jenkins_callback
646
647 zuul.lib.gerrit.Gerrit = FakeGerrit
648
James E. Blair4886cc12012-07-18 15:39:41 -0700649 self.gerrit = FakeGerritTrigger(self.config, self.sched)
James E. Blair8cc15a82012-08-01 11:17:57 -0700650 self.gerrit.replication_timeout = 1.5
651 self.gerrit.replication_retry_interval = 0.5
James E. Blairb0fcae42012-07-17 11:12:10 -0700652 self.fake_gerrit = self.gerrit.gerrit
653
654 self.sched.setLauncher(self.jenkins)
655 self.sched.setTrigger(self.gerrit)
656
657 self.sched.start()
658 self.sched.reconfigure(self.config)
659 self.sched.resume()
660
661 def tearDown(self):
662 self.jenkins.stop()
663 self.gerrit.stop()
664 self.sched.stop()
665 self.sched.join()
James E. Blair1dbd5082012-08-23 15:12:15 -0700666 #shutil.rmtree(TEST_ROOT)
James E. Blairb0fcae42012-07-17 11:12:10 -0700667
668 def waitUntilSettled(self):
669 self.log.debug("Waiting until settled...")
670 start = time.time()
671 while True:
672 if time.time() - start > 10:
673 print 'queue status:',
674 print self.sched.trigger_event_queue.empty(),
675 print self.sched.result_event_queue.empty(),
676 print self.fake_gerrit.event_queue.empty(),
677 raise Exception("Timeout waiting for Zuul to settle")
678 self.fake_gerrit.event_queue.join()
679 self.sched.queue_lock.acquire()
680 if (self.sched.trigger_event_queue.empty() and
681 self.sched.result_event_queue.empty() and
682 self.fake_gerrit.event_queue.empty() and
683 self.fake_jenkins.fakeAllWaiting()):
684 self.sched.queue_lock.release()
685 self.log.debug("...settled.")
686 return
687 self.sched.queue_lock.release()
688 self.sched.wake_event.wait(0.1)
689
James E. Blaird466dc42012-07-31 10:42:56 -0700690 def countJobResults(self, jobs, result):
691 jobs = filter(lambda x: x['result'] == result, jobs)
692 return len(jobs)
693
James E. Blaire0487072012-08-29 17:38:31 -0700694 def assertEmptyQueues(self):
695 # Make sure there are no orphaned jobs
696 for pipeline in self.sched.pipelines.values():
697 for queue in pipeline.queues:
698 if len(queue.queue) != 0:
699 print 'queue', queue.queue
700 assert len(queue.queue) == 0
701 if len(queue.severed_heads) != 0:
702 print 'heads', queue.severed_heads
703 assert len(queue.severed_heads) == 0
704
James E. Blairb0fcae42012-07-17 11:12:10 -0700705 def test_jobs_launched(self):
706 "Test that jobs are launched and a change is merged"
707 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair8c803f82012-07-31 16:25:42 -0700708 A.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700709 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
710 self.waitUntilSettled()
711 jobs = self.fake_jenkins.job_history
712 job_names = [x['name'] for x in jobs]
713 assert 'project-merge' in job_names
714 assert 'project-test1' in job_names
715 assert 'project-test2' in job_names
716 assert jobs[0]['result'] == 'SUCCESS'
717 assert jobs[1]['result'] == 'SUCCESS'
718 assert jobs[2]['result'] == 'SUCCESS'
719 assert A.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700720 assert A.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700721 self.assertEmptyQueues()
James E. Blairb0fcae42012-07-17 11:12:10 -0700722
723 def test_parallel_changes(self):
724 "Test that changes are tested in parallel and merged in series"
725 self.fake_jenkins.hold_jobs_in_build = True
726 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
727 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
728 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700729 A.addApproval('CRVW', 2)
730 B.addApproval('CRVW', 2)
731 C.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700732
733 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
734 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
735 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
736
737 self.waitUntilSettled()
738 jobs = self.fake_jenkins.all_jobs
739 assert len(jobs) == 1
740 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700741 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700742
743 self.fake_jenkins.fakeRelease('.*-merge')
744 self.waitUntilSettled()
745 assert len(jobs) == 3
746 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700747 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700748 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700749 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700750 assert jobs[2].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700751 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700752
753 self.fake_jenkins.fakeRelease('.*-merge')
754 self.waitUntilSettled()
755 assert len(jobs) == 5
756 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700757 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700758 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700759 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700760
761 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700762 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700763 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700764 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700765
766 assert jobs[4].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700767 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700768
769 self.fake_jenkins.fakeRelease('.*-merge')
770 self.waitUntilSettled()
771 assert len(jobs) == 6
772 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700773 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700774 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700775 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700776
777 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700778 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700779 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700780 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700781
782 assert jobs[4].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700783 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700784 assert jobs[5].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700785 assert job_has_changes(jobs[5], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700786
787 self.fake_jenkins.hold_jobs_in_build = False
788 self.fake_jenkins.fakeRelease()
789 self.waitUntilSettled()
790 assert len(jobs) == 0
791
792 jobs = self.fake_jenkins.job_history
793 assert len(jobs) == 9
794 assert A.data['status'] == 'MERGED'
795 assert B.data['status'] == 'MERGED'
796 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700797 assert A.reported == 2
798 assert B.reported == 2
799 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700800 self.assertEmptyQueues()
James E. Blairb02a3bb2012-07-30 17:49:55 -0700801
802 def test_failed_changes(self):
803 "Test that a change behind a failed change is retested"
804 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
805 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
James E. Blair8c803f82012-07-31 16:25:42 -0700806 A.addApproval('CRVW', 2)
807 B.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700808
809 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
810 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
811
James E. Blair4886cc12012-07-18 15:39:41 -0700812 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700813
814 self.waitUntilSettled()
815 jobs = self.fake_jenkins.job_history
816 assert len(jobs) > 6
817 assert A.data['status'] == 'NEW'
818 assert B.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700819 assert A.reported == 2
820 assert B.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700821 self.assertEmptyQueues()
James E. Blairb02a3bb2012-07-30 17:49:55 -0700822
823 def test_independent_queues(self):
824 "Test that changes end up in the right queues"
825 self.fake_jenkins.hold_jobs_in_build = True
Zhongyue Luo5d556072012-09-21 02:00:47 +0900826 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blairb02a3bb2012-07-30 17:49:55 -0700827 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
828 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700829 A.addApproval('CRVW', 2)
830 B.addApproval('CRVW', 2)
831 C.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700832
833 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
834 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
835 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
836
837 jobs = self.fake_jenkins.all_jobs
838 self.waitUntilSettled()
839
840 # There should be one merge job at the head of each queue running
841 assert len(jobs) == 2
842 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700843 assert job_has_changes(jobs[0], A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700844 assert jobs[1].name == 'project1-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700845 assert job_has_changes(jobs[1], B)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700846
847 # Release the current merge jobs
848 self.fake_jenkins.fakeRelease('.*-merge')
849 self.waitUntilSettled()
850 # Release the merge job for project2 which is behind project1
851 self.fake_jenkins.fakeRelease('.*-merge')
852 self.waitUntilSettled()
853
854 # All the test jobs should be running:
855 # project1 (3) + project2 (3) + project (2) = 8
856 assert len(jobs) == 8
857
858 self.fake_jenkins.fakeRelease()
859 self.waitUntilSettled()
860 assert len(jobs) == 0
861
862 jobs = self.fake_jenkins.job_history
863 assert len(jobs) == 11
864 assert A.data['status'] == 'MERGED'
865 assert B.data['status'] == 'MERGED'
866 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700867 assert A.reported == 2
868 assert B.reported == 2
869 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700870 self.assertEmptyQueues()
James E. Blaird466dc42012-07-31 10:42:56 -0700871
872 def test_failed_change_at_head(self):
873 "Test that if a change at the head fails, jobs behind it are canceled"
874 self.fake_jenkins.hold_jobs_in_build = True
875
876 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
877 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
878 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700879 A.addApproval('CRVW', 2)
880 B.addApproval('CRVW', 2)
881 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700882
James E. Blair4886cc12012-07-18 15:39:41 -0700883 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700884
885 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
886 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
887 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
888
889 self.waitUntilSettled()
890 jobs = self.fake_jenkins.all_jobs
891 finished_jobs = self.fake_jenkins.job_history
892
893 assert len(jobs) == 1
894 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700895 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700896
897 self.fake_jenkins.fakeRelease('.*-merge')
898 self.waitUntilSettled()
899 self.fake_jenkins.fakeRelease('.*-merge')
900 self.waitUntilSettled()
901 self.fake_jenkins.fakeRelease('.*-merge')
902 self.waitUntilSettled()
903
904 assert len(jobs) == 6
905 assert jobs[0].name == 'project-test1'
906 assert jobs[1].name == 'project-test2'
907 assert jobs[2].name == 'project-test1'
908 assert jobs[3].name == 'project-test2'
909 assert jobs[4].name == 'project-test1'
910 assert jobs[5].name == 'project-test2'
911
912 jobs[0].release()
913 self.waitUntilSettled()
914
James E. Blairec590122012-08-22 15:19:31 -0700915 assert len(jobs) == 2 # project-test2, project-merge for B
James E. Blaird466dc42012-07-31 10:42:56 -0700916 assert self.countJobResults(finished_jobs, 'ABORTED') == 4
917
918 self.fake_jenkins.hold_jobs_in_build = False
919 self.fake_jenkins.fakeRelease()
920 self.waitUntilSettled()
921
922 assert len(jobs) == 0
923 assert len(finished_jobs) == 15
924 assert A.data['status'] == 'NEW'
925 assert B.data['status'] == 'MERGED'
926 assert C.data['status'] == 'MERGED'
927 assert A.reported == 2
928 assert B.reported == 2
929 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700930 self.assertEmptyQueues()
James E. Blaird466dc42012-07-31 10:42:56 -0700931
932 def test_failed_change_at_head_with_queue(self):
933 "Test that if a change at the head fails, queued jobs are canceled"
934 self.fake_jenkins.hold_jobs_in_queue = True
935
936 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
937 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
938 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700939 A.addApproval('CRVW', 2)
940 B.addApproval('CRVW', 2)
941 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700942
James E. Blair4886cc12012-07-18 15:39:41 -0700943 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700944
945 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
946 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
947 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
948
949 self.waitUntilSettled()
950 jobs = self.fake_jenkins.all_jobs
951 finished_jobs = self.fake_jenkins.job_history
952 queue = self.fake_jenkins.queue
953
954 assert len(jobs) == 1
955 assert len(queue) == 1
956 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700957 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700958
959 self.fake_jenkins.fakeRelease('.*-merge')
960 self.waitUntilSettled()
961 self.fake_jenkins.fakeRelease('.*-merge')
962 self.waitUntilSettled()
963 self.fake_jenkins.fakeRelease('.*-merge')
964 self.waitUntilSettled()
965
966 assert len(jobs) == 6
967 assert len(queue) == 6
968 assert jobs[0].name == 'project-test1'
969 assert jobs[1].name == 'project-test2'
970 assert jobs[2].name == 'project-test1'
971 assert jobs[3].name == 'project-test2'
972 assert jobs[4].name == 'project-test1'
973 assert jobs[5].name == 'project-test2'
974
975 jobs[0].release()
976 self.waitUntilSettled()
977
James E. Blairec590122012-08-22 15:19:31 -0700978 assert len(jobs) == 2 # project-test2, project-merge for B
979 assert len(queue) == 2
James E. Blaird466dc42012-07-31 10:42:56 -0700980 assert self.countJobResults(finished_jobs, 'ABORTED') == 0
981
982 self.fake_jenkins.hold_jobs_in_queue = False
983 self.fake_jenkins.fakeRelease()
984 self.waitUntilSettled()
985
986 assert len(jobs) == 0
987 assert len(finished_jobs) == 11
988 assert A.data['status'] == 'NEW'
989 assert B.data['status'] == 'MERGED'
990 assert C.data['status'] == 'MERGED'
991 assert A.reported == 2
992 assert B.reported == 2
993 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700994 self.assertEmptyQueues()
James E. Blair8c803f82012-07-31 16:25:42 -0700995
996 def test_patch_order(self):
997 "Test that dependent patches are tested in the right order"
998 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
999 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1000 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1001 A.addApproval('CRVW', 2)
1002 B.addApproval('CRVW', 2)
1003 C.addApproval('CRVW', 2)
1004
1005 M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
1006 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1007 M2.setMerged()
1008 M1.setMerged()
1009
1010 # C -> B -> A -> M1 -> M2
1011 # M2 is here to make sure it is never queried. If it is, it
1012 # means zuul is walking down the entire history of merged
1013 # changes.
1014
1015 C.setDependsOn(B, 1)
1016 B.setDependsOn(A, 1)
1017 A.setDependsOn(M1, 1)
1018 M1.setDependsOn(M2, 1)
1019
1020 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1021
1022 self.waitUntilSettled()
1023
1024 assert A.data['status'] == 'NEW'
1025 assert B.data['status'] == 'NEW'
1026 assert C.data['status'] == 'NEW'
1027
1028 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1029 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1030
1031 self.waitUntilSettled()
1032 assert M2.queried == 0
1033 assert A.data['status'] == 'MERGED'
1034 assert B.data['status'] == 'MERGED'
1035 assert C.data['status'] == 'MERGED'
1036 assert A.reported == 2
1037 assert B.reported == 2
1038 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001039 self.assertEmptyQueues()
James E. Blair8c803f82012-07-31 16:25:42 -07001040
1041 def test_can_merge(self):
James E. Blair4886cc12012-07-18 15:39:41 -07001042 "Test whether a change is ready to merge"
James E. Blair8c803f82012-07-31 16:25:42 -07001043 # TODO: move to test_gerrit (this is a unit test!)
1044 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair4aea70c2012-07-26 14:23:24 -07001045 a = self.sched.trigger.getChange(1, 2)
1046 mgr = self.sched.pipelines['gate'].manager
1047 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001048
1049 A.addApproval('CRVW', 2)
James E. Blair4aea70c2012-07-26 14:23:24 -07001050 a = self.sched.trigger.getChange(1, 2)
1051 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001052
1053 A.addApproval('APRV', 1)
James E. Blair4aea70c2012-07-26 14:23:24 -07001054 a = self.sched.trigger.getChange(1, 2)
1055 assert self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blaire0487072012-08-29 17:38:31 -07001056 self.assertEmptyQueues()
James E. Blair4886cc12012-07-18 15:39:41 -07001057
1058 def test_build_configuration(self):
1059 "Test that zuul merges the right commits for testing"
1060 self.fake_jenkins.hold_jobs_in_queue = True
1061 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1062 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1063 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1064 A.addApproval('CRVW', 2)
1065 B.addApproval('CRVW', 2)
1066 C.addApproval('CRVW', 2)
1067 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1068 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1069 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1070 self.waitUntilSettled()
1071
1072 jobs = self.fake_jenkins.all_jobs
1073
1074 self.fake_jenkins.fakeRelease('.*-merge')
1075 self.waitUntilSettled()
1076 self.fake_jenkins.fakeRelease('.*-merge')
1077 self.waitUntilSettled()
1078 self.fake_jenkins.fakeRelease('.*-merge')
1079 self.waitUntilSettled()
James E. Blair1dbd5082012-08-23 15:12:15 -07001080
James E. Blair4886cc12012-07-18 15:39:41 -07001081 ref = jobs[-1].parameters['ZUUL_REF']
1082 self.fake_jenkins.hold_jobs_in_queue = False
1083 self.fake_jenkins.fakeRelease()
James E. Blair973721f2012-08-15 10:19:43 -07001084 self.waitUntilSettled()
James E. Blair4886cc12012-07-18 15:39:41 -07001085
James E. Blair1dbd5082012-08-23 15:12:15 -07001086 path = os.path.join(GIT_ROOT, "org/project")
James E. Blair4886cc12012-07-18 15:39:41 -07001087 repo = git.Repo(path)
1088 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1089 repo_messages.reverse()
James E. Blair4886cc12012-07-18 15:39:41 -07001090 correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
1091 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001092 self.assertEmptyQueues()
James E. Blair973721f2012-08-15 10:19:43 -07001093
1094 def test_build_configuration_conflict(self):
1095 "Test that merge conflicts are handled"
1096 self.fake_jenkins.hold_jobs_in_queue = True
1097 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1098 A.addPatchset(['conflict'])
1099 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1100 B.addPatchset(['conflict'])
1101 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1102 A.addApproval('CRVW', 2)
1103 B.addApproval('CRVW', 2)
1104 C.addApproval('CRVW', 2)
1105 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1106 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1107 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1108 self.waitUntilSettled()
1109
1110 jobs = self.fake_jenkins.all_jobs
1111
1112 self.fake_jenkins.fakeRelease('.*-merge')
1113 self.waitUntilSettled()
1114 self.fake_jenkins.fakeRelease('.*-merge')
1115 self.waitUntilSettled()
1116 self.fake_jenkins.fakeRelease('.*-merge')
1117 self.waitUntilSettled()
1118 ref = jobs[-1].parameters['ZUUL_REF']
1119 self.fake_jenkins.hold_jobs_in_queue = False
1120 self.fake_jenkins.fakeRelease()
1121 self.waitUntilSettled()
1122
1123 assert A.data['status'] == 'MERGED'
1124 assert B.data['status'] == 'NEW'
1125 assert C.data['status'] == 'MERGED'
1126 assert A.reported == 2
1127 assert B.reported == 2
1128 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001129 self.assertEmptyQueues()
James E. Blairdaabed22012-08-15 15:38:57 -07001130
1131 def test_post(self):
1132 "Test that post jobs run"
Zhongyue Luo5d556072012-09-21 02:00:47 +09001133 e = {
1134 "type": "ref-updated",
1135 "submitter": {
1136 "name": "User Name",
1137 },
1138 "refUpdate": {
1139 "oldRev": "90f173846e3af9154517b88543ffbd1691f31366",
1140 "newRev": "d479a0bfcb34da57a31adb2a595c0cf687812543",
1141 "refName": "master",
1142 "project": "org/project",
1143 }
1144 }
James E. Blairdaabed22012-08-15 15:38:57 -07001145 self.fake_gerrit.addEvent(e)
1146 self.waitUntilSettled()
1147
1148 jobs = self.fake_jenkins.job_history
1149 job_names = [x['name'] for x in jobs]
1150 assert len(jobs) == 1
1151 assert 'project-post' in job_names
James E. Blaire0487072012-08-29 17:38:31 -07001152 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001153
1154 def test_build_configuration_branch(self):
1155 "Test that the right commits are on alternate branches"
1156 self.fake_jenkins.hold_jobs_in_queue = True
1157 A = self.fake_gerrit.addFakeChange('org/project', 'mp', 'A')
1158 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1159 C = self.fake_gerrit.addFakeChange('org/project', 'mp', 'C')
1160 A.addApproval('CRVW', 2)
1161 B.addApproval('CRVW', 2)
1162 C.addApproval('CRVW', 2)
1163 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1164 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1165 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1166 self.waitUntilSettled()
1167
1168 jobs = self.fake_jenkins.all_jobs
1169
1170 self.fake_jenkins.fakeRelease('.*-merge')
1171 self.waitUntilSettled()
1172 self.fake_jenkins.fakeRelease('.*-merge')
1173 self.waitUntilSettled()
1174 self.fake_jenkins.fakeRelease('.*-merge')
1175 self.waitUntilSettled()
1176 ref = jobs[-1].parameters['ZUUL_REF']
1177 self.fake_jenkins.hold_jobs_in_queue = False
1178 self.fake_jenkins.fakeRelease()
1179 self.waitUntilSettled()
1180
James E. Blair1dbd5082012-08-23 15:12:15 -07001181 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001182 repo = git.Repo(path)
1183 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1184 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001185 correct_messages = ['initial commit', 'mp commit', 'A-1', 'B-1', 'C-1']
1186 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001187 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001188
1189 def test_build_configuration_branch_interaction(self):
1190 "Test that switching between branches works"
1191 self.test_build_configuration()
1192 self.test_build_configuration_branch()
1193 # C has been merged, undo that
James E. Blair1dbd5082012-08-23 15:12:15 -07001194 path = os.path.join(UPSTREAM_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001195 repo = git.Repo(path)
1196 repo.heads.master.commit = repo.commit('init')
1197 self.test_build_configuration()
James E. Blaire0487072012-08-29 17:38:31 -07001198 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001199
1200 def test_build_configuration_multi_branch(self):
1201 "Test that dependent changes on multiple branches are merged"
1202 self.fake_jenkins.hold_jobs_in_queue = True
1203 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1204 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1205 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1206 A.addApproval('CRVW', 2)
1207 B.addApproval('CRVW', 2)
1208 C.addApproval('CRVW', 2)
1209 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1210 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1211 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1212 self.waitUntilSettled()
1213
1214 jobs = self.fake_jenkins.all_jobs
1215
1216 self.fake_jenkins.fakeRelease('.*-merge')
1217 self.waitUntilSettled()
1218 ref_mp = jobs[-1].parameters['ZUUL_REF']
1219 self.fake_jenkins.fakeRelease('.*-merge')
1220 self.waitUntilSettled()
1221 self.fake_jenkins.fakeRelease('.*-merge')
1222 self.waitUntilSettled()
1223 ref_master = jobs[-1].parameters['ZUUL_REF']
1224 self.fake_jenkins.hold_jobs_in_queue = False
1225 self.fake_jenkins.fakeRelease()
1226 self.waitUntilSettled()
1227
James E. Blair1dbd5082012-08-23 15:12:15 -07001228 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001229 repo = git.Repo(path)
1230
1231 repo_messages = [c.message.strip()
1232 for c in repo.iter_commits(ref_master)]
1233 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001234 correct_messages = ['initial commit', 'A-1', 'C-1']
1235 assert repo_messages == correct_messages
1236
1237 repo_messages = [c.message.strip()
1238 for c in repo.iter_commits(ref_mp)]
1239 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001240 correct_messages = ['initial commit', 'mp commit', 'B-1']
1241 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001242 self.assertEmptyQueues()
James E. Blair7f71c802012-08-22 13:04:32 -07001243
1244 def test_one_job_project(self):
1245 "Test that queueing works with one job"
1246 A = self.fake_gerrit.addFakeChange('org/one-job-project',
1247 'master', 'A')
1248 B = self.fake_gerrit.addFakeChange('org/one-job-project',
1249 'master', 'B')
1250 A.addApproval('CRVW', 2)
1251 B.addApproval('CRVW', 2)
1252 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1253 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1254 self.waitUntilSettled()
1255
1256 jobs = self.fake_jenkins.all_jobs
1257 finished_jobs = self.fake_jenkins.job_history
James E. Blair7f71c802012-08-22 13:04:32 -07001258
1259 assert A.data['status'] == 'MERGED'
1260 assert A.reported == 2
1261 assert B.data['status'] == 'MERGED'
1262 assert B.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001263 self.assertEmptyQueues()
James E. Blaircaec0c52012-08-22 14:52:22 -07001264
1265 def test_dependent_changes_dequeue(self):
1266 "Test that dependent patches are not needlessly tested"
1267 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1268 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1269 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1270 A.addApproval('CRVW', 2)
1271 B.addApproval('CRVW', 2)
1272 C.addApproval('CRVW', 2)
1273
1274 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1275 M1.setMerged()
1276
1277 # C -> B -> A -> M1
1278
1279 C.setDependsOn(B, 1)
1280 B.setDependsOn(A, 1)
1281 A.setDependsOn(M1, 1)
1282
1283 self.fake_jenkins.fakeAddFailTest('project-merge', A)
1284
1285 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1286 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1287 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1288
1289 self.waitUntilSettled()
1290
1291 jobs = self.fake_jenkins.all_jobs
1292 finished_jobs = self.fake_jenkins.job_history
1293
James E. Blair127bc182012-08-28 15:55:15 -07001294 for x in jobs:
1295 print x
1296 for x in finished_jobs:
1297 print x
1298
James E. Blairec590122012-08-22 15:19:31 -07001299 assert A.data['status'] == 'NEW'
1300 assert A.reported == 2
1301 assert B.data['status'] == 'NEW'
1302 assert B.reported == 2
1303 assert C.data['status'] == 'NEW'
1304 assert C.reported == 2
1305 assert len(finished_jobs) == 1
James E. Blaire0487072012-08-29 17:38:31 -07001306 self.assertEmptyQueues()
James E. Blairec590122012-08-22 15:19:31 -07001307
1308 def test_head_is_dequeued_once(self):
1309 "Test that if a change at the head fails it is dequeud only once"
1310 # If it's dequeued more than once, we should see extra
1311 # aborted jobs.
1312 self.fake_jenkins.hold_jobs_in_build = True
1313
1314 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1315 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1316 C = self.fake_gerrit.addFakeChange('org/project1', 'master', 'C')
1317 A.addApproval('CRVW', 2)
1318 B.addApproval('CRVW', 2)
1319 C.addApproval('CRVW', 2)
1320
1321 self.fake_jenkins.fakeAddFailTest('project1-test1', A)
1322 self.fake_jenkins.fakeAddFailTest('project1-test2', A)
1323 self.fake_jenkins.fakeAddFailTest('project1-project2-integration', A)
1324
1325 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1326 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1327 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1328
1329 self.waitUntilSettled()
1330 jobs = self.fake_jenkins.all_jobs
1331 finished_jobs = self.fake_jenkins.job_history
1332
1333 assert len(jobs) == 1
1334 assert jobs[0].name == 'project1-merge'
1335 assert job_has_changes(jobs[0], A)
1336
1337 self.fake_jenkins.fakeRelease('.*-merge')
1338 self.waitUntilSettled()
1339 self.fake_jenkins.fakeRelease('.*-merge')
1340 self.waitUntilSettled()
1341 self.fake_jenkins.fakeRelease('.*-merge')
1342 self.waitUntilSettled()
1343
1344 assert len(jobs) == 9
1345 assert jobs[0].name == 'project1-test1'
1346 assert jobs[1].name == 'project1-test2'
1347 assert jobs[2].name == 'project1-project2-integration'
1348 assert jobs[3].name == 'project1-test1'
1349 assert jobs[4].name == 'project1-test2'
1350 assert jobs[5].name == 'project1-project2-integration'
1351 assert jobs[6].name == 'project1-test1'
1352 assert jobs[7].name == 'project1-test2'
1353 assert jobs[8].name == 'project1-project2-integration'
1354
1355 jobs[0].release()
1356 self.waitUntilSettled()
1357
1358 assert len(jobs) == 3 # test2, integration, merge for B
1359 assert self.countJobResults(finished_jobs, 'ABORTED') == 6
1360
1361 self.fake_jenkins.hold_jobs_in_build = False
1362 self.fake_jenkins.fakeRelease()
1363 self.waitUntilSettled()
1364
1365 assert len(jobs) == 0
1366 assert len(finished_jobs) == 20
James E. Blaircaec0c52012-08-22 14:52:22 -07001367
1368 assert A.data['status'] == 'NEW'
James E. Blairec590122012-08-22 15:19:31 -07001369 assert B.data['status'] == 'MERGED'
1370 assert C.data['status'] == 'MERGED'
1371 assert A.reported == 2
1372 assert B.reported == 2
1373 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001374 self.assertEmptyQueues()
James E. Blair4ec821f2012-08-23 15:28:28 -07001375
1376 def test_nonvoting_job(self):
1377 "Test that non-voting jobs don't vote."
1378 A = self.fake_gerrit.addFakeChange('org/nonvoting-project',
1379 'master', 'A')
1380 A.addApproval('CRVW', 2)
1381 self.fake_jenkins.fakeAddFailTest('nonvoting-project-test2', A)
1382 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1383
1384 self.waitUntilSettled()
1385 jobs = self.fake_jenkins.all_jobs
1386 finished_jobs = self.fake_jenkins.job_history
1387
1388 assert A.data['status'] == 'MERGED'
1389 assert A.reported == 2
1390 assert finished_jobs[0]['result'] == 'SUCCESS'
1391 assert finished_jobs[1]['result'] == 'SUCCESS'
1392 assert finished_jobs[2]['result'] == 'FAILURE'
James E. Blaire0487072012-08-29 17:38:31 -07001393 self.assertEmptyQueues()
1394
1395 def test_check_queue_success(self):
1396 "Test successful check queue jobs."
1397 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1398 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1399
1400 self.waitUntilSettled()
1401 jobs = self.fake_jenkins.all_jobs
1402 finished_jobs = self.fake_jenkins.job_history
1403
1404 assert A.data['status'] == 'NEW'
1405 assert A.reported == 1
1406 assert finished_jobs[0]['result'] == 'SUCCESS'
1407 assert finished_jobs[1]['result'] == 'SUCCESS'
1408 assert finished_jobs[2]['result'] == 'SUCCESS'
1409 self.assertEmptyQueues()
1410
1411 def test_check_queue_failure(self):
1412 "Test failed check queue jobs."
1413 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1414 self.fake_jenkins.fakeAddFailTest('project-test2', A)
1415 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1416
1417 self.waitUntilSettled()
1418 jobs = self.fake_jenkins.all_jobs
1419 finished_jobs = self.fake_jenkins.job_history
1420
1421 assert A.data['status'] == 'NEW'
1422 assert A.reported == 1
1423 assert finished_jobs[0]['result'] == 'SUCCESS'
1424 assert finished_jobs[1]['result'] == 'SUCCESS'
1425 assert finished_jobs[2]['result'] == 'FAILURE'
1426 self.assertEmptyQueues()
James E. Blair127bc182012-08-28 15:55:15 -07001427
1428 def test_dependent_behind_dequeue(self):
1429 "test that dependent changes behind dequeued changes work"
1430 # This complicated test is a reproduction of a real life bug
1431 self.sched.reconfigure(self.config)
1432 self.fake_jenkins.hold_jobs_in_build = True
1433
1434 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1435 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1436 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
1437 D = self.fake_gerrit.addFakeChange('org/project2', 'master', 'D')
1438 E = self.fake_gerrit.addFakeChange('org/project2', 'master', 'E')
1439 F = self.fake_gerrit.addFakeChange('org/project3', 'master', 'F')
1440 D.setDependsOn(C, 1)
1441 E.setDependsOn(D, 1)
1442 A.addApproval('CRVW', 2)
1443 B.addApproval('CRVW', 2)
1444 C.addApproval('CRVW', 2)
1445 D.addApproval('CRVW', 2)
1446 E.addApproval('CRVW', 2)
1447 F.addApproval('CRVW', 2)
1448
1449 A.fail_merge = True
1450 jobs = self.fake_jenkins.all_jobs
1451 finished_jobs = self.fake_jenkins.job_history
1452
1453 # Change object re-use in the gerrit trigger is hidden if
1454 # changes are added in quick succession; waiting makes it more
1455 # like real life.
1456 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1457 self.waitUntilSettled()
1458 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1459 self.waitUntilSettled()
1460
1461 self.fake_jenkins.fakeRelease('.*-merge')
1462 self.waitUntilSettled()
1463 self.fake_jenkins.fakeRelease('.*-merge')
1464 self.waitUntilSettled()
1465
1466 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1467 self.waitUntilSettled()
1468 self.fake_gerrit.addEvent(D.addApproval('APRV', 1))
1469 self.waitUntilSettled()
1470 self.fake_gerrit.addEvent(E.addApproval('APRV', 1))
1471 self.waitUntilSettled()
1472 self.fake_gerrit.addEvent(F.addApproval('APRV', 1))
1473 self.waitUntilSettled()
1474
1475 self.fake_jenkins.fakeRelease('.*-merge')
1476 self.waitUntilSettled()
1477 self.fake_jenkins.fakeRelease('.*-merge')
1478 self.waitUntilSettled()
1479 self.fake_jenkins.fakeRelease('.*-merge')
1480 self.waitUntilSettled()
1481 self.fake_jenkins.fakeRelease('.*-merge')
1482 self.waitUntilSettled()
1483
James E. Blair4aa1ad62012-10-05 12:39:26 -07001484 for x in jobs:
1485 print x
James E. Blair127bc182012-08-28 15:55:15 -07001486 # all jobs running
1487 jobs[0].release()
1488 jobs[1].release()
1489 jobs[2].release()
1490 self.waitUntilSettled()
1491
1492 self.fake_jenkins.hold_jobs_in_build = False
1493 self.fake_jenkins.fakeRelease()
1494 self.waitUntilSettled()
1495
1496 for x in jobs:
1497 print x
1498 for x in finished_jobs:
1499 print x
1500 print self.sched.formatStatusHTML()
1501
1502 assert A.data['status'] == 'NEW'
1503 assert B.data['status'] == 'MERGED'
1504 assert C.data['status'] == 'MERGED'
1505 assert D.data['status'] == 'MERGED'
1506 assert E.data['status'] == 'MERGED'
1507 assert F.data['status'] == 'MERGED'
1508
1509 assert A.reported == 2
1510 assert B.reported == 2
1511 assert C.reported == 2
1512 assert D.reported == 2
1513 assert E.reported == 2
1514 assert F.reported == 2
1515
James E. Blair127bc182012-08-28 15:55:15 -07001516 assert self.countJobResults(finished_jobs, 'ABORTED') == 15
1517 assert len(finished_jobs) == 44
James E. Blaire0487072012-08-29 17:38:31 -07001518 self.assertEmptyQueues()
James E. Blair05fed602012-09-07 12:45:24 -07001519
1520 def test_merger_repack(self):
1521 "Test that the merger works after a repack"
1522 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1523 A.addApproval('CRVW', 2)
1524 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1525 self.waitUntilSettled()
1526 jobs = self.fake_jenkins.job_history
1527 job_names = [x['name'] for x in jobs]
1528 assert 'project-merge' in job_names
1529 assert 'project-test1' in job_names
1530 assert 'project-test2' in job_names
1531 assert jobs[0]['result'] == 'SUCCESS'
1532 assert jobs[1]['result'] == 'SUCCESS'
1533 assert jobs[2]['result'] == 'SUCCESS'
1534 assert A.data['status'] == 'MERGED'
1535 assert A.reported == 2
1536 self.assertEmptyQueues()
1537
1538 path = os.path.join(GIT_ROOT, "org/project")
1539 os.system('git --git-dir=%s/.git repack -afd' % path)
1540
1541 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1542 A.addApproval('CRVW', 2)
1543 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1544 self.waitUntilSettled()
1545 jobs = self.fake_jenkins.job_history
1546 job_names = [x['name'] for x in jobs]
1547 assert 'project-merge' in job_names
1548 assert 'project-test1' in job_names
1549 assert 'project-test2' in job_names
1550 assert jobs[0]['result'] == 'SUCCESS'
1551 assert jobs[1]['result'] == 'SUCCESS'
1552 assert jobs[2]['result'] == 'SUCCESS'
1553 assert A.data['status'] == 'MERGED'
1554 assert A.reported == 2
1555 self.assertEmptyQueues()
James E. Blair7ee88a22012-09-12 18:59:31 +02001556
1557 def test_nonexistent_job(self):
1558 "Test launching a job that doesn't exist"
1559 self.fake_jenkins.nonexistent_jobs.append('project-merge')
1560 self.jenkins.launch_retry_timeout = 0.1
1561
1562 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1563 A.addApproval('CRVW', 2)
1564 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1565 # There may be a thread about to report a lost change
1566 while A.reported < 2:
1567 self.waitUntilSettled()
1568 jobs = self.fake_jenkins.job_history
1569 job_names = [x['name'] for x in jobs]
1570 assert not job_names
1571 assert A.data['status'] == 'NEW'
1572 assert A.reported == 2
1573 self.assertEmptyQueues()
1574
1575 # Make sure things still work:
1576 self.fake_jenkins.nonexistent_jobs = []
1577 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1578 A.addApproval('CRVW', 2)
1579 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1580 self.waitUntilSettled()
1581 jobs = self.fake_jenkins.job_history
1582 job_names = [x['name'] for x in jobs]
1583 assert 'project-merge' in job_names
1584 assert 'project-test1' in job_names
1585 assert 'project-test2' in job_names
1586 assert jobs[0]['result'] == 'SUCCESS'
1587 assert jobs[1]['result'] == 'SUCCESS'
1588 assert jobs[2]['result'] == 'SUCCESS'
1589 assert A.data['status'] == 'MERGED'
1590 assert A.reported == 2
1591 self.assertEmptyQueues()