| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # Copyright 2019 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Unittests for running tests after updating packages.""" |
| |
| from __future__ import print_function |
| |
| import json |
| import subprocess |
| import unittest |
| import unittest.mock as mock |
| |
| import chroot |
| import get_llvm_hash |
| import git |
| import test_helpers |
| import update_chromeos_llvm_hash |
| import update_packages_and_run_tests |
| |
| |
| # Testing with tryjobs. |
| class UpdatePackagesAndRunTryjobsTest(unittest.TestCase): |
| """Unittests when running tryjobs after updating packages.""" |
| |
| def testNoLastTestedFile(self): |
| self.assertEqual( |
| update_packages_and_run_tests.UnchangedSinceLastRun(None, {}), False) |
| |
| def testEmptyLastTestedFile(self): |
| with test_helpers.CreateTemporaryFile() as temp_file: |
| self.assertEqual( |
| update_packages_and_run_tests.UnchangedSinceLastRun(temp_file, {}), |
| False) |
| |
| def testLastTestedFileDoesNotExist(self): |
| # Simulate 'open()' on a lasted tested file that does not exist. |
| mock.mock_open(read_data='') |
| |
| self.assertEqual( |
| update_packages_and_run_tests.UnchangedSinceLastRun( |
| '/some/file/that/does/not/exist.txt', {}), False) |
| |
| def testMatchedLastTestedFile(self): |
| with test_helpers.CreateTemporaryFile() as last_tested_file: |
| arg_dict = { |
| 'svn_version': |
| 1234, |
| 'ebuilds': [ |
| '/path/to/package1-r2.ebuild', |
| '/path/to/package2/package2-r3.ebuild' |
| ], |
| 'builders': [ |
| 'kevin-llvm-next-toolchain-tryjob', |
| 'eve-llvm-next-toolchain-tryjob' |
| ], |
| 'extra_cls': [10, 1], |
| 'tryjob_options': ['latest-toolchain', 'hwtest'] |
| } |
| |
| with open(last_tested_file, 'w') as f: |
| f.write(json.dumps(arg_dict, indent=2)) |
| |
| self.assertEqual( |
| update_packages_and_run_tests.UnchangedSinceLastRun( |
| last_tested_file, arg_dict), True) |
| |
| def testGetTryJobCommandWithNoExtraInformation(self): |
| change_list = 1234 |
| |
| builder = 'nocturne' |
| |
| expected_cmd = [ |
| 'cros', 'tryjob', '--yes', '--json', '-g', |
| '%d' % change_list, builder |
| ] |
| |
| self.assertEqual( |
| update_packages_and_run_tests.GetTryJobCommand(change_list, None, None, |
| builder), expected_cmd) |
| |
| def testGetTryJobCommandWithExtraInformation(self): |
| change_list = 4321 |
| extra_cls = [1000, 10] |
| options = ['option1', 'option2'] |
| builder = 'kevin' |
| |
| expected_cmd = [ |
| 'cros', |
| 'tryjob', |
| '--yes', |
| '--json', |
| '-g', |
| '%d' % change_list, |
| '-g', |
| '%d' % extra_cls[0], |
| '-g', |
| '%d' % extra_cls[1], |
| '--%s' % options[0], |
| '--%s' % options[1], |
| builder, |
| ] |
| |
| self.assertEqual( |
| update_packages_and_run_tests.GetTryJobCommand( |
| change_list, extra_cls, options, builder), expected_cmd) |
| |
| @mock.patch.object( |
| update_packages_and_run_tests, |
| 'GetCurrentTimeInUTC', |
| return_value='2019-09-09') |
| @mock.patch.object(update_packages_and_run_tests, 'AddLinksToCL') |
| @mock.patch.object(subprocess, 'check_output') |
| def testSuccessfullySubmittedTryJob(self, mock_cmd, mock_add_links_to_cl, |
| mock_launch_time): |
| |
| expected_cmd = [ |
| 'cros', 'tryjob', '--yes', '--json', '-g', |
| '%d' % 900, '-g', |
| '%d' % 1200, '--some_option', 'builder1' |
| ] |
| |
| bb_id = '1234' |
| url = 'https://some_tryjob_url.com' |
| |
| mock_cmd.return_value = json.dumps([{'buildbucket_id': bb_id, 'url': url}]) |
| |
| chroot_path = '/some/path/to/chroot' |
| cl = 900 |
| extra_cls = [1200] |
| options = ['some_option'] |
| builders = ['builder1'] |
| |
| tests = update_packages_and_run_tests.RunTryJobs(cl, extra_cls, options, |
| builders, chroot_path) |
| |
| expected_tests = [{ |
| 'launch_time': mock_launch_time.return_value, |
| 'link': url, |
| 'buildbucket_id': int(bb_id), |
| 'extra_cls': extra_cls, |
| 'options': options, |
| 'builder': builders |
| }] |
| |
| self.assertEqual(tests, expected_tests) |
| |
| mock_cmd.assert_called_once_with( |
| expected_cmd, cwd=chroot_path, encoding='utf-8') |
| |
| mock_add_links_to_cl.assert_called_once() |
| |
| @mock.patch.object(update_packages_and_run_tests, 'AddLinksToCL') |
| @mock.patch.object(subprocess, 'check_output') |
| def testSuccessfullySubmittedRecipeBuilders(self, mock_cmd, |
| mock_add_links_to_cl): |
| |
| expected_cmd = [ |
| 'bb', 'add', '-json', '-cl', |
| 'crrev.com/c/%s' % 900, '-cl', |
| 'crrev.com/c/%s' % 1200, 'some_option', 'builder1' |
| ] |
| |
| bb_id = '1234' |
| create_time = '2020-04-18T00:03:53.978767Z' |
| |
| mock_cmd.return_value = json.dumps({'id': bb_id, 'createTime': create_time}) |
| |
| chroot_path = '/some/path/to/chroot' |
| cl = 900 |
| extra_cls = [1200] |
| options = ['some_option'] |
| builders = ['builder1'] |
| |
| tests = update_packages_and_run_tests.StartRecipeBuilders( |
| cl, extra_cls, options, builders, chroot_path) |
| |
| expected_tests = [{ |
| 'launch_time': create_time, |
| 'link': 'http://ci.chromium.org/b/%s' % bb_id, |
| 'buildbucket_id': bb_id, |
| 'extra_cls': extra_cls, |
| 'options': options, |
| 'builder': builders |
| }] |
| |
| self.assertEqual(tests, expected_tests) |
| |
| mock_cmd.assert_called_once_with( |
| expected_cmd, cwd=chroot_path, encoding='utf-8') |
| |
| mock_add_links_to_cl.assert_called_once() |
| |
| @mock.patch.object(subprocess, 'check_output', return_value=None) |
| def testSuccessfullyAddedTestLinkToCL(self, mock_exec_cmd): |
| chroot_path = '/abs/path/to/chroot' |
| |
| test_cl_number = 1000 |
| |
| tests = [{'link': 'https://some_tryjob_link.com'}] |
| |
| update_packages_and_run_tests.AddLinksToCL(tests, test_cl_number, |
| chroot_path) |
| |
| expected_gerrit_message = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'message', |
| str(test_cl_number), |
| 'Started the following tests:\n%s' % tests[0]['link'] |
| ] |
| |
| mock_exec_cmd.assert_called_once_with(expected_gerrit_message) |
| |
| @mock.patch.object(update_packages_and_run_tests, 'RunTryJobs') |
| @mock.patch.object(update_chromeos_llvm_hash, 'UpdatePackages') |
| @mock.patch.object(update_packages_and_run_tests, 'GetCommandLineArgs') |
| @mock.patch.object(get_llvm_hash, 'GetLLVMHashAndVersionFromSVNOption') |
| @mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True) |
| @mock.patch.object(chroot, 'GetChrootEbuildPaths') |
| def testUpdatedLastTestedFileWithNewTestedRevision( |
| self, mock_get_chroot_build_paths, mock_outside_chroot, |
| mock_get_hash_and_version, mock_get_commandline_args, |
| mock_update_packages, mock_run_tryjobs): |
| |
| # Create a temporary file to simulate the last tested file that contains a |
| # revision. |
| with test_helpers.CreateTemporaryFile() as last_tested_file: |
| builders = [ |
| 'kevin-llvm-next-toolchain-tryjob', 'eve-llvm-next-toolchain-tryjob' |
| ] |
| extra_cls = [10, 1] |
| tryjob_options = ['latest-toolchain', 'hwtest'] |
| ebuilds = [ |
| '/path/to/package1/package1-r2.ebuild', |
| '/path/to/package2/package2-r3.ebuild' |
| ] |
| |
| arg_dict = { |
| 'svn_version': 100, |
| 'ebuilds': ebuilds, |
| 'builders': builders, |
| 'extra_cls': extra_cls, |
| 'tryjob_options': tryjob_options |
| } |
| # Parepared last tested file |
| with open(last_tested_file, 'w') as f: |
| json.dump(arg_dict, f, indent=2) |
| |
| # Call with a changed LLVM svn version |
| args_output = test_helpers.ArgsOutputTest() |
| args_output.is_llvm_next = True |
| args_output.extra_change_lists = extra_cls |
| args_output.last_tested = last_tested_file |
| args_output.reviewers = [] |
| |
| args_output.subparser_name = 'tryjobs' |
| args_output.builders = builders |
| args_output.options = tryjob_options |
| |
| mock_get_commandline_args.return_value = args_output |
| |
| mock_get_chroot_build_paths.return_value = ebuilds |
| |
| mock_get_hash_and_version.return_value = ('a123testhash2', 200) |
| |
| mock_update_packages.return_value = git.CommitContents( |
| url='https://some_cl_url.com', cl_number=12345) |
| |
| mock_run_tryjobs.return_value = [{ |
| 'link': 'https://some_tryjob_url.com', |
| 'buildbucket_id': 1234 |
| }] |
| |
| update_packages_and_run_tests.main() |
| |
| # Verify that the lasted tested file has been updated to the new LLVM |
| # revision. |
| with open(last_tested_file) as f: |
| arg_dict = json.load(f) |
| |
| self.assertEqual(arg_dict['svn_version'], 200) |
| |
| mock_outside_chroot.assert_called_once() |
| |
| mock_get_commandline_args.assert_called_once() |
| |
| mock_get_hash_and_version.assert_called_once() |
| |
| mock_run_tryjobs.assert_called_once() |
| |
| mock_update_packages.assert_called_once() |
| |
| |
| class UpdatePackagesAndRunTestCQTest(unittest.TestCase): |
| """Unittests for CQ dry run after updating packages.""" |
| |
| def testGetCQDependString(self): |
| test_no_changelists = [] |
| test_single_changelist = [1234] |
| test_multiple_changelists = [1234, 5678] |
| |
| self.assertIsNone( |
| update_packages_and_run_tests.GetCQDependString(test_no_changelists)) |
| |
| self.assertEqual( |
| update_packages_and_run_tests.GetCQDependString(test_single_changelist), |
| '\nCq-Depend: chromium:1234') |
| |
| self.assertEqual( |
| update_packages_and_run_tests.GetCQDependString( |
| test_multiple_changelists), |
| '\nCq-Depend: chromium:1234, chromium:5678') |
| |
| def testGetCQIncludeTrybotsString(self): |
| test_no_trybot = None |
| test_valid_trybot = 'llvm-next' |
| test_invalid_trybot = 'invalid-name' |
| |
| self.assertIsNone( |
| update_packages_and_run_tests.GetCQIncludeTrybotsString(test_no_trybot)) |
| |
| self.assertEqual( |
| update_packages_and_run_tests.GetCQIncludeTrybotsString( |
| test_valid_trybot), |
| '\nCq-Include-Trybots:chromeos/cq:cq-llvm-next-orchestrator') |
| |
| with self.assertRaises(ValueError) as context: |
| update_packages_and_run_tests.GetCQIncludeTrybotsString( |
| test_invalid_trybot) |
| |
| self.assertIn('is not a valid llvm trybot', str(context.exception)) |
| |
| @mock.patch.object(subprocess, 'check_output', return_value=None) |
| def testStartCQDryRunNoDeps(self, mock_exec_cmd): |
| chroot_path = '/abs/path/to/chroot' |
| test_cl_number = 1000 |
| |
| # test with no deps cls. |
| extra_cls = [] |
| update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls, |
| chroot_path) |
| |
| expected_gerrit_message = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'label-cq', |
| str(test_cl_number), '1' |
| ] |
| |
| mock_exec_cmd.assert_called_once_with(expected_gerrit_message) |
| |
| # Mock ExecCommandAndCaptureOutput for the gerrit command execution. |
| @mock.patch.object(subprocess, 'check_output', return_value=None) |
| # test with a single deps cl. |
| def testStartCQDryRunSingleDep(self, mock_exec_cmd): |
| chroot_path = '/abs/path/to/chroot' |
| test_cl_number = 1000 |
| |
| extra_cls = [2000] |
| update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls, |
| chroot_path) |
| |
| expected_gerrit_cmd_1 = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'label-cq', |
| str(test_cl_number), '1' |
| ] |
| expected_gerrit_cmd_2 = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'label-cq', |
| str(2000), '1' |
| ] |
| |
| self.assertEqual(mock_exec_cmd.call_count, 2) |
| self.assertEqual(mock_exec_cmd.call_args_list[0], |
| mock.call(expected_gerrit_cmd_1)) |
| self.assertEqual(mock_exec_cmd.call_args_list[1], |
| mock.call(expected_gerrit_cmd_2)) |
| |
| # Mock ExecCommandAndCaptureOutput for the gerrit command execution. |
| @mock.patch.object(subprocess, 'check_output', return_value=None) |
| def testStartCQDryRunMultipleDep(self, mock_exec_cmd): |
| chroot_path = '/abs/path/to/chroot' |
| test_cl_number = 1000 |
| |
| # test with multiple deps cls. |
| extra_cls = [3000, 4000] |
| update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls, |
| chroot_path) |
| |
| expected_gerrit_cmd_1 = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'label-cq', |
| str(test_cl_number), '1' |
| ] |
| expected_gerrit_cmd_2 = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'label-cq', |
| str(3000), '1' |
| ] |
| expected_gerrit_cmd_3 = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'label-cq', |
| str(4000), '1' |
| ] |
| |
| self.assertEqual(mock_exec_cmd.call_count, 3) |
| self.assertEqual(mock_exec_cmd.call_args_list[0], |
| mock.call(expected_gerrit_cmd_1)) |
| self.assertEqual(mock_exec_cmd.call_args_list[1], |
| mock.call(expected_gerrit_cmd_2)) |
| self.assertEqual(mock_exec_cmd.call_args_list[2], |
| mock.call(expected_gerrit_cmd_3)) |
| |
| # Mock ExecCommandAndCaptureOutput for the gerrit command execution. |
| @mock.patch.object(subprocess, 'check_output', return_value=None) |
| # test with no reviewers. |
| def testAddReviewersNone(self, mock_exec_cmd): |
| chroot_path = '/abs/path/to/chroot' |
| reviewers = [] |
| test_cl_number = 1000 |
| |
| update_packages_and_run_tests.AddReviewers(test_cl_number, reviewers, |
| chroot_path) |
| self.assertTrue(mock_exec_cmd.not_called) |
| |
| # Mock ExecCommandAndCaptureOutput for the gerrit command execution. |
| @mock.patch.object(subprocess, 'check_output', return_value=None) |
| # test with multiple reviewers. |
| def testAddReviewersMultiple(self, mock_exec_cmd): |
| chroot_path = '/abs/path/to/chroot' |
| reviewers = ['none1@chromium.org', 'none2@chromium.org'] |
| test_cl_number = 1000 |
| |
| update_packages_and_run_tests.AddReviewers(test_cl_number, reviewers, |
| chroot_path) |
| |
| expected_gerrit_cmd_1 = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'reviewers', |
| str(test_cl_number), 'none1@chromium.org' |
| ] |
| expected_gerrit_cmd_2 = [ |
| '%s/chromite/bin/gerrit' % chroot_path, 'reviewers', |
| str(test_cl_number), 'none2@chromium.org' |
| ] |
| |
| self.assertEqual(mock_exec_cmd.call_count, 2) |
| self.assertEqual(mock_exec_cmd.call_args_list[0], |
| mock.call(expected_gerrit_cmd_1)) |
| self.assertEqual(mock_exec_cmd.call_args_list[1], |
| mock.call(expected_gerrit_cmd_2)) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |