blob: dcef6666c6517de1452958b23a35cf961e901686 [file] [log] [blame]
Maru Newby3fe5f852015-01-13 04:22:14 +00001# Copyright 2015 Red Hat, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
Joshua Hesketh0aa7e8b2016-07-14 00:12:25 +100015
James E. Blairce8a2132016-05-19 15:21:52 -070016import os
17import random
18
19import fixtures
James E. Blair4317e9f2016-07-15 10:05:47 -070020import testtools
James E. Blairce8a2132016-05-19 15:21:52 -070021
Maru Newby3fe5f852015-01-13 04:22:14 +000022from zuul import model
James E. Blair83005782015-12-11 14:46:03 -080023from zuul import configloader
James E. Blairbf1a4f22017-03-17 10:59:37 -070024from zuul.lib import encryption
Clint Byrum88fdfde2017-03-28 16:22:42 -070025from zuul.lib import yamlutil as yaml
Maru Newby3fe5f852015-01-13 04:22:14 +000026
James E. Blair18f86a32017-03-15 14:43:26 -070027from tests.base import BaseTestCase, FIXTURE_DIR
Maru Newby3fe5f852015-01-13 04:22:14 +000028
29
James E. Blair0a899752017-03-29 13:22:16 -070030class Dummy(object):
31 def __init__(self, **kw):
32 for k, v in kw.items():
33 setattr(self, k, v)
James E. Blairb3f5db12017-03-17 12:57:39 -070034
35
Maru Newby3fe5f852015-01-13 04:22:14 +000036class TestJob(BaseTestCase):
James E. Blaira7f51ca2017-02-07 16:01:26 -080037 def setUp(self):
38 super(TestJob, self).setUp()
James E. Blair0a899752017-03-29 13:22:16 -070039 self.connection = Dummy(connection_name='dummy_connection')
40 self.source = Dummy(canonical_hostname='git.example.com',
James E. Blair0a899752017-03-29 13:22:16 -070041 connection=self.connection)
James E. Blairb3f5db12017-03-17 12:57:39 -070042 self.tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -070043 self.layout = model.Layout(self.tenant)
James E. Blair0a899752017-03-29 13:22:16 -070044 self.project = model.Project('project', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -070045 self.tpc = model.TenantProjectConfig(self.project)
46 self.tenant.addUntrustedProject(self.tpc)
James E. Blairb3f5db12017-03-17 12:57:39 -070047 self.pipeline = model.Pipeline('gate', self.layout)
48 self.layout.addPipeline(self.pipeline)
49 self.queue = model.ChangeQueue(self.pipeline)
James E. Blairf94a6d32018-02-16 12:05:32 -080050 self.pcontext = configloader.ParseContext(
51 None, None, self.tenant, self.layout)
James E. Blairb3f5db12017-03-17 12:57:39 -070052
James E. Blair18f86a32017-03-15 14:43:26 -070053 private_key_file = os.path.join(FIXTURE_DIR, 'private.pem')
54 with open(private_key_file, "rb") as f:
James E. Blairbf1a4f22017-03-17 10:59:37 -070055 self.project.private_key, self.project.public_key = \
56 encryption.deserialize_rsa_keypair(f.read())
James E. Blair6f140c72017-03-03 10:32:07 -080057 self.context = model.SourceContext(self.project, 'master',
58 'test', True)
James E. Blair892cca62017-08-09 11:36:58 -070059 self.untrusted_context = model.SourceContext(self.project, 'master',
60 'test', False)
James E. Blair1cebebf2017-07-14 11:39:03 -070061 m = yaml.Mark('name', 0, 0, 0, '', 0)
62 self.start_mark = configloader.ZuulMark(m, m, '')
James E. Blaira7f51ca2017-02-07 16:01:26 -080063
Maru Newby3fe5f852015-01-13 04:22:14 +000064 @property
65 def job(self):
James E. Blairf94a6d32018-02-16 12:05:32 -080066 job = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -080067 '_source_context': self.context,
68 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -080069 'name': 'job',
James E. Blair2bab6e72017-08-07 09:52:45 -070070 'parent': None,
James E. Blair83005782015-12-11 14:46:03 -080071 'irrelevant-files': [
72 '^docs/.*$'
73 ]})
Maru Newby3fe5f852015-01-13 04:22:14 +000074 return job
75
76 def test_change_matches_returns_false_for_matched_skip_if(self):
77 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030078 change.files = ['/COMMIT_MSG', 'docs/foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000079 self.assertFalse(self.job.changeMatches(change))
80
Jan Hruban570d01c2016-03-10 21:51:32 +010081 def test_change_matches_returns_false_for_single_matched_skip_if(self):
82 change = model.Change('project')
83 change.files = ['docs/foo']
84 self.assertFalse(self.job.changeMatches(change))
85
Maru Newby3fe5f852015-01-13 04:22:14 +000086 def test_change_matches_returns_true_for_unmatched_skip_if(self):
87 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030088 change.files = ['/COMMIT_MSG', 'foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000089 self.assertTrue(self.job.changeMatches(change))
90
Jan Hruban570d01c2016-03-10 21:51:32 +010091 def test_change_matches_returns_true_for_single_unmatched_skip_if(self):
92 change = model.Change('project')
93 change.files = ['foo']
94 self.assertTrue(self.job.changeMatches(change))
95
Maru Newby79427a42015-02-17 17:54:45 +000096 def test_job_sets_defaults_for_boolean_attributes(self):
James E. Blair83005782015-12-11 14:46:03 -080097 self.assertIsNotNone(self.job.voting)
98
James E. Blaira7f51ca2017-02-07 16:01:26 -080099 def test_job_variants(self):
100 # This simulates freezing a job.
101
James E. Blair892cca62017-08-09 11:36:58 -0700102 secrets = ['foo']
103 py27_pre = model.PlaybookContext(self.context, 'py27-pre', [], secrets)
104 py27_run = model.PlaybookContext(self.context, 'py27-run', [], secrets)
105 py27_post = model.PlaybookContext(self.context, 'py27-post', [],
106 secrets)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800107
108 py27 = model.Job('py27')
109 py27.timeout = 30
110 py27.pre_run = [py27_pre]
111 py27.run = [py27_run]
112 py27.post_run = [py27_post]
James E. Blaira7f51ca2017-02-07 16:01:26 -0800113
114 job = py27.copy()
115 self.assertEqual(30, job.timeout)
116
117 # Apply the diablo variant
118 diablo = model.Job('py27')
119 diablo.timeout = 40
120 job.applyVariant(diablo)
121
122 self.assertEqual(40, job.timeout)
123 self.assertEqual(['py27-pre'],
124 [x.path for x in job.pre_run])
125 self.assertEqual(['py27-run'],
126 [x.path for x in job.run])
127 self.assertEqual(['py27-post'],
128 [x.path for x in job.post_run])
James E. Blair892cca62017-08-09 11:36:58 -0700129 self.assertEqual(secrets, job.pre_run[0].secrets)
130 self.assertEqual(secrets, job.run[0].secrets)
131 self.assertEqual(secrets, job.post_run[0].secrets)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800132
133 # Set the job to final for the following checks
134 job.final = True
135 self.assertTrue(job.voting)
136
137 good_final = model.Job('py27')
138 good_final.voting = False
139 job.applyVariant(good_final)
140 self.assertFalse(job.voting)
141
142 bad_final = model.Job('py27')
143 bad_final.timeout = 600
144 with testtools.ExpectedException(
145 Exception,
146 "Unable to modify final job"):
147 job.applyVariant(bad_final)
148
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700149 def test_job_inheritance_job_tree(self):
James E. Blairf94a6d32018-02-16 12:05:32 -0800150 pipeline = model.Pipeline('gate', self.layout)
151 self.layout.addPipeline(pipeline)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700152 queue = model.ChangeQueue(pipeline)
153
James E. Blairf94a6d32018-02-16 12:05:32 -0800154 base = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800155 '_source_context': self.context,
156 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700157 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700158 'parent': None,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700159 'timeout': 30,
160 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800161 self.layout.addJob(base)
162 python27 = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800163 '_source_context': self.context,
164 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700165 'name': 'python27',
166 'parent': 'base',
167 'timeout': 40,
168 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800169 self.layout.addJob(python27)
170 python27diablo = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800171 '_source_context': self.context,
172 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700173 'name': 'python27',
174 'branches': [
175 'stable/diablo'
176 ],
177 'timeout': 50,
178 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800179 self.layout.addJob(python27diablo)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700180
James E. Blairf94a6d32018-02-16 12:05:32 -0800181 project_config = self.pcontext.project_parser.fromYaml([{
James E. Blairec7ff302017-03-04 07:31:32 -0800182 '_source_context': self.context,
183 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700184 'name': 'project',
185 'gate': {
186 'jobs': [
James E. Blair00292672017-10-26 15:29:39 -0700187 {'python27': {'timeout': 70,
188 'run': 'playbooks/python27.yaml'}}
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700189 ]
190 }
James E. Blairff555742017-02-19 11:34:27 -0800191 }])
James E. Blairf94a6d32018-02-16 12:05:32 -0800192 self.layout.addProjectConfig(project_config)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700193
James E. Blairec7ff302017-03-04 07:31:32 -0800194 change = model.Change(self.project)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700195 change.branch = 'master'
196 item = queue.enqueueChange(change)
James E. Blairf94a6d32018-02-16 12:05:32 -0800197 item.layout = self.layout
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700198
199 self.assertTrue(base.changeMatches(change))
200 self.assertTrue(python27.changeMatches(change))
201 self.assertFalse(python27diablo.changeMatches(change))
202
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200203 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700204 self.assertEqual(len(item.getJobs()), 1)
205 job = item.getJobs()[0]
206 self.assertEqual(job.name, 'python27')
207 self.assertEqual(job.timeout, 70)
208
209 change.branch = 'stable/diablo'
210 item = queue.enqueueChange(change)
James E. Blairf94a6d32018-02-16 12:05:32 -0800211 item.layout = self.layout
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700212
213 self.assertTrue(base.changeMatches(change))
214 self.assertTrue(python27.changeMatches(change))
215 self.assertTrue(python27diablo.changeMatches(change))
216
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200217 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700218 self.assertEqual(len(item.getJobs()), 1)
219 job = item.getJobs()[0]
220 self.assertEqual(job.name, 'python27')
221 self.assertEqual(job.timeout, 70)
222
Clint Byrum85493602016-11-18 11:59:47 -0800223 def test_inheritance_keeps_matchers(self):
James E. Blairf94a6d32018-02-16 12:05:32 -0800224 pipeline = model.Pipeline('gate', self.layout)
225 self.layout.addPipeline(pipeline)
Clint Byrum85493602016-11-18 11:59:47 -0800226 queue = model.ChangeQueue(pipeline)
Clint Byrum85493602016-11-18 11:59:47 -0800227
James E. Blairf94a6d32018-02-16 12:05:32 -0800228 base = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800229 '_source_context': self.context,
230 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800231 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700232 'parent': None,
Clint Byrum85493602016-11-18 11:59:47 -0800233 'timeout': 30,
234 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800235 self.layout.addJob(base)
236 python27 = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800237 '_source_context': self.context,
238 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800239 'name': 'python27',
240 'parent': 'base',
241 'timeout': 40,
242 'irrelevant-files': ['^ignored-file$'],
243 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800244 self.layout.addJob(python27)
Clint Byrum85493602016-11-18 11:59:47 -0800245
James E. Blairf94a6d32018-02-16 12:05:32 -0800246 project_config = self.pcontext.project_parser.fromYaml([{
James E. Blairec7ff302017-03-04 07:31:32 -0800247 '_source_context': self.context,
248 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800249 'name': 'project',
250 'gate': {
251 'jobs': [
252 'python27',
253 ]
254 }
James E. Blairff555742017-02-19 11:34:27 -0800255 }])
James E. Blairf94a6d32018-02-16 12:05:32 -0800256 self.layout.addProjectConfig(project_config)
Clint Byrum85493602016-11-18 11:59:47 -0800257
James E. Blairf94a6d32018-02-16 12:05:32 -0800258 change = model.Change(self.project)
Clint Byrum85493602016-11-18 11:59:47 -0800259 change.branch = 'master'
260 change.files = ['/COMMIT_MSG', 'ignored-file']
261 item = queue.enqueueChange(change)
James E. Blairf94a6d32018-02-16 12:05:32 -0800262 item.layout = self.layout
Clint Byrum85493602016-11-18 11:59:47 -0800263
264 self.assertTrue(base.changeMatches(change))
265 self.assertFalse(python27.changeMatches(change))
266
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200267 item.freezeJobGraph()
Clint Byrum85493602016-11-18 11:59:47 -0800268 self.assertEqual([], item.getJobs())
269
James E. Blair4317e9f2016-07-15 10:05:47 -0700270 def test_job_source_project(self):
James E. Blair0a899752017-03-29 13:22:16 -0700271 base_project = model.Project('base_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800272 base_context = model.SourceContext(base_project, 'master',
273 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700274 tpc = model.TenantProjectConfig(base_project)
James E. Blairf94a6d32018-02-16 12:05:32 -0800275 self.tenant.addUntrustedProject(tpc)
James E. Blaircdab2032017-02-01 09:09:29 -0800276
James E. Blairf94a6d32018-02-16 12:05:32 -0800277 base = self.pcontext.job_parser.fromYaml({
James E. Blaircdab2032017-02-01 09:09:29 -0800278 '_source_context': base_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800279 '_start_mark': self.start_mark,
James E. Blair2bab6e72017-08-07 09:52:45 -0700280 'parent': None,
James E. Blair4317e9f2016-07-15 10:05:47 -0700281 'name': 'base',
282 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800283 self.layout.addJob(base)
James E. Blair4317e9f2016-07-15 10:05:47 -0700284
James E. Blair0a899752017-03-29 13:22:16 -0700285 other_project = model.Project('other_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800286 other_context = model.SourceContext(other_project, 'master',
287 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700288 tpc = model.TenantProjectConfig(other_project)
James E. Blairf94a6d32018-02-16 12:05:32 -0800289 self.tenant.addUntrustedProject(tpc)
290 base2 = self.pcontext.job_parser.fromYaml({
James E. Blaircdab2032017-02-01 09:09:29 -0800291 '_source_context': other_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800292 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700293 'name': 'base',
294 })
295 with testtools.ExpectedException(
296 Exception,
297 "Job base in other_project is not permitted "
298 "to shadow job base in base_project"):
James E. Blairf94a6d32018-02-16 12:05:32 -0800299 self.layout.addJob(base2)
James E. Blair4317e9f2016-07-15 10:05:47 -0700300
James E. Blair8eb564a2017-08-10 09:21:41 -0700301 def test_job_pipeline_allow_untrusted_secrets(self):
302 self.pipeline.post_review = False
James E. Blairf94a6d32018-02-16 12:05:32 -0800303 job = self.pcontext.job_parser.fromYaml({
James E. Blaird2348362017-03-17 13:59:35 -0700304 '_source_context': self.context,
305 '_start_mark': self.start_mark,
306 'name': 'job',
James E. Blair2bab6e72017-08-07 09:52:45 -0700307 'parent': None,
James E. Blaird2348362017-03-17 13:59:35 -0700308 })
James E. Blair8eb564a2017-08-10 09:21:41 -0700309 job.post_review = True
James E. Blaird2348362017-03-17 13:59:35 -0700310
311 self.layout.addJob(job)
312
James E. Blairf94a6d32018-02-16 12:05:32 -0800313 project_config = self.pcontext.project_parser.fromYaml(
James E. Blairaf8b2082017-10-03 15:38:27 -0700314 [{
James E. Blaird2348362017-03-17 13:59:35 -0700315 '_source_context': self.context,
316 '_start_mark': self.start_mark,
317 'name': 'project',
318 'gate': {
319 'jobs': [
320 'job'
321 ]
322 }
323 }]
324 )
325 self.layout.addProjectConfig(project_config)
326
327 change = model.Change(self.project)
328 # Test master
329 change.branch = 'master'
330 item = self.queue.enqueueChange(change)
James E. Blair29a24fd2017-10-02 15:04:56 -0700331 item.layout = self.layout
James E. Blaird2348362017-03-17 13:59:35 -0700332 with testtools.ExpectedException(
333 Exception,
James E. Blair8eb564a2017-08-10 09:21:41 -0700334 "Pre-review pipeline gate does not allow post-review job"):
James E. Blaird2348362017-03-17 13:59:35 -0700335 item.freezeJobGraph()
336
James E. Blairce8a2132016-05-19 15:21:52 -0700337
338class TestJobTimeData(BaseTestCase):
339 def setUp(self):
340 super(TestJobTimeData, self).setUp()
341 self.tmp_root = self.useFixture(fixtures.TempDir(
342 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
343 ).path
344
345 def test_empty_timedata(self):
346 path = os.path.join(self.tmp_root, 'job-name')
347 self.assertFalse(os.path.exists(path))
348 self.assertFalse(os.path.exists(path + '.tmp'))
349 td = model.JobTimeData(path)
350 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
351 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
352 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
353
354 def test_save_reload(self):
355 path = os.path.join(self.tmp_root, 'job-name')
356 self.assertFalse(os.path.exists(path))
357 self.assertFalse(os.path.exists(path + '.tmp'))
358 td = model.JobTimeData(path)
359 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
360 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
361 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
362 success_times = []
363 failure_times = []
364 results = []
365 for x in range(10):
366 success_times.append(int(random.random() * 1000))
367 failure_times.append(int(random.random() * 1000))
368 results.append(0)
369 results.append(1)
370 random.shuffle(results)
371 s = f = 0
372 for result in results:
373 if result:
374 td.add(failure_times[f], 'FAILURE')
375 f += 1
376 else:
377 td.add(success_times[s], 'SUCCESS')
378 s += 1
379 self.assertEqual(td.success_times, success_times)
380 self.assertEqual(td.failure_times, failure_times)
381 self.assertEqual(td.results, results[10:])
382 td.save()
383 self.assertTrue(os.path.exists(path))
384 self.assertFalse(os.path.exists(path + '.tmp'))
385 td = model.JobTimeData(path)
386 td.load()
387 self.assertEqual(td.success_times, success_times)
388 self.assertEqual(td.failure_times, failure_times)
389 self.assertEqual(td.results, results[10:])
390
391
392class TestTimeDataBase(BaseTestCase):
393 def setUp(self):
394 super(TestTimeDataBase, self).setUp()
395 self.tmp_root = self.useFixture(fixtures.TempDir(
396 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
397 ).path
398 self.db = model.TimeDataBase(self.tmp_root)
399
400 def test_timedatabase(self):
James E. Blairae0f23c2017-09-13 10:55:15 -0600401 pipeline = Dummy(layout=Dummy(tenant=Dummy(name='test-tenant')))
402 change = Dummy(project=Dummy(canonical_name='git.example.com/foo/bar'))
403 job = Dummy(name='job-name')
404 item = Dummy(pipeline=pipeline,
405 change=change)
406 build = Dummy(build_set=Dummy(item=item),
407 job=job)
408
409 self.assertEqual(self.db.getEstimatedTime(build), 0)
410 self.db.update(build, 50, 'SUCCESS')
411 self.assertEqual(self.db.getEstimatedTime(build), 50)
412 self.db.update(build, 100, 'SUCCESS')
413 self.assertEqual(self.db.getEstimatedTime(build), 75)
James E. Blairce8a2132016-05-19 15:21:52 -0700414 for x in range(10):
James E. Blairae0f23c2017-09-13 10:55:15 -0600415 self.db.update(build, 100, 'SUCCESS')
416 self.assertEqual(self.db.getEstimatedTime(build), 100)
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200417
418
419class TestGraph(BaseTestCase):
420 def test_job_graph_disallows_multiple_jobs_with_same_name(self):
421 graph = model.JobGraph()
422 job1 = model.Job('job')
423 job2 = model.Job('job')
424 graph.addJob(job1)
425 with testtools.ExpectedException(Exception,
426 "Job job already added"):
427 graph.addJob(job2)
428
429 def test_job_graph_disallows_circular_dependencies(self):
430 graph = model.JobGraph()
431 jobs = [model.Job('job%d' % i) for i in range(0, 10)]
432 prevjob = None
433 for j in jobs[:3]:
434 if prevjob:
435 j.dependencies = frozenset([prevjob.name])
436 graph.addJob(j)
437 prevjob = j
438 # 0 triggers 1 triggers 2 triggers 3...
439
440 # Cannot depend on itself
441 with testtools.ExpectedException(
442 Exception,
443 "Dependency cycle detected in job jobX"):
444 j = model.Job('jobX')
445 j.dependencies = frozenset([j.name])
446 graph.addJob(j)
447
448 # Disallow circular dependencies
449 with testtools.ExpectedException(
450 Exception,
451 "Dependency cycle detected in job job3"):
452 jobs[4].dependencies = frozenset([jobs[3].name])
453 graph.addJob(jobs[4])
454 jobs[3].dependencies = frozenset([jobs[4].name])
455 graph.addJob(jobs[3])
456
457 jobs[5].dependencies = frozenset([jobs[4].name])
458 graph.addJob(jobs[5])
459
460 with testtools.ExpectedException(
461 Exception,
462 "Dependency cycle detected in job job3"):
463 jobs[3].dependencies = frozenset([jobs[5].name])
464 graph.addJob(jobs[3])
465
466 jobs[3].dependencies = frozenset([jobs[2].name])
467 graph.addJob(jobs[3])
468 jobs[6].dependencies = frozenset([jobs[2].name])
469 graph.addJob(jobs[6])
James E. Blairc2a54fd2017-03-29 15:19:26 -0700470
471
472class TestTenant(BaseTestCase):
473 def test_add_project(self):
474 tenant = model.Tenant('tenant')
475 connection1 = Dummy(connection_name='dummy_connection1')
476 source1 = Dummy(canonical_hostname='git1.example.com',
477 name='dummy', # TODOv3(jeblair): remove
478 connection=connection1)
479
480 source1_project1 = model.Project('project1', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700481 source1_project1_tpc = model.TenantProjectConfig(source1_project1)
482 tenant.addConfigProject(source1_project1_tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700483 d = {'project1':
484 {'git1.example.com': source1_project1}}
485 self.assertEqual(d, tenant.projects)
486 self.assertEqual((True, source1_project1),
487 tenant.getProject('project1'))
488 self.assertEqual((True, source1_project1),
489 tenant.getProject('git1.example.com/project1'))
490
491 source1_project2 = model.Project('project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700492 tpc = model.TenantProjectConfig(source1_project2)
493 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700494 d = {'project1':
495 {'git1.example.com': source1_project1},
496 'project2':
497 {'git1.example.com': source1_project2}}
498 self.assertEqual(d, tenant.projects)
499 self.assertEqual((False, source1_project2),
500 tenant.getProject('project2'))
501 self.assertEqual((False, source1_project2),
502 tenant.getProject('git1.example.com/project2'))
503
504 connection2 = Dummy(connection_name='dummy_connection2')
505 source2 = Dummy(canonical_hostname='git2.example.com',
506 name='dummy', # TODOv3(jeblair): remove
507 connection=connection2)
508
509 source2_project1 = model.Project('project1', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700510 tpc = model.TenantProjectConfig(source2_project1)
511 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700512 d = {'project1':
513 {'git1.example.com': source1_project1,
514 'git2.example.com': source2_project1},
515 'project2':
516 {'git1.example.com': source1_project2}}
517 self.assertEqual(d, tenant.projects)
518 with testtools.ExpectedException(
519 Exception,
520 "Project name 'project1' is ambiguous"):
521 tenant.getProject('project1')
522 self.assertEqual((False, source1_project2),
523 tenant.getProject('project2'))
524 self.assertEqual((True, source1_project1),
525 tenant.getProject('git1.example.com/project1'))
526 self.assertEqual((False, source2_project1),
527 tenant.getProject('git2.example.com/project1'))
528
529 source2_project2 = model.Project('project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700530 tpc = model.TenantProjectConfig(source2_project2)
531 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700532 d = {'project1':
533 {'git1.example.com': source1_project1,
534 'git2.example.com': source2_project1},
535 'project2':
536 {'git1.example.com': source1_project2,
537 'git2.example.com': source2_project2}}
538 self.assertEqual(d, tenant.projects)
539 with testtools.ExpectedException(
540 Exception,
541 "Project name 'project1' is ambiguous"):
542 tenant.getProject('project1')
543 with testtools.ExpectedException(
544 Exception,
545 "Project name 'project2' is ambiguous"):
546 tenant.getProject('project2')
547 self.assertEqual((True, source1_project1),
548 tenant.getProject('git1.example.com/project1'))
549 self.assertEqual((False, source2_project1),
550 tenant.getProject('git2.example.com/project1'))
551 self.assertEqual((False, source1_project2),
552 tenant.getProject('git1.example.com/project2'))
553 self.assertEqual((True, source2_project2),
554 tenant.getProject('git2.example.com/project2'))
555
556 source1_project2b = model.Project('subpath/project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700557 tpc = model.TenantProjectConfig(source1_project2b)
558 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700559 d = {'project1':
560 {'git1.example.com': source1_project1,
561 'git2.example.com': source2_project1},
562 'project2':
563 {'git1.example.com': source1_project2,
564 'git2.example.com': source2_project2},
565 'subpath/project2':
566 {'git1.example.com': source1_project2b}}
567 self.assertEqual(d, tenant.projects)
568 self.assertEqual((False, source1_project2),
569 tenant.getProject('git1.example.com/project2'))
570 self.assertEqual((True, source2_project2),
571 tenant.getProject('git2.example.com/project2'))
572 self.assertEqual((True, source1_project2b),
573 tenant.getProject('subpath/project2'))
574 self.assertEqual(
575 (True, source1_project2b),
576 tenant.getProject('git1.example.com/subpath/project2'))
577
578 source2_project2b = model.Project('subpath/project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700579 tpc = model.TenantProjectConfig(source2_project2b)
580 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700581 d = {'project1':
582 {'git1.example.com': source1_project1,
583 'git2.example.com': source2_project1},
584 'project2':
585 {'git1.example.com': source1_project2,
586 'git2.example.com': source2_project2},
587 'subpath/project2':
588 {'git1.example.com': source1_project2b,
589 'git2.example.com': source2_project2b}}
590 self.assertEqual(d, tenant.projects)
591 self.assertEqual((False, source1_project2),
592 tenant.getProject('git1.example.com/project2'))
593 self.assertEqual((True, source2_project2),
594 tenant.getProject('git2.example.com/project2'))
595 with testtools.ExpectedException(
596 Exception,
597 "Project name 'subpath/project2' is ambiguous"):
598 tenant.getProject('subpath/project2')
599 self.assertEqual(
600 (True, source1_project2b),
601 tenant.getProject('git1.example.com/subpath/project2'))
602 self.assertEqual(
603 (True, source2_project2b),
604 tenant.getProject('git2.example.com/subpath/project2'))
605
606 with testtools.ExpectedException(
607 Exception,
608 "Project project1 is already in project index"):
James E. Blair08d9b782017-06-29 14:22:48 -0700609 tenant._addProject(source1_project1_tpc)