blob: 335d7c3035588be400ce9db8438e8e9a189f503c [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. Blair01f83b72017-03-15 13:03:40 -0700305 conf = yaml.safe_load('''
306- secret:
307 name: pypi-credentials
308 data:
309 username: test-username
310 password: !encrypted/pkcs1 |
311 BFhtdnm8uXx7kn79RFL/zJywmzLkT1GY78P3bOtp4WghUFWobkifSu7ZpaV4NeO0s71YUsi1wGZZ
312 L0LveZjUN0t6OU1VZKSG8R5Ly7urjaSo1pPVIq5Rtt/H7W14Lecd+cUeKb4joeusC9drN3AA8a4o
313 ykcVpt1wVqUnTbMGC9ARMCQP6eopcs1l7tzMseprW4RDNhIuz3CRgd0QBMPl6VDoFgBPB8vxtJw+
314 3m0rqBYZCLZgCXekqlny8s2s92nJMuUABbJOEcDRarzibDsSXsfJt1y+5n7yOURsC7lovMg4GF/v
315 Cl/0YMKjBO5bpv9EM5fToeKYyPGSKQoHOnCYceb3cAVcv5UawcCic8XjhEhp4K7WPdYf2HVAC/qt
316 xhbpjTxG4U5Q/SoppOJ60WqEkQvbXs6n5Dvy7xmph6GWmU/bAv3eUK3pdD3xa2Ue1lHWz3U+rsYr
317 aI+AKYsMYx3RBlfAmCeC1ve2BXPrqnOo7G8tnUvfdYPbK4Aakk0ds/AVqFHEZN+S6hRBmBjLaRFW
318 Z3QSO1NjbBxWnaHKZYT7nkrJm8AMCgZU0ZArFLpaufKCeiK5ECSsDxic4FIsY1OkWT42qEUfL0Wd
319 +150AKGNZpPJnnP3QYY4W/MWcKH/zdO400+zWN52WevbSqZy90tqKDJrBkMl1ydqbuw1E4ZHvIs=
320''')[0]['secret']
321
322 conf['_source_context'] = self.context
323 conf['_start_mark'] = self.start_mark
324
325 secret = configloader.SecretParser.fromYaml(layout, conf)
326 layout.addSecret(secret)
327
James E. Blair5ac93842017-01-20 06:47:34 -0800328 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800329 '_source_context': self.context,
330 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000331 'name': 'base',
332 'timeout': 30,
333 })
334 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800335 pypi_upload_without_inherit = configloader.JobParser.fromYaml(
336 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800337 '_source_context': self.context,
338 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800339 'name': 'pypi-upload-without-inherit',
340 'parent': 'base',
341 'timeout': 40,
342 'auth': {
343 'secrets': [
344 'pypi-credentials',
345 ]
346 }
347 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000348 layout.addJob(pypi_upload_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800349 pypi_upload_with_inherit = configloader.JobParser.fromYaml(
350 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800351 '_source_context': self.context,
352 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800353 'name': 'pypi-upload-with-inherit',
354 'parent': 'base',
355 'timeout': 40,
356 'auth': {
357 'inherit': True,
358 'secrets': [
359 'pypi-credentials',
360 ]
361 }
362 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000363 layout.addJob(pypi_upload_with_inherit)
364 pypi_upload_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800365 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800366 '_source_context': self.context,
367 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000368 'name': 'pypi-upload-with-inherit-false',
369 'parent': 'base',
370 'timeout': 40,
371 'auth': {
372 'inherit': False,
Ricardo Carrillo Cruz12c892b2016-11-18 15:35:49 +0000373 'secrets': [
374 'pypi-credentials',
375 ]
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000376 }
377 })
378 layout.addJob(pypi_upload_with_inherit_false)
James E. Blair5ac93842017-01-20 06:47:34 -0800379 in_repo_job_without_inherit = configloader.JobParser.fromYaml(
380 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800381 '_source_context': self.context,
382 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800383 'name': 'in-repo-job-without-inherit',
384 'parent': 'pypi-upload-without-inherit',
385 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000386 layout.addJob(in_repo_job_without_inherit)
James E. Blair5ac93842017-01-20 06:47:34 -0800387 in_repo_job_with_inherit = configloader.JobParser.fromYaml(
388 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800389 '_source_context': self.context,
390 '_start_mark': self.start_mark,
James E. Blair5ac93842017-01-20 06:47:34 -0800391 'name': 'in-repo-job-with-inherit',
392 'parent': 'pypi-upload-with-inherit',
393 })
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000394 layout.addJob(in_repo_job_with_inherit)
395 in_repo_job_with_inherit_false = configloader.JobParser.fromYaml(
James E. Blair5ac93842017-01-20 06:47:34 -0800396 tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800397 '_source_context': self.context,
398 '_start_mark': self.start_mark,
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000399 'name': 'in-repo-job-with-inherit-false',
400 'parent': 'pypi-upload-with-inherit-false',
401 })
402 layout.addJob(in_repo_job_with_inherit_false)
403
404 self.assertNotIn('auth', in_repo_job_without_inherit.auth)
Ricardo Carrillo Cruz12c892b2016-11-18 15:35:49 +0000405 self.assertIn('secrets', in_repo_job_with_inherit.auth)
406 self.assertEquals(in_repo_job_with_inherit.auth['secrets'],
407 ['pypi-credentials'])
Ricardo Carrillo Cruz4e94f612016-07-25 16:11:56 +0000408 self.assertNotIn('auth', in_repo_job_with_inherit_false.auth)
409
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700410 def test_job_inheritance_job_tree(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800411 tenant = model.Tenant('tenant')
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700412 layout = model.Layout()
413
414 pipeline = model.Pipeline('gate', layout)
415 layout.addPipeline(pipeline)
416 queue = model.ChangeQueue(pipeline)
417
James E. Blair5ac93842017-01-20 06:47:34 -0800418 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800419 '_source_context': self.context,
420 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700421 'name': 'base',
422 'timeout': 30,
423 })
424 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800425 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800426 '_source_context': self.context,
427 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700428 'name': 'python27',
429 'parent': 'base',
430 'timeout': 40,
431 })
432 layout.addJob(python27)
James E. Blair5ac93842017-01-20 06:47:34 -0800433 python27diablo = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800434 '_source_context': self.context,
435 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700436 'name': 'python27',
437 'branches': [
438 'stable/diablo'
439 ],
440 'timeout': 50,
441 })
442 layout.addJob(python27diablo)
443
James E. Blairff555742017-02-19 11:34:27 -0800444 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800445 '_source_context': self.context,
446 '_start_mark': self.start_mark,
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700447 'name': 'project',
448 'gate': {
449 'jobs': [
450 {'python27': {'timeout': 70}}
451 ]
452 }
James E. Blairff555742017-02-19 11:34:27 -0800453 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800454 layout.addProjectConfig(project_config)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700455
James E. Blairec7ff302017-03-04 07:31:32 -0800456 change = model.Change(self.project)
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700457 change.branch = 'master'
458 item = queue.enqueueChange(change)
459 item.current_build_set.layout = layout
460
461 self.assertTrue(base.changeMatches(change))
462 self.assertTrue(python27.changeMatches(change))
463 self.assertFalse(python27diablo.changeMatches(change))
464
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200465 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700466 self.assertEqual(len(item.getJobs()), 1)
467 job = item.getJobs()[0]
468 self.assertEqual(job.name, 'python27')
469 self.assertEqual(job.timeout, 70)
470
471 change.branch = 'stable/diablo'
472 item = queue.enqueueChange(change)
473 item.current_build_set.layout = layout
474
475 self.assertTrue(base.changeMatches(change))
476 self.assertTrue(python27.changeMatches(change))
477 self.assertTrue(python27diablo.changeMatches(change))
478
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200479 item.freezeJobGraph()
James E. Blair8b1dc3f2016-07-05 16:49:00 -0700480 self.assertEqual(len(item.getJobs()), 1)
481 job = item.getJobs()[0]
482 self.assertEqual(job.name, 'python27')
483 self.assertEqual(job.timeout, 70)
484
Clint Byrum85493602016-11-18 11:59:47 -0800485 def test_inheritance_keeps_matchers(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800486 tenant = model.Tenant('tenant')
Clint Byrum85493602016-11-18 11:59:47 -0800487 layout = model.Layout()
488
489 pipeline = model.Pipeline('gate', layout)
490 layout.addPipeline(pipeline)
491 queue = model.ChangeQueue(pipeline)
James E. Blairc73c73a2017-01-20 15:15:15 -0800492 project = model.Project('project', None)
Clint Byrum85493602016-11-18 11:59:47 -0800493
James E. Blair5ac93842017-01-20 06:47:34 -0800494 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800495 '_source_context': self.context,
496 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800497 'name': 'base',
498 'timeout': 30,
499 })
500 layout.addJob(base)
James E. Blair5ac93842017-01-20 06:47:34 -0800501 python27 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blairec7ff302017-03-04 07:31:32 -0800502 '_source_context': self.context,
503 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800504 'name': 'python27',
505 'parent': 'base',
506 'timeout': 40,
507 'irrelevant-files': ['^ignored-file$'],
508 })
509 layout.addJob(python27)
510
James E. Blairff555742017-02-19 11:34:27 -0800511 project_config = configloader.ProjectParser.fromYaml(tenant, layout, [{
James E. Blairec7ff302017-03-04 07:31:32 -0800512 '_source_context': self.context,
513 '_start_mark': self.start_mark,
Clint Byrum85493602016-11-18 11:59:47 -0800514 'name': 'project',
515 'gate': {
516 'jobs': [
517 'python27',
518 ]
519 }
James E. Blairff555742017-02-19 11:34:27 -0800520 }])
James E. Blairf59f3cf2017-02-19 14:50:26 -0800521 layout.addProjectConfig(project_config)
Clint Byrum85493602016-11-18 11:59:47 -0800522
523 change = model.Change(project)
524 change.branch = 'master'
525 change.files = ['/COMMIT_MSG', 'ignored-file']
526 item = queue.enqueueChange(change)
527 item.current_build_set.layout = layout
528
529 self.assertTrue(base.changeMatches(change))
530 self.assertFalse(python27.changeMatches(change))
531
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200532 item.freezeJobGraph()
Clint Byrum85493602016-11-18 11:59:47 -0800533 self.assertEqual([], item.getJobs())
534
James E. Blair4317e9f2016-07-15 10:05:47 -0700535 def test_job_source_project(self):
James E. Blair5ac93842017-01-20 06:47:34 -0800536 tenant = model.Tenant('tenant')
James E. Blair4317e9f2016-07-15 10:05:47 -0700537 layout = model.Layout()
James E. Blairc73c73a2017-01-20 15:15:15 -0800538 base_project = model.Project('base_project', None)
James E. Blair6f140c72017-03-03 10:32:07 -0800539 base_context = model.SourceContext(base_project, 'master',
540 'test', True)
James E. Blaircdab2032017-02-01 09:09:29 -0800541
James E. Blair5ac93842017-01-20 06:47:34 -0800542 base = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800543 '_source_context': base_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800544 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700545 'name': 'base',
546 })
547 layout.addJob(base)
548
James E. Blairc73c73a2017-01-20 15:15:15 -0800549 other_project = model.Project('other_project', None)
James E. Blair6f140c72017-03-03 10:32:07 -0800550 other_context = model.SourceContext(other_project, 'master',
551 'test', True)
James E. Blair5ac93842017-01-20 06:47:34 -0800552 base2 = configloader.JobParser.fromYaml(tenant, layout, {
James E. Blaircdab2032017-02-01 09:09:29 -0800553 '_source_context': other_context,
James E. Blairec7ff302017-03-04 07:31:32 -0800554 '_start_mark': self.start_mark,
James E. Blair4317e9f2016-07-15 10:05:47 -0700555 'name': 'base',
556 })
557 with testtools.ExpectedException(
558 Exception,
559 "Job base in other_project is not permitted "
560 "to shadow job base in base_project"):
561 layout.addJob(base2)
562
James E. Blairce8a2132016-05-19 15:21:52 -0700563
564class TestJobTimeData(BaseTestCase):
565 def setUp(self):
566 super(TestJobTimeData, self).setUp()
567 self.tmp_root = self.useFixture(fixtures.TempDir(
568 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
569 ).path
570
571 def test_empty_timedata(self):
572 path = os.path.join(self.tmp_root, 'job-name')
573 self.assertFalse(os.path.exists(path))
574 self.assertFalse(os.path.exists(path + '.tmp'))
575 td = model.JobTimeData(path)
576 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
577 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
578 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
579
580 def test_save_reload(self):
581 path = os.path.join(self.tmp_root, 'job-name')
582 self.assertFalse(os.path.exists(path))
583 self.assertFalse(os.path.exists(path + '.tmp'))
584 td = model.JobTimeData(path)
585 self.assertEqual(td.success_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
586 self.assertEqual(td.failure_times, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
587 self.assertEqual(td.results, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
588 success_times = []
589 failure_times = []
590 results = []
591 for x in range(10):
592 success_times.append(int(random.random() * 1000))
593 failure_times.append(int(random.random() * 1000))
594 results.append(0)
595 results.append(1)
596 random.shuffle(results)
597 s = f = 0
598 for result in results:
599 if result:
600 td.add(failure_times[f], 'FAILURE')
601 f += 1
602 else:
603 td.add(success_times[s], 'SUCCESS')
604 s += 1
605 self.assertEqual(td.success_times, success_times)
606 self.assertEqual(td.failure_times, failure_times)
607 self.assertEqual(td.results, results[10:])
608 td.save()
609 self.assertTrue(os.path.exists(path))
610 self.assertFalse(os.path.exists(path + '.tmp'))
611 td = model.JobTimeData(path)
612 td.load()
613 self.assertEqual(td.success_times, success_times)
614 self.assertEqual(td.failure_times, failure_times)
615 self.assertEqual(td.results, results[10:])
616
617
618class TestTimeDataBase(BaseTestCase):
619 def setUp(self):
620 super(TestTimeDataBase, self).setUp()
621 self.tmp_root = self.useFixture(fixtures.TempDir(
622 rootdir=os.environ.get("ZUUL_TEST_ROOT"))
623 ).path
624 self.db = model.TimeDataBase(self.tmp_root)
625
626 def test_timedatabase(self):
627 self.assertEqual(self.db.getEstimatedTime('job-name'), 0)
628 self.db.update('job-name', 50, 'SUCCESS')
629 self.assertEqual(self.db.getEstimatedTime('job-name'), 50)
630 self.db.update('job-name', 100, 'SUCCESS')
631 self.assertEqual(self.db.getEstimatedTime('job-name'), 75)
632 for x in range(10):
633 self.db.update('job-name', 100, 'SUCCESS')
634 self.assertEqual(self.db.getEstimatedTime('job-name'), 100)
Fredrik Medleyf8aec832015-09-28 13:40:20 +0200635
636
637class TestGraph(BaseTestCase):
638 def test_job_graph_disallows_multiple_jobs_with_same_name(self):
639 graph = model.JobGraph()
640 job1 = model.Job('job')
641 job2 = model.Job('job')
642 graph.addJob(job1)
643 with testtools.ExpectedException(Exception,
644 "Job job already added"):
645 graph.addJob(job2)
646
647 def test_job_graph_disallows_circular_dependencies(self):
648 graph = model.JobGraph()
649 jobs = [model.Job('job%d' % i) for i in range(0, 10)]
650 prevjob = None
651 for j in jobs[:3]:
652 if prevjob:
653 j.dependencies = frozenset([prevjob.name])
654 graph.addJob(j)
655 prevjob = j
656 # 0 triggers 1 triggers 2 triggers 3...
657
658 # Cannot depend on itself
659 with testtools.ExpectedException(
660 Exception,
661 "Dependency cycle detected in job jobX"):
662 j = model.Job('jobX')
663 j.dependencies = frozenset([j.name])
664 graph.addJob(j)
665
666 # Disallow circular dependencies
667 with testtools.ExpectedException(
668 Exception,
669 "Dependency cycle detected in job job3"):
670 jobs[4].dependencies = frozenset([jobs[3].name])
671 graph.addJob(jobs[4])
672 jobs[3].dependencies = frozenset([jobs[4].name])
673 graph.addJob(jobs[3])
674
675 jobs[5].dependencies = frozenset([jobs[4].name])
676 graph.addJob(jobs[5])
677
678 with testtools.ExpectedException(
679 Exception,
680 "Dependency cycle detected in job job3"):
681 jobs[3].dependencies = frozenset([jobs[5].name])
682 graph.addJob(jobs[3])
683
684 jobs[3].dependencies = frozenset([jobs[2].name])
685 graph.addJob(jobs[3])
686 jobs[6].dependencies = frozenset([jobs[2].name])
687 graph.addJob(jobs[6])