Add more tests.
Add tests for canceling/aborting jobs behind a failed change.
Change-Id: I589f5eda64223ad8ab0e00d936456b568b5aa4c9
Reviewed-on: https://review.openstack.org/10613
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index edae02e..d921ebf 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -47,6 +47,7 @@
'VRFY': 'Verified'}
def __init__(self, number, project, branch, subject, status='NEW'):
+ self.reported = 0
self.patchsets = []
self.submit_records = []
self.number = number
@@ -123,6 +124,9 @@
self.data['status'] = 'MERGED'
self.open = False
+ def setReported(self):
+ self.reported += 1
+
class FakeGerrit(object):
def __init__(self, *args, **kw):
@@ -147,10 +151,12 @@
self.event_queue.task_done()
def review(self, project, changeid, message, action):
+ number, ps = changeid.split(',')
+ change = self.changes[int(number)]
if 'submit' in action:
- number, ps = changeid.split(',')
- change = self.changes[int(number)]
change.setMerged()
+ if message:
+ change.setReported()
def query(self, number):
change = self.changes[int(number)]
@@ -187,6 +193,9 @@
self.parameters = parameters
self.wait_condition = threading.Condition()
self.waiting = False
+ self.aborted = False
+ self.canceled = False
+ self.created = time.time()
def release(self):
self.wait_condition.acquire()
@@ -216,6 +225,9 @@
if self.jenkins.hold_jobs_in_queue:
self._wait()
self.jenkins.fakeDequeue(self)
+ if self.canceled:
+ self.jenkins.all_jobs.remove(self)
+ return
self.callback.jenkins_endpoint(FakeJenkinsEvent(
self.name, self.number, self.parameters,
'STARTED'))
@@ -228,6 +240,8 @@
self.name,
self.parameters['GERRIT_CHANGES']):
result = 'FAILURE'
+ if self.aborted:
+ result = 'ABORTED'
self.jenkins.fakeAddHistory(name=self.name, number=self.number,
result=result)
@@ -247,6 +261,7 @@
self.queue = []
self.all_jobs = []
self.job_counter = {}
+ self.queue_counter = 0
self.job_history = []
self.hold_jobs_in_queue = False
self.hold_jobs_in_build = False
@@ -299,10 +314,57 @@
count = self.job_counter.get(name, 0)
count += 1
self.job_counter[name] = count
+
+ queue_count = self.queue_counter
+ self.queue_counter += 1
job = FakeJenkinsJob(self, self.callback, name, count, parameters)
+ job.queue_id = queue_count
+
self.all_jobs.append(job)
job.start()
+ def stop_build(self, name, number):
+ for job in self.all_jobs:
+ if job.name == name and job.number == number:
+ job.aborted = True
+ job.release()
+ return
+
+ def cancel_queue(self, id):
+ for job in self.queue:
+ if job.queue_id == id:
+ job.canceled = True
+ job.release()
+ return
+
+ def get_queue_info(self):
+ items = []
+ for job in self.queue:
+ paramstr = ''
+ paramlst = []
+ d = {'actions': [{'parameters': paramlst},
+ {'causes': [{'shortDescription':
+ 'Started by user Jenkins',
+ 'userId': 'jenkins',
+ 'userName': 'Jenkins'}]}],
+ 'blocked': False,
+ 'buildable': True,
+ 'buildableStartMilliseconds': (job.created * 1000) + 5,
+ 'id': job.queue_id,
+ 'inQueueSince': (job.created * 1000),
+ 'params': paramstr,
+ 'stuck': False,
+ 'task': {'color': 'blue',
+ 'name': job.name,
+ 'url': 'https://server/job/%s/' % job.name},
+ 'why': 'Waiting for next available executor'}
+ for k, v in job.parameters.items():
+ paramstr += "\n(StringParameterValue) %s='%s'" % (k, v)
+ pd = {'name': k, 'value': v}
+ paramlst.append(pd)
+ items.append(d)
+ return items
+
def set_build_description(self, *args, **kw):
pass
@@ -372,6 +434,10 @@
self.sched.queue_lock.release()
self.sched.wake_event.wait(0.1)
+ def countJobResults(self, jobs, result):
+ jobs = filter(lambda x: x['result'] == result, jobs)
+ return len(jobs)
+
def test_jobs_launched(self):
"Test that jobs are launched and a change is merged"
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
@@ -386,6 +452,7 @@
assert jobs[1]['result'] == 'SUCCESS'
assert jobs[2]['result'] == 'SUCCESS'
assert A.data['status'] == 'MERGED'
+ assert A.reported == 2
def test_parallel_changes(self):
"Test that changes are tested in parallel and merged in series"
@@ -484,6 +551,9 @@
assert A.data['status'] == 'MERGED'
assert B.data['status'] == 'MERGED'
assert C.data['status'] == 'MERGED'
+ assert A.reported == 2
+ assert B.reported == 2
+ assert C.reported == 2
def test_failed_changes(self):
"Test that a change behind a failed change is retested"
@@ -502,6 +572,8 @@
assert len(jobs) > 6
assert A.data['status'] == 'NEW'
assert B.data['status'] == 'MERGED'
+ assert A.reported == 2
+ assert B.reported == 2
def test_independent_queues(self):
"Test that changes end up in the right queues"
@@ -546,3 +618,128 @@
assert A.data['status'] == 'MERGED'
assert B.data['status'] == 'MERGED'
assert C.data['status'] == 'MERGED'
+ assert A.reported == 2
+ assert B.reported == 2
+ assert C.reported == 2
+
+ def test_failed_change_at_head(self):
+ "Test that if a change at the head fails, jobs behind it are canceled"
+ self.fake_jenkins.hold_jobs_in_build = True
+
+ A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+ B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
+ C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
+
+ self.fake_jenkins.fakeAddFailTest(
+ 'project-test1',
+ 'org/project:master:refs/changes/1/1/1')
+
+ self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+ self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
+ self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
+
+ self.waitUntilSettled()
+ jobs = self.fake_jenkins.all_jobs
+ finished_jobs = self.fake_jenkins.job_history
+
+ assert len(jobs) == 1
+ assert jobs[0].name == 'project-merge'
+ assert (jobs[0].parameters['GERRIT_CHANGES'] ==
+ 'org/project:master:refs/changes/1/1/1')
+
+ self.fake_jenkins.fakeRelease('.*-merge')
+ self.waitUntilSettled()
+ self.fake_jenkins.fakeRelease('.*-merge')
+ self.waitUntilSettled()
+ self.fake_jenkins.fakeRelease('.*-merge')
+ self.waitUntilSettled()
+
+ assert len(jobs) == 6
+ assert jobs[0].name == 'project-test1'
+ assert jobs[1].name == 'project-test2'
+ assert jobs[2].name == 'project-test1'
+ assert jobs[3].name == 'project-test2'
+ assert jobs[4].name == 'project-test1'
+ assert jobs[5].name == 'project-test2'
+
+ jobs[0].release()
+ self.waitUntilSettled()
+
+ assert len(jobs) == 1 # project-test2
+ assert self.countJobResults(finished_jobs, 'ABORTED') == 4
+
+ self.fake_jenkins.hold_jobs_in_build = False
+ self.fake_jenkins.fakeRelease()
+ self.waitUntilSettled()
+
+ assert len(jobs) == 0
+ assert len(finished_jobs) == 15
+ assert A.data['status'] == 'NEW'
+ assert B.data['status'] == 'MERGED'
+ assert C.data['status'] == 'MERGED'
+ assert A.reported == 2
+ assert B.reported == 2
+ assert C.reported == 2
+
+ def test_failed_change_at_head_with_queue(self):
+ "Test that if a change at the head fails, queued jobs are canceled"
+ self.fake_jenkins.hold_jobs_in_queue = True
+
+ A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+ B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
+ C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C')
+
+ self.fake_jenkins.fakeAddFailTest(
+ 'project-test1',
+ 'org/project:master:refs/changes/1/1/1')
+
+ self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+ self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
+ self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
+
+ self.waitUntilSettled()
+ jobs = self.fake_jenkins.all_jobs
+ finished_jobs = self.fake_jenkins.job_history
+ queue = self.fake_jenkins.queue
+
+ assert len(jobs) == 1
+ assert len(queue) == 1
+ assert jobs[0].name == 'project-merge'
+ assert (jobs[0].parameters['GERRIT_CHANGES'] ==
+ 'org/project:master:refs/changes/1/1/1')
+
+ self.fake_jenkins.fakeRelease('.*-merge')
+ self.waitUntilSettled()
+ self.fake_jenkins.fakeRelease('.*-merge')
+ self.waitUntilSettled()
+ self.fake_jenkins.fakeRelease('.*-merge')
+ self.waitUntilSettled()
+
+ assert len(jobs) == 6
+ assert len(queue) == 6
+ assert jobs[0].name == 'project-test1'
+ assert jobs[1].name == 'project-test2'
+ assert jobs[2].name == 'project-test1'
+ assert jobs[3].name == 'project-test2'
+ assert jobs[4].name == 'project-test1'
+ assert jobs[5].name == 'project-test2'
+
+ jobs[0].release()
+ self.waitUntilSettled()
+
+ assert len(jobs) == 1 # project-test2
+ assert len(queue) == 1
+ assert self.countJobResults(finished_jobs, 'ABORTED') == 0
+
+ self.fake_jenkins.hold_jobs_in_queue = False
+ self.fake_jenkins.fakeRelease()
+ self.waitUntilSettled()
+
+ assert len(jobs) == 0
+ assert len(finished_jobs) == 11
+ assert A.data['status'] == 'NEW'
+ assert B.data['status'] == 'MERGED'
+ assert C.data['status'] == 'MERGED'
+ assert A.reported == 2
+ assert B.reported == 2
+ assert C.reported == 2