diff options
Diffstat (limited to 'tests/unit/test_v3.py')
-rw-r--r-- | tests/unit/test_v3.py | 626 |
1 files changed, 459 insertions, 167 deletions
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py index 23c78b3ef..fe5b46e0c 100644 --- a/tests/unit/test_v3.py +++ b/tests/unit/test_v3.py @@ -25,12 +25,14 @@ from time import sleep from unittest import skip, skipIf from zuul.lib import yamlutil +import fixtures import git import paramiko import zuul.configloader from zuul.lib import yamlutil as yaml from zuul.model import MergeRequest +from zuul.zk.blob_store import BlobStore from tests.base import ( AnsibleZuulTestCase, @@ -865,6 +867,62 @@ class TestBranchMismatch(ZuulTestCase): dict(name='project-test2', result='SUCCESS', changes='1,1'), ], ordered=False) + def test_implied_branch_matcher_regex(self): + # Test that branch names that look like regexes aren't treated + # as such for implied branch matchers. + + # Make sure the parent job repo is branched, so it gets + # implied branch matchers. + + # The '+' in the branch name would cause the change not to + # match if it is treated as a regex. + self.create_branch('org/project1', 'feature/foo-0.1.12+bar') + self.fake_gerrit.addEvent( + self.fake_gerrit.getFakeBranchCreatedEvent( + 'org/project1', 'feature/foo-0.1.12+bar')) + + A = self.fake_gerrit.addFakeChange( + 'org/project1', 'feature/foo-0.1.12+bar', 'A') + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertHistory([ + dict(name='project-test1', result='SUCCESS', changes='1,1'), + ], ordered=False) + + def test_implied_branch_matcher_pragma_syntax_error(self): + # Test that syntax errors are reported if the implied branch + # matcher pragma is set. This catches potential errors when + # serializing configuration errors since the pragma causes + # extra information to be added to the error source context. + self.create_branch('org/project1', 'feature/test') + self.fake_gerrit.addEvent( + self.fake_gerrit.getFakeBranchCreatedEvent( + 'org/project1', 'feature/test')) + + in_repo_conf = textwrap.dedent( + """ + - job: + name: project-test1 + nodeset: bar + - pragma: + implied-branches: + - master + - feature/r1 + """) + file_dict = {'zuul.yaml': in_repo_conf} + A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A', + files=file_dict) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertHistory([]) + self.assertEqual(A.data['status'], 'NEW') + self.assertEqual(A.reported, 1, + "A should report failure") + self.assertIn('nodeset "bar" was not found', A.messages[0], + "A should have a syntax error reported") + class TestBranchRef(ZuulTestCase): tenant_config_file = 'config/branch-ref/main.yaml' @@ -1421,6 +1479,38 @@ class TestInRepoConfig(ZuulTestCase): "A should have failed the check pipeline") self.assertHistory([]) + def test_dynamic_nonexistent_job_dependency(self): + # Tests that a reference to a nonexistent job dependency is an + # error. + in_repo_conf = textwrap.dedent( + """ + - job: + name: project-test1 + run: playbooks/project-test1.yaml + + - project: + name: org/project + check: + jobs: + - project-test1: + dependencies: + - name: non-existent-job + soft: true + """) + + file_dict = {'.zuul.yaml': in_repo_conf} + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A', + files=file_dict) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + self.assertEqual(A.reported, 1, + "A should report failure") + self.assertEqual(A.patchsets[0]['approvals'][0]['value'], "-1") + self.assertIn('Job non-existent-job not defined', A.messages[0], + "A should have failed the check pipeline") + self.assertNotIn('freezing', A.messages[0]) + self.assertHistory([]) + def test_dynamic_config_new_patchset(self): self.executor_server.hold_jobs_in_build = True @@ -1692,6 +1782,25 @@ class TestInRepoConfig(ZuulTestCase): self.assertIn('while constructing a mapping', A.messages[0], "A should have a syntax error reported") + def test_yaml_dict_error3(self): + in_repo_conf = textwrap.dedent( + """ + - job: + """) + + file_dict = {'.zuul.yaml': in_repo_conf} + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A', + files=file_dict) + A.addApproval('Code-Review', 2) + self.fake_gerrit.addEvent(A.addApproval('Approved', 1)) + self.waitUntilSettled() + + self.assertEqual(A.data['status'], 'NEW') + self.assertEqual(A.reported, 1, + "A should report failure") + self.assertIn('is not a dictionary', A.messages[0], + "A should have a syntax error reported") + def test_yaml_duplicate_key_error(self): in_repo_conf = textwrap.dedent( """ @@ -2573,6 +2682,69 @@ class TestInRepoConfig(ZuulTestCase): self.assertIn('Debug information:', A.messages[0], "A should have debug info") + def test_nodeset_alternates_cycle(self): + in_repo_conf = textwrap.dedent( + """ + - nodeset: + name: red + alternatives: [blue] + - nodeset: + name: blue + alternatives: [red] + - job: + name: project-test1 + run: playbooks/project-test1.yaml + nodeset: blue + """) + + file_dict = {'.zuul.yaml': in_repo_conf} + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A', + files=file_dict) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertEqual(A.reported, 1) + self.assertIn("cycle detected", A.messages[0]) + + def test_nodeset_alternates_missing_from_nodeset(self): + in_repo_conf = textwrap.dedent( + """ + - nodeset: + name: red + alternatives: [blue] + - job: + name: project-test1 + run: playbooks/project-test1.yaml + """) + + file_dict = {'.zuul.yaml': in_repo_conf} + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A', + files=file_dict) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertEqual(A.reported, 1) + self.assertIn('nodeset "blue" was not found', A.messages[0]) + + def test_nodeset_alternates_missing_from_job(self): + in_repo_conf = textwrap.dedent( + """ + - job: + name: project-test1 + run: playbooks/project-test1.yaml + nodeset: + alternatives: [red] + """) + + file_dict = {'.zuul.yaml': in_repo_conf} + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A', + files=file_dict) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertEqual(A.reported, 1) + self.assertIn('nodeset "red" was not found', A.messages[0]) + @skipIfMultiScheduler() # See comment in TestInRepoConfigDir.scheduler_count for further # details. @@ -2880,6 +3052,41 @@ class TestInRepoConfigDir(ZuulTestCase): ], ordered=True) +class TestExtraConfigInDependent(ZuulTestCase): + # in org/project2, jobs are defined in extra config paths, while + # project is defined in .zuul.yaml + tenant_config_file = 'config/in-repo-dir/main.yaml' + scheduler_count = 1 + + def test_extra_config_in_dependent_change(self): + # Test that when jobs are defined in a extra-config-paths in a repo, if + # another change is dependent on a change of that repo, the jobs should + # still be loaded. + + # Add an empty zuul.yaml here so we are triggering dynamic layout load + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A', + files={'zuul.yaml': ''}) + B = self.fake_gerrit.addFakeChange('org/project2', 'master', 'B', + files={'zuul.yaml': ''}) + # A Depends-On: B who has private jobs defined in extra-config-paths + A.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % ( + A.subject, B.data['url']) + + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + # Jobs in both changes should be success + self.assertHistory([ + dict(name='project2-private-extra-file', result='SUCCESS', + changes='2,1'), + dict(name='project2-private-extra-dir', result='SUCCESS', + changes='2,1'), + dict(name='project-test1', result='SUCCESS', + changes='2,1 1,1'), + ], ordered=False) + + class TestGlobalRepoState(AnsibleZuulTestCase): config_file = 'zuul-connections-gerrit-and-github.conf' tenant_config_file = 'config/global-repo-state/main.yaml' @@ -3589,9 +3796,9 @@ class TestInRepoJoin(ZuulTestCase): class FunctionalAnsibleMixIn(object): # A temporary class to hold new tests while others are disabled + # These should be overridden in child classes. tenant_config_file = 'config/ansible/main.yaml' - # This should be overriden in child classes. - ansible_version = '2.9' + ansible_major_minor = 'X.Y' def test_playbook(self): # This test runs a bit long and needs extra time. @@ -3677,8 +3884,12 @@ class FunctionalAnsibleMixIn(object): build_python27.uuid + '.secrets') with open(secrets_path) as f: self.assertEqual(f.read(), "test-username test-password") + build_bubblewrap = self.getJobFromHistory('bubblewrap') + with self.jobLog(build_bubblewrap): + self.assertEqual(build_bubblewrap.result, 'SUCCESS') def test_repo_ansible(self): + self.executor_server.keep_jobdir = True A = self.fake_gerrit.addFakeChange('org/ansible', 'master', 'A') self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) self.waitUntilSettled() @@ -3688,103 +3899,21 @@ class FunctionalAnsibleMixIn(object): self.assertHistory([ dict(name='hello-ansible', result='SUCCESS', changes='1,1'), ]) + build = self.getJobFromHistory('hello-ansible', result='SUCCESS') + with open(build.jobdir.job_output_file) as f: + output = f.read() + self.assertIn(f'Ansible version={self.ansible_major_minor}', + output) - def _add_job(self, job_name): - conf = textwrap.dedent( - """ - - job: - name: {job_name} - run: playbooks/{job_name}.yaml - ansible-version: {ansible_version} - - - project: - check: - jobs: - - {job_name} - """.format(job_name=job_name, - ansible_version=self.ansible_version)) - - file_dict = {'.zuul.yaml': conf} - A = self.fake_gerrit.addFakeChange('org/plugin-project', 'master', 'A', - files=file_dict) - self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) - def _test_plugins(self, plugin_tests): - # This test runs a bit long and needs extra time. - self.wait_timeout = 180 +class TestAnsible5(AnsibleZuulTestCase, FunctionalAnsibleMixIn): + tenant_config_file = 'config/ansible/main5.yaml' + ansible_major_minor = '2.12' - # Keep the jobdir around so we can inspect contents if an - # assert fails. - self.executor_server.keep_jobdir = True - # Output extra ansible info so we might see errors. - self.executor_server.verbose = True - count = 0 - - # Kick off all test jobs in parallel - for job_name, result in plugin_tests: - count += 1 - self._add_job(job_name) - # Wait for all jobs to complete - self.waitUntilSettled() - - # Check the correct number of jobs ran - self.assertEqual(count, len(self.history)) - # Check the job results - for job_name, result in plugin_tests: - build = self.getJobFromHistory(job_name) - with self.jobLog(build): - self.assertEqual(build.result, result) - - # TODOv3(jeblair): parse the ansible output and verify we're - # getting the exception we expect. - - def test_plugins_1(self): - ''' - Split plugin tests to avoid timeouts and exceeding subunit - report lengths. - ''' - plugin_tests = [ - ('passwd', 'FAILURE'), - ('cartesian', 'SUCCESS'), - ('consul_kv', 'FAILURE'), - ('credstash', 'FAILURE'), - ('csvfile_good', 'SUCCESS'), - ('csvfile_bad', 'FAILURE'), - ('uri_bad_path', 'FAILURE'), - ('uri_bad_scheme', 'FAILURE'), - ] - self._test_plugins(plugin_tests) - - def test_plugins_2(self): - ''' - Split plugin tests to avoid timeouts and exceeding subunit - report lengths. - ''' - plugin_tests = [ - ('block_local_override', 'FAILURE'), - ('file_local_good', 'SUCCESS'), - ('file_local_bad', 'FAILURE'), - ('fileglob_local_good', 'SUCCESS'), - ('fileglob_local_bad', 'FAILURE'), - ('find_local_good', 'SUCCESS'), - ('find_local_bad', 'FAILURE'), - ('zuul_return', 'SUCCESS'), - ('password_create_good', 'SUCCESS'), - ('password_null_good', 'SUCCESS'), - ('password_read_good', 'SUCCESS'), - ('password_create_bad', 'FAILURE'), - ('password_read_bad', 'FAILURE'), - ] - self._test_plugins(plugin_tests) - - -class TestAnsible28(AnsibleZuulTestCase, FunctionalAnsibleMixIn): - ansible_version = '2.8' - - -class TestAnsible29(AnsibleZuulTestCase, FunctionalAnsibleMixIn): - ansible_version = '2.9' +class TestAnsible6(AnsibleZuulTestCase, FunctionalAnsibleMixIn): + tenant_config_file = 'config/ansible/main6.yaml' + ansible_major_minor = '2.13' class TestPrePlaybooks(AnsibleZuulTestCase): @@ -4868,7 +4997,7 @@ class TestDataReturn(AnsibleZuulTestCase): self.assertIn( '- data-return https://zuul.example.com/', A.messages[-1]) - self.assertTrue(re.search('child .* SKIPPED', A.messages[-1])) + self.assertTrue('Skipped 1 job' in A.messages[-1]) self.assertIn('Build succeeded', A.messages[-1]) def test_data_return_invalid_child_job(self): @@ -4881,7 +5010,7 @@ class TestDataReturn(AnsibleZuulTestCase): self.assertIn( '- data-return-invalid-child-job https://zuul.example.com', A.messages[-1]) - self.assertTrue(re.search('data-return .* SKIPPED', A.messages[-1])) + self.assertTrue('Skipped 1 job' in A.messages[-1]) self.assertIn('Build succeeded', A.messages[-1]) def test_data_return_skip_all_child_jobs(self): @@ -4895,8 +5024,7 @@ class TestDataReturn(AnsibleZuulTestCase): self.assertIn( '- data-return-skip-all https://zuul.example.com/', A.messages[-1]) - self.assertTrue(re.search('child .* SKIPPED', A.messages[-1])) - self.assertTrue(re.search('data-return .* SKIPPED', A.messages[-1])) + self.assertTrue('Skipped 2 jobs' in A.messages[-1]) self.assertIn('Build succeeded', A.messages[-1]) def test_data_return_skip_all_child_jobs_with_soft_dependencies(self): @@ -4910,8 +5038,7 @@ class TestDataReturn(AnsibleZuulTestCase): ]) self.assertIn('- data-return-cd https://zuul.example.com/', A.messages[-1]) - self.assertTrue(re.search('data-return-a .* SKIPPED', A.messages[-1])) - self.assertTrue(re.search('data-return-b .* SKIPPED', A.messages[-1])) + self.assertTrue('Skipped 2 jobs' in A.messages[-1]) self.assertIn('Build succeeded', A.messages[-1]) def test_several_zuul_return(self): @@ -4925,9 +5052,22 @@ class TestDataReturn(AnsibleZuulTestCase): self.assertIn( '- several-zuul-return-child https://zuul.example.com/', A.messages[-1]) - self.assertTrue(re.search('data-return .* SKIPPED', A.messages[-1])) + self.assertTrue('Skipped 1 job' in A.messages[-1]) self.assertIn('Build succeeded', A.messages[-1]) + def test_data_return_skip_retry(self): + A = self.fake_gerrit.addFakeChange( + 'org/project-skip-retry', + 'master', + 'A' + ) + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + self.assertHistory([ + dict(name='skip-retry-return', result='FAILURE', + changes='1,1'), + ]) + def test_data_return_child_jobs_failure(self): A = self.fake_gerrit.addFakeChange('org/project5', 'master', 'A') self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) @@ -5587,6 +5727,39 @@ class TestSecrets(ZuulTestCase): self._getSecrets('project2-complex', 'playbooks'), [secret]) + def test_blobstore_secret(self): + # Test the large secret blob store + self.executor_server.hold_jobs_in_build = True + self.useFixture(fixtures.MonkeyPatch( + 'zuul.model.Job.SECRET_BLOB_SIZE', + 1)) + + A = self.fake_gerrit.addFakeChange('org/project1', 'master', 'A') + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + context = self.scheds.first.sched.createZKContext(None, self.log) + bs = BlobStore(context) + self.assertEqual(len(bs), 1) + + self.scheds.first.sched._runBlobStoreCleanup() + self.assertEqual(len(bs), 1) + + self.executor_server.hold_jobs_in_build = False + self.executor_server.release() + self.waitUntilSettled() + + self.assertEqual(A.reported, 1, "A should report success") + self.assertHistory([ + dict(name='project1-secret', result='SUCCESS', changes='1,1'), + ]) + self.assertEqual( + [{'secret_name': self.secret}], + self._getSecrets('project1-secret', 'playbooks')) + + self.scheds.first.sched._runBlobStoreCleanup() + self.assertEqual(len(bs), 0) + class TestSecretInheritance(ZuulTestCase): tenant_config_file = 'config/secret-inheritance/main.yaml' @@ -6395,68 +6568,21 @@ class TestJobOutput(AnsibleZuulTestCase): self.assertIn('Final playbook failed', log_output) self.assertIn('Failure test', log_output) - -class TestPlugins(AnsibleZuulTestCase): - tenant_config_file = 'config/speculative-plugins/main.yaml' - - def _run_job(self, job_name, project='org/project', roles=''): - # Output extra ansible info so we might see errors. - self.executor_server.verbose = True - conf = textwrap.dedent( - """ - - job: - name: {job_name} - run: playbooks/{job_name}/test.yaml - nodeset: - nodes: - - name: controller - label: whatever - {roles} - - project: - check: - jobs: - - {job_name} - """.format(job_name=job_name, roles=roles)) - - file_dict = {'zuul.yaml': conf} - A = self.fake_gerrit.addFakeChange(project, 'master', 'A', - files=file_dict) + def test_job_POST_FAILURE_reports_statsd(self): + """Test that POST_FAILURES output job stats.""" + self.statsd.clear() + A = self.fake_gerrit.addFakeChange('org/project2', 'master', 'A') self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) self.waitUntilSettled() - return A - - def _check_job(self, job_name, project='org/project', roles=''): - A = self._run_job(job_name, project, roles) - - message = A.messages[0] - self.assertIn('ERROR Ansible plugin dir', message) - self.assertIn('found adjacent to playbook', message) - self.assertIn('in non-trusted repo', message) - - def test_filter_plugin(self): - self._check_job('filter-plugin-playbook') - self._check_job('filter-plugin-playbook-symlink') - self._check_job('filter-plugin-bare-role') - self._check_job('filter-plugin-role') - self._check_job('filter-plugin-repo-role', project='org/projectrole', - roles="roles: [{zuul: 'org/projectrole'}]") - self._check_job('filter-plugin-shared-role', - roles="roles: [{zuul: 'org/project2'}]") - self._check_job( - 'filter-plugin-shared-bare-role', - roles="roles: [{zuul: 'org/project3', name: 'shared'}]") - - def test_implicit_role_not_added(self): - # This fails because the job uses the role which isn't added - # to the role path, but it's a normal ansible failure, not a - # Zuul executor error. - A = self._run_job('filter-plugin-repo-role', project='org/projectrole') self.assertHistory([ - dict(name='filter-plugin-repo-role', result='FAILURE', - changes='1,1'), + dict(name='job-output-failure', + result='POST_FAILURE', changes='1,1'), ], ordered=False) - message = A.messages[0] - self.assertNotIn('Ansible plugin', message) + post_failure_stat = 'zuul.tenant.tenant-one.pipeline.check.project.' \ + 'review_example_com.org_project2.master.job.' \ + 'job-output-failure.POST_FAILURE' + self.assertReportedStat(post_failure_stat, value='1', kind='c') + self.assertReportedStat(post_failure_stat, kind='ms') class TestNoLog(AnsibleZuulTestCase): @@ -6486,6 +6612,51 @@ class TestNoLog(AnsibleZuulTestCase): self.assertNotIn('my-very-secret-password-2', text_log) +class TestJsonStringResults(AnsibleZuulTestCase): + tenant_config_file = 'config/ansible-json-string-results/main.yaml' + + def _get_file(self, build, path): + p = os.path.join(build.jobdir.root, path) + with open(p) as f: + return f.read() + + def test_ansible_json_string_results(self): + """Test modules that return string results are captured + + The yum/dnf modules are seemily almost unique in setting + "results" in their module return value to a list of strings + (other things might too, but not many other built-in + components). Confusingly, when using loops in ansible the + output also has a "results" which is a list of dicts with + return values from each iteration. + + The zuul_json callback handler needs to deal with both; We've + broken this before making changes to its results parsing. + This test fakes some string return values like the yum modules + do, and ensures they are captured. + + """ + + self.executor_server.keep_jobdir = True + + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') + + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + json_log = self._get_file(self.history[0], 'work/logs/job-output.json') + text_log = self._get_file(self.history[0], 'work/logs/job-output.txt') + + self.assertIn('if you see this string, it is working', json_log) + # Note the text log doesn't include the detail of the returned + # results, just the msg field, hence to following "not in" + self.assertNotIn('if you see this string, it is working', text_log) + self.assertIn('A plugin message', text_log) + # no_log checking + self.assertNotIn('this is a secret string', json_log) + self.assertNotIn('this is a secret string', text_log) + + class TestUnreachable(AnsibleZuulTestCase): tenant_config_file = 'config/ansible-unreachable/main.yaml' @@ -6799,7 +6970,7 @@ class TestJobPause(AnsibleZuulTestCase): dict(name='compile', result='SUCCESS', changes='1,1'), ]) - self.assertTrue(re.search('test .* SKIPPED', A.messages[0])) + self.assertTrue('Skipped 1 job' in A.messages[0]) def test_job_pause_pre_skipped_child(self): """ @@ -6847,7 +7018,7 @@ class TestJobPause(AnsibleZuulTestCase): dict(name='compile', result='SUCCESS', changes='1,1'), ]) - self.assertTrue(re.search('test .* SKIPPED', A.messages[0])) + self.assertTrue('Skipped 1 job' in A.messages[0]) def test_job_pause_skipped_child_retry(self): """ @@ -7716,7 +7887,7 @@ class TestProvidesRequiresMysql(ZuulTestCase): dict(name='image-builder', result='FAILURE', changes='1,1'), dict(name='hold', result='SUCCESS', changes='1,1'), ], ordered=False) - self.assertTrue(re.search('image-user .* SKIPPED', A.messages[0])) + self.assertTrue('Skipped 1 job' in A.messages[0]) B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B') B.data['commitMessage'] = '%s\n\nDepends-On: %s\n' % ( @@ -7835,8 +8006,8 @@ class TestAnsibleVersion(AnsibleZuulTestCase): self.assertHistory([ dict(name='ansible-default', result='SUCCESS', changes='1,1'), - dict(name='ansible-28', result='SUCCESS', changes='1,1'), - dict(name='ansible-29', result='SUCCESS', changes='1,1'), + dict(name='ansible-5', result='SUCCESS', changes='1,1'), + dict(name='ansible-6', result='SUCCESS', changes='1,1'), ], ordered=False) @@ -7855,8 +8026,8 @@ class TestDefaultAnsibleVersion(AnsibleZuulTestCase): self.assertHistory([ dict(name='ansible-default-zuul-conf', result='SUCCESS', changes='1,1'), - dict(name='ansible-28', result='SUCCESS', changes='1,1'), - dict(name='ansible-29', result='SUCCESS', changes='1,1'), + dict(name='ansible-5', result='SUCCESS', changes='1,1'), + dict(name='ansible-6', result='SUCCESS', changes='1,1'), ], ordered=False) @@ -7988,3 +8159,124 @@ class TestConnectionVars(AnsibleZuulTestCase): # job_output = self._get_file(job, 'work/logs/job-output.txt') # self.log.debug(job_output) # self.assertNotIn("/bin/du", job_output) + + +class IncludeBranchesTestCase(ZuulTestCase): + def _test_include_branches(self, history1, history2, history3, history4): + self.create_branch('org/project', 'stable') + self.create_branch('org/project', 'feature/foo') + self.fake_gerrit.addEvent( + self.fake_gerrit.getFakeBranchCreatedEvent( + 'org/project', 'stable')) + self.fake_gerrit.addEvent( + self.fake_gerrit.getFakeBranchCreatedEvent( + 'org/project', 'feature/foo')) + self.waitUntilSettled() + + # Test the jobs on the master branch. + A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A') + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertHistory(history1, ordered=False) + + # Test the jobs on the excluded feature branch. + B = self.fake_gerrit.addFakeChange('org/project', 'feature/foo', 'A') + self.fake_gerrit.addEvent(B.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertHistory(history1 + history2, ordered=False) + + # Test in-repo config proposed on the excluded feature branch. + conf = textwrap.dedent( + """ + - job: + name: project-dynamic + + - project: + check: + jobs: + - project-dynamic + """) + file_dict = {'zuul.yaml': conf} + C = self.fake_gerrit.addFakeChange('org/project', 'feature/foo', 'A', + files=file_dict) + self.fake_gerrit.addEvent(C.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + + self.assertHistory(history1 + history2 + history3, ordered=False) + + old = self.scheds.first.sched.tenant_layout_state.get('tenant-one') + # Merge a change to the excluded feature branch. + B.addApproval('Code-Review', 2) + self.fake_gerrit.addEvent(B.addApproval('Approved', 1)) + self.waitUntilSettled() + self.assertEqual(B.data['status'], 'MERGED') + self.assertHistory(history1 + history2 + history3 + history4, + ordered=False) + new = self.scheds.first.sched.tenant_layout_state.get('tenant-one') + # Verify we haven't performed a tenant reconfiguration + self.assertTrue(old == new) + + +class TestIncludeBranchesProject(IncludeBranchesTestCase): + tenant_config_file = 'config/dynamic-only-project/include.yaml' + + def test_include_branches(self): + history1 = [ + dict(name='central-test', result='SUCCESS', changes='1,1'), + dict(name='project-test', result='SUCCESS', changes='1,1'), + ] + history2 = [ + dict(name='central-test', result='SUCCESS', changes='2,1'), + ] + history3 = [ + dict(name='central-test', result='SUCCESS', changes='3,1'), + ] + history4 = [ + dict(name='central-test', result='SUCCESS', changes='2,1'), + ] + self._test_include_branches(history1, history2, history3, history4) + + +class TestExcludeBranchesProject(IncludeBranchesTestCase): + tenant_config_file = 'config/dynamic-only-project/exclude.yaml' + + def test_exclude_branches(self): + history1 = [ + dict(name='central-test', result='SUCCESS', changes='1,1'), + dict(name='project-test', result='SUCCESS', changes='1,1'), + ] + history2 = [ + dict(name='central-test', result='SUCCESS', changes='2,1'), + ] + history3 = [ + dict(name='central-test', result='SUCCESS', changes='3,1'), + ] + history4 = [ + dict(name='central-test', result='SUCCESS', changes='2,1'), + ] + self._test_include_branches(history1, history2, history3, history4) + + +class TestDynamicBranchesProject(IncludeBranchesTestCase): + tenant_config_file = 'config/dynamic-only-project/dynamic.yaml' + + def test_dynamic_branches(self): + history1 = [ + dict(name='central-test', result='SUCCESS', changes='1,1'), + dict(name='project-test', result='SUCCESS', changes='1,1'), + ] + history2 = [ + dict(name='central-test', result='SUCCESS', changes='2,1'), + dict(name='project-test', result='SUCCESS', changes='2,1'), + ] + history3 = [ + dict(name='central-test', result='SUCCESS', changes='3,1'), + dict(name='project-dynamic', result='SUCCESS', changes='3,1'), + ] + history4 = [ + dict(name='central-test', result='SUCCESS', changes='2,1'), + dict(name='project-test', result='SUCCESS', changes='2,1'), + ] + self._test_include_branches(history1, history2, history3, history4) |