blob: 1951c37221e661f62780c626edd672b4a1cb7979 [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. Blair412e5582013-04-22 15:50:12 -070031import select
32import statsd
James E. Blair4886cc12012-07-18 15:39:41 -070033import shutil
James E. Blair412e5582013-04-22 15:50:12 -070034import socket
James E. Blair4886f282012-11-15 09:27:33 -080035import string
James E. Blair4886cc12012-07-18 15:39:41 -070036import git
James E. Blairb0fcae42012-07-17 11:12:10 -070037
James E. Blairb0fcae42012-07-17 11:12:10 -070038import zuul.scheduler
39import zuul.launcher.jenkins
40import zuul.trigger.gerrit
41
42FIXTURE_DIR = os.path.join(os.path.dirname(__file__),
43 'fixtures')
44CONFIG = ConfigParser.ConfigParser()
45CONFIG.read(os.path.join(FIXTURE_DIR, "zuul.conf"))
46
47CONFIG.set('zuul', 'layout_config',
48 os.path.join(FIXTURE_DIR, "layout.yaml"))
49
James E. Blair1dbd5082012-08-23 15:12:15 -070050TMP_ROOT = os.environ.get("ZUUL_TEST_ROOT", "/tmp")
51TEST_ROOT = os.path.join(TMP_ROOT, "zuul-test")
52UPSTREAM_ROOT = os.path.join(TEST_ROOT, "upstream")
53GIT_ROOT = os.path.join(TEST_ROOT, "git")
54
55CONFIG.set('zuul', 'git_dir', GIT_ROOT)
56
James E. Blairb0fcae42012-07-17 11:12:10 -070057logging.basicConfig(level=logging.DEBUG)
58
59
James E. Blair8cc15a82012-08-01 11:17:57 -070060def random_sha1():
61 return hashlib.sha1(str(random.random())).hexdigest()
62
63
James E. Blair4886cc12012-07-18 15:39:41 -070064class ChangeReference(git.Reference):
65 _common_path_default = "refs/changes"
66 _points_to_commits_only = True
67
68
69def init_repo(project):
70 parts = project.split('/')
James E. Blair1dbd5082012-08-23 15:12:15 -070071 path = os.path.join(UPSTREAM_ROOT, *parts[:-1])
James E. Blair4886cc12012-07-18 15:39:41 -070072 if not os.path.exists(path):
73 os.makedirs(path)
James E. Blair1dbd5082012-08-23 15:12:15 -070074 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blair4886cc12012-07-18 15:39:41 -070075 repo = git.Repo.init(path)
76
Paul Belangerb67aba12013-05-13 19:22:14 -040077 repo.config_writer().set_value('user', 'email', 'user@example.com')
78 repo.config_writer().set_value('user', 'name', 'User Name')
79 repo.config_writer().write()
80
James E. Blair4886cc12012-07-18 15:39:41 -070081 fn = os.path.join(path, 'README')
82 f = open(fn, 'w')
83 f.write("test\n")
84 f.close()
85 repo.index.add([fn])
86 repo.index.commit('initial commit')
James E. Blairc6294a52012-08-17 10:19:48 -070087 master = repo.create_head('master')
James E. Blair4886cc12012-07-18 15:39:41 -070088 repo.create_tag('init')
89
James E. Blairc6294a52012-08-17 10:19:48 -070090 mp = repo.create_head('mp')
91 repo.head.reference = mp
92 f = open(fn, 'a')
93 f.write("test mp\n")
94 f.close()
95 repo.index.add([fn])
96 repo.index.commit('mp commit')
97
98 repo.head.reference = master
99 repo.head.reset(index=True, working_tree=True)
100 repo.git.clean('-x', '-f', '-d')
101
James E. Blair4886cc12012-07-18 15:39:41 -0700102
James E. Blair4886f282012-11-15 09:27:33 -0800103def add_fake_change_to_repo(project, branch, change_num, patchset, msg, fn,
104 large):
James E. Blair1dbd5082012-08-23 15:12:15 -0700105 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blair4886cc12012-07-18 15:39:41 -0700106 repo = git.Repo(path)
107 ref = ChangeReference.create(repo, '1/%s/%s' % (change_num,
108 patchset),
109 'refs/tags/init')
110 repo.head.reference = ref
111 repo.head.reset(index=True, working_tree=True)
112 repo.git.clean('-x', '-f', '-d')
113
James E. Blair1dbd5082012-08-23 15:12:15 -0700114 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blair4886f282012-11-15 09:27:33 -0800115 if not large:
116 fn = os.path.join(path, fn)
117 f = open(fn, 'w')
118 f.write("test %s %s %s\n" % (branch, change_num, patchset))
119 f.close()
120 repo.index.add([fn])
121 else:
122 for fni in range(100):
123 fn = os.path.join(path, str(fni))
124 f = open(fn, 'w')
125 for ci in range(4096):
126 f.write(random.choice(string.printable))
127 f.close()
128 repo.index.add([fn])
129
James E. Blairdaabed22012-08-15 15:38:57 -0700130 return repo.index.commit(msg)
James E. Blair4886cc12012-07-18 15:39:41 -0700131
132
133def ref_has_change(ref, change):
James E. Blair1dbd5082012-08-23 15:12:15 -0700134 path = os.path.join(GIT_ROOT, change.project)
James E. Blair4886cc12012-07-18 15:39:41 -0700135 repo = git.Repo(path)
136 for commit in repo.iter_commits(ref):
137 if commit.message.strip() == ('%s-1' % change.subject):
138 return True
139 return False
140
141
142def job_has_changes(*args):
143 job = args[0]
144 commits = args[1:]
145 project = job.parameters['ZUUL_PROJECT']
James E. Blair1dbd5082012-08-23 15:12:15 -0700146 path = os.path.join(GIT_ROOT, project)
James E. Blair4886cc12012-07-18 15:39:41 -0700147 repo = git.Repo(path)
148 ref = job.parameters['ZUUL_REF']
James E. Blair81515ad2012-10-01 18:29:08 -0700149 sha = job.parameters['ZUUL_COMMIT']
James E. Blair4886cc12012-07-18 15:39:41 -0700150 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
James E. Blair81515ad2012-10-01 18:29:08 -0700151 repo_shas = [c.hexsha for c in repo.iter_commits(ref)]
James E. Blair4886cc12012-07-18 15:39:41 -0700152 commit_messages = ['%s-1' % commit.subject for commit in commits]
James E. Blair4886cc12012-07-18 15:39:41 -0700153 for msg in commit_messages:
154 if msg not in repo_messages:
155 return False
James E. Blair81515ad2012-10-01 18:29:08 -0700156 if repo_shas[0] != sha:
157 return False
James E. Blair4886cc12012-07-18 15:39:41 -0700158 return True
159
160
James E. Blairb0fcae42012-07-17 11:12:10 -0700161class FakeChange(object):
James E. Blair8c803f82012-07-31 16:25:42 -0700162 categories = {'APRV': ('Approved', -1, 1),
163 'CRVW': ('Code-Review', -2, 2),
164 'VRFY': ('Verified', -2, 2)}
James E. Blairb0fcae42012-07-17 11:12:10 -0700165
James E. Blair8cc15a82012-08-01 11:17:57 -0700166 def __init__(self, gerrit, number, project, branch, subject, status='NEW'):
167 self.gerrit = gerrit
James E. Blaird466dc42012-07-31 10:42:56 -0700168 self.reported = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700169 self.queried = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700170 self.patchsets = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700171 self.number = number
172 self.project = project
173 self.branch = branch
174 self.subject = subject
175 self.latest_patchset = 0
James E. Blair8c803f82012-07-31 16:25:42 -0700176 self.depends_on_change = None
177 self.needed_by_changes = []
James E. Blair127bc182012-08-28 15:55:15 -0700178 self.fail_merge = False
James E. Blair42f74822013-05-14 15:18:03 -0700179 self.messages = []
James E. Blairb0fcae42012-07-17 11:12:10 -0700180 self.data = {
181 'branch': branch,
182 'comments': [],
183 'commitMessage': subject,
184 'createdOn': time.time(),
James E. Blair8cc15a82012-08-01 11:17:57 -0700185 'id': 'I' + random_sha1(),
James E. Blairb0fcae42012-07-17 11:12:10 -0700186 'lastUpdated': time.time(),
187 'number': str(number),
188 'open': True,
189 'owner': {'email': 'user@example.com',
190 'name': 'User Name',
191 'username': 'username'},
192 'patchSets': self.patchsets,
193 'project': project,
194 'status': status,
195 'subject': subject,
James E. Blair8c803f82012-07-31 16:25:42 -0700196 'submitRecords': [],
James E. Blairb0fcae42012-07-17 11:12:10 -0700197 'url': 'https://hostname/%s' % number}
198
199 self.addPatchset()
James E. Blair8c803f82012-07-31 16:25:42 -0700200 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700201
James E. Blair70c71582013-03-06 08:50:50 -0800202 def addPatchset(self, files=[], large=False):
James E. Blairb0fcae42012-07-17 11:12:10 -0700203 self.latest_patchset += 1
James E. Blairdaabed22012-08-15 15:38:57 -0700204 if files:
205 fn = files[0]
206 else:
207 fn = '%s-%s' % (self.branch, self.number)
208 msg = self.subject + '-' + str(self.latest_patchset)
209 c = add_fake_change_to_repo(self.project, self.branch,
210 self.number, self.latest_patchset,
James E. Blair4886f282012-11-15 09:27:33 -0800211 msg, fn, large)
James E. Blair70c71582013-03-06 08:50:50 -0800212 ps_files = [{'file': '/COMMIT_MSG',
213 'type': 'ADDED'},
214 {'file': 'README',
215 'type': 'MODIFIED'}]
216 for f in files:
217 ps_files.append({'file': f, 'type': 'ADDED'})
James E. Blairb0fcae42012-07-17 11:12:10 -0700218 d = {'approvals': [],
219 'createdOn': time.time(),
James E. Blair70c71582013-03-06 08:50:50 -0800220 'files': ps_files,
James E. Blair8c803f82012-07-31 16:25:42 -0700221 'number': str(self.latest_patchset),
James E. Blairb0fcae42012-07-17 11:12:10 -0700222 'ref': 'refs/changes/1/%s/%s' % (self.number,
223 self.latest_patchset),
James E. Blairdaabed22012-08-15 15:38:57 -0700224 'revision': c.hexsha,
James E. Blairb0fcae42012-07-17 11:12:10 -0700225 'uploader': {'email': 'user@example.com',
226 'name': 'User name',
227 'username': 'user'}}
228 self.data['currentPatchSet'] = d
229 self.patchsets.append(d)
James E. Blair8c803f82012-07-31 16:25:42 -0700230 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700231
James E. Blaire0487072012-08-29 17:38:31 -0700232 def getPatchsetCreatedEvent(self, patchset):
233 event = {"type": "patchset-created",
234 "change": {"project": self.project,
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800235 "branch": self.branch,
236 "id": "I5459869c07352a31bfb1e7a8cac379cabfcb25af",
237 "number": str(self.number),
238 "subject": self.subject,
239 "owner": {"name": "User Name"},
240 "url": "https://hostname/3"},
James E. Blaire0487072012-08-29 17:38:31 -0700241 "patchSet": self.patchsets[patchset - 1],
242 "uploader": {"name": "User Name"}}
243 return event
244
James E. Blair42f74822013-05-14 15:18:03 -0700245 def getChangeRestoredEvent(self):
246 event = {"type": "change-restored",
247 "change": {"project": self.project,
248 "branch": self.branch,
249 "id": "I5459869c07352a31bfb1e7a8cac379cabfcb25af",
250 "number": str(self.number),
251 "subject": self.subject,
252 "owner": {"name": "User Name"},
253 "url": "https://hostname/3"},
254 "restorer": {"name": "User Name"},
255 "reason": ""}
256 return event
257
James E. Blairb0fcae42012-07-17 11:12:10 -0700258 def addApproval(self, category, value):
James E. Blair8c803f82012-07-31 16:25:42 -0700259 approval = {'description': self.categories[category][0],
260 'type': category,
261 'value': str(value)}
262 self.patchsets[-1]['approvals'].append(approval)
263 event = {'approvals': [approval],
James E. Blairb0fcae42012-07-17 11:12:10 -0700264 'author': {'email': 'user@example.com',
265 'name': 'User Name',
266 'username': 'username'},
267 'change': {'branch': self.branch,
268 'id': 'Iaa69c46accf97d0598111724a38250ae76a22c87',
269 'number': str(self.number),
270 'owner': {'email': 'user@example.com',
271 'name': 'User Name',
272 'username': 'username'},
273 'project': self.project,
274 'subject': self.subject,
275 'topic': 'master',
276 'url': 'https://hostname/459'},
277 'comment': '',
278 'patchSet': self.patchsets[-1],
279 'type': 'comment-added'}
James E. Blair8c803f82012-07-31 16:25:42 -0700280 self.data['submitRecords'] = self.getSubmitRecords()
James E. Blairb0fcae42012-07-17 11:12:10 -0700281 return json.loads(json.dumps(event))
282
James E. Blair8c803f82012-07-31 16:25:42 -0700283 def getSubmitRecords(self):
284 status = {}
285 for cat in self.categories.keys():
286 status[cat] = 0
287
288 for a in self.patchsets[-1]['approvals']:
289 cur = status[a['type']]
290 cat_min, cat_max = self.categories[a['type']][1:]
291 new = int(a['value'])
292 if new == cat_min:
293 cur = new
294 elif abs(new) > abs(cur):
295 cur = new
296 status[a['type']] = cur
297
298 labels = []
299 ok = True
300 for typ, cat in self.categories.items():
301 cur = status[typ]
302 cat_min, cat_max = cat[1:]
303 if cur == cat_min:
304 value = 'REJECT'
305 ok = False
306 elif cur == cat_max:
307 value = 'OK'
308 else:
309 value = 'NEED'
310 ok = False
311 labels.append({'label': cat[0], 'status': value})
312 if ok:
313 return [{'status': 'OK'}]
314 return [{'status': 'NOT_READY',
315 'labels': labels}]
316
317 def setDependsOn(self, other, patchset):
318 self.depends_on_change = other
319 d = {'id': other.data['id'],
320 'number': other.data['number'],
321 'ref': other.patchsets[patchset - 1]['ref']
322 }
323 self.data['dependsOn'] = [d]
324
325 other.needed_by_changes.append(self)
326 needed = other.data.get('neededBy', [])
327 d = {'id': self.data['id'],
328 'number': self.data['number'],
329 'ref': self.patchsets[patchset - 1]['ref'],
330 'revision': self.patchsets[patchset - 1]['revision']
331 }
332 needed.append(d)
333 other.data['neededBy'] = needed
334
James E. Blairb0fcae42012-07-17 11:12:10 -0700335 def query(self):
James E. Blair8c803f82012-07-31 16:25:42 -0700336 self.queried += 1
337 d = self.data.get('dependsOn')
338 if d:
339 d = d[0]
340 if (self.depends_on_change.patchsets[-1]['ref'] == d['ref']):
341 d['isCurrentPatchSet'] = True
342 else:
343 d['isCurrentPatchSet'] = False
James E. Blairb0fcae42012-07-17 11:12:10 -0700344 return json.loads(json.dumps(self.data))
345
346 def setMerged(self):
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800347 if (self.depends_on_change and
348 self.depends_on_change.data['status'] != 'MERGED'):
James E. Blaircaec0c52012-08-22 14:52:22 -0700349 return
James E. Blair127bc182012-08-28 15:55:15 -0700350 if self.fail_merge:
351 return
James E. Blairb0fcae42012-07-17 11:12:10 -0700352 self.data['status'] = 'MERGED'
353 self.open = False
James E. Blairdaabed22012-08-15 15:38:57 -0700354
James E. Blair1dbd5082012-08-23 15:12:15 -0700355 path = os.path.join(UPSTREAM_ROOT, self.project)
James E. Blairdaabed22012-08-15 15:38:57 -0700356 repo = git.Repo(path)
357 repo.heads[self.branch].commit = \
358 repo.commit(self.patchsets[-1]['revision'])
James E. Blairb0fcae42012-07-17 11:12:10 -0700359
James E. Blaird466dc42012-07-31 10:42:56 -0700360 def setReported(self):
361 self.reported += 1
362
James E. Blairb0fcae42012-07-17 11:12:10 -0700363
364class FakeGerrit(object):
365 def __init__(self, *args, **kw):
366 self.event_queue = Queue.Queue()
367 self.fixture_dir = os.path.join(FIXTURE_DIR, 'gerrit')
368 self.change_number = 0
369 self.changes = {}
370
371 def addFakeChange(self, project, branch, subject):
372 self.change_number += 1
James E. Blair8cc15a82012-08-01 11:17:57 -0700373 c = FakeChange(self, self.change_number, project, branch, subject)
James E. Blairb0fcae42012-07-17 11:12:10 -0700374 self.changes[self.change_number] = c
375 return c
376
377 def addEvent(self, data):
378 return self.event_queue.put(data)
379
380 def getEvent(self):
381 return self.event_queue.get()
382
383 def eventDone(self):
384 self.event_queue.task_done()
385
386 def review(self, project, changeid, message, action):
James E. Blaird466dc42012-07-31 10:42:56 -0700387 number, ps = changeid.split(',')
388 change = self.changes[int(number)]
James E. Blair42f74822013-05-14 15:18:03 -0700389 change.messages.append(message)
James E. Blairb0fcae42012-07-17 11:12:10 -0700390 if 'submit' in action:
James E. Blairb0fcae42012-07-17 11:12:10 -0700391 change.setMerged()
James E. Blaird466dc42012-07-31 10:42:56 -0700392 if message:
393 change.setReported()
James E. Blairb0fcae42012-07-17 11:12:10 -0700394
395 def query(self, number):
396 change = self.changes[int(number)]
397 return change.query()
398
399 def startWatching(self, *args, **kw):
400 pass
401
402
403class FakeJenkinsEvent(object):
404 def __init__(self, name, number, parameters, phase, status=None):
Zhongyue Luo5d556072012-09-21 02:00:47 +0900405 data = {
406 'build': {
407 'full_url': 'https://server/job/%s/%s/' % (name, number),
408 'number': number,
409 'parameters': parameters,
410 'phase': phase,
411 'url': 'job/%s/%s/' % (name, number),
412 },
413 'name': name,
414 'url': 'job/%s/' % name,
415 }
James E. Blairb0fcae42012-07-17 11:12:10 -0700416 if status:
417 data['build']['status'] = status
418 self.body = json.dumps(data)
419
420
421class FakeJenkinsJob(threading.Thread):
422 log = logging.getLogger("zuul.test")
423
424 def __init__(self, jenkins, callback, name, number, parameters):
425 threading.Thread.__init__(self)
426 self.jenkins = jenkins
427 self.callback = callback
428 self.name = name
429 self.number = number
430 self.parameters = parameters
431 self.wait_condition = threading.Condition()
432 self.waiting = False
James E. Blaird466dc42012-07-31 10:42:56 -0700433 self.aborted = False
434 self.canceled = False
435 self.created = time.time()
James E. Blairb0fcae42012-07-17 11:12:10 -0700436
437 def release(self):
438 self.wait_condition.acquire()
439 self.wait_condition.notify()
440 self.waiting = False
441 self.log.debug("Job %s released" % (self.parameters['UUID']))
442 self.wait_condition.release()
443
444 def isWaiting(self):
445 self.wait_condition.acquire()
446 if self.waiting:
447 ret = True
448 else:
449 ret = False
450 self.wait_condition.release()
451 return ret
452
453 def _wait(self):
454 self.wait_condition.acquire()
455 self.waiting = True
456 self.log.debug("Job %s waiting" % (self.parameters['UUID']))
457 self.wait_condition.wait()
458 self.wait_condition.release()
459
460 def run(self):
461 self.jenkins.fakeEnqueue(self)
462 if self.jenkins.hold_jobs_in_queue:
463 self._wait()
464 self.jenkins.fakeDequeue(self)
James E. Blaird466dc42012-07-31 10:42:56 -0700465 if self.canceled:
466 self.jenkins.all_jobs.remove(self)
467 return
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800468 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
469 self.number,
470 self.parameters,
471 'STARTED'))
James E. Blairb0fcae42012-07-17 11:12:10 -0700472 if self.jenkins.hold_jobs_in_build:
473 self._wait()
474 self.log.debug("Job %s continuing" % (self.parameters['UUID']))
James E. Blairb02a3bb2012-07-30 17:49:55 -0700475
476 result = 'SUCCESS'
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800477 if (('ZUUL_REF' in self.parameters) and
478 self.jenkins.fakeShouldFailTest(self.name,
479 self.parameters['ZUUL_REF'])):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700480 result = 'FAILURE'
James E. Blaird466dc42012-07-31 10:42:56 -0700481 if self.aborted:
482 result = 'ABORTED'
James E. Blairb02a3bb2012-07-30 17:49:55 -0700483
James E. Blair0018a6c2013-02-27 14:11:45 -0800484 changes = None
485 if 'ZUUL_CHANGE_IDS' in self.parameters:
486 changes = self.parameters['ZUUL_CHANGE_IDS']
487
James E. Blairb0fcae42012-07-17 11:12:10 -0700488 self.jenkins.fakeAddHistory(name=self.name, number=self.number,
James E. Blair0018a6c2013-02-27 14:11:45 -0800489 result=result, changes=changes)
James E. Blairff791972013-01-09 11:45:43 -0800490 self.jenkins.lock.acquire()
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800491 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
492 self.number,
493 self.parameters,
494 'COMPLETED',
495 result))
496 self.callback.jenkins_endpoint(FakeJenkinsEvent(self.name,
497 self.number,
498 self.parameters,
499 'FINISHED',
500 result))
James E. Blairb0fcae42012-07-17 11:12:10 -0700501 self.jenkins.all_jobs.remove(self)
James E. Blairff791972013-01-09 11:45:43 -0800502 self.jenkins.lock.release()
James E. Blairb0fcae42012-07-17 11:12:10 -0700503
504
505class FakeJenkins(object):
506 log = logging.getLogger("zuul.test")
507
508 def __init__(self, *args, **kw):
509 self.queue = []
510 self.all_jobs = []
511 self.job_counter = {}
James E. Blaird466dc42012-07-31 10:42:56 -0700512 self.queue_counter = 0
James E. Blairb0fcae42012-07-17 11:12:10 -0700513 self.job_history = []
514 self.hold_jobs_in_queue = False
515 self.hold_jobs_in_build = False
James E. Blairb02a3bb2012-07-30 17:49:55 -0700516 self.fail_tests = {}
James E. Blair7ee88a22012-09-12 18:59:31 +0200517 self.nonexistent_jobs = []
James E. Blairff791972013-01-09 11:45:43 -0800518 self.lock = threading.Lock()
James E. Blairb0fcae42012-07-17 11:12:10 -0700519
520 def fakeEnqueue(self, job):
521 self.queue.append(job)
522
523 def fakeDequeue(self, job):
524 self.queue.remove(job)
525
James E. Blair0018a6c2013-02-27 14:11:45 -0800526 class FakeJobHistory(object):
527 def __init__(self, **kw):
528 self.__dict__.update(kw)
529
530 def __repr__(self):
531 return ("<Completed job, result: %s name: %s #%s changes: %s>" %
532 (self.result, self.name, self.number, self.changes))
533
James E. Blairb0fcae42012-07-17 11:12:10 -0700534 def fakeAddHistory(self, **kw):
James E. Blair0018a6c2013-02-27 14:11:45 -0800535 self.job_history.append(self.FakeJobHistory(**kw))
James E. Blairb0fcae42012-07-17 11:12:10 -0700536
537 def fakeRelease(self, regex=None):
538 all_jobs = self.all_jobs[:]
539 self.log.debug("releasing jobs %s (%s)" % (regex, len(self.all_jobs)))
540 for job in all_jobs:
541 if not regex or re.match(regex, job.name):
542 self.log.debug("releasing job %s" % (job.parameters['UUID']))
543 job.release()
544 else:
Zhongyue Luoaa85ebf2012-09-21 16:38:33 +0800545 self.log.debug("not releasing job %s" %
546 (job.parameters['UUID']))
James E. Blairb0fcae42012-07-17 11:12:10 -0700547 self.log.debug("done releasing jobs %s (%s)" % (regex,
548 len(self.all_jobs)))
549
550 def fakeAllWaiting(self, regex=None):
James E. Blair4aa1ad62012-10-05 12:39:26 -0700551 all_jobs = self.all_jobs[:] + self.queue[:]
James E. Blairb0fcae42012-07-17 11:12:10 -0700552 for job in all_jobs:
553 self.log.debug("job %s %s" % (job.parameters['UUID'],
554 job.isWaiting()))
555 if not job.isWaiting():
556 return False
557 return True
558
James E. Blairb02a3bb2012-07-30 17:49:55 -0700559 def fakeAddFailTest(self, name, change):
560 l = self.fail_tests.get(name, [])
561 l.append(change)
562 self.fail_tests[name] = l
563
James E. Blair4886cc12012-07-18 15:39:41 -0700564 def fakeShouldFailTest(self, name, ref):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700565 l = self.fail_tests.get(name, [])
566 for change in l:
James E. Blair4886cc12012-07-18 15:39:41 -0700567 if ref_has_change(ref, change):
James E. Blairb02a3bb2012-07-30 17:49:55 -0700568 return True
569 return False
570
James E. Blairb0fcae42012-07-17 11:12:10 -0700571 def build_job(self, name, parameters):
James E. Blair7ee88a22012-09-12 18:59:31 +0200572 if name in self.nonexistent_jobs:
573 raise Exception("Job does not exist")
James E. Blairb0fcae42012-07-17 11:12:10 -0700574 count = self.job_counter.get(name, 0)
575 count += 1
576 self.job_counter[name] = count
James E. Blaird466dc42012-07-31 10:42:56 -0700577
578 queue_count = self.queue_counter
579 self.queue_counter += 1
James E. Blairb0fcae42012-07-17 11:12:10 -0700580 job = FakeJenkinsJob(self, self.callback, name, count, parameters)
James E. Blaird466dc42012-07-31 10:42:56 -0700581 job.queue_id = queue_count
582
James E. Blairb0fcae42012-07-17 11:12:10 -0700583 self.all_jobs.append(job)
584 job.start()
585
James E. Blaird466dc42012-07-31 10:42:56 -0700586 def stop_build(self, name, number):
587 for job in self.all_jobs:
588 if job.name == name and job.number == number:
589 job.aborted = True
590 job.release()
591 return
592
593 def cancel_queue(self, id):
594 for job in self.queue:
595 if job.queue_id == id:
596 job.canceled = True
597 job.release()
598 return
599
600 def get_queue_info(self):
601 items = []
James E. Blair1490eba2013-03-06 19:14:00 -0800602 for job in self.queue[:]:
603 self.log.debug("Queue info: %s %s" % (job.name,
604 job.parameters['UUID']))
James E. Blaird466dc42012-07-31 10:42:56 -0700605 paramstr = ''
606 paramlst = []
607 d = {'actions': [{'parameters': paramlst},
608 {'causes': [{'shortDescription':
609 'Started by user Jenkins',
610 'userId': 'jenkins',
611 'userName': 'Jenkins'}]}],
612 'blocked': False,
613 'buildable': True,
614 'buildableStartMilliseconds': (job.created * 1000) + 5,
615 'id': job.queue_id,
616 'inQueueSince': (job.created * 1000),
617 'params': paramstr,
618 'stuck': False,
619 'task': {'color': 'blue',
620 'name': job.name,
621 'url': 'https://server/job/%s/' % job.name},
622 'why': 'Waiting for next available executor'}
623 for k, v in job.parameters.items():
624 paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
625 pd = {'name': k, 'value': v}
626 paramlst.append(pd)
627 items.append(d)
628 return items
629
James E. Blairb0fcae42012-07-17 11:12:10 -0700630 def set_build_description(self, *args, **kw):
631 pass
632
633
634class FakeJenkinsCallback(zuul.launcher.jenkins.JenkinsCallback):
635 def start(self):
636 pass
637
638
James E. Blair8cc15a82012-08-01 11:17:57 -0700639class FakeURLOpener(object):
640 def __init__(self, fake_gerrit, url):
641 self.fake_gerrit = fake_gerrit
642 self.url = url
643
644 def read(self):
645 res = urlparse.urlparse(self.url)
646 path = res.path
647 project = '/'.join(path.split('/')[2:-2])
James E. Blair35956c52012-09-17 22:13:36 +0200648 ret = '001e# service=git-upload-pack\n'
649 ret += ('000000a31270149696713ba7e06f1beb760f20d359c4abed HEAD\x00'
650 'multi_ack thin-pack side-band side-band-64k ofs-delta '
651 'shallow no-progress include-tag multi_ack_detailed no-done\n')
James E. Blair1dbd5082012-08-23 15:12:15 -0700652 path = os.path.join(UPSTREAM_ROOT, project)
James E. Blairdaabed22012-08-15 15:38:57 -0700653 repo = git.Repo(path)
654 for ref in repo.refs:
James E. Blair35956c52012-09-17 22:13:36 +0200655 r = ref.object.hexsha + ' ' + ref.path + '\n'
656 ret += '%04x%s' % (len(r) + 4, r)
657 ret += '0000'
James E. Blair8cc15a82012-08-01 11:17:57 -0700658 return ret
659
660
James E. Blair4886cc12012-07-18 15:39:41 -0700661class FakeGerritTrigger(zuul.trigger.gerrit.Gerrit):
662 def getGitUrl(self, project):
James E. Blair1dbd5082012-08-23 15:12:15 -0700663 return os.path.join(UPSTREAM_ROOT, project.name)
James E. Blair4886cc12012-07-18 15:39:41 -0700664
665
James E. Blair412e5582013-04-22 15:50:12 -0700666class FakeStatsd(threading.Thread):
667 def __init__(self):
668 threading.Thread.__init__(self)
669 self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
670 self.sock.bind(('', 0))
671 self.port = self.sock.getsockname()[1]
672 self.wake_read, self.wake_write = os.pipe()
673 self.stats = []
674
675 def run(self):
676 while True:
Ori Livnehd75cc182013-05-02 18:44:46 -0700677 read_ready = select.select([self.sock, self.wake_read], [], [])[0]
678 for sock in read_ready:
679 if sock is self.sock:
James E. Blair412e5582013-04-22 15:50:12 -0700680 data = self.sock.recvfrom(1024)
681 if not data:
682 return
683 self.stats.append(data[0])
Ori Livnehd75cc182013-05-02 18:44:46 -0700684 else:
685 # wake_read
James E. Blair412e5582013-04-22 15:50:12 -0700686 return
687
688 def stop(self):
689 os.write(self.wake_write, '1\n')
690
691
James E. Blairb0fcae42012-07-17 11:12:10 -0700692class testScheduler(unittest.TestCase):
693 log = logging.getLogger("zuul.test")
694
695 def setUp(self):
James E. Blair1dbd5082012-08-23 15:12:15 -0700696 if os.path.exists(TEST_ROOT):
697 shutil.rmtree(TEST_ROOT)
698 os.makedirs(TEST_ROOT)
699 os.makedirs(UPSTREAM_ROOT)
700 os.makedirs(GIT_ROOT)
James E. Blair4886cc12012-07-18 15:39:41 -0700701
702 # For each project in config:
703 init_repo("org/project")
704 init_repo("org/project1")
705 init_repo("org/project2")
James E. Blair127bc182012-08-28 15:55:15 -0700706 init_repo("org/project3")
James E. Blair7f71c802012-08-22 13:04:32 -0700707 init_repo("org/one-job-project")
James E. Blair4ec821f2012-08-23 15:28:28 -0700708 init_repo("org/nonvoting-project")
Antoine Musso80edd5a2013-02-13 15:37:53 +0100709 init_repo("org/templated-project")
James E. Blairb0fcae42012-07-17 11:12:10 -0700710 self.config = CONFIG
James E. Blair412e5582013-04-22 15:50:12 -0700711
712 self.statsd = FakeStatsd()
713 os.environ['STATSD_HOST'] = 'localhost'
714 os.environ['STATSD_PORT'] = str(self.statsd.port)
715 self.statsd.start()
716 # the statsd client object is configured in the statsd module import
717 reload(statsd)
718 reload(zuul.scheduler)
719
James E. Blairb0fcae42012-07-17 11:12:10 -0700720 self.sched = zuul.scheduler.Scheduler()
721
722 def jenkinsFactory(*args, **kw):
723 self.fake_jenkins = FakeJenkins()
724 return self.fake_jenkins
725
726 def jenkinsCallbackFactory(*args, **kw):
727 self.fake_jenkins_callback = FakeJenkinsCallback(*args, **kw)
728 return self.fake_jenkins_callback
729
James E. Blair8cc15a82012-08-01 11:17:57 -0700730 def URLOpenerFactory(*args, **kw):
731 args = [self.fake_gerrit] + list(args)
732 return FakeURLOpener(*args, **kw)
733
James E. Blairb0fcae42012-07-17 11:12:10 -0700734 zuul.launcher.jenkins.ExtendedJenkins = jenkinsFactory
735 zuul.launcher.jenkins.JenkinsCallback = jenkinsCallbackFactory
James E. Blair8cc15a82012-08-01 11:17:57 -0700736 urllib2.urlopen = URLOpenerFactory
James E. Blairb0fcae42012-07-17 11:12:10 -0700737 self.jenkins = zuul.launcher.jenkins.Jenkins(self.config, self.sched)
738 self.fake_jenkins.callback = self.fake_jenkins_callback
739
740 zuul.lib.gerrit.Gerrit = FakeGerrit
741
James E. Blair4886cc12012-07-18 15:39:41 -0700742 self.gerrit = FakeGerritTrigger(self.config, self.sched)
James E. Blair8cc15a82012-08-01 11:17:57 -0700743 self.gerrit.replication_timeout = 1.5
744 self.gerrit.replication_retry_interval = 0.5
James E. Blairb0fcae42012-07-17 11:12:10 -0700745 self.fake_gerrit = self.gerrit.gerrit
746
747 self.sched.setLauncher(self.jenkins)
748 self.sched.setTrigger(self.gerrit)
749
750 self.sched.start()
751 self.sched.reconfigure(self.config)
752 self.sched.resume()
753
754 def tearDown(self):
755 self.jenkins.stop()
756 self.gerrit.stop()
757 self.sched.stop()
758 self.sched.join()
James E. Blair412e5582013-04-22 15:50:12 -0700759 self.statsd.stop()
760 self.statsd.join()
James E. Blair1dbd5082012-08-23 15:12:15 -0700761 #shutil.rmtree(TEST_ROOT)
James E. Blairb0fcae42012-07-17 11:12:10 -0700762
763 def waitUntilSettled(self):
764 self.log.debug("Waiting until settled...")
765 start = time.time()
766 while True:
767 if time.time() - start > 10:
768 print 'queue status:',
769 print self.sched.trigger_event_queue.empty(),
770 print self.sched.result_event_queue.empty(),
771 print self.fake_gerrit.event_queue.empty(),
772 raise Exception("Timeout waiting for Zuul to settle")
James E. Blairff791972013-01-09 11:45:43 -0800773 # Make sure our fake jenkins doesn't end any jobs
774 # (and therefore, emit events) while we're checking
775 self.fake_jenkins.lock.acquire()
776 # Join ensures that the queue is empty _and_ events have been
777 # processed
James E. Blairb0fcae42012-07-17 11:12:10 -0700778 self.fake_gerrit.event_queue.join()
James E. Blairff791972013-01-09 11:45:43 -0800779 self.sched.trigger_event_queue.join()
780 self.sched.result_event_queue.join()
James E. Blairb0fcae42012-07-17 11:12:10 -0700781 if (self.sched.trigger_event_queue.empty() and
782 self.sched.result_event_queue.empty() and
783 self.fake_gerrit.event_queue.empty() and
784 self.fake_jenkins.fakeAllWaiting()):
James E. Blairff791972013-01-09 11:45:43 -0800785 self.fake_jenkins.lock.release()
James E. Blairb0fcae42012-07-17 11:12:10 -0700786 self.log.debug("...settled.")
787 return
James E. Blairff791972013-01-09 11:45:43 -0800788 self.fake_jenkins.lock.release()
James E. Blairb0fcae42012-07-17 11:12:10 -0700789 self.sched.wake_event.wait(0.1)
790
James E. Blaird466dc42012-07-31 10:42:56 -0700791 def countJobResults(self, jobs, result):
James E. Blair0018a6c2013-02-27 14:11:45 -0800792 jobs = filter(lambda x: x.result == result, jobs)
James E. Blaird466dc42012-07-31 10:42:56 -0700793 return len(jobs)
794
James E. Blaire0487072012-08-29 17:38:31 -0700795 def assertEmptyQueues(self):
796 # Make sure there are no orphaned jobs
797 for pipeline in self.sched.pipelines.values():
798 for queue in pipeline.queues:
799 if len(queue.queue) != 0:
James E. Blairf62d4282012-12-31 17:01:50 -0800800 print 'pipeline %s queue %s contents %s' % (
801 pipeline.name, queue.name, queue.queue)
James E. Blaire0487072012-08-29 17:38:31 -0700802 assert len(queue.queue) == 0
803 if len(queue.severed_heads) != 0:
804 print 'heads', queue.severed_heads
805 assert len(queue.severed_heads) == 0
806
James E. Blair412e5582013-04-22 15:50:12 -0700807 def assertReportedStat(self, key, value=None):
808 start = time.time()
809 while time.time() < (start + 5):
810 for stat in self.statsd.stats:
811 k, v = stat.split(':')
812 if key == k:
813 if value is None:
814 return
815 if value == v:
816 return
817 time.sleep(0.1)
818
819 pprint.pprint(self.statsd.stats)
820 raise Exception("Key %s not found in reported stats" % key)
821
James E. Blairb0fcae42012-07-17 11:12:10 -0700822 def test_jobs_launched(self):
823 "Test that jobs are launched and a change is merged"
824 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair8c803f82012-07-31 16:25:42 -0700825 A.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700826 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
827 self.waitUntilSettled()
828 jobs = self.fake_jenkins.job_history
James E. Blair0018a6c2013-02-27 14:11:45 -0800829 job_names = [x.name for x in jobs]
James E. Blairb0fcae42012-07-17 11:12:10 -0700830 assert 'project-merge' in job_names
831 assert 'project-test1' in job_names
832 assert 'project-test2' in job_names
James E. Blair0018a6c2013-02-27 14:11:45 -0800833 assert jobs[0].result == 'SUCCESS'
834 assert jobs[1].result == 'SUCCESS'
835 assert jobs[2].result == 'SUCCESS'
James E. Blairb0fcae42012-07-17 11:12:10 -0700836 assert A.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700837 assert A.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700838 self.assertEmptyQueues()
James E. Blairb0fcae42012-07-17 11:12:10 -0700839
James E. Blair412e5582013-04-22 15:50:12 -0700840 self.assertReportedStat('gerrit.event.comment-added', '1|c')
841 self.assertReportedStat('zuul.pipeline.gate.current_changes', '1|g')
842 self.assertReportedStat('zuul.job.project-merge')
843 self.assertReportedStat('zuul.pipeline.gate.resident_time')
844 self.assertReportedStat('zuul.pipeline.gate.total_changes', '1|c')
845 self.assertReportedStat(
846 'zuul.pipeline.gate.org.project.resident_time')
847 self.assertReportedStat(
848 'zuul.pipeline.gate.org.project.total_changes', '1|c')
849
James E. Blair42f74822013-05-14 15:18:03 -0700850 def test_duplicate_pipelines(self):
851 "Test that a change matching multiple pipelines works"
852 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
853 self.fake_gerrit.addEvent(A.getChangeRestoredEvent())
854 self.waitUntilSettled()
855 jobs = self.fake_jenkins.job_history
856
857 print jobs
858 print A.messages
859
860 self.assertEmptyQueues()
861
862 assert len(jobs) == 2
863 jobs[0].name == 'project-test1'
864 jobs[1].name == 'project-test1'
865
866 assert len(A.messages) == 2
867 if 'dup1/project-test1' in A.messages[0]:
868 assert 'dup1/project-test1' in A.messages[0]
869 assert 'dup2/project-test1' not in A.messages[0]
870 assert 'dup1/project-test1' not in A.messages[1]
871 assert 'dup2/project-test1' in A.messages[1]
872 else:
873 assert 'dup1/project-test1' in A.messages[1]
874 assert 'dup2/project-test1' not in A.messages[1]
875 assert 'dup1/project-test1' not in A.messages[0]
876 assert 'dup2/project-test1' in A.messages[0]
877
James E. Blairb0fcae42012-07-17 11:12:10 -0700878 def test_parallel_changes(self):
879 "Test that changes are tested in parallel and merged in series"
880 self.fake_jenkins.hold_jobs_in_build = True
881 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
882 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
883 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700884 A.addApproval('CRVW', 2)
885 B.addApproval('CRVW', 2)
886 C.addApproval('CRVW', 2)
James E. Blairb0fcae42012-07-17 11:12:10 -0700887
888 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
889 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
890 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
891
892 self.waitUntilSettled()
893 jobs = self.fake_jenkins.all_jobs
894 assert len(jobs) == 1
895 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700896 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700897
898 self.fake_jenkins.fakeRelease('.*-merge')
899 self.waitUntilSettled()
900 assert len(jobs) == 3
901 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700902 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700903 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700904 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700905 assert jobs[2].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700906 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700907
908 self.fake_jenkins.fakeRelease('.*-merge')
909 self.waitUntilSettled()
910 assert len(jobs) == 5
911 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700912 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700913 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700914 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700915
916 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700917 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700918 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700919 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700920
921 assert jobs[4].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700922 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700923
924 self.fake_jenkins.fakeRelease('.*-merge')
925 self.waitUntilSettled()
926 assert len(jobs) == 6
927 assert jobs[0].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700928 assert job_has_changes(jobs[0], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700929 assert jobs[1].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700930 assert job_has_changes(jobs[1], A)
James E. Blairb0fcae42012-07-17 11:12:10 -0700931
932 assert jobs[2].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700933 assert job_has_changes(jobs[2], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700934 assert jobs[3].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700935 assert job_has_changes(jobs[3], A, B)
James E. Blairb0fcae42012-07-17 11:12:10 -0700936
937 assert jobs[4].name == 'project-test1'
James E. Blair4886cc12012-07-18 15:39:41 -0700938 assert job_has_changes(jobs[4], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700939 assert jobs[5].name == 'project-test2'
James E. Blair4886cc12012-07-18 15:39:41 -0700940 assert job_has_changes(jobs[5], A, B, C)
James E. Blairb0fcae42012-07-17 11:12:10 -0700941
942 self.fake_jenkins.hold_jobs_in_build = False
943 self.fake_jenkins.fakeRelease()
944 self.waitUntilSettled()
945 assert len(jobs) == 0
946
947 jobs = self.fake_jenkins.job_history
948 assert len(jobs) == 9
949 assert A.data['status'] == 'MERGED'
950 assert B.data['status'] == 'MERGED'
951 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700952 assert A.reported == 2
953 assert B.reported == 2
954 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700955 self.assertEmptyQueues()
James E. Blairb02a3bb2012-07-30 17:49:55 -0700956
957 def test_failed_changes(self):
958 "Test that a change behind a failed change is retested"
959 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
960 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
James E. Blair8c803f82012-07-31 16:25:42 -0700961 A.addApproval('CRVW', 2)
962 B.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700963
964 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
965 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
966
James E. Blair4886cc12012-07-18 15:39:41 -0700967 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700968
969 self.waitUntilSettled()
970 jobs = self.fake_jenkins.job_history
971 assert len(jobs) > 6
972 assert A.data['status'] == 'NEW'
973 assert B.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -0700974 assert A.reported == 2
975 assert B.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -0700976 self.assertEmptyQueues()
James E. Blairb02a3bb2012-07-30 17:49:55 -0700977
978 def test_independent_queues(self):
979 "Test that changes end up in the right queues"
980 self.fake_jenkins.hold_jobs_in_build = True
Zhongyue Luo5d556072012-09-21 02:00:47 +0900981 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blairb02a3bb2012-07-30 17:49:55 -0700982 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
983 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -0700984 A.addApproval('CRVW', 2)
985 B.addApproval('CRVW', 2)
986 C.addApproval('CRVW', 2)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700987
988 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
989 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
990 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
991
992 jobs = self.fake_jenkins.all_jobs
993 self.waitUntilSettled()
994
995 # There should be one merge job at the head of each queue running
996 assert len(jobs) == 2
997 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -0700998 assert job_has_changes(jobs[0], A)
James E. Blairb02a3bb2012-07-30 17:49:55 -0700999 assert jobs[1].name == 'project1-merge'
James E. Blair4886cc12012-07-18 15:39:41 -07001000 assert job_has_changes(jobs[1], B)
James E. Blairb02a3bb2012-07-30 17:49:55 -07001001
1002 # Release the current merge jobs
1003 self.fake_jenkins.fakeRelease('.*-merge')
1004 self.waitUntilSettled()
1005 # Release the merge job for project2 which is behind project1
1006 self.fake_jenkins.fakeRelease('.*-merge')
1007 self.waitUntilSettled()
1008
1009 # All the test jobs should be running:
1010 # project1 (3) + project2 (3) + project (2) = 8
1011 assert len(jobs) == 8
1012
1013 self.fake_jenkins.fakeRelease()
1014 self.waitUntilSettled()
1015 assert len(jobs) == 0
1016
1017 jobs = self.fake_jenkins.job_history
1018 assert len(jobs) == 11
1019 assert A.data['status'] == 'MERGED'
1020 assert B.data['status'] == 'MERGED'
1021 assert C.data['status'] == 'MERGED'
James E. Blaird466dc42012-07-31 10:42:56 -07001022 assert A.reported == 2
1023 assert B.reported == 2
1024 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001025 self.assertEmptyQueues()
James E. Blaird466dc42012-07-31 10:42:56 -07001026
1027 def test_failed_change_at_head(self):
1028 "Test that if a change at the head fails, jobs behind it are canceled"
1029 self.fake_jenkins.hold_jobs_in_build = True
1030
1031 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1032 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1033 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -07001034 A.addApproval('CRVW', 2)
1035 B.addApproval('CRVW', 2)
1036 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -07001037
James E. Blair4886cc12012-07-18 15:39:41 -07001038 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -07001039
1040 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1041 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1042 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1043
1044 self.waitUntilSettled()
1045 jobs = self.fake_jenkins.all_jobs
1046 finished_jobs = self.fake_jenkins.job_history
1047
1048 assert len(jobs) == 1
1049 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -07001050 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -07001051
1052 self.fake_jenkins.fakeRelease('.*-merge')
1053 self.waitUntilSettled()
1054 self.fake_jenkins.fakeRelease('.*-merge')
1055 self.waitUntilSettled()
1056 self.fake_jenkins.fakeRelease('.*-merge')
1057 self.waitUntilSettled()
1058
1059 assert len(jobs) == 6
1060 assert jobs[0].name == 'project-test1'
1061 assert jobs[1].name == 'project-test2'
1062 assert jobs[2].name == 'project-test1'
1063 assert jobs[3].name == 'project-test2'
1064 assert jobs[4].name == 'project-test1'
1065 assert jobs[5].name == 'project-test2'
1066
1067 jobs[0].release()
1068 self.waitUntilSettled()
1069
James E. Blairec590122012-08-22 15:19:31 -07001070 assert len(jobs) == 2 # project-test2, project-merge for B
James E. Blaird466dc42012-07-31 10:42:56 -07001071 assert self.countJobResults(finished_jobs, 'ABORTED') == 4
1072
1073 self.fake_jenkins.hold_jobs_in_build = False
1074 self.fake_jenkins.fakeRelease()
1075 self.waitUntilSettled()
1076
James E. Blair0018a6c2013-02-27 14:11:45 -08001077 for x in jobs:
1078 print x
1079 for x in finished_jobs:
1080 print x
1081
James E. Blaird466dc42012-07-31 10:42:56 -07001082 assert len(jobs) == 0
1083 assert len(finished_jobs) == 15
1084 assert A.data['status'] == 'NEW'
1085 assert B.data['status'] == 'MERGED'
1086 assert C.data['status'] == 'MERGED'
1087 assert A.reported == 2
1088 assert B.reported == 2
1089 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001090 self.assertEmptyQueues()
James E. Blaird466dc42012-07-31 10:42:56 -07001091
1092 def test_failed_change_at_head_with_queue(self):
1093 "Test that if a change at the head fails, queued jobs are canceled"
1094 self.fake_jenkins.hold_jobs_in_queue = True
1095
1096 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1097 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1098 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
James E. Blair8c803f82012-07-31 16:25:42 -07001099 A.addApproval('CRVW', 2)
1100 B.addApproval('CRVW', 2)
1101 C.addApproval('CRVW', 2)
James E. Blaird466dc42012-07-31 10:42:56 -07001102
James E. Blair4886cc12012-07-18 15:39:41 -07001103 self.fake_jenkins.fakeAddFailTest('project-test1', A)
James E. Blaird466dc42012-07-31 10:42:56 -07001104
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
1109 self.waitUntilSettled()
1110 jobs = self.fake_jenkins.all_jobs
1111 finished_jobs = self.fake_jenkins.job_history
1112 queue = self.fake_jenkins.queue
1113
1114 assert len(jobs) == 1
1115 assert len(queue) == 1
1116 assert jobs[0].name == 'project-merge'
James E. Blair4886cc12012-07-18 15:39:41 -07001117 assert job_has_changes(jobs[0], A)
James E. Blaird466dc42012-07-31 10:42:56 -07001118
1119 self.fake_jenkins.fakeRelease('.*-merge')
1120 self.waitUntilSettled()
1121 self.fake_jenkins.fakeRelease('.*-merge')
1122 self.waitUntilSettled()
1123 self.fake_jenkins.fakeRelease('.*-merge')
1124 self.waitUntilSettled()
1125
1126 assert len(jobs) == 6
1127 assert len(queue) == 6
1128 assert jobs[0].name == 'project-test1'
1129 assert jobs[1].name == 'project-test2'
1130 assert jobs[2].name == 'project-test1'
1131 assert jobs[3].name == 'project-test2'
1132 assert jobs[4].name == 'project-test1'
1133 assert jobs[5].name == 'project-test2'
1134
1135 jobs[0].release()
1136 self.waitUntilSettled()
1137
James E. Blairec590122012-08-22 15:19:31 -07001138 assert len(jobs) == 2 # project-test2, project-merge for B
1139 assert len(queue) == 2
James E. Blaird466dc42012-07-31 10:42:56 -07001140 assert self.countJobResults(finished_jobs, 'ABORTED') == 0
1141
1142 self.fake_jenkins.hold_jobs_in_queue = False
1143 self.fake_jenkins.fakeRelease()
1144 self.waitUntilSettled()
1145
1146 assert len(jobs) == 0
1147 assert len(finished_jobs) == 11
1148 assert A.data['status'] == 'NEW'
1149 assert B.data['status'] == 'MERGED'
1150 assert C.data['status'] == 'MERGED'
1151 assert A.reported == 2
1152 assert B.reported == 2
1153 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001154 self.assertEmptyQueues()
James E. Blair8c803f82012-07-31 16:25:42 -07001155
1156 def test_patch_order(self):
1157 "Test that dependent patches are tested in the right order"
1158 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1159 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1160 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1161 A.addApproval('CRVW', 2)
1162 B.addApproval('CRVW', 2)
1163 C.addApproval('CRVW', 2)
1164
1165 M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
1166 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1167 M2.setMerged()
1168 M1.setMerged()
1169
1170 # C -> B -> A -> M1 -> M2
1171 # M2 is here to make sure it is never queried. If it is, it
1172 # means zuul is walking down the entire history of merged
1173 # changes.
1174
1175 C.setDependsOn(B, 1)
1176 B.setDependsOn(A, 1)
1177 A.setDependsOn(M1, 1)
1178 M1.setDependsOn(M2, 1)
1179
1180 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1181
1182 self.waitUntilSettled()
1183
1184 assert A.data['status'] == 'NEW'
1185 assert B.data['status'] == 'NEW'
1186 assert C.data['status'] == 'NEW'
1187
1188 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1189 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1190
1191 self.waitUntilSettled()
1192 assert M2.queried == 0
1193 assert A.data['status'] == 'MERGED'
1194 assert B.data['status'] == 'MERGED'
1195 assert C.data['status'] == 'MERGED'
1196 assert A.reported == 2
1197 assert B.reported == 2
1198 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001199 self.assertEmptyQueues()
James E. Blair8c803f82012-07-31 16:25:42 -07001200
1201 def test_can_merge(self):
James E. Blair4886cc12012-07-18 15:39:41 -07001202 "Test whether a change is ready to merge"
James E. Blair8c803f82012-07-31 16:25:42 -07001203 # TODO: move to test_gerrit (this is a unit test!)
1204 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
James E. Blair4aea70c2012-07-26 14:23:24 -07001205 a = self.sched.trigger.getChange(1, 2)
1206 mgr = self.sched.pipelines['gate'].manager
1207 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001208
1209 A.addApproval('CRVW', 2)
James E. Blair4aea70c2012-07-26 14:23:24 -07001210 a = self.sched.trigger.getChange(1, 2)
1211 assert not self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blair8c803f82012-07-31 16:25:42 -07001212
1213 A.addApproval('APRV', 1)
James E. Blair4aea70c2012-07-26 14:23:24 -07001214 a = self.sched.trigger.getChange(1, 2)
1215 assert self.sched.trigger.canMerge(a, mgr.getSubmitAllowNeeds())
James E. Blaire0487072012-08-29 17:38:31 -07001216 self.assertEmptyQueues()
James E. Blair4886cc12012-07-18 15:39:41 -07001217
1218 def test_build_configuration(self):
1219 "Test that zuul merges the right commits for testing"
1220 self.fake_jenkins.hold_jobs_in_queue = True
1221 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1222 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1223 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1224 A.addApproval('CRVW', 2)
1225 B.addApproval('CRVW', 2)
1226 C.addApproval('CRVW', 2)
1227 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1228 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1229 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1230 self.waitUntilSettled()
1231
1232 jobs = self.fake_jenkins.all_jobs
1233
1234 self.fake_jenkins.fakeRelease('.*-merge')
1235 self.waitUntilSettled()
1236 self.fake_jenkins.fakeRelease('.*-merge')
1237 self.waitUntilSettled()
1238 self.fake_jenkins.fakeRelease('.*-merge')
1239 self.waitUntilSettled()
James E. Blair1dbd5082012-08-23 15:12:15 -07001240
James E. Blair4886cc12012-07-18 15:39:41 -07001241 ref = jobs[-1].parameters['ZUUL_REF']
1242 self.fake_jenkins.hold_jobs_in_queue = False
1243 self.fake_jenkins.fakeRelease()
James E. Blair973721f2012-08-15 10:19:43 -07001244 self.waitUntilSettled()
James E. Blair4886cc12012-07-18 15:39:41 -07001245
James E. Blair1dbd5082012-08-23 15:12:15 -07001246 path = os.path.join(GIT_ROOT, "org/project")
James E. Blair4886cc12012-07-18 15:39:41 -07001247 repo = git.Repo(path)
1248 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1249 repo_messages.reverse()
James E. Blair4886cc12012-07-18 15:39:41 -07001250 correct_messages = ['initial commit', 'A-1', 'B-1', 'C-1']
1251 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001252 self.assertEmptyQueues()
James E. Blair973721f2012-08-15 10:19:43 -07001253
1254 def test_build_configuration_conflict(self):
1255 "Test that merge conflicts are handled"
1256 self.fake_jenkins.hold_jobs_in_queue = True
1257 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1258 A.addPatchset(['conflict'])
1259 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1260 B.addPatchset(['conflict'])
1261 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1262 A.addApproval('CRVW', 2)
1263 B.addApproval('CRVW', 2)
1264 C.addApproval('CRVW', 2)
1265 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1266 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1267 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1268 self.waitUntilSettled()
1269
1270 jobs = self.fake_jenkins.all_jobs
1271
1272 self.fake_jenkins.fakeRelease('.*-merge')
1273 self.waitUntilSettled()
1274 self.fake_jenkins.fakeRelease('.*-merge')
1275 self.waitUntilSettled()
1276 self.fake_jenkins.fakeRelease('.*-merge')
1277 self.waitUntilSettled()
1278 ref = jobs[-1].parameters['ZUUL_REF']
1279 self.fake_jenkins.hold_jobs_in_queue = False
1280 self.fake_jenkins.fakeRelease()
1281 self.waitUntilSettled()
1282
1283 assert A.data['status'] == 'MERGED'
1284 assert B.data['status'] == 'NEW'
1285 assert C.data['status'] == 'MERGED'
1286 assert A.reported == 2
1287 assert B.reported == 2
1288 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001289 self.assertEmptyQueues()
James E. Blairdaabed22012-08-15 15:38:57 -07001290
1291 def test_post(self):
1292 "Test that post jobs run"
Zhongyue Luo5d556072012-09-21 02:00:47 +09001293 e = {
1294 "type": "ref-updated",
1295 "submitter": {
1296 "name": "User Name",
1297 },
1298 "refUpdate": {
1299 "oldRev": "90f173846e3af9154517b88543ffbd1691f31366",
1300 "newRev": "d479a0bfcb34da57a31adb2a595c0cf687812543",
1301 "refName": "master",
1302 "project": "org/project",
1303 }
1304 }
James E. Blairdaabed22012-08-15 15:38:57 -07001305 self.fake_gerrit.addEvent(e)
1306 self.waitUntilSettled()
1307
1308 jobs = self.fake_jenkins.job_history
James E. Blair0018a6c2013-02-27 14:11:45 -08001309 job_names = [x.name for x in jobs]
James E. Blairdaabed22012-08-15 15:38:57 -07001310 assert len(jobs) == 1
1311 assert 'project-post' in job_names
James E. Blaire0487072012-08-29 17:38:31 -07001312 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001313
1314 def test_build_configuration_branch(self):
1315 "Test that the right commits are on alternate branches"
1316 self.fake_jenkins.hold_jobs_in_queue = True
1317 A = self.fake_gerrit.addFakeChange('org/project', 'mp', 'A')
1318 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1319 C = self.fake_gerrit.addFakeChange('org/project', 'mp', 'C')
1320 A.addApproval('CRVW', 2)
1321 B.addApproval('CRVW', 2)
1322 C.addApproval('CRVW', 2)
1323 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1324 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1325 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1326 self.waitUntilSettled()
1327
1328 jobs = self.fake_jenkins.all_jobs
1329
1330 self.fake_jenkins.fakeRelease('.*-merge')
1331 self.waitUntilSettled()
1332 self.fake_jenkins.fakeRelease('.*-merge')
1333 self.waitUntilSettled()
1334 self.fake_jenkins.fakeRelease('.*-merge')
1335 self.waitUntilSettled()
1336 ref = jobs[-1].parameters['ZUUL_REF']
1337 self.fake_jenkins.hold_jobs_in_queue = False
1338 self.fake_jenkins.fakeRelease()
1339 self.waitUntilSettled()
1340
James E. Blair1dbd5082012-08-23 15:12:15 -07001341 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001342 repo = git.Repo(path)
1343 repo_messages = [c.message.strip() for c in repo.iter_commits(ref)]
1344 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001345 correct_messages = ['initial commit', 'mp commit', 'A-1', 'B-1', 'C-1']
1346 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001347 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001348
1349 def test_build_configuration_branch_interaction(self):
1350 "Test that switching between branches works"
1351 self.test_build_configuration()
1352 self.test_build_configuration_branch()
1353 # C has been merged, undo that
James E. Blair1dbd5082012-08-23 15:12:15 -07001354 path = os.path.join(UPSTREAM_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001355 repo = git.Repo(path)
1356 repo.heads.master.commit = repo.commit('init')
1357 self.test_build_configuration()
James E. Blaire0487072012-08-29 17:38:31 -07001358 self.assertEmptyQueues()
James E. Blairc6294a52012-08-17 10:19:48 -07001359
1360 def test_build_configuration_multi_branch(self):
1361 "Test that dependent changes on multiple branches are merged"
1362 self.fake_jenkins.hold_jobs_in_queue = True
1363 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1364 B = self.fake_gerrit.addFakeChange('org/project', 'mp', 'B')
1365 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1366 A.addApproval('CRVW', 2)
1367 B.addApproval('CRVW', 2)
1368 C.addApproval('CRVW', 2)
1369 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1370 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1371 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1372 self.waitUntilSettled()
1373
1374 jobs = self.fake_jenkins.all_jobs
1375
1376 self.fake_jenkins.fakeRelease('.*-merge')
1377 self.waitUntilSettled()
1378 ref_mp = jobs[-1].parameters['ZUUL_REF']
1379 self.fake_jenkins.fakeRelease('.*-merge')
1380 self.waitUntilSettled()
1381 self.fake_jenkins.fakeRelease('.*-merge')
1382 self.waitUntilSettled()
1383 ref_master = jobs[-1].parameters['ZUUL_REF']
1384 self.fake_jenkins.hold_jobs_in_queue = False
1385 self.fake_jenkins.fakeRelease()
1386 self.waitUntilSettled()
1387
James E. Blair1dbd5082012-08-23 15:12:15 -07001388 path = os.path.join(GIT_ROOT, "org/project")
James E. Blairc6294a52012-08-17 10:19:48 -07001389 repo = git.Repo(path)
1390
1391 repo_messages = [c.message.strip()
1392 for c in repo.iter_commits(ref_master)]
1393 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001394 correct_messages = ['initial commit', 'A-1', 'C-1']
1395 assert repo_messages == correct_messages
1396
1397 repo_messages = [c.message.strip()
1398 for c in repo.iter_commits(ref_mp)]
1399 repo_messages.reverse()
James E. Blairc6294a52012-08-17 10:19:48 -07001400 correct_messages = ['initial commit', 'mp commit', 'B-1']
1401 assert repo_messages == correct_messages
James E. Blaire0487072012-08-29 17:38:31 -07001402 self.assertEmptyQueues()
James E. Blair7f71c802012-08-22 13:04:32 -07001403
1404 def test_one_job_project(self):
1405 "Test that queueing works with one job"
1406 A = self.fake_gerrit.addFakeChange('org/one-job-project',
1407 'master', 'A')
1408 B = self.fake_gerrit.addFakeChange('org/one-job-project',
1409 'master', 'B')
1410 A.addApproval('CRVW', 2)
1411 B.addApproval('CRVW', 2)
1412 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1413 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1414 self.waitUntilSettled()
1415
1416 jobs = self.fake_jenkins.all_jobs
1417 finished_jobs = self.fake_jenkins.job_history
James E. Blair7f71c802012-08-22 13:04:32 -07001418
1419 assert A.data['status'] == 'MERGED'
1420 assert A.reported == 2
1421 assert B.data['status'] == 'MERGED'
1422 assert B.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001423 self.assertEmptyQueues()
James E. Blaircaec0c52012-08-22 14:52:22 -07001424
Antoine Musso80edd5a2013-02-13 15:37:53 +01001425 def test_job_from_templates_launched(self):
1426 "Test whether a job generated via a template can be launched"
1427 A = self.fake_gerrit.addFakeChange(
1428 'org/templated-project', 'master', 'A')
1429 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1430 self.waitUntilSettled()
1431 jobs = self.fake_jenkins.job_history
1432 job_names = [x.name for x in jobs]
1433
1434 assert 'project-test1' in job_names
1435 assert 'project-test2' in job_names
1436 assert jobs[0].result == 'SUCCESS'
1437 assert jobs[1].result == 'SUCCESS'
1438
James E. Blaircaec0c52012-08-22 14:52:22 -07001439 def test_dependent_changes_dequeue(self):
1440 "Test that dependent patches are not needlessly tested"
1441 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1442 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1443 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1444 A.addApproval('CRVW', 2)
1445 B.addApproval('CRVW', 2)
1446 C.addApproval('CRVW', 2)
1447
1448 M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
1449 M1.setMerged()
1450
1451 # C -> B -> A -> M1
1452
1453 C.setDependsOn(B, 1)
1454 B.setDependsOn(A, 1)
1455 A.setDependsOn(M1, 1)
1456
1457 self.fake_jenkins.fakeAddFailTest('project-merge', A)
1458
1459 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1460 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1461 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1462
1463 self.waitUntilSettled()
1464
1465 jobs = self.fake_jenkins.all_jobs
1466 finished_jobs = self.fake_jenkins.job_history
1467
James E. Blair127bc182012-08-28 15:55:15 -07001468 for x in jobs:
1469 print x
1470 for x in finished_jobs:
1471 print x
1472
James E. Blairec590122012-08-22 15:19:31 -07001473 assert A.data['status'] == 'NEW'
1474 assert A.reported == 2
1475 assert B.data['status'] == 'NEW'
1476 assert B.reported == 2
1477 assert C.data['status'] == 'NEW'
1478 assert C.reported == 2
1479 assert len(finished_jobs) == 1
James E. Blaire0487072012-08-29 17:38:31 -07001480 self.assertEmptyQueues()
James E. Blairec590122012-08-22 15:19:31 -07001481
1482 def test_head_is_dequeued_once(self):
James E. Blair2fa50962013-01-30 21:50:41 -08001483 "Test that if a change at the head fails it is dequeued only once"
James E. Blairec590122012-08-22 15:19:31 -07001484 # If it's dequeued more than once, we should see extra
1485 # aborted jobs.
1486 self.fake_jenkins.hold_jobs_in_build = True
1487
1488 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1489 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1490 C = self.fake_gerrit.addFakeChange('org/project1', 'master', 'C')
1491 A.addApproval('CRVW', 2)
1492 B.addApproval('CRVW', 2)
1493 C.addApproval('CRVW', 2)
1494
1495 self.fake_jenkins.fakeAddFailTest('project1-test1', A)
1496 self.fake_jenkins.fakeAddFailTest('project1-test2', A)
1497 self.fake_jenkins.fakeAddFailTest('project1-project2-integration', A)
1498
1499 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1500 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1501 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1502
1503 self.waitUntilSettled()
1504 jobs = self.fake_jenkins.all_jobs
1505 finished_jobs = self.fake_jenkins.job_history
1506
1507 assert len(jobs) == 1
1508 assert jobs[0].name == 'project1-merge'
1509 assert job_has_changes(jobs[0], A)
1510
1511 self.fake_jenkins.fakeRelease('.*-merge')
1512 self.waitUntilSettled()
1513 self.fake_jenkins.fakeRelease('.*-merge')
1514 self.waitUntilSettled()
1515 self.fake_jenkins.fakeRelease('.*-merge')
1516 self.waitUntilSettled()
1517
1518 assert len(jobs) == 9
1519 assert jobs[0].name == 'project1-test1'
1520 assert jobs[1].name == 'project1-test2'
1521 assert jobs[2].name == 'project1-project2-integration'
1522 assert jobs[3].name == 'project1-test1'
1523 assert jobs[4].name == 'project1-test2'
1524 assert jobs[5].name == 'project1-project2-integration'
1525 assert jobs[6].name == 'project1-test1'
1526 assert jobs[7].name == 'project1-test2'
1527 assert jobs[8].name == 'project1-project2-integration'
1528
1529 jobs[0].release()
1530 self.waitUntilSettled()
1531
1532 assert len(jobs) == 3 # test2, integration, merge for B
1533 assert self.countJobResults(finished_jobs, 'ABORTED') == 6
1534
1535 self.fake_jenkins.hold_jobs_in_build = False
1536 self.fake_jenkins.fakeRelease()
1537 self.waitUntilSettled()
1538
1539 assert len(jobs) == 0
1540 assert len(finished_jobs) == 20
James E. Blaircaec0c52012-08-22 14:52:22 -07001541
1542 assert A.data['status'] == 'NEW'
James E. Blairec590122012-08-22 15:19:31 -07001543 assert B.data['status'] == 'MERGED'
1544 assert C.data['status'] == 'MERGED'
1545 assert A.reported == 2
1546 assert B.reported == 2
1547 assert C.reported == 2
James E. Blaire0487072012-08-29 17:38:31 -07001548 self.assertEmptyQueues()
James E. Blair4ec821f2012-08-23 15:28:28 -07001549
1550 def test_nonvoting_job(self):
1551 "Test that non-voting jobs don't vote."
1552 A = self.fake_gerrit.addFakeChange('org/nonvoting-project',
1553 'master', 'A')
1554 A.addApproval('CRVW', 2)
1555 self.fake_jenkins.fakeAddFailTest('nonvoting-project-test2', A)
1556 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1557
1558 self.waitUntilSettled()
1559 jobs = self.fake_jenkins.all_jobs
1560 finished_jobs = self.fake_jenkins.job_history
1561
1562 assert A.data['status'] == 'MERGED'
1563 assert A.reported == 2
James E. Blair0018a6c2013-02-27 14:11:45 -08001564 assert finished_jobs[0].result == 'SUCCESS'
1565 assert finished_jobs[1].result == 'SUCCESS'
1566 assert finished_jobs[2].result == 'FAILURE'
James E. Blaire0487072012-08-29 17:38:31 -07001567 self.assertEmptyQueues()
1568
1569 def test_check_queue_success(self):
1570 "Test successful check queue jobs."
1571 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1572 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1573
1574 self.waitUntilSettled()
1575 jobs = self.fake_jenkins.all_jobs
1576 finished_jobs = self.fake_jenkins.job_history
1577
1578 assert A.data['status'] == 'NEW'
1579 assert A.reported == 1
James E. Blair0018a6c2013-02-27 14:11:45 -08001580 assert finished_jobs[0].result == 'SUCCESS'
1581 assert finished_jobs[1].result == 'SUCCESS'
1582 assert finished_jobs[2].result == 'SUCCESS'
James E. Blaire0487072012-08-29 17:38:31 -07001583 self.assertEmptyQueues()
1584
1585 def test_check_queue_failure(self):
1586 "Test failed check queue jobs."
1587 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1588 self.fake_jenkins.fakeAddFailTest('project-test2', A)
1589 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1590
1591 self.waitUntilSettled()
1592 jobs = self.fake_jenkins.all_jobs
1593 finished_jobs = self.fake_jenkins.job_history
1594
1595 assert A.data['status'] == 'NEW'
1596 assert A.reported == 1
James E. Blair0018a6c2013-02-27 14:11:45 -08001597 assert finished_jobs[0].result == 'SUCCESS'
1598 assert finished_jobs[1].result == 'SUCCESS'
1599 assert finished_jobs[2].result == 'FAILURE'
James E. Blaire0487072012-08-29 17:38:31 -07001600 self.assertEmptyQueues()
James E. Blair127bc182012-08-28 15:55:15 -07001601
1602 def test_dependent_behind_dequeue(self):
1603 "test that dependent changes behind dequeued changes work"
1604 # This complicated test is a reproduction of a real life bug
1605 self.sched.reconfigure(self.config)
1606 self.fake_jenkins.hold_jobs_in_build = True
1607
1608 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1609 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
1610 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
1611 D = self.fake_gerrit.addFakeChange('org/project2', 'master', 'D')
1612 E = self.fake_gerrit.addFakeChange('org/project2', 'master', 'E')
1613 F = self.fake_gerrit.addFakeChange('org/project3', 'master', 'F')
1614 D.setDependsOn(C, 1)
1615 E.setDependsOn(D, 1)
1616 A.addApproval('CRVW', 2)
1617 B.addApproval('CRVW', 2)
1618 C.addApproval('CRVW', 2)
1619 D.addApproval('CRVW', 2)
1620 E.addApproval('CRVW', 2)
1621 F.addApproval('CRVW', 2)
1622
1623 A.fail_merge = True
1624 jobs = self.fake_jenkins.all_jobs
1625 finished_jobs = self.fake_jenkins.job_history
1626
1627 # Change object re-use in the gerrit trigger is hidden if
1628 # changes are added in quick succession; waiting makes it more
1629 # like real life.
1630 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1631 self.waitUntilSettled()
1632 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1633 self.waitUntilSettled()
1634
1635 self.fake_jenkins.fakeRelease('.*-merge')
1636 self.waitUntilSettled()
1637 self.fake_jenkins.fakeRelease('.*-merge')
1638 self.waitUntilSettled()
1639
1640 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1641 self.waitUntilSettled()
1642 self.fake_gerrit.addEvent(D.addApproval('APRV', 1))
1643 self.waitUntilSettled()
1644 self.fake_gerrit.addEvent(E.addApproval('APRV', 1))
1645 self.waitUntilSettled()
1646 self.fake_gerrit.addEvent(F.addApproval('APRV', 1))
1647 self.waitUntilSettled()
1648
1649 self.fake_jenkins.fakeRelease('.*-merge')
1650 self.waitUntilSettled()
1651 self.fake_jenkins.fakeRelease('.*-merge')
1652 self.waitUntilSettled()
1653 self.fake_jenkins.fakeRelease('.*-merge')
1654 self.waitUntilSettled()
1655 self.fake_jenkins.fakeRelease('.*-merge')
1656 self.waitUntilSettled()
1657
James E. Blair4aa1ad62012-10-05 12:39:26 -07001658 for x in jobs:
1659 print x
James E. Blair127bc182012-08-28 15:55:15 -07001660 # all jobs running
James E. Blaire955e062012-10-08 09:49:03 -07001661
1662 # Grab pointers to the jobs we want to release before
1663 # releasing any, because list indexes may change as
1664 # the jobs complete.
1665 a, b, c = jobs[:3]
1666 a.release()
1667 b.release()
1668 c.release()
James E. Blair127bc182012-08-28 15:55:15 -07001669 self.waitUntilSettled()
1670
1671 self.fake_jenkins.hold_jobs_in_build = False
1672 self.fake_jenkins.fakeRelease()
1673 self.waitUntilSettled()
1674
1675 for x in jobs:
1676 print x
1677 for x in finished_jobs:
1678 print x
1679 print self.sched.formatStatusHTML()
1680
1681 assert A.data['status'] == 'NEW'
1682 assert B.data['status'] == 'MERGED'
1683 assert C.data['status'] == 'MERGED'
1684 assert D.data['status'] == 'MERGED'
1685 assert E.data['status'] == 'MERGED'
1686 assert F.data['status'] == 'MERGED'
1687
1688 assert A.reported == 2
1689 assert B.reported == 2
1690 assert C.reported == 2
1691 assert D.reported == 2
1692 assert E.reported == 2
1693 assert F.reported == 2
1694
James E. Blair127bc182012-08-28 15:55:15 -07001695 assert self.countJobResults(finished_jobs, 'ABORTED') == 15
1696 assert len(finished_jobs) == 44
James E. Blaire0487072012-08-29 17:38:31 -07001697 self.assertEmptyQueues()
James E. Blair05fed602012-09-07 12:45:24 -07001698
1699 def test_merger_repack(self):
1700 "Test that the merger works after a repack"
1701 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1702 A.addApproval('CRVW', 2)
1703 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1704 self.waitUntilSettled()
1705 jobs = self.fake_jenkins.job_history
James E. Blair0018a6c2013-02-27 14:11:45 -08001706 job_names = [x.name for x in jobs]
James E. Blair05fed602012-09-07 12:45:24 -07001707 assert 'project-merge' in job_names
1708 assert 'project-test1' in job_names
1709 assert 'project-test2' in job_names
James E. Blair0018a6c2013-02-27 14:11:45 -08001710 assert jobs[0].result == 'SUCCESS'
1711 assert jobs[1].result == 'SUCCESS'
1712 assert jobs[2].result == 'SUCCESS'
James E. Blair05fed602012-09-07 12:45:24 -07001713 assert A.data['status'] == 'MERGED'
1714 assert A.reported == 2
1715 self.assertEmptyQueues()
1716
1717 path = os.path.join(GIT_ROOT, "org/project")
1718 os.system('git --git-dir=%s/.git repack -afd' % path)
1719
1720 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1721 A.addApproval('CRVW', 2)
1722 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1723 self.waitUntilSettled()
1724 jobs = self.fake_jenkins.job_history
James E. Blair0018a6c2013-02-27 14:11:45 -08001725 job_names = [x.name for x in jobs]
James E. Blair05fed602012-09-07 12:45:24 -07001726 assert 'project-merge' in job_names
1727 assert 'project-test1' in job_names
1728 assert 'project-test2' in job_names
James E. Blair0018a6c2013-02-27 14:11:45 -08001729 assert jobs[0].result == 'SUCCESS'
1730 assert jobs[1].result == 'SUCCESS'
1731 assert jobs[2].result == 'SUCCESS'
James E. Blair05fed602012-09-07 12:45:24 -07001732 assert A.data['status'] == 'MERGED'
1733 assert A.reported == 2
1734 self.assertEmptyQueues()
James E. Blair7ee88a22012-09-12 18:59:31 +02001735
James E. Blair4886f282012-11-15 09:27:33 -08001736 def test_merger_repack_large_change(self):
1737 "Test that the merger works with large changes after a repack"
1738 # https://bugs.launchpad.net/zuul/+bug/1078946
1739 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
1740 A.addPatchset(large=True)
1741 path = os.path.join(UPSTREAM_ROOT, "org/project1")
1742 os.system('git --git-dir=%s/.git repack -afd' % path)
1743 path = os.path.join(GIT_ROOT, "org/project1")
1744 os.system('git --git-dir=%s/.git repack -afd' % path)
1745
1746 A.addApproval('CRVW', 2)
1747 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1748 self.waitUntilSettled()
1749 jobs = self.fake_jenkins.job_history
James E. Blair0018a6c2013-02-27 14:11:45 -08001750 job_names = [x.name for x in jobs]
James E. Blair4886f282012-11-15 09:27:33 -08001751 assert 'project1-merge' in job_names
1752 assert 'project1-test1' in job_names
1753 assert 'project1-test2' in job_names
James E. Blair0018a6c2013-02-27 14:11:45 -08001754 assert jobs[0].result == 'SUCCESS'
1755 assert jobs[1].result == 'SUCCESS'
1756 assert jobs[2].result == 'SUCCESS'
James E. Blair4886f282012-11-15 09:27:33 -08001757 assert A.data['status'] == 'MERGED'
1758 assert A.reported == 2
1759 self.assertEmptyQueues()
1760
James E. Blair7ee88a22012-09-12 18:59:31 +02001761 def test_nonexistent_job(self):
1762 "Test launching a job that doesn't exist"
1763 self.fake_jenkins.nonexistent_jobs.append('project-merge')
1764 self.jenkins.launch_retry_timeout = 0.1
1765
1766 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1767 A.addApproval('CRVW', 2)
1768 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1769 # There may be a thread about to report a lost change
1770 while A.reported < 2:
1771 self.waitUntilSettled()
1772 jobs = self.fake_jenkins.job_history
James E. Blair0018a6c2013-02-27 14:11:45 -08001773 job_names = [x.name for x in jobs]
James E. Blair7ee88a22012-09-12 18:59:31 +02001774 assert not job_names
1775 assert A.data['status'] == 'NEW'
1776 assert A.reported == 2
1777 self.assertEmptyQueues()
1778
1779 # Make sure things still work:
1780 self.fake_jenkins.nonexistent_jobs = []
1781 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1782 A.addApproval('CRVW', 2)
1783 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1784 self.waitUntilSettled()
1785 jobs = self.fake_jenkins.job_history
James E. Blair0018a6c2013-02-27 14:11:45 -08001786 job_names = [x.name for x in jobs]
James E. Blair7ee88a22012-09-12 18:59:31 +02001787 assert 'project-merge' in job_names
1788 assert 'project-test1' in job_names
1789 assert 'project-test2' in job_names
James E. Blair0018a6c2013-02-27 14:11:45 -08001790 assert jobs[0].result == 'SUCCESS'
1791 assert jobs[1].result == 'SUCCESS'
1792 assert jobs[2].result == 'SUCCESS'
James E. Blair7ee88a22012-09-12 18:59:31 +02001793 assert A.data['status'] == 'MERGED'
1794 assert A.reported == 2
1795 self.assertEmptyQueues()
James E. Blairf62d4282012-12-31 17:01:50 -08001796
1797 def test_single_nonexistent_post_job(self):
1798 "Test launching a single post job that doesn't exist"
1799 self.fake_jenkins.nonexistent_jobs.append('project-post')
1800 self.jenkins.launch_retry_timeout = 0.1
1801
1802 e = {
1803 "type": "ref-updated",
1804 "submitter": {
1805 "name": "User Name",
1806 },
1807 "refUpdate": {
1808 "oldRev": "90f173846e3af9154517b88543ffbd1691f31366",
1809 "newRev": "d479a0bfcb34da57a31adb2a595c0cf687812543",
1810 "refName": "master",
1811 "project": "org/project",
1812 }
1813 }
1814 self.fake_gerrit.addEvent(e)
1815 self.waitUntilSettled()
1816
1817 jobs = self.fake_jenkins.job_history
1818 assert len(jobs) == 0
1819 self.assertEmptyQueues()
James E. Blair2fa50962013-01-30 21:50:41 -08001820
1821 def test_new_patchset_dequeues_old(self):
1822 "Test that a new patchset causes the old to be dequeued"
1823 # D -> C (depends on B) -> B (depends on A) -> A -> M
1824 self.fake_jenkins.hold_jobs_in_build = True
1825
1826 M = self.fake_gerrit.addFakeChange('org/project', 'master', 'M')
1827 M.setMerged()
1828
1829 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1830 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1831 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1832 D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D')
1833 A.addApproval('CRVW', 2)
1834 B.addApproval('CRVW', 2)
1835 C.addApproval('CRVW', 2)
1836 D.addApproval('CRVW', 2)
1837
1838 C.setDependsOn(B, 1)
1839 B.setDependsOn(A, 1)
1840 A.setDependsOn(M, 1)
1841
1842 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1843 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1844 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1845 self.fake_gerrit.addEvent(D.addApproval('APRV', 1))
1846 self.waitUntilSettled()
1847
1848 B.addPatchset()
1849 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
1850 self.waitUntilSettled()
1851
1852 self.fake_jenkins.hold_jobs_in_build = False
1853 self.fake_jenkins.fakeRelease()
1854 self.waitUntilSettled()
1855
1856 jobs = self.fake_jenkins.all_jobs
1857 finished_jobs = self.fake_jenkins.job_history
1858
1859 for x in jobs:
1860 print x
1861 for x in finished_jobs:
1862 print x
1863
1864 assert A.data['status'] == 'MERGED'
1865 assert A.reported == 2
1866 assert B.data['status'] == 'NEW'
1867 assert B.reported == 2
1868 assert C.data['status'] == 'NEW'
1869 assert C.reported == 2
1870 assert D.data['status'] == 'MERGED'
1871 assert D.reported == 2
1872 assert len(finished_jobs) == 9 # 3 each for A, B, D.
1873 self.assertEmptyQueues()
1874
1875 def test_new_patchset_dequeues_old_on_head(self):
1876 "Test that a new patchset causes the old to be dequeued (at head)"
1877 # D -> C (depends on B) -> B (depends on A) -> A -> M
1878 self.fake_jenkins.hold_jobs_in_build = True
1879
1880 M = self.fake_gerrit.addFakeChange('org/project', 'master', 'M')
1881 M.setMerged()
1882 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1883 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1884 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1885 D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D')
1886 A.addApproval('CRVW', 2)
1887 B.addApproval('CRVW', 2)
1888 C.addApproval('CRVW', 2)
1889 D.addApproval('CRVW', 2)
1890
1891 C.setDependsOn(B, 1)
1892 B.setDependsOn(A, 1)
1893 A.setDependsOn(M, 1)
1894
1895 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1896 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1897 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1898 self.fake_gerrit.addEvent(D.addApproval('APRV', 1))
1899 self.waitUntilSettled()
1900
1901 A.addPatchset()
1902 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
1903 self.waitUntilSettled()
1904
1905 self.fake_jenkins.hold_jobs_in_build = False
1906 self.fake_jenkins.fakeRelease()
1907 self.waitUntilSettled()
1908
1909 jobs = self.fake_jenkins.all_jobs
1910 finished_jobs = self.fake_jenkins.job_history
1911
1912 for x in jobs:
1913 print x
1914 for x in finished_jobs:
1915 print x
1916
1917 assert A.data['status'] == 'NEW'
1918 assert A.reported == 2
1919 assert B.data['status'] == 'NEW'
1920 assert B.reported == 2
1921 assert C.data['status'] == 'NEW'
1922 assert C.reported == 2
1923 assert D.data['status'] == 'MERGED'
1924 assert D.reported == 2
1925 assert len(finished_jobs) == 7
1926 self.assertEmptyQueues()
1927
1928 def test_new_patchset_dequeues_old_without_dependents(self):
1929 "Test that a new patchset causes only the old to be dequeued"
1930 self.fake_jenkins.hold_jobs_in_build = True
1931
1932 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1933 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1934 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1935 A.addApproval('CRVW', 2)
1936 B.addApproval('CRVW', 2)
1937 C.addApproval('CRVW', 2)
1938
1939 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
1940 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
1941 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
1942 self.waitUntilSettled()
1943
1944 B.addPatchset()
1945 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
1946 self.waitUntilSettled()
1947
1948 self.fake_jenkins.hold_jobs_in_build = False
1949 self.fake_jenkins.fakeRelease()
1950 self.waitUntilSettled()
1951
1952 jobs = self.fake_jenkins.all_jobs
1953 finished_jobs = self.fake_jenkins.job_history
1954
1955 for x in jobs:
1956 print x
1957 for x in finished_jobs:
1958 print x
1959
1960 assert A.data['status'] == 'MERGED'
1961 assert A.reported == 2
1962 assert B.data['status'] == 'NEW'
1963 assert B.reported == 2
1964 assert C.data['status'] == 'MERGED'
1965 assert C.reported == 2
1966 assert len(finished_jobs) == 9
1967 self.assertEmptyQueues()
1968
1969 def test_new_patchset_dequeues_old_independent_queue(self):
1970 "Test that a new patchset causes the old to be dequeued (independent)"
1971 self.fake_jenkins.hold_jobs_in_build = True
1972
1973 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
1974 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
1975 C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
1976 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
1977 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
1978 self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
1979 self.waitUntilSettled()
1980
1981 B.addPatchset()
1982 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
1983 self.waitUntilSettled()
1984
1985 self.fake_jenkins.hold_jobs_in_build = False
1986 self.fake_jenkins.fakeRelease()
1987 self.waitUntilSettled()
1988
1989 jobs = self.fake_jenkins.all_jobs
1990 finished_jobs = self.fake_jenkins.job_history
1991
1992 for x in jobs:
1993 print x
1994 for x in finished_jobs:
1995 print x
1996
1997 assert A.data['status'] == 'NEW'
1998 assert A.reported == 1
1999 assert B.data['status'] == 'NEW'
2000 assert B.reported == 1
2001 assert C.data['status'] == 'NEW'
2002 assert C.reported == 1
2003 assert len(finished_jobs) == 10
2004 assert self.countJobResults(finished_jobs, 'ABORTED') == 1
2005 self.assertEmptyQueues()
James E. Blair7d0dedc2013-02-21 17:26:09 -08002006
2007 def test_zuul_refs(self):
2008 "Test that zuul refs exist and have the right changes"
2009 self.fake_jenkins.hold_jobs_in_build = True
2010
2011 M1 = self.fake_gerrit.addFakeChange('org/project1', 'master', 'M1')
2012 M1.setMerged()
2013 M2 = self.fake_gerrit.addFakeChange('org/project2', 'master', 'M2')
2014 M2.setMerged()
2015
2016 A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
2017 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
2018 C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
2019 D = self.fake_gerrit.addFakeChange('org/project2', 'master', 'D')
2020 A.addApproval('CRVW', 2)
2021 B.addApproval('CRVW', 2)
2022 C.addApproval('CRVW', 2)
2023 D.addApproval('CRVW', 2)
2024 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
2025 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
2026 self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
2027 self.fake_gerrit.addEvent(D.addApproval('APRV', 1))
2028
2029 self.waitUntilSettled()
2030 self.fake_jenkins.fakeRelease('.*-merge')
2031 self.waitUntilSettled()
2032 self.fake_jenkins.fakeRelease('.*-merge')
2033 self.waitUntilSettled()
2034 self.fake_jenkins.fakeRelease('.*-merge')
2035 self.waitUntilSettled()
2036 self.fake_jenkins.fakeRelease('.*-merge')
2037 self.waitUntilSettled()
2038
2039 jobs = self.fake_jenkins.all_jobs
2040 finished_jobs = self.fake_jenkins.job_history
2041
2042 a_zref = b_zref = c_zref = d_zref = None
2043 for x in jobs:
2044 if x.parameters['ZUUL_CHANGE'] == '3':
2045 a_zref = x.parameters['ZUUL_REF']
2046 if x.parameters['ZUUL_CHANGE'] == '4':
2047 b_zref = x.parameters['ZUUL_REF']
2048 if x.parameters['ZUUL_CHANGE'] == '5':
2049 c_zref = x.parameters['ZUUL_REF']
2050 if x.parameters['ZUUL_CHANGE'] == '6':
2051 d_zref = x.parameters['ZUUL_REF']
2052
2053 # There are... four... refs.
2054 assert a_zref is not None
2055 assert b_zref is not None
2056 assert c_zref is not None
2057 assert d_zref is not None
2058
2059 # And they should all be different
2060 refs = set([a_zref, b_zref, c_zref, d_zref])
2061 assert len(refs) == 4
2062
2063 # a ref should have a, not b, and should not be in project2
2064 assert ref_has_change(a_zref, A)
2065 assert not ref_has_change(a_zref, B)
2066 assert not ref_has_change(a_zref, M2)
2067
2068 # b ref should have a and b, and should not be in project2
2069 assert ref_has_change(b_zref, A)
2070 assert ref_has_change(b_zref, B)
2071 assert not ref_has_change(b_zref, M2)
2072
2073 # c ref should have a and b in 1, c in 2
2074 assert ref_has_change(c_zref, A)
2075 assert ref_has_change(c_zref, B)
2076 assert ref_has_change(c_zref, C)
2077 assert not ref_has_change(c_zref, D)
2078
2079 # d ref should have a and b in 1, c and d in 2
2080 assert ref_has_change(d_zref, A)
2081 assert ref_has_change(d_zref, B)
2082 assert ref_has_change(d_zref, C)
2083 assert ref_has_change(d_zref, D)
2084
2085 self.fake_jenkins.hold_jobs_in_build = False
2086 self.fake_jenkins.fakeRelease()
2087 self.waitUntilSettled()
2088
2089 assert A.data['status'] == 'MERGED'
2090 assert A.reported == 2
2091 assert B.data['status'] == 'MERGED'
2092 assert B.reported == 2
2093 assert C.data['status'] == 'MERGED'
2094 assert C.reported == 2
2095 assert D.data['status'] == 'MERGED'
2096 assert D.reported == 2
2097 self.assertEmptyQueues()
James E. Blair70c71582013-03-06 08:50:50 -08002098
James E. Blair412e5582013-04-22 15:50:12 -07002099 def test_statsd(self):
2100 "Test each of the statsd methods used in the scheduler"
2101 import extras
2102 statsd = extras.try_import('statsd.statsd')
2103 statsd.incr('test-incr')
2104 statsd.timing('test-timing', 3)
2105 statsd.gauge('test-guage', 12)
2106 self.assertReportedStat('test-incr', '1|c')
2107 self.assertReportedStat('test-timing', '3|ms')
2108 self.assertReportedStat('test-guage', '12|g')
2109
James E. Blair70c71582013-03-06 08:50:50 -08002110 def test_file_jobs(self):
2111 "Test that file jobs run only when appropriate"
2112 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
2113 A.addPatchset(['pip-requires'])
2114 B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
2115 A.addApproval('CRVW', 2)
2116 B.addApproval('CRVW', 2)
2117 self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
2118 self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
2119 self.waitUntilSettled()
2120
2121 jobs = self.fake_jenkins.all_jobs
2122 finished_jobs = self.fake_jenkins.job_history
2123
2124 testfile_jobs = [x for x in finished_jobs
2125 if x.name == 'project-testfile']
2126
2127 assert len(testfile_jobs) == 1
2128 assert testfile_jobs[0].changes == '1,2'
2129 assert A.data['status'] == 'MERGED'
2130 assert A.reported == 2
2131 assert B.data['status'] == 'MERGED'
2132 assert B.reported == 2
2133 self.assertEmptyQueues()
James E. Blair3c5e5b52013-04-26 11:17:03 -07002134
2135 def test_test_config(self):
2136 "Test that we can test the config"
2137 sched = zuul.scheduler.Scheduler()
2138 sched.testConfig(CONFIG.get('zuul', 'layout_config'))