Merge branch 'master' into v3_merge
Includes minor py3 fixes (for pep8 on py3).
Conflicts:
tests/base.py
tests/test_model.py
tests/test_scheduler.py
tox.ini
zuul/model.py
zuul/reporter/__init__.py
zuul/scheduler.py
zuul/source/gerrit.py
Change-Id: I99daf9acd746767967b42396881a2dff82134a07
diff --git a/tests/base.py b/tests/base.py
index d888904..a3c9242 100755
--- a/tests/base.py
+++ b/tests/base.py
@@ -22,10 +22,12 @@
import os
import pprint
from six.moves import queue as Queue
+from six.moves import urllib
import random
import re
import select
import shutil
+from six.moves import reload_module
import socket
import string
import subprocess
@@ -33,12 +35,10 @@
import tempfile
import threading
import time
-import urllib2
import git
import gear
import fixtures
-import six.moves.urllib.parse as urlparse
import statsd
import testtools
from git import GitCommandError
@@ -482,7 +482,7 @@
self.url = url
def read(self):
- res = urlparse.urlparse(self.url)
+ res = urllib.parse.urlparse(self.url)
path = res.path
project = '/'.join(path.split('/')[2:-2])
ret = '001e# service=git-upload-pack\n'
@@ -882,6 +882,28 @@
format='%(asctime)s %(name)-32s '
'%(levelname)-8s %(message)s'))
+ # NOTE(notmorgan): Extract logging overrides for specific libraries
+ # from the OS_LOG_DEFAULTS env and create FakeLogger fixtures for
+ # each. This is used to limit the output during test runs from
+ # libraries that zuul depends on such as gear.
+ log_defaults_from_env = os.environ.get('OS_LOG_DEFAULTS')
+
+ if log_defaults_from_env:
+ for default in log_defaults_from_env.split(','):
+ try:
+ name, level_str = default.split('=', 1)
+ level = getattr(logging, level_str, logging.DEBUG)
+ self.useFixture(fixtures.FakeLogger(
+ name=name,
+ level=level,
+ format='%(asctime)s %(name)-32s '
+ '%(levelname)-8s %(message)s'))
+ except ValueError:
+ # NOTE(notmorgan): Invalid format of the log default,
+ # skip and don't try and apply a logger for the
+ # specified module
+ pass
+
class ZuulTestCase(BaseTestCase):
config_file = 'zuul.conf'
@@ -897,11 +919,13 @@
self.test_root = os.path.join(tmp_root, "zuul-test")
self.upstream_root = os.path.join(self.test_root, "upstream")
self.git_root = os.path.join(self.test_root, "git")
+ self.state_root = os.path.join(self.test_root, "lib")
if os.path.exists(self.test_root):
shutil.rmtree(self.test_root)
os.makedirs(self.test_root)
os.makedirs(self.upstream_root)
+ os.makedirs(self.state_root)
# Make per test copy of Configuration.
self.setup_config()
@@ -909,6 +933,7 @@
os.path.join(FIXTURE_DIR,
self.config.get('zuul', 'tenant_config')))
self.config.set('merger', 'git_dir', self.git_root)
+ self.config.set('zuul', 'state_dir', self.state_root)
# For each project in config:
self.init_repo("org/project")
@@ -937,8 +962,8 @@
os.environ['STATSD_PORT'] = str(self.statsd.port)
self.statsd.start()
# the statsd client object is configured in the statsd module import
- reload(statsd)
- reload(zuul.scheduler)
+ reload_module(statsd)
+ reload_module(zuul.scheduler)
self.gearman_server = FakeGearmanServer()
@@ -967,12 +992,12 @@
self.ansible_server.start()
def URLOpenerFactory(*args, **kw):
- if isinstance(args[0], urllib2.Request):
+ if isinstance(args[0], urllib.request.Request):
return old_urlopen(*args, **kw)
return FakeURLOpener(self.upstream_root, *args, **kw)
- old_urlopen = urllib2.urlopen
- urllib2.urlopen = URLOpenerFactory
+ old_urlopen = urllib.request.urlopen
+ urllib.request.urlopen = URLOpenerFactory
self.launcher = zuul.launcher.client.LaunchClient(
self.config, self.sched, self.swift)
@@ -982,7 +1007,8 @@
self.sched.setLauncher(self.launcher)
self.sched.setMerger(self.merge_client)
- self.webapp = zuul.webapp.WebApp(self.sched, port=0)
+ self.webapp = zuul.webapp.WebApp(
+ self.sched, port=0, listen_address='127.0.0.1')
self.rpc = zuul.rpclistener.RPCListener(self.config, self.sched)
self.sched.start()
@@ -1179,6 +1205,17 @@
zuul.merger.merger.reset_repo_to_head(repo)
repo.git.clean('-x', '-f', '-d')
+ def create_commit(self, project):
+ path = os.path.join(self.upstream_root, project)
+ repo = git.Repo(path)
+ repo.head.reference = repo.heads['master']
+ file_name = os.path.join(path, 'README')
+ with open(file_name, 'a') as f:
+ f.write('creating fake commit\n')
+ repo.index.add([file_name])
+ commit = repo.index.commit('Creating a fake commit')
+ return commit.hexsha
+
def ref_has_change(self, ref, change):
path = os.path.join(self.git_root, change.project)
repo = git.Repo(path)
@@ -1325,9 +1362,11 @@
start = time.time()
while True:
if time.time() - start > 10:
- print 'queue status:',
- print ' '.join(self.eventQueuesEmpty())
- print self.areAllBuildsWaiting()
+ self.log.debug("Queue status:")
+ for queue in self.event_queues:
+ self.log.debug(" %s: %s" % (queue, queue.empty()))
+ self.log.debug("All builds waiting: %s" %
+ (self.areAllBuildsWaiting(),))
raise Exception("Timeout waiting for Zuul to settle")
# Make sure no new events show up while we're checking
# have all build states propogated to zuul?
@@ -1369,8 +1408,8 @@
for pipeline in tenant.layout.pipelines.values():
for queue in pipeline.queues:
if len(queue.queue) != 0:
- print 'pipeline %s queue %s contents %s' % (
- pipeline.name, queue.name, queue.queue)
+ print('pipeline %s queue %s contents %s' % (
+ pipeline.name, queue.name, queue.queue))
self.assertEqual(len(queue.queue), 0,
"Pipelines queues should be empty")
diff --git a/tests/fixtures/layout-requirement-username.yaml b/tests/fixtures/layout-requirement-username.yaml
index 7a549f0..f9e6477 100644
--- a/tests/fixtures/layout-requirement-username.yaml
+++ b/tests/fixtures/layout-requirement-username.yaml
@@ -3,7 +3,7 @@
manager: IndependentPipelineManager
require:
approval:
- - username: jenkins
+ - username: ^(jenkins|zuul)$
trigger:
gerrit:
- event: comment-added
diff --git a/tests/fixtures/layout-success-pattern.yaml b/tests/fixtures/layout-success-pattern.yaml
new file mode 100644
index 0000000..cea15f1
--- /dev/null
+++ b/tests/fixtures/layout-success-pattern.yaml
@@ -0,0 +1,21 @@
+pipelines:
+ - name: check
+ manager: IndependentPipelineManager
+ trigger:
+ gerrit:
+ - event: patchset-created
+ success:
+ smtp:
+ to: me@example.org
+
+jobs:
+ - name: docs-draft-test
+ success-pattern: http://docs-draft.example.org/{build.parameters[LOG_PATH]}/publish-docs/
+ - name: docs-draft-test2
+ success-pattern: http://docs-draft.example.org/{NOPE}/{build.parameters[BAD]}/publish-docs/
+
+projects:
+ - name: org/docs
+ check:
+ - docs-draft-test:
+ - docs-draft-test2
diff --git a/tests/test_change_matcher.py b/tests/test_change_matcher.py
index 1f4ab93..0585322 100644
--- a/tests/test_change_matcher.py
+++ b/tests/test_change_matcher.py
@@ -123,13 +123,13 @@
self._test_matches(False)
def test_matches_returns_false_when_not_all_files_match(self):
- self._test_matches(False, files=['docs/foo', 'foo/bar'])
+ self._test_matches(False, files=['/COMMIT_MSG', 'docs/foo', 'foo/bar'])
- def test_matches_returns_true_when_commit_message_matches(self):
- self._test_matches(True, files=['/COMMIT_MSG'])
+ def test_matches_returns_false_when_commit_message_matches(self):
+ self._test_matches(False, files=['/COMMIT_MSG'])
def test_matches_returns_true_when_all_files_match(self):
- self._test_matches(True, files=['docs/foo'])
+ self._test_matches(True, files=['/COMMIT_MSG', 'docs/foo'])
class TestMatchAll(BaseTestMatcher):
diff --git a/tests/test_cloner.py b/tests/test_cloner.py
index 82d1812..67b5303 100644
--- a/tests/test_cloner.py
+++ b/tests/test_cloner.py
@@ -568,3 +568,57 @@
self.worker.hold_jobs_in_build = False
self.worker.release()
self.waitUntilSettled()
+
+ def test_post_checkout(self):
+ project = "org/project"
+ path = os.path.join(self.upstream_root, project)
+ repo = git.Repo(path)
+ repo.head.reference = repo.heads['master']
+ commits = []
+ for i in range(0, 3):
+ commits.append(self.create_commit(project))
+ newRev = commits[1]
+
+ cloner = zuul.lib.cloner.Cloner(
+ git_base_url=self.upstream_root,
+ projects=[project],
+ workspace=self.workspace_root,
+ zuul_branch=None,
+ zuul_ref='master',
+ zuul_url=self.git_root,
+ zuul_project=project,
+ zuul_newrev=newRev,
+ )
+ cloner.execute()
+ repos = self.getWorkspaceRepos([project])
+ cloned_sha = repos[project].rev_parse('HEAD').hexsha
+ self.assertEqual(newRev, cloned_sha)
+
+ def test_post_and_master_checkout(self):
+ project = "org/project1"
+ master_project = "org/project2"
+ path = os.path.join(self.upstream_root, project)
+ repo = git.Repo(path)
+ repo.head.reference = repo.heads['master']
+ commits = []
+ for i in range(0, 3):
+ commits.append(self.create_commit(project))
+ newRev = commits[1]
+
+ cloner = zuul.lib.cloner.Cloner(
+ git_base_url=self.upstream_root,
+ projects=[project, master_project],
+ workspace=self.workspace_root,
+ zuul_branch=None,
+ zuul_ref='master',
+ zuul_url=self.git_root,
+ zuul_project=project,
+ zuul_newrev=newRev
+ )
+ cloner.execute()
+ repos = self.getWorkspaceRepos([project, master_project])
+ cloned_sha = repos[project].rev_parse('HEAD').hexsha
+ self.assertEqual(newRev, cloned_sha)
+ self.assertEqual(
+ repos[master_project].rev_parse('HEAD').hexsha,
+ repos[master_project].rev_parse('master').hexsha)
diff --git a/tests/test_layoutvalidator.py b/tests/test_layoutvalidator.py
index bd507d1..38c8e29 100644
--- a/tests/test_layoutvalidator.py
+++ b/tests/test_layoutvalidator.py
@@ -14,7 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-import ConfigParser
+from six.moves import configparser as ConfigParser
import os
import re
@@ -36,13 +36,13 @@
def test_layouts(self):
"""Test layout file validation"""
- print
+ print()
errors = []
for fn in os.listdir(os.path.join(FIXTURE_DIR, 'layouts')):
m = LAYOUT_RE.match(fn)
if not m:
continue
- print fn
+ print(fn)
# Load any .conf file by the same name but .conf extension.
config_file = ("%s.conf" %
@@ -72,7 +72,7 @@
fn)
except voluptuous.Invalid as e:
error = str(e)
- print ' ', error
+ print(' ', error)
if error in errors:
raise Exception("Error has already been tested: %s" %
error)
diff --git a/tests/test_model.py b/tests/test_model.py
index f8f74dc..145c119 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -12,6 +12,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+
+import os
+import random
+
+import fixtures
+
from zuul import model
from zuul import configloader
@@ -32,12 +38,12 @@
def test_change_matches_returns_false_for_matched_skip_if(self):
change = model.Change('project')
- change.files = ['docs/foo']
+ change.files = ['/COMMIT_MSG', 'docs/foo']
self.assertFalse(self.job.changeMatches(change))
def test_change_matches_returns_true_for_unmatched_skip_if(self):
change = model.Change('project')
- change.files = ['foo']
+ change.files = ['/COMMIT_MSG', 'foo']
self.assertTrue(self.job.changeMatches(change))
def test_job_sets_defaults_for_boolean_attributes(self):
@@ -98,3 +104,76 @@
job = item.getJobs()[0]
self.assertEqual(job.name, 'python27')
self.assertEqual(job.timeout, 50)
+
+
+class TestJobTimeData(BaseTestCase):
+ def setUp(self):
+ super(TestJobTimeData, self).setUp()
+ self.tmp_root = self.useFixture(fixtures.TempDir(
+ rootdir=os.environ.get("ZUUL_TEST_ROOT"))
+ ).path
+
+ def test_empty_timedata(self):
+ path = os.path.join(self.tmp_root, 'job-name')
+ self.assertFalse(os.path.exists(path))
+ self.assertFalse(os.path.exists(path + '.tmp'))
+ td = model.JobTimeData(path)
+ self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+ self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+ self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+
+ def test_save_reload(self):
+ path = os.path.join(self.tmp_root, 'job-name')
+ self.assertFalse(os.path.exists(path))
+ self.assertFalse(os.path.exists(path + '.tmp'))
+ td = model.JobTimeData(path)
+ self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+ self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+ self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+ success_times = []
+ failure_times = []
+ results = []
+ for x in range(10):
+ success_times.append(int(random.random() * 1000))
+ failure_times.append(int(random.random() * 1000))
+ results.append(0)
+ results.append(1)
+ random.shuffle(results)
+ s = f = 0
+ for result in results:
+ if result:
+ td.add(failure_times[f], 'FAILURE')
+ f += 1
+ else:
+ td.add(success_times[s], 'SUCCESS')
+ s += 1
+ self.assertEqual(td.success_times, success_times)
+ self.assertEqual(td.failure_times, failure_times)
+ self.assertEqual(td.results, results[10:])
+ td.save()
+ self.assertTrue(os.path.exists(path))
+ self.assertFalse(os.path.exists(path + '.tmp'))
+ td = model.JobTimeData(path)
+ td.load()
+ self.assertEqual(td.success_times, success_times)
+ self.assertEqual(td.failure_times, failure_times)
+ self.assertEqual(td.results, results[10:])
+
+
+class TestTimeDataBase(BaseTestCase):
+ def setUp(self):
+ super(TestTimeDataBase, self).setUp()
+ self.tmp_root = self.useFixture(fixtures.TempDir(
+ rootdir=os.environ.get("ZUUL_TEST_ROOT"))
+ ).path
+ self.db = model.TimeDataBase(self.tmp_root)
+
+ def test_timedatabase(self):
+ self.assertEqual(self.db.getEstimatedTime('job-name'), 0)
+ self.db.update('job-name', 50, 'SUCCESS')
+ self.assertEqual(self.db.getEstimatedTime('job-name'), 50)
+ self.db.update('job-name', 100, 'SUCCESS')
+ self.assertEqual(self.db.getEstimatedTime('job-name'), 75)
+ for x in range(10):
+ self.db.update('job-name', 100, 'SUCCESS')
+ self.assertEqual(self.db.getEstimatedTime('job-name'), 100)
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 7ae7de5..df6bc1b 100755
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -20,11 +20,10 @@
import re
import shutil
import time
-import urllib
-import urllib2
import yaml
import git
+from six.moves import urllib
import testtools
import zuul.change_matcher
@@ -501,6 +500,46 @@
self.assertEqual(B.reported, 2)
self.assertEqual(C.reported, 2)
+ def _test_time_database(self, iteration):
+ self.worker.hold_jobs_in_build = True
+ A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+ A.addApproval('CRVW', 2)
+ self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+ self.waitUntilSettled()
+ time.sleep(2)
+
+ data = json.loads(self.sched.formatStatusJSON())
+ 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.worker.hold_jobs_in_build = False
+ self.worker.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"
@@ -606,6 +645,36 @@
self.assertEqual(B.reported, 2)
self.assertEqual(C.reported, 2)
+ def test_parse_skip_if(self):
+ job_yaml = """
+jobs:
+ - name: job_name
+ skip-if:
+ - project: ^project_name$
+ branch: ^stable/icehouse$
+ all-files-match-any:
+ - ^filename$
+ - project: ^project2_name$
+ all-files-match-any:
+ - ^filename2$
+ """.strip()
+ data = yaml.load(job_yaml)
+ config_job = data.get('jobs')[0]
+ cm = zuul.change_matcher
+ expected = cm.MatchAny([
+ cm.MatchAll([
+ cm.ProjectMatcher('^project_name$'),
+ cm.BranchMatcher('^stable/icehouse$'),
+ cm.MatchAllFiles([cm.FileMatcher('^filename$')]),
+ ]),
+ cm.MatchAll([
+ cm.ProjectMatcher('^project2_name$'),
+ cm.MatchAllFiles([cm.FileMatcher('^filename2$')]),
+ ]),
+ ])
+ matcher = self.sched._parseSkipIf(config_job)
+ self.assertEqual(expected, matcher)
+
def test_patch_order(self):
"Test that dependent patches are tested in the right order"
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
@@ -1455,7 +1524,7 @@
self.worker.build_history = []
path = os.path.join(self.git_root, "org/project")
- print repack_repo(path)
+ print(repack_repo(path))
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
A.addApproval('CRVW', 2)
@@ -1480,9 +1549,9 @@
A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A')
A.addPatchset(large=True)
path = os.path.join(self.upstream_root, "org/project1")
- print repack_repo(path)
+ print(repack_repo(path))
path = os.path.join(self.git_root, "org/project1")
- print repack_repo(path)
+ print(repack_repo(path))
A.addApproval('CRVW', 2)
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
@@ -2241,15 +2310,18 @@
self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
self.waitUntilSettled()
+ self.worker.release('project-merge')
+ self.waitUntilSettled()
+
port = self.webapp.server.socket.getsockname()[1]
- req = urllib2.Request("http://localhost:%s/status.json" % port)
- f = urllib2.urlopen(req)
+ req = urllib.request.Request("http://localhost:%s/status.json" % port)
+ f = urllib.request.urlopen(req)
headers = f.info()
self.assertIn('Content-Length', headers)
self.assertIn('Content-Type', headers)
- self.assertEqual(headers['Content-Type'],
- 'application/json; charset=UTF-8')
+ self.assertIsNotNone(re.match('^application/json(; charset=UTF-8)?$',
+ headers['Content-Type']))
self.assertIn('Access-Control-Allow-Origin', headers)
self.assertIn('Cache-Control', headers)
self.assertIn('Last-Modified', headers)
@@ -2261,7 +2333,7 @@
self.waitUntilSettled()
data = json.loads(data)
- status_jobs = set()
+ status_jobs = []
for p in data['pipelines']:
for q in p['change_queues']:
if p['name'] in ['gate', 'conflict']:
@@ -2273,10 +2345,24 @@
self.assertTrue(change['active'])
self.assertEqual(change['id'], '1,1')
for job in change['jobs']:
- status_jobs.add(job['name'])
- self.assertIn('project-merge', status_jobs)
- self.assertIn('project-test1', status_jobs)
- self.assertIn('project-test2', status_jobs)
+ status_jobs.append(job)
+ self.assertEqual('project-merge', status_jobs[0]['name'])
+ self.assertEqual('https://server/job/project-merge/0/',
+ status_jobs[0]['url'])
+ self.assertEqual('http://logs.example.com/1/1/gate/project-merge/0',
+ status_jobs[0]['report_url'])
+
+ self.assertEqual('project-test1', status_jobs[1]['name'])
+ self.assertEqual('https://server/job/project-test1/1/',
+ status_jobs[1]['url'])
+ self.assertEqual('http://logs.example.com/1/1/gate/project-test1/1',
+ status_jobs[1]['report_url'])
+
+ self.assertEqual('project-test2', status_jobs[2]['name'])
+ self.assertEqual('https://server/job/project-test2/2/',
+ status_jobs[2]['url'])
+ self.assertEqual('http://logs.example.com/1/1/gate/project-test2/2',
+ status_jobs[2]['report_url'])
def test_merging_queues(self):
"Test that transitively-connected change queues are merged"
@@ -2829,7 +2915,8 @@
port = self.webapp.server.socket.getsockname()[1]
- f = urllib.urlopen("http://localhost:%s/status.json" % port)
+ req = urllib.request.Request("http://localhost:%s/status.json" % port)
+ f = urllib.request.urlopen(req)
data = f.read()
self.worker.hold_jobs_in_build = False
@@ -4215,6 +4302,45 @@
self.waitUntilSettled()
self.assertEqual(self.history[-1].changes, '3,2 2,1 1,2')
+ def test_crd_cycle_join(self):
+ "Test an updated change creates a cycle"
+ A = self.fake_gerrit.addFakeChange('org/project2', 'master', 'A')
+
+ self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+ self.waitUntilSettled()
+
+ # Create B->A
+ B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
+ B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
+ B.subject, A.data['id'])
+ self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
+ self.waitUntilSettled()
+
+ # Update A to add A->B (a cycle).
+ A.addPatchset()
+ A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
+ A.subject, B.data['id'])
+ # Normally we would submit the patchset-created event for
+ # processing here, however, we have no way of noting whether
+ # the dependency cycle detection correctly raised an
+ # exception, so instead, we reach into the source driver and
+ # call the method that would ultimately be called by the event
+ # processing.
+
+ source = self.sched.layout.pipelines['gate'].source
+ with testtools.ExpectedException(
+ Exception, "Dependency cycle detected"):
+ source._getChange(u'1', u'2', True)
+ self.log.debug("Got expected dependency cycle exception")
+
+ # 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,)
+ source._getChange(u'1', u'2', True)
+ source._getChange(u'2', u'2', True)
+
def test_disable_at(self):
"Test a pipeline will only report to the disabled trigger when failing"
@@ -4336,3 +4462,38 @@
self.assertIn('Build failed.', K.messages[0])
# No more messages reported via smtp
self.assertEqual(3, len(self.smtp_messages))
+
+ def test_success_pattern(self):
+ "Ensure bad build params are ignored"
+
+ # Use SMTP reporter to grab the result message easier
+ self.init_repo("org/docs")
+ self.config.set('zuul', 'layout_config',
+ 'tests/fixtures/layout-success-pattern.yaml')
+ self.sched.reconfigure(self.config)
+ self.worker.hold_jobs_in_build = True
+ self.registerJobs()
+
+ A = self.fake_gerrit.addFakeChange('org/docs', 'master', 'A')
+ self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+ self.waitUntilSettled()
+
+ # Grab build id
+ self.assertEqual(len(self.builds), 1)
+ uuid = self.builds[0].unique[:7]
+
+ self.worker.hold_jobs_in_build = False
+ self.worker.release()
+ self.waitUntilSettled()
+
+ self.assertEqual(len(self.smtp_messages), 1)
+ body = self.smtp_messages[0]['body'].splitlines()
+ self.assertEqual('Build succeeded.', body[0])
+
+ self.assertIn(
+ '- docs-draft-test http://docs-draft.example.org/1/1/1/check/'
+ 'docs-draft-test/%s/publish-docs/' % uuid,
+ body[2])
+ self.assertIn(
+ '- docs-draft-test2 https://server/job/docs-draft-test2/1/',
+ body[3])
diff --git a/tests/test_webapp.py b/tests/test_webapp.py
index bc5961f..555c08f 100644
--- a/tests/test_webapp.py
+++ b/tests/test_webapp.py
@@ -16,7 +16,8 @@
# under the License.
import json
-import urllib2
+
+from six.moves import urllib
from tests.base import ZuulTestCase
@@ -46,41 +47,41 @@
def test_webapp_status(self):
"Test that we can filter to only certain changes in the webapp."
- req = urllib2.Request(
+ req = urllib.request.Request(
"http://localhost:%s/status" % self.port)
- f = urllib2.urlopen(req)
+ f = urllib.request.urlopen(req)
data = json.loads(f.read())
self.assertIn('pipelines', data)
def test_webapp_status_compat(self):
# testing compat with status.json
- req = urllib2.Request(
+ req = urllib.request.Request(
"http://localhost:%s/status.json" % self.port)
- f = urllib2.urlopen(req)
+ f = urllib.request.urlopen(req)
data = json.loads(f.read())
self.assertIn('pipelines', data)
def test_webapp_bad_url(self):
# do we 404 correctly
- req = urllib2.Request(
+ req = urllib.request.Request(
"http://localhost:%s/status/foo" % self.port)
- self.assertRaises(urllib2.HTTPError, urllib2.urlopen, req)
+ self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, req)
def test_webapp_find_change(self):
# can we filter by change id
- req = urllib2.Request(
+ req = urllib.request.Request(
"http://localhost:%s/status/change/1,1" % self.port)
- f = urllib2.urlopen(req)
+ f = urllib.request.urlopen(req)
data = json.loads(f.read())
self.assertEqual(1, len(data), data)
self.assertEqual("org/project", data[0]['project'])
- req = urllib2.Request(
+ req = urllib.request.Request(
"http://localhost:%s/status/change/2,1" % self.port)
- f = urllib2.urlopen(req)
+ f = urllib.request.urlopen(req)
data = json.loads(f.read())
self.assertEqual(1, len(data), data)