You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5799 lines
234 KiB
5799 lines
234 KiB
#!/usr/bin/env python |
|
|
|
# Copyright 2012 Hewlett-Packard Development Company, L.P. |
|
# |
|
# 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. |
|
|
|
import gc |
|
import json |
|
import textwrap |
|
|
|
import os |
|
import re |
|
import shutil |
|
import time |
|
from unittest import skip |
|
|
|
import git |
|
import testtools |
|
import urllib |
|
|
|
import zuul.change_matcher |
|
from zuul.driver.gerrit import gerritreporter |
|
import zuul.scheduler |
|
import zuul.rpcclient |
|
import zuul.model |
|
|
|
from tests.base import ( |
|
SSLZuulTestCase, |
|
ZuulTestCase, |
|
repack_repo, |
|
simple_layout, |
|
) |
|
|
|
|
|
class TestSchedulerSSL(SSLZuulTestCase): |
|
tenant_config_file = 'config/single-tenant/main.yaml' |
|
|
|
def test_jobs_executed(self): |
|
"Test that jobs are executed and a change is merged" |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(self.getJobFromHistory('project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'SUCCESS') |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(self.getJobFromHistory('project-test1').node, |
|
'label1') |
|
self.assertEqual(self.getJobFromHistory('project-test2').node, |
|
'label1') |
|
|
|
|
|
class TestScheduler(ZuulTestCase): |
|
tenant_config_file = 'config/single-tenant/main.yaml' |
|
|
|
def test_jobs_executed(self): |
|
"Test that jobs are executed and a change is merged" |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(self.getJobFromHistory('project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'SUCCESS') |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(self.getJobFromHistory('project-test1').node, |
|
'label1') |
|
self.assertEqual(self.getJobFromHistory('project-test2').node, |
|
'label1') |
|
|
|
for stat in self.statsd.stats: |
|
k, v = stat.decode('utf-8').split(':') |
|
self.log.debug('stat %s:%s', k, v) |
|
# TODOv3(jeblair): we may want to report stats by tenant (also?). |
|
# Per-driver |
|
self.assertReportedStat('zuul.event.gerrit.comment-added', value='1|c') |
|
# Per-driver per-connection |
|
self.assertReportedStat('zuul.event.gerrit.gerrit.comment-added', |
|
value='1|c') |
|
self.assertReportedStat( |
|
'zuul.tenant.tenant-one.pipeline.gate.current_changes', |
|
value='1|g') |
|
self.assertReportedStat( |
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.' |
|
'org_project.master.job.project-merge.SUCCESS', kind='ms') |
|
self.assertReportedStat( |
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.' |
|
'org_project.master.job.project-merge.SUCCESS', value='1|c') |
|
self.assertReportedStat( |
|
'zuul.tenant.tenant-one.pipeline.gate.resident_time', kind='ms') |
|
self.assertReportedStat( |
|
'zuul.tenant.tenant-one.pipeline.gate.total_changes', value='1|c') |
|
self.assertReportedStat( |
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.' |
|
'org_project.master.resident_time', kind='ms') |
|
self.assertReportedStat( |
|
'zuul.tenant.tenant-one.pipeline.gate.project.review_example_com.' |
|
'org_project.master.total_changes', value='1|c') |
|
exec_key = 'zuul.executor.%s' % self.executor_server.hostname |
|
self.assertReportedStat(exec_key + '.builds', value='1|c') |
|
|
|
for build in self.history: |
|
self.assertTrue(build.parameters['zuul']['voting']) |
|
|
|
def test_initial_pipeline_gauges(self): |
|
"Test that each pipeline reported its length on start" |
|
self.assertReportedStat('zuul.pipeline.gate.current_changes', |
|
value='0|g') |
|
self.assertReportedStat('zuul.pipeline.check.current_changes', |
|
value='0|g') |
|
|
|
def test_job_branch(self): |
|
"Test the correct variant of a job runs on a branch" |
|
self.create_branch('org/project', 'stable') |
|
A = self.fake_gerrit.addFakeChange('org/project', 'stable', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'SUCCESS') |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2, |
|
"A should report start and success") |
|
self.assertIn('gate', A.messages[1], |
|
"A should transit gate") |
|
self.assertEqual(self.getJobFromHistory('project-test1').node, |
|
'label2') |
|
|
|
def test_parallel_changes(self): |
|
"Test that changes are tested in parallel and merged in series" |
|
|
|
self.executor_server.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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.builds), 1) |
|
self.assertEqual(self.builds[0].name, 'project-merge') |
|
self.assertTrue(self.builds[0].hasChanges(A)) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.builds), 3) |
|
self.assertEqual(self.builds[0].name, 'project-test1') |
|
self.assertTrue(self.builds[0].hasChanges(A)) |
|
self.assertEqual(self.builds[1].name, 'project-test2') |
|
self.assertTrue(self.builds[1].hasChanges(A)) |
|
self.assertEqual(self.builds[2].name, 'project-merge') |
|
self.assertTrue(self.builds[2].hasChanges(A, B)) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.builds), 5) |
|
self.assertEqual(self.builds[0].name, 'project-test1') |
|
self.assertTrue(self.builds[0].hasChanges(A)) |
|
self.assertEqual(self.builds[1].name, 'project-test2') |
|
self.assertTrue(self.builds[1].hasChanges(A)) |
|
|
|
self.assertEqual(self.builds[2].name, 'project-test1') |
|
self.assertTrue(self.builds[2].hasChanges(A, B)) |
|
self.assertEqual(self.builds[3].name, 'project-test2') |
|
self.assertTrue(self.builds[3].hasChanges(A, B)) |
|
|
|
self.assertEqual(self.builds[4].name, 'project-merge') |
|
self.assertTrue(self.builds[4].hasChanges(A, B, C)) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.builds), 6) |
|
self.assertEqual(self.builds[0].name, 'project-test1') |
|
self.assertTrue(self.builds[0].hasChanges(A)) |
|
self.assertEqual(self.builds[1].name, 'project-test2') |
|
self.assertTrue(self.builds[1].hasChanges(A)) |
|
|
|
self.assertEqual(self.builds[2].name, 'project-test1') |
|
self.assertTrue(self.builds[2].hasChanges(A, B)) |
|
self.assertEqual(self.builds[3].name, 'project-test2') |
|
self.assertTrue(self.builds[3].hasChanges(A, B)) |
|
|
|
self.assertEqual(self.builds[4].name, 'project-test1') |
|
self.assertTrue(self.builds[4].hasChanges(A, B, C)) |
|
self.assertEqual(self.builds[5].name, 'project-test2') |
|
self.assertTrue(self.builds[5].hasChanges(A, B, C)) |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.builds), 0) |
|
|
|
self.assertEqual(len(self.history), 9) |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.reported, 2) |
|
|
|
def test_failed_changes(self): |
|
"Test that a change behind a failed change is retested" |
|
self.executor_server.hold_jobs_in_build = True |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B') |
|
A.addApproval('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
|
|
self.executor_server.failJob('project-test1', A) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertBuilds([dict(name='project-merge', changes='1,1')]) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
# A/project-merge is complete |
|
self.assertBuilds([ |
|
dict(name='project-test1', changes='1,1'), |
|
dict(name='project-test2', changes='1,1'), |
|
dict(name='project-merge', changes='1,1 2,1'), |
|
]) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
# A/project-merge is complete |
|
# B/project-merge is complete |
|
self.assertBuilds([ |
|
dict(name='project-test1', changes='1,1'), |
|
dict(name='project-test2', changes='1,1'), |
|
dict(name='project-test1', changes='1,1 2,1'), |
|
dict(name='project-test2', changes='1,1 2,1'), |
|
]) |
|
|
|
# Release project-test1 for A which will fail. This will |
|
# abort both running B jobs and reexecute project-merge for B. |
|
self.builds[0].release() |
|
self.waitUntilSettled() |
|
|
|
self.orderedRelease() |
|
self.assertHistory([ |
|
dict(name='project-merge', result='SUCCESS', changes='1,1'), |
|
dict(name='project-merge', result='SUCCESS', changes='1,1 2,1'), |
|
dict(name='project-test1', result='FAILURE', changes='1,1'), |
|
dict(name='project-test1', result='ABORTED', changes='1,1 2,1'), |
|
dict(name='project-test2', result='ABORTED', changes='1,1 2,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='1,1'), |
|
dict(name='project-merge', result='SUCCESS', changes='2,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='2,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='2,1'), |
|
], ordered=False) |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
|
|
def test_independent_queues(self): |
|
"Test that changes end up in the right queues" |
|
|
|
self.executor_server.hold_jobs_in_build = True |
|
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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
|
|
# There should be one merge job at the head of each queue running |
|
self.assertBuilds([ |
|
dict(name='project-merge', changes='1,1'), |
|
dict(name='project-merge', changes='2,1'), |
|
]) |
|
|
|
# Release the current merge builds |
|
self.builds[0].release() |
|
self.waitUntilSettled() |
|
self.builds[0].release() |
|
self.waitUntilSettled() |
|
# Release the merge job for project2 which is behind project1 |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
# All the test builds should be running: |
|
self.assertBuilds([ |
|
dict(name='project-test1', changes='1,1'), |
|
dict(name='project-test2', changes='1,1'), |
|
dict(name='project-test1', changes='2,1'), |
|
dict(name='project-test2', changes='2,1'), |
|
dict(name='project1-project2-integration', changes='2,1'), |
|
dict(name='project-test1', changes='2,1 3,1'), |
|
dict(name='project-test2', changes='2,1 3,1'), |
|
dict(name='project1-project2-integration', changes='2,1 3,1'), |
|
]) |
|
|
|
self.orderedRelease() |
|
self.assertHistory([ |
|
dict(name='project-merge', result='SUCCESS', changes='1,1'), |
|
dict(name='project-merge', result='SUCCESS', changes='2,1'), |
|
dict(name='project-merge', result='SUCCESS', changes='2,1 3,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='2,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='2,1'), |
|
dict( |
|
name='project1-project2-integration', |
|
result='SUCCESS', |
|
changes='2,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='2,1 3,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='2,1 3,1'), |
|
dict(name='project1-project2-integration', |
|
result='SUCCESS', |
|
changes='2,1 3,1'), |
|
]) |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(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.executor_server.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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
self.executor_server.failJob('project-test1', A) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertBuilds([ |
|
dict(name='project-merge', changes='1,1'), |
|
]) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
self.assertBuilds([ |
|
dict(name='project-test1', changes='1,1'), |
|
dict(name='project-test2', changes='1,1'), |
|
dict(name='project-test1', changes='1,1 2,1'), |
|
dict(name='project-test2', changes='1,1 2,1'), |
|
dict(name='project-test1', changes='1,1 2,1 3,1'), |
|
dict(name='project-test2', changes='1,1 2,1 3,1'), |
|
]) |
|
|
|
self.release(self.builds[0]) |
|
self.waitUntilSettled() |
|
|
|
# project-test2, project-merge for B |
|
self.assertBuilds([ |
|
dict(name='project-test2', changes='1,1'), |
|
dict(name='project-merge', changes='2,1'), |
|
]) |
|
# Unordered history comparison because the aborts can finish |
|
# in any order. |
|
self.assertHistory([ |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='1,1'), |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='1,1 2,1'), |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='1,1 2,1 3,1'), |
|
dict(name='project-test1', result='FAILURE', |
|
changes='1,1'), |
|
dict(name='project-test1', result='ABORTED', |
|
changes='1,1 2,1'), |
|
dict(name='project-test2', result='ABORTED', |
|
changes='1,1 2,1'), |
|
dict(name='project-test1', result='ABORTED', |
|
changes='1,1 2,1 3,1'), |
|
dict(name='project-test2', result='ABORTED', |
|
changes='1,1 2,1 3,1'), |
|
], ordered=False) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.orderedRelease() |
|
|
|
self.assertBuilds([]) |
|
self.assertHistory([ |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='1,1'), |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='1,1 2,1'), |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='1,1 2,1 3,1'), |
|
dict(name='project-test1', result='FAILURE', |
|
changes='1,1'), |
|
dict(name='project-test1', result='ABORTED', |
|
changes='1,1 2,1'), |
|
dict(name='project-test2', result='ABORTED', |
|
changes='1,1 2,1'), |
|
dict(name='project-test1', result='ABORTED', |
|
changes='1,1 2,1 3,1'), |
|
dict(name='project-test2', result='ABORTED', |
|
changes='1,1 2,1 3,1'), |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='2,1'), |
|
dict(name='project-merge', result='SUCCESS', |
|
changes='2,1 3,1'), |
|
dict(name='project-test2', result='SUCCESS', |
|
changes='1,1'), |
|
dict(name='project-test1', result='SUCCESS', |
|
changes='2,1'), |
|
dict(name='project-test2', result='SUCCESS', |
|
changes='2,1'), |
|
dict(name='project-test1', result='SUCCESS', |
|
changes='2,1 3,1'), |
|
dict(name='project-test2', result='SUCCESS', |
|
changes='2,1 3,1'), |
|
], ordered=False) |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.reported, 2) |
|
|
|
def test_failed_change_in_middle(self): |
|
"Test a failed change in the middle of the queue" |
|
|
|
self.executor_server.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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
self.executor_server.failJob('project-test1', B) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.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.assertEqual(len(self.builds), 6) |
|
self.assertEqual(self.builds[0].name, 'project-test1') |
|
self.assertEqual(self.builds[1].name, 'project-test2') |
|
self.assertEqual(self.builds[2].name, 'project-test1') |
|
self.assertEqual(self.builds[3].name, 'project-test2') |
|
self.assertEqual(self.builds[4].name, 'project-test1') |
|
self.assertEqual(self.builds[5].name, 'project-test2') |
|
|
|
self.release(self.builds[2]) |
|
self.waitUntilSettled() |
|
|
|
# project-test1 and project-test2 for A |
|
# project-test2 for B |
|
# project-merge for C (without B) |
|
self.assertEqual(len(self.builds), 4) |
|
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 2) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
# project-test1 and project-test2 for A |
|
# project-test2 for B |
|
# project-test1 and project-test2 for C |
|
self.assertEqual(len(self.builds), 5) |
|
|
|
tenant = self.sched.abide.tenants.get('tenant-one') |
|
items = tenant.layout.pipelines['gate'].getAllItems() |
|
builds = items[0].current_build_set.getBuilds() |
|
self.assertEqual(self.countJobResults(builds, 'SUCCESS'), 1) |
|
self.assertEqual(self.countJobResults(builds, None), 2) |
|
builds = items[1].current_build_set.getBuilds() |
|
self.assertEqual(self.countJobResults(builds, 'SUCCESS'), 1) |
|
self.assertEqual(self.countJobResults(builds, 'FAILURE'), 1) |
|
self.assertEqual(self.countJobResults(builds, None), 1) |
|
builds = items[2].current_build_set.getBuilds() |
|
self.assertEqual(self.countJobResults(builds, 'SUCCESS'), 1) |
|
self.assertEqual(self.countJobResults(builds, None), 2) |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 0) |
|
self.assertEqual(len(self.history), 12) |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(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.gearman_server.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') |
|
A.addApproval('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
self.executor_server.failJob('project-test1', A) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
queue = self.gearman_server.getQueue() |
|
self.assertEqual(len(self.builds), 0) |
|
self.assertEqual(len(queue), 1) |
|
self.assertEqual(queue[0].name, b'executor:execute') |
|
job_args = json.loads(queue[0].arguments.decode('utf8')) |
|
self.assertEqual(job_args['job'], 'project-merge') |
|
self.assertEqual(job_args['items'][0]['number'], '%d' % A.number) |
|
|
|
self.gearman_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.gearman_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.gearman_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
queue = self.gearman_server.getQueue() |
|
|
|
self.assertEqual(len(self.builds), 0) |
|
self.assertEqual(len(queue), 6) |
|
|
|
self.assertEqual( |
|
json.loads(queue[0].arguments.decode('utf8'))['job'], |
|
'project-test1') |
|
self.assertEqual( |
|
json.loads(queue[1].arguments.decode('utf8'))['job'], |
|
'project-test2') |
|
self.assertEqual( |
|
json.loads(queue[2].arguments.decode('utf8'))['job'], |
|
'project-test1') |
|
self.assertEqual( |
|
json.loads(queue[3].arguments.decode('utf8'))['job'], |
|
'project-test2') |
|
self.assertEqual( |
|
json.loads(queue[4].arguments.decode('utf8'))['job'], |
|
'project-test1') |
|
self.assertEqual( |
|
json.loads(queue[5].arguments.decode('utf8'))['job'], |
|
'project-test2') |
|
|
|
self.release(queue[0]) |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 0) |
|
queue = self.gearman_server.getQueue() |
|
self.assertEqual(len(queue), 2) # project-test2, project-merge for B |
|
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 0) |
|
|
|
self.gearman_server.hold_jobs_in_queue = False |
|
self.gearman_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 0) |
|
self.assertEqual(len(self.history), 11) |
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.reported, 2) |
|
|
|
def _test_time_database(self, iteration): |
|
self.executor_server.hold_jobs_in_build = True |
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
time.sleep(2) |
|
|
|
data = json.loads(self.sched.formatStatusJSON('tenant-one')) |
|
found_job = None |
|
for pipeline in data['pipelines']: |
|
if pipeline['name'] != 'gate': |
|
continue |
|
for queue in pipeline['change_queues']: |
|
for head in queue['heads']: |
|
for item in head: |
|
for job in item['jobs']: |
|
if job['name'] == 'project-merge': |
|
found_job = job |
|
break |
|
|
|
self.assertIsNotNone(found_job) |
|
if iteration == 1: |
|
self.assertIsNotNone(found_job['estimated_time']) |
|
self.assertIsNone(found_job['remaining_time']) |
|
else: |
|
self.assertIsNotNone(found_job['estimated_time']) |
|
self.assertTrue(found_job['estimated_time'] >= 2) |
|
self.assertIsNotNone(found_job['remaining_time']) |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
def test_time_database(self): |
|
"Test the time database" |
|
|
|
self._test_time_database(1) |
|
self._test_time_database(2) |
|
|
|
def test_two_failed_changes_at_head(self): |
|
"Test that changes are reparented correctly if 2 fail at head" |
|
|
|
self.executor_server.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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
self.executor_server.failJob('project-test1', A) |
|
self.executor_server.failJob('project-test1', B) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.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.assertEqual(len(self.builds), 6) |
|
self.assertEqual(self.builds[0].name, 'project-test1') |
|
self.assertEqual(self.builds[1].name, 'project-test2') |
|
self.assertEqual(self.builds[2].name, 'project-test1') |
|
self.assertEqual(self.builds[3].name, 'project-test2') |
|
self.assertEqual(self.builds[4].name, 'project-test1') |
|
self.assertEqual(self.builds[5].name, 'project-test2') |
|
|
|
self.assertTrue(self.builds[0].hasChanges(A)) |
|
self.assertTrue(self.builds[2].hasChanges(A)) |
|
self.assertTrue(self.builds[2].hasChanges(B)) |
|
self.assertTrue(self.builds[4].hasChanges(A)) |
|
self.assertTrue(self.builds[4].hasChanges(B)) |
|
self.assertTrue(self.builds[4].hasChanges(C)) |
|
|
|
# Fail change B first |
|
self.release(self.builds[2]) |
|
self.waitUntilSettled() |
|
|
|
# restart of C after B failure |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 5) |
|
self.assertEqual(self.builds[0].name, 'project-test1') |
|
self.assertEqual(self.builds[1].name, 'project-test2') |
|
self.assertEqual(self.builds[2].name, 'project-test2') |
|
self.assertEqual(self.builds[3].name, 'project-test1') |
|
self.assertEqual(self.builds[4].name, 'project-test2') |
|
|
|
self.assertTrue(self.builds[1].hasChanges(A)) |
|
self.assertTrue(self.builds[2].hasChanges(A)) |
|
self.assertTrue(self.builds[2].hasChanges(B)) |
|
self.assertTrue(self.builds[4].hasChanges(A)) |
|
self.assertFalse(self.builds[4].hasChanges(B)) |
|
self.assertTrue(self.builds[4].hasChanges(C)) |
|
|
|
# Finish running all passing jobs for change A |
|
self.release(self.builds[1]) |
|
self.waitUntilSettled() |
|
# Fail and report change A |
|
self.release(self.builds[0]) |
|
self.waitUntilSettled() |
|
|
|
# restart of B,C after A failure |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 4) |
|
self.assertEqual(self.builds[0].name, 'project-test1') # B |
|
self.assertEqual(self.builds[1].name, 'project-test2') # B |
|
self.assertEqual(self.builds[2].name, 'project-test1') # C |
|
self.assertEqual(self.builds[3].name, 'project-test2') # C |
|
|
|
self.assertFalse(self.builds[1].hasChanges(A)) |
|
self.assertTrue(self.builds[1].hasChanges(B)) |
|
self.assertFalse(self.builds[1].hasChanges(C)) |
|
|
|
self.assertFalse(self.builds[2].hasChanges(A)) |
|
# After A failed and B and C restarted, B should be back in |
|
# C's tests because it has not failed yet. |
|
self.assertTrue(self.builds[2].hasChanges(B)) |
|
self.assertTrue(self.builds[2].hasChanges(C)) |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 0) |
|
self.assertEqual(len(self.history), 21) |
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 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('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(C.data['status'], 'NEW') |
|
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
self.assertEqual(M2.queried, 0) |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.reported, 2) |
|
|
|
def test_needed_changes_enqueue(self): |
|
"Test that a needed change is enqueued ahead" |
|
# A Given a git tree like this, if we enqueue |
|
# / \ change C, we should walk up and down the tree |
|
# B G and enqueue changes in the order ABCDEFG. |
|
# /|\ This is also the order that you would get if |
|
# *C E F you enqueued changes in the order ABCDEFG, so |
|
# / the ordering is stable across re-enqueue events. |
|
# D |
|
|
|
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') |
|
E = self.fake_gerrit.addFakeChange('org/project', 'master', 'E') |
|
F = self.fake_gerrit.addFakeChange('org/project', 'master', 'F') |
|
G = self.fake_gerrit.addFakeChange('org/project', 'master', 'G') |
|
B.setDependsOn(A, 1) |
|
C.setDependsOn(B, 1) |
|
D.setDependsOn(C, 1) |
|
E.setDependsOn(B, 1) |
|
F.setDependsOn(B, 1) |
|
G.setDependsOn(A, 1) |
|
|
|
A.addApproval('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
D.addApproval('Code-Review', 2) |
|
E.addApproval('Code-Review', 2) |
|
F.addApproval('Code-Review', 2) |
|
G.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(C.data['status'], 'NEW') |
|
self.assertEqual(D.data['status'], 'NEW') |
|
self.assertEqual(E.data['status'], 'NEW') |
|
self.assertEqual(F.data['status'], 'NEW') |
|
self.assertEqual(G.data['status'], 'NEW') |
|
|
|
# We're about to add approvals to changes without adding the |
|
# triggering events to Zuul, so that we can be sure that it is |
|
# enqueing the changes based on dependencies, not because of |
|
# triggering events. Since it will have the changes cached |
|
# already (without approvals), we need to clear the cache |
|
# first. |
|
for connection in self.connections.connections.values(): |
|
connection.maintainCache([]) |
|
|
|
self.executor_server.hold_jobs_in_build = True |
|
A.addApproval('Approved', 1) |
|
B.addApproval('Approved', 1) |
|
D.addApproval('Approved', 1) |
|
E.addApproval('Approved', 1) |
|
F.addApproval('Approved', 1) |
|
G.addApproval('Approved', 1) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
|
|
for x in range(8): |
|
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.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(D.data['status'], 'MERGED') |
|
self.assertEqual(E.data['status'], 'MERGED') |
|
self.assertEqual(F.data['status'], 'MERGED') |
|
self.assertEqual(G.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.reported, 2) |
|
self.assertEqual(D.reported, 2) |
|
self.assertEqual(E.reported, 2) |
|
self.assertEqual(F.reported, 2) |
|
self.assertEqual(G.reported, 2) |
|
self.assertEqual(self.history[6].changes, |
|
'1,1 2,1 3,1 4,1 5,1 6,1 7,1') |
|
|
|
def test_source_cache(self): |
|
"Test that the source cache operates correctly" |
|
self.executor_server.hold_jobs_in_build = True |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B') |
|
X = self.fake_gerrit.addFakeChange('org/project', 'master', 'X') |
|
A.addApproval('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
|
|
M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1') |
|
M1.setMerged() |
|
|
|
B.setDependsOn(A, 1) |
|
A.setDependsOn(M1, 1) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(X.getPatchsetCreatedEvent(1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
for build in self.builds: |
|
if build.pipeline == 'check': |
|
build.release() |
|
self.waitUntilSettled() |
|
for build in self.builds: |
|
if build.pipeline == 'check': |
|
build.release() |
|
self.waitUntilSettled() |
|
|
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
|
|
self.log.debug("len %s" % self.fake_gerrit._change_cache.keys()) |
|
# there should still be changes in the cache |
|
self.assertNotEqual(len(self.fake_gerrit._change_cache.keys()), 0) |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(A.queried, 2) # Initial and isMerged |
|
self.assertEqual(B.queried, 3) # Initial A, refresh from B, isMerged |
|
|
|
def test_can_merge(self): |
|
"Test 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') |
|
tenant = self.sched.abide.tenants.get('tenant-one') |
|
(trusted, project) = tenant.getProject('org/project') |
|
source = project.source |
|
|
|
# TODO(pabelanger): As we add more source / trigger APIs we should make |
|
# it easier for users to create events for testing. |
|
event = zuul.model.TriggerEvent() |
|
event.trigger_name = 'gerrit' |
|
event.change_number = '1' |
|
event.patch_number = '2' |
|
|
|
a = source.getChange(event) |
|
mgr = tenant.layout.pipelines['gate'].manager |
|
self.assertFalse(source.canMerge(a, mgr.getSubmitAllowNeeds())) |
|
|
|
A.addApproval('Code-Review', 2) |
|
a = source.getChange(event, refresh=True) |
|
self.assertFalse(source.canMerge(a, mgr.getSubmitAllowNeeds())) |
|
|
|
A.addApproval('Approved', 1) |
|
a = source.getChange(event, refresh=True) |
|
self.assertTrue(source.canMerge(a, mgr.getSubmitAllowNeeds())) |
|
|
|
def test_project_merge_conflict(self): |
|
"Test that gate merge conflicts are handled properly" |
|
|
|
self.gearman_server.hold_jobs_in_queue = True |
|
A = self.fake_gerrit.addFakeChange('org/project', |
|
'master', 'A', |
|
files={'conflict': 'foo'}) |
|
B = self.fake_gerrit.addFakeChange('org/project', |
|
'master', 'B', |
|
files={'conflict': 'bar'}) |
|
C = self.fake_gerrit.addFakeChange('org/project', |
|
'master', 'C') |
|
A.addApproval('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.reported, 1) |
|
self.assertEqual(C.reported, 1) |
|
|
|
self.gearman_server.release('project-merge') |
|
self.waitUntilSettled() |
|
self.gearman_server.release('project-merge') |
|
self.waitUntilSettled() |
|
self.gearman_server.release('project-merge') |
|
self.waitUntilSettled() |
|
|
|
self.gearman_server.hold_jobs_in_queue = False |
|
self.gearman_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertIn('Merge Failed', B.messages[-1]) |
|
self.assertEqual(C.reported, 2) |
|
|
|
self.assertHistory([ |
|
dict(name='project-merge', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='1,1'), |
|
dict(name='project-merge', result='SUCCESS', changes='1,1 3,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='1,1 3,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='1,1 3,1'), |
|
], ordered=False) |
|
|
|
def test_delayed_merge_conflict(self): |
|
"Test that delayed check merge conflicts are handled properly" |
|
|
|
# Hold jobs in the gearman queue so that we can test whether |
|
# the executor sucesfully merges a change based on an old |
|
# repo state (frozen by the scheduler) which would otherwise |
|
# conflict. |
|
self.gearman_server.hold_jobs_in_queue = True |
|
A = self.fake_gerrit.addFakeChange('org/project', |
|
'master', 'A', |
|
files={'conflict': 'foo'}) |
|
B = self.fake_gerrit.addFakeChange('org/project', |
|
'master', 'B', |
|
files={'conflict': 'bar'}) |
|
C = self.fake_gerrit.addFakeChange('org/project', |
|
'master', 'C') |
|
C.setDependsOn(B, 1) |
|
|
|
# A enters the gate queue; B and C enter the check queue |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.reported, 1) |
|
self.assertEqual(B.reported, 0) # Check does not report start |
|
self.assertEqual(C.reported, 0) # Check does not report start |
|
|
|
# A merges while B and C are queued in check |
|
# Release A project-merge |
|
queue = self.gearman_server.getQueue() |
|
self.release(queue[0]) |
|
self.waitUntilSettled() |
|
|
|
# Release A project-test* |
|
# gate has higher precedence, so A's test jobs are added in |
|
# front of the merge jobs for B and C |
|
queue = self.gearman_server.getQueue() |
|
self.release(queue[0]) |
|
self.release(queue[1]) |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(C.data['status'], 'NEW') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 0) |
|
self.assertEqual(C.reported, 0) |
|
self.assertHistory([ |
|
dict(name='project-merge', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='1,1'), |
|
], ordered=False) |
|
|
|
# B and C report merge conflicts |
|
# Release B project-merge |
|
queue = self.gearman_server.getQueue() |
|
self.release(queue[0]) |
|
self.waitUntilSettled() |
|
|
|
# Release C |
|
self.gearman_server.hold_jobs_in_queue = False |
|
self.gearman_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(C.data['status'], 'NEW') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 1) |
|
self.assertEqual(C.reported, 1) |
|
|
|
self.assertHistory([ |
|
dict(name='project-merge', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='1,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='1,1'), |
|
dict(name='project-merge', result='SUCCESS', changes='2,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='2,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='2,1'), |
|
dict(name='project-merge', result='SUCCESS', changes='2,1 3,1'), |
|
dict(name='project-test1', result='SUCCESS', changes='2,1 3,1'), |
|
dict(name='project-test2', result='SUCCESS', changes='2,1 3,1'), |
|
], ordered=False) |
|
|
|
def test_post(self): |
|
"Test that post jobs run" |
|
p = "review.example.com/org/project" |
|
upstream = self.getUpstreamRepos([p]) |
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.setMerged() |
|
A_commit = str(upstream[p].commit('master')) |
|
self.log.debug("A commit: %s" % A_commit) |
|
|
|
e = { |
|
"type": "ref-updated", |
|
"submitter": { |
|
"name": "User Name", |
|
}, |
|
"refUpdate": { |
|
"oldRev": "90f173846e3af9154517b88543ffbd1691f31366", |
|
"newRev": A_commit, |
|
"refName": "master", |
|
"project": "org/project", |
|
} |
|
} |
|
self.fake_gerrit.addEvent(e) |
|
self.waitUntilSettled() |
|
|
|
job_names = [x.name for x in self.history] |
|
self.assertEqual(len(self.history), 1) |
|
self.assertIn('project-post', job_names) |
|
|
|
def test_post_ignore_deletes(self): |
|
"Test that deleting refs does not trigger post jobs" |
|
|
|
e = { |
|
"type": "ref-updated", |
|
"submitter": { |
|
"name": "User Name", |
|
}, |
|
"refUpdate": { |
|
"oldRev": "90f173846e3af9154517b88543ffbd1691f31366", |
|
"newRev": "0000000000000000000000000000000000000000", |
|
"refName": "master", |
|
"project": "org/project", |
|
} |
|
} |
|
self.fake_gerrit.addEvent(e) |
|
self.waitUntilSettled() |
|
|
|
job_names = [x.name for x in self.history] |
|
self.assertEqual(len(self.history), 0) |
|
self.assertNotIn('project-post', job_names) |
|
|
|
@simple_layout('layouts/dont-ignore-ref-deletes.yaml') |
|
def test_post_ignore_deletes_negative(self): |
|
"Test that deleting refs does trigger post jobs" |
|
e = { |
|
"type": "ref-updated", |
|
"submitter": { |
|
"name": "User Name", |
|
}, |
|
"refUpdate": { |
|
"oldRev": "90f173846e3af9154517b88543ffbd1691f31366", |
|
"newRev": "0000000000000000000000000000000000000000", |
|
"refName": "testbranch", |
|
"project": "org/project", |
|
} |
|
} |
|
self.fake_gerrit.addEvent(e) |
|
self.waitUntilSettled() |
|
|
|
job_names = [x.name for x in self.history] |
|
self.assertEqual(len(self.history), 1) |
|
self.assertIn('project-post', job_names) |
|
|
|
@skip("Disabled for early v3 development") |
|
def test_build_configuration_branch_interaction(self): |
|
"Test that switching between branches works" |
|
self.test_build_configuration() |
|
self.test_build_configuration_branch() |
|
# C has been merged, undo that |
|
path = os.path.join(self.upstream_root, "org/project") |
|
repo = git.Repo(path) |
|
repo.heads.master.commit = repo.commit('init') |
|
self.test_build_configuration() |
|
|
|
def test_dependent_changes_rebase(self): |
|
# Test that no errors occur when we walk a dependency tree |
|
# with an unused leaf node due to a rebase. |
|
# Start by constructing: C -> B -> A |
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B') |
|
B.setDependsOn(A, 1) |
|
|
|
C = self.fake_gerrit.addFakeChange('org/project', 'master', 'C') |
|
C.setDependsOn(B, 1) |
|
|
|
# Then rebase to form: D -> C -> A |
|
C.addPatchset() # C,2 |
|
C.setDependsOn(A, 1) |
|
|
|
D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D') |
|
D.setDependsOn(C, 2) |
|
|
|
# Walk the entire tree |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.history), 3) |
|
|
|
# Verify that walking just part of the tree still works |
|
self.fake_gerrit.addEvent(D.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.history), 6) |
|
|
|
def test_dependent_changes_dequeue(self): |
|
"Test that dependent patches are not needlessly tested" |
|
|
|
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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
M1 = self.fake_gerrit.addFakeChange('org/project', 'master', 'M1') |
|
M1.setMerged() |
|
|
|
# C -> B -> A -> M1 |
|
|
|
C.setDependsOn(B, 1) |
|
B.setDependsOn(A, 1) |
|
A.setDependsOn(M1, 1) |
|
|
|
self.executor_server.failJob('project-merge', A) |
|
|
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.data['status'], 'NEW') |
|
self.assertIn('This change depends on a change that failed to merge.', |
|
C.messages[-1]) |
|
self.assertEqual(len(self.history), 1) |
|
|
|
def test_failing_dependent_changes(self): |
|
"Test that failing dependent patches are taken out of stream" |
|
self.executor_server.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') |
|
D = self.fake_gerrit.addFakeChange('org/project', 'master', 'D') |
|
E = self.fake_gerrit.addFakeChange('org/project', 'master', 'E') |
|
A.addApproval('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
D.addApproval('Code-Review', 2) |
|
E.addApproval('Code-Review', 2) |
|
|
|
# E, D -> C -> B, A |
|
|
|
D.setDependsOn(C, 1) |
|
C.setDependsOn(B, 1) |
|
|
|
self.executor_server.failJob('project-test1', B) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(D.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(E.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.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
for build in self.builds: |
|
if build.parameters['zuul']['change'] != '1': |
|
build.release() |
|
self.waitUntilSettled() |
|
|
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertIn('Build succeeded', A.messages[1]) |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(B.reported, 2) |
|
self.assertIn('Build failed', B.messages[1]) |
|
self.assertEqual(C.data['status'], 'NEW') |
|
self.assertEqual(C.reported, 2) |
|
self.assertIn('depends on a change', C.messages[1]) |
|
self.assertEqual(D.data['status'], 'NEW') |
|
self.assertEqual(D.reported, 2) |
|
self.assertIn('depends on a change', D.messages[1]) |
|
self.assertEqual(E.data['status'], 'MERGED') |
|
self.assertEqual(E.reported, 2) |
|
self.assertIn('Build succeeded', E.messages[1]) |
|
self.assertEqual(len(self.history), 18) |
|
|
|
def test_head_is_dequeued_once(self): |
|
"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.executor_server.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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
|
|
self.executor_server.failJob('project-test1', A) |
|
self.executor_server.failJob('project-test2', A) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 1) |
|
self.assertEqual(self.builds[0].name, 'project-merge') |
|
self.assertTrue(self.builds[0].hasChanges(A)) |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 6) |
|
self.assertEqual(self.builds[0].name, 'project-test1') |
|
self.assertEqual(self.builds[1].name, 'project-test2') |
|
self.assertEqual(self.builds[2].name, 'project-test1') |
|
self.assertEqual(self.builds[3].name, 'project-test2') |
|
self.assertEqual(self.builds[4].name, 'project-test1') |
|
self.assertEqual(self.builds[5].name, 'project-test2') |
|
|
|
self.release(self.builds[0]) |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 2) # test2, merge for B |
|
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 4) |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 0) |
|
self.assertEqual(len(self.history), 15) |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.reported, 2) |
|
|
|
@simple_layout('layouts/nonvoting-job.yaml') |
|
def test_nonvoting_job(self): |
|
"Test that non-voting jobs don't vote." |
|
|
|
A = self.fake_gerrit.addFakeChange('org/nonvoting-project', |
|
'master', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.executor_server.failJob('nonvoting-project-test2', A) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual( |
|
self.getJobFromHistory('nonvoting-project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual( |
|
self.getJobFromHistory('nonvoting-project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual( |
|
self.getJobFromHistory('nonvoting-project-test2').result, |
|
'FAILURE') |
|
|
|
self.assertTrue(self.getJobFromHistory('nonvoting-project-merge'). |
|
parameters['zuul']['voting']) |
|
self.assertTrue(self.getJobFromHistory('nonvoting-project-test1'). |
|
parameters['zuul']['voting']) |
|
self.assertFalse(self.getJobFromHistory('nonvoting-project-test2'). |
|
parameters['zuul']['voting']) |
|
|
|
def test_check_queue_success(self): |
|
"Test successful check queue jobs." |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(A.reported, 1) |
|
self.assertEqual(self.getJobFromHistory('project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'SUCCESS') |
|
|
|
def test_check_queue_failure(self): |
|
"Test failed check queue jobs." |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
self.executor_server.failJob('project-test2', A) |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(A.reported, 1) |
|
self.assertEqual(self.getJobFromHistory('project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'FAILURE') |
|
|
|
@simple_layout('layouts/autohold.yaml') |
|
def test_autohold(self): |
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
|
|
client = zuul.rpcclient.RPCClient('127.0.0.1', |
|
self.gearman_server.port) |
|
self.addCleanup(client.shutdown) |
|
r = client.autohold('tenant-one', 'org/project', 'project-test2', |
|
"reason text", 1) |
|
self.assertTrue(r) |
|
|
|
self.executor_server.failJob('project-test2', A) |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) |
|
|
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(A.reported, 1) |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'FAILURE') |
|
|
|
# Check nodepool for a held node |
|
held_node = None |
|
for node in self.fake_nodepool.getNodes(): |
|
if node['state'] == zuul.model.STATE_HOLD: |
|
held_node = node |
|
break |
|
self.assertIsNotNone(held_node) |
|
|
|
# Validate node has recorded the failed job |
|
self.assertEqual( |
|
held_node['hold_job'], |
|
" ".join(['tenant-one', |
|
'review.example.com/org/project', |
|
'project-test2']) |
|
) |
|
self.assertEqual(held_node['comment'], "reason text") |
|
|
|
# Another failed change should not hold any more nodes |
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B') |
|
self.executor_server.failJob('project-test2', B) |
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(B.reported, 1) |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'FAILURE') |
|
|
|
held_nodes = 0 |
|
for node in self.fake_nodepool.getNodes(): |
|
if node['state'] == zuul.model.STATE_HOLD: |
|
held_nodes += 1 |
|
self.assertEqual(held_nodes, 1) |
|
|
|
@simple_layout('layouts/three-projects.yaml') |
|
def test_dependent_behind_dequeue(self): |
|
# This particular test does a large amount of merges and needs a little |
|
# more time to complete |
|
self.wait_timeout = 120 |
|
"test that dependent changes behind dequeued changes work" |
|
# This complicated test is a reproduction of a real life bug |
|
self.sched.reconfigure(self.config) |
|
|
|
self.executor_server.hold_jobs_in_build = True |
|
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A') |
|
B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B') |
|
C = self.fake_gerrit.addFakeChange('org/project2', 'master', 'C') |
|
D = self.fake_gerrit.addFakeChange('org/project2', 'master', 'D') |
|
E = self.fake_gerrit.addFakeChange('org/project2', 'master', 'E') |
|
F = self.fake_gerrit.addFakeChange('org/project3', 'master', 'F') |
|
D.setDependsOn(C, 1) |
|
E.setDependsOn(D, 1) |
|
A.addApproval('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
D.addApproval('Code-Review', 2) |
|
E.addApproval('Code-Review', 2) |
|
F.addApproval('Code-Review', 2) |
|
|
|
A.fail_merge = True |
|
|
|
# Change object re-use in the gerrit trigger is hidden if |
|
# changes are added in quick succession; waiting makes it more |
|
# like real life. |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
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.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.fake_gerrit.addEvent(D.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.fake_gerrit.addEvent(E.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.fake_gerrit.addEvent(F.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.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
# all jobs running |
|
|
|
# Grab pointers to the jobs we want to release before |
|
# releasing any, because list indexes may change as |
|
# the jobs complete. |
|
a, b, c = self.builds[:3] |
|
a.release() |
|
b.release() |
|
c.release() |
|
self.waitUntilSettled() |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'NEW') |
|
self.assertEqual(B.data['status'], 'MERGED') |
|
self.assertEqual(C.data['status'], 'MERGED') |
|
self.assertEqual(D.data['status'], 'MERGED') |
|
self.assertEqual(E.data['status'], 'MERGED') |
|
self.assertEqual(F.data['status'], 'MERGED') |
|
|
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.reported, 2) |
|
self.assertEqual(D.reported, 2) |
|
self.assertEqual(E.reported, 2) |
|
self.assertEqual(F.reported, 2) |
|
|
|
self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 15) |
|
self.assertEqual(len(self.history), 44) |
|
|
|
def test_merger_repack(self): |
|
"Test that the merger works after a repack" |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(self.getJobFromHistory('project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'SUCCESS') |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEmptyQueues() |
|
self.build_history = [] |
|
|
|
path = os.path.join(self.merger_src_root, "review.example.com", |
|
"org/project") |
|
if os.path.exists(path): |
|
repack_repo(path) |
|
path = os.path.join(self.executor_src_root, "review.example.com", |
|
"org/project") |
|
if os.path.exists(path): |
|
repack_repo(path) |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(self.getJobFromHistory('project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'SUCCESS') |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
|
|
def test_merger_repack_large_change(self): |
|
"Test that the merger works with large changes after a repack" |
|
# https://bugs.executepad.net/zuul/+bug/1078946 |
|
# This test assumes the repo is already cloned; make sure it is |
|
tenant = self.sched.abide.tenants.get('tenant-one') |
|
trusted, project = tenant.getProject('org/project') |
|
url = self.fake_gerrit.getGitUrl(project) |
|
self.executor_server.merger._addProject('review.example.com', |
|
'org/project', url, None) |
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.addPatchset(large=True) |
|
# TODOv3(jeblair): add hostname to upstream root |
|
path = os.path.join(self.upstream_root, 'org/project') |
|
repack_repo(path) |
|
path = os.path.join(self.merger_src_root, 'review.example.com', |
|
'org/project') |
|
if os.path.exists(path): |
|
repack_repo(path) |
|
path = os.path.join(self.executor_src_root, 'review.example.com', |
|
'org/project') |
|
if os.path.exists(path): |
|
repack_repo(path) |
|
|
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(self.getJobFromHistory('project-merge').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test1').result, |
|
'SUCCESS') |
|
self.assertEqual(self.getJobFromHistory('project-test2').result, |
|
'SUCCESS') |
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
|
|
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.executor_server.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('Code-Review', 2) |
|
B.addApproval('Code-Review', 2) |
|
C.addApproval('Code-Review', 2) |
|
D.addApproval('Code-Review', 2) |
|
|
|
C.setDependsOn(B, 1) |
|
B.setDependsOn(A, 1) |
|
A.setDependsOn(M, 1) |
|
|
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(C.addApproval('Approved', 1)) |
|
self.fake_gerrit.addEvent(D.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
|
|
B.addPatchset() |
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2)) |
|
self.waitUntilSettled() |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.data['status'], 'MERGED') |
|
self.assertEqual(A.reported, 2) |
|
self.assertEqual(B.data['status'], 'NEW') |
|
self.assertEqual(B.reported, 2) |
|
self.assertEqual(C.data['status'], 'NEW') |
|
self.assertEqual(C.reported, 2) |
|
self.assertEqual(D.data['status'], 'MERGED') |
|
self.assertEqual(D.reported, 2) |
|
self.assertEqual(len(self.history), 9) # 3 each for A, B, D. |
|
|
|
def test_new_patchset_check(self): |
|
"Test a new patchset in check" |
|
|
|
self.executor_server.hold_jobs_in_build = True |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B') |
|
tenant = self.sched.abide.tenants.get('tenant-one') |
|
check_pipeline = tenant.layout.pipelines['check'] |
|
|
|
# Add two git-dependent changes |
|
B.setDependsOn(A, 1) |
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
|
|
# A live item, and a non-live/live pair |
|
items = check_pipeline.getAllItems() |
|
self.assertEqual(len(items), 3) |
|
|
|
self.assertEqual(items[0].change.number, '1') |
|
self.assertEqual(items[0].change.patchset, '1') |
|
self.assertFalse(items[0].live) |
|
|
|
self.assertEqual(items[1].change.number, '2') |
|
self.assertEqual(items[1].change.patchset, '1') |
|
self.assertTrue(items[1].live) |
|
|
|
self.assertEqual(items[2].change.number, '1') |
|
self.assertEqual(items[2].change.patchset, '1') |
|
self.assertTrue(items[2].live) |
|
|
|
# Add a new patchset to A |
|
A.addPatchset() |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2)) |
|
self.waitUntilSettled() |
|
|
|
# The live copy of A,1 should be gone, but the non-live and B |
|
# should continue, and we should have a new A,2 |
|
items = check_pipeline.getAllItems() |
|
self.assertEqual(len(items), 3) |
|
|
|
self.assertEqual(items[0].change.number, '1') |
|
self.assertEqual(items[0].change.patchset, '1') |
|
self.assertFalse(items[0].live) |
|
|
|
self.assertEqual(items[1].change.number, '2') |
|
self.assertEqual(items[1].change.patchset, '1') |
|
self.assertTrue(items[1].live) |
|
|
|
self.assertEqual(items[2].change.number, '1') |
|
self.assertEqual(items[2].change.patchset, '2') |
|
self.assertTrue(items[2].live) |
|
|
|
# Add a new patchset to B |
|
B.addPatchset() |
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2)) |
|
self.waitUntilSettled() |
|
|
|
# The live copy of B,1 should be gone, and it's non-live copy of A,1 |
|
# but we should have a new B,2 (still based on A,1) |
|
items = check_pipeline.getAllItems() |
|
self.assertEqual(len(items), 3) |
|
|
|
self.assertEqual(items[0].change.number, '1') |
|
self.assertEqual(items[0].change.patchset, '2') |
|
self.assertTrue(items[0].live) |
|
|
|
self.assertEqual(items[1].change.number, '1') |
|
self.assertEqual(items[1].change.patchset, '1') |
|
self.assertFalse(items[1].live) |
|
|
|
self.assertEqual(items[2].change.number, '2') |
|
self.assertEqual(items[2].change.patchset, '2') |
|
self.assertTrue(items[2].live) |
|
|
|
self.builds[0].release() |
|
self.waitUntilSettled() |
|
self.builds[0].release() |
|
self.waitUntilSettled() |
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(A.reported, 1) |
|
self.assertEqual(B.reported, 1) |
|
self.assertEqual(self.history[0].result, 'ABORTED') |
|
self.assertEqual(self.history[0].changes, '1,1') |
|
self.assertEqual(self.history[1].result, 'ABORTED') |
|
self.assertEqual(self.history[1].changes, '1,1 2,1') |
|
self.assertEqual(self.history[2].result, 'SUCCESS') |
|
self.assertEqual(self.history[2].changes, '1,2') |
|
self.assertEqual(self.history[3].result, 'SUCCESS') |
|
self.assertEqual(self.history[3].changes, '1,1 2,2') |
|
|
|
def test_abandoned_gate(self): |
|
"Test that an abandoned change is dequeued from gate" |
|
|
|
self.executor_server.hold_jobs_in_build = True |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
A.addApproval('Code-Review', 2) |
|
self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.builds), 1, "One job being built (on hold)") |
|
self.assertEqual(self.builds[0].name, 'project-merge') |
|
|
|
self.fake_gerrit.addEvent(A.getChangeAbandonedEvent()) |
|
self.waitUntilSettled() |
|
|
|
self.executor_server.release('.*-merge') |
|
self.waitUntilSettled() |
|
|
|
self.assertBuilds([]) |
|
self.assertHistory([ |
|
dict(name='project-merge', result='ABORTED', changes='1,1')], |
|
ordered=False) |
|
self.assertEqual(A.reported, 1, |
|
"Abandoned gate change should report only start") |
|
|
|
def test_abandoned_check(self): |
|
"Test that an abandoned change is dequeued from check" |
|
|
|
self.executor_server.hold_jobs_in_build = True |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B') |
|
tenant = self.sched.abide.tenants.get('tenant-one') |
|
check_pipeline = tenant.layout.pipelines['check'] |
|
|
|
# Add two git-dependent changes |
|
B.setDependsOn(A, 1) |
|
self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
# A live item, and a non-live/live pair |
|
items = check_pipeline.getAllItems() |
|
self.assertEqual(len(items), 3) |
|
|
|
self.assertEqual(items[0].change.number, '1') |
|
self.assertFalse(items[0].live) |
|
|
|
self.assertEqual(items[1].change.number, '2') |
|
self.assertTrue(items[1].live) |
|
|
|
self.assertEqual(items[2].change.number, '1') |
|
self.assertTrue(items[2].live) |
|
|
|
# Abandon A |
|
self.fake_gerrit.addEvent(A.getChangeAbandonedEvent()) |
|
self.waitUntilSettled() |
|
|
|
# The live copy of A should be gone, but the non-live and B |
|
# should continue |
|
items = check_pipeline.getAllItems() |
|
self.assertEqual(len(items), 2) |
|
|
|
self.assertEqual(items[0].change.number, '1') |
|
self.assertFalse(items[0].live) |
|
|
|
self.assertEqual(items[1].change.number, '2') |
|
self.assertTrue(items[1].live) |
|
|
|
self.executor_server.hold_jobs_in_build = False |
|
self.executor_server.release() |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.history), 4) |
|
self.assertEqual(self.history[0].result, 'ABORTED', |
|
'Build should have been aborted') |
|
self.assertEqual(A.reported, 0, "Abandoned change should not report") |
|
self.assertEqual(B.reported, 1, "Change should report") |
|
|
|
def test_abandoned_not_timer(self): |
|
"Test that an abandoned change does not cancel timer jobs" |
|
# This test can not use simple_layout because it must start |
|
# with a configuration which does not include a |
|
# timer-triggered job so that we have an opportunity to set |
|
# the hold flag before the first job. |
|
self.executor_server.hold_jobs_in_build = True |
|
# Start timer trigger - also org/project |
|
self.commitConfigUpdate('common-config', 'layouts/idle.yaml') |
|
self.sched.reconfigure(self.config) |
|
# The pipeline triggers every second, so we should have seen |
|
# several by now. |
|
time.sleep(5) |
|
self.waitUntilSettled() |
|
# Stop queuing timer triggered jobs so that the assertions |
|
# below don't race against more jobs being queued. |
|
# Must be in same repo, so overwrite config with another one |
|
self.commitConfigUpdate('common-config', 'layouts/no-timer.yaml') |
|
self.sched.reconfigure(self.config) |
|
self.waitUntilSettled() |
|
# If APScheduler is in mid-event when we remove the job, we |
|
# can end up with one more event firing, so give it an extra |
|
# second to settle. |
|
time.sleep(1) |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 1, "One timer job") |
|
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') |
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) |
|
self.waitUntilSettled() |
|
self.assertEqual(len(self.builds), 2, "One change plus one timer job") |
|
|
|
self.fake_gerrit.addEvent(A.getChangeAbandonedEvent()) |
|
self.waitUntilSettled() |
|
|
|
self.assertEqual(len(self.builds), 1, "One timer job remains") |
|
|
|
self.execut |