diff options
Diffstat (limited to 'llvm_tools/update_chromeos_llvm_hash_unittest.py')
-rwxr-xr-x | llvm_tools/update_chromeos_llvm_hash_unittest.py | 2034 |
1 files changed, 1177 insertions, 857 deletions
diff --git a/llvm_tools/update_chromeos_llvm_hash_unittest.py b/llvm_tools/update_chromeos_llvm_hash_unittest.py index adb20598..b758538c 100755 --- a/llvm_tools/update_chromeos_llvm_hash_unittest.py +++ b/llvm_tools/update_chromeos_llvm_hash_unittest.py @@ -1,18 +1,18 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2019 The Chromium OS Authors. All rights reserved. +# Copyright 2019 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Unit tests for updating LLVM hashes.""" -from __future__ import print_function import collections import datetime import os -import re +from pathlib import Path import subprocess +import sys import unittest import unittest.mock as mock @@ -20,865 +20,1185 @@ import chroot import failure_modes import get_llvm_hash import git -import llvm_patch_management import test_helpers import update_chromeos_llvm_hash + # These are unittests; protected access is OK to a point. # pylint: disable=protected-access class UpdateLLVMHashTest(unittest.TestCase): - """Test class for updating LLVM hashes of packages.""" - - @mock.patch.object(os.path, 'realpath') - def testDefaultCrosRootFromCrOSCheckout(self, mock_llvm_tools): - llvm_tools_path = '/path/to/cros/src/third_party/toolchain-utils/llvm_tools' - mock_llvm_tools.return_value = llvm_tools_path - self.assertEqual(update_chromeos_llvm_hash.defaultCrosRoot(), - '%s/../../../../' % llvm_tools_path) - - @mock.patch.object(os.path, 'realpath') - def testDefaultCrosRootFromOutsideCrOSCheckout(self, mock_llvm_tools): - mock_llvm_tools.return_value = '~/toolchain-utils/llvm_tools' - self.assertEqual(update_chromeos_llvm_hash.defaultCrosRoot(), - '~/chromiumos') - - # Simulate behavior of 'os.path.isfile()' when the ebuild path to a package - # does not exist. - @mock.patch.object(os.path, 'isfile', return_value=False) - def testFailedToUpdateLLVMHashForInvalidEbuildPath(self, mock_isfile): - ebuild_path = '/some/path/to/package.ebuild' - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current - git_hash = 'a123testhash1' - svn_version = 1000 - - # Verify the exception is raised when the ebuild path does not exist. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UpdateEbuildLLVMHash(ebuild_path, llvm_variant, - git_hash, svn_version) - - self.assertEqual( - str(err.exception), 'Invalid ebuild path provided: %s' % ebuild_path) - - mock_isfile.assert_called_once() - - # Simulate 'os.path.isfile' behavior on a valid ebuild path. - @mock.patch.object(os.path, 'isfile', return_value=True) - def testFailedToUpdateLLVMHash(self, mock_isfile): - # Create a temporary file to simulate an ebuild file of a package. - with test_helpers.CreateTemporaryJsonFile() as ebuild_file: - with open(ebuild_file, 'w') as f: - f.write('\n'.join([ - 'First line in the ebuild', 'Second line in the ebuild', - 'Last line in the ebuild' - ])) - - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current - git_hash = 'a123testhash1' - svn_version = 1000 - - # Verify the exception is raised when the ebuild file does not have - # 'LLVM_HASH'. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UpdateEbuildLLVMHash(ebuild_file, - llvm_variant, git_hash, - svn_version) - - self.assertEqual(str(err.exception), 'Failed to update LLVM_HASH') - - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next - - self.assertEqual(mock_isfile.call_count, 2) - - # Simulate 'os.path.isfile' behavior on a valid ebuild path. - @mock.patch.object(os.path, 'isfile', return_value=True) - def testFailedToUpdateLLVMNextHash(self, mock_isfile): - # Create a temporary file to simulate an ebuild file of a package. - with test_helpers.CreateTemporaryJsonFile() as ebuild_file: - with open(ebuild_file, 'w') as f: - f.write('\n'.join([ - 'First line in the ebuild', 'Second line in the ebuild', - 'Last line in the ebuild' - ])) - - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next - git_hash = 'a123testhash1' - svn_version = 1000 - - # Verify the exception is raised when the ebuild file does not have - # 'LLVM_NEXT_HASH'. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UpdateEbuildLLVMHash(ebuild_file, - llvm_variant, git_hash, - svn_version) - - self.assertEqual(str(err.exception), 'Failed to update LLVM_NEXT_HASH') - - self.assertEqual(mock_isfile.call_count, 2) - - @mock.patch.object(os.path, 'isfile', return_value=True) - @mock.patch.object(subprocess, 'check_output', return_value=None) - def testSuccessfullyStageTheEbuildForCommitForLLVMHashUpdate( - self, mock_stage_commit_command, mock_isfile): - - # Create a temporary file to simulate an ebuild file of a package. - with test_helpers.CreateTemporaryJsonFile() as ebuild_file: - # Updates LLVM_HASH to 'git_hash' and revision to - # 'svn_version'. - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current - git_hash = 'a123testhash1' - svn_version = 1000 - - with open(ebuild_file, 'w') as f: - f.write('\n'.join([ - 'First line in the ebuild', 'Second line in the ebuild', - 'LLVM_HASH=\"a12b34c56d78e90\" # r500', 'Last line in the ebuild' - ])) - - update_chromeos_llvm_hash.UpdateEbuildLLVMHash(ebuild_file, llvm_variant, - git_hash, svn_version) - - expected_file_contents = [ - 'First line in the ebuild\n', 'Second line in the ebuild\n', - 'LLVM_HASH=\"a123testhash1\" # r1000\n', 'Last line in the ebuild' - ] - - # Verify the new file contents of the ebuild file match the expected file - # contents. - with open(ebuild_file) as new_file: - file_contents_as_a_list = [cur_line for cur_line in new_file] - self.assertListEqual(file_contents_as_a_list, expected_file_contents) - - self.assertEqual(mock_isfile.call_count, 2) - - mock_stage_commit_command.assert_called_once() - - @mock.patch.object(os.path, 'isfile', return_value=True) - @mock.patch.object(subprocess, 'check_output', return_value=None) - def testSuccessfullyStageTheEbuildForCommitForLLVMNextHashUpdate( - self, mock_stage_commit_command, mock_isfile): - - # Create a temporary file to simulate an ebuild file of a package. - with test_helpers.CreateTemporaryJsonFile() as ebuild_file: - # Updates LLVM_NEXT_HASH to 'git_hash' and revision to - # 'svn_version'. - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next - git_hash = 'a123testhash1' - svn_version = 1000 - - with open(ebuild_file, 'w') as f: - f.write('\n'.join([ - 'First line in the ebuild', 'Second line in the ebuild', - 'LLVM_NEXT_HASH=\"a12b34c56d78e90\" # r500', - 'Last line in the ebuild' - ])) - - update_chromeos_llvm_hash.UpdateEbuildLLVMHash(ebuild_file, llvm_variant, - git_hash, svn_version) - - expected_file_contents = [ - 'First line in the ebuild\n', 'Second line in the ebuild\n', - 'LLVM_NEXT_HASH=\"a123testhash1\" # r1000\n', - 'Last line in the ebuild' - ] - - # Verify the new file contents of the ebuild file match the expected file - # contents. - with open(ebuild_file) as new_file: - file_contents_as_a_list = [cur_line for cur_line in new_file] - self.assertListEqual(file_contents_as_a_list, expected_file_contents) - - self.assertEqual(mock_isfile.call_count, 2) - - mock_stage_commit_command.assert_called_once() - - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - @mock.patch.object(os.path, 'islink', return_value=False) - def testFailedToUprevEbuildToVersionForInvalidSymlink(self, mock_islink, - mock_llvm_version): - symlink_path = '/path/to/chroot/package/package.ebuild' - svn_version = 1000 - git_hash = 'badf00d' - mock_llvm_version.return_value = '1234' - - # Verify the exception is raised when a invalid symbolic link is passed in. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UprevEbuildToVersion(symlink_path, svn_version, - git_hash) - - self.assertEqual( - str(err.exception), 'Invalid symlink provided: %s' % symlink_path) - - mock_islink.assert_called_once() - mock_llvm_version.assert_not_called() - - @mock.patch.object(os.path, 'islink', return_value=False) - def testFailedToUprevEbuildSymlinkForInvalidSymlink(self, mock_islink): - symlink_path = '/path/to/chroot/package/package.ebuild' - - # Verify the exception is raised when a invalid symbolic link is passed in. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UprevEbuildSymlink(symlink_path) - - self.assertEqual( - str(err.exception), 'Invalid symlink provided: %s' % symlink_path) - - mock_islink.assert_called_once() - - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - # Simulate 'os.path.islink' when a symbolic link is passed in. - @mock.patch.object(os.path, 'islink', return_value=True) - # Simulate 'os.path.realpath' when a symbolic link is passed in. - @mock.patch.object(os.path, 'realpath', return_value=True) - def testFailedToUprevEbuildToVersion(self, mock_realpath, mock_islink, - mock_llvm_version): - symlink_path = '/path/to/chroot/llvm/llvm_pre123_p.ebuild' - mock_realpath.return_value = '/abs/path/to/llvm/llvm_pre123_p.ebuild' - git_hash = 'badf00d' - mock_llvm_version.return_value = '1234' - svn_version = 1000 - - # Verify the exception is raised when the symlink does not match the - # expected pattern - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UprevEbuildToVersion(symlink_path, svn_version, - git_hash) - - self.assertEqual(str(err.exception), 'Failed to uprev the ebuild.') - - mock_llvm_version.assert_called_once_with(git_hash) - mock_islink.assert_called_once_with(symlink_path) - - # Simulate 'os.path.islink' when a symbolic link is passed in. - @mock.patch.object(os.path, 'islink', return_value=True) - def testFailedToUprevEbuildSymlink(self, mock_islink): - symlink_path = '/path/to/chroot/llvm/llvm_pre123_p.ebuild' - - # Verify the exception is raised when the symlink does not match the - # expected pattern - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UprevEbuildSymlink(symlink_path) - - self.assertEqual(str(err.exception), 'Failed to uprev the symlink.') - - mock_islink.assert_called_once_with(symlink_path) - - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - @mock.patch.object(os.path, 'islink', return_value=True) - @mock.patch.object(os.path, 'realpath') - @mock.patch.object(subprocess, 'check_output', return_value=None) - def testSuccessfullyUprevEbuildToVersionLLVM(self, mock_command_output, - mock_realpath, mock_islink, - mock_llvm_version): - symlink = '/path/to/llvm/llvm-12.0_pre3_p2-r10.ebuild' - ebuild = '/abs/path/to/llvm/llvm-12.0_pre3_p2.ebuild' - mock_realpath.return_value = ebuild - git_hash = 'badf00d' - mock_llvm_version.return_value = '1234' - svn_version = 1000 - - update_chromeos_llvm_hash.UprevEbuildToVersion(symlink, svn_version, - git_hash) - - mock_llvm_version.assert_called_once_with(git_hash) - - mock_islink.assert_called() - - mock_realpath.assert_called_once_with(symlink) - - mock_command_output.assert_called() - - # Verify commands - symlink_dir = os.path.dirname(symlink) - timestamp = datetime.datetime.today().strftime('%Y%m%d') - new_ebuild = '/abs/path/to/llvm/llvm-1234.0_pre1000_p%s.ebuild' % timestamp - new_symlink = new_ebuild[:-len('.ebuild')] + '-r1.ebuild' - - expected_cmd = ['git', '-C', symlink_dir, 'mv', ebuild, new_ebuild] - self.assertEqual(mock_command_output.call_args_list[0], - mock.call(expected_cmd)) - - expected_cmd = ['ln', '-s', '-r', new_ebuild, new_symlink] - self.assertEqual(mock_command_output.call_args_list[1], - mock.call(expected_cmd)) - - expected_cmd = ['git', '-C', symlink_dir, 'add', new_symlink] - self.assertEqual(mock_command_output.call_args_list[2], - mock.call(expected_cmd)) - - expected_cmd = ['git', '-C', symlink_dir, 'rm', symlink] - self.assertEqual(mock_command_output.call_args_list[3], - mock.call(expected_cmd)) - - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - @mock.patch.object(os.path, 'islink', return_value=True) - @mock.patch.object(os.path, 'realpath') - @mock.patch.object(subprocess, 'check_output', return_value=None) - def testSuccessfullyUprevEbuildToVersionNonLLVM(self, mock_command_output, - mock_realpath, mock_islink, - mock_llvm_version): - symlink = '/abs/path/to/compiler-rt/compiler-rt-12.0_pre314159265-r4.ebuild' - ebuild = '/abs/path/to/compiler-rt/compiler-rt-12.0_pre314159265.ebuild' - mock_realpath.return_value = ebuild - mock_llvm_version.return_value = '1234' - svn_version = 1000 - git_hash = '5678' - - update_chromeos_llvm_hash.UprevEbuildToVersion(symlink, svn_version, - git_hash) - - mock_islink.assert_called() - - mock_realpath.assert_called_once_with(symlink) - - mock_llvm_version.assert_called_once_with(git_hash) - - mock_command_output.assert_called() - - # Verify commands - symlink_dir = os.path.dirname(symlink) - new_ebuild = '/abs/path/to/compiler-rt/compiler-rt-1234.0_pre1000.ebuild' - new_symlink = new_ebuild[:-len('.ebuild')] + '-r1.ebuild' - - expected_cmd = ['git', '-C', symlink_dir, 'mv', ebuild, new_ebuild] - self.assertEqual(mock_command_output.call_args_list[0], - mock.call(expected_cmd)) - - expected_cmd = ['ln', '-s', '-r', new_ebuild, new_symlink] - self.assertEqual(mock_command_output.call_args_list[1], - mock.call(expected_cmd)) - - expected_cmd = ['git', '-C', symlink_dir, 'add', new_symlink] - self.assertEqual(mock_command_output.call_args_list[2], - mock.call(expected_cmd)) - - expected_cmd = ['git', '-C', symlink_dir, 'rm', symlink] - self.assertEqual(mock_command_output.call_args_list[3], - mock.call(expected_cmd)) - - @mock.patch.object(os.path, 'islink', return_value=True) - @mock.patch.object(subprocess, 'check_output', return_value=None) - def testSuccessfullyUprevEbuildSymlink(self, mock_command_output, - mock_islink): - symlink_to_uprev = '/symlink/to/package-r1.ebuild' - - update_chromeos_llvm_hash.UprevEbuildSymlink(symlink_to_uprev) - - mock_islink.assert_called_once_with(symlink_to_uprev) - - mock_command_output.assert_called_once() - - # Simulate behavior of 'os.path.isdir()' when the path to the repo is not a - - # directory. - - @mock.patch.object(chroot, 'GetChrootEbuildPaths') - @mock.patch.object(chroot, 'ConvertChrootPathsToAbsolutePaths') - def testExceptionRaisedWhenCreatingPathDictionaryFromPackages( - self, mock_chroot_paths_to_symlinks, mock_get_chroot_paths): - - chroot_path = '/some/path/to/chroot' - - package_name = 'test-pckg/package' - package_chroot_path = '/some/chroot/path/to/package-r1.ebuild' - - # Test function to simulate 'ConvertChrootPathsToAbsolutePaths' when a - # symlink does not start with the prefix '/mnt/host/source'. - def BadPrefixChrootPath(*args): - assert len(args) == 2 - raise ValueError('Invalid prefix for the chroot path: ' - '%s' % package_chroot_path) - - # Simulate 'GetChrootEbuildPaths' when valid packages are passed in. - # - # Returns a list of chroot paths. - mock_get_chroot_paths.return_value = [package_chroot_path] - - # Use test function to simulate 'ConvertChrootPathsToAbsolutePaths' - # behavior. - mock_chroot_paths_to_symlinks.side_effect = BadPrefixChrootPath - - # Verify exception is raised when for an invalid prefix in the symlink. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.CreatePathDictionaryFromPackages( - chroot_path, [package_name]) - - self.assertEqual( - str(err.exception), 'Invalid prefix for the chroot path: ' - '%s' % package_chroot_path) - - mock_get_chroot_paths.assert_called_once_with(chroot_path, [package_name]) - - mock_chroot_paths_to_symlinks.assert_called_once_with( - chroot_path, [package_chroot_path]) - - @mock.patch.object(chroot, 'GetChrootEbuildPaths') - @mock.patch.object(chroot, 'ConvertChrootPathsToAbsolutePaths') - @mock.patch.object(update_chromeos_llvm_hash, - 'GetEbuildPathsFromSymLinkPaths') - def testSuccessfullyCreatedPathDictionaryFromPackages( - self, mock_ebuild_paths_from_symlink_paths, mock_chroot_paths_to_symlinks, - mock_get_chroot_paths): - - package_chroot_path = '/mnt/host/source/src/path/to/package-r1.ebuild' - - # Simulate 'GetChrootEbuildPaths' when returning a chroot path for a valid - # package. - # - # Returns a list of chroot paths. - mock_get_chroot_paths.return_value = [package_chroot_path] - - package_symlink_path = '/some/path/to/chroot/src/path/to/package-r1.ebuild' - - # Simulate 'ConvertChrootPathsToAbsolutePaths' when returning a symlink to - # a chroot path that points to a package. - # - # Returns a list of symlink file paths. - mock_chroot_paths_to_symlinks.return_value = [package_symlink_path] - - chroot_package_path = '/some/path/to/chroot/src/path/to/package.ebuild' - - # Simulate 'GetEbuildPathsFromSymlinkPaths' when returning a dictionary of - # a symlink that points to an ebuild. - # - # Returns a dictionary of a symlink and ebuild file path pair - # where the key is the absolute path to the symlink of the ebuild file - # and the value is the absolute path to the ebuild file of the package. - mock_ebuild_paths_from_symlink_paths.return_value = { - package_symlink_path: chroot_package_path - } - - chroot_path = '/some/path/to/chroot' - package_name = 'test-pckg/package' - - self.assertEqual( - update_chromeos_llvm_hash.CreatePathDictionaryFromPackages( - chroot_path, [package_name]), - {package_symlink_path: chroot_package_path}) - - mock_get_chroot_paths.assert_called_once_with(chroot_path, [package_name]) - - mock_chroot_paths_to_symlinks.assert_called_once_with( - chroot_path, [package_chroot_path]) - - mock_ebuild_paths_from_symlink_paths.assert_called_once_with( - [package_symlink_path]) - - @mock.patch.object(subprocess, 'check_output', return_value=None) - def testSuccessfullyRemovedPatchesFromFilesDir(self, mock_run_cmd): - patches_to_remove_list = [ - '/abs/path/to/filesdir/cherry/fix_output.patch', - '/abs/path/to/filesdir/display_results.patch' - ] - - update_chromeos_llvm_hash.RemovePatchesFromFilesDir(patches_to_remove_list) - - self.assertEqual(mock_run_cmd.call_count, 2) - - @mock.patch.object(os.path, 'isfile', return_value=False) - def testInvalidPatchMetadataFileStagedForCommit(self, mock_isfile): - patch_metadata_path = '/abs/path/to/filesdir/PATCHES' - - # Verify the exception is raised when the absolute path to the patch - # metadata file does not exist or is not a file. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.StagePatchMetadataFileForCommit( - patch_metadata_path) - - self.assertEqual( - str(err.exception), 'Invalid patch metadata file provided: ' - '%s' % patch_metadata_path) - - mock_isfile.assert_called_once() - - @mock.patch.object(os.path, 'isfile', return_value=True) - @mock.patch.object(subprocess, 'check_output', return_value=None) - def testSuccessfullyStagedPatchMetadataFileForCommit(self, mock_run_cmd, _): - - patch_metadata_path = '/abs/path/to/filesdir/PATCHES.json' - - update_chromeos_llvm_hash.StagePatchMetadataFileForCommit( - patch_metadata_path) - - mock_run_cmd.assert_called_once() - - def testNoPatchResultsForCommit(self): - package_1_patch_info_dict = { - 'applied_patches': ['display_results.patch'], - 'failed_patches': ['fixes_output.patch'], - 'non_applicable_patches': [], - 'disabled_patches': [], - 'removed_patches': [], - 'modified_metadata': None - } - - package_2_patch_info_dict = { - 'applied_patches': ['redirects_stdout.patch', 'fix_display.patch'], - 'failed_patches': [], - 'non_applicable_patches': [], - 'disabled_patches': [], - 'removed_patches': [], - 'modified_metadata': None - } - - test_package_info_dict = { - 'test-packages/package1': package_1_patch_info_dict, - 'test-packages/package2': package_2_patch_info_dict - } - - test_commit_message = ['Updated packages'] - - self.assertListEqual( - update_chromeos_llvm_hash.StagePackagesPatchResultsForCommit( - test_package_info_dict, test_commit_message), test_commit_message) - - @mock.patch.object(update_chromeos_llvm_hash, - 'StagePatchMetadataFileForCommit') - @mock.patch.object(update_chromeos_llvm_hash, 'RemovePatchesFromFilesDir') - def testAddedPatchResultsForCommit(self, mock_remove_patches, - mock_stage_patches_for_commit): - - package_1_patch_info_dict = { - 'applied_patches': [], - 'failed_patches': [], - 'non_applicable_patches': [], - 'disabled_patches': ['fixes_output.patch'], - 'removed_patches': [], - 'modified_metadata': '/abs/path/to/filesdir/PATCHES.json' - } - - package_2_patch_info_dict = { - 'applied_patches': ['fix_display.patch'], - 'failed_patches': [], - 'non_applicable_patches': [], - 'disabled_patches': [], - 'removed_patches': ['/abs/path/to/filesdir/redirect_stdout.patch'], - 'modified_metadata': '/abs/path/to/filesdir/PATCHES.json' - } - - test_package_info_dict = { - 'test-packages/package1': package_1_patch_info_dict, - 'test-packages/package2': package_2_patch_info_dict - } - - test_commit_message = ['Updated packages'] - - expected_commit_messages = [ - 'Updated packages', '\nFor the package test-packages/package1:', - 'The patch metadata file PATCHES.json was modified', - 'The following patches were disabled:', 'fixes_output.patch', - '\nFor the package test-packages/package2:', - 'The patch metadata file PATCHES.json was modified', - 'The following patches were removed:', 'redirect_stdout.patch' - ] - - self.assertListEqual( - update_chromeos_llvm_hash.StagePackagesPatchResultsForCommit( - test_package_info_dict, test_commit_message), - expected_commit_messages) - - path_to_removed_patch = '/abs/path/to/filesdir/redirect_stdout.patch' - - mock_remove_patches.assert_called_once_with([path_to_removed_patch]) - - self.assertEqual(mock_stage_patches_for_commit.call_count, 2) - - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - @mock.patch.object(update_chromeos_llvm_hash, - 'CreatePathDictionaryFromPackages') - @mock.patch.object(git, 'CreateBranch') - @mock.patch.object(update_chromeos_llvm_hash, 'UpdateEbuildLLVMHash') - @mock.patch.object(update_chromeos_llvm_hash, 'UprevEbuildSymlink') - @mock.patch.object(git, 'UploadChanges') - @mock.patch.object(git, 'DeleteBranch') - @mock.patch.object(os.path, 'realpath') - def testExceptionRaisedWhenUpdatingPackages( - self, mock_realpath, mock_delete_repo, mock_upload_changes, - mock_uprev_symlink, mock_update_llvm_next, mock_create_repo, - mock_create_path_dict, mock_llvm_major_version): - - path_to_package_dir = '/some/path/to/chroot/src/path/to' - abs_path_to_package = os.path.join(path_to_package_dir, 'package.ebuild') - symlink_path_to_package = os.path.join(path_to_package_dir, - 'package-r1.ebuild') - - mock_llvm_major_version.return_value = '1234' - - # Test function to simulate 'CreateBranch' when successfully created the - # branch on a valid repo path. - def SuccessfullyCreateBranchForChanges(_, branch): - self.assertEqual(branch, 'update-LLVM_NEXT_HASH-a123testhash4') - - # Test function to simulate 'UpdateEbuildLLVMHash' when successfully - # updated the ebuild's 'LLVM_NEXT_HASH'. - def SuccessfullyUpdatedLLVMHash(ebuild_path, _, git_hash, svn_version): - self.assertEqual(ebuild_path, abs_path_to_package) - self.assertEqual(git_hash, 'a123testhash4') - self.assertEqual(svn_version, 1000) - - # Test function to simulate 'UprevEbuildSymlink' when the symlink to the - # ebuild does not have a revision number. - def FailedToUprevEbuildSymlink(_): - # Raises a 'ValueError' exception because the symlink did not have have a - # revision number. - raise ValueError('Failed to uprev the ebuild.') - - # Test function to fail on 'UploadChanges' if the function gets called - # when an exception is raised. - def ShouldNotExecuteUploadChanges(*args): - # Test function should not be called (i.e. execution should resume in the - # 'finally' block) because 'UprevEbuildSymlink' raised an - # exception. - assert len(args) == 3 - assert False, ('Failed to go to "finally" block ' - 'after the exception was raised.') - - test_package_path_dict = {symlink_path_to_package: abs_path_to_package} - - # Simulate behavior of 'CreatePathDictionaryFromPackages()' when - # successfully created a dictionary where the key is the absolute path to - # the symlink of the package and value is the absolute path to the ebuild of - # the package. - mock_create_path_dict.return_value = test_package_path_dict - - # Use test function to simulate behavior. - mock_create_repo.side_effect = SuccessfullyCreateBranchForChanges - mock_update_llvm_next.side_effect = SuccessfullyUpdatedLLVMHash - mock_uprev_symlink.side_effect = FailedToUprevEbuildSymlink - mock_upload_changes.side_effect = ShouldNotExecuteUploadChanges - mock_realpath.return_value = '/abs/path/to/test-packages/package1.ebuild' - - packages_to_update = ['test-packages/package1'] - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next - git_hash = 'a123testhash4' - svn_version = 1000 - chroot_path = '/some/path/to/chroot' - patch_metadata_file = 'PATCHES.json' - git_hash_source = 'google3' - branch = 'update-LLVM_NEXT_HASH-a123testhash4' - extra_commit_msg = None - - # Verify exception is raised when an exception is thrown within - # the 'try' block by UprevEbuildSymlink function. - with self.assertRaises(ValueError) as err: - update_chromeos_llvm_hash.UpdatePackages(packages_to_update, llvm_variant, - git_hash, svn_version, - chroot_path, patch_metadata_file, - failure_modes.FailureModes.FAIL, - git_hash_source, - extra_commit_msg) - - self.assertEqual(str(err.exception), 'Failed to uprev the ebuild.') - - mock_create_path_dict.assert_called_once_with(chroot_path, - packages_to_update) - - mock_create_repo.assert_called_once_with(path_to_package_dir, branch) - - mock_update_llvm_next.assert_called_once_with(abs_path_to_package, - llvm_variant, git_hash, - svn_version) - - mock_uprev_symlink.assert_called_once_with(symlink_path_to_package) - - mock_upload_changes.assert_not_called() - - mock_delete_repo.assert_called_once_with(path_to_package_dir, branch) - - @mock.patch.object(update_chromeos_llvm_hash, 'EnsurePackageMaskContains') - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - @mock.patch.object(update_chromeos_llvm_hash, - 'CreatePathDictionaryFromPackages') - @mock.patch.object(git, 'CreateBranch') - @mock.patch.object(update_chromeos_llvm_hash, 'UpdateEbuildLLVMHash') - @mock.patch.object(update_chromeos_llvm_hash, 'UprevEbuildSymlink') - @mock.patch.object(git, 'UploadChanges') - @mock.patch.object(git, 'DeleteBranch') - @mock.patch.object(llvm_patch_management, 'UpdatePackagesPatchMetadataFile') - @mock.patch.object(update_chromeos_llvm_hash, - 'StagePatchMetadataFileForCommit') - def testSuccessfullyUpdatedPackages(self, mock_stage_patch_file, - mock_update_package_metadata_file, - mock_delete_repo, mock_upload_changes, - mock_uprev_symlink, mock_update_llvm_next, - mock_create_repo, mock_create_path_dict, - mock_llvm_version, mock_mask_contains): - - path_to_package_dir = '/some/path/to/chroot/src/path/to' - abs_path_to_package = os.path.join(path_to_package_dir, 'package.ebuild') - symlink_path_to_package = os.path.join(path_to_package_dir, - 'package-r1.ebuild') - - # Test function to simulate 'CreateBranch' when successfully created the - # branch for the changes to be made to the ebuild files. - def SuccessfullyCreateBranchForChanges(_, branch): - self.assertEqual(branch, 'update-LLVM_NEXT_HASH-a123testhash5') - - # Test function to simulate 'UploadChanges' after a successfull update of - # 'LLVM_NEXT_HASH" of the ebuild file. - def SuccessfullyUpdatedLLVMHash(ebuild_path, _, git_hash, svn_version): - self.assertEqual(ebuild_path, - '/some/path/to/chroot/src/path/to/package.ebuild') - self.assertEqual(git_hash, 'a123testhash5') - self.assertEqual(svn_version, 1000) - - # Test function to simulate 'UprevEbuildSymlink' when successfully - # incremented the revision number by 1. - def SuccessfullyUprevedEbuildSymlink(symlink_path): - self.assertEqual(symlink_path, - '/some/path/to/chroot/src/path/to/package-r1.ebuild') - - # Test function to simulate 'UpdatePackagesPatchMetadataFile()' when the - # patch results contains a disabled patch in 'disable_patches' mode. - def RetrievedPatchResults(chroot_path, svn_version, patch_metadata_file, - packages, mode): - - self.assertEqual(chroot_path, '/some/path/to/chroot') - self.assertEqual(svn_version, 1000) - self.assertEqual(patch_metadata_file, 'PATCHES.json') - self.assertListEqual(packages, ['path/to']) - self.assertEqual(mode, failure_modes.FailureModes.DISABLE_PATCHES) - - PatchInfo = collections.namedtuple('PatchInfo', [ - 'applied_patches', 'failed_patches', 'non_applicable_patches', - 'disabled_patches', 'removed_patches', 'modified_metadata' - ]) - - package_patch_info = PatchInfo( - applied_patches=['fix_display.patch'], - failed_patches=['fix_stdout.patch'], - non_applicable_patches=[], - disabled_patches=['fix_stdout.patch'], - removed_patches=[], - modified_metadata='/abs/path/to/filesdir/%s' % patch_metadata_file) - - package_info_dict = {'path/to': package_patch_info._asdict()} - - # Returns a dictionary where the key is the package and the value is a - # dictionary that contains information about the package's patch results - # produced by the patch manager. - return package_info_dict - - # Test function to simulate 'UploadChanges()' when successfully created a - # commit for the changes made to the packages and their patches and - # retrieved the change list of the commit. - def SuccessfullyUploadedChanges(*args): - assert len(args) == 3 - commit_url = 'https://some_name/path/to/commit/+/12345' - return git.CommitContents(url=commit_url, cl_number=12345) - - test_package_path_dict = {symlink_path_to_package: abs_path_to_package} - - # Simulate behavior of 'CreatePathDictionaryFromPackages()' when - # successfully created a dictionary where the key is the absolute path to - # the symlink of the package and value is the absolute path to the ebuild of - # the package. - mock_create_path_dict.return_value = test_package_path_dict - - # Use test function to simulate behavior. - mock_create_repo.side_effect = SuccessfullyCreateBranchForChanges - mock_update_llvm_next.side_effect = SuccessfullyUpdatedLLVMHash - mock_uprev_symlink.side_effect = SuccessfullyUprevedEbuildSymlink - mock_update_package_metadata_file.side_effect = RetrievedPatchResults - mock_upload_changes.side_effect = SuccessfullyUploadedChanges - mock_llvm_version.return_value = '1234' - mock_mask_contains.reurn_value = None - - packages_to_update = ['test-packages/package1'] - llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next - git_hash = 'a123testhash5' - svn_version = 1000 - chroot_path = '/some/path/to/chroot' - patch_metadata_file = 'PATCHES.json' - git_hash_source = 'tot' - branch = 'update-LLVM_NEXT_HASH-a123testhash5' - extra_commit_msg = '\ncommit-message-end' - - change_list = update_chromeos_llvm_hash.UpdatePackages( - packages_to_update, llvm_variant, git_hash, svn_version, chroot_path, - patch_metadata_file, failure_modes.FailureModes.DISABLE_PATCHES, - git_hash_source, extra_commit_msg) - - self.assertEqual(change_list.url, - 'https://some_name/path/to/commit/+/12345') - - self.assertEqual(change_list.cl_number, 12345) - - mock_create_path_dict.assert_called_once_with(chroot_path, - packages_to_update) - - mock_create_repo.assert_called_once_with(path_to_package_dir, branch) - - mock_update_llvm_next.assert_called_once_with(abs_path_to_package, - llvm_variant, git_hash, - svn_version) - - mock_uprev_symlink.assert_called_once_with(symlink_path_to_package) - - mock_mask_contains.assert_called_once_with(chroot_path, git_hash) - - expected_commit_messages = [ - 'llvm-next/tot: upgrade to a123testhash5 (r1000)\n', - 'The following packages have been updated:', 'path/to', - '\nFor the package path/to:', - 'The patch metadata file PATCHES.json was modified', - 'The following patches were disabled:', 'fix_stdout.patch', - '\ncommit-message-end' - ] - - mock_update_package_metadata_file.assert_called_once() - - mock_stage_patch_file.assert_called_once_with( - '/abs/path/to/filesdir/PATCHES.json') - - mock_upload_changes.assert_called_once_with(path_to_package_dir, branch, - expected_commit_messages) - - mock_delete_repo.assert_called_once_with(path_to_package_dir, branch) - - @mock.patch.object(subprocess, 'check_output', return_value=None) - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - def testEnsurePackageMaskContainsExisting(self, mock_llvm_version, - mock_git_add): - chroot_path = 'absolute/path/to/chroot' - git_hash = 'badf00d' - mock_llvm_version.return_value = '1234' - with mock.patch( - 'update_chromeos_llvm_hash.open', - mock.mock_open(read_data='\n=sys-devel/llvm-1234.0_pre*\n'), - create=True) as mock_file: - update_chromeos_llvm_hash.EnsurePackageMaskContains(chroot_path, git_hash) - handle = mock_file() - handle.write.assert_not_called() - mock_llvm_version.assert_called_once_with(git_hash) - - overlay_dir = 'absolute/path/to/chroot/src/third_party/chromiumos-overlay' - mask_path = overlay_dir + '/profiles/targets/chromeos/package.mask' - mock_git_add.assert_called_once_with( - ['git', '-C', overlay_dir, 'add', mask_path]) - - @mock.patch.object(subprocess, 'check_output', return_value=None) - @mock.patch.object(get_llvm_hash, 'GetLLVMMajorVersion') - def testEnsurePackageMaskContainsNotExisting(self, mock_llvm_version, - mock_git_add): - chroot_path = 'absolute/path/to/chroot' - git_hash = 'badf00d' - mock_llvm_version.return_value = '1234' - with mock.patch( - 'update_chromeos_llvm_hash.open', - mock.mock_open(read_data='nothing relevant'), - create=True) as mock_file: - update_chromeos_llvm_hash.EnsurePackageMaskContains(chroot_path, git_hash) - handle = mock_file() - handle.write.assert_called_once_with('=sys-devel/llvm-1234.0_pre*\n') - mock_llvm_version.assert_called_once_with(git_hash) - - overlay_dir = 'absolute/path/to/chroot/src/third_party/chromiumos-overlay' - mask_path = overlay_dir + '/profiles/targets/chromeos/package.mask' - mock_git_add.assert_called_once_with( - ['git', '-C', overlay_dir, 'add', mask_path]) - - -if __name__ == '__main__': - unittest.main() + """Test class for updating LLVM hashes of packages.""" + + @mock.patch.object(os.path, "realpath") + def testDefaultCrosRootFromCrOSCheckout(self, mock_llvm_tools): + llvm_tools_path = ( + "/path/to/cros/src/third_party/toolchain-utils/llvm_tools" + ) + mock_llvm_tools.return_value = llvm_tools_path + self.assertEqual( + update_chromeos_llvm_hash.defaultCrosRoot(), Path("/path/to/cros") + ) + + @mock.patch.object(os.path, "realpath") + def testDefaultCrosRootFromOutsideCrOSCheckout(self, mock_llvm_tools): + mock_llvm_tools.return_value = "~/toolchain-utils/llvm_tools" + self.assertEqual( + update_chromeos_llvm_hash.defaultCrosRoot(), + Path.home() / "chromiumos", + ) + + # Simulate behavior of 'os.path.isfile()' when the ebuild path to a package + # does not exist. + @mock.patch.object(os.path, "isfile", return_value=False) + def testFailedToUpdateLLVMHashForInvalidEbuildPath(self, mock_isfile): + ebuild_path = "/some/path/to/package.ebuild" + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current + git_hash = "a123testhash1" + svn_version = 1000 + + # Verify the exception is raised when the ebuild path does not exist. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UpdateEbuildLLVMHash( + ebuild_path, llvm_variant, git_hash, svn_version + ) + + self.assertEqual( + str(err.exception), "Invalid ebuild path provided: %s" % ebuild_path + ) + + mock_isfile.assert_called_once() + + # Simulate 'os.path.isfile' behavior on a valid ebuild path. + @mock.patch.object(os.path, "isfile", return_value=True) + def testFailedToUpdateLLVMHash(self, mock_isfile): + # Create a temporary file to simulate an ebuild file of a package. + with test_helpers.CreateTemporaryJsonFile() as ebuild_file: + with open(ebuild_file, "w") as f: + f.write( + "\n".join( + [ + "First line in the ebuild", + "Second line in the ebuild", + "Last line in the ebuild", + ] + ) + ) + + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current + git_hash = "a123testhash1" + svn_version = 1000 + + # Verify the exception is raised when the ebuild file does not have + # 'LLVM_HASH'. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UpdateEbuildLLVMHash( + ebuild_file, llvm_variant, git_hash, svn_version + ) + + self.assertEqual(str(err.exception), "Failed to update LLVM_HASH") + + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next + + self.assertEqual(mock_isfile.call_count, 2) + + # Simulate 'os.path.isfile' behavior on a valid ebuild path. + @mock.patch.object(os.path, "isfile", return_value=True) + def testFailedToUpdateLLVMNextHash(self, mock_isfile): + # Create a temporary file to simulate an ebuild file of a package. + with test_helpers.CreateTemporaryJsonFile() as ebuild_file: + with open(ebuild_file, "w") as f: + f.write( + "\n".join( + [ + "First line in the ebuild", + "Second line in the ebuild", + "Last line in the ebuild", + ] + ) + ) + + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next + git_hash = "a123testhash1" + svn_version = 1000 + + # Verify the exception is raised when the ebuild file does not have + # 'LLVM_NEXT_HASH'. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UpdateEbuildLLVMHash( + ebuild_file, llvm_variant, git_hash, svn_version + ) + + self.assertEqual( + str(err.exception), "Failed to update LLVM_NEXT_HASH" + ) + + self.assertEqual(mock_isfile.call_count, 2) + + @mock.patch.object(os.path, "isfile", return_value=True) + @mock.patch.object(subprocess, "check_output", return_value=None) + def testSuccessfullyStageTheEbuildForCommitForLLVMHashUpdate( + self, mock_stage_commit_command, mock_isfile + ): + + # Create a temporary file to simulate an ebuild file of a package. + with test_helpers.CreateTemporaryJsonFile() as ebuild_file: + # Updates LLVM_HASH to 'git_hash' and revision to + # 'svn_version'. + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current + git_hash = "a123testhash1" + svn_version = 1000 + + with open(ebuild_file, "w") as f: + f.write( + "\n".join( + [ + "First line in the ebuild", + "Second line in the ebuild", + 'LLVM_HASH="a12b34c56d78e90" # r500', + "Last line in the ebuild", + ] + ) + ) + + update_chromeos_llvm_hash.UpdateEbuildLLVMHash( + ebuild_file, llvm_variant, git_hash, svn_version + ) + + expected_file_contents = [ + "First line in the ebuild\n", + "Second line in the ebuild\n", + 'LLVM_HASH="a123testhash1" # r1000\n', + "Last line in the ebuild", + ] + + # Verify the new file contents of the ebuild file match the expected file + # contents. + with open(ebuild_file) as new_file: + file_contents_as_a_list = [cur_line for cur_line in new_file] + self.assertListEqual( + file_contents_as_a_list, expected_file_contents + ) + + self.assertEqual(mock_isfile.call_count, 2) + + mock_stage_commit_command.assert_called_once() + + @mock.patch.object(os.path, "isfile", return_value=True) + @mock.patch.object(subprocess, "check_output", return_value=None) + def testSuccessfullyStageTheEbuildForCommitForLLVMNextHashUpdate( + self, mock_stage_commit_command, mock_isfile + ): + + # Create a temporary file to simulate an ebuild file of a package. + with test_helpers.CreateTemporaryJsonFile() as ebuild_file: + # Updates LLVM_NEXT_HASH to 'git_hash' and revision to + # 'svn_version'. + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next + git_hash = "a123testhash1" + svn_version = 1000 + + with open(ebuild_file, "w") as f: + f.write( + "\n".join( + [ + "First line in the ebuild", + "Second line in the ebuild", + 'LLVM_NEXT_HASH="a12b34c56d78e90" # r500', + "Last line in the ebuild", + ] + ) + ) + + update_chromeos_llvm_hash.UpdateEbuildLLVMHash( + ebuild_file, llvm_variant, git_hash, svn_version + ) + + expected_file_contents = [ + "First line in the ebuild\n", + "Second line in the ebuild\n", + 'LLVM_NEXT_HASH="a123testhash1" # r1000\n', + "Last line in the ebuild", + ] + + # Verify the new file contents of the ebuild file match the expected file + # contents. + with open(ebuild_file) as new_file: + file_contents_as_a_list = [cur_line for cur_line in new_file] + self.assertListEqual( + file_contents_as_a_list, expected_file_contents + ) + + self.assertEqual(mock_isfile.call_count, 2) + + mock_stage_commit_command.assert_called_once() + + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + @mock.patch.object(os.path, "islink", return_value=False) + def testFailedToUprevEbuildToVersionForInvalidSymlink( + self, mock_islink, mock_llvm_version + ): + symlink_path = "/path/to/chroot/package/package.ebuild" + svn_version = 1000 + git_hash = "badf00d" + mock_llvm_version.return_value = "1234" + + # Verify the exception is raised when a invalid symbolic link is passed in. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UprevEbuildToVersion( + symlink_path, svn_version, git_hash + ) + + self.assertEqual( + str(err.exception), "Invalid symlink provided: %s" % symlink_path + ) + + mock_islink.assert_called_once() + mock_llvm_version.assert_not_called() + + @mock.patch.object(os.path, "islink", return_value=False) + def testFailedToUprevEbuildSymlinkForInvalidSymlink(self, mock_islink): + symlink_path = "/path/to/chroot/package/package.ebuild" + + # Verify the exception is raised when a invalid symbolic link is passed in. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UprevEbuildSymlink(symlink_path) + + self.assertEqual( + str(err.exception), "Invalid symlink provided: %s" % symlink_path + ) + + mock_islink.assert_called_once() + + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + # Simulate 'os.path.islink' when a symbolic link is passed in. + @mock.patch.object(os.path, "islink", return_value=True) + # Simulate 'os.path.realpath' when a symbolic link is passed in. + @mock.patch.object(os.path, "realpath", return_value=True) + def testFailedToUprevEbuildToVersion( + self, mock_realpath, mock_islink, mock_llvm_version + ): + symlink_path = "/path/to/chroot/llvm/llvm_pre123_p.ebuild" + mock_realpath.return_value = "/abs/path/to/llvm/llvm_pre123_p.ebuild" + git_hash = "badf00d" + mock_llvm_version.return_value = "1234" + svn_version = 1000 + + # Verify the exception is raised when the symlink does not match the + # expected pattern + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UprevEbuildToVersion( + symlink_path, svn_version, git_hash + ) + + self.assertEqual(str(err.exception), "Failed to uprev the ebuild.") + + mock_llvm_version.assert_called_once_with(git_hash) + mock_islink.assert_called_once_with(symlink_path) + + # Simulate 'os.path.islink' when a symbolic link is passed in. + @mock.patch.object(os.path, "islink", return_value=True) + def testFailedToUprevEbuildSymlink(self, mock_islink): + symlink_path = "/path/to/chroot/llvm/llvm_pre123_p.ebuild" + + # Verify the exception is raised when the symlink does not match the + # expected pattern + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UprevEbuildSymlink(symlink_path) + + self.assertEqual(str(err.exception), "Failed to uprev the symlink.") + + mock_islink.assert_called_once_with(symlink_path) + + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + @mock.patch.object(os.path, "islink", return_value=True) + @mock.patch.object(os.path, "realpath") + @mock.patch.object(subprocess, "check_output", return_value=None) + def testSuccessfullyUprevEbuildToVersionLLVM( + self, mock_command_output, mock_realpath, mock_islink, mock_llvm_version + ): + symlink = "/path/to/llvm/llvm-12.0_pre3_p2-r10.ebuild" + ebuild = "/abs/path/to/llvm/llvm-12.0_pre3_p2.ebuild" + mock_realpath.return_value = ebuild + git_hash = "badf00d" + mock_llvm_version.return_value = "1234" + svn_version = 1000 + + update_chromeos_llvm_hash.UprevEbuildToVersion( + symlink, svn_version, git_hash + ) + + mock_llvm_version.assert_called_once_with(git_hash) + + mock_islink.assert_called() + + mock_realpath.assert_called_once_with(symlink) + + mock_command_output.assert_called() + + # Verify commands + symlink_dir = os.path.dirname(symlink) + timestamp = datetime.datetime.today().strftime("%Y%m%d") + new_ebuild = ( + "/abs/path/to/llvm/llvm-1234.0_pre1000_p%s.ebuild" % timestamp + ) + new_symlink = new_ebuild[: -len(".ebuild")] + "-r1.ebuild" + + expected_cmd = ["git", "-C", symlink_dir, "mv", ebuild, new_ebuild] + self.assertEqual( + mock_command_output.call_args_list[0], mock.call(expected_cmd) + ) + + expected_cmd = ["ln", "-s", "-r", new_ebuild, new_symlink] + self.assertEqual( + mock_command_output.call_args_list[1], mock.call(expected_cmd) + ) + + expected_cmd = ["git", "-C", symlink_dir, "add", new_symlink] + self.assertEqual( + mock_command_output.call_args_list[2], mock.call(expected_cmd) + ) + + expected_cmd = ["git", "-C", symlink_dir, "rm", symlink] + self.assertEqual( + mock_command_output.call_args_list[3], mock.call(expected_cmd) + ) + + @mock.patch.object( + chroot, + "GetChrootEbuildPaths", + return_value=["/chroot/path/test.ebuild"], + ) + @mock.patch.object(subprocess, "check_output", return_value="") + def testManifestUpdate(self, mock_subprocess, mock_ebuild_paths): + manifest_packages = ["sys-devel/llvm"] + chroot_path = "/path/to/chroot" + update_chromeos_llvm_hash.UpdateManifests( + manifest_packages, chroot_path + ) + + args = mock_subprocess.call_args[0][-1] + manifest_cmd = [ + "cros_sdk", + "--", + "ebuild", + "/chroot/path/test.ebuild", + "manifest", + ] + self.assertEqual(args, manifest_cmd) + mock_ebuild_paths.assert_called_once() + + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + @mock.patch.object(os.path, "islink", return_value=True) + @mock.patch.object(os.path, "realpath") + @mock.patch.object(subprocess, "check_output", return_value=None) + def testSuccessfullyUprevEbuildToVersionNonLLVM( + self, mock_command_output, mock_realpath, mock_islink, mock_llvm_version + ): + symlink = ( + "/abs/path/to/compiler-rt/compiler-rt-12.0_pre314159265-r4.ebuild" + ) + ebuild = "/abs/path/to/compiler-rt/compiler-rt-12.0_pre314159265.ebuild" + mock_realpath.return_value = ebuild + mock_llvm_version.return_value = "1234" + svn_version = 1000 + git_hash = "5678" + + update_chromeos_llvm_hash.UprevEbuildToVersion( + symlink, svn_version, git_hash + ) + + mock_islink.assert_called() + + mock_realpath.assert_called_once_with(symlink) + + mock_llvm_version.assert_called_once_with(git_hash) + + mock_command_output.assert_called() + + # Verify commands + symlink_dir = os.path.dirname(symlink) + new_ebuild = ( + "/abs/path/to/compiler-rt/compiler-rt-1234.0_pre1000.ebuild" + ) + new_symlink = new_ebuild[: -len(".ebuild")] + "-r1.ebuild" + + expected_cmd = ["git", "-C", symlink_dir, "mv", ebuild, new_ebuild] + self.assertEqual( + mock_command_output.call_args_list[0], mock.call(expected_cmd) + ) + + expected_cmd = ["ln", "-s", "-r", new_ebuild, new_symlink] + self.assertEqual( + mock_command_output.call_args_list[1], mock.call(expected_cmd) + ) + + expected_cmd = ["git", "-C", symlink_dir, "add", new_symlink] + self.assertEqual( + mock_command_output.call_args_list[2], mock.call(expected_cmd) + ) + + expected_cmd = ["git", "-C", symlink_dir, "rm", symlink] + self.assertEqual( + mock_command_output.call_args_list[3], mock.call(expected_cmd) + ) + + @mock.patch.object(os.path, "islink", return_value=True) + @mock.patch.object(subprocess, "check_output", return_value=None) + def testSuccessfullyUprevEbuildSymlink( + self, mock_command_output, mock_islink + ): + symlink_to_uprev = "/symlink/to/package-r1.ebuild" + + update_chromeos_llvm_hash.UprevEbuildSymlink(symlink_to_uprev) + + mock_islink.assert_called_once_with(symlink_to_uprev) + + mock_command_output.assert_called_once() + + # Simulate behavior of 'os.path.isdir()' when the path to the repo is not a + + # directory. + + @mock.patch.object(chroot, "GetChrootEbuildPaths") + @mock.patch.object(chroot, "ConvertChrootPathsToAbsolutePaths") + def testExceptionRaisedWhenCreatingPathDictionaryFromPackages( + self, mock_chroot_paths_to_symlinks, mock_get_chroot_paths + ): + + chroot_path = "/some/path/to/chroot" + + package_name = "test-pckg/package" + package_chroot_path = "/some/chroot/path/to/package-r1.ebuild" + + # Test function to simulate 'ConvertChrootPathsToAbsolutePaths' when a + # symlink does not start with the prefix '/mnt/host/source'. + def BadPrefixChrootPath(*args): + assert len(args) == 2 + raise ValueError( + "Invalid prefix for the chroot path: " + "%s" % package_chroot_path + ) + + # Simulate 'GetChrootEbuildPaths' when valid packages are passed in. + # + # Returns a list of chroot paths. + mock_get_chroot_paths.return_value = [package_chroot_path] + + # Use test function to simulate 'ConvertChrootPathsToAbsolutePaths' + # behavior. + mock_chroot_paths_to_symlinks.side_effect = BadPrefixChrootPath + + # Verify exception is raised when for an invalid prefix in the symlink. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.CreatePathDictionaryFromPackages( + chroot_path, [package_name] + ) + + self.assertEqual( + str(err.exception), + "Invalid prefix for the chroot path: " "%s" % package_chroot_path, + ) + + mock_get_chroot_paths.assert_called_once_with( + chroot_path, [package_name] + ) + + mock_chroot_paths_to_symlinks.assert_called_once_with( + chroot_path, [package_chroot_path] + ) + + @mock.patch.object(chroot, "GetChrootEbuildPaths") + @mock.patch.object(chroot, "ConvertChrootPathsToAbsolutePaths") + @mock.patch.object( + update_chromeos_llvm_hash, "GetEbuildPathsFromSymLinkPaths" + ) + def testSuccessfullyCreatedPathDictionaryFromPackages( + self, + mock_ebuild_paths_from_symlink_paths, + mock_chroot_paths_to_symlinks, + mock_get_chroot_paths, + ): + + package_chroot_path = "/mnt/host/source/src/path/to/package-r1.ebuild" + + # Simulate 'GetChrootEbuildPaths' when returning a chroot path for a valid + # package. + # + # Returns a list of chroot paths. + mock_get_chroot_paths.return_value = [package_chroot_path] + + package_symlink_path = ( + "/some/path/to/chroot/src/path/to/package-r1.ebuild" + ) + + # Simulate 'ConvertChrootPathsToAbsolutePaths' when returning a symlink to + # a chroot path that points to a package. + # + # Returns a list of symlink file paths. + mock_chroot_paths_to_symlinks.return_value = [package_symlink_path] + + chroot_package_path = "/some/path/to/chroot/src/path/to/package.ebuild" + + # Simulate 'GetEbuildPathsFromSymlinkPaths' when returning a dictionary of + # a symlink that points to an ebuild. + # + # Returns a dictionary of a symlink and ebuild file path pair + # where the key is the absolute path to the symlink of the ebuild file + # and the value is the absolute path to the ebuild file of the package. + mock_ebuild_paths_from_symlink_paths.return_value = { + package_symlink_path: chroot_package_path + } + + chroot_path = "/some/path/to/chroot" + package_name = "test-pckg/package" + + self.assertEqual( + update_chromeos_llvm_hash.CreatePathDictionaryFromPackages( + chroot_path, [package_name] + ), + {package_symlink_path: chroot_package_path}, + ) + + mock_get_chroot_paths.assert_called_once_with( + chroot_path, [package_name] + ) + + mock_chroot_paths_to_symlinks.assert_called_once_with( + chroot_path, [package_chroot_path] + ) + + mock_ebuild_paths_from_symlink_paths.assert_called_once_with( + [package_symlink_path] + ) + + @mock.patch.object(subprocess, "check_output", return_value=None) + def testSuccessfullyRemovedPatchesFromFilesDir(self, mock_run_cmd): + patches_to_remove_list = [ + "/abs/path/to/filesdir/cherry/fix_output.patch", + "/abs/path/to/filesdir/display_results.patch", + ] + + update_chromeos_llvm_hash.RemovePatchesFromFilesDir( + patches_to_remove_list + ) + + self.assertEqual(mock_run_cmd.call_count, 2) + + @mock.patch.object(os.path, "isfile", return_value=False) + def testInvalidPatchMetadataFileStagedForCommit(self, mock_isfile): + patch_metadata_path = "/abs/path/to/filesdir/PATCHES" + + # Verify the exception is raised when the absolute path to the patch + # metadata file does not exist or is not a file. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.StagePatchMetadataFileForCommit( + patch_metadata_path + ) + + self.assertEqual( + str(err.exception), + "Invalid patch metadata file provided: " "%s" % patch_metadata_path, + ) + + mock_isfile.assert_called_once() + + @mock.patch.object(os.path, "isfile", return_value=True) + @mock.patch.object(subprocess, "check_output", return_value=None) + def testSuccessfullyStagedPatchMetadataFileForCommit(self, mock_run_cmd, _): + + patch_metadata_path = "/abs/path/to/filesdir/PATCHES.json" + + update_chromeos_llvm_hash.StagePatchMetadataFileForCommit( + patch_metadata_path + ) + + mock_run_cmd.assert_called_once() + + def testNoPatchResultsForCommit(self): + package_1_patch_info_dict = { + "applied_patches": ["display_results.patch"], + "failed_patches": ["fixes_output.patch"], + "non_applicable_patches": [], + "disabled_patches": [], + "removed_patches": [], + "modified_metadata": None, + } + + package_2_patch_info_dict = { + "applied_patches": ["redirects_stdout.patch", "fix_display.patch"], + "failed_patches": [], + "non_applicable_patches": [], + "disabled_patches": [], + "removed_patches": [], + "modified_metadata": None, + } + + test_package_info_dict = { + "test-packages/package1": package_1_patch_info_dict, + "test-packages/package2": package_2_patch_info_dict, + } + + test_commit_message = ["Updated packages"] + + self.assertListEqual( + update_chromeos_llvm_hash.StagePackagesPatchResultsForCommit( + test_package_info_dict, test_commit_message + ), + test_commit_message, + ) + + @mock.patch.object( + update_chromeos_llvm_hash, "StagePatchMetadataFileForCommit" + ) + @mock.patch.object(update_chromeos_llvm_hash, "RemovePatchesFromFilesDir") + def testAddedPatchResultsForCommit( + self, mock_remove_patches, mock_stage_patches_for_commit + ): + + package_1_patch_info_dict = { + "applied_patches": [], + "failed_patches": [], + "non_applicable_patches": [], + "disabled_patches": ["fixes_output.patch"], + "removed_patches": [], + "modified_metadata": "/abs/path/to/filesdir/PATCHES.json", + } + + package_2_patch_info_dict = { + "applied_patches": ["fix_display.patch"], + "failed_patches": [], + "non_applicable_patches": [], + "disabled_patches": [], + "removed_patches": ["/abs/path/to/filesdir/redirect_stdout.patch"], + "modified_metadata": "/abs/path/to/filesdir/PATCHES.json", + } + + test_package_info_dict = { + "test-packages/package1": package_1_patch_info_dict, + "test-packages/package2": package_2_patch_info_dict, + } + + test_commit_message = ["Updated packages"] + + expected_commit_messages = [ + "Updated packages", + "\nFor the package test-packages/package1:", + "The patch metadata file PATCHES.json was modified", + "The following patches were disabled:", + "fixes_output.patch", + "\nFor the package test-packages/package2:", + "The patch metadata file PATCHES.json was modified", + "The following patches were removed:", + "redirect_stdout.patch", + ] + + self.assertListEqual( + update_chromeos_llvm_hash.StagePackagesPatchResultsForCommit( + test_package_info_dict, test_commit_message + ), + expected_commit_messages, + ) + + path_to_removed_patch = "/abs/path/to/filesdir/redirect_stdout.patch" + + mock_remove_patches.assert_called_once_with([path_to_removed_patch]) + + self.assertEqual(mock_stage_patches_for_commit.call_count, 2) + + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + @mock.patch.object( + update_chromeos_llvm_hash, "CreatePathDictionaryFromPackages" + ) + @mock.patch.object(git, "CreateBranch") + @mock.patch.object(update_chromeos_llvm_hash, "UpdateEbuildLLVMHash") + @mock.patch.object(update_chromeos_llvm_hash, "UprevEbuildSymlink") + @mock.patch.object(git, "UploadChanges") + @mock.patch.object(git, "DeleteBranch") + @mock.patch.object(os.path, "realpath") + def testExceptionRaisedWhenUpdatingPackages( + self, + mock_realpath, + mock_delete_repo, + mock_upload_changes, + mock_uprev_symlink, + mock_update_llvm_next, + mock_create_repo, + mock_create_path_dict, + mock_llvm_major_version, + ): + + path_to_package_dir = "/some/path/to/chroot/src/path/to" + abs_path_to_package = os.path.join( + path_to_package_dir, "package.ebuild" + ) + symlink_path_to_package = os.path.join( + path_to_package_dir, "package-r1.ebuild" + ) + + mock_llvm_major_version.return_value = "1234" + + # Test function to simulate 'CreateBranch' when successfully created the + # branch on a valid repo path. + def SuccessfullyCreateBranchForChanges(_, branch): + self.assertEqual(branch, "update-LLVM_NEXT_HASH-a123testhash4") + + # Test function to simulate 'UpdateEbuildLLVMHash' when successfully + # updated the ebuild's 'LLVM_NEXT_HASH'. + def SuccessfullyUpdatedLLVMHash(ebuild_path, _, git_hash, svn_version): + self.assertEqual(ebuild_path, abs_path_to_package) + self.assertEqual(git_hash, "a123testhash4") + self.assertEqual(svn_version, 1000) + + # Test function to simulate 'UprevEbuildSymlink' when the symlink to the + # ebuild does not have a revision number. + def FailedToUprevEbuildSymlink(_): + # Raises a 'ValueError' exception because the symlink did not have have a + # revision number. + raise ValueError("Failed to uprev the ebuild.") + + # Test function to fail on 'UploadChanges' if the function gets called + # when an exception is raised. + def ShouldNotExecuteUploadChanges(*args): + # Test function should not be called (i.e. execution should resume in the + # 'finally' block) because 'UprevEbuildSymlink' raised an + # exception. + assert len(args) == 3 + assert False, ( + 'Failed to go to "finally" block ' + "after the exception was raised." + ) + + test_package_path_dict = {symlink_path_to_package: abs_path_to_package} + + # Simulate behavior of 'CreatePathDictionaryFromPackages()' when + # successfully created a dictionary where the key is the absolute path to + # the symlink of the package and value is the absolute path to the ebuild of + # the package. + mock_create_path_dict.return_value = test_package_path_dict + + # Use test function to simulate behavior. + mock_create_repo.side_effect = SuccessfullyCreateBranchForChanges + mock_update_llvm_next.side_effect = SuccessfullyUpdatedLLVMHash + mock_uprev_symlink.side_effect = FailedToUprevEbuildSymlink + mock_upload_changes.side_effect = ShouldNotExecuteUploadChanges + mock_realpath.return_value = ( + "/abs/path/to/test-packages/package1.ebuild" + ) + + packages_to_update = ["test-packages/package1"] + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next + git_hash = "a123testhash4" + svn_version = 1000 + chroot_path = Path("/some/path/to/chroot") + git_hash_source = "google3" + branch = "update-LLVM_NEXT_HASH-a123testhash4" + extra_commit_msg = None + + # Verify exception is raised when an exception is thrown within + # the 'try' block by UprevEbuildSymlink function. + with self.assertRaises(ValueError) as err: + update_chromeos_llvm_hash.UpdatePackages( + packages=packages_to_update, + manifest_packages=[], + llvm_variant=llvm_variant, + git_hash=git_hash, + svn_version=svn_version, + chroot_path=chroot_path, + mode=failure_modes.FailureModes.FAIL, + git_hash_source=git_hash_source, + extra_commit_msg=extra_commit_msg, + ) + + self.assertEqual(str(err.exception), "Failed to uprev the ebuild.") + + mock_create_path_dict.assert_called_once_with( + chroot_path, packages_to_update + ) + + mock_create_repo.assert_called_once_with(path_to_package_dir, branch) + + mock_update_llvm_next.assert_called_once_with( + abs_path_to_package, llvm_variant, git_hash, svn_version + ) + + mock_uprev_symlink.assert_called_once_with(symlink_path_to_package) + + mock_upload_changes.assert_not_called() + + mock_delete_repo.assert_called_once_with(path_to_package_dir, branch) + + @mock.patch.object(update_chromeos_llvm_hash, "EnsurePackageMaskContains") + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + @mock.patch.object( + update_chromeos_llvm_hash, "CreatePathDictionaryFromPackages" + ) + @mock.patch.object(git, "CreateBranch") + @mock.patch.object(update_chromeos_llvm_hash, "UpdateEbuildLLVMHash") + @mock.patch.object(update_chromeos_llvm_hash, "UprevEbuildSymlink") + @mock.patch.object(git, "UploadChanges") + @mock.patch.object(git, "DeleteBranch") + @mock.patch.object( + update_chromeos_llvm_hash, "UpdatePackagesPatchMetadataFile" + ) + @mock.patch.object( + update_chromeos_llvm_hash, "StagePatchMetadataFileForCommit" + ) + def testSuccessfullyUpdatedPackages( + self, + mock_stage_patch_file, + mock_update_package_metadata_file, + mock_delete_repo, + mock_upload_changes, + mock_uprev_symlink, + mock_update_llvm_next, + mock_create_repo, + mock_create_path_dict, + mock_llvm_version, + mock_mask_contains, + ): + + path_to_package_dir = "/some/path/to/chroot/src/path/to" + abs_path_to_package = os.path.join( + path_to_package_dir, "package.ebuild" + ) + symlink_path_to_package = os.path.join( + path_to_package_dir, "package-r1.ebuild" + ) + + # Test function to simulate 'CreateBranch' when successfully created the + # branch for the changes to be made to the ebuild files. + def SuccessfullyCreateBranchForChanges(_, branch): + self.assertEqual(branch, "update-LLVM_NEXT_HASH-a123testhash5") + + # Test function to simulate 'UploadChanges' after a successfull update of + # 'LLVM_NEXT_HASH" of the ebuild file. + def SuccessfullyUpdatedLLVMHash(ebuild_path, _, git_hash, svn_version): + self.assertEqual( + ebuild_path, "/some/path/to/chroot/src/path/to/package.ebuild" + ) + self.assertEqual(git_hash, "a123testhash5") + self.assertEqual(svn_version, 1000) + + # Test function to simulate 'UprevEbuildSymlink' when successfully + # incremented the revision number by 1. + def SuccessfullyUprevedEbuildSymlink(symlink_path): + self.assertEqual( + symlink_path, + "/some/path/to/chroot/src/path/to/package-r1.ebuild", + ) + + # Test function to simulate 'UpdatePackagesPatchMetadataFile()' when the + # patch results contains a disabled patch in 'disable_patches' mode. + def RetrievedPatchResults(chroot_path, svn_version, packages, mode): + + self.assertEqual(chroot_path, Path("/some/path/to/chroot")) + self.assertEqual(svn_version, 1000) + self.assertListEqual(packages, ["path/to"]) + self.assertEqual(mode, failure_modes.FailureModes.DISABLE_PATCHES) + + patch_metadata_file = "PATCHES.json" + PatchInfo = collections.namedtuple( + "PatchInfo", + [ + "applied_patches", + "failed_patches", + "non_applicable_patches", + "disabled_patches", + "removed_patches", + "modified_metadata", + ], + ) + + package_patch_info = PatchInfo( + applied_patches=["fix_display.patch"], + failed_patches=["fix_stdout.patch"], + non_applicable_patches=[], + disabled_patches=["fix_stdout.patch"], + removed_patches=[], + modified_metadata="/abs/path/to/filesdir/%s" + % patch_metadata_file, + ) + + package_info_dict = {"path/to": package_patch_info._asdict()} + + # Returns a dictionary where the key is the package and the value is a + # dictionary that contains information about the package's patch results + # produced by the patch manager. + return package_info_dict + + # Test function to simulate 'UploadChanges()' when successfully created a + # commit for the changes made to the packages and their patches and + # retrieved the change list of the commit. + def SuccessfullyUploadedChanges(*args): + assert len(args) == 3 + commit_url = "https://some_name/path/to/commit/+/12345" + return git.CommitContents(url=commit_url, cl_number=12345) + + test_package_path_dict = {symlink_path_to_package: abs_path_to_package} + + # Simulate behavior of 'CreatePathDictionaryFromPackages()' when + # successfully created a dictionary where the key is the absolute path to + # the symlink of the package and value is the absolute path to the ebuild of + # the package. + mock_create_path_dict.return_value = test_package_path_dict + + # Use test function to simulate behavior. + mock_create_repo.side_effect = SuccessfullyCreateBranchForChanges + mock_update_llvm_next.side_effect = SuccessfullyUpdatedLLVMHash + mock_uprev_symlink.side_effect = SuccessfullyUprevedEbuildSymlink + mock_update_package_metadata_file.side_effect = RetrievedPatchResults + mock_upload_changes.side_effect = SuccessfullyUploadedChanges + mock_llvm_version.return_value = "1234" + mock_mask_contains.reurn_value = None + + packages_to_update = ["test-packages/package1"] + llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next + git_hash = "a123testhash5" + svn_version = 1000 + chroot_path = Path("/some/path/to/chroot") + git_hash_source = "tot" + branch = "update-LLVM_NEXT_HASH-a123testhash5" + extra_commit_msg = "\ncommit-message-end" + + change_list = update_chromeos_llvm_hash.UpdatePackages( + packages=packages_to_update, + manifest_packages=[], + llvm_variant=llvm_variant, + git_hash=git_hash, + svn_version=svn_version, + chroot_path=chroot_path, + mode=failure_modes.FailureModes.DISABLE_PATCHES, + git_hash_source=git_hash_source, + extra_commit_msg=extra_commit_msg, + ) + + self.assertEqual( + change_list.url, "https://some_name/path/to/commit/+/12345" + ) + + self.assertEqual(change_list.cl_number, 12345) + + mock_create_path_dict.assert_called_once_with( + chroot_path, packages_to_update + ) + + mock_create_repo.assert_called_once_with(path_to_package_dir, branch) + + mock_update_llvm_next.assert_called_once_with( + abs_path_to_package, llvm_variant, git_hash, svn_version + ) + + mock_uprev_symlink.assert_called_once_with(symlink_path_to_package) + + mock_mask_contains.assert_called_once_with(chroot_path, git_hash) + + expected_commit_messages = [ + "llvm-next/tot: upgrade to a123testhash5 (r1000)\n", + "The following packages have been updated:", + "path/to", + "\nFor the package path/to:", + "The patch metadata file PATCHES.json was modified", + "The following patches were disabled:", + "fix_stdout.patch", + "\ncommit-message-end", + ] + + mock_update_package_metadata_file.assert_called_once() + + mock_stage_patch_file.assert_called_once_with( + "/abs/path/to/filesdir/PATCHES.json" + ) + + mock_upload_changes.assert_called_once_with( + path_to_package_dir, branch, expected_commit_messages + ) + + mock_delete_repo.assert_called_once_with(path_to_package_dir, branch) + + @mock.patch.object(chroot, "VerifyOutsideChroot") + @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption") + @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages") + def testMainDefaults( + self, mock_update_packages, mock_gethash, mock_outside_chroot + ): + git_hash = "1234abcd" + svn_version = 5678 + mock_gethash.return_value = (git_hash, svn_version) + argv = [ + "./update_chromeos_llvm_hash_unittest.py", + "--llvm_version", + "google3", + ] + + with mock.patch.object(sys, "argv", argv) as mock.argv: + update_chromeos_llvm_hash.main() + + expected_packages = set(update_chromeos_llvm_hash.DEFAULT_PACKAGES) + expected_manifest_packages = set( + update_chromeos_llvm_hash.DEFAULT_MANIFEST_PACKAGES, + ) + expected_llvm_variant = update_chromeos_llvm_hash.LLVMVariant.current + expected_chroot = update_chromeos_llvm_hash.defaultCrosRoot() + mock_update_packages.assert_called_once_with( + packages=expected_packages, + manifest_packages=expected_manifest_packages, + llvm_variant=expected_llvm_variant, + git_hash=git_hash, + svn_version=svn_version, + chroot_path=expected_chroot, + mode=failure_modes.FailureModes.FAIL, + git_hash_source="google3", + extra_commit_msg=None, + ) + mock_outside_chroot.assert_called() + + @mock.patch.object(chroot, "VerifyOutsideChroot") + @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption") + @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages") + def testMainLlvmNext( + self, mock_update_packages, mock_gethash, mock_outside_chroot + ): + git_hash = "1234abcd" + svn_version = 5678 + mock_gethash.return_value = (git_hash, svn_version) + argv = [ + "./update_chromeos_llvm_hash_unittest.py", + "--llvm_version", + "google3", + "--is_llvm_next", + ] + + with mock.patch.object(sys, "argv", argv) as mock.argv: + update_chromeos_llvm_hash.main() + + expected_packages = set(update_chromeos_llvm_hash.DEFAULT_PACKAGES) + expected_llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next + expected_chroot = update_chromeos_llvm_hash.defaultCrosRoot() + # llvm-next upgrade does not update manifest by default. + mock_update_packages.assert_called_once_with( + packages=expected_packages, + manifest_packages=set(), + llvm_variant=expected_llvm_variant, + git_hash=git_hash, + svn_version=svn_version, + chroot_path=expected_chroot, + mode=failure_modes.FailureModes.FAIL, + git_hash_source="google3", + extra_commit_msg=None, + ) + mock_outside_chroot.assert_called() + + @mock.patch.object(chroot, "VerifyOutsideChroot") + @mock.patch.object(get_llvm_hash, "GetLLVMHashAndVersionFromSVNOption") + @mock.patch.object(update_chromeos_llvm_hash, "UpdatePackages") + def testMainAllArgs( + self, mock_update_packages, mock_gethash, mock_outside_chroot + ): + packages_to_update = "test-packages/package1,test-libs/lib1" + manifest_packages = "test-libs/lib1,test-libs/lib2" + failure_mode = failure_modes.FailureModes.REMOVE_PATCHES + chroot_path = Path("/some/path/to/chroot") + llvm_ver = 435698 + git_hash = "1234abcd" + svn_version = 5678 + mock_gethash.return_value = (git_hash, svn_version) + + argv = [ + "./update_chromeos_llvm_hash_unittest.py", + "--llvm_version", + str(llvm_ver), + "--is_llvm_next", + "--chroot_path", + str(chroot_path), + "--update_packages", + packages_to_update, + "--manifest_packages", + manifest_packages, + "--failure_mode", + failure_mode.value, + "--patch_metadata_file", + "META.json", + ] + + with mock.patch.object(sys, "argv", argv) as mock.argv: + update_chromeos_llvm_hash.main() + + expected_packages = {"test-packages/package1", "test-libs/lib1"} + expected_manifest_packages = {"test-libs/lib1", "test-libs/lib2"} + expected_llvm_variant = update_chromeos_llvm_hash.LLVMVariant.next + mock_update_packages.assert_called_once_with( + packages=expected_packages, + manifest_packages=expected_manifest_packages, + llvm_variant=expected_llvm_variant, + git_hash=git_hash, + svn_version=svn_version, + chroot_path=chroot_path, + mode=failure_mode, + git_hash_source=llvm_ver, + extra_commit_msg=None, + ) + mock_outside_chroot.assert_called() + + @mock.patch.object(subprocess, "check_output", return_value=None) + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + def testEnsurePackageMaskContainsExisting( + self, mock_llvm_version, mock_git_add + ): + chroot_path = "absolute/path/to/chroot" + git_hash = "badf00d" + mock_llvm_version.return_value = "1234" + with mock.patch( + "update_chromeos_llvm_hash.open", + mock.mock_open(read_data="\n=sys-devel/llvm-1234.0_pre*\n"), + create=True, + ) as mock_file: + update_chromeos_llvm_hash.EnsurePackageMaskContains( + chroot_path, git_hash + ) + handle = mock_file() + handle.write.assert_not_called() + mock_llvm_version.assert_called_once_with(git_hash) + + overlay_dir = ( + "absolute/path/to/chroot/src/third_party/chromiumos-overlay" + ) + mask_path = overlay_dir + "/profiles/targets/chromeos/package.mask" + mock_git_add.assert_called_once_with( + ["git", "-C", overlay_dir, "add", mask_path] + ) + + @mock.patch.object(subprocess, "check_output", return_value=None) + @mock.patch.object(get_llvm_hash, "GetLLVMMajorVersion") + def testEnsurePackageMaskContainsNotExisting( + self, mock_llvm_version, mock_git_add + ): + chroot_path = "absolute/path/to/chroot" + git_hash = "badf00d" + mock_llvm_version.return_value = "1234" + with mock.patch( + "update_chromeos_llvm_hash.open", + mock.mock_open(read_data="nothing relevant"), + create=True, + ) as mock_file: + update_chromeos_llvm_hash.EnsurePackageMaskContains( + chroot_path, git_hash + ) + handle = mock_file() + handle.write.assert_called_once_with( + "=sys-devel/llvm-1234.0_pre*\n" + ) + mock_llvm_version.assert_called_once_with(git_hash) + + overlay_dir = ( + "absolute/path/to/chroot/src/third_party/chromiumos-overlay" + ) + mask_path = overlay_dir + "/profiles/targets/chromeos/package.mask" + mock_git_add.assert_called_once_with( + ["git", "-C", overlay_dir, "add", mask_path] + ) + + +if __name__ == "__main__": + unittest.main() |