blob: ee7c6abe7e0c476f876bed866ff459e00970e06d [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. Blairec7ff302017-03-04 07:31:32 -080021import yaml
James E. Blairce8a2132016-05-19 15:21:52 -070022
Maru Newby3fe5f852015-01-13 04:22:14 +000023from zuul import model
James E. Blair83005782015-12-11 14:46:03 -080024from zuul import configloader
Maru Newby3fe5f852015-01-13 04:22:14 +000025
26from tests.base import BaseTestCase
27
28
29class TestJob(BaseTestCase):
30
James E. Blaira7f51ca2017-02-07 16:01:26 -080031 def setUp(self):
32 super(TestJob, self).setUp()
33 self.project = model.Project('project', None)
James E. Blair6f140c72017-03-03 10:32:07 -080034 self.context = model.SourceContext(self.project, 'master',
35 'test', True)
James E. Blairec7ff302017-03-04 07:31:32 -080036 self.start_mark = yaml.Mark('name', 0, 0, 0, '', 0)
James E. Blaira7f51ca2017-02-07 16:01:26 -080037
Maru Newby3fe5f852015-01-13 04:22:14 +000038 @property
39 def job(self):
James E. Blair5ac93842017-01-20 06:47:34 -080040 tenant = model.Tenant('tenant')
James E. Blair83005782015-12-11 14:46:03 -080041 layout = model.Layout()
James E. Blair5ac93842017-01-20 06:47:34 -080042 job = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -080043 '_source_context': self.context,
44 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -080045 'name': 'job',
46 'irrelevant-files': [
47 '^docs/.*$'
48 ]})
Maru Newby3fe5f852015-01-13 04:22:14 +000049 return job
50
51 def test_change_matches_returns_false_for_matched_skip_if(self):
52 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030053 change.files = ['/COMMIT_MSG', 'docs/foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000054 self.assertFalse(self.job.changeMatches(change))
55
56 def test_change_matches_returns_true_for_unmatched_skip_if(self):
57 change = model.Change('project')
Alexander Evseevdbe6fab2015-11-19 12:46:34 +030058 change.files = ['/COMMIT_MSG', 'foo']
Maru Newby3fe5f852015-01-13 04:22:14 +000059 self.assertTrue(self.job.changeMatches(change))
60
Maru Newby79427a42015-02-17 17:54:45 +000061 def test_job_sets_defaults_for_boolean_attributes(self):
James E. Blair83005782015-12-11 14:46:03 -080062 self.assertIsNotNone(self.job.voting)
63
64 def test_job_inheritance(self):
James E. Blaira7f51ca2017-02-07 16:01:26 -080065 # This is standard job inheritance.
66
67 base_pre = model.PlaybookContext(self.context, 'base-pre')
68 base_run = model.PlaybookContext(self.context, 'base-run')
69 base_post = model.PlaybookContext(self.context, 'base-post')
70
71 base = model.Job('base')
72 base.timeout = 30
73 base.pre_run = [base_pre]
74 base.run = [base_run]
75 base.post_run = [base_post]
76 base.auth = dict(foo='bar', inherit=False)
77
78 py27 = model.Job('py27')
79 self.assertEqual(None, py27.timeout)
80 py27.inheritFrom(base)
81 self.assertEqual(30, py27.timeout)
82 self.assertEqual(['base-pre'],
83 [x.path for x in py27.pre_run])
84 self.assertEqual(['base-run'],
85 [x.path for x in py27.run])
86 self.assertEqual(['base-post'],
87 [x.path for x in py27.post_run])
88 self.assertEqual({}, py27.auth)
89
90 def test_job_variants(self):
91 # This simulates freezing a job.
92
93 py27_pre = model.PlaybookContext(self.context, 'py27-pre')
94 py27_run = model.PlaybookContext(self.context, 'py27-run')
95 py27_post = model.PlaybookContext(self.context, 'py27-post')
96
97 py27 = model.Job('py27')
98 py27.timeout = 30
99 py27.pre_run = [py27_pre]
100 py27.run = [py27_run]
101 py27.post_run = [py27_post]
102 auth = dict(foo='bar', inherit=False)
103 py27.auth = auth
104
105 job = py27.copy()
106 self.assertEqual(30, job.timeout)
107
108 # Apply the diablo variant
109 diablo = model.Job('py27')
110 diablo.timeout = 40
111 job.applyVariant(diablo)
112
113 self.assertEqual(40, job.timeout)
114 self.assertEqual(['py27-pre'],
115 [x.path for x in job.pre_run])
116 self.assertEqual(['py27-run'],
117 [x.path for x in job.run])
118 self.assertEqual(['py27-post'],
119 [x.path for x in job.post_run])
120 self.assertEqual(auth, job.auth)
121
122 # Set the job to final for the following checks
123 job.final = True
124 self.assertTrue(job.voting)
125
126 good_final = model.Job('py27')
127 good_final.voting = False
128 job.applyVariant(good_final)
129 self.assertFalse(job.voting)
130
131 bad_final = model.Job('py27')
132 bad_final.timeout = 600
133 with testtools.ExpectedException(
134 Exception,
135 "Unable to modify final job"):
136 job.applyVariant(bad_final)
137
138 def test_job_inheritance_configloader(self):
139 # TODO(jeblair): move this to a configloader test
James E. Blair5ac93842017-01-20 06:47:34 -0800140 tenant = model.Tenant('tenant')
James E. Blair83005782015-12-11 14:46:03 -0800141 layout = model.Layout()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700142
143 pipeline = model.Pipeline('gate', layout)
144 layout.addPipeline(pipeline)
145 queue = model.ChangeQueue(pipeline)
James E. Blairc73c73a2017-01-20 15:15:15 -0800146 project = model.Project('project', None)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700147
James E. Blair5ac93842017-01-20 06:47:34 -0800148 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800149 '_source_context': self.context,
150 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800151 'name': 'base',
152 'timeout': 30,
James E. Blaira7f51ca2017-02-07 16:01:26 -0800153 'pre-run': 'base-pre',
154 'post-run': 'base-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800155 'nodes': [{
156 'name': 'controller',
157 'image': 'base',
158 }],
James E. Blair83005782015-12-11 14:46:03 -0800159 })
160 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800161 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800162 '_source_context': self.context,
163 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800164 'name': 'python27',
165 'parent': 'base',
James E. Blaira7f51ca2017-02-07 16:01:26 -0800166 'pre-run': 'py27-pre',
167 'post-run': 'py27-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800168 'nodes': [{
169 'name': 'controller',
170 'image': 'new',
171 }],
James E. Blair83005782015-12-11 14:46:03 -0800172 'timeout': 40,
173 })
174 layout.addJob(python27)
James E. Blair5ac93842017-01-20 06:47:34 -0800175 python27diablo = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800176 '_source_context': self.context,
177 '_start_mark': self.start_mark,
James E. Blair83005782015-12-11 14:46:03 -0800178 'name': 'python27',
179 'branches': [
180 'stable/diablo'
181 ],
James E. Blaira7f51ca2017-02-07 16:01:26 -0800182 'pre-run': 'py27-diablo-pre',
183 'run': 'py27-diablo',
184 'post-run': 'py27-diablo-post',
James E. Blair1774dd52017-02-03 10:52:32 -0800185 'nodes': [{
186 'name': 'controller',
187 'image': 'old',
188 }],
James E. Blair83005782015-12-11 14:46:03 -0800189 'timeout': 50,
190 })
191 layout.addJob(python27diablo)
192
James E. Blair5ac93842017-01-20 06:47:34 -0800193 python27essex = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800194 '_source_context': self.context,
195 '_start_mark': self.start_mark,
James E. Blaira7f51ca2017-02-07 16:01:26 -0800196 'name': 'python27',
197 'branches': [
198 'stable/essex'
199 ],
200 'pre-run': 'py27-essex-pre',
201 'post-run': 'py27-essex-post',
202 })
203 layout.addJob(python27essex)
204
James E. Blairff555742017-02-19 11:34:27 -0800205 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800206 '_source_context': self.context,
207 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700208 'name': 'project',
209 'gate': {
210 'jobs': [
211 'python27'
212 ]
213 }
James E. Blairff555742017-02-19 11:34:27 -0800214 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800215 layout.addProjectConfig(project_config)
James E. Blair83005782015-12-11 14:46:03 -0800216
James E. Blair83005782015-12-11 14:46:03 -0800217 change = model.Change(project)
James E. Blair1774dd52017-02-03 10:52:32 -0800218 # Test master
James E. Blair83005782015-12-11 14:46:03 -0800219 change.branch = 'master'
220 item = queue.enqueueChange(change)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700221 item.current_build_set.layout = layout
James E. Blair83005782015-12-11 14:46:03 -0800222
223 self.assertTrue(base.changeMatches(change))
224 self.assertTrue(python27.changeMatches(change))
225 self.assertFalse(python27diablo.changeMatches(change))
James E. Blaira7f51ca2017-02-07 16:01:26 -0800226 self.assertFalse(python27essex.changeMatches(change))
James E. Blair83005782015-12-11 14:46:03 -0800227
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200228 item.freezeJobGraph()
James E. Blair83005782015-12-11 14:46:03 -0800229 self.assertEqual(len(item.getJobs()), 1)
230 job = item.getJobs()[0]
231 self.assertEqual(job.name, 'python27')
232 self.assertEqual(job.timeout, 40)
James E. Blair1774dd52017-02-03 10:52:32 -0800233 nodes = job.nodeset.getNodes()
234 self.assertEqual(len(nodes), 1)
235 self.assertEqual(nodes[0].image, 'new')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800236 self.assertEqual([x.path for x in job.pre_run],
237 ['playbooks/base-pre',
238 'playbooks/py27-pre'])
239 self.assertEqual([x.path for x in job.post_run],
240 ['playbooks/py27-post',
241 'playbooks/base-post'])
242 self.assertEqual([x.path for x in job.run],
243 ['playbooks/python27',
244 'playbooks/base'])
James E. Blair83005782015-12-11 14:46:03 -0800245
James E. Blair1774dd52017-02-03 10:52:32 -0800246 # Test diablo
James E. Blair83005782015-12-11 14:46:03 -0800247 change.branch = 'stable/diablo'
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700248 item = queue.enqueueChange(change)
249 item.current_build_set.layout = layout
James E. Blair83005782015-12-11 14:46:03 -0800250
251 self.assertTrue(base.changeMatches(change))
252 self.assertTrue(python27.changeMatches(change))
253 self.assertTrue(python27diablo.changeMatches(change))
James E. Blaira7f51ca2017-02-07 16:01:26 -0800254 self.assertFalse(python27essex.changeMatches(change))
James E. Blair83005782015-12-11 14:46:03 -0800255
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200256 item.freezeJobGraph()
James E. Blair83005782015-12-11 14:46:03 -0800257 self.assertEqual(len(item.getJobs()), 1)
258 job = item.getJobs()[0]
259 self.assertEqual(job.name, 'python27')
260 self.assertEqual(job.timeout, 50)
James E. Blair1774dd52017-02-03 10:52:32 -0800261 nodes = job.nodeset.getNodes()
262 self.assertEqual(len(nodes), 1)
263 self.assertEqual(nodes[0].image, 'old')
James E. Blaira7f51ca2017-02-07 16:01:26 -0800264 self.assertEqual([x.path for x in job.pre_run],
265 ['playbooks/base-pre',
266 'playbooks/py27-pre',
267 'playbooks/py27-diablo-pre'])
268 self.assertEqual([x.path for x in job.post_run],
269 ['playbooks/py27-diablo-post',
270 'playbooks/py27-post',
271 'playbooks/base-post'])
272 self.assertEqual([x.path for x in job.run],
273 ['playbooks/py27-diablo']),
274
275 # Test essex
276 change.branch = 'stable/essex'
277 item = queue.enqueueChange(change)
278 item.current_build_set.layout = layout
279
280 self.assertTrue(base.changeMatches(change))
281 self.assertTrue(python27.changeMatches(change))
282 self.assertFalse(python27diablo.changeMatches(change))
283 self.assertTrue(python27essex.changeMatches(change))
284
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200285 item.freezeJobGraph()
James E. Blaira7f51ca2017-02-07 16:01:26 -0800286 self.assertEqual(len(item.getJobs()), 1)
287 job = item.getJobs()[0]
288 self.assertEqual(job.name, 'python27')
289 self.assertEqual([x.path for x in job.pre_run],
290 ['playbooks/base-pre',
291 'playbooks/py27-pre',
292 'playbooks/py27-essex-pre'])
293 self.assertEqual([x.path for x in job.post_run],
294 ['playbooks/py27-essex-post',
295 'playbooks/py27-post',
296 'playbooks/base-post'])
297 self.assertEqual([x.path for x in job.run],
298 ['playbooks/python27',
299 'playbooks/base'])
James E. Blairce8a2132016-05-19 15:21:52 -0700300
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000301 def test_job_auth_inheritance(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800302 tenant = model.Tenant('tenant')
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000303 layout = model.Layout()
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000304
James E. Blair5ac93842017-01-20 06:47:34 -0800305 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800306 '_source_context': self.context,
307 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000308 'name': 'base',
309 'timeout': 30,
310 })
311 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800312 pypi_upload_without_inherit = configloader.JobParser.fromYaml(
313 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800314 '_source_context': self.context,
315 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800316 'name': 'pypi-upload-without-inherit',
317 'parent': 'base',
318 'timeout': 40,
319 'auth': {
320 'secrets': [
321 'pypi-credentials',
322 ]
323 }
324 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000325 layout.addJob(pypi_upload_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800326 pypi_upload_with_inherit = configloader.JobParser.fromYaml(
327 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800328 '_source_context': self.context,
329 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800330 'name': 'pypi-upload-with-inherit',
331 'parent': 'base',
332 'timeout': 40,
333 'auth': {
334 'inherit': True,
335 'secrets': [
336 'pypi-credentials',
337 ]
338 }
339 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000340 layout.addJob(pypi_upload_with_inherit)
341 pypi_upload_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800342 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800343 '_source_context': self.context,
344 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000345 'name': 'pypi-upload-with-inherit-false',
346 'parent': 'base',
347 'timeout': 40,
348 'auth': {
349 'inherit': False,
Ricardo Carrillo Cruz12c892b2016-11-18 15:35:49 +0000350 'secrets': [
351 'pypi-credentials',
352 ]
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000353 }
354 })
355 layout.addJob(pypi_upload_with_inherit_false)
James E. Blair5ac93842017-01-20 06:47:34 -0800356 in_repo_job_without_inherit = configloader.JobParser.fromYaml(
357 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800358 '_source_context': self.context,
359 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800360 'name': 'in-repo-job-without-inherit',
361 'parent': 'pypi-upload-without-inherit',
362 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000363 layout.addJob(in_repo_job_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800364 in_repo_job_with_inherit = configloader.JobParser.fromYaml(
365 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800366 '_source_context': self.context,
367 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800368 'name': 'in-repo-job-with-inherit',
369 'parent': 'pypi-upload-with-inherit',
370 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000371 layout.addJob(in_repo_job_with_inherit)
372 in_repo_job_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800373 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800374 '_source_context': self.context,
375 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000376 'name': 'in-repo-job-with-inherit-false',
377 'parent': 'pypi-upload-with-inherit-false',
378 })
379 layout.addJob(in_repo_job_with_inherit_false)
380
381 self.assertNotIn('auth', in_repo_job_without_inherit.auth)
Ricardo Carrillo Cruz12c892b2016-11-18 15:35:49 +0000382 self.assertIn('secrets', in_repo_job_with_inherit.auth)
383 self.assertEquals(in_repo_job_with_inherit.auth['secrets'],
384 ['pypi-credentials'])
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000385 self.assertNotIn('auth', in_repo_job_with_inherit_false.auth)
386
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700387 def test_job_inheritance_job_tree(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800388 tenant = model.Tenant('tenant')
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700389 layout = model.Layout()
390
391 pipeline = model.Pipeline('gate', layout)
392 layout.addPipeline(pipeline)
393 queue = model.ChangeQueue(pipeline)
394
James E. Blair5ac93842017-01-20 06:47:34 -0800395 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800396 '_source_context': self.context,
397 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700398 'name': 'base',
399 'timeout': 30,
400 })
401 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800402 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800403 '_source_context': self.context,
404 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700405 'name': 'python27',
406 'parent': 'base',
407 'timeout': 40,
408 })
409 layout.addJob(python27)
James E. Blair5ac93842017-01-20 06:47:34 -0800410 python27diablo = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800411 '_source_context': self.context,
412 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700413 'name': 'python27',
414 'branches': [
415 'stable/diablo'
416 ],
417 'timeout': 50,
418 })
419 layout.addJob(python27diablo)
420
James E. Blairff555742017-02-19 11:34:27 -0800421 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800422 '_source_context': self.context,
423 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700424 'name': 'project',
425 'gate': {
426 'jobs': [
427 {'python27': {'timeout': 70}}
428 ]
429 }
James E. Blairff555742017-02-19 11:34:27 -0800430 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800431 layout.addProjectConfig(project_config)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700432
James E. Blairec7ff302017-03-04 07:31:32 -0800433 change = model.Change(self.project)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700434 change.branch = 'master'
435 item = queue.enqueueChange(change)
436 item.current_build_set.layout = layout
437
438 self.assertTrue(base.changeMatches(change))
439 self.assertTrue(python27.changeMatches(change))
440 self.assertFalse(python27diablo.changeMatches(change))
441
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200442 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700443 self.assertEqual(len(item.getJobs()), 1)
444 job = item.getJobs()[0]
445 self.assertEqual(job.name, 'python27')
446 self.assertEqual(job.timeout, 70)
447
448 change.branch = 'stable/diablo'
449 item = queue.enqueueChange(change)
450 item.current_build_set.layout = layout
451
452 self.assertTrue(base.changeMatches(change))
453 self.assertTrue(python27.changeMatches(change))
454 self.assertTrue(python27diablo.changeMatches(change))
455
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200456 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700457 self.assertEqual(len(item.getJobs()), 1)
458 job = item.getJobs()[0]
459 self.assertEqual(job.name, 'python27')
460 self.assertEqual(job.timeout, 70)
461
Clint Byrum85493602016-11-18 11:59:47 -0800462 def test_inheritance_keeps_matchers(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800463 tenant = model.Tenant('tenant')
Clint Byrum85493602016-11-18 11:59:47 -0800464 layout = model.Layout()
465
466 pipeline = model.Pipeline('gate', layout)
467 layout.addPipeline(pipeline)
468 queue = model.ChangeQueue(pipeline)
James E. Blairc73c73a2017-01-20 15:15:15 -0800469 project = model.Project('project', None)
Clint Byrum85493602016-11-18 11:59:47 -0800470
James E. Blair5ac93842017-01-20 06:47:34 -0800471 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800472 '_source_context': self.context,
473 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800474 'name': 'base',
475 'timeout': 30,
476 })
477 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800478 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800479 '_source_context': self.context,
480 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800481 'name': 'python27',
482 'parent': 'base',
483 'timeout': 40,
484 'irrelevant-files': ['^ignored-file$'],
485 })
486 layout.addJob(python27)
487
James E. Blairff555742017-02-19 11:34:27 -0800488 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800489 '_source_context': self.context,
490 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800491 'name': 'project',
492 'gate': {
493 'jobs': [
494 'python27',
495 ]
496 }
James E. Blairff555742017-02-19 11:34:27 -0800497 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800498 layout.addProjectConfig(project_config)
Clint Byrum85493602016-11-18 11:59:47 -0800499
500 change = model.Change(project)
501 change.branch = 'master'
502 change.files = ['/COMMIT_MSG', 'ignored-file']
503 item = queue.enqueueChange(change)
504 item.current_build_set.layout = layout
505
506 self.assertTrue(base.changeMatches(change))
507 self.assertFalse(python27.changeMatches(change))
508
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200509 item.freezeJobGraph()
Clint Byrum85493602016-11-18 11:59:47 -0800510 self.assertEqual([], item.getJobs())
511
James E. Blair4317e9f2016-07-15 10:05:47 -0700512 def test_job_source_project(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800513 tenant = model.Tenant('tenant')
James E. Blair4317e9f2016-07-15 10:05:47 -0700514 layout = model.Layout()
James E. Blairc73c73a2017-01-20 15:15:15 -0800515 base_project = model.Project('base_project', None)
James E. Blair6f140c72017-03-03 10:32:07 -0800516 base_context = model.SourceContext(base_project, 'master',
517 'test', True)
James E. Blaircdab2032017-02-01 09:09:29 -0800518
James E. Blair5ac93842017-01-20 06:47:34 -0800519 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800520 '_source_context': base_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800521 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700522 'name': 'base',
523 })
524 layout.addJob(base)
525
James E. Blairc73c73a2017-01-20 15:15:15 -0800526 other_project = model.Project('other_project', None)
James E. Blair6f140c72017-03-03 10:32:07 -0800527 other_context = model.SourceContext(other_project, 'master',
528 'test', True)
James E. Blair5ac93842017-01-20 06:47:34 -0800529 base2 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800530 '_source_context': other_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800531 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700532 'name': 'base',
533 })
534 with testtools.ExpectedException(
535 Exception,
536 "Job base in other_project is not permitted "
537 "to shadow job base in base_project"):
538 layout.addJob(base2)
539
James E. Blairce8a2132016-05-19 15:21:52 -0700540
541class TestJobTimeData(BaseTestCase):
542 def setUp(self):
543 super(TestJobTimeData, self).setUp()
544 self.tmp_root = self.useFixture(fixtures.TempDir(
545 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
546 ).path
547
548 def test_empty_timedata(self):
549 path = os.path.join(self.tmp_root, 'job-name')
550 self.assertFalse(os.path.exists(path))
551 self.assertFalse(os.path.exists(path + '.tmp'))
552 td = model.JobTimeData(path)
553 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
554 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
555 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
556
557 def test_save_reload(self):
558 path = os.path.join(self.tmp_root, 'job-name')
559 self.assertFalse(os.path.exists(path))
560 self.assertFalse(os.path.exists(path + '.tmp'))
561 td = model.JobTimeData(path)
562 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
563 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
564 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
565 success_times = []
566 failure_times = []
567 results = []
568 for x in range(10):
569 success_times.append(int(random.random() * 1000))
570 failure_times.append(int(random.random() * 1000))
571 results.append(0)
572 results.append(1)
573 random.shuffle(results)
574 s = f = 0
575 for result in results:
576 if result:
577 td.add(failure_times[f], 'FAILURE')
578 f += 1
579 else:
580 td.add(success_times[s], 'SUCCESS')
581 s += 1
582 self.assertEqual(td.success_times, success_times)
583 self.assertEqual(td.failure_times, failure_times)
584 self.assertEqual(td.results, results[10:])
585 td.save()
586 self.assertTrue(os.path.exists(path))
587 self.assertFalse(os.path.exists(path + '.tmp'))
588 td = model.JobTimeData(path)
589 td.load()
590 self.assertEqual(td.success_times, success_times)
591 self.assertEqual(td.failure_times, failure_times)
592 self.assertEqual(td.results, results[10:])
593
594
595class TestTimeDataBase(BaseTestCase):
596 def setUp(self):
597 super(TestTimeDataBase, self).setUp()
598 self.tmp_root = self.useFixture(fixtures.TempDir(
599 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
600 ).path
601 self.db = model.TimeDataBase(self.tmp_root)
602
603 def test_timedatabase(self):
604 self.assertEqual(self.db.getEstimatedTime('job-name'), 0)
605 self.db.update('job-name', 50, 'SUCCESS')
606 self.assertEqual(self.db.getEstimatedTime('job-name'), 50)
607 self.db.update('job-name', 100, 'SUCCESS')
608 self.assertEqual(self.db.getEstimatedTime('job-name'), 75)
609 for x in range(10):
610 self.db.update('job-name', 100, 'SUCCESS')
611 self.assertEqual(self.db.getEstimatedTime('job-name'), 100)
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200612
613
614class TestGraph(BaseTestCase):
615 def test_job_graph_disallows_multiple_jobs_with_same_name(self):
616 graph = model.JobGraph()
617 job1 = model.Job('job')
618 job2 = model.Job('job')
619 graph.addJob(job1)
620 with testtools.ExpectedException(Exception,
621 "Job job already added"):
622 graph.addJob(job2)
623
624 def test_job_graph_disallows_circular_dependencies(self):
625 graph = model.JobGraph()
626 jobs = [model.Job('job%d' % i) for i in range(0, 10)]
627 prevjob = None
628 for j in jobs[:3]:
629 if prevjob:
630 j.dependencies = frozenset([prevjob.name])
631 graph.addJob(j)
632 prevjob = j
633 # 0 triggers 1 triggers 2 triggers 3...
634
635 # Cannot depend on itself
636 with testtools.ExpectedException(
637 Exception,
638 "Dependency cycle detected in job jobX"):
639 j = model.Job('jobX')
640 j.dependencies = frozenset([j.name])
641 graph.addJob(j)
642
643 # Disallow circular dependencies
644 with testtools.ExpectedException(
645 Exception,
646 "Dependency cycle detected in job job3"):
647 jobs[4].dependencies = frozenset([jobs[3].name])
648 graph.addJob(jobs[4])
649 jobs[3].dependencies = frozenset([jobs[4].name])
650 graph.addJob(jobs[3])
651
652 jobs[5].dependencies = frozenset([jobs[4].name])
653 graph.addJob(jobs[5])
654
655 with testtools.ExpectedException(
656 Exception,
657 "Dependency cycle detected in job job3"):
658 jobs[3].dependencies = frozenset([jobs[5].name])
659 graph.addJob(jobs[3])
660
661 jobs[3].dependencies = frozenset([jobs[2].name])
662 graph.addJob(jobs[3])
663 jobs[6].dependencies = frozenset([jobs[2].name])
664 graph.addJob(jobs[6])