blob: 6ec523270485d4f235713df78ddf817d630855e7 [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
James E. Blair719889f2018-02-19 10:41:09 -080026import zuul.lib.connections
Maru Newby3fe5f852015-01-13 04:22:14 +000027
James E. Blair18f86a32017-03-15 14:43:26 -070028from tests.base import BaseTestCase, FIXTURE_DIR
Maru Newby3fe5f852015-01-13 04:22:14 +000029
30
James E. Blair0a899752017-03-29 13:22:16 -070031class Dummy(object):
32 def __init__(self, **kw):
33 for k, v in kw.items():
34 setattr(self, k, v)
James E. Blairb3f5db12017-03-17 12:57:39 -070035
36
Maru Newby3fe5f852015-01-13 04:22:14 +000037class TestJob(BaseTestCase):
James E. Blaira7f51ca2017-02-07 16:01:26 -080038 def setUp(self):
39 super(TestJob, self).setUp()
James E. Blair719889f2018-02-19 10:41:09 -080040 self.connections = zuul.lib.connections.ConnectionRegistry()
41 self.addCleanup(self.connections.stop)
James E. Blair0a899752017-03-29 13:22:16 -070042 self.connection = Dummy(connection_name='dummy_connection')
43 self.source = Dummy(canonical_hostname='git.example.com',
James E. Blair0a899752017-03-29 13:22:16 -070044 connection=self.connection)
James E. Blairb3f5db12017-03-17 12:57:39 -070045 self.tenant = model.Tenant('tenant')
James E. Blair6459db12017-06-29 14:57:20 -070046 self.layout = model.Layout(self.tenant)
James E. Blair0a899752017-03-29 13:22:16 -070047 self.project = model.Project('project', self.source)
James E. Blair08d9b782017-06-29 14:22:48 -070048 self.tpc = model.TenantProjectConfig(self.project)
49 self.tenant.addUntrustedProject(self.tpc)
James E. Blairb3f5db12017-03-17 12:57:39 -070050 self.pipeline = model.Pipeline('gate', self.layout)
51 self.layout.addPipeline(self.pipeline)
52 self.queue = model.ChangeQueue(self.pipeline)
James E. Blairf94a6d32018-02-16 12:05:32 -080053 self.pcontext = configloader.ParseContext(
James E. Blair719889f2018-02-19 10:41:09 -080054 self.connections, None, self.tenant, self.layout)
55 self.pcontext.setPipelines()
James E. Blairb3f5db12017-03-17 12:57:39 -070056
James E. Blair18f86a32017-03-15 14:43:26 -070057 private_key_file = os.path.join(FIXTURE_DIR, 'private.pem')
58 with open(private_key_file, "rb") as f:
James E. Blairbf1a4f22017-03-17 10:59:37 -070059 self.project.private_key, self.project.public_key = \
60 encryption.deserialize_rsa_keypair(f.read())
James E. Blair6f140c72017-03-03 10:32:07 -080061 self.context = model.SourceContext(self.project, 'master',
62 'test', True)
James E. Blair892cca62017-08-09 11:36:58 -070063 self.untrusted_context = model.SourceContext(self.project, 'master',
64 'test', False)
James E. Blair1cebebf2017-07-14 11:39:03 -070065 m = yaml.Mark('name', 0, 0, 0, '', 0)
66 self.start_mark = configloader.ZuulMark(m, m, '')
James E. Blaira7f51ca2017-02-07 16:01:26 -080067
Maru Newby3fe5f852015-01-13 04:22:14 +000068 @property
69 def job(self):
James E. Blairf94a6d32018-02-16 12:05:32 -080070 job = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -080071 '_source_context': self.context,
72 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -080073 'name': 'job',
James E. Blair2bab6e72017-08-07 09:52:45 -070074 'parent': None,
James E. Blair83005782015-12-11 14:46:03 -080075 'irrelevant-files': [
76 '^docs/.*$'
77 ]})
Maru Newby3fe5f852015-01-13 04:22:14 +000078 return job
79
80 def test_change_matches_returns_false_for_matched_skip_if(self):
81 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030082 change.files = ['/COMMIT_MSG', 'docs/foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000083 self.assertFalse(self.job.changeMatches(change))
84
Jan Hruban570d01c2016-03-10 21:51:32 +010085 def test_change_matches_returns_false_for_single_matched_skip_if(self):
86 change = model.Change('project')
87 change.files = ['docs/foo']
88 self.assertFalse(self.job.changeMatches(change))
89
Maru Newby3fe5f852015-01-13 04:22:14 +000090 def test_change_matches_returns_true_for_unmatched_skip_if(self):
91 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030092 change.files = ['/COMMIT_MSG', 'foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000093 self.assertTrue(self.job.changeMatches(change))
94
Jan Hruban570d01c2016-03-10 21:51:32 +010095 def test_change_matches_returns_true_for_single_unmatched_skip_if(self):
96 change = model.Change('project')
97 change.files = ['foo']
98 self.assertTrue(self.job.changeMatches(change))
99
Maru Newby79427a42015-02-17 17:54:45 +0000100 def test_job_sets_defaults_for_boolean_attributes(self):
James E. Blair83005782015-12-11 14:46:03 -0800101 self.assertIsNotNone(self.job.voting)
102
James E. Blaira7f51ca2017-02-07 16:01:26 -0800103 def test_job_variants(self):
104 # This simulates freezing a job.
105
James E. Blair892cca62017-08-09 11:36:58 -0700106 secrets = ['foo']
107 py27_pre = model.PlaybookContext(self.context, 'py27-pre', [], secrets)
108 py27_run = model.PlaybookContext(self.context, 'py27-run', [], secrets)
109 py27_post = model.PlaybookContext(self.context, 'py27-post', [],
110 secrets)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800111
112 py27 = model.Job('py27')
113 py27.timeout = 30
114 py27.pre_run = [py27_pre]
115 py27.run = [py27_run]
116 py27.post_run = [py27_post]
James E. Blaira7f51ca2017-02-07 16:01:26 -0800117
118 job = py27.copy()
119 self.assertEqual(30, job.timeout)
120
121 # Apply the diablo variant
122 diablo = model.Job('py27')
123 diablo.timeout = 40
124 job.applyVariant(diablo)
125
126 self.assertEqual(40, job.timeout)
127 self.assertEqual(['py27-pre'],
128 [x.path for x in job.pre_run])
129 self.assertEqual(['py27-run'],
130 [x.path for x in job.run])
131 self.assertEqual(['py27-post'],
132 [x.path for x in job.post_run])
James E. Blair892cca62017-08-09 11:36:58 -0700133 self.assertEqual(secrets, job.pre_run[0].secrets)
134 self.assertEqual(secrets, job.run[0].secrets)
135 self.assertEqual(secrets, job.post_run[0].secrets)
James E. Blaira7f51ca2017-02-07 16:01:26 -0800136
137 # Set the job to final for the following checks
138 job.final = True
139 self.assertTrue(job.voting)
140
141 good_final = model.Job('py27')
142 good_final.voting = False
143 job.applyVariant(good_final)
144 self.assertFalse(job.voting)
145
146 bad_final = model.Job('py27')
147 bad_final.timeout = 600
148 with testtools.ExpectedException(
149 Exception,
150 "Unable to modify final job"):
151 job.applyVariant(bad_final)
152
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700153 def test_job_inheritance_job_tree(self):
James E. Blairf94a6d32018-02-16 12:05:32 -0800154 pipeline = model.Pipeline('gate', self.layout)
155 self.layout.addPipeline(pipeline)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700156 queue = model.ChangeQueue(pipeline)
157
James E. Blairf94a6d32018-02-16 12:05:32 -0800158 base = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800159 '_source_context': self.context,
160 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700161 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700162 'parent': None,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700163 'timeout': 30,
164 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800165 self.layout.addJob(base)
166 python27 = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800167 '_source_context': self.context,
168 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700169 'name': 'python27',
170 'parent': 'base',
171 'timeout': 40,
172 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800173 self.layout.addJob(python27)
174 python27diablo = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800175 '_source_context': self.context,
176 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700177 'name': 'python27',
178 'branches': [
179 'stable/diablo'
180 ],
181 'timeout': 50,
182 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800183 self.layout.addJob(python27diablo)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700184
James E. Blairf94a6d32018-02-16 12:05:32 -0800185 project_config = self.pcontext.project_parser.fromYaml([{
James E. Blairec7ff302017-03-04 07:31:32 -0800186 '_source_context': self.context,
187 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700188 'name': 'project',
189 'gate': {
190 'jobs': [
James E. Blair00292672017-10-26 15:29:39 -0700191 {'python27': {'timeout': 70,
192 'run': 'playbooks/python27.yaml'}}
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700193 ]
194 }
James E. Blairff555742017-02-19 11:34:27 -0800195 }])
James E. Blairf94a6d32018-02-16 12:05:32 -0800196 self.layout.addProjectConfig(project_config)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700197
James E. Blairec7ff302017-03-04 07:31:32 -0800198 change = model.Change(self.project)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700199 change.branch = 'master'
200 item = queue.enqueueChange(change)
James E. Blairf94a6d32018-02-16 12:05:32 -0800201 item.layout = self.layout
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700202
203 self.assertTrue(base.changeMatches(change))
204 self.assertTrue(python27.changeMatches(change))
205 self.assertFalse(python27diablo.changeMatches(change))
206
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200207 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700208 self.assertEqual(len(item.getJobs()), 1)
209 job = item.getJobs()[0]
210 self.assertEqual(job.name, 'python27')
211 self.assertEqual(job.timeout, 70)
212
213 change.branch = 'stable/diablo'
214 item = queue.enqueueChange(change)
James E. Blairf94a6d32018-02-16 12:05:32 -0800215 item.layout = self.layout
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700216
217 self.assertTrue(base.changeMatches(change))
218 self.assertTrue(python27.changeMatches(change))
219 self.assertTrue(python27diablo.changeMatches(change))
220
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200221 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700222 self.assertEqual(len(item.getJobs()), 1)
223 job = item.getJobs()[0]
224 self.assertEqual(job.name, 'python27')
225 self.assertEqual(job.timeout, 70)
226
Clint Byrum85493602016-11-18 11:59:47 -0800227 def test_inheritance_keeps_matchers(self):
James E. Blairf94a6d32018-02-16 12:05:32 -0800228 pipeline = model.Pipeline('gate', self.layout)
229 self.layout.addPipeline(pipeline)
Clint Byrum85493602016-11-18 11:59:47 -0800230 queue = model.ChangeQueue(pipeline)
Clint Byrum85493602016-11-18 11:59:47 -0800231
James E. Blairf94a6d32018-02-16 12:05:32 -0800232 base = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800233 '_source_context': self.context,
234 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800235 'name': 'base',
James E. Blair2bab6e72017-08-07 09:52:45 -0700236 'parent': None,
Clint Byrum85493602016-11-18 11:59:47 -0800237 'timeout': 30,
238 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800239 self.layout.addJob(base)
240 python27 = self.pcontext.job_parser.fromYaml({
James E. Blairec7ff302017-03-04 07:31:32 -0800241 '_source_context': self.context,
242 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800243 'name': 'python27',
244 'parent': 'base',
245 'timeout': 40,
246 'irrelevant-files': ['^ignored-file$'],
247 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800248 self.layout.addJob(python27)
Clint Byrum85493602016-11-18 11:59:47 -0800249
James E. Blairf94a6d32018-02-16 12:05:32 -0800250 project_config = self.pcontext.project_parser.fromYaml([{
James E. Blairec7ff302017-03-04 07:31:32 -0800251 '_source_context': self.context,
252 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800253 'name': 'project',
254 'gate': {
255 'jobs': [
256 'python27',
257 ]
258 }
James E. Blairff555742017-02-19 11:34:27 -0800259 }])
James E. Blairf94a6d32018-02-16 12:05:32 -0800260 self.layout.addProjectConfig(project_config)
Clint Byrum85493602016-11-18 11:59:47 -0800261
James E. Blairf94a6d32018-02-16 12:05:32 -0800262 change = model.Change(self.project)
Clint Byrum85493602016-11-18 11:59:47 -0800263 change.branch = 'master'
264 change.files = ['/COMMIT_MSG', 'ignored-file']
265 item = queue.enqueueChange(change)
James E. Blairf94a6d32018-02-16 12:05:32 -0800266 item.layout = self.layout
Clint Byrum85493602016-11-18 11:59:47 -0800267
268 self.assertTrue(base.changeMatches(change))
269 self.assertFalse(python27.changeMatches(change))
270
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200271 item.freezeJobGraph()
Clint Byrum85493602016-11-18 11:59:47 -0800272 self.assertEqual([], item.getJobs())
273
James E. Blair4317e9f2016-07-15 10:05:47 -0700274 def test_job_source_project(self):
James E. Blair0a899752017-03-29 13:22:16 -0700275 base_project = model.Project('base_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800276 base_context = model.SourceContext(base_project, 'master',
277 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700278 tpc = model.TenantProjectConfig(base_project)
James E. Blairf94a6d32018-02-16 12:05:32 -0800279 self.tenant.addUntrustedProject(tpc)
James E. Blaircdab2032017-02-01 09:09:29 -0800280
James E. Blairf94a6d32018-02-16 12:05:32 -0800281 base = self.pcontext.job_parser.fromYaml({
James E. Blaircdab2032017-02-01 09:09:29 -0800282 '_source_context': base_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800283 '_start_mark': self.start_mark,
James E. Blair2bab6e72017-08-07 09:52:45 -0700284 'parent': None,
James E. Blair4317e9f2016-07-15 10:05:47 -0700285 'name': 'base',
286 })
James E. Blairf94a6d32018-02-16 12:05:32 -0800287 self.layout.addJob(base)
James E. Blair4317e9f2016-07-15 10:05:47 -0700288
James E. Blair0a899752017-03-29 13:22:16 -0700289 other_project = model.Project('other_project', self.source)
James E. Blair6f140c72017-03-03 10:32:07 -0800290 other_context = model.SourceContext(other_project, 'master',
291 'test', True)
James E. Blair6459db12017-06-29 14:57:20 -0700292 tpc = model.TenantProjectConfig(other_project)
James E. Blairf94a6d32018-02-16 12:05:32 -0800293 self.tenant.addUntrustedProject(tpc)
294 base2 = self.pcontext.job_parser.fromYaml({
James E. Blaircdab2032017-02-01 09:09:29 -0800295 '_source_context': other_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800296 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700297 'name': 'base',
298 })
299 with testtools.ExpectedException(
300 Exception,
301 "Job base in other_project is not permitted "
302 "to shadow job base in base_project"):
James E. Blairf94a6d32018-02-16 12:05:32 -0800303 self.layout.addJob(base2)
James E. Blair4317e9f2016-07-15 10:05:47 -0700304
James E. Blair8eb564a2017-08-10 09:21:41 -0700305 def test_job_pipeline_allow_untrusted_secrets(self):
306 self.pipeline.post_review = False
James E. Blairf94a6d32018-02-16 12:05:32 -0800307 job = self.pcontext.job_parser.fromYaml({
James E. Blaird2348362017-03-17 13:59:35 -0700308 '_source_context': self.context,
309 '_start_mark': self.start_mark,
310 'name': 'job',
James E. Blair2bab6e72017-08-07 09:52:45 -0700311 'parent': None,
James E. Blaird2348362017-03-17 13:59:35 -0700312 })
James E. Blair8eb564a2017-08-10 09:21:41 -0700313 job.post_review = True
James E. Blaird2348362017-03-17 13:59:35 -0700314
315 self.layout.addJob(job)
316
James E. Blairf94a6d32018-02-16 12:05:32 -0800317 project_config = self.pcontext.project_parser.fromYaml(
James E. Blairaf8b2082017-10-03 15:38:27 -0700318 [{
James E. Blaird2348362017-03-17 13:59:35 -0700319 '_source_context': self.context,
320 '_start_mark': self.start_mark,
321 'name': 'project',
322 'gate': {
323 'jobs': [
324 'job'
325 ]
326 }
327 }]
328 )
329 self.layout.addProjectConfig(project_config)
330
331 change = model.Change(self.project)
332 # Test master
333 change.branch = 'master'
334 item = self.queue.enqueueChange(change)
James E. Blair29a24fd2017-10-02 15:04:56 -0700335 item.layout = self.layout
James E. Blaird2348362017-03-17 13:59:35 -0700336 with testtools.ExpectedException(
337 Exception,
James E. Blair8eb564a2017-08-10 09:21:41 -0700338 "Pre-review pipeline gate does not allow post-review job"):
James E. Blaird2348362017-03-17 13:59:35 -0700339 item.freezeJobGraph()
340
James E. Blairce8a2132016-05-19 15:21:52 -0700341
342class TestJobTimeData(BaseTestCase):
343 def setUp(self):
344 super(TestJobTimeData, self).setUp()
345 self.tmp_root = self.useFixture(fixtures.TempDir(
346 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
347 ).path
348
349 def test_empty_timedata(self):
350 path = os.path.join(self.tmp_root, 'job-name')
351 self.assertFalse(os.path.exists(path))
352 self.assertFalse(os.path.exists(path + '.tmp'))
353 td = model.JobTimeData(path)
354 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
355 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
356 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
357
358 def test_save_reload(self):
359 path = os.path.join(self.tmp_root, 'job-name')
360 self.assertFalse(os.path.exists(path))
361 self.assertFalse(os.path.exists(path + '.tmp'))
362 td = model.JobTimeData(path)
363 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
364 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
365 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
366 success_times = []
367 failure_times = []
368 results = []
369 for x in range(10):
370 success_times.append(int(random.random() * 1000))
371 failure_times.append(int(random.random() * 1000))
372 results.append(0)
373 results.append(1)
374 random.shuffle(results)
375 s = f = 0
376 for result in results:
377 if result:
378 td.add(failure_times[f], 'FAILURE')
379 f += 1
380 else:
381 td.add(success_times[s], 'SUCCESS')
382 s += 1
383 self.assertEqual(td.success_times, success_times)
384 self.assertEqual(td.failure_times, failure_times)
385 self.assertEqual(td.results, results[10:])
386 td.save()
387 self.assertTrue(os.path.exists(path))
388 self.assertFalse(os.path.exists(path + '.tmp'))
389 td = model.JobTimeData(path)
390 td.load()
391 self.assertEqual(td.success_times, success_times)
392 self.assertEqual(td.failure_times, failure_times)
393 self.assertEqual(td.results, results[10:])
394
395
396class TestTimeDataBase(BaseTestCase):
397 def setUp(self):
398 super(TestTimeDataBase, self).setUp()
399 self.tmp_root = self.useFixture(fixtures.TempDir(
400 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
401 ).path
402 self.db = model.TimeDataBase(self.tmp_root)
403
404 def test_timedatabase(self):
James E. Blairae0f23c2017-09-13 10:55:15 -0600405 pipeline = Dummy(layout=Dummy(tenant=Dummy(name='test-tenant')))
406 change = Dummy(project=Dummy(canonical_name='git.example.com/foo/bar'))
407 job = Dummy(name='job-name')
408 item = Dummy(pipeline=pipeline,
409 change=change)
410 build = Dummy(build_set=Dummy(item=item),
411 job=job)
412
413 self.assertEqual(self.db.getEstimatedTime(build), 0)
414 self.db.update(build, 50, 'SUCCESS')
415 self.assertEqual(self.db.getEstimatedTime(build), 50)
416 self.db.update(build, 100, 'SUCCESS')
417 self.assertEqual(self.db.getEstimatedTime(build), 75)
James E. Blairce8a2132016-05-19 15:21:52 -0700418 for x in range(10):
James E. Blairae0f23c2017-09-13 10:55:15 -0600419 self.db.update(build, 100, 'SUCCESS')
420 self.assertEqual(self.db.getEstimatedTime(build), 100)
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200421
422
423class TestGraph(BaseTestCase):
424 def test_job_graph_disallows_multiple_jobs_with_same_name(self):
425 graph = model.JobGraph()
426 job1 = model.Job('job')
427 job2 = model.Job('job')
428 graph.addJob(job1)
429 with testtools.ExpectedException(Exception,
430 "Job job already added"):
431 graph.addJob(job2)
432
433 def test_job_graph_disallows_circular_dependencies(self):
434 graph = model.JobGraph()
435 jobs = [model.Job('job%d' % i) for i in range(0, 10)]
436 prevjob = None
437 for j in jobs[:3]:
438 if prevjob:
439 j.dependencies = frozenset([prevjob.name])
440 graph.addJob(j)
441 prevjob = j
442 # 0 triggers 1 triggers 2 triggers 3...
443
444 # Cannot depend on itself
445 with testtools.ExpectedException(
446 Exception,
447 "Dependency cycle detected in job jobX"):
448 j = model.Job('jobX')
449 j.dependencies = frozenset([j.name])
450 graph.addJob(j)
451
452 # Disallow circular dependencies
453 with testtools.ExpectedException(
454 Exception,
455 "Dependency cycle detected in job job3"):
456 jobs[4].dependencies = frozenset([jobs[3].name])
457 graph.addJob(jobs[4])
458 jobs[3].dependencies = frozenset([jobs[4].name])
459 graph.addJob(jobs[3])
460
461 jobs[5].dependencies = frozenset([jobs[4].name])
462 graph.addJob(jobs[5])
463
464 with testtools.ExpectedException(
465 Exception,
466 "Dependency cycle detected in job job3"):
467 jobs[3].dependencies = frozenset([jobs[5].name])
468 graph.addJob(jobs[3])
469
470 jobs[3].dependencies = frozenset([jobs[2].name])
471 graph.addJob(jobs[3])
472 jobs[6].dependencies = frozenset([jobs[2].name])
473 graph.addJob(jobs[6])
James E. Blairc2a54fd2017-03-29 15:19:26 -0700474
475
476class TestTenant(BaseTestCase):
477 def test_add_project(self):
478 tenant = model.Tenant('tenant')
479 connection1 = Dummy(connection_name='dummy_connection1')
480 source1 = Dummy(canonical_hostname='git1.example.com',
481 name='dummy', # TODOv3(jeblair): remove
482 connection=connection1)
483
484 source1_project1 = model.Project('project1', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700485 source1_project1_tpc = model.TenantProjectConfig(source1_project1)
486 tenant.addConfigProject(source1_project1_tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700487 d = {'project1':
488 {'git1.example.com': source1_project1}}
489 self.assertEqual(d, tenant.projects)
490 self.assertEqual((True, source1_project1),
491 tenant.getProject('project1'))
492 self.assertEqual((True, source1_project1),
493 tenant.getProject('git1.example.com/project1'))
494
495 source1_project2 = model.Project('project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700496 tpc = model.TenantProjectConfig(source1_project2)
497 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700498 d = {'project1':
499 {'git1.example.com': source1_project1},
500 'project2':
501 {'git1.example.com': source1_project2}}
502 self.assertEqual(d, tenant.projects)
503 self.assertEqual((False, source1_project2),
504 tenant.getProject('project2'))
505 self.assertEqual((False, source1_project2),
506 tenant.getProject('git1.example.com/project2'))
507
508 connection2 = Dummy(connection_name='dummy_connection2')
509 source2 = Dummy(canonical_hostname='git2.example.com',
510 name='dummy', # TODOv3(jeblair): remove
511 connection=connection2)
512
513 source2_project1 = model.Project('project1', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700514 tpc = model.TenantProjectConfig(source2_project1)
515 tenant.addUntrustedProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700516 d = {'project1':
517 {'git1.example.com': source1_project1,
518 'git2.example.com': source2_project1},
519 'project2':
520 {'git1.example.com': source1_project2}}
521 self.assertEqual(d, tenant.projects)
522 with testtools.ExpectedException(
523 Exception,
524 "Project name 'project1' is ambiguous"):
525 tenant.getProject('project1')
526 self.assertEqual((False, source1_project2),
527 tenant.getProject('project2'))
528 self.assertEqual((True, source1_project1),
529 tenant.getProject('git1.example.com/project1'))
530 self.assertEqual((False, source2_project1),
531 tenant.getProject('git2.example.com/project1'))
532
533 source2_project2 = model.Project('project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700534 tpc = model.TenantProjectConfig(source2_project2)
535 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700536 d = {'project1':
537 {'git1.example.com': source1_project1,
538 'git2.example.com': source2_project1},
539 'project2':
540 {'git1.example.com': source1_project2,
541 'git2.example.com': source2_project2}}
542 self.assertEqual(d, tenant.projects)
543 with testtools.ExpectedException(
544 Exception,
545 "Project name 'project1' is ambiguous"):
546 tenant.getProject('project1')
547 with testtools.ExpectedException(
548 Exception,
549 "Project name 'project2' is ambiguous"):
550 tenant.getProject('project2')
551 self.assertEqual((True, source1_project1),
552 tenant.getProject('git1.example.com/project1'))
553 self.assertEqual((False, source2_project1),
554 tenant.getProject('git2.example.com/project1'))
555 self.assertEqual((False, source1_project2),
556 tenant.getProject('git1.example.com/project2'))
557 self.assertEqual((True, source2_project2),
558 tenant.getProject('git2.example.com/project2'))
559
560 source1_project2b = model.Project('subpath/project2', source1)
James E. Blair08d9b782017-06-29 14:22:48 -0700561 tpc = model.TenantProjectConfig(source1_project2b)
562 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700563 d = {'project1':
564 {'git1.example.com': source1_project1,
565 'git2.example.com': source2_project1},
566 'project2':
567 {'git1.example.com': source1_project2,
568 'git2.example.com': source2_project2},
569 'subpath/project2':
570 {'git1.example.com': source1_project2b}}
571 self.assertEqual(d, tenant.projects)
572 self.assertEqual((False, source1_project2),
573 tenant.getProject('git1.example.com/project2'))
574 self.assertEqual((True, source2_project2),
575 tenant.getProject('git2.example.com/project2'))
576 self.assertEqual((True, source1_project2b),
577 tenant.getProject('subpath/project2'))
578 self.assertEqual(
579 (True, source1_project2b),
580 tenant.getProject('git1.example.com/subpath/project2'))
581
582 source2_project2b = model.Project('subpath/project2', source2)
James E. Blair08d9b782017-06-29 14:22:48 -0700583 tpc = model.TenantProjectConfig(source2_project2b)
584 tenant.addConfigProject(tpc)
James E. Blairc2a54fd2017-03-29 15:19:26 -0700585 d = {'project1':
586 {'git1.example.com': source1_project1,
587 'git2.example.com': source2_project1},
588 'project2':
589 {'git1.example.com': source1_project2,
590 'git2.example.com': source2_project2},
591 'subpath/project2':
592 {'git1.example.com': source1_project2b,
593 'git2.example.com': source2_project2b}}
594 self.assertEqual(d, tenant.projects)
595 self.assertEqual((False, source1_project2),
596 tenant.getProject('git1.example.com/project2'))
597 self.assertEqual((True, source2_project2),
598 tenant.getProject('git2.example.com/project2'))
599 with testtools.ExpectedException(
600 Exception,
601 "Project name 'subpath/project2' is ambiguous"):
602 tenant.getProject('subpath/project2')
603 self.assertEqual(
604 (True, source1_project2b),
605 tenant.getProject('git1.example.com/subpath/project2'))
606 self.assertEqual(
607 (True, source2_project2b),
608 tenant.getProject('git2.example.com/subpath/project2'))
609
610 with testtools.ExpectedException(
611 Exception,
612 "Project project1 is already in project index"):
James E. Blair08d9b782017-06-29 14:22:48 -0700613 tenant._addProject(source1_project1_tpc)