blob: 9b621c4b38130dda0c17da18b497773d38528d03 [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']
James E. Blair81515ad2012-10-01 18:29:08 -0700131 sha = job.parameters['ZUUL_COMMIT']
James E. Blair4886cc12012-07-18 15:39:41 -0700132 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
James E. Blair81515ad2012-10-01 18:29:08 -0700133 repo_shas = [c.hexsha for c in repo.iter_commits(ref)]
James E. Blair4886cc12012-07-18 15:39:41 -0700134 commit_messages = ['%s-1' % commit.subject for commit in commits]
James E. Blair4886cc12012-07-18 15:39:41 -0700135 for msg in commit_messages:
136 if msg not in repo_messages:
137 return False
James E. Blair81515ad2012-10-01 18:29:08 -0700138 if repo_shas[0] != sha:
139 return False
James E. Blair4886cc12012-07-18 15:39:41 -0700140 return True
141
142
James E. Blairb0fcae42012-07-17 11:12:10 -0700143class FakeChange(object):
James E. Blair8c803f82012-07-31 16:25:42 -0700144 categories = {'APRV': ('Approved', -1, 1),
145 'CRVW': ('Code-Review', -2, 2),
146 'VRFY': ('Verified', -2, 2)}
James E. Blairb0fcae42012-07-17 11:12:10 -0700147
James E. Blair8cc15a82012-08-01 11:17:57 -0700148 def __init__(self, gerrit, number, project, branch, subject, status='NEW'):
149 self.gerrit = gerrit
James E. Blaird466dc42012-07-31 10:42:56 -0700150 self.reported = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700151 self.queried = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700152 self.patchsets = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700153 self.number = number
154 self.project = project
155 self.branch = branch
156 self.subject = subject
157 self.latest_patchset = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700158 self.depends_on_change = None
159 self.needed_by_changes = []
James E. Blair127bc182012-08-28 15:55:15 -0700160 self.fail_merge = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700161 self.data = {
162 'branch': branch,
163 'comments': [],
164 'commitMessage': subject,
165 'createdOn': time.time(),
James E. Blair8cc15a82012-08-01 11:17:57 -0700166 'id': 'I' + random_sha1(),
James E. Blairb0fcae42012-07-17 11:12:10 -0700167 'lastUpdated': time.time(),
168 'number': str(number),
169 'open': True,
170 'owner': {'email': 'user@example.com',
171 'name': 'User Name',
172 'username': 'username'},
173 'patchSets': self.patchsets,
174 'project': project,
175 'status': status,
176 'subject': subject,
James E. Blair8c803f82012-07-31 16:25:42 -0700177 'submitRecords': [],
James E. Blairb0fcae42012-07-17 11:12:10 -0700178 'url': 'https://hostname/%s' % number}
179
180 self.addPatchset()
James E. Blair8c803f82012-07-31 16:25:42 -0700181 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700182
183 def addPatchset(self, files=None):
184 self.latest_patchset += 1
James E. Blairdaabed22012-08-15 15:38:57 -0700185 if files:
186 fn = files[0]
187 else:
188 fn = '%s-%s' % (self.branch, self.number)
189 msg = self.subject + '-' + str(self.latest_patchset)
190 c = add_fake_change_to_repo(self.project, self.branch,
191 self.number, self.latest_patchset,
192 msg, fn)
James E. Blairb0fcae42012-07-17 11:12:10 -0700193 d = {'approvals': [],
194 'createdOn': time.time(),
195 'files': [{'file': '/COMMIT_MSG',
196 'type': 'ADDED'},
197 {'file': 'README',
198 'type': 'MODIFIED'}],
James E. Blair8c803f82012-07-31 16:25:42 -0700199 'number': str(self.latest_patchset),
James E. Blairb0fcae42012-07-17 11:12:10 -0700200 'ref': 'refs/changes/1/%s/%s' % (self.number,
201 self.latest_patchset),
James E. Blairdaabed22012-08-15 15:38:57 -0700202 'revision': c.hexsha,
James E. Blairb0fcae42012-07-17 11:12:10 -0700203 'uploader': {'email': 'user@example.com',
204 'name': 'User name',
205 'username': 'user'}}
206 self.data['currentPatchSet'] = d
207 self.patchsets.append(d)
James E. Blair8c803f82012-07-31 16:25:42 -0700208 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700209
James E. Blaire0487072012-08-29 17:38:31 -0700210 def getPatchsetCreatedEvent(self, patchset):
211 event = {"type": "patchset-created",
212 "change": {"project": self.project,
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800213 "branch": self.branch,
214 "id": "I5459869c07352a31bfb1e7a8cac379cabfcb25af",
215 "number": str(self.number),
216 "subject": self.subject,
217 "owner": {"name": "User Name"},
218 "url": "https://hostname/3"},
James E. Blaire0487072012-08-29 17:38:31 -0700219 "patchSet": self.patchsets[patchset - 1],
220 "uploader": {"name": "User Name"}}
221 return event
222
James E. Blairb0fcae42012-07-17 11:12:10 -0700223 def addApproval(self, category, value):
James E. Blair8c803f82012-07-31 16:25:42 -0700224 approval = {'description': self.categories[category][0],
225 'type': category,
226 'value': str(value)}
227 self.patchsets[-1]['approvals'].append(approval)
228 event = {'approvals': [approval],
James E. Blairb0fcae42012-07-17 11:12:10 -0700229 'author': {'email': 'user@example.com',
230 'name': 'User Name',
231 'username': 'username'},
232 'change': {'branch': self.branch,
233 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
234 'number': str(self.number),
235 'owner': {'email': 'user@example.com',
236 'name': 'User Name',
237 'username': 'username'},
238 'project': self.project,
239 'subject': self.subject,
240 'topic': 'master',
241 'url': 'https://hostname/459'},
242 'comment': '',
243 'patchSet': self.patchsets[-1],
244 'type': 'comment-added'}
James E. Blair8c803f82012-07-31 16:25:42 -0700245 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700246 return json.loads(json.dumps(event))
247
James E. Blair8c803f82012-07-31 16:25:42 -0700248 def getSubmitRecords(self):
249 status = {}
250 for cat in self.categories.keys():
251 status[cat] = 0
252
253 for a in self.patchsets[-1]['approvals']:
254 cur = status[a['type']]
255 cat_min, cat_max = self.categories[a['type']][1:]
256 new = int(a['value'])
257 if new == cat_min:
258 cur = new
259 elif abs(new) > abs(cur):
260 cur = new
261 status[a['type']] = cur
262
263 labels = []
264 ok = True
265 for typ, cat in self.categories.items():
266 cur = status[typ]
267 cat_min, cat_max = cat[1:]
268 if cur == cat_min:
269 value = 'REJECT'
270 ok = False
271 elif cur == cat_max:
272 value = 'OK'
273 else:
274 value = 'NEED'
275 ok = False
276 labels.append({'label': cat[0], 'status': value})
277 if ok:
278 return [{'status': 'OK'}]
279 return [{'status': 'NOT_READY',
280 'labels': labels}]
281
282 def setDependsOn(self, other, patchset):
283 self.depends_on_change = other
284 d = {'id': other.data['id'],
285 'number': other.data['number'],
286 'ref': other.patchsets[patchset - 1]['ref']
287 }
288 self.data['dependsOn'] = [d]
289
290 other.needed_by_changes.append(self)
291 needed = other.data.get('neededBy', [])
292 d = {'id': self.data['id'],
293 'number': self.data['number'],
294 'ref': self.patchsets[patchset - 1]['ref'],
295 'revision': self.patchsets[patchset - 1]['revision']
296 }
297 needed.append(d)
298 other.data['neededBy'] = needed
299
James E. Blairb0fcae42012-07-17 11:12:10 -0700300 def query(self):
James E. Blair8c803f82012-07-31 16:25:42 -0700301 self.queried += 1
302 d = self.data.get('dependsOn')
303 if d:
304 d = d[0]
305 if (self.depends_on_change.patchsets[-1]['ref'] == d['ref']):
306 d['isCurrentPatchSet'] = True
307 else:
308 d['isCurrentPatchSet'] = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700309 return json.loads(json.dumps(self.data))
310
311 def setMerged(self):
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800312 if (self.depends_on_change and
313 self.depends_on_change.data['status'] != 'MERGED'):
James E. Blaircaec0c52012-08-22 14:52:22 -0700314 return
James E. Blair127bc182012-08-28 15:55:15 -0700315 if self.fail_merge:
316 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700317 self.data['status'] = 'MERGED'
318 self.open = False
James E. Blairdaabed22012-08-15 15:38:57 -0700319
James E. Blair1dbd5082012-08-23 15:12:15 -0700320 path = os.path.join(UPSTREAM_ROOT, self.project)
James E. Blairdaabed22012-08-15 15:38:57 -0700321 repo = git.Repo(path)
322 repo.heads[self.branch].commit = \
323 repo.commit(self.patchsets[-1]['revision'])
James E. Blairb0fcae42012-07-17 11:12:10 -0700324
James E. Blaird466dc42012-07-31 10:42:56 -0700325 def setReported(self):
326 self.reported += 1
327
James E. Blairb0fcae42012-07-17 11:12:10 -0700328
329class FakeGerrit(object):
330 def __init__(self, *args, **kw):
331 self.event_queue = Queue.Queue()
332 self.fixture_dir = os.path.join(FIXTURE_DIR, 'gerrit')
333 self.change_number = 0
334 self.changes = {}
335
336 def addFakeChange(self, project, branch, subject):
337 self.change_number += 1
James E. Blair8cc15a82012-08-01 11:17:57 -0700338 c = FakeChange(self, self.change_number, project, branch, subject)
James E. Blairb0fcae42012-07-17 11:12:10 -0700339 self.changes[self.change_number] = c
340 return c
341
342 def addEvent(self, data):
343 return self.event_queue.put(data)
344
345 def getEvent(self):
346 return self.event_queue.get()
347
348 def eventDone(self):
349 self.event_queue.task_done()
350
351 def review(self, project, changeid, message, action):
James E. Blaird466dc42012-07-31 10:42:56 -0700352 number, ps = changeid.split(',')
353 change = self.changes[int(number)]
James E. Blairb0fcae42012-07-17 11:12:10 -0700354 if 'submit' in action:
James E. Blairb0fcae42012-07-17 11:12:10 -0700355 change.setMerged()
James E. Blaird466dc42012-07-31 10:42:56 -0700356 if message:
357 change.setReported()
James E. Blairb0fcae42012-07-17 11:12:10 -0700358
359 def query(self, number):
360 change = self.changes[int(number)]
361 return change.query()
362
363 def startWatching(self, *args, **kw):
364 pass
365
366
367class FakeJenkinsEvent(object):
368 def __init__(self, name, number, parameters, phase, status=None):
Zhongyue Luo5d556072012-09-21 02:00:47 +0900369 data = {
370 'build': {
371 'full_url': 'https://server/job/%s/%s/' % (name, number),
372 'number': number,
373 'parameters': parameters,
374 'phase': phase,
375 'url': 'job/%s/%s/' % (name, number),
376 },
377 'name': name,
378 'url': 'job/%s/' % name,
379 }
James E. Blairb0fcae42012-07-17 11:12:10 -0700380 if status:
381 data['build']['status'] = status
382 self.body = json.dumps(data)
383
384
385class FakeJenkinsJob(threading.Thread):
386 log = logging.getLogger("zuul.test")
387
388 def __init__(self, jenkins, callback, name, number, parameters):
389 threading.Thread.__init__(self)
390 self.jenkins = jenkins
391 self.callback = callback
392 self.name = name
393 self.number = number
394 self.parameters = parameters
395 self.wait_condition = threading.Condition()
396 self.waiting = False
James E. Blaird466dc42012-07-31 10:42:56 -0700397 self.aborted = False
398 self.canceled = False
399 self.created = time.time()
James E. Blairb0fcae42012-07-17 11:12:10 -0700400
401 def release(self):
402 self.wait_condition.acquire()
403 self.wait_condition.notify()
404 self.waiting = False
405 self.log.debug("Job %s released" % (self.parameters['UUID']))
406 self.wait_condition.release()
407
408 def isWaiting(self):
409 self.wait_condition.acquire()
410 if self.waiting:
411 ret = True
412 else:
413 ret = False
414 self.wait_condition.release()
415 return ret
416
417 def _wait(self):
418 self.wait_condition.acquire()
419 self.waiting = True
420 self.log.debug("Job %s waiting" % (self.parameters['UUID']))
421 self.wait_condition.wait()
422 self.wait_condition.release()
423
424 def run(self):
425 self.jenkins.fakeEnqueue(self)
426 if self.jenkins.hold_jobs_in_queue:
427 self._wait()
428 self.jenkins.fakeDequeue(self)
James E. Blaird466dc42012-07-31 10:42:56 -0700429 if self.canceled:
430 self.jenkins.all_jobs.remove(self)
431 return
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800432 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
433 self.number,
434 self.parameters,
435 'STARTED'))
James E. Blairb0fcae42012-07-17 11:12:10 -0700436 if self.jenkins.hold_jobs_in_build:
437 self._wait()
438 self.log.debug("Job %s continuing" % (self.parameters['UUID']))
James E. Blairb02a3bb2012-07-30 17:49:55 -0700439
440 result = 'SUCCESS'
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800441 if (('ZUUL_REF' in self.parameters) and
442 self.jenkins.fakeShouldFailTest(self.name,
443 self.parameters['ZUUL_REF'])):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700444 result = 'FAILURE'
James E. Blaird466dc42012-07-31 10:42:56 -0700445 if self.aborted:
446 result = 'ABORTED'
James E. Blairb02a3bb2012-07-30 17:49:55 -0700447
James E. Blairb0fcae42012-07-17 11:12:10 -0700448 self.jenkins.fakeAddHistory(name=self.name, number=self.number,
James E. Blairb02a3bb2012-07-30 17:49:55 -0700449 result=result)
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800450 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
451 self.number,
452 self.parameters,
453 'COMPLETED',
454 result))
455 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
456 self.number,
457 self.parameters,
458 'FINISHED',
459 result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700460 self.jenkins.all_jobs.remove(self)
461
462
463class FakeJenkins(object):
464 log = logging.getLogger("zuul.test")
465
466 def __init__(self, *args, **kw):
467 self.queue = []
468 self.all_jobs = []
469 self.job_counter = {}
James E. Blaird466dc42012-07-31 10:42:56 -0700470 self.queue_counter = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700471 self.job_history = []
472 self.hold_jobs_in_queue = False
473 self.hold_jobs_in_build = False
James E. Blairb02a3bb2012-07-30 17:49:55 -0700474 self.fail_tests = {}
James E. Blair7ee88a22012-09-12 18:59:31 +0200475 self.nonexistent_jobs = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700476
477 def fakeEnqueue(self, job):
478 self.queue.append(job)
479
480 def fakeDequeue(self, job):
481 self.queue.remove(job)
482
483 def fakeAddHistory(self, **kw):
484 self.job_history.append(kw)
485
486 def fakeRelease(self, regex=None):
487 all_jobs = self.all_jobs[:]
488 self.log.debug("releasing jobs %s (%s)" % (regex, len(self.all_jobs)))
489 for job in all_jobs:
490 if not regex or re.match(regex, job.name):
491 self.log.debug("releasing job %s" % (job.parameters['UUID']))
492 job.release()
493 else:
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800494 self.log.debug("not releasing job %s" %
495 (job.parameters['UUID']))
James E. Blairb0fcae42012-07-17 11:12:10 -0700496 self.log.debug("done releasing jobs %s (%s)" % (regex,
497 len(self.all_jobs)))
498
499 def fakeAllWaiting(self, regex=None):
James E. Blair4aa1ad62012-10-05 12:39:26 -0700500 all_jobs = self.all_jobs[:] + self.queue[:]
James E. Blairb0fcae42012-07-17 11:12:10 -0700501 for job in all_jobs:
502 self.log.debug("job %s %s" % (job.parameters['UUID'],
503 job.isWaiting()))
504 if not job.isWaiting():
505 return False
506 return True
507
James E. Blairb02a3bb2012-07-30 17:49:55 -0700508 def fakeAddFailTest(self, name, change):
509 l = self.fail_tests.get(name, [])
510 l.append(change)
511 self.fail_tests[name] = l
512
James E. Blair4886cc12012-07-18 15:39:41 -0700513 def fakeShouldFailTest(self, name, ref):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700514 l = self.fail_tests.get(name, [])
515 for change in l:
James E. Blair4886cc12012-07-18 15:39:41 -0700516 if ref_has_change(ref, change):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700517 return True
518 return False
519
James E. Blairb0fcae42012-07-17 11:12:10 -0700520 def build_job(self, name, parameters):
James E. Blair7ee88a22012-09-12 18:59:31 +0200521 if name in self.nonexistent_jobs:
522 raise Exception("Job does not exist")
James E. Blairb0fcae42012-07-17 11:12:10 -0700523 count = self.job_counter.get(name, 0)
524 count += 1
525 self.job_counter[name] = count
James E. Blaird466dc42012-07-31 10:42:56 -0700526
527 queue_count = self.queue_counter
528 self.queue_counter += 1
James E. Blairb0fcae42012-07-17 11:12:10 -0700529 job = FakeJenkinsJob(self, self.callback, name, count, parameters)
James E. Blaird466dc42012-07-31 10:42:56 -0700530 job.queue_id = queue_count
531
James E. Blairb0fcae42012-07-17 11:12:10 -0700532 self.all_jobs.append(job)
533 job.start()
534
James E. Blaird466dc42012-07-31 10:42:56 -0700535 def stop_build(self, name, number):
536 for job in self.all_jobs:
537 if job.name == name and job.number == number:
538 job.aborted = True
539 job.release()
540 return
541
542 def cancel_queue(self, id):
543 for job in self.queue:
544 if job.queue_id == id:
545 job.canceled = True
546 job.release()
547 return
548
549 def get_queue_info(self):
550 items = []
551 for job in self.queue:
552 paramstr = ''
553 paramlst = []
554 d = {'actions': [{'parameters': paramlst},
555 {'causes': [{'shortDescription':
556 'Started by user Jenkins',
557 'userId': 'jenkins',
558 'userName': 'Jenkins'}]}],
559 'blocked': False,
560 'buildable': True,
561 'buildableStartMilliseconds': (job.created * 1000) + 5,
562 'id': job.queue_id,
563 'inQueueSince': (job.created * 1000),
564 'params': paramstr,
565 'stuck': False,
566 'task': {'color': 'blue',
567 'name': job.name,
568 'url': 'https://server/job/%s/' % job.name},
569 'why': 'Waiting for next available executor'}
570 for k, v in job.parameters.items():
571 paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
572 pd = {'name': k, 'value': v}
573 paramlst.append(pd)
574 items.append(d)
575 return items
576
James E. Blairb0fcae42012-07-17 11:12:10 -0700577 def set_build_description(self, *args, **kw):
578 pass
579
580
581class FakeJenkinsCallback(zuul.launcher.jenkins.JenkinsCallback):
582 def start(self):
583 pass
584
585
James E. Blair8cc15a82012-08-01 11:17:57 -0700586class FakeURLOpener(object):
587 def __init__(self, fake_gerrit, url):
588 self.fake_gerrit = fake_gerrit
589 self.url = url
590
591 def read(self):
592 res = urlparse.urlparse(self.url)
593 path = res.path
594 project = '/'.join(path.split('/')[2:-2])
James E. Blair35956c52012-09-17 22:13:36 +0200595 ret = '001e# service=git-upload-pack\n'
596 ret += ('000000a31270149696713ba7e06f1beb760f20d359c4abed HEAD\x00'
597 'multi_ack thin-pack side-band side-band-64k ofs-delta '
598 'shallow no-progress include-tag multi_ack_detailed no-done\n')
James E. Blair1dbd5082012-08-23 15:12:15 -0700599 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blairdaabed22012-08-15 15:38:57 -0700600 repo = git.Repo(path)
601 for ref in repo.refs:
James E. Blair35956c52012-09-17 22:13:36 +0200602 r = ref.object.hexsha + ' ' + ref.path + '\n'
603 ret += '%04x%s' % (len(r) + 4, r)
604 ret += '0000'
James E. Blair8cc15a82012-08-01 11:17:57 -0700605 return ret
606
607
James E. Blair4886cc12012-07-18 15:39:41 -0700608class FakeGerritTrigger(zuul.trigger.gerrit.Gerrit):
609 def getGitUrl(self, project):
James E. Blair1dbd5082012-08-23 15:12:15 -0700610 return os.path.join(UPSTREAM_ROOT, project.name)
James E. Blair4886cc12012-07-18 15:39:41 -0700611
612
James E. Blairb0fcae42012-07-17 11:12:10 -0700613class testScheduler(unittest.TestCase):
614 log = logging.getLogger("zuul.test")
615
616 def setUp(self):
James E. Blair1dbd5082012-08-23 15:12:15 -0700617 if os.path.exists(TEST_ROOT):
618 shutil.rmtree(TEST_ROOT)
619 os.makedirs(TEST_ROOT)
620 os.makedirs(UPSTREAM_ROOT)
621 os.makedirs(GIT_ROOT)
James E. Blair4886cc12012-07-18 15:39:41 -0700622
623 # For each project in config:
624 init_repo("org/project")
625 init_repo("org/project1")
626 init_repo("org/project2")
James E. Blair127bc182012-08-28 15:55:15 -0700627 init_repo("org/project3")
James E. Blair7f71c802012-08-22 13:04:32 -0700628 init_repo("org/one-job-project")
James E. Blair4ec821f2012-08-23 15:28:28 -0700629 init_repo("org/nonvoting-project")
James E. Blairb0fcae42012-07-17 11:12:10 -0700630 self.config = CONFIG
631 self.sched = zuul.scheduler.Scheduler()
632
633 def jenkinsFactory(*args, **kw):
634 self.fake_jenkins = FakeJenkins()
635 return self.fake_jenkins
636
637 def jenkinsCallbackFactory(*args, **kw):
638 self.fake_jenkins_callback = FakeJenkinsCallback(*args, **kw)
639 return self.fake_jenkins_callback
640
James E. Blair8cc15a82012-08-01 11:17:57 -0700641 def URLOpenerFactory(*args, **kw):
642 args = [self.fake_gerrit] + list(args)
643 return FakeURLOpener(*args, **kw)
644
James E. Blairb0fcae42012-07-17 11:12:10 -0700645 zuul.launcher.jenkins.ExtendedJenkins = jenkinsFactory
646 zuul.launcher.jenkins.JenkinsCallback = jenkinsCallbackFactory
James E. Blair8cc15a82012-08-01 11:17:57 -0700647 urllib2.urlopen = URLOpenerFactory
James E. Blairb0fcae42012-07-17 11:12:10 -0700648 self.jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
649 self.fake_jenkins.callback = self.fake_jenkins_callback
650
651 zuul.lib.gerrit.Gerrit = FakeGerrit
652
James E. Blair4886cc12012-07-18 15:39:41 -0700653 self.gerrit = FakeGerritTrigger(self.config, self.sched)
James E. Blair8cc15a82012-08-01 11:17:57 -0700654 self.gerrit.replication_timeout = 1.5
655 self.gerrit.replication_retry_interval = 0.5
James E. Blairb0fcae42012-07-17 11:12:10 -0700656 self.fake_gerrit = self.gerrit.gerrit
657
658 self.sched.setLauncher(self.jenkins)
659 self.sched.setTrigger(self.gerrit)
660
661 self.sched.start()
662 self.sched.reconfigure(self.config)
663 self.sched.resume()
664
665 def tearDown(self):
666 self.jenkins.stop()
667 self.gerrit.stop()
668 self.sched.stop()
669 self.sched.join()
James E. Blair1dbd5082012-08-23 15:12:15 -0700670 #shutil.rmtree(TEST_ROOT)
James E. Blairb0fcae42012-07-17 11:12:10 -0700671
672 def waitUntilSettled(self):
673 self.log.debug("Waiting until settled...")
674 start = time.time()
675 while True:
676 if time.time() - start > 10:
677 print 'queue status:',
678 print self.sched.trigger_event_queue.empty(),
679 print self.sched.result_event_queue.empty(),
680 print self.fake_gerrit.event_queue.empty(),
681 raise Exception("Timeout waiting for Zuul to settle")
682 self.fake_gerrit.event_queue.join()
683 self.sched.queue_lock.acquire()
684 if (self.sched.trigger_event_queue.empty() and
685 self.sched.result_event_queue.empty() and
686 self.fake_gerrit.event_queue.empty() and
687 self.fake_jenkins.fakeAllWaiting()):
688 self.sched.queue_lock.release()
689 self.log.debug("...settled.")
690 return
691 self.sched.queue_lock.release()
692 self.sched.wake_event.wait(0.1)
693
James E. Blaird466dc42012-07-31 10:42:56 -0700694 def countJobResults(self, jobs, result):
695 jobs = filter(lambda x: x['result'] == result, jobs)
696 return len(jobs)
697
James E. Blaire0487072012-08-29 17:38:31 -0700698 def assertEmptyQueues(self):
699 # Make sure there are no orphaned jobs
700 for pipeline in self.sched.pipelines.values():
701 for queue in pipeline.queues:
702 if len(queue.queue) != 0:
703 print 'queue', queue.queue
704 assert len(queue.queue) == 0
705 if len(queue.severed_heads) != 0:
706 print 'heads', queue.severed_heads
707 assert len(queue.severed_heads) == 0
708
James E. Blairb0fcae42012-07-17 11:12:10 -0700709 def test_jobs_launched(self):
710 "Test that jobs are launched and a change is merged"
711 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair8c803f82012-07-31 16:25:42 -0700712 A.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700713 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
714 self.waitUntilSettled()
715 jobs = self.fake_jenkins.job_history
716 job_names = [x['name'] for x in jobs]
717 assert 'project-merge' in job_names
718 assert 'project-test1' in job_names
719 assert 'project-test2' in job_names
720 assert jobs[0]['result'] == 'SUCCESS'
721 assert jobs[1]['result'] == 'SUCCESS'
722 assert jobs[2]['result'] == 'SUCCESS'
723 assert A.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700724 assert A.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700725 self.assertEmptyQueues()
James E. Blairb0fcae42012-07-17 11:12:10 -0700726
727 def test_parallel_changes(self):
728 "Test that changes are tested in parallel and merged in series"
729 self.fake_jenkins.hold_jobs_in_build = True
730 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
731 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
732 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700733 A.addApproval('CRVW', 2)
734 B.addApproval('CRVW', 2)
735 C.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700736
737 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
738 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
739 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
740
741 self.waitUntilSettled()
742 jobs = self.fake_jenkins.all_jobs
743 assert len(jobs) == 1
744 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700745 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700746
747 self.fake_jenkins.fakeRelease('.*-merge')
748 self.waitUntilSettled()
749 assert len(jobs) == 3
750 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700751 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700752 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700753 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700754 assert jobs[2].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700755 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700756
757 self.fake_jenkins.fakeRelease('.*-merge')
758 self.waitUntilSettled()
759 assert len(jobs) == 5
760 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700761 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700762 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700763 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700764
765 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700766 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700767 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700768 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700769
770 assert jobs[4].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700771 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700772
773 self.fake_jenkins.fakeRelease('.*-merge')
774 self.waitUntilSettled()
775 assert len(jobs) == 6
776 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700777 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700778 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700779 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700780
781 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700782 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700783 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700784 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700785
786 assert jobs[4].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700787 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700788 assert jobs[5].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700789 assert job_has_changes(jobs[5], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700790
791 self.fake_jenkins.hold_jobs_in_build = False
792 self.fake_jenkins.fakeRelease()
793 self.waitUntilSettled()
794 assert len(jobs) == 0
795
796 jobs = self.fake_jenkins.job_history
797 assert len(jobs) == 9
798 assert A.data['status'] == 'MERGED'
799 assert B.data['status'] == 'MERGED'
800 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700801 assert A.reported == 2
802 assert B.reported == 2
803 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700804 self.assertEmptyQueues()
James E. Blairb02a3bb2012-07-30 17:49:55 -0700805
806 def test_failed_changes(self):
807 "Test that a change behind a failed change is retested"
808 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
809 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
James E. Blair8c803f82012-07-31 16:25:42 -0700810 A.addApproval('CRVW', 2)
811 B.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700812
813 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
814 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
815
James E. Blair4886cc12012-07-18 15:39:41 -0700816 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700817
818 self.waitUntilSettled()
819 jobs = self.fake_jenkins.job_history
820 assert len(jobs) > 6
821 assert A.data['status'] == 'NEW'
822 assert B.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700823 assert A.reported == 2
824 assert B.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700825 self.assertEmptyQueues()
James E. Blairb02a3bb2012-07-30 17:49:55 -0700826
827 def test_independent_queues(self):
828 "Test that changes end up in the right queues"
829 self.fake_jenkins.hold_jobs_in_build = True
Zhongyue Luo5d556072012-09-21 02:00:47 +0900830 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blairb02a3bb2012-07-30 17:49:55 -0700831 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
832 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700833 A.addApproval('CRVW', 2)
834 B.addApproval('CRVW', 2)
835 C.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700836
837 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
838 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
839 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
840
841 jobs = self.fake_jenkins.all_jobs
842 self.waitUntilSettled()
843
844 # There should be one merge job at the head of each queue running
845 assert len(jobs) == 2
846 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700847 assert job_has_changes(jobs[0], A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700848 assert jobs[1].name == 'project1-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700849 assert job_has_changes(jobs[1], B)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700850
851 # Release the current merge jobs
852 self.fake_jenkins.fakeRelease('.*-merge')
853 self.waitUntilSettled()
854 # Release the merge job for project2 which is behind project1
855 self.fake_jenkins.fakeRelease('.*-merge')
856 self.waitUntilSettled()
857
858 # All the test jobs should be running:
859 # project1 (3) + project2 (3) + project (2) = 8
860 assert len(jobs) == 8
861
862 self.fake_jenkins.fakeRelease()
863 self.waitUntilSettled()
864 assert len(jobs) == 0
865
866 jobs = self.fake_jenkins.job_history
867 assert len(jobs) == 11
868 assert A.data['status'] == 'MERGED'
869 assert B.data['status'] == 'MERGED'
870 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700871 assert A.reported == 2
872 assert B.reported == 2
873 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700874 self.assertEmptyQueues()
James E. Blaird466dc42012-07-31 10:42:56 -0700875
876 def test_failed_change_at_head(self):
877 "Test that if a change at the head fails, jobs behind it are canceled"
878 self.fake_jenkins.hold_jobs_in_build = True
879
880 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
881 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
882 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700883 A.addApproval('CRVW', 2)
884 B.addApproval('CRVW', 2)
885 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700886
James E. Blair4886cc12012-07-18 15:39:41 -0700887 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700888
889 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
890 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
891 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
892
893 self.waitUntilSettled()
894 jobs = self.fake_jenkins.all_jobs
895 finished_jobs = self.fake_jenkins.job_history
896
897 assert len(jobs) == 1
898 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700899 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700900
901 self.fake_jenkins.fakeRelease('.*-merge')
902 self.waitUntilSettled()
903 self.fake_jenkins.fakeRelease('.*-merge')
904 self.waitUntilSettled()
905 self.fake_jenkins.fakeRelease('.*-merge')
906 self.waitUntilSettled()
907
908 assert len(jobs) == 6
909 assert jobs[0].name == 'project-test1'
910 assert jobs[1].name == 'project-test2'
911 assert jobs[2].name == 'project-test1'
912 assert jobs[3].name == 'project-test2'
913 assert jobs[4].name == 'project-test1'
914 assert jobs[5].name == 'project-test2'
915
916 jobs[0].release()
917 self.waitUntilSettled()
918
James E. Blairec590122012-08-22 15:19:31 -0700919 assert len(jobs) == 2 # project-test2, project-merge for B
James E. Blaird466dc42012-07-31 10:42:56 -0700920 assert self.countJobResults(finished_jobs, 'ABORTED') == 4
921
922 self.fake_jenkins.hold_jobs_in_build = False
923 self.fake_jenkins.fakeRelease()
924 self.waitUntilSettled()
925
926 assert len(jobs) == 0
927 assert len(finished_jobs) == 15
928 assert A.data['status'] == 'NEW'
929 assert B.data['status'] == 'MERGED'
930 assert C.data['status'] == 'MERGED'
931 assert A.reported == 2
932 assert B.reported == 2
933 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700934 self.assertEmptyQueues()
James E. Blaird466dc42012-07-31 10:42:56 -0700935
936 def test_failed_change_at_head_with_queue(self):
937 "Test that if a change at the head fails, queued jobs are canceled"
938 self.fake_jenkins.hold_jobs_in_queue = True
939
940 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
941 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
942 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700943 A.addApproval('CRVW', 2)
944 B.addApproval('CRVW', 2)
945 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -0700946
James E. Blair4886cc12012-07-18 15:39:41 -0700947 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -0700948
949 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
950 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
951 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
952
953 self.waitUntilSettled()
954 jobs = self.fake_jenkins.all_jobs
955 finished_jobs = self.fake_jenkins.job_history
956 queue = self.fake_jenkins.queue
957
958 assert len(jobs) == 1
959 assert len(queue) == 1
960 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700961 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -0700962
963 self.fake_jenkins.fakeRelease('.*-merge')
964 self.waitUntilSettled()
965 self.fake_jenkins.fakeRelease('.*-merge')
966 self.waitUntilSettled()
967 self.fake_jenkins.fakeRelease('.*-merge')
968 self.waitUntilSettled()
969
970 assert len(jobs) == 6
971 assert len(queue) == 6
972 assert jobs[0].name == 'project-test1'
973 assert jobs[1].name == 'project-test2'
974 assert jobs[2].name == 'project-test1'
975 assert jobs[3].name == 'project-test2'
976 assert jobs[4].name == 'project-test1'
977 assert jobs[5].name == 'project-test2'
978
979 jobs[0].release()
980 self.waitUntilSettled()
981
James E. Blairec590122012-08-22 15:19:31 -0700982 assert len(jobs) == 2 # project-test2, project-merge for B
983 assert len(queue) == 2
James E. Blaird466dc42012-07-31 10:42:56 -0700984 assert self.countJobResults(finished_jobs, 'ABORTED') == 0
985
986 self.fake_jenkins.hold_jobs_in_queue = False
987 self.fake_jenkins.fakeRelease()
988 self.waitUntilSettled()
989
990 assert len(jobs) == 0
991 assert len(finished_jobs) == 11
992 assert A.data['status'] == 'NEW'
993 assert B.data['status'] == 'MERGED'
994 assert C.data['status'] == 'MERGED'
995 assert A.reported == 2
996 assert B.reported == 2
997 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700998 self.assertEmptyQueues()
James E. Blair8c803f82012-07-31 16:25:42 -0700999
1000 def test_patch_order(self):
1001 "Test that dependent patches are tested in the right order"
1002 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1003 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1004 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1005 A.addApproval('CRVW', 2)
1006 B.addApproval('CRVW', 2)
1007 C.addApproval('CRVW', 2)
1008
1009 M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
1010 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1011 M2.setMerged()
1012 M1.setMerged()
1013
1014 # C -> B -> A -> M1 -> M2
1015 # M2 is here to make sure it is never queried. If it is, it
1016 # means zuul is walking down the entire history of merged
1017 # changes.
1018
1019 C.setDependsOn(B, 1)
1020 B.setDependsOn(A, 1)
1021 A.setDependsOn(M1, 1)
1022 M1.setDependsOn(M2, 1)
1023
1024 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1025
1026 self.waitUntilSettled()
1027
1028 assert A.data['status'] == 'NEW'
1029 assert B.data['status'] == 'NEW'
1030 assert C.data['status'] == 'NEW'
1031
1032 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1033 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1034
1035 self.waitUntilSettled()
1036 assert M2.queried == 0
1037 assert A.data['status'] == 'MERGED'
1038 assert B.data['status'] == 'MERGED'
1039 assert C.data['status'] == 'MERGED'
1040 assert A.reported == 2
1041 assert B.reported == 2
1042 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001043 self.assertEmptyQueues()
James E. Blair8c803f82012-07-31 16:25:42 -07001044
1045 def test_can_merge(self):
James E. Blair4886cc12012-07-18 15:39:41 -07001046 "Test whether a change is ready to merge"
James E. Blair8c803f82012-07-31 16:25:42 -07001047 # TODO: move to test_gerrit (this is a unit test!)
1048 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair4aea70c2012-07-26 14:23:24 -07001049 a = self.sched.trigger.getChange(1, 2)
1050 mgr = self.sched.pipelines['gate'].manager
1051 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001052
1053 A.addApproval('CRVW', 2)
James E. Blair4aea70c2012-07-26 14:23:24 -07001054 a = self.sched.trigger.getChange(1, 2)
1055 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001056
1057 A.addApproval('APRV', 1)
James E. Blair4aea70c2012-07-26 14:23:24 -07001058 a = self.sched.trigger.getChange(1, 2)
1059 assert self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blaire0487072012-08-29 17:38:31 -07001060 self.assertEmptyQueues()
James E. Blair4886cc12012-07-18 15:39:41 -07001061
1062 def test_build_configuration(self):
1063 "Test that zuul merges the right commits for testing"
1064 self.fake_jenkins.hold_jobs_in_queue = True
1065 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1066 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1067 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1068 A.addApproval('CRVW', 2)
1069 B.addApproval('CRVW', 2)
1070 C.addApproval('CRVW', 2)
1071 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1072 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1073 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1074 self.waitUntilSettled()
1075
1076 jobs = self.fake_jenkins.all_jobs
1077
1078 self.fake_jenkins.fakeRelease('.*-merge')
1079 self.waitUntilSettled()
1080 self.fake_jenkins.fakeRelease('.*-merge')
1081 self.waitUntilSettled()
1082 self.fake_jenkins.fakeRelease('.*-merge')
1083 self.waitUntilSettled()
James E. Blair1dbd5082012-08-23 15:12:15 -07001084
James E. Blair4886cc12012-07-18 15:39:41 -07001085 ref = jobs[-1].parameters['ZUUL_REF']
1086 self.fake_jenkins.hold_jobs_in_queue = False
1087 self.fake_jenkins.fakeRelease()
James E. Blair973721f2012-08-15 10:19:43 -07001088 self.waitUntilSettled()
James E. Blair4886cc12012-07-18 15:39:41 -07001089
James E. Blair1dbd5082012-08-23 15:12:15 -07001090 path = os.path.join(GIT_ROOT, "org/project")
James E. Blair4886cc12012-07-18 15:39:41 -07001091 repo = git.Repo(path)
1092 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1093 repo_messages.reverse()
James E. Blair4886cc12012-07-18 15:39:41 -07001094 correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
1095 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001096 self.assertEmptyQueues()
James E. Blair973721f2012-08-15 10:19:43 -07001097
1098 def test_build_configuration_conflict(self):
1099 "Test that merge conflicts are handled"
1100 self.fake_jenkins.hold_jobs_in_queue = True
1101 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1102 A.addPatchset(['conflict'])
1103 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1104 B.addPatchset(['conflict'])
1105 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1106 A.addApproval('CRVW', 2)
1107 B.addApproval('CRVW', 2)
1108 C.addApproval('CRVW', 2)
1109 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1110 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1111 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1112 self.waitUntilSettled()
1113
1114 jobs = self.fake_jenkins.all_jobs
1115
1116 self.fake_jenkins.fakeRelease('.*-merge')
1117 self.waitUntilSettled()
1118 self.fake_jenkins.fakeRelease('.*-merge')
1119 self.waitUntilSettled()
1120 self.fake_jenkins.fakeRelease('.*-merge')
1121 self.waitUntilSettled()
1122 ref = jobs[-1].parameters['ZUUL_REF']
1123 self.fake_jenkins.hold_jobs_in_queue = False
1124 self.fake_jenkins.fakeRelease()
1125 self.waitUntilSettled()
1126
1127 assert A.data['status'] == 'MERGED'
1128 assert B.data['status'] == 'NEW'
1129 assert C.data['status'] == 'MERGED'
1130 assert A.reported == 2
1131 assert B.reported == 2
1132 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001133 self.assertEmptyQueues()
James E. Blairdaabed22012-08-15 15:38:57 -07001134
1135 def test_post(self):
1136 "Test that post jobs run"
Zhongyue Luo5d556072012-09-21 02:00:47 +09001137 e = {
1138 "type": "ref-updated",
1139 "submitter": {
1140 "name": "User Name",
1141 },
1142 "refUpdate": {
1143 "oldRev": "90f173846e3af9154517b88543ffbd1691f31366",
1144 "newRev": "d479a0bfcb34da57a31adb2a595c0cf687812543",
1145 "refName": "master",
1146 "project": "org/project",
1147 }
1148 }
James E. Blairdaabed22012-08-15 15:38:57 -07001149 self.fake_gerrit.addEvent(e)
1150 self.waitUntilSettled()
1151
1152 jobs = self.fake_jenkins.job_history
1153 job_names = [x['name'] for x in jobs]
1154 assert len(jobs) == 1
1155 assert 'project-post' in job_names
James E. Blaire0487072012-08-29 17:38:31 -07001156 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001157
1158 def test_build_configuration_branch(self):
1159 "Test that the right commits are on alternate branches"
1160 self.fake_jenkins.hold_jobs_in_queue = True
1161 A = self.fake_gerrit.addFakeChange('org/project', 'mp', 'A')
1162 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1163 C = self.fake_gerrit.addFakeChange('org/project', 'mp', 'C')
1164 A.addApproval('CRVW', 2)
1165 B.addApproval('CRVW', 2)
1166 C.addApproval('CRVW', 2)
1167 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1168 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1169 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1170 self.waitUntilSettled()
1171
1172 jobs = self.fake_jenkins.all_jobs
1173
1174 self.fake_jenkins.fakeRelease('.*-merge')
1175 self.waitUntilSettled()
1176 self.fake_jenkins.fakeRelease('.*-merge')
1177 self.waitUntilSettled()
1178 self.fake_jenkins.fakeRelease('.*-merge')
1179 self.waitUntilSettled()
1180 ref = jobs[-1].parameters['ZUUL_REF']
1181 self.fake_jenkins.hold_jobs_in_queue = False
1182 self.fake_jenkins.fakeRelease()
1183 self.waitUntilSettled()
1184
James E. Blair1dbd5082012-08-23 15:12:15 -07001185 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001186 repo = git.Repo(path)
1187 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1188 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001189 correct_messages = ['initial commit', 'mp commit', 'A-1', 'B-1', 'C-1']
1190 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001191 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001192
1193 def test_build_configuration_branch_interaction(self):
1194 "Test that switching between branches works"
1195 self.test_build_configuration()
1196 self.test_build_configuration_branch()
1197 # C has been merged, undo that
James E. Blair1dbd5082012-08-23 15:12:15 -07001198 path = os.path.join(UPSTREAM_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001199 repo = git.Repo(path)
1200 repo.heads.master.commit = repo.commit('init')
1201 self.test_build_configuration()
James E. Blaire0487072012-08-29 17:38:31 -07001202 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001203
1204 def test_build_configuration_multi_branch(self):
1205 "Test that dependent changes on multiple branches are merged"
1206 self.fake_jenkins.hold_jobs_in_queue = True
1207 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1208 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1209 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1210 A.addApproval('CRVW', 2)
1211 B.addApproval('CRVW', 2)
1212 C.addApproval('CRVW', 2)
1213 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1214 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1215 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1216 self.waitUntilSettled()
1217
1218 jobs = self.fake_jenkins.all_jobs
1219
1220 self.fake_jenkins.fakeRelease('.*-merge')
1221 self.waitUntilSettled()
1222 ref_mp = jobs[-1].parameters['ZUUL_REF']
1223 self.fake_jenkins.fakeRelease('.*-merge')
1224 self.waitUntilSettled()
1225 self.fake_jenkins.fakeRelease('.*-merge')
1226 self.waitUntilSettled()
1227 ref_master = jobs[-1].parameters['ZUUL_REF']
1228 self.fake_jenkins.hold_jobs_in_queue = False
1229 self.fake_jenkins.fakeRelease()
1230 self.waitUntilSettled()
1231
James E. Blair1dbd5082012-08-23 15:12:15 -07001232 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001233 repo = git.Repo(path)
1234
1235 repo_messages = [c.message.strip()
1236 for c in repo.iter_commits(ref_master)]
1237 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001238 correct_messages = ['initial commit', 'A-1', 'C-1']
1239 assert repo_messages == correct_messages
1240
1241 repo_messages = [c.message.strip()
1242 for c in repo.iter_commits(ref_mp)]
1243 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001244 correct_messages = ['initial commit', 'mp commit', 'B-1']
1245 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001246 self.assertEmptyQueues()
James E. Blair7f71c802012-08-22 13:04:32 -07001247
1248 def test_one_job_project(self):
1249 "Test that queueing works with one job"
1250 A = self.fake_gerrit.addFakeChange('org/one-job-project',
1251 'master', 'A')
1252 B = self.fake_gerrit.addFakeChange('org/one-job-project',
1253 'master', 'B')
1254 A.addApproval('CRVW', 2)
1255 B.addApproval('CRVW', 2)
1256 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1257 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1258 self.waitUntilSettled()
1259
1260 jobs = self.fake_jenkins.all_jobs
1261 finished_jobs = self.fake_jenkins.job_history
James E. Blair7f71c802012-08-22 13:04:32 -07001262
1263 assert A.data['status'] == 'MERGED'
1264 assert A.reported == 2
1265 assert B.data['status'] == 'MERGED'
1266 assert B.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001267 self.assertEmptyQueues()
James E. Blaircaec0c52012-08-22 14:52:22 -07001268
1269 def test_dependent_changes_dequeue(self):
1270 "Test that dependent patches are not needlessly tested"
1271 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1272 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1273 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1274 A.addApproval('CRVW', 2)
1275 B.addApproval('CRVW', 2)
1276 C.addApproval('CRVW', 2)
1277
1278 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1279 M1.setMerged()
1280
1281 # C -> B -> A -> M1
1282
1283 C.setDependsOn(B, 1)
1284 B.setDependsOn(A, 1)
1285 A.setDependsOn(M1, 1)
1286
1287 self.fake_jenkins.fakeAddFailTest('project-merge', A)
1288
1289 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1290 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1291 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1292
1293 self.waitUntilSettled()
1294
1295 jobs = self.fake_jenkins.all_jobs
1296 finished_jobs = self.fake_jenkins.job_history
1297
James E. Blair127bc182012-08-28 15:55:15 -07001298 for x in jobs:
1299 print x
1300 for x in finished_jobs:
1301 print x
1302
James E. Blairec590122012-08-22 15:19:31 -07001303 assert A.data['status'] == 'NEW'
1304 assert A.reported == 2
1305 assert B.data['status'] == 'NEW'
1306 assert B.reported == 2
1307 assert C.data['status'] == 'NEW'
1308 assert C.reported == 2
1309 assert len(finished_jobs) == 1
James E. Blaire0487072012-08-29 17:38:31 -07001310 self.assertEmptyQueues()
James E. Blairec590122012-08-22 15:19:31 -07001311
1312 def test_head_is_dequeued_once(self):
1313 "Test that if a change at the head fails it is dequeud only once"
1314 # If it's dequeued more than once, we should see extra
1315 # aborted jobs.
1316 self.fake_jenkins.hold_jobs_in_build = True
1317
1318 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1319 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1320 C = self.fake_gerrit.addFakeChange('org/project1', 'master', 'C')
1321 A.addApproval('CRVW', 2)
1322 B.addApproval('CRVW', 2)
1323 C.addApproval('CRVW', 2)
1324
1325 self.fake_jenkins.fakeAddFailTest('project1-test1', A)
1326 self.fake_jenkins.fakeAddFailTest('project1-test2', A)
1327 self.fake_jenkins.fakeAddFailTest('project1-project2-integration', A)
1328
1329 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1330 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1331 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1332
1333 self.waitUntilSettled()
1334 jobs = self.fake_jenkins.all_jobs
1335 finished_jobs = self.fake_jenkins.job_history
1336
1337 assert len(jobs) == 1
1338 assert jobs[0].name == 'project1-merge'
1339 assert job_has_changes(jobs[0], A)
1340
1341 self.fake_jenkins.fakeRelease('.*-merge')
1342 self.waitUntilSettled()
1343 self.fake_jenkins.fakeRelease('.*-merge')
1344 self.waitUntilSettled()
1345 self.fake_jenkins.fakeRelease('.*-merge')
1346 self.waitUntilSettled()
1347
1348 assert len(jobs) == 9
1349 assert jobs[0].name == 'project1-test1'
1350 assert jobs[1].name == 'project1-test2'
1351 assert jobs[2].name == 'project1-project2-integration'
1352 assert jobs[3].name == 'project1-test1'
1353 assert jobs[4].name == 'project1-test2'
1354 assert jobs[5].name == 'project1-project2-integration'
1355 assert jobs[6].name == 'project1-test1'
1356 assert jobs[7].name == 'project1-test2'
1357 assert jobs[8].name == 'project1-project2-integration'
1358
1359 jobs[0].release()
1360 self.waitUntilSettled()
1361
1362 assert len(jobs) == 3 # test2, integration, merge for B
1363 assert self.countJobResults(finished_jobs, 'ABORTED') == 6
1364
1365 self.fake_jenkins.hold_jobs_in_build = False
1366 self.fake_jenkins.fakeRelease()
1367 self.waitUntilSettled()
1368
1369 assert len(jobs) == 0
1370 assert len(finished_jobs) == 20
James E. Blaircaec0c52012-08-22 14:52:22 -07001371
1372 assert A.data['status'] == 'NEW'
James E. Blairec590122012-08-22 15:19:31 -07001373 assert B.data['status'] == 'MERGED'
1374 assert C.data['status'] == 'MERGED'
1375 assert A.reported == 2
1376 assert B.reported == 2
1377 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001378 self.assertEmptyQueues()
James E. Blair4ec821f2012-08-23 15:28:28 -07001379
1380 def test_nonvoting_job(self):
1381 "Test that non-voting jobs don't vote."
1382 A = self.fake_gerrit.addFakeChange('org/nonvoting-project',
1383 'master', 'A')
1384 A.addApproval('CRVW', 2)
1385 self.fake_jenkins.fakeAddFailTest('nonvoting-project-test2', A)
1386 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1387
1388 self.waitUntilSettled()
1389 jobs = self.fake_jenkins.all_jobs
1390 finished_jobs = self.fake_jenkins.job_history
1391
1392 assert A.data['status'] == 'MERGED'
1393 assert A.reported == 2
1394 assert finished_jobs[0]['result'] == 'SUCCESS'
1395 assert finished_jobs[1]['result'] == 'SUCCESS'
1396 assert finished_jobs[2]['result'] == 'FAILURE'
James E. Blaire0487072012-08-29 17:38:31 -07001397 self.assertEmptyQueues()
1398
1399 def test_check_queue_success(self):
1400 "Test successful check queue jobs."
1401 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1402 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1403
1404 self.waitUntilSettled()
1405 jobs = self.fake_jenkins.all_jobs
1406 finished_jobs = self.fake_jenkins.job_history
1407
1408 assert A.data['status'] == 'NEW'
1409 assert A.reported == 1
1410 assert finished_jobs[0]['result'] == 'SUCCESS'
1411 assert finished_jobs[1]['result'] == 'SUCCESS'
1412 assert finished_jobs[2]['result'] == 'SUCCESS'
1413 self.assertEmptyQueues()
1414
1415 def test_check_queue_failure(self):
1416 "Test failed check queue jobs."
1417 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1418 self.fake_jenkins.fakeAddFailTest('project-test2', A)
1419 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1420
1421 self.waitUntilSettled()
1422 jobs = self.fake_jenkins.all_jobs
1423 finished_jobs = self.fake_jenkins.job_history
1424
1425 assert A.data['status'] == 'NEW'
1426 assert A.reported == 1
1427 assert finished_jobs[0]['result'] == 'SUCCESS'
1428 assert finished_jobs[1]['result'] == 'SUCCESS'
1429 assert finished_jobs[2]['result'] == 'FAILURE'
1430 self.assertEmptyQueues()
James E. Blair127bc182012-08-28 15:55:15 -07001431
1432 def test_dependent_behind_dequeue(self):
1433 "test that dependent changes behind dequeued changes work"
1434 # This complicated test is a reproduction of a real life bug
1435 self.sched.reconfigure(self.config)
1436 self.fake_jenkins.hold_jobs_in_build = True
1437
1438 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1439 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1440 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
1441 D = self.fake_gerrit.addFakeChange('org/project2', 'master', 'D')
1442 E = self.fake_gerrit.addFakeChange('org/project2', 'master', 'E')
1443 F = self.fake_gerrit.addFakeChange('org/project3', 'master', 'F')
1444 D.setDependsOn(C, 1)
1445 E.setDependsOn(D, 1)
1446 A.addApproval('CRVW', 2)
1447 B.addApproval('CRVW', 2)
1448 C.addApproval('CRVW', 2)
1449 D.addApproval('CRVW', 2)
1450 E.addApproval('CRVW', 2)
1451 F.addApproval('CRVW', 2)
1452
1453 A.fail_merge = True
1454 jobs = self.fake_jenkins.all_jobs
1455 finished_jobs = self.fake_jenkins.job_history
1456
1457 # Change object re-use in the gerrit trigger is hidden if
1458 # changes are added in quick succession; waiting makes it more
1459 # like real life.
1460 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1461 self.waitUntilSettled()
1462 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1463 self.waitUntilSettled()
1464
1465 self.fake_jenkins.fakeRelease('.*-merge')
1466 self.waitUntilSettled()
1467 self.fake_jenkins.fakeRelease('.*-merge')
1468 self.waitUntilSettled()
1469
1470 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1471 self.waitUntilSettled()
1472 self.fake_gerrit.addEvent(D.addApproval('APRV', 1))
1473 self.waitUntilSettled()
1474 self.fake_gerrit.addEvent(E.addApproval('APRV', 1))
1475 self.waitUntilSettled()
1476 self.fake_gerrit.addEvent(F.addApproval('APRV', 1))
1477 self.waitUntilSettled()
1478
1479 self.fake_jenkins.fakeRelease('.*-merge')
1480 self.waitUntilSettled()
1481 self.fake_jenkins.fakeRelease('.*-merge')
1482 self.waitUntilSettled()
1483 self.fake_jenkins.fakeRelease('.*-merge')
1484 self.waitUntilSettled()
1485 self.fake_jenkins.fakeRelease('.*-merge')
1486 self.waitUntilSettled()
1487
James E. Blair4aa1ad62012-10-05 12:39:26 -07001488 for x in jobs:
1489 print x
James E. Blair127bc182012-08-28 15:55:15 -07001490 # all jobs running
1491 jobs[0].release()
1492 jobs[1].release()
1493 jobs[2].release()
1494 self.waitUntilSettled()
1495
1496 self.fake_jenkins.hold_jobs_in_build = False
1497 self.fake_jenkins.fakeRelease()
1498 self.waitUntilSettled()
1499
1500 for x in jobs:
1501 print x
1502 for x in finished_jobs:
1503 print x
1504 print self.sched.formatStatusHTML()
1505
1506 assert A.data['status'] == 'NEW'
1507 assert B.data['status'] == 'MERGED'
1508 assert C.data['status'] == 'MERGED'
1509 assert D.data['status'] == 'MERGED'
1510 assert E.data['status'] == 'MERGED'
1511 assert F.data['status'] == 'MERGED'
1512
1513 assert A.reported == 2
1514 assert B.reported == 2
1515 assert C.reported == 2
1516 assert D.reported == 2
1517 assert E.reported == 2
1518 assert F.reported == 2
1519
James E. Blair127bc182012-08-28 15:55:15 -07001520 assert self.countJobResults(finished_jobs, 'ABORTED') == 15
1521 assert len(finished_jobs) == 44
James E. Blaire0487072012-08-29 17:38:31 -07001522 self.assertEmptyQueues()
James E. Blair05fed602012-09-07 12:45:24 -07001523
1524 def test_merger_repack(self):
1525 "Test that the merger works after a repack"
1526 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1527 A.addApproval('CRVW', 2)
1528 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1529 self.waitUntilSettled()
1530 jobs = self.fake_jenkins.job_history
1531 job_names = [x['name'] for x in jobs]
1532 assert 'project-merge' in job_names
1533 assert 'project-test1' in job_names
1534 assert 'project-test2' in job_names
1535 assert jobs[0]['result'] == 'SUCCESS'
1536 assert jobs[1]['result'] == 'SUCCESS'
1537 assert jobs[2]['result'] == 'SUCCESS'
1538 assert A.data['status'] == 'MERGED'
1539 assert A.reported == 2
1540 self.assertEmptyQueues()
1541
1542 path = os.path.join(GIT_ROOT, "org/project")
1543 os.system('git --git-dir=%s/.git repack -afd' % path)
1544
1545 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1546 A.addApproval('CRVW', 2)
1547 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1548 self.waitUntilSettled()
1549 jobs = self.fake_jenkins.job_history
1550 job_names = [x['name'] for x in jobs]
1551 assert 'project-merge' in job_names
1552 assert 'project-test1' in job_names
1553 assert 'project-test2' in job_names
1554 assert jobs[0]['result'] == 'SUCCESS'
1555 assert jobs[1]['result'] == 'SUCCESS'
1556 assert jobs[2]['result'] == 'SUCCESS'
1557 assert A.data['status'] == 'MERGED'
1558 assert A.reported == 2
1559 self.assertEmptyQueues()
James E. Blair7ee88a22012-09-12 18:59:31 +02001560
1561 def test_nonexistent_job(self):
1562 "Test launching a job that doesn't exist"
1563 self.fake_jenkins.nonexistent_jobs.append('project-merge')
1564 self.jenkins.launch_retry_timeout = 0.1
1565
1566 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1567 A.addApproval('CRVW', 2)
1568 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1569 # There may be a thread about to report a lost change
1570 while A.reported < 2:
1571 self.waitUntilSettled()
1572 jobs = self.fake_jenkins.job_history
1573 job_names = [x['name'] for x in jobs]
1574 assert not job_names
1575 assert A.data['status'] == 'NEW'
1576 assert A.reported == 2
1577 self.assertEmptyQueues()
1578
1579 # Make sure things still work:
1580 self.fake_jenkins.nonexistent_jobs = []
1581 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1582 A.addApproval('CRVW', 2)
1583 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1584 self.waitUntilSettled()
1585 jobs = self.fake_jenkins.job_history
1586 job_names = [x['name'] for x in jobs]
1587 assert 'project-merge' in job_names
1588 assert 'project-test1' in job_names
1589 assert 'project-test2' in job_names
1590 assert jobs[0]['result'] == 'SUCCESS'
1591 assert jobs[1]['result'] == 'SUCCESS'
1592 assert jobs[2]['result'] == 'SUCCESS'
1593 assert A.data['status'] == 'MERGED'
1594 assert A.reported == 2
1595 self.assertEmptyQueues()