Add more tests.

* Test that patches are queued in gerrit dependency order.
 * Test that we correctly decide when a change is able to merge.

Change-Id: Ib541314e7c956202a3f1f33a6b2185dd22f83f73
Reviewed-on: https://review.openstack.org/10633
Reviewed-by: Clark Boylan <clark.boylan@gmail.com>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
This commit is contained in:
James E. Blair 2012-07-31 16:25:42 -07:00 committed by Jenkins
parent d466dc4f32
commit 8c803f838c
2 changed files with 154 additions and 9 deletions

View File

@ -42,19 +42,21 @@ logging.basicConfig(level=logging.DEBUG)
class FakeChange(object):
categories = {'APRV': 'Approved',
'CRVW': 'Code-Review',
'VRFY': 'Verified'}
categories = {'APRV': ('Approved', -1, 1),
'CRVW': ('Code-Review', -2, 2),
'VRFY': ('Verified', -2, 2)}
def __init__(self, number, project, branch, subject, status='NEW'):
self.reported = 0
self.queried = 0
self.patchsets = []
self.submit_records = []
self.number = number
self.project = project
self.branch = branch
self.subject = subject
self.latest_patchset = 0
self.depends_on_change = None
self.needed_by_changes = []
self.data = {
'branch': branch,
'comments': [],
@ -71,10 +73,11 @@ class FakeChange(object):
'project': project,
'status': status,
'subject': subject,
'submitRecords': self.submit_records,
'submitRecords': [],
'url': 'https://hostname/%s' % number}
self.addPatchset()
self.data['submitRecords'] = self.getSubmitRecords()
def addPatchset(self, files=None):
self.latest_patchset += 1
@ -84,7 +87,7 @@ class FakeChange(object):
'type': 'ADDED'},
{'file': 'README',
'type': 'MODIFIED'}],
'number': self.latest_patchset,
'number': str(self.latest_patchset),
'ref': 'refs/changes/1/%s/%s' % (self.number,
self.latest_patchset),
'revision':
@ -94,11 +97,14 @@ class FakeChange(object):
'username': 'user'}}
self.data['currentPatchSet'] = d
self.patchsets.append(d)
self.data['submitRecords'] = self.getSubmitRecords()
def addApproval(self, category, value):
event = {'approvals': [{'description': self.categories[category],
'type': category,
'value': str(value)}],
approval = {'description': self.categories[category][0],
'type': category,
'value': str(value)}
self.patchsets[-1]['approvals'].append(approval)
event = {'approvals': [approval],
'author': {'email': 'user@example.com',
'name': 'User Name',
'username': 'username'},
@ -115,9 +121,70 @@ class FakeChange(object):
'comment': '',
'patchSet': self.patchsets[-1],
'type': 'comment-added'}
self.data['submitRecords'] = self.getSubmitRecords()
return json.loads(json.dumps(event))
def getSubmitRecords(self):
status = {}
for cat in self.categories.keys():
status[cat] = 0
for a in self.patchsets[-1]['approvals']:
cur = status[a['type']]
cat_min, cat_max = self.categories[a['type']][1:]
new = int(a['value'])
if new == cat_min:
cur = new
elif abs(new) > abs(cur):
cur = new
status[a['type']] = cur
labels = []
ok = True
for typ, cat in self.categories.items():
cur = status[typ]
cat_min, cat_max = cat[1:]
if cur == cat_min:
value = 'REJECT'
ok = False
elif cur == cat_max:
value = 'OK'
else:
value = 'NEED'
ok = False
labels.append({'label': cat[0], 'status': value})
if ok:
return [{'status': 'OK'}]
return [{'status': 'NOT_READY',
'labels': labels}]
def setDependsOn(self, other, patchset):
self.depends_on_change = other
d = {'id': other.data['id'],
'number': other.data['number'],
'ref': other.patchsets[patchset - 1]['ref']
}
self.data['dependsOn'] = [d]
other.needed_by_changes.append(self)
needed = other.data.get('neededBy', [])
d = {'id': self.data['id'],
'number': self.data['number'],
'ref': self.patchsets[patchset - 1]['ref'],
'revision': self.patchsets[patchset - 1]['revision']
}
needed.append(d)
other.data['neededBy'] = needed
def query(self):
self.queried += 1
d = self.data.get('dependsOn')
if d:
d = d[0]
if (self.depends_on_change.patchsets[-1]['ref'] == d['ref']):
d['isCurrentPatchSet'] = True
else:
d['isCurrentPatchSet'] = False
return json.loads(json.dumps(self.data))
def setMerged(self):
@ -441,6 +508,7 @@ class testScheduler(unittest.TestCase):
def test_jobs_launched(self):
"Test that jobs are launched and a change is merged"
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
A.addApproval('CRVW', 2)
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
self.waitUntilSettled()
jobs = self.fake_jenkins.job_history
@ -460,6 +528,9 @@ class testScheduler(unittest.TestCase):
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')
A.addApproval('CRVW', 2)
B.addApproval('CRVW', 2)
C.addApproval('CRVW', 2)
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
@ -559,6 +630,8 @@ class testScheduler(unittest.TestCase):
"Test that a change behind a failed change is retested"
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
A.addApproval('CRVW', 2)
B.addApproval('CRVW', 2)
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
@ -581,6 +654,9 @@ class testScheduler(unittest.TestCase):
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C')
A.addApproval('CRVW', 2)
B.addApproval('CRVW', 2)
C.addApproval('CRVW', 2)
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
@ -629,6 +705,9 @@ class testScheduler(unittest.TestCase):
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')
A.addApproval('CRVW', 2)
B.addApproval('CRVW', 2)
C.addApproval('CRVW', 2)
self.fake_jenkins.fakeAddFailTest(
'project-test1',
@ -688,6 +767,9 @@ class testScheduler(unittest.TestCase):
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')
A.addApproval('CRVW', 2)
B.addApproval('CRVW', 2)
C.addApproval('CRVW', 2)
self.fake_jenkins.fakeAddFailTest(
'project-test1',
@ -743,3 +825,64 @@ class testScheduler(unittest.TestCase):
assert A.reported == 2
assert B.reported == 2
assert C.reported == 2
def test_patch_order(self):
"Test that dependent patches are tested in the right order"
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')
A.addApproval('CRVW', 2)
B.addApproval('CRVW', 2)
C.addApproval('CRVW', 2)
M2 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M2')
M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1')
M2.setMerged()
M1.setMerged()
# C -> B -> A -> M1 -> M2
# M2 is here to make sure it is never queried. If it is, it
# means zuul is walking down the entire history of merged
# changes.
C.setDependsOn(B, 1)
B.setDependsOn(A, 1)
A.setDependsOn(M1, 1)
M1.setDependsOn(M2, 1)
self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
self.waitUntilSettled()
assert A.data['status'] == 'NEW'
assert B.data['status'] == 'NEW'
assert C.data['status'] == 'NEW'
self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
self.waitUntilSettled()
assert M2.queried == 0
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_can_merge(self):
"Test that whether a change is ready to merge"
# TODO: move to test_gerrit (this is a unit test!)
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
a = self.sched.trigger.getChange(1, 2, 'gate')
assert not a.can_merge
A.addApproval('CRVW', 2)
a = self.sched.trigger.getChange(1, 2, 'gate')
assert not a.can_merge
A.addApproval('APRV', 1)
a = self.sched.trigger.getChange(1, 2, 'gate')
assert a.can_merge
return True

View File

@ -654,6 +654,8 @@ class DependentQueueManager(BaseQueueManager):
self.log.debug(" Change %s needs %s and is ready to merge" %
(needs, change))
to_enqueue.append(needs)
if not to_enqueue:
self.log.debug(" No changes need %s" % change)
return to_enqueue
def addChange(self, change):