blob: 7d68989ab3e6313dc1961deee482e65c1773aea3 [file] [log] [blame]
James E. Blair54145e02018-01-10 16:07:41 -08001#!/usr/bin/env python
2
3# Copyright 2012 Hewlett-Packard Development Company, L.P.
4# Copyright 2018 Red Hat, Inc.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18from tests.base import (
19 ZuulTestCase,
20)
21
22
23class TestGerritToGithubCRD(ZuulTestCase):
24 config_file = 'zuul-gerrit-github.conf'
25 tenant_config_file = 'config/cross-source/main.yaml'
26
27 def test_crd_gate(self):
28 "Test cross-repo dependencies"
29 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
30 B = self.fake_github.openFakePullRequest('github/project2', 'master',
31 'B')
32
33 A.addApproval('Code-Review', 2)
34
35 AM2 = self.fake_gerrit.addFakeChange('gerrit/project1', 'master',
36 'AM2')
37 AM1 = self.fake_gerrit.addFakeChange('gerrit/project1', 'master',
38 'AM1')
39 AM2.setMerged()
40 AM1.setMerged()
41
42 # A -> AM1 -> AM2
43 # A Depends-On: B
44 # M2 is here to make sure it is never queried. If it is, it
45 # means zuul is walking down the entire history of merged
46 # changes.
47
48 A.setDependsOn(AM1, 1)
49 AM1.setDependsOn(AM2, 1)
50
51 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
52 A.subject, B.url)
53
54 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
55 self.waitUntilSettled()
56
57 self.assertEqual(A.data['status'], 'NEW')
58 self.assertFalse(B.is_merged)
59
60 for connection in self.connections.connections.values():
61 connection.maintainCache([])
62
63 self.executor_server.hold_jobs_in_build = True
64 B.addLabel('approved')
65 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
66 self.waitUntilSettled()
67
68 self.executor_server.release('.*-merge')
69 self.waitUntilSettled()
70 self.executor_server.release('.*-merge')
71 self.waitUntilSettled()
72 self.executor_server.hold_jobs_in_build = False
73 self.executor_server.release()
74 self.waitUntilSettled()
75
76 self.assertEqual(AM2.queried, 0)
77 self.assertEqual(A.data['status'], 'MERGED')
78 self.assertTrue(B.is_merged)
79 self.assertEqual(A.reported, 2)
80 self.assertEqual(len(B.comments), 2)
81
82 changes = self.getJobFromHistory(
83 'project-merge', 'gerrit/project1').changes
84 self.assertEqual(changes, '1,%s 1,1' % B.head_sha)
85
86 def test_crd_branch(self):
87 "Test cross-repo dependencies in multiple branches"
88
89 self.create_branch('github/project2', 'mp')
90 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
91 B = self.fake_github.openFakePullRequest('github/project2', 'master',
92 'B')
93 C1 = self.fake_github.openFakePullRequest('github/project2', 'mp',
94 'C1')
95
96 A.addApproval('Code-Review', 2)
97
98 # A Depends-On: B+C1
99 A.data['commitMessage'] = '%s\n\nDepends-On: %s\nDepends-On: %s\n' % (
100 A.subject, B.url, C1.url)
101
102 self.executor_server.hold_jobs_in_build = True
103 B.addLabel('approved')
104 C1.addLabel('approved')
105 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
106 self.waitUntilSettled()
107
108 self.executor_server.release('.*-merge')
109 self.waitUntilSettled()
110 self.executor_server.release('.*-merge')
111 self.waitUntilSettled()
112 self.executor_server.release('.*-merge')
113 self.waitUntilSettled()
114 self.executor_server.hold_jobs_in_build = False
115 self.executor_server.release()
116 self.waitUntilSettled()
117
118 self.assertEqual(A.data['status'], 'MERGED')
119 self.assertTrue(B.is_merged)
120 self.assertTrue(C1.is_merged)
121 self.assertEqual(A.reported, 2)
122 self.assertEqual(len(B.comments), 2)
123 self.assertEqual(len(C1.comments), 2)
124
125 changes = self.getJobFromHistory(
126 'project-merge', 'gerrit/project1').changes
127 self.assertEqual(changes, '1,%s 2,%s 1,1' %
128 (B.head_sha, C1.head_sha))
129
130 def test_crd_gate_reverse(self):
131 "Test reverse cross-repo dependencies"
132 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
133 B = self.fake_github.openFakePullRequest('github/project2', 'master',
134 'B')
135 A.addApproval('Code-Review', 2)
136
137 # A Depends-On: B
138
139 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
140 A.subject, B.url)
141
142 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
143 self.waitUntilSettled()
144
145 self.assertEqual(A.data['status'], 'NEW')
146 self.assertFalse(B.is_merged)
147
148 self.executor_server.hold_jobs_in_build = True
149 A.addApproval('Approved', 1)
150 self.fake_github.emitEvent(B.addLabel('approved'))
151 self.waitUntilSettled()
152
153 self.executor_server.release('.*-merge')
154 self.waitUntilSettled()
155 self.executor_server.release('.*-merge')
156 self.waitUntilSettled()
157 self.executor_server.hold_jobs_in_build = False
158 self.executor_server.release()
159 self.waitUntilSettled()
160
161 self.assertEqual(A.data['status'], 'MERGED')
162 self.assertTrue(B.is_merged)
163 self.assertEqual(A.reported, 2)
164 self.assertEqual(len(B.comments), 2)
165
166 changes = self.getJobFromHistory(
167 'project-merge', 'gerrit/project1').changes
168 self.assertEqual(changes, '1,%s 1,1' %
169 (B.head_sha,))
170
171 def test_crd_cycle(self):
172 "Test cross-repo dependency cycles"
173 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
174 msg = "Depends-On: %s" % (A.data['url'],)
175 B = self.fake_github.openFakePullRequest('github/project2', 'master',
176 'B', body=msg)
177 A.addApproval('Code-Review', 2)
178 B.addLabel('approved')
179
180 # A -> B -> A (via commit-depends)
181
182 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
183 A.subject, B.url)
184
185 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
186 self.waitUntilSettled()
187
188 self.assertEqual(A.reported, 0)
189 self.assertEqual(len(B.comments), 0)
190 self.assertEqual(A.data['status'], 'NEW')
191 self.assertFalse(B.is_merged)
192
193 def test_crd_gate_unknown(self):
194 "Test unknown projects in dependent pipeline"
195 self.init_repo("github/unknown", tag='init')
196 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
197 B = self.fake_github.openFakePullRequest('github/unknown', 'master',
198 'B')
199 A.addApproval('Code-Review', 2)
200
201 # A Depends-On: B
202 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
203 A.subject, B.url)
204
205 event = B.addLabel('approved')
206 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
207 self.waitUntilSettled()
208
209 # Unknown projects cannot share a queue with any other
210 # since they don't have common jobs with any other (they have no jobs).
211 # Changes which depend on unknown project changes
212 # should not be processed in dependent pipeline
213 self.assertEqual(A.data['status'], 'NEW')
214 self.assertFalse(B.is_merged)
215 self.assertEqual(A.reported, 0)
216 self.assertEqual(len(B.comments), 0)
217 self.assertEqual(len(self.history), 0)
218
219 # Simulate change B being gated outside this layout Set the
220 # change merged before submitting the event so that when the
221 # event triggers a gerrit query to update the change, we get
222 # the information that it was merged.
223 B.setMerged('merged')
224 self.fake_github.emitEvent(event)
225 self.waitUntilSettled()
226 self.assertEqual(len(self.history), 0)
227
228 # Now that B is merged, A should be able to be enqueued and
229 # merged.
230 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
231 self.waitUntilSettled()
232
233 self.assertEqual(A.data['status'], 'MERGED')
234 self.assertEqual(A.reported, 2)
235 self.assertTrue(B.is_merged)
236 self.assertEqual(len(B.comments), 0)
237
238 def test_crd_check(self):
239 "Test cross-repo dependencies in independent pipelines"
240 self.executor_server.hold_jobs_in_build = True
241 self.gearman_server.hold_jobs_in_queue = True
242 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
243 B = self.fake_github.openFakePullRequest(
244 'github/project2', 'master', 'B')
245
246 # A Depends-On: B
247 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
248 A.subject, B.url)
249
250 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
251 self.waitUntilSettled()
252
253 self.gearman_server.hold_jobs_in_queue = False
254 self.gearman_server.release()
255 self.waitUntilSettled()
256
257 self.executor_server.release('.*-merge')
258 self.waitUntilSettled()
259
260 self.assertTrue(self.builds[0].hasChanges(A, B))
261
262 self.executor_server.hold_jobs_in_build = False
263 self.executor_server.release()
264 self.waitUntilSettled()
265
266 self.assertEqual(A.data['status'], 'NEW')
267 self.assertFalse(B.is_merged)
268 self.assertEqual(A.reported, 1)
269 self.assertEqual(len(B.comments), 0)
270
271 changes = self.getJobFromHistory(
272 'project-merge', 'gerrit/project1').changes
273 self.assertEqual(changes, '1,%s 1,1' %
274 (B.head_sha,))
275
276 tenant = self.sched.abide.tenants.get('tenant-one')
277 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
278
279 def test_crd_check_duplicate(self):
280 "Test duplicate check in independent pipelines"
281 self.executor_server.hold_jobs_in_build = True
282 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
283 B = self.fake_github.openFakePullRequest(
284 'github/project2', 'master', 'B')
285
286 # A Depends-On: B
287 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
288 A.subject, B.url)
289 tenant = self.sched.abide.tenants.get('tenant-one')
290 check_pipeline = tenant.layout.pipelines['check']
291
292 # Add two dependent changes...
293 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
294 self.waitUntilSettled()
295 self.assertEqual(len(check_pipeline.getAllItems()), 2)
296
297 # ...make sure the live one is not duplicated...
298 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
299 self.waitUntilSettled()
300 self.assertEqual(len(check_pipeline.getAllItems()), 2)
301
302 # ...but the non-live one is able to be.
303 self.fake_github.emitEvent(B.getPullRequestEditedEvent())
304 self.waitUntilSettled()
305 self.assertEqual(len(check_pipeline.getAllItems()), 3)
306
307 # Release jobs in order to avoid races with change A jobs
308 # finishing before change B jobs.
309 self.orderedRelease()
310 self.executor_server.hold_jobs_in_build = False
311 self.executor_server.release()
312 self.waitUntilSettled()
313
314 self.assertEqual(A.data['status'], 'NEW')
315 self.assertFalse(B.is_merged)
316 self.assertEqual(A.reported, 1)
317 self.assertEqual(len(B.comments), 1)
318
319 changes = self.getJobFromHistory(
320 'project-merge', 'gerrit/project1').changes
321 self.assertEqual(changes, '1,%s 1,1' %
322 (B.head_sha,))
323
324 changes = self.getJobFromHistory(
325 'project-merge', 'github/project2').changes
326 self.assertEqual(changes, '1,%s' %
327 (B.head_sha,))
328 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
329
330 self.assertIn('Build succeeded', A.messages[0])
331
332 def _test_crd_check_reconfiguration(self, project1, project2):
333 "Test cross-repo dependencies re-enqueued in independent pipelines"
334
335 self.gearman_server.hold_jobs_in_queue = True
336 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
337 B = self.fake_github.openFakePullRequest(
338 'github/project2', 'master', 'B')
339
340 # A Depends-On: B
341 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
342 A.subject, B.url)
343
344 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
345 self.waitUntilSettled()
346
347 self.sched.reconfigure(self.config)
348
349 # Make sure the items still share a change queue, and the
350 # first one is not live.
351 tenant = self.sched.abide.tenants.get('tenant-one')
352 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 1)
353 queue = tenant.layout.pipelines['check'].queues[0]
354 first_item = queue.queue[0]
355 for item in queue.queue:
356 self.assertEqual(item.queue, first_item.queue)
357 self.assertFalse(first_item.live)
358 self.assertTrue(queue.queue[1].live)
359
360 self.gearman_server.hold_jobs_in_queue = False
361 self.gearman_server.release()
362 self.waitUntilSettled()
363
364 self.assertEqual(A.data['status'], 'NEW')
365 self.assertFalse(B.is_merged)
366 self.assertEqual(A.reported, 1)
367 self.assertEqual(len(B.comments), 0)
368
369 changes = self.getJobFromHistory(
370 'project-merge', 'gerrit/project1').changes
371 self.assertEqual(changes, '1,%s 1,1' %
372 (B.head_sha,))
373 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
374
375 def test_crd_check_reconfiguration(self):
376 self._test_crd_check_reconfiguration('org/project1', 'org/project2')
377
378 def test_crd_undefined_project(self):
379 """Test that undefined projects in dependencies are handled for
380 independent pipelines"""
381 # It's a hack for fake github,
382 # as it implies repo creation upon the creation of any change
383 self.init_repo("github/unknown", tag='init')
384 self._test_crd_check_reconfiguration('gerrit/project1',
385 'github/unknown')
386
387 def test_crd_check_transitive(self):
388 "Test transitive cross-repo dependencies"
389 # Specifically, if A -> B -> C, and C gets a new patchset and
390 # A gets a new patchset, ensure the test of A,2 includes B,1
391 # and C,2 (not C,1 which would indicate stale data in the
392 # cache for B).
393 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
394 C = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'C')
395 # B Depends-On: C
396 msg = "Depends-On: %s" % (C.data['url'],)
397 B = self.fake_github.openFakePullRequest(
398 'github/project2', 'master', 'B', body=msg)
399
400 # A Depends-On: B
401 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
402 A.subject, B.url)
403
404 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
405 self.waitUntilSettled()
406 self.assertEqual(self.history[-1].changes, '2,1 1,%s 1,1' %
407 (B.head_sha,))
408
409 self.fake_github.emitEvent(B.getPullRequestEditedEvent())
410 self.waitUntilSettled()
411 self.assertEqual(self.history[-1].changes, '2,1 1,%s' %
412 (B.head_sha,))
413
414 self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1))
415 self.waitUntilSettled()
416 self.assertEqual(self.history[-1].changes, '2,1')
417
418 C.addPatchset()
419 self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(2))
420 self.waitUntilSettled()
421 self.assertEqual(self.history[-1].changes, '2,2')
422
423 A.addPatchset()
424 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(2))
425 self.waitUntilSettled()
426 self.assertEqual(self.history[-1].changes, '2,2 1,%s 1,2' %
427 (B.head_sha,))
428
429 def test_crd_check_unknown(self):
430 "Test unknown projects in independent pipeline"
431 self.init_repo("github/unknown", tag='init')
432 A = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'A')
433 B = self.fake_github.openFakePullRequest(
434 'github/unknown', 'master', 'B')
435 # A Depends-On: B
436 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
437 A.subject, B.url)
438
439 # Make sure zuul has seen an event on B.
440 self.fake_github.emitEvent(B.getPullRequestEditedEvent())
441 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
442 self.waitUntilSettled()
443
444 self.assertEqual(A.data['status'], 'NEW')
445 self.assertEqual(A.reported, 1)
446 self.assertFalse(B.is_merged)
447 self.assertEqual(len(B.comments), 0)
448
449 def test_crd_cycle_join(self):
450 "Test an updated change creates a cycle"
451 A = self.fake_github.openFakePullRequest(
452 'github/project2', 'master', 'A')
453
454 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
455 self.waitUntilSettled()
456 self.assertEqual(len(A.comments), 1)
457
458 # Create B->A
459 B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
460 B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
461 B.subject, A.url)
462 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
463 self.waitUntilSettled()
464
465 # Dep is there so zuul should have reported on B
466 self.assertEqual(B.reported, 1)
467
468 # Update A to add A->B (a cycle).
469 A.editBody('Depends-On: %s\n' % (B.data['url']))
470 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
471 self.waitUntilSettled()
472
473 # Dependency cycle injected so zuul should not have reported again on A
474 self.assertEqual(len(A.comments), 1)
475
476 # Now if we update B to remove the depends-on, everything
477 # should be okay. B; A->B
478
479 B.addPatchset()
480 B.data['commitMessage'] = '%s\n' % (B.subject,)
481 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
482 self.waitUntilSettled()
483
484 # Cycle was removed so now zuul should have reported again on A
485 self.assertEqual(len(A.comments), 2)
486
487 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(2))
488 self.waitUntilSettled()
489 self.assertEqual(B.reported, 2)
490
491
492class TestGithubToGerritCRD(ZuulTestCase):
493 config_file = 'zuul-gerrit-github.conf'
494 tenant_config_file = 'config/cross-source/main.yaml'
495
496 def test_crd_gate(self):
497 "Test cross-repo dependencies"
498 A = self.fake_github.openFakePullRequest('github/project2', 'master',
499 'A')
500 B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
501
502 B.addApproval('Code-Review', 2)
503
504 # A Depends-On: B
505 A.editBody('Depends-On: %s\n' % (B.data['url']))
506
507 event = A.addLabel('approved')
508 self.fake_github.emitEvent(event)
509 self.waitUntilSettled()
510
511 self.assertFalse(A.is_merged)
512 self.assertEqual(B.data['status'], 'NEW')
513
514 for connection in self.connections.connections.values():
515 connection.maintainCache([])
516
517 self.executor_server.hold_jobs_in_build = True
518 B.addApproval('Approved', 1)
519 self.fake_github.emitEvent(event)
520 self.waitUntilSettled()
521
522 self.executor_server.release('.*-merge')
523 self.waitUntilSettled()
524 self.executor_server.release('.*-merge')
525 self.waitUntilSettled()
526 self.executor_server.hold_jobs_in_build = False
527 self.executor_server.release()
528 self.waitUntilSettled()
529
530 self.assertTrue(A.is_merged)
531 self.assertEqual(B.data['status'], 'MERGED')
532 self.assertEqual(len(A.comments), 2)
533 self.assertEqual(B.reported, 2)
534
535 changes = self.getJobFromHistory(
536 'project-merge', 'github/project2').changes
537 self.assertEqual(changes, '1,1 1,%s' % A.head_sha)
538
539 def test_crd_branch(self):
540 "Test cross-repo dependencies in multiple branches"
541
542 self.create_branch('gerrit/project1', 'mp')
543 A = self.fake_github.openFakePullRequest('github/project2', 'master',
544 'A')
545 B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
546 C1 = self.fake_gerrit.addFakeChange('gerrit/project1', 'mp', 'C1')
547
548 B.addApproval('Code-Review', 2)
549 C1.addApproval('Code-Review', 2)
550
551 # A Depends-On: B+C1
552 A.editBody('Depends-On: %s\nDepends-On: %s\n' % (
553 B.data['url'], C1.data['url']))
554
555 self.executor_server.hold_jobs_in_build = True
556 B.addApproval('Approved', 1)
557 C1.addApproval('Approved', 1)
558 self.fake_github.emitEvent(A.addLabel('approved'))
559 self.waitUntilSettled()
560
561 self.executor_server.release('.*-merge')
562 self.waitUntilSettled()
563 self.executor_server.release('.*-merge')
564 self.waitUntilSettled()
565 self.executor_server.release('.*-merge')
566 self.waitUntilSettled()
567 self.executor_server.hold_jobs_in_build = False
568 self.executor_server.release()
569 self.waitUntilSettled()
570 self.assertTrue(A.is_merged)
571 self.assertEqual(B.data['status'], 'MERGED')
572 self.assertEqual(C1.data['status'], 'MERGED')
573 self.assertEqual(len(A.comments), 2)
574 self.assertEqual(B.reported, 2)
575 self.assertEqual(C1.reported, 2)
576
577 changes = self.getJobFromHistory(
578 'project-merge', 'github/project2').changes
579 self.assertEqual(changes, '1,1 2,1 1,%s' %
580 (A.head_sha,))
581
582 def test_crd_gate_reverse(self):
583 "Test reverse cross-repo dependencies"
584 A = self.fake_github.openFakePullRequest('github/project2', 'master',
585 'A')
586 B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
587 B.addApproval('Code-Review', 2)
588
589 # A Depends-On: B
590 A.editBody('Depends-On: %s\n' % (B.data['url'],))
591
592 self.fake_github.emitEvent(A.addLabel('approved'))
593 self.waitUntilSettled()
594
595 self.assertFalse(A.is_merged)
596 self.assertEqual(B.data['status'], 'NEW')
597
598 self.executor_server.hold_jobs_in_build = True
599 A.addLabel('approved')
600 self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
601 self.waitUntilSettled()
602
603 self.executor_server.release('.*-merge')
604 self.waitUntilSettled()
605 self.executor_server.release('.*-merge')
606 self.waitUntilSettled()
607 self.executor_server.hold_jobs_in_build = False
608 self.executor_server.release()
609 self.waitUntilSettled()
610
611 self.assertTrue(A.is_merged)
612 self.assertEqual(B.data['status'], 'MERGED')
613 self.assertEqual(len(A.comments), 2)
614 self.assertEqual(B.reported, 2)
615
616 changes = self.getJobFromHistory(
617 'project-merge', 'github/project2').changes
618 self.assertEqual(changes, '1,1 1,%s' %
619 (A.head_sha,))
620
621 def test_crd_cycle(self):
622 "Test cross-repo dependency cycles"
623 A = self.fake_github.openFakePullRequest('github/project2', 'master',
624 'A')
625 B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
626 B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
627 B.subject, A.url)
628
629 B.addApproval('Code-Review', 2)
630 B.addApproval('Approved', 1)
631
632 # A -> B -> A (via commit-depends)
633 A.editBody('Depends-On: %s\n' % (B.data['url'],))
634
635 self.fake_github.emitEvent(A.addLabel('approved'))
636 self.waitUntilSettled()
637
638 self.assertEqual(len(A.comments), 0)
639 self.assertEqual(B.reported, 0)
640 self.assertFalse(A.is_merged)
641 self.assertEqual(B.data['status'], 'NEW')
642
643 def test_crd_gate_unknown(self):
644 "Test unknown projects in dependent pipeline"
645 self.init_repo("gerrit/unknown", tag='init')
646 A = self.fake_github.openFakePullRequest('github/project2', 'master',
647 'A')
648 B = self.fake_gerrit.addFakeChange('gerrit/unknown', 'master', 'B')
649 B.addApproval('Code-Review', 2)
650
651 # A Depends-On: B
652 A.editBody('Depends-On: %s\n' % (B.data['url'],))
653
654 B.addApproval('Approved', 1)
655 event = A.addLabel('approved')
656 self.fake_github.emitEvent(event)
657 self.waitUntilSettled()
658
659 # Unknown projects cannot share a queue with any other
660 # since they don't have common jobs with any other (they have no jobs).
661 # Changes which depend on unknown project changes
662 # should not be processed in dependent pipeline
663 self.assertFalse(A.is_merged)
664 self.assertEqual(B.data['status'], 'NEW')
665 self.assertEqual(len(A.comments), 0)
666 self.assertEqual(B.reported, 0)
667 self.assertEqual(len(self.history), 0)
668
669 # Simulate change B being gated outside this layout Set the
670 # change merged before submitting the event so that when the
671 # event triggers a gerrit query to update the change, we get
672 # the information that it was merged.
673 B.setMerged()
674 self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
675 self.waitUntilSettled()
676 self.assertEqual(len(self.history), 0)
677
678 # Now that B is merged, A should be able to be enqueued and
679 # merged.
680 self.fake_github.emitEvent(event)
681 self.waitUntilSettled()
682
683 self.assertTrue(A.is_merged)
684 self.assertEqual(len(A.comments), 2)
685 self.assertEqual(B.data['status'], 'MERGED')
686 self.assertEqual(B.reported, 0)
687
688 def test_crd_check(self):
689 "Test cross-repo dependencies in independent pipelines"
690 self.executor_server.hold_jobs_in_build = True
691 self.gearman_server.hold_jobs_in_queue = True
692 A = self.fake_github.openFakePullRequest('github/project2', 'master',
693 'A')
694 B = self.fake_gerrit.addFakeChange(
695 'gerrit/project1', 'master', 'B')
696
697 # A Depends-On: B
698 A.editBody('Depends-On: %s\n' % (B.data['url'],))
699
700 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
701 self.waitUntilSettled()
702
703 self.gearman_server.hold_jobs_in_queue = False
704 self.gearman_server.release()
705 self.waitUntilSettled()
706
707 self.executor_server.release('.*-merge')
708 self.waitUntilSettled()
709
710 self.assertTrue(self.builds[0].hasChanges(A, B))
711
712 self.executor_server.hold_jobs_in_build = False
713 self.executor_server.release()
714 self.waitUntilSettled()
715
716 self.assertFalse(A.is_merged)
717 self.assertEqual(B.data['status'], 'NEW')
718 self.assertEqual(len(A.comments), 1)
719 self.assertEqual(B.reported, 0)
720
721 changes = self.getJobFromHistory(
722 'project-merge', 'github/project2').changes
723 self.assertEqual(changes, '1,1 1,%s' %
724 (A.head_sha,))
725
726 tenant = self.sched.abide.tenants.get('tenant-one')
727 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
728
729 def test_crd_check_duplicate(self):
730 "Test duplicate check in independent pipelines"
731 self.executor_server.hold_jobs_in_build = True
732 A = self.fake_github.openFakePullRequest('github/project2', 'master',
733 'A')
734 B = self.fake_gerrit.addFakeChange(
735 'gerrit/project1', 'master', 'B')
736
737 # A Depends-On: B
738 A.editBody('Depends-On: %s\n' % (B.data['url'],))
739 tenant = self.sched.abide.tenants.get('tenant-one')
740 check_pipeline = tenant.layout.pipelines['check']
741
742 # Add two dependent changes...
743 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
744 self.waitUntilSettled()
745 self.assertEqual(len(check_pipeline.getAllItems()), 2)
746
747 # ...make sure the live one is not duplicated...
748 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
749 self.waitUntilSettled()
750 self.assertEqual(len(check_pipeline.getAllItems()), 2)
751
752 # ...but the non-live one is able to be.
753 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
754 self.waitUntilSettled()
755 self.assertEqual(len(check_pipeline.getAllItems()), 3)
756
757 # Release jobs in order to avoid races with change A jobs
758 # finishing before change B jobs.
759 self.orderedRelease()
760 self.executor_server.hold_jobs_in_build = False
761 self.executor_server.release()
762 self.waitUntilSettled()
763
764 self.assertFalse(A.is_merged)
765 self.assertEqual(B.data['status'], 'NEW')
766 self.assertEqual(len(A.comments), 1)
767 self.assertEqual(B.reported, 1)
768
769 changes = self.getJobFromHistory(
770 'project-merge', 'github/project2').changes
771 self.assertEqual(changes, '1,1 1,%s' %
772 (A.head_sha,))
773
774 changes = self.getJobFromHistory(
775 'project-merge', 'gerrit/project1').changes
776 self.assertEqual(changes, '1,1')
777 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
778
779 self.assertIn('Build succeeded', A.comments[0])
780
781 def _test_crd_check_reconfiguration(self, project1, project2):
782 "Test cross-repo dependencies re-enqueued in independent pipelines"
783
784 self.gearman_server.hold_jobs_in_queue = True
785 A = self.fake_github.openFakePullRequest('github/project2', 'master',
786 'A')
787 B = self.fake_gerrit.addFakeChange(
788 'gerrit/project1', 'master', 'B')
789
790 # A Depends-On: B
791 A.editBody('Depends-On: %s\n' % (B.data['url'],))
792
793 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
794 self.waitUntilSettled()
795
796 self.sched.reconfigure(self.config)
797
798 # Make sure the items still share a change queue, and the
799 # first one is not live.
800 tenant = self.sched.abide.tenants.get('tenant-one')
801 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 1)
802 queue = tenant.layout.pipelines['check'].queues[0]
803 first_item = queue.queue[0]
804 for item in queue.queue:
805 self.assertEqual(item.queue, first_item.queue)
806 self.assertFalse(first_item.live)
807 self.assertTrue(queue.queue[1].live)
808
809 self.gearman_server.hold_jobs_in_queue = False
810 self.gearman_server.release()
811 self.waitUntilSettled()
812
813 self.assertFalse(A.is_merged)
814 self.assertEqual(B.data['status'], 'NEW')
815 self.assertEqual(len(A.comments), 1)
816 self.assertEqual(B.reported, 0)
817
818 changes = self.getJobFromHistory(
819 'project-merge', 'github/project2').changes
820 self.assertEqual(changes, '1,1 1,%s' %
821 (A.head_sha,))
822 self.assertEqual(len(tenant.layout.pipelines['check'].queues), 0)
823
824 def test_crd_check_reconfiguration(self):
825 self._test_crd_check_reconfiguration('org/project1', 'org/project2')
826
827 def test_crd_undefined_project(self):
828 """Test that undefined projects in dependencies are handled for
829 independent pipelines"""
830 # It's a hack for fake gerrit,
831 # as it implies repo creation upon the creation of any change
832 self.init_repo("gerrit/unknown", tag='init')
833 self._test_crd_check_reconfiguration('github/project2',
834 'gerrit/unknown')
835
836 def test_crd_check_transitive(self):
837 "Test transitive cross-repo dependencies"
838 # Specifically, if A -> B -> C, and C gets a new patchset and
839 # A gets a new patchset, ensure the test of A,2 includes B,1
840 # and C,2 (not C,1 which would indicate stale data in the
841 # cache for B).
842 A = self.fake_github.openFakePullRequest('github/project2', 'master',
843 'A')
844 B = self.fake_gerrit.addFakeChange('gerrit/project1', 'master', 'B')
845 C = self.fake_github.openFakePullRequest('github/project2', 'master',
846 'C')
847
848 # B Depends-On: C
849 B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
850 B.subject, C.url)
851
852 # A Depends-On: B
853 A.editBody('Depends-On: %s\n' % (B.data['url'],))
854
855 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
856 self.waitUntilSettled()
857 self.assertEqual(self.history[-1].changes, '2,%s 1,1 1,%s' %
858 (C.head_sha, A.head_sha))
859
860 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
861 self.waitUntilSettled()
862 self.assertEqual(self.history[-1].changes, '2,%s 1,1' %
863 (C.head_sha,))
864
865 self.fake_github.emitEvent(C.getPullRequestEditedEvent())
866 self.waitUntilSettled()
867 self.assertEqual(self.history[-1].changes, '2,%s' %
868 (C.head_sha,))
869
870 new_c_head = C.head_sha
871 C.addCommit()
872 old_c_head = C.head_sha
873 self.assertNotEqual(old_c_head, new_c_head)
874 self.fake_github.emitEvent(C.getPullRequestEditedEvent())
875 self.waitUntilSettled()
876 self.assertEqual(self.history[-1].changes, '2,%s' %
877 (C.head_sha,))
878
879 new_a_head = A.head_sha
880 A.addCommit()
881 old_a_head = A.head_sha
882 self.assertNotEqual(old_a_head, new_a_head)
883 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
884 self.waitUntilSettled()
885 self.assertEqual(self.history[-1].changes, '2,%s 1,1 1,%s' %
886 (C.head_sha, A.head_sha,))
887
888 def test_crd_check_unknown(self):
889 "Test unknown projects in independent pipeline"
890 self.init_repo("gerrit/unknown", tag='init')
891 A = self.fake_github.openFakePullRequest('github/project2', 'master',
892 'A')
893 B = self.fake_gerrit.addFakeChange(
894 'gerrit/unknown', 'master', 'B')
895
896 # A Depends-On: B
897 A.editBody('Depends-On: %s\n' % (B.data['url'],))
898
899 # Make sure zuul has seen an event on B.
900 self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1))
901 self.fake_github.emitEvent(A.getPullRequestEditedEvent())
902 self.waitUntilSettled()
903
904 self.assertFalse(A.is_merged)
905 self.assertEqual(len(A.comments), 1)
906 self.assertEqual(B.data['status'], 'NEW')
907 self.assertEqual(B.reported, 0)
908
909 def test_crd_cycle_join(self):
910 "Test an updated change creates a cycle"
911 A = self.fake_gerrit.addFakeChange(
912 'gerrit/project1', 'master', 'A')
913
914 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
915 self.waitUntilSettled()
916 self.assertEqual(A.reported, 1)
917
918 # Create B->A
919 B = self.fake_github.openFakePullRequest('github/project2', 'master',
920 'B')
921 B.editBody('Depends-On: %s\n' % (A.data['url'],))
922 self.fake_github.emitEvent(B.getPullRequestEditedEvent())
923 self.waitUntilSettled()
924
925 # Dep is there so zuul should have reported on B
926 self.assertEqual(len(B.comments), 1)
927
928 # Update A to add A->B (a cycle).
929 A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % (
930 A.subject, B.url)
931 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
932 self.waitUntilSettled()
933
934 # Dependency cycle injected so zuul should not have reported again on A
935 self.assertEqual(A.reported, 1)
936
937 # Now if we update B to remove the depends-on, everything
938 # should be okay. B; A->B
939
940 B.addCommit()
941 B.editBody('')
942 self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
943 self.waitUntilSettled()
944
945 # Cycle was removed so now zuul should have reported again on A
946 self.assertEqual(A.reported, 2)
947
948 self.fake_github.emitEvent(B.getPullRequestEditedEvent())
949 self.waitUntilSettled()
950 self.assertEqual(len(B.comments), 2)