Dequeue changes when new patchset created.
When a new patchset is created for a change that is in a pipeline,
cancel running builds and dequeue that change (and possibly
dependent changes that can no longer merge).
Make this optional (and document the option).
Fixes bug 1022643.
Change-Id: I8f591956cf86645443e4b6075b8cdfc95a939e4f
Reviewed-on: https://review.openstack.org/20948
Reviewed-by: Jeremy Stanley <fungi@yuggoth.org>
Approved: James E. Blair <corvus@inaugust.com>
Tested-by: Jenkins
diff --git a/tests/fixtures/layout.yaml b/tests/fixtures/layout.yaml
index 586e2a5..cab97b9 100644
--- a/tests/fixtures/layout.yaml
+++ b/tests/fixtures/layout.yaml
@@ -31,6 +31,7 @@
- name: unused
manager: IndependentPipelineManager
+ dequeue-on-new-patchset: false
trigger:
- event: comment-added
approval:
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 20ad4ec..aa7001e 100644
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -1332,7 +1332,7 @@
self.assertEmptyQueues()
def test_head_is_dequeued_once(self):
- "Test that if a change at the head fails it is dequeud only once"
+ "Test that if a change at the head fails it is dequeued only once"
# If it's dequeued more than once, we should see extra
# aborted jobs.
self.fake_jenkins.hold_jobs_in_build = True
@@ -1669,3 +1669,189 @@
jobs = self.fake_jenkins.job_history
assert len(jobs) == 0
self.assertEmptyQueues()
+
+ def test_new_patchset_dequeues_old(self):
+ "Test that a new patchset causes the old to be dequeued"
+ # D -> C (depends on B) -> B (depends on A) -> A -> M
+ self.fake_jenkins.hold_jobs_in_build = True
+
+ M = self.fake_gerrit.addFakeChange('org/project', 'master', 'M')
+ M.setMerged()
+
+ 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')
+ D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D')
+ A.addApproval('CRVW', 2)
+ B.addApproval('CRVW', 2)
+ C.addApproval('CRVW', 2)
+ D.addApproval('CRVW', 2)
+
+ C.setDependsOn(B, 1)
+ B.setDependsOn(A, 1)
+ A.setDependsOn(M, 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.fake_gerrit.addEvent(D.addApproval('APRV', 1))
+ self.waitUntilSettled()
+
+ B.addPatchset()
+ self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
+ self.waitUntilSettled()
+
+ self.fake_jenkins.hold_jobs_in_build = False
+ self.fake_jenkins.fakeRelease()
+ self.waitUntilSettled()
+
+ jobs = self.fake_jenkins.all_jobs
+ finished_jobs = self.fake_jenkins.job_history
+
+ for x in jobs:
+ print x
+ for x in finished_jobs:
+ print x
+
+ assert A.data['status'] == 'MERGED'
+ assert A.reported == 2
+ assert B.data['status'] == 'NEW'
+ assert B.reported == 2
+ assert C.data['status'] == 'NEW'
+ assert C.reported == 2
+ assert D.data['status'] == 'MERGED'
+ assert D.reported == 2
+ assert len(finished_jobs) == 9 # 3 each for A, B, D.
+ self.assertEmptyQueues()
+
+ def test_new_patchset_dequeues_old_on_head(self):
+ "Test that a new patchset causes the old to be dequeued (at head)"
+ # D -> C (depends on B) -> B (depends on A) -> A -> M
+ self.fake_jenkins.hold_jobs_in_build = True
+
+ M = self.fake_gerrit.addFakeChange('org/project', 'master', 'M')
+ M.setMerged()
+ 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')
+ D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D')
+ A.addApproval('CRVW', 2)
+ B.addApproval('CRVW', 2)
+ C.addApproval('CRVW', 2)
+ D.addApproval('CRVW', 2)
+
+ C.setDependsOn(B, 1)
+ B.setDependsOn(A, 1)
+ A.setDependsOn(M, 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.fake_gerrit.addEvent(D.addApproval('APRV', 1))
+ self.waitUntilSettled()
+
+ A.addPatchset()
+ self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
+ self.waitUntilSettled()
+
+ self.fake_jenkins.hold_jobs_in_build = False
+ self.fake_jenkins.fakeRelease()
+ self.waitUntilSettled()
+
+ jobs = self.fake_jenkins.all_jobs
+ finished_jobs = self.fake_jenkins.job_history
+
+ for x in jobs:
+ print x
+ for x in finished_jobs:
+ print x
+
+ assert A.data['status'] == 'NEW'
+ assert A.reported == 2
+ assert B.data['status'] == 'NEW'
+ assert B.reported == 2
+ assert C.data['status'] == 'NEW'
+ assert C.reported == 2
+ assert D.data['status'] == 'MERGED'
+ assert D.reported == 2
+ assert len(finished_jobs) == 7
+ self.assertEmptyQueues()
+
+ def test_new_patchset_dequeues_old_without_dependents(self):
+ "Test that a new patchset causes only the old to be dequeued"
+ 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')
+ A.addApproval('CRVW', 2)
+ B.addApproval('CRVW', 2)
+ C.addApproval('CRVW', 2)
+
+ self.fake_gerrit.addEvent(C.addApproval('APRV', 1))
+ self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
+ self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+ self.waitUntilSettled()
+
+ B.addPatchset()
+ self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
+ self.waitUntilSettled()
+
+ self.fake_jenkins.hold_jobs_in_build = False
+ self.fake_jenkins.fakeRelease()
+ self.waitUntilSettled()
+
+ jobs = self.fake_jenkins.all_jobs
+ finished_jobs = self.fake_jenkins.job_history
+
+ for x in jobs:
+ print x
+ for x in finished_jobs:
+ print x
+
+ assert A.data['status'] == 'MERGED'
+ assert A.reported == 2
+ assert B.data['status'] == 'NEW'
+ assert B.reported == 2
+ assert C.data['status'] == 'MERGED'
+ assert C.reported == 2
+ assert len(finished_jobs) == 9
+ self.assertEmptyQueues()
+
+ def test_new_patchset_dequeues_old_independent_queue(self):
+ "Test that a new patchset causes the old to be dequeued (independent)"
+ 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_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+ self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
+ self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
+ self.waitUntilSettled()
+
+ B.addPatchset()
+ self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
+ self.waitUntilSettled()
+
+ self.fake_jenkins.hold_jobs_in_build = False
+ self.fake_jenkins.fakeRelease()
+ self.waitUntilSettled()
+
+ jobs = self.fake_jenkins.all_jobs
+ finished_jobs = self.fake_jenkins.job_history
+
+ for x in jobs:
+ print x
+ for x in finished_jobs:
+ print x
+
+ assert A.data['status'] == 'NEW'
+ assert A.reported == 1
+ assert B.data['status'] == 'NEW'
+ assert B.reported == 1
+ assert C.data['status'] == 'NEW'
+ assert C.reported == 1
+ assert len(finished_jobs) == 10
+ assert self.countJobResults(finished_jobs, 'ABORTED') == 1
+ self.assertEmptyQueues()