| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # 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.""" |
| |
| |
| import collections |
| import datetime |
| import os |
| from pathlib import Path |
| import subprocess |
| import sys |
| import unittest |
| import unittest.mock as mock |
| |
| import chroot |
| import failure_modes |
| import get_llvm_hash |
| import git |
| 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(), 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() |