#!/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 = 'http://ci.chromium.org/b/%s' % bb_id mock_cmd.return_value = json.dumps([{'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()