blob: 967a226f4438ff025c4897f793d788c11f54a1eb [file] [log] [blame]
Gregory Haynes4fc12542015-04-22 20:38:06 -07001# Copyright 2015 GoodData
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
15import logging
Jan Hrubane252a732017-01-03 15:03:09 +010016import re
17from testtools.matchers import MatchesRegex
Jan Hruban324ca5b2015-11-05 19:28:54 +010018import time
Gregory Haynes4fc12542015-04-22 20:38:06 -070019
Wayne1a78c612015-06-11 17:14:13 -070020from tests.base import ZuulTestCase, simple_layout, random_sha1
Gregory Haynes4fc12542015-04-22 20:38:06 -070021
22logging.basicConfig(level=logging.DEBUG,
23 format='%(asctime)s %(name)-32s '
24 '%(levelname)-8s %(message)s')
25
26
27class TestGithubDriver(ZuulTestCase):
28 config_file = 'zuul-github-driver.conf'
29
30 @simple_layout('layouts/basic-github.yaml', driver='github')
31 def test_pull_event(self):
32 self.executor_server.hold_jobs_in_build = True
33
Jan Hruban37615e52015-11-19 14:30:49 +010034 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
35 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Gregory Haynes4fc12542015-04-22 20:38:06 -070036 self.waitUntilSettled()
37
38 build_params = self.builds[0].parameters
39 self.assertEqual('master', build_params['ZUUL_BRANCH'])
Jan Hruban37615e52015-11-19 14:30:49 +010040 self.assertEqual(str(A.number), build_params['ZUUL_CHANGE'])
41 self.assertEqual(A.head_sha, build_params['ZUUL_PATCHSET'])
Gregory Haynes4fc12542015-04-22 20:38:06 -070042
43 self.executor_server.hold_jobs_in_build = False
44 self.executor_server.release()
45 self.waitUntilSettled()
46
47 self.assertEqual('SUCCESS',
48 self.getJobFromHistory('project-test1').result)
49 self.assertEqual('SUCCESS',
50 self.getJobFromHistory('project-test2').result)
51
52 job = self.getJobFromHistory('project-test2')
53 zuulvars = job.parameters['vars']['zuul']
Jan Hruban37615e52015-11-19 14:30:49 +010054 self.assertEqual(A.number, zuulvars['change'])
55 self.assertEqual(A.head_sha, zuulvars['patchset'])
56 self.assertEqual(1, len(A.comments))
Wayne1a78c612015-06-11 17:14:13 -070057
Jan Hrubanc7ab1602015-10-14 15:29:33 +020058 @simple_layout('layouts/basic-github.yaml', driver='github')
59 def test_comment_event(self):
Jan Hruban37615e52015-11-19 14:30:49 +010060 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
61 self.fake_github.emitEvent(A.getCommentAddedEvent('test me'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020062 self.waitUntilSettled()
63 self.assertEqual(2, len(self.history))
64
65 # Test an unmatched comment, history should remain the same
Jan Hruban37615e52015-11-19 14:30:49 +010066 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
67 self.fake_github.emitEvent(B.getCommentAddedEvent('casual comment'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020068 self.waitUntilSettled()
69 self.assertEqual(2, len(self.history))
70
Wayne1a78c612015-06-11 17:14:13 -070071 @simple_layout('layouts/push-tag-github.yaml', driver='github')
72 def test_tag_event(self):
73 self.executor_server.hold_jobs_in_build = True
74
75 sha = random_sha1()
76 self.fake_github.emitEvent(
77 self.fake_github.getPushEvent('org/project', 'refs/tags/newtag',
78 new_rev=sha))
79 self.waitUntilSettled()
80
81 build_params = self.builds[0].parameters
82 self.assertEqual('refs/tags/newtag', build_params['ZUUL_REF'])
83 self.assertEqual('00000000000000000000000000000000',
84 build_params['ZUUL_OLDREV'])
85 self.assertEqual(sha, build_params['ZUUL_NEWREV'])
86
87 self.executor_server.hold_jobs_in_build = False
88 self.executor_server.release()
89 self.waitUntilSettled()
90
91 self.assertEqual('SUCCESS',
92 self.getJobFromHistory('project-tag').result)
93
94 @simple_layout('layouts/push-tag-github.yaml', driver='github')
95 def test_push_event(self):
96 self.executor_server.hold_jobs_in_build = True
97
98 old_sha = random_sha1()
99 new_sha = random_sha1()
100 self.fake_github.emitEvent(
101 self.fake_github.getPushEvent('org/project', 'refs/heads/master',
102 old_sha, new_sha))
103 self.waitUntilSettled()
104
105 build_params = self.builds[0].parameters
106 self.assertEqual('refs/heads/master', build_params['ZUUL_REF'])
107 self.assertEqual(old_sha, build_params['ZUUL_OLDREV'])
108 self.assertEqual(new_sha, build_params['ZUUL_NEWREV'])
109
110 self.executor_server.hold_jobs_in_build = False
111 self.executor_server.release()
112 self.waitUntilSettled()
113
114 self.assertEqual('SUCCESS',
115 self.getJobFromHistory('project-post').result)
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200116
Jan Hruban16ad31f2015-11-07 14:39:07 +0100117 @simple_layout('layouts/labeling-github.yaml', driver='github')
118 def test_labels(self):
Jan Hruban37615e52015-11-19 14:30:49 +0100119 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100120 self.fake_github.emitEvent(A.addLabel('test'))
121 self.waitUntilSettled()
122 self.assertEqual(1, len(self.history))
123 self.assertEqual('project-labels', self.history[0].name)
124 self.assertEqual(['tests passed'], A.labels)
125
126 # test label removed
Jan Hruban37615e52015-11-19 14:30:49 +0100127 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100128 B.addLabel('do not test')
129 self.fake_github.emitEvent(B.removeLabel('do not test'))
130 self.waitUntilSettled()
131 self.assertEqual(2, len(self.history))
132 self.assertEqual('project-labels', self.history[1].name)
133 self.assertEqual(['tests passed'], B.labels)
134
135 # test unmatched label
Jan Hruban37615e52015-11-19 14:30:49 +0100136 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100137 self.fake_github.emitEvent(C.addLabel('other label'))
138 self.waitUntilSettled()
139 self.assertEqual(2, len(self.history))
140 self.assertEqual(['other label'], C.labels)
141
Jan Hruban324ca5b2015-11-05 19:28:54 +0100142 @simple_layout('layouts/dequeue-github.yaml', driver='github')
143 def test_dequeue_pull_synchronized(self):
144 self.executor_server.hold_jobs_in_build = True
145
Jan Hruban37615e52015-11-19 14:30:49 +0100146 A = self.fake_github.openFakePullRequest(
147 'org/one-job-project', 'master', 'A')
148 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100149 self.waitUntilSettled()
150
151 # event update stamp has resolution one second, wait so the latter
152 # one has newer timestamp
153 time.sleep(1)
Jan Hruban37615e52015-11-19 14:30:49 +0100154 A.addCommit()
155 self.fake_github.emitEvent(A.getPullRequestSynchronizeEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100156 self.waitUntilSettled()
157
158 self.executor_server.hold_jobs_in_build = False
159 self.executor_server.release()
160 self.waitUntilSettled()
161
162 self.assertEqual(2, len(self.history))
163 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
164
165 @simple_layout('layouts/dequeue-github.yaml', driver='github')
166 def test_dequeue_pull_abandoned(self):
167 self.executor_server.hold_jobs_in_build = True
168
Jan Hruban37615e52015-11-19 14:30:49 +0100169 A = self.fake_github.openFakePullRequest(
170 'org/one-job-project', 'master', 'A')
171 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100172 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100173 self.fake_github.emitEvent(A.getPullRequestClosedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100174 self.waitUntilSettled()
175
176 self.executor_server.hold_jobs_in_build = False
177 self.executor_server.release()
178 self.waitUntilSettled()
179
180 self.assertEqual(1, len(self.history))
181 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
182
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200183 @simple_layout('layouts/basic-github.yaml', driver='github')
184 def test_git_https_url(self):
185 """Test that git_ssh option gives git url with ssh"""
186 url = self.fake_github.real_getGitUrl('org/project')
187 self.assertEqual('https://github.com/org/project', url)
188
189 @simple_layout('layouts/basic-github.yaml', driver='github')
190 def test_git_ssh_url(self):
191 """Test that git_ssh option gives git url with ssh"""
192 url = self.fake_github_ssh.real_getGitUrl('org/project')
193 self.assertEqual('ssh://git@github.com/org/project.git', url)
Jan Hrubane252a732017-01-03 15:03:09 +0100194
195 @simple_layout('layouts/reporting-github.yaml', driver='github')
196 def test_reporting(self):
197 # pipeline reports pull status both on start and success
198 self.executor_server.hold_jobs_in_build = True
Jan Hruban37615e52015-11-19 14:30:49 +0100199 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
200 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hrubane252a732017-01-03 15:03:09 +0100201 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100202 self.assertIn('check', A.statuses)
203 check_status = A.statuses['check']
Jan Hrubane252a732017-01-03 15:03:09 +0100204 self.assertEqual('Standard check', check_status['description'])
205 self.assertEqual('pending', check_status['state'])
206 self.assertEqual('http://zuul.example.com/status', check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100207 self.assertEqual(0, len(A.comments))
Jan Hrubane252a732017-01-03 15:03:09 +0100208
209 self.executor_server.hold_jobs_in_build = False
210 self.executor_server.release()
211 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100212 check_status = A.statuses['check']
Jan Hrubane252a732017-01-03 15:03:09 +0100213 self.assertEqual('Standard check', check_status['description'])
214 self.assertEqual('success', check_status['state'])
215 self.assertEqual('http://zuul.example.com/status', check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100216 self.assertEqual(1, len(A.comments))
217 self.assertThat(A.comments[0],
Jan Hrubane252a732017-01-03 15:03:09 +0100218 MatchesRegex('.*Build succeeded.*', re.DOTALL))
219
220 # pipeline does not report any status but does comment
221 self.executor_server.hold_jobs_in_build = True
222 self.fake_github.emitEvent(
Jan Hruban37615e52015-11-19 14:30:49 +0100223 A.getCommentAddedEvent('reporting check'))
Jan Hrubane252a732017-01-03 15:03:09 +0100224 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100225 self.assertNotIn('reporting', A.statuses)
Jan Hrubane252a732017-01-03 15:03:09 +0100226 # comments increased by one for the start message
Jan Hruban37615e52015-11-19 14:30:49 +0100227 self.assertEqual(2, len(A.comments))
228 self.assertThat(A.comments[1],
Jan Hrubane252a732017-01-03 15:03:09 +0100229 MatchesRegex('.*Starting reporting jobs.*', re.DOTALL))
230 self.executor_server.hold_jobs_in_build = False
231 self.executor_server.release()
232 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100233 self.assertNotIn('reporting', A.statuses)
234 self.assertEqual(2, len(A.comments))
Jan Hruban49bff072015-11-03 11:45:46 +0100235
236 @simple_layout('layouts/merging-github.yaml', driver='github')
237 def test_report_pull_merge(self):
238 # pipeline merges the pull request on success
Jan Hruban37615e52015-11-19 14:30:49 +0100239 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
Jan Hruban49bff072015-11-03 11:45:46 +0100240 self.fake_github.emitEvent(A.getCommentAddedEvent('merge me'))
241 self.waitUntilSettled()
242 self.assertTrue(A.is_merged)
243
244 # pipeline merges the pull request on success after failure
245 self.fake_github.merge_failure = True
Jan Hruban37615e52015-11-19 14:30:49 +0100246 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban49bff072015-11-03 11:45:46 +0100247 self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
248 self.waitUntilSettled()
249 self.assertFalse(B.is_merged)
250 self.fake_github.merge_failure = False
251
252 # pipeline merges the pull request on second run of merge
253 # first merge failed on 405 Method Not Allowed error
254 self.fake_github.merge_not_allowed_count = 1
Jan Hruban37615e52015-11-19 14:30:49 +0100255 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban49bff072015-11-03 11:45:46 +0100256 self.fake_github.emitEvent(C.getCommentAddedEvent('merge me'))
257 self.waitUntilSettled()
258 self.assertTrue(C.is_merged)
259
260 # pipeline does not merge the pull request
261 # merge failed on 405 Method Not Allowed error - twice
262 self.fake_github.merge_not_allowed_count = 2
Jan Hruban37615e52015-11-19 14:30:49 +0100263 D = self.fake_github.openFakePullRequest('org/project', 'master', 'D')
Jan Hruban49bff072015-11-03 11:45:46 +0100264 self.fake_github.emitEvent(D.getCommentAddedEvent('merge me'))
265 self.waitUntilSettled()
266 self.assertFalse(D.is_merged)
Jan Hruban37615e52015-11-19 14:30:49 +0100267
268 @simple_layout('layouts/dependent-github.yaml', driver='github')
269 def test_parallel_changes(self):
270 "Test that changes are tested in parallel and merged in series"
271
272 self.executor_server.hold_jobs_in_build = True
273 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
274 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
275 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
276
277 self.fake_github.emitEvent(A.addLabel('merge'))
278 self.fake_github.emitEvent(B.addLabel('merge'))
279 self.fake_github.emitEvent(C.addLabel('merge'))
280
281 self.waitUntilSettled()
282 self.assertEqual(len(self.builds), 1)
283 self.assertEqual(self.builds[0].name, 'project-merge')
284 self.assertTrue(self.builds[0].hasChanges(A))
285
286 self.executor_server.release('.*-merge')
287 self.waitUntilSettled()
288 self.assertEqual(len(self.builds), 3)
289 self.assertEqual(self.builds[0].name, 'project-test1')
290 self.assertTrue(self.builds[0].hasChanges(A))
291 self.assertEqual(self.builds[1].name, 'project-test2')
292 self.assertTrue(self.builds[1].hasChanges(A))
293 self.assertEqual(self.builds[2].name, 'project-merge')
294 self.assertTrue(self.builds[2].hasChanges(A, B))
295
296 self.executor_server.release('.*-merge')
297 self.waitUntilSettled()
298 self.assertEqual(len(self.builds), 5)
299 self.assertEqual(self.builds[0].name, 'project-test1')
300 self.assertTrue(self.builds[0].hasChanges(A))
301 self.assertEqual(self.builds[1].name, 'project-test2')
302 self.assertTrue(self.builds[1].hasChanges(A))
303
304 self.assertEqual(self.builds[2].name, 'project-test1')
305 self.assertTrue(self.builds[2].hasChanges(A))
306 self.assertEqual(self.builds[3].name, 'project-test2')
307 self.assertTrue(self.builds[3].hasChanges(A, B))
308
309 self.assertEqual(self.builds[4].name, 'project-merge')
310 self.assertTrue(self.builds[4].hasChanges(A, B, C))
311
312 self.executor_server.release('.*-merge')
313 self.waitUntilSettled()
314 self.assertEqual(len(self.builds), 6)
315 self.assertEqual(self.builds[0].name, 'project-test1')
316 self.assertTrue(self.builds[0].hasChanges(A))
317 self.assertEqual(self.builds[1].name, 'project-test2')
318 self.assertTrue(self.builds[1].hasChanges(A))
319
320 self.assertEqual(self.builds[2].name, 'project-test1')
321 self.assertTrue(self.builds[2].hasChanges(A, B))
322 self.assertEqual(self.builds[3].name, 'project-test2')
323 self.assertTrue(self.builds[3].hasChanges(A, B))
324
325 self.assertEqual(self.builds[4].name, 'project-test1')
326 self.assertTrue(self.builds[4].hasChanges(A, B, C))
327 self.assertEqual(self.builds[5].name, 'project-test2')
328 self.assertTrue(self.builds[5].hasChanges(A, B, C))
329
330 all_builds = self.builds[:]
331 self.release(all_builds[2])
332 self.release(all_builds[3])
333 self.waitUntilSettled()
334 self.assertFalse(A.is_merged)
335 self.assertFalse(B.is_merged)
336 self.assertFalse(C.is_merged)
337
338 self.release(all_builds[0])
339 self.release(all_builds[1])
340 self.waitUntilSettled()
341 self.assertTrue(A.is_merged)
342 self.assertTrue(B.is_merged)
343 self.assertFalse(C.is_merged)
344
345 self.executor_server.hold_jobs_in_build = False
346 self.executor_server.release()
347 self.waitUntilSettled()
348 self.assertEqual(len(self.builds), 0)
349 self.assertEqual(len(self.history), 9)
350 self.assertTrue(C.is_merged)
351
352 self.assertNotIn('merge', A.labels)
353 self.assertNotIn('merge', B.labels)
354 self.assertNotIn('merge', C.labels)
355
356 @simple_layout('layouts/dependent-github.yaml', driver='github')
357 def test_failed_changes(self):
358 "Test that a change behind a failed change is retested"
359 self.executor_server.hold_jobs_in_build = True
360
361 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
362 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
363
364 self.executor_server.failJob('project-test1', A)
365
366 self.fake_github.emitEvent(A.addLabel('merge'))
367 self.fake_github.emitEvent(B.addLabel('merge'))
368 self.waitUntilSettled()
369
370 self.executor_server.release('.*-merge')
371 self.waitUntilSettled()
372
373 self.executor_server.hold_jobs_in_build = False
374 self.executor_server.release()
375
376 self.waitUntilSettled()
377 # It's certain that the merge job for change 2 will run, but
378 # the test1 and test2 jobs may or may not run.
379 self.assertTrue(len(self.history) > 6)
380 self.assertFalse(A.is_merged)
381 self.assertTrue(B.is_merged)
382 self.assertNotIn('merge', A.labels)
383 self.assertNotIn('merge', B.labels)
384
385 @simple_layout('layouts/dependent-github.yaml', driver='github')
386 def test_failed_change_at_head(self):
387 "Test that if a change at the head fails, jobs behind it are canceled"
388
389 self.executor_server.hold_jobs_in_build = True
390 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
391 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
392 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
393
394 self.executor_server.failJob('project-test1', A)
395
396 self.fake_github.emitEvent(A.addLabel('merge'))
397 self.fake_github.emitEvent(B.addLabel('merge'))
398 self.fake_github.emitEvent(C.addLabel('merge'))
399
400 self.waitUntilSettled()
401
402 self.assertEqual(len(self.builds), 1)
403 self.assertEqual(self.builds[0].name, 'project-merge')
404 self.assertTrue(self.builds[0].hasChanges(A))
405
406 self.executor_server.release('.*-merge')
407 self.waitUntilSettled()
408 self.executor_server.release('.*-merge')
409 self.waitUntilSettled()
410 self.executor_server.release('.*-merge')
411 self.waitUntilSettled()
412
413 self.assertEqual(len(self.builds), 6)
414 self.assertEqual(self.builds[0].name, 'project-test1')
415 self.assertEqual(self.builds[1].name, 'project-test2')
416 self.assertEqual(self.builds[2].name, 'project-test1')
417 self.assertEqual(self.builds[3].name, 'project-test2')
418 self.assertEqual(self.builds[4].name, 'project-test1')
419 self.assertEqual(self.builds[5].name, 'project-test2')
420
421 self.release(self.builds[0])
422 self.waitUntilSettled()
423
424 # project-test2, project-merge for B
425 self.assertEqual(len(self.builds), 2)
426 self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 4)
427
428 self.executor_server.hold_jobs_in_build = False
429 self.executor_server.release()
430 self.waitUntilSettled()
431
432 self.assertEqual(len(self.builds), 0)
433 self.assertEqual(len(self.history), 15)
434 self.assertFalse(A.is_merged)
435 self.assertTrue(B.is_merged)
436 self.assertTrue(C.is_merged)
437 self.assertNotIn('merge', A.labels)
438 self.assertNotIn('merge', B.labels)
439 self.assertNotIn('merge', C.labels)