blob: 7267b832a600217197b48cccf450e1f0a2a70e57 [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))
Jan Hruband4edee82015-12-16 12:49:51 +010057 self.assertEqual(2, len(self.history))
58
59 # test_pull_unmatched_branch_event(self):
60 self.create_branch('org/project', 'unmatched_branch')
61 B = self.fake_github.openFakePullRequest(
62 'org/project', 'unmatched_branch', 'B')
63 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
64 self.waitUntilSettled()
65
66 self.assertEqual(2, len(self.history))
Wayne1a78c612015-06-11 17:14:13 -070067
Jan Hrubanc7ab1602015-10-14 15:29:33 +020068 @simple_layout('layouts/basic-github.yaml', driver='github')
69 def test_comment_event(self):
Jan Hruban37615e52015-11-19 14:30:49 +010070 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
71 self.fake_github.emitEvent(A.getCommentAddedEvent('test me'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020072 self.waitUntilSettled()
73 self.assertEqual(2, len(self.history))
74
75 # Test an unmatched comment, history should remain the same
Jan Hruban37615e52015-11-19 14:30:49 +010076 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
77 self.fake_github.emitEvent(B.getCommentAddedEvent('casual comment'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020078 self.waitUntilSettled()
79 self.assertEqual(2, len(self.history))
80
Wayne1a78c612015-06-11 17:14:13 -070081 @simple_layout('layouts/push-tag-github.yaml', driver='github')
82 def test_tag_event(self):
83 self.executor_server.hold_jobs_in_build = True
84
85 sha = random_sha1()
86 self.fake_github.emitEvent(
87 self.fake_github.getPushEvent('org/project', 'refs/tags/newtag',
88 new_rev=sha))
89 self.waitUntilSettled()
90
91 build_params = self.builds[0].parameters
92 self.assertEqual('refs/tags/newtag', build_params['ZUUL_REF'])
93 self.assertEqual('00000000000000000000000000000000',
94 build_params['ZUUL_OLDREV'])
95 self.assertEqual(sha, build_params['ZUUL_NEWREV'])
96
97 self.executor_server.hold_jobs_in_build = False
98 self.executor_server.release()
99 self.waitUntilSettled()
100
101 self.assertEqual('SUCCESS',
102 self.getJobFromHistory('project-tag').result)
103
104 @simple_layout('layouts/push-tag-github.yaml', driver='github')
105 def test_push_event(self):
106 self.executor_server.hold_jobs_in_build = True
107
108 old_sha = random_sha1()
109 new_sha = random_sha1()
110 self.fake_github.emitEvent(
111 self.fake_github.getPushEvent('org/project', 'refs/heads/master',
112 old_sha, new_sha))
113 self.waitUntilSettled()
114
115 build_params = self.builds[0].parameters
116 self.assertEqual('refs/heads/master', build_params['ZUUL_REF'])
117 self.assertEqual(old_sha, build_params['ZUUL_OLDREV'])
118 self.assertEqual(new_sha, build_params['ZUUL_NEWREV'])
119
120 self.executor_server.hold_jobs_in_build = False
121 self.executor_server.release()
122 self.waitUntilSettled()
123
124 self.assertEqual('SUCCESS',
125 self.getJobFromHistory('project-post').result)
Jan Hruband4edee82015-12-16 12:49:51 +0100126 self.assertEqual(1, len(self.history))
127
128 # test unmatched push event
129 old_sha = random_sha1()
130 new_sha = random_sha1()
131 self.fake_github.emitEvent(
132 self.fake_github.getPushEvent('org/project',
133 'refs/heads/unmatched_branch',
134 old_sha, new_sha))
135 self.waitUntilSettled()
136
137 self.assertEqual(1, len(self.history))
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200138
Jan Hruban16ad31f2015-11-07 14:39:07 +0100139 @simple_layout('layouts/labeling-github.yaml', driver='github')
140 def test_labels(self):
Jan Hruban37615e52015-11-19 14:30:49 +0100141 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100142 self.fake_github.emitEvent(A.addLabel('test'))
143 self.waitUntilSettled()
144 self.assertEqual(1, len(self.history))
145 self.assertEqual('project-labels', self.history[0].name)
146 self.assertEqual(['tests passed'], A.labels)
147
148 # test label removed
Jan Hruban37615e52015-11-19 14:30:49 +0100149 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100150 B.addLabel('do not test')
151 self.fake_github.emitEvent(B.removeLabel('do not test'))
152 self.waitUntilSettled()
153 self.assertEqual(2, len(self.history))
154 self.assertEqual('project-labels', self.history[1].name)
155 self.assertEqual(['tests passed'], B.labels)
156
157 # test unmatched label
Jan Hruban37615e52015-11-19 14:30:49 +0100158 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100159 self.fake_github.emitEvent(C.addLabel('other label'))
160 self.waitUntilSettled()
161 self.assertEqual(2, len(self.history))
162 self.assertEqual(['other label'], C.labels)
163
Jan Hruban324ca5b2015-11-05 19:28:54 +0100164 @simple_layout('layouts/dequeue-github.yaml', driver='github')
165 def test_dequeue_pull_synchronized(self):
166 self.executor_server.hold_jobs_in_build = True
167
Jan Hruban37615e52015-11-19 14:30:49 +0100168 A = self.fake_github.openFakePullRequest(
169 'org/one-job-project', 'master', 'A')
170 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100171 self.waitUntilSettled()
172
173 # event update stamp has resolution one second, wait so the latter
174 # one has newer timestamp
175 time.sleep(1)
Jan Hruban37615e52015-11-19 14:30:49 +0100176 A.addCommit()
177 self.fake_github.emitEvent(A.getPullRequestSynchronizeEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100178 self.waitUntilSettled()
179
180 self.executor_server.hold_jobs_in_build = False
181 self.executor_server.release()
182 self.waitUntilSettled()
183
184 self.assertEqual(2, len(self.history))
185 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
186
187 @simple_layout('layouts/dequeue-github.yaml', driver='github')
188 def test_dequeue_pull_abandoned(self):
189 self.executor_server.hold_jobs_in_build = True
190
Jan Hruban37615e52015-11-19 14:30:49 +0100191 A = self.fake_github.openFakePullRequest(
192 'org/one-job-project', 'master', 'A')
193 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100194 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100195 self.fake_github.emitEvent(A.getPullRequestClosedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100196 self.waitUntilSettled()
197
198 self.executor_server.hold_jobs_in_build = False
199 self.executor_server.release()
200 self.waitUntilSettled()
201
202 self.assertEqual(1, len(self.history))
203 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
204
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200205 @simple_layout('layouts/basic-github.yaml', driver='github')
206 def test_git_https_url(self):
207 """Test that git_ssh option gives git url with ssh"""
208 url = self.fake_github.real_getGitUrl('org/project')
209 self.assertEqual('https://github.com/org/project', url)
210
211 @simple_layout('layouts/basic-github.yaml', driver='github')
212 def test_git_ssh_url(self):
213 """Test that git_ssh option gives git url with ssh"""
214 url = self.fake_github_ssh.real_getGitUrl('org/project')
215 self.assertEqual('ssh://git@github.com/org/project.git', url)
Jan Hrubane252a732017-01-03 15:03:09 +0100216
217 @simple_layout('layouts/reporting-github.yaml', driver='github')
218 def test_reporting(self):
219 # pipeline reports pull status both on start and success
220 self.executor_server.hold_jobs_in_build = True
Jan Hruban37615e52015-11-19 14:30:49 +0100221 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
222 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hrubane252a732017-01-03 15:03:09 +0100223 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100224 self.assertIn('check', A.statuses)
225 check_status = A.statuses['check']
Jan Hrubane252a732017-01-03 15:03:09 +0100226 self.assertEqual('Standard check', check_status['description'])
227 self.assertEqual('pending', check_status['state'])
228 self.assertEqual('http://zuul.example.com/status', check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100229 self.assertEqual(0, len(A.comments))
Jan Hrubane252a732017-01-03 15:03:09 +0100230
231 self.executor_server.hold_jobs_in_build = False
232 self.executor_server.release()
233 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100234 check_status = A.statuses['check']
Jan Hrubane252a732017-01-03 15:03:09 +0100235 self.assertEqual('Standard check', check_status['description'])
236 self.assertEqual('success', check_status['state'])
237 self.assertEqual('http://zuul.example.com/status', check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100238 self.assertEqual(1, len(A.comments))
239 self.assertThat(A.comments[0],
Jan Hrubane252a732017-01-03 15:03:09 +0100240 MatchesRegex('.*Build succeeded.*', re.DOTALL))
241
242 # pipeline does not report any status but does comment
243 self.executor_server.hold_jobs_in_build = True
244 self.fake_github.emitEvent(
Jan Hruban37615e52015-11-19 14:30:49 +0100245 A.getCommentAddedEvent('reporting check'))
Jan Hrubane252a732017-01-03 15:03:09 +0100246 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100247 self.assertNotIn('reporting', A.statuses)
Jan Hrubane252a732017-01-03 15:03:09 +0100248 # comments increased by one for the start message
Jan Hruban37615e52015-11-19 14:30:49 +0100249 self.assertEqual(2, len(A.comments))
250 self.assertThat(A.comments[1],
Jan Hrubane252a732017-01-03 15:03:09 +0100251 MatchesRegex('.*Starting reporting jobs.*', re.DOTALL))
252 self.executor_server.hold_jobs_in_build = False
253 self.executor_server.release()
254 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100255 self.assertNotIn('reporting', A.statuses)
256 self.assertEqual(2, len(A.comments))
Jan Hruban49bff072015-11-03 11:45:46 +0100257
258 @simple_layout('layouts/merging-github.yaml', driver='github')
259 def test_report_pull_merge(self):
260 # pipeline merges the pull request on success
Jan Hruban3b415922016-02-03 13:10:22 +0100261 A = self.fake_github.openFakePullRequest('org/project', 'master',
262 'PR title')
Jan Hruban49bff072015-11-03 11:45:46 +0100263 self.fake_github.emitEvent(A.getCommentAddedEvent('merge me'))
264 self.waitUntilSettled()
265 self.assertTrue(A.is_merged)
Jan Hruban3b415922016-02-03 13:10:22 +0100266 self.assertThat(A.merge_message,
267 MatchesRegex('.*PR title.*Reviewed-by.*', re.DOTALL))
Jan Hruban49bff072015-11-03 11:45:46 +0100268
269 # pipeline merges the pull request on success after failure
270 self.fake_github.merge_failure = True
Jan Hruban37615e52015-11-19 14:30:49 +0100271 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban49bff072015-11-03 11:45:46 +0100272 self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
273 self.waitUntilSettled()
274 self.assertFalse(B.is_merged)
275 self.fake_github.merge_failure = False
276
277 # pipeline merges the pull request on second run of merge
278 # first merge failed on 405 Method Not Allowed error
279 self.fake_github.merge_not_allowed_count = 1
Jan Hruban37615e52015-11-19 14:30:49 +0100280 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban49bff072015-11-03 11:45:46 +0100281 self.fake_github.emitEvent(C.getCommentAddedEvent('merge me'))
282 self.waitUntilSettled()
283 self.assertTrue(C.is_merged)
284
285 # pipeline does not merge the pull request
286 # merge failed on 405 Method Not Allowed error - twice
287 self.fake_github.merge_not_allowed_count = 2
Jan Hruban37615e52015-11-19 14:30:49 +0100288 D = self.fake_github.openFakePullRequest('org/project', 'master', 'D')
Jan Hruban49bff072015-11-03 11:45:46 +0100289 self.fake_github.emitEvent(D.getCommentAddedEvent('merge me'))
290 self.waitUntilSettled()
291 self.assertFalse(D.is_merged)
Jan Hruban37615e52015-11-19 14:30:49 +0100292
293 @simple_layout('layouts/dependent-github.yaml', driver='github')
294 def test_parallel_changes(self):
295 "Test that changes are tested in parallel and merged in series"
296
297 self.executor_server.hold_jobs_in_build = True
298 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
299 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
300 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
301
302 self.fake_github.emitEvent(A.addLabel('merge'))
303 self.fake_github.emitEvent(B.addLabel('merge'))
304 self.fake_github.emitEvent(C.addLabel('merge'))
305
306 self.waitUntilSettled()
307 self.assertEqual(len(self.builds), 1)
308 self.assertEqual(self.builds[0].name, 'project-merge')
309 self.assertTrue(self.builds[0].hasChanges(A))
310
311 self.executor_server.release('.*-merge')
312 self.waitUntilSettled()
313 self.assertEqual(len(self.builds), 3)
314 self.assertEqual(self.builds[0].name, 'project-test1')
315 self.assertTrue(self.builds[0].hasChanges(A))
316 self.assertEqual(self.builds[1].name, 'project-test2')
317 self.assertTrue(self.builds[1].hasChanges(A))
318 self.assertEqual(self.builds[2].name, 'project-merge')
319 self.assertTrue(self.builds[2].hasChanges(A, B))
320
321 self.executor_server.release('.*-merge')
322 self.waitUntilSettled()
323 self.assertEqual(len(self.builds), 5)
324 self.assertEqual(self.builds[0].name, 'project-test1')
325 self.assertTrue(self.builds[0].hasChanges(A))
326 self.assertEqual(self.builds[1].name, 'project-test2')
327 self.assertTrue(self.builds[1].hasChanges(A))
328
329 self.assertEqual(self.builds[2].name, 'project-test1')
330 self.assertTrue(self.builds[2].hasChanges(A))
331 self.assertEqual(self.builds[3].name, 'project-test2')
332 self.assertTrue(self.builds[3].hasChanges(A, B))
333
334 self.assertEqual(self.builds[4].name, 'project-merge')
335 self.assertTrue(self.builds[4].hasChanges(A, B, C))
336
337 self.executor_server.release('.*-merge')
338 self.waitUntilSettled()
339 self.assertEqual(len(self.builds), 6)
340 self.assertEqual(self.builds[0].name, 'project-test1')
341 self.assertTrue(self.builds[0].hasChanges(A))
342 self.assertEqual(self.builds[1].name, 'project-test2')
343 self.assertTrue(self.builds[1].hasChanges(A))
344
345 self.assertEqual(self.builds[2].name, 'project-test1')
346 self.assertTrue(self.builds[2].hasChanges(A, B))
347 self.assertEqual(self.builds[3].name, 'project-test2')
348 self.assertTrue(self.builds[3].hasChanges(A, B))
349
350 self.assertEqual(self.builds[4].name, 'project-test1')
351 self.assertTrue(self.builds[4].hasChanges(A, B, C))
352 self.assertEqual(self.builds[5].name, 'project-test2')
353 self.assertTrue(self.builds[5].hasChanges(A, B, C))
354
355 all_builds = self.builds[:]
356 self.release(all_builds[2])
357 self.release(all_builds[3])
358 self.waitUntilSettled()
359 self.assertFalse(A.is_merged)
360 self.assertFalse(B.is_merged)
361 self.assertFalse(C.is_merged)
362
363 self.release(all_builds[0])
364 self.release(all_builds[1])
365 self.waitUntilSettled()
366 self.assertTrue(A.is_merged)
367 self.assertTrue(B.is_merged)
368 self.assertFalse(C.is_merged)
369
370 self.executor_server.hold_jobs_in_build = False
371 self.executor_server.release()
372 self.waitUntilSettled()
373 self.assertEqual(len(self.builds), 0)
374 self.assertEqual(len(self.history), 9)
375 self.assertTrue(C.is_merged)
376
377 self.assertNotIn('merge', A.labels)
378 self.assertNotIn('merge', B.labels)
379 self.assertNotIn('merge', C.labels)
380
381 @simple_layout('layouts/dependent-github.yaml', driver='github')
382 def test_failed_changes(self):
383 "Test that a change behind a failed change is retested"
384 self.executor_server.hold_jobs_in_build = True
385
386 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
387 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
388
389 self.executor_server.failJob('project-test1', A)
390
391 self.fake_github.emitEvent(A.addLabel('merge'))
392 self.fake_github.emitEvent(B.addLabel('merge'))
393 self.waitUntilSettled()
394
395 self.executor_server.release('.*-merge')
396 self.waitUntilSettled()
397
398 self.executor_server.hold_jobs_in_build = False
399 self.executor_server.release()
400
401 self.waitUntilSettled()
402 # It's certain that the merge job for change 2 will run, but
403 # the test1 and test2 jobs may or may not run.
404 self.assertTrue(len(self.history) > 6)
405 self.assertFalse(A.is_merged)
406 self.assertTrue(B.is_merged)
407 self.assertNotIn('merge', A.labels)
408 self.assertNotIn('merge', B.labels)
409
410 @simple_layout('layouts/dependent-github.yaml', driver='github')
411 def test_failed_change_at_head(self):
412 "Test that if a change at the head fails, jobs behind it are canceled"
413
414 self.executor_server.hold_jobs_in_build = True
415 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
416 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
417 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
418
419 self.executor_server.failJob('project-test1', A)
420
421 self.fake_github.emitEvent(A.addLabel('merge'))
422 self.fake_github.emitEvent(B.addLabel('merge'))
423 self.fake_github.emitEvent(C.addLabel('merge'))
424
425 self.waitUntilSettled()
426
427 self.assertEqual(len(self.builds), 1)
428 self.assertEqual(self.builds[0].name, 'project-merge')
429 self.assertTrue(self.builds[0].hasChanges(A))
430
431 self.executor_server.release('.*-merge')
432 self.waitUntilSettled()
433 self.executor_server.release('.*-merge')
434 self.waitUntilSettled()
435 self.executor_server.release('.*-merge')
436 self.waitUntilSettled()
437
438 self.assertEqual(len(self.builds), 6)
439 self.assertEqual(self.builds[0].name, 'project-test1')
440 self.assertEqual(self.builds[1].name, 'project-test2')
441 self.assertEqual(self.builds[2].name, 'project-test1')
442 self.assertEqual(self.builds[3].name, 'project-test2')
443 self.assertEqual(self.builds[4].name, 'project-test1')
444 self.assertEqual(self.builds[5].name, 'project-test2')
445
446 self.release(self.builds[0])
447 self.waitUntilSettled()
448
449 # project-test2, project-merge for B
450 self.assertEqual(len(self.builds), 2)
451 self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 4)
452
453 self.executor_server.hold_jobs_in_build = False
454 self.executor_server.release()
455 self.waitUntilSettled()
456
457 self.assertEqual(len(self.builds), 0)
458 self.assertEqual(len(self.history), 15)
459 self.assertFalse(A.is_merged)
460 self.assertTrue(B.is_merged)
461 self.assertTrue(C.is_merged)
462 self.assertNotIn('merge', A.labels)
463 self.assertNotIn('merge', B.labels)
464 self.assertNotIn('merge', C.labels)