zuul/tests/unit/test_cross_crd.py

953 lines
36 KiB
Python

# Copyright 2012 Hewlett-Packard Development Company, L.P.
# Copyright 2018 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tests.base import (
ZuulTestCase,
)
class TestGerritToGithubCRD(ZuulTestCase):
config_file = 'zuul-gerrit-github.conf'
tenant_config_file = 'config/cross-source/main.yaml'
def test_crd_gate(self):
"Test cross-repo dependencies"
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest('github/project2', 'master',
'B')
A.addApproval('Code-Review', 2)
AM2 = self.fake_gerrit.addFakeChange('gerrit/project1', 'master',
'AM2')
AM1 = self.fake_gerrit.addFakeChange('gerrit/project1', 'master',
'AM1')
AM2.setMerged()
AM1.setMerged()
# A -> AM1 -> AM2
# A Depends-On: B
# 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.
A.setDependsOn(AM1, 1)
AM1.setDependsOn(AM2, 1)
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertFalse(B.is_merged)
for connection in self.connections.connections.values():
connection.maintainCache([])
self.executor_server.hold_jobs_in_build = True
B.addLabel('approved')
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertEqual(AM2.queried, 0)
self.assertEqual(A.data['status'], 'MERGED')
self.assertTrue(B.is_merged)
self.assertEqual(A.reported, 2)
self.assertEqual(len(B.comments), 2)
changes = self.getJobFromHistory(
'project-merge', 'gerrit/project1').changes
self.assertEqual(changes, '1,%s 1,1' % B.head_sha)
def test_crd_branch(self):
"Test cross-repo dependencies in multiple branches"
self.create_branch('github/project2', 'mp')
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest('github/project2', 'master',
'B')
C1 = self.fake_github.openFakePullRequest('github/project2', 'mp',
'C1')
A.addApproval('Code-Review', 2)
# A Depends-On: B+C1
A.data['commitMessage'] = '%s\n\nDepends-On: %s\nDepends-On: %s\n' % (
A.subject, B.url, C1.url)
self.executor_server.hold_jobs_in_build = True
B.addLabel('approved')
C1.addLabel('approved')
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'MERGED')
self.assertTrue(B.is_merged)
self.assertTrue(C1.is_merged)
self.assertEqual(A.reported, 2)
self.assertEqual(len(B.comments), 2)
self.assertEqual(len(C1.comments), 2)
changes = self.getJobFromHistory(
'project-merge', 'gerrit/project1').changes
self.assertEqual(changes, '1,%s 2,%s 1,1' %
(B.head_sha, C1.head_sha))
def test_crd_gate_reverse(self):
"Test reverse cross-repo dependencies"
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest('github/project2', 'master',
'B')
A.addApproval('Code-Review', 2)
# A Depends-On: B
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertFalse(B.is_merged)
self.executor_server.hold_jobs_in_build = True
A.addApproval('Approved', 1)
self.fake_github.emitEvent(B.addLabel('approved'))
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'MERGED')
self.assertTrue(B.is_merged)
self.assertEqual(A.reported, 2)
self.assertEqual(len(B.comments), 2)
changes = self.getJobFromHistory(
'project-merge', 'gerrit/project1').changes
self.assertEqual(changes, '1,%s 1,1' %
(B.head_sha,))
def test_crd_cycle(self):
"Test cross-repo dependency cycles"
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
msg = "Depends-On: %s" % (A.data['url'],)
B = self.fake_github.openFakePullRequest('github/project2', 'master',
'B', body=msg)
A.addApproval('Code-Review', 2)
B.addLabel('approved')
# A -> B -> A (via commit-depends)
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
self.assertEqual(
A.messages[0],
"Build failed.\n\n\nWarning:\n Dependency cycle detected\n")
self.assertEqual(len(B.comments), 0)
self.assertEqual(A.data['status'], 'NEW')
self.assertFalse(B.is_merged)
def test_crd_gate_unknown(self):
"Test unknown projects in dependent pipeline"
self.init_repo("github/unknown", tag='init')
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest('github/unknown', 'master',
'B')
A.addApproval('Code-Review', 2)
# A Depends-On: B
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
event = B.addLabel('approved')
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
# Unknown projects cannot share a queue with any other
# since they don't have common jobs with any other (they have no jobs).
# Changes which depend on unknown project changes
# should not be processed in dependent pipeline
self.assertEqual(A.data['status'], 'NEW')
self.assertFalse(B.is_merged)
self.assertEqual(A.reported, 0)
self.assertEqual(len(B.comments), 0)
self.assertEqual(len(self.history), 0)
# Simulate change B being gated outside this layout Set the
# change merged before submitting the event so that when the
# event triggers a gerrit query to update the change, we get
# the information that it was merged.
B.setMerged('merged')
self.fake_github.emitEvent(event)
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# Now that B is merged, A should be able to be enqueued and
# merged.
self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'MERGED')
self.assertEqual(A.reported, 2)
self.assertTrue(B.is_merged)
self.assertEqual(len(B.comments), 0)
def test_crd_check(self):
"Test cross-repo dependencies in independent pipelines"
self.executor_server.hold_jobs_in_build = True
self.gearman_server.hold_jobs_in_queue = True
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest(
'github/project2', 'master', 'B')
# A Depends-On: B
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.gearman_server.hold_jobs_in_queue = False
self.gearman_server.release()
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.assertTrue(self.builds[0].hasChanges(A, B))
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertFalse(B.is_merged)
self.assertEqual(A.reported, 1)
self.assertEqual(len(B.comments), 0)
changes = self.getJobFromHistory(
'project-merge', 'gerrit/project1').changes
self.assertEqual(changes, '1,%s 1,1' %
(B.head_sha,))
tenant = self.sched.abide.tenants.get('tenant-one')
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
def test_crd_check_duplicate(self):
"Test duplicate check in independent pipelines"
self.executor_server.hold_jobs_in_build = True
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest(
'github/project2', 'master', 'B')
self.waitUntilSettled()
# A Depends-On: B
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
tenant = self.sched.abide.tenants.get('tenant-one')
check_pipeline = tenant.layout.pipelines['check']
# Add two dependent changes...
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(check_pipeline.getAllItems()), 2)
# ...make sure the live one is not duplicated...
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(check_pipeline.getAllItems()), 2)
# ...but the non-live one is able to be.
self.fake_github.emitEvent(B.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(len(check_pipeline.getAllItems()), 3)
# Release jobs in order to avoid races with change A jobs
# finishing before change B jobs.
self.orderedRelease()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertFalse(B.is_merged)
self.assertEqual(A.reported, 1)
self.assertEqual(len(B.comments), 1)
changes = self.getJobFromHistory(
'project-merge', 'gerrit/project1').changes
self.assertEqual(changes, '1,%s 1,1' %
(B.head_sha,))
changes = self.getJobFromHistory(
'project-merge', 'github/project2').changes
self.assertEqual(changes, '1,%s' %
(B.head_sha,))
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
self.assertIn('Build succeeded', A.messages[0])
def _test_crd_check_reconfiguration(self, project1, project2):
"Test cross-repo dependencies re-enqueued in independent pipelines"
self.gearman_server.hold_jobs_in_queue = True
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest(
'github/project2', 'master', 'B')
# A Depends-On: B
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.scheds.execute(lambda app: app.sched.reconfigure(app.config))
# Make sure the items still share a change queue, and the
# first one is not live.
tenant = self.sched.abide.tenants.get('tenant-one')
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 1)
queue = tenant.layout.pipelines['check'].queues[0]
first_item = queue.queue[0]
for item in queue.queue:
self.assertEqual(item.queue, first_item.queue)
self.assertFalse(first_item.live)
self.assertTrue(queue.queue[1].live)
self.gearman_server.hold_jobs_in_queue = False
self.gearman_server.release()
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertFalse(B.is_merged)
self.assertEqual(A.reported, 1)
self.assertEqual(len(B.comments), 0)
changes = self.getJobFromHistory(
'project-merge', 'gerrit/project1').changes
self.assertEqual(changes, '1,%s 1,1' %
(B.head_sha,))
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
def test_crd_check_reconfiguration(self):
self._test_crd_check_reconfiguration('org/project1', 'org/project2')
def test_crd_undefined_project(self):
"""Test that undefined projects in dependencies are handled for
independent pipelines"""
# It's a hack for fake github,
# as it implies repo creation upon the creation of any change
self.init_repo("github/unknown", tag='init')
self._test_crd_check_reconfiguration('gerrit/project1',
'github/unknown')
def test_crd_check_transitive(self):
"Test transitive cross-repo dependencies"
# Specifically, if A -> B -> C, and C gets a new patchset and
# A gets a new patchset, ensure the test of A,2 includes B,1
# and C,2 (not C,1 which would indicate stale data in the
# cache for B).
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
C = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'C')
# B Depends-On: C
msg = "Depends-On: %s" % (C.data['url'],)
B = self.fake_github.openFakePullRequest(
'github/project2', 'master', 'B', body=msg)
# A Depends-On: B
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,1 1,%s 1,1' %
(B.head_sha,))
self.fake_github.emitEvent(B.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,1 1,%s' %
(B.head_sha,))
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,1')
C.addPatchset()
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(2))
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,2')
A.addPatchset()
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,2 1,%s 1,2' %
(B.head_sha,))
def test_crd_check_unknown(self):
"Test unknown projects in independent pipeline"
self.init_repo("github/unknown", tag='init')
A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
B = self.fake_github.openFakePullRequest(
'github/unknown', 'master', 'B')
# A Depends-On: B
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
# Make sure zuul has seen an event on B.
self.fake_github.emitEvent(B.getPullRequestEditedEvent())
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.data['status'], 'NEW')
self.assertEqual(A.reported, 1)
self.assertFalse(B.is_merged)
self.assertEqual(len(B.comments), 0)
def test_crd_cycle_join(self):
"Test an updated change creates a cycle"
A = self.fake_github.openFakePullRequest(
'github/project2', 'master', 'A')
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(len(A.comments), 1)
# Create B->A
B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
B.subject, A.url)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# Dep is there so zuul should have reported on B
self.assertEqual(B.reported, 1)
# Update A to add A->B (a cycle).
A.editBody('Depends-On: %s\n' % (B.data['url']))
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
# Dependency cycle injected so zuul should have reported again on A
self.assertEqual(len(A.comments), 2)
# Now if we update B to remove the depends-on, everything
# should be okay. B; A->B
B.addPatchset()
B.data['commitMessage'] = '%s\n' % (B.subject,)
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
# Cycle was removed so now zuul should have reported again on A
self.assertEqual(len(A.comments), 3)
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
self.waitUntilSettled()
self.assertEqual(B.reported, 2)
class TestGithubToGerritCRD(ZuulTestCase):
config_file = 'zuul-gerrit-github.conf'
tenant_config_file = 'config/cross-source/main.yaml'
def test_crd_gate(self):
"Test cross-repo dependencies"
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
B.addApproval('Code-Review', 2)
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url']))
event = A.addLabel('approved')
self.fake_github.emitEvent(event)
self.waitUntilSettled()
self.assertFalse(A.is_merged)
self.assertEqual(B.data['status'], 'NEW')
for connection in self.connections.connections.values():
connection.maintainCache([])
self.executor_server.hold_jobs_in_build = True
B.addApproval('Approved', 1)
self.fake_github.emitEvent(event)
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertTrue(A.is_merged)
self.assertEqual(B.data['status'], 'MERGED')
self.assertEqual(len(A.comments), 2)
self.assertEqual(B.reported, 2)
changes = self.getJobFromHistory(
'project-merge', 'github/project2').changes
self.assertEqual(changes, '1,1 1,%s' % A.head_sha)
def test_crd_branch(self):
"Test cross-repo dependencies in multiple branches"
self.create_branch('gerrit/project1', 'mp')
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
C1 = self.fake_gerrit.addFakeChange('gerrit/project1', 'mp', 'C1')
B.addApproval('Code-Review', 2)
C1.addApproval('Code-Review', 2)
# A Depends-On: B+C1
A.editBody('Depends-On: %s\nDepends-On: %s\n' % (
B.data['url'], C1.data['url']))
self.executor_server.hold_jobs_in_build = True
B.addApproval('Approved', 1)
C1.addApproval('Approved', 1)
self.fake_github.emitEvent(A.addLabel('approved'))
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertTrue(A.is_merged)
self.assertEqual(B.data['status'], 'MERGED')
self.assertEqual(C1.data['status'], 'MERGED')
self.assertEqual(len(A.comments), 2)
self.assertEqual(B.reported, 2)
self.assertEqual(C1.reported, 2)
changes = self.getJobFromHistory(
'project-merge', 'github/project2').changes
self.assertEqual(changes, '1,1 2,1 1,%s' %
(A.head_sha,))
def test_crd_gate_reverse(self):
"Test reverse cross-repo dependencies"
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
B.addApproval('Code-Review', 2)
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url'],))
self.fake_github.emitEvent(A.addLabel('approved'))
self.waitUntilSettled()
self.assertFalse(A.is_merged)
self.assertEqual(B.data['status'], 'NEW')
self.executor_server.hold_jobs_in_build = True
A.addLabel('approved')
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertTrue(A.is_merged)
self.assertEqual(B.data['status'], 'MERGED')
self.assertEqual(len(A.comments), 2)
self.assertEqual(B.reported, 2)
changes = self.getJobFromHistory(
'project-merge', 'github/project2').changes
self.assertEqual(changes, '1,1 1,%s' %
(A.head_sha,))
def test_crd_cycle(self):
"Test cross-repo dependency cycles"
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
B.subject, A.url)
B.addApproval('Code-Review', 2)
B.addApproval('Approved', 1)
# A -> B -> A (via commit-depends)
A.editBody('Depends-On: %s\n' % (B.data['url'],))
self.fake_github.emitEvent(A.addLabel('approved'))
self.waitUntilSettled()
self.assertEqual(len(A.comments), 1)
self.assertEqual(B.reported, 0)
self.assertFalse(A.is_merged)
self.assertEqual(B.data['status'], 'NEW')
def test_crd_gate_unknown(self):
"Test unknown projects in dependent pipeline"
self.init_repo("gerrit/unknown", tag='init')
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange('gerrit/unknown', 'master', 'B')
B.addApproval('Code-Review', 2)
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url'],))
B.addApproval('Approved', 1)
event = A.addLabel('approved')
self.fake_github.emitEvent(event)
self.waitUntilSettled()
# Unknown projects cannot share a queue with any other
# since they don't have common jobs with any other (they have no jobs).
# Changes which depend on unknown project changes
# should not be processed in dependent pipeline
self.assertFalse(A.is_merged)
self.assertEqual(B.data['status'], 'NEW')
self.assertEqual(len(A.comments), 0)
self.assertEqual(B.reported, 0)
self.assertEqual(len(self.history), 0)
# Simulate change B being gated outside this layout Set the
# change merged before submitting the event so that when the
# event triggers a gerrit query to update the change, we get
# the information that it was merged.
B.setMerged()
self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
self.waitUntilSettled()
self.assertEqual(len(self.history), 0)
# Now that B is merged, A should be able to be enqueued and
# merged.
self.fake_github.emitEvent(event)
self.waitUntilSettled()
self.assertTrue(A.is_merged)
self.assertEqual(len(A.comments), 2)
self.assertEqual(B.data['status'], 'MERGED')
self.assertEqual(B.reported, 0)
def test_crd_check(self):
"Test cross-repo dependencies in independent pipelines"
self.executor_server.hold_jobs_in_build = True
self.gearman_server.hold_jobs_in_queue = True
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange(
'gerrit/project1', 'master', 'B')
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url'],))
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.gearman_server.hold_jobs_in_queue = False
self.gearman_server.release()
self.waitUntilSettled()
self.executor_server.release('.*-merge')
self.waitUntilSettled()
self.assertTrue(self.builds[0].hasChanges(A, B))
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertFalse(A.is_merged)
self.assertEqual(B.data['status'], 'NEW')
self.assertEqual(len(A.comments), 1)
self.assertEqual(B.reported, 0)
changes = self.getJobFromHistory(
'project-merge', 'github/project2').changes
self.assertEqual(changes, '1,1 1,%s' %
(A.head_sha,))
tenant = self.sched.abide.tenants.get('tenant-one')
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
def test_crd_check_duplicate(self):
"Test duplicate check in independent pipelines"
self.executor_server.hold_jobs_in_build = True
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange(
'gerrit/project1', 'master', 'B')
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url'],))
tenant = self.sched.abide.tenants.get('tenant-one')
check_pipeline = tenant.layout.pipelines['check']
# Add two dependent changes...
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(len(check_pipeline.getAllItems()), 2)
# ...make sure the live one is not duplicated...
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(len(check_pipeline.getAllItems()), 2)
# ...but the non-live one is able to be.
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(len(check_pipeline.getAllItems()), 3)
# Release jobs in order to avoid races with change A jobs
# finishing before change B jobs.
self.orderedRelease()
self.executor_server.hold_jobs_in_build = False
self.executor_server.release()
self.waitUntilSettled()
self.assertFalse(A.is_merged)
self.assertEqual(B.data['status'], 'NEW')
self.assertEqual(len(A.comments), 1)
self.assertEqual(B.reported, 1)
changes = self.getJobFromHistory(
'project-merge', 'github/project2').changes
self.assertEqual(changes, '1,1 1,%s' %
(A.head_sha,))
changes = self.getJobFromHistory(
'project-merge', 'gerrit/project1').changes
self.assertEqual(changes, '1,1')
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
self.assertIn('Build succeeded', A.comments[0])
def _test_crd_check_reconfiguration(self, project1, project2):
"Test cross-repo dependencies re-enqueued in independent pipelines"
self.gearman_server.hold_jobs_in_queue = True
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange(
'gerrit/project1', 'master', 'B')
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url'],))
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.scheds.execute(lambda app: app.sched.reconfigure(app.config))
# Make sure the items still share a change queue, and the
# first one is not live.
tenant = self.sched.abide.tenants.get('tenant-one')
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 1)
queue = tenant.layout.pipelines['check'].queues[0]
first_item = queue.queue[0]
for item in queue.queue:
self.assertEqual(item.queue, first_item.queue)
self.assertFalse(first_item.live)
self.assertTrue(queue.queue[1].live)
self.gearman_server.hold_jobs_in_queue = False
self.gearman_server.release()
self.waitUntilSettled()
self.assertFalse(A.is_merged)
self.assertEqual(B.data['status'], 'NEW')
self.assertEqual(len(A.comments), 1)
self.assertEqual(B.reported, 0)
changes = self.getJobFromHistory(
'project-merge', 'github/project2').changes
self.assertEqual(changes, '1,1 1,%s' %
(A.head_sha,))
self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
def test_crd_check_reconfiguration(self):
self._test_crd_check_reconfiguration('org/project1', 'org/project2')
def test_crd_undefined_project(self):
"""Test that undefined projects in dependencies are handled for
independent pipelines"""
# It's a hack for fake gerrit,
# as it implies repo creation upon the creation of any change
self.init_repo("gerrit/unknown", tag='init')
self._test_crd_check_reconfiguration('github/project2',
'gerrit/unknown')
def test_crd_check_transitive(self):
"Test transitive cross-repo dependencies"
# Specifically, if A -> B -> C, and C gets a new patchset and
# A gets a new patchset, ensure the test of A,2 includes B,1
# and C,2 (not C,1 which would indicate stale data in the
# cache for B).
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
C = self.fake_github.openFakePullRequest('github/project2', 'master',
'C')
# B Depends-On: C
B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
B.subject, C.url)
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url'],))
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,%s 1,1 1,%s' %
(C.head_sha, A.head_sha))
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,%s 1,1' %
(C.head_sha,))
self.fake_github.emitEvent(C.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,%s' %
(C.head_sha,))
new_c_head = C.head_sha
C.addCommit()
old_c_head = C.head_sha
self.assertNotEqual(old_c_head, new_c_head)
self.fake_github.emitEvent(C.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,%s' %
(C.head_sha,))
new_a_head = A.head_sha
A.addCommit()
old_a_head = A.head_sha
self.assertNotEqual(old_a_head, new_a_head)
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '2,%s 1,1 1,%s' %
(C.head_sha, A.head_sha,))
def test_crd_check_unknown(self):
"Test unknown projects in independent pipeline"
self.init_repo("gerrit/unknown", tag='init')
A = self.fake_github.openFakePullRequest('github/project2', 'master',
'A')
B = self.fake_gerrit.addFakeChange(
'gerrit/unknown', 'master', 'B')
# A Depends-On: B
A.editBody('Depends-On: %s\n' % (B.data['url'],))
# Make sure zuul has seen an event on B.
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
self.fake_github.emitEvent(A.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertFalse(A.is_merged)
self.assertEqual(len(A.comments), 1)
self.assertEqual(B.data['status'], 'NEW')
self.assertEqual(B.reported, 0)
def test_crd_cycle_join(self):
"Test an updated change creates a cycle"
A = self.fake_gerrit.addFakeChange(
'gerrit/project1', 'master', 'A')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
self.assertEqual(A.reported, 1)
# Create B->A
B = self.fake_github.openFakePullRequest('github/project2', 'master',
'B')
B.editBody('Depends-On: %s\n' % (A.data['url'],))
self.fake_github.emitEvent(B.getPullRequestEditedEvent())
self.waitUntilSettled()
# Dep is there so zuul should have reported on B
self.assertEqual(len(B.comments), 1)
# Update A to add A->B (a cycle).
A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
A.subject, B.url)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# Dependency cycle injected so zuul should have reported again on A
self.assertEqual(A.reported, 2)
# Now if we update B to remove the depends-on, everything
# should be okay. B; A->B
B.addCommit()
B.editBody('')
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
# Cycle was removed so now zuul should have reported again on A
self.assertEqual(A.reported, 3)
self.fake_github.emitEvent(B.getPullRequestEditedEvent())
self.waitUntilSettled()
self.assertEqual(len(B.comments), 2)