#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2019 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # pylint: disable=protected-access """Unit tests when creating the arguments for the patch manager.""" from __future__ import print_function from collections import namedtuple import os import unittest import unittest.mock as mock from failure_modes import FailureModes import get_llvm_hash import llvm_patch_management import patch_manager import subprocess_helpers class LlvmPatchManagementTest(unittest.TestCase): """Test class when constructing the arguments for the patch manager.""" # Simulate the behavior of `os.path.isdir()` when the chroot path does not # exist or is not a directory. @mock.patch.object(os.path, 'isdir', return_value=False) def testInvalidChrootPathWhenGetPathToFilesDir(self, mock_isdir): chroot_path = '/some/path/to/chroot' package = 'sys-devel/llvm' # Verify the exception is raised when an invalid absolute path to the chroot # is passed in. with self.assertRaises(ValueError) as err: llvm_patch_management.GetPathToFilesDirectory(chroot_path, package) self.assertEqual( str(err.exception), 'Invalid chroot provided: %s' % chroot_path) mock_isdir.assert_called_once() # Simulate the behavior of 'os.path.isdir()' when a valid chroot path is # passed in. @mock.patch.object(os.path, 'isdir', return_value=True) @mock.patch.object(subprocess_helpers, 'ChrootRunCommand') @mock.patch.object(llvm_patch_management, '_GetRelativePathOfChrootPath') def testSuccessfullyGetPathToFilesDir( self, mock_get_relative_path_of_chroot_path, mock_chroot_cmd, mock_isdir): package_chroot_path = '/mnt/host/source/path/to/llvm/llvm.ebuild' # Simulate behavior of 'ChrootRunCommand()' when successfully # retrieved the absolute chroot path to the package's ebuild. mock_chroot_cmd.return_value = package_chroot_path # Simulate behavior of '_GetRelativePathOfChrootPath()' when successfully # removed '/mnt/host/source' of the absolute chroot path to the package's # ebuild. # # Returns relative path after '/mnt/host/source/'. mock_get_relative_path_of_chroot_path.return_value = 'path/to/llvm' chroot_path = '/some/path/to/chroot' package = 'sys-devel/llvm' self.assertEqual( llvm_patch_management.GetPathToFilesDirectory(chroot_path, package), '/some/path/to/chroot/path/to/llvm/files/') mock_isdir.assert_called_once() mock_chroot_cmd.assert_called_once() mock_get_relative_path_of_chroot_path.assert_called_once_with( '/mnt/host/source/path/to/llvm') def testInvalidPrefixForChrootPath(self): package_chroot_path = '/path/to/llvm' # Verify the exception is raised when the chroot path does not start with # '/mnt/host/source/'. with self.assertRaises(ValueError) as err: llvm_patch_management._GetRelativePathOfChrootPath(package_chroot_path) self.assertEqual( str(err.exception), 'Invalid prefix for the chroot path: %s' % package_chroot_path) def testValidPrefixForChrootPath(self): package_chroot_path = '/mnt/host/source/path/to/llvm' package_rel_path = 'path/to/llvm' self.assertEqual( llvm_patch_management._GetRelativePathOfChrootPath(package_chroot_path), package_rel_path) # Simulate behavior of 'os.path.isfile()' when the patch metadata file does # not exist. @mock.patch.object(os.path, 'isfile', return_value=False) def testInvalidFileForPatchMetadataPath(self, mock_isfile): abs_path_to_patch_file = '/abs/path/to/files/test.json' # Verify the exception is raised when the absolute path to the patch # metadata file does not exist. with self.assertRaises(ValueError) as err: llvm_patch_management._CheckPatchMetadataPath(abs_path_to_patch_file) self.assertEqual( str(err.exception), 'Invalid file provided: %s' % abs_path_to_patch_file) mock_isfile.assert_called_once() # Simulate behavior of 'os.path.isfile()' when the absolute path to the # patch metadata file exists. @mock.patch.object(os.path, 'isfile', return_value=True) def testPatchMetadataFileDoesNotEndInJson(self, mock_isfile): abs_path_to_patch_file = '/abs/path/to/files/PATCHES' # Verify the exception is raised when the patch metadata file does not end # in '.json'. with self.assertRaises(ValueError) as err: llvm_patch_management._CheckPatchMetadataPath(abs_path_to_patch_file) self.assertEqual( str(err.exception), 'File does not end in ".json": %s' % abs_path_to_patch_file) mock_isfile.assert_called_once() @mock.patch.object(os.path, 'isfile') def testValidPatchMetadataFile(self, mock_isfile): abs_path_to_patch_file = '/abs/path/to/files/PATCHES.json' # Simulate behavior of 'os.path.isfile()' when the absolute path to the # patch metadata file exists. mock_isfile.return_value = True llvm_patch_management._CheckPatchMetadataPath(abs_path_to_patch_file) mock_isfile.assert_called_once() # Simulate `GetGitHashFrom()` when successfully retrieved the git hash # of the version passed in. @mock.patch.object( get_llvm_hash, 'GetGitHashFrom', return_value='a123testhash1') # Simulate `CreateTempLLVMRepo()` when successfully created a work tree from # the LLVM repo copy in `llvm_tools` directory. @mock.patch.object(get_llvm_hash, 'CreateTempLLVMRepo') # Simulate behavior of `_MoveSrcTreeHEADToGitHash()` when successfully moved # the head pointer to the git hash of the revision. @mock.patch.object(llvm_patch_management, '_MoveSrcTreeHEADToGitHash') @mock.patch.object(llvm_patch_management, 'GetPathToFilesDirectory') @mock.patch.object(llvm_patch_management, '_CheckPatchMetadataPath') def testExceptionIsRaisedWhenUpdatingAPackagesMetadataFile( self, mock_check_patch_metadata_path, mock_get_filesdir_path, mock_move_head_pointer, mock_create_temp_llvm_repo, mock_get_git_hash): abs_path_to_patch_file = \ '/some/path/to/chroot/some/path/to/filesdir/PATCHES' # Simulate the behavior of '_CheckPatchMetadataPath()' when the patch # metadata file in $FILESDIR does not exist or does not end in '.json'. def InvalidPatchMetadataFile(patch_metadata_path): self.assertEqual(patch_metadata_path, abs_path_to_patch_file) raise ValueError( 'File does not end in ".json": %s' % abs_path_to_patch_file) # Use the test function to simulate behavior of '_CheckPatchMetadataPath()'. mock_check_patch_metadata_path.side_effect = InvalidPatchMetadataFile abs_path_to_filesdir = '/some/path/to/chroot/some/path/to/filesdir' # Simulate the behavior of 'GetPathToFilesDirectory()' when successfully # constructed the absolute path to $FILESDIR of a package. mock_get_filesdir_path.return_value = abs_path_to_filesdir temp_work_tree = '/abs/path/to/tmpWorkTree' # Simulate the behavior of returning the absolute path to a worktree via # `git worktree add`. mock_create_temp_llvm_repo.return_value.__enter__.return_value.name = \ temp_work_tree chroot_path = '/some/path/to/chroot' revision = 1000 patch_file_name = 'PATCHES' package_name = 'test-package/package1' # Verify the exception is raised when a package is constructing the # arguments for the patch manager to update its patch metadata file and an # exception is raised in the process. with self.assertRaises(ValueError) as err: llvm_patch_management.UpdatePackagesPatchMetadataFile( chroot_path, revision, patch_file_name, [package_name], FailureModes.FAIL) self.assertEqual( str(err.exception), 'File does not end in ".json": %s' % abs_path_to_patch_file) mock_get_filesdir_path.assert_called_once_with(chroot_path, package_name) mock_get_git_hash.assert_called_once() mock_check_patch_metadata_path.assert_called_once() mock_move_head_pointer.assert_called_once() mock_create_temp_llvm_repo.assert_called_once() # Simulate `CleanSrcTree()` when successfully removed changes from the # worktree. @mock.patch.object(patch_manager, 'CleanSrcTree') # Simulate `GetGitHashFrom()` when successfully retrieved the git hash # of the version passed in. @mock.patch.object( get_llvm_hash, 'GetGitHashFrom', return_value='a123testhash1') # Simulate `CreateTempLLVMRepo()` when successfully created a work tree from # the LLVM repo copy in `llvm_tools` directory. @mock.patch.object(get_llvm_hash, 'CreateTempLLVMRepo') # Simulate behavior of `_MoveSrcTreeHEADToGitHash()` when successfully moved # the head pointer to the git hash of the revision. @mock.patch.object(llvm_patch_management, '_MoveSrcTreeHEADToGitHash') @mock.patch.object(llvm_patch_management, 'GetPathToFilesDirectory') @mock.patch.object(llvm_patch_management, '_CheckPatchMetadataPath') @mock.patch.object(patch_manager, 'HandlePatches') def testSuccessfullyRetrievedPatchResults( self, mock_handle_patches, mock_check_patch_metadata_path, mock_get_filesdir_path, mock_move_head_pointer, mock_create_temp_llvm_repo, mock_get_git_hash, mock_clean_src_tree): abs_path_to_filesdir = '/some/path/to/chroot/some/path/to/filesdir' abs_path_to_patch_file = \ '/some/path/to/chroot/some/path/to/filesdir/PATCHES.json' # Simulate the behavior of 'GetPathToFilesDirectory()' when successfully # constructed the absolute path to $FILESDIR of a package. mock_get_filesdir_path.return_value = abs_path_to_filesdir PatchInfo = namedtuple('PatchInfo', [ 'applied_patches', 'failed_patches', 'non_applicable_patches', 'disabled_patches', 'removed_patches', 'modified_metadata' ]) # Simulate the behavior of 'HandlePatches()' when successfully iterated # through every patch in the patch metadata file and a dictionary is # returned that contains information about the patches' status. mock_handle_patches.return_value = PatchInfo( applied_patches=['fixes_something.patch'], failed_patches=['disables_output.patch'], non_applicable_patches=[], disabled_patches=[], removed_patches=[], modified_metadata=None) temp_work_tree = '/abs/path/to/tmpWorkTree' # Simulate the behavior of returning the absolute path to a worktree via # `git worktree add`. mock_create_temp_llvm_repo.return_value.__enter__.return_value.name = \ temp_work_tree expected_patch_results = { 'applied_patches': ['fixes_something.patch'], 'failed_patches': ['disables_output.patch'], 'non_applicable_patches': [], 'disabled_patches': [], 'removed_patches': [], 'modified_metadata': None } chroot_path = '/some/path/to/chroot' revision = 1000 patch_file_name = 'PATCHES.json' package_name = 'test-package/package2' patch_info = llvm_patch_management.UpdatePackagesPatchMetadataFile( chroot_path, revision, patch_file_name, [package_name], FailureModes.CONTINUE) self.assertDictEqual(patch_info, {package_name: expected_patch_results}) mock_get_filesdir_path.assert_called_once_with(chroot_path, package_name) mock_check_patch_metadata_path.assert_called_once_with( abs_path_to_patch_file) mock_handle_patches.assert_called_once() mock_create_temp_llvm_repo.assert_called_once() mock_get_git_hash.assert_called_once() mock_move_head_pointer.assert_called_once() mock_clean_src_tree.assert_called_once() if __name__ == '__main__': unittest.main()