aboutsummaryrefslogtreecommitdiff
path: root/llvm_tools
diff options
context:
space:
mode:
authorSalud Lemus <saludlemus@google.com>2019-07-11 11:55:07 -0700
committerSalud Lemus <saludlemus@google.com>2019-07-12 21:18:59 +0000
commit6652ca32cb1194acb1771121189f1531c3644402 (patch)
tree5ef5d89e7da93b288474eb2f5682193ce939e545 /llvm_tools
parentf3bf30332ef8bdb5a10e847dbfb84e61b4c50c62 (diff)
downloadtoolchain-utils-6652ca32cb1194acb1771121189f1531c3644402.tar.gz
LLVM tools: Unittests for update_chromeos_llvm_next_hash.py
BUG=None TEST='./update_chromeos_llvm_next_hash_unittest.py' passes Change-Id: I1716861a81ef74cfa94d5894050fb1ecf7b0a727 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/1697026 Reviewed-by: Manoj Gupta <manojgupta@chromium.org> Tested-by: Salud Lemus <saludlemus@google.com>
Diffstat (limited to 'llvm_tools')
-rwxr-xr-xllvm_tools/update_chromeos_llvm_next_hash_unittest.py867
1 files changed, 867 insertions, 0 deletions
diff --git a/llvm_tools/update_chromeos_llvm_next_hash_unittest.py b/llvm_tools/update_chromeos_llvm_next_hash_unittest.py
new file mode 100755
index 00000000..f3096f2e
--- /dev/null
+++ b/llvm_tools/update_chromeos_llvm_next_hash_unittest.py
@@ -0,0 +1,867 @@
+#!/usr/bin/env python2
+# -*- 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.
+
+"""Unit tests for updating the LLVM next hash."""
+
+from __future__ import print_function
+
+from pipes import quote
+from tempfile import mkstemp
+import mock
+import os
+import unittest
+
+from cros_utils import command_executer
+import update_chromeos_llvm_next_hash
+
+
+def CallCountsToMockFunctions(mock_function):
+ """A decorator that passes a call count to the function it decorates.
+
+ Examples:
+ @CallCountsToMockFunctions
+ def foo(call_count):
+ return call_count
+ ...
+ ...
+ [foo(), foo(), foo()]
+ [0, 1, 2]
+
+ NOTE: This decorator will not handle recursive functions properly.
+ """
+
+ counter = [0]
+
+ def Result(*args, **kwargs):
+ ret_value = mock_function(counter[0], *args, **kwargs)
+ counter[0] += 1
+ return ret_value
+
+ return Result
+
+
+class UpdateLLVMNextHashTest(unittest.TestCase):
+ """Test class for updating 'LLVM_NEXT_HASH' of packages."""
+
+ @mock.patch.object(command_executer.CommandExecuter,
+ 'ChrootRunCommandWOutput')
+ def testFailedToGetChrootPathForInvalidPackage(self, mock_chroot_command):
+
+ # Emulate ChrootRunCommandWOutput behavior when an invalid package is
+ # passed in.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_chroot_command.return_value = (1, None, 'Invalid package provided.')
+
+ # Verify the exception is raised when an invalid package is passed in.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.GetChrootBuildPaths(
+ '/test/chroot/path', ['test-pckg/test'])
+
+ self.assertEqual(
+ err.exception.message,
+ 'Failed to get chroot path for the package (test-pckg/test): '
+ 'Invalid package provided.')
+
+ mock_chroot_command.assert_called_once_with(
+ chromeos_root='/test/chroot/path',
+ command='equery w test-pckg/test',
+ print_to_console=False)
+
+ @mock.patch.object(command_executer.CommandExecuter,
+ 'ChrootRunCommandWOutput')
+ def testSucceedsToGetChrootPathForPackage(self, mock_chroot_command):
+ # Emulate ChrootRunCommandWOutput behavior when a chroot path is found for
+ # a valid package.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_chroot_command.return_value = (0, '/chroot/path/to/package.ebuild\n',
+ 0)
+
+ self.assertEqual(
+ update_chromeos_llvm_next_hash.GetChrootBuildPaths(
+ '/test/chroot/path', ['new-test/package']),
+ ['/chroot/path/to/package.ebuild'])
+
+ mock_chroot_command.assert_called_once_with(
+ chromeos_root='/test/chroot/path',
+ command='equery w new-test/package',
+ print_to_console=False)
+
+ def testFailedToConvertChrootPathWithInvalidPrefixToSymlinkPath(self):
+ # Verify the exception is raised when a symlink does not have the prefix
+ # '/mnt/host/source/'.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash._ConvertChrootPathsToSymLinkPaths(
+ '/path/to/chroot', ['/src/package.ebuild'])
+
+ self.assertEqual(
+ err.exception.message, 'Invalid prefix for the chroot path: '
+ '/src/package.ebuild')
+
+ def testSucceedsToConvertChrootPathToSymlinkPath(self):
+ self.assertEqual(
+ update_chromeos_llvm_next_hash._ConvertChrootPathsToSymLinkPaths(
+ '/path/to/chroot', ['/mnt/host/source/src/package.ebuild']),
+ ['/path/to/chroot/src/package.ebuild'])
+
+ @mock.patch.object(os.path, 'islink')
+ def testFailedToGetEbuildPathFromInvalidSymlink(self, mock_islink):
+ # Simulate 'os.path.islink' when a path is not a symbolic link.
+ mock_islink.return_value = False
+
+ # Verify the exception is raised when the argument is not a symbolic link.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.GetEbuildPathsFromSymLinkPaths(
+ ['/symlink/path/src/to/package-r1.ebuild'])
+
+ self.assertEqual(
+ err.exception.message,
+ 'Invalid symlink provided: /symlink/path/src/to/package-r1.ebuild')
+
+ mock_islink.assert_called_once_with(
+ '/symlink/path/src/to/package-r1.ebuild')
+
+ @mock.patch.object(os.path, 'islink')
+ @mock.patch.object(os.path, 'realpath')
+ def testSucceedsToGetEbuildPathFromValidSymlink(self, mock_realpath,
+ mock_islink):
+
+ # Simulate 'os.path.realpath' when a valid path is passed in.
+ mock_realpath.return_value = '/abs/path/to/src/package.ebuild'
+
+ # Simulate 'os.path.islink' when a path is a symbolic link.
+ mock_islink.return_value = True
+
+ self.assertEqual(
+ update_chromeos_llvm_next_hash.GetEbuildPathsFromSymLinkPaths(
+ ['/path/to/chroot/src/package-r1.ebuild']), {
+ '/path/to/chroot/src/package-r1.ebuild':
+ '/abs/path/to/src/package.ebuild'
+ })
+
+ mock_realpath.assert_called_once_with(
+ '/path/to/chroot/src/package-r1.ebuild')
+
+ mock_islink.assert_called_once_with('/path/to/chroot/src/package-r1.ebuild')
+
+ def testFailedToUpdateLLVMNextHashForInvalidEbuildPath(self):
+ # Verify the exception is raised when the ebuild path does not exist.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UpdateBuildLLVMNextHash(
+ '/some/path/to/package.ebuild', 'a123testhash1', 1000)
+
+ self.assertEqual(
+ err.exception.message,
+ 'Invalid ebuild path provided: /some/path/to/package.ebuild')
+
+ @mock.patch.object(os.path, 'isfile')
+ def testFailedToUpdateLLVMNextHash(self, mock_isfile):
+ # Simulate 'os.path.isfile' behavior on a valid ebuild path.
+ mock_isfile.return_value = True
+
+ # Create a temporary file to simulate an ebuild file of a package.
+ ebuild_file, file_path = mkstemp()
+
+ os.write(
+ ebuild_file, '\n'.join([
+ 'First line in the ebuild', 'Second line in the ebuild',
+ 'Last line in the ebuild'
+ ]))
+
+ os.close(ebuild_file)
+
+ try:
+ # Verify the exception is raised when the ebuild file does not have
+ # 'LLVM_NEXT_HASH'.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UpdateBuildLLVMNextHash(
+ file_path, 'a123testhash1', 1000)
+
+ self.assertEqual(err.exception.message, 'Failed to update the LLVM hash.')
+ finally:
+ os.remove(file_path)
+
+ mock_isfile.assert_called_once()
+
+ @mock.patch.object(os.path, 'isfile')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testFailedToStageTheEbuildForCommitForLLVMNextHashUpdate(
+ self, mock_stage_commit_command, mock_isfile):
+
+ # Simulate 'os.path.isfile' behavior on a valid ebuild path.
+ mock_isfile.return_value = True
+
+ # Simulate 'RunCommandWOutput' when failed to stage the ebuild file for
+ # commit.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_stage_commit_command.return_value = (1, None, 'Failed to add file.')
+
+ # Create a temporary file to simulate an ebuild file of a package.
+ ebuild_file, file_path = mkstemp()
+
+ os.write(
+ ebuild_file, '\n'.join([
+ 'First line in the ebuild', 'Second line in the ebuild',
+ 'LLVM_NEXT_HASH=\"a12b34c56d78e90\" # EGIT_COMMIT r500',
+ 'Last line in the ebuild'
+ ]))
+
+ os.close(ebuild_file)
+
+ try:
+ # Verify the exception is raised when staging the ebuild file.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UpdateBuildLLVMNextHash(
+ file_path, 'a123testhash1', 1000)
+
+ self.assertEqual(
+ err.exception.message, 'Failed to stage the ebuild for commit: '
+ 'Failed to add file.')
+
+ expected_file_contents = [
+ 'First line in the ebuild\n', 'Second line in the ebuild\n',
+ 'LLVM_NEXT_HASH=\"a123testhash1\" # EGIT_COMMIT r1000\n',
+ 'Last line in the ebuild'
+ ]
+
+ # Verify the new file contents of the ebuild file match
+ # the expected file contents.
+ with open(file_path) 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)
+
+ finally:
+ os.remove(file_path)
+
+ mock_isfile.assert_called_once()
+ mock_stage_commit_command.assert_called_once()
+
+ @mock.patch.object(os.path, 'isfile')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testSuccessfullyStageTheEbuildForCommitForLLVMNextHashUpdate(
+ self, mock_stage_commit_command, mock_isfile):
+
+ # Simulate 'os.path.isfile' behavior on a valid ebuild path.
+ mock_isfile.return_value = True
+
+ # Simulate 'RunCommandWOutput' when successfully staged the ebuild file for
+ # commit.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_stage_commit_command.return_value = (0, None, 0)
+
+ # Create a temporary file to simulate an ebuild file of a package.
+ ebuild_file, file_path = mkstemp()
+
+ os.write(
+ ebuild_file, '\n'.join([
+ 'First line in the ebuild', 'Second line in the ebuild',
+ 'LLVM_NEXT_HASH=\"a12b34c56d78e90\" # EGIT_COMMIT r500',
+ 'Last line in the ebuild'
+ ]))
+
+ os.close(ebuild_file)
+
+ try:
+ update_chromeos_llvm_next_hash.UpdateBuildLLVMNextHash(
+ file_path, 'a123testhash1', 1000)
+
+ expected_file_contents = [
+ 'First line in the ebuild\n', 'Second line in the ebuild\n',
+ 'LLVM_NEXT_HASH=\"a123testhash1\" # EGIT_COMMIT r1000\n',
+ 'Last line in the ebuild'
+ ]
+
+ # Verify the new file contents of the ebuild file match the expected file
+ # contents.
+ with open(file_path) 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)
+
+ finally:
+ os.remove(file_path)
+
+ mock_isfile.assert_called_once()
+ mock_stage_commit_command.assert_called_once()
+
+ def testFailedToUprevEbuildForInvalidSymlink(self):
+ # Verify the exception is raised when a symbolic link is not passed in.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UprevEbuild('/symlink/to/package.ebuild')
+
+ self.assertEqual(err.exception.message,
+ 'Invalid symlink provided: /symlink/to/package.ebuild')
+
+ @mock.patch.object(os.path, 'islink')
+ def testFailedToUprevEbuild(self, mock_islink):
+ # Simulate 'os.path.islink' when a symbolic link is passed in.
+ mock_islink.return_value = True
+
+ # Verify the exception is raised when the symlink does not have a revision
+ # number.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UprevEbuild('/symlink/to/package.ebuild')
+
+ self.assertEqual(err.exception.message, 'Failed to uprev the ebuild.')
+
+ mock_islink.assert_called_once_with('/symlink/to/package.ebuild')
+
+ @mock.patch.object(os.path, 'islink')
+ @mock.patch.object(os.path, 'dirname')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testSuccessfullyUprevEbuild(self, mock_command_output, mock_dirname,
+ mock_islink):
+
+ # Simulate 'os.path.islink' when a valid symbolic link is passed in.
+ mock_islink.return_value = True
+
+ # Simulate 'os.path.dirname' when returning a path to the directory of a
+ # valid symbolic link.
+ mock_dirname.return_value = '/symlink/to'
+
+ # Simulate 'RunCommandWOutput' when the symbolic link was incremented by 1
+ # and staged for commit.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_command_output.return_value = (0, None, 0)
+
+ update_chromeos_llvm_next_hash.UprevEbuild('/symlink/to/package-r1.ebuild')
+
+ mock_islink.assert_called_once_with('/symlink/to/package-r1.ebuild')
+
+ mock_dirname.assert_called_once_with('/symlink/to/package-r1.ebuild')
+
+ mock_command_output.assert_called_once_with(
+ 'git -C /symlink/to mv '
+ '/symlink/to/package-r1.ebuild /symlink/to/package-r2.ebuild',
+ print_to_console=False)
+
+ def testFailedToCreateRepoForInvalidDirectoryPath(self):
+ # Verify the exception is raised when provided an invalid directory path.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash._CreateRepo('/path/to/repo',
+ 'a123testhash1')
+
+ self.assertEqual(err.exception.message,
+ 'Invalid directory path provided: /path/to/repo')
+
+ @mock.patch.object(os.path, 'isdir')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testFailedToCreateRepo(self, mock_command_output, mock_isdir):
+ # Simulate 'os.path.isdir' when the path to the repo is valid.
+ mock_isdir.return_value = True
+
+ # Simulate 'RunCommandWOutput' when 'repo start' fails.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_command_output.return_value = (1, None, 'Invalid branch name.')
+
+ # Verify exception is raised when failed to create a repo for the changes.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash._CreateRepo('/path/to/repo',
+ 'a123testhash1')
+
+ self.assertEqual(
+ err.exception.message,
+ 'Failed to create the repo (llvm-next-update-a123testhash1): '
+ 'Invalid branch name.')
+
+ mock_isdir.assert_called_once_with('/path/to/repo')
+
+ mock_command_output.assert_called_once()
+
+ @mock.patch.object(os.path, 'isdir')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testSuccessfullyCreatedRepo(self, mock_command_output, mock_isdir):
+ # Test function to simulate 'RunCommandWOutput' when 'repo start' succeeds.
+ def GoodRepoStart(create_repo_cmd, print_to_console):
+ self.assertEqual(create_repo_cmd.split()[-1],
+ 'llvm-next-update-a123testhash1')
+
+ # Returns shell error code, stdout, stderr.
+ return 0, None, 0
+
+ # Simulate 'os.path.isdir' when a valid repo path is provided.
+ mock_isdir.return_value = True
+
+ # Use test function to simulate 'RunCommandWOutput' behavior.
+ mock_command_output.side_effect = GoodRepoStart
+
+ update_chromeos_llvm_next_hash._CreateRepo('/path/to/repo', 'a123testhash1')
+
+ mock_isdir.assert_called_once_with('/path/to/repo')
+
+ mock_command_output.assert_called_once()
+
+ def testFailedToDeleteRepoForInvalidDirectoryPath(self):
+ # Verify the exception is raised on an invalid repo path.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash._DeleteRepo('/some/path/to/repo',
+ 'a123testhash2')
+
+ self.assertEqual(err.exception.message,
+ 'Invalid directory path provided: /some/path/to/repo')
+
+ @mock.patch.object(os.path, 'isdir')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testFailedToDeleteRepo(self, mock_command_output, mock_isdir):
+ # Simulate 'os.path.isdir' on a valid directory.
+ mock_isdir.return_value = True
+
+ # Simulate 'RunCommandWOutput' when failed to delete a branch.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_command_output.return_value = (1, None, 'Invalid branch name.')
+
+ # Verify exception is raised when failed to delete the repo.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash._DeleteRepo('/some/path/to/repo',
+ 'a123testhash2')
+
+ self.assertEqual(
+ err.exception.message,
+ 'Failed to delete the repo (llvm-next-update-a123testhash2): '
+ 'Invalid branch name.')
+
+ mock_isdir.assert_called_once_with('/some/path/to/repo')
+
+ mock_command_output.assert_called_once()
+
+ @mock.patch.object(os.path, 'isdir')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testSuccessfullyDeletedRepo(self, mock_command_output, mock_isdir):
+ # Test function to simulate 'RunCommandWOutput' when successfully deleted a
+ # repo.
+ def GoodRepoDelete(create_repo_cmd, print_to_console):
+ self.assertEqual(create_repo_cmd.split()[-1],
+ 'llvm-next-update-a123testhash2')
+
+ # Returns shell error code, stdout, stderr.
+ return 0, None, 0
+
+ # Simulate 'os.path.isdir' on valid directory path.
+ mock_isdir.return_value = True
+
+ # Use test function to simulate 'RunCommandWOutput' behavior.
+ mock_command_output.side_effect = GoodRepoDelete
+
+ update_chromeos_llvm_next_hash._DeleteRepo('/some/path/to/repo',
+ 'a123testhash2')
+
+ mock_isdir.assert_called_once_with('/some/path/to/repo')
+
+ mock_command_output.assert_called_once()
+
+ def testFailedToUploadChangesForInvalidPathDirectory(self):
+ # Verify exception is raised when on an invalid repo path.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UploadChanges(
+ '/some/path/to/repo', 'a123testhash3', '-m \"Test message\"')
+
+ self.assertEqual(err.exception.message,
+ 'Invalid directory path provided: /some/path/to/repo')
+
+ @mock.patch.object(os.path, 'isdir')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testFailedToCreateACommitForTheChanges(self, mock_command_output,
+ mock_isdir):
+
+ # Simulate 'os.path.isdir' on a valid repo directory.
+ mock_isdir.return_value = True
+
+ # Simulate 'RunCommandWOutput' when failed to create a commit for the
+ # changes.
+ #
+ # Returns shell error code, stdout, stderr.
+ mock_command_output.return_value = (1, None, 'Nothing to commit.')
+
+ # Verify exception is raised when failed to create a commit.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UploadChanges(
+ '/some/path/to/repo', 'a123testhash3', '-m \"Test message\"')
+
+ self.assertEqual(
+ err.exception.message,
+ 'Failed to create a commit for the changes: Nothing to commit.')
+
+ mock_isdir.assert_called_once_with('/some/path/to/repo')
+
+ mock_command_output.assert_called_once_with(
+ 'cd /some/path/to/repo && git commit -m \"Test message\"',
+ print_to_console=False)
+
+ @mock.patch.object(os.path, 'isdir')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testFailedToUploadChangesForReview(self, mock_command_output, mock_isdir):
+ # Test function to simulate 'RunCommandWOutput' when attempting to create
+ # a commit and upload the changes for review.
+ @CallCountsToMockFunctions
+ def MultipleCallsToUploadACommit(call_count, cmd, print_to_console):
+ # Creating a commit for the changes.
+ if call_count == 0: # first call to 'RunCommandWOutput'
+ self.assertEqual(
+ cmd, 'cd /some/path/to/repo && git commit -m \"Test message\"')
+
+ # Returns shell error code, stdout, stderr.
+ return 0, None, 0
+
+ # Trying to upload the commit for review.
+ if call_count == 1: # second call to 'RunCommandWOutput'
+ # Make sure the branch name matches expected.
+ self.assertEqual(cmd.split()[-2], '--br=llvm-next-update-a123testhash3')
+
+ # Returns shell error code, stdout, stderr.
+ return 1, None, 'Branch does not exist.'
+
+ # Testing function was called more times than expected (2 times).
+ assert False, 'RunCommandWOutput was called more than 2 times.'
+
+ # Simulate 'os.path.isdir' on a valid repo path.
+ mock_isdir.return_value = True
+
+ # Use test function to simulate 'RunCommandWOutput' behavior.
+ mock_command_output.side_effect = MultipleCallsToUploadACommit
+
+ # Verify exception is raised when failed to upload the changes for review.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UploadChanges(
+ '/some/path/to/repo', 'a123testhash3', '-m \"Test message\"')
+
+ self.assertEqual(
+ err.exception.message,
+ 'Failed to upload changes for review: Branch does not exist.')
+
+ mock_isdir.assert_called_once_with('/some/path/to/repo')
+
+ self.assertEqual(mock_command_output.call_count, 2)
+
+ @mock.patch.object(os.path, 'isdir')
+ @mock.patch.object(command_executer.CommandExecuter, 'RunCommandWOutput')
+ def testSuccessfullyUploadedChangesForReview(self, mock_command_output,
+ mock_isdir):
+
+ # Test function to simulate 'RunCommandWOutput' when creating a commit for
+ # the changes and uploading the changes for review.
+ @CallCountsToMockFunctions
+ def MultipleCallsToUploadACommit(call_count, cmd, print_to_console):
+ # Creating a commit in the repo path.
+ if call_count == 0: # first call to 'RunCommandWOutput'
+ self.assertEqual(
+ cmd, 'cd /some/path/to/repo && git commit -m \"Test message\"')
+
+ # Returns shell error code, stdout, stderr.
+ return 0, None, 0
+ # Uploading the changes for review.
+ if call_count == 1: # second call to 'RunCommandWOutput'
+ # Make sure the branch name matches expected.
+ self.assertEqual(cmd.split()[-2], '--br=llvm-next-update-a123testhash3')
+
+ # Returns shell error code, stdout, stderr.
+ return 0, None, 0
+
+ # Testing function was called more times than expected (2 times).
+ assert False, 'RunCommandWOutput was called more than 2 times.'
+
+ # Simulate 'os.path.isdir' when a valid repo path is passed in.
+ mock_isdir.return_value = True
+
+ # Use test function to simulate 'RunCommandWOutput' behavior.
+ mock_command_output.side_effect = MultipleCallsToUploadACommit
+
+ update_chromeos_llvm_next_hash.UploadChanges(
+ '/some/path/to/repo', 'a123testhash3', '-m \"Test message\"')
+
+ mock_isdir.assert_called_once_with('/some/path/to/repo')
+
+ self.assertEqual(mock_command_output.call_count, 2)
+
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'GetChrootBuildPaths')
+ @mock.patch.object(update_chromeos_llvm_next_hash,
+ '_ConvertChrootPathsToSymLinkPaths')
+ def testExceptionRaisedWhenCreatingPathDictionaryFromPackages(
+ self, mock_chroot_paths_to_symlinks, mock_get_chroot_paths):
+
+ # Test function to simulate '_ConvertChrootPathsToSymLinkPaths' when a
+ # symlink does not start with the prefix '/mnt/host/source'.
+ def BadPrefixChrootPath(chroot_path, chroot_file_paths):
+ raise ValueError('Invalid prefix for the chroot path: '
+ '/some/chroot/path/to/package-r1.ebuild')
+
+ # Simulate 'GetChrootBuildPaths' when valid packages are passed in.
+ #
+ # Returns a list of chroot paths.
+ mock_get_chroot_paths.return_value = [
+ '/some/chroot/path/to/package-r1.ebuild'
+ ]
+
+ # Use test function to simulate '_ConvertChrootPathsToSymLinkPaths'
+ # 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_next_hash.CreatePathDictionaryFromPackages(
+ '/some/path/to/chroot', ['test-pckg/package'])
+
+ self.assertEqual(
+ err.exception.message, 'Invalid prefix for the chroot path: '
+ '/some/chroot/path/to/package-r1.ebuild')
+
+ mock_get_chroot_paths.assert_called_once_with('/some/path/to/chroot',
+ ['test-pckg/package'])
+
+ mock_chroot_paths_to_symlinks.assert_called_once_with(
+ '/some/path/to/chroot', ['/some/chroot/path/to/package-r1.ebuild'])
+
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'GetChrootBuildPaths')
+ @mock.patch.object(update_chromeos_llvm_next_hash,
+ '_ConvertChrootPathsToSymLinkPaths')
+ @mock.patch.object(update_chromeos_llvm_next_hash,
+ 'GetEbuildPathsFromSymLinkPaths')
+ def testSuccessfullyCreatedPathDictionaryFromPackages(
+ self, mock_ebuild_paths_from_symlink_paths, mock_chroot_paths_to_symlinks,
+ mock_get_chroot_paths):
+
+ # Simulate 'GetChrootBuildPaths' when returning a chroot path for a valid
+ # package.
+ #
+ # Returns a list of chroot paths.
+ mock_get_chroot_paths.return_value = [
+ '/mnt/host/source/src/path/to/package-r1.ebuild'
+ ]
+
+ # Simulate '_ConvertChrootPathsToSymLinkPaths' 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 = [
+ '/some/path/to/chroot/src/path/to/package-r1.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 = {
+ '/some/path/to/chroot/src/path/to/package-r1.ebuild':
+ '/some/path/to/chroot/src/path/to/package.ebuild'
+ }
+
+ self.assertEqual(
+ update_chromeos_llvm_next_hash.CreatePathDictionaryFromPackages(
+ '/some/path/to/chroot', ['test-pckg/package']), {
+ '/some/path/to/chroot/src/path/to/package-r1.ebuild':
+ '/some/path/to/chroot/src/path/to/package.ebuild'
+ })
+
+ mock_get_chroot_paths.assert_called_once_with('/some/path/to/chroot',
+ ['test-pckg/package'])
+
+ mock_chroot_paths_to_symlinks.assert_called_once_with(
+ '/some/path/to/chroot',
+ ['/mnt/host/source/src/path/to/package-r1.ebuild'])
+
+ mock_ebuild_paths_from_symlink_paths.assert_called_once_with(
+ ['/some/path/to/chroot/src/path/to/package-r1.ebuild'])
+
+ @mock.patch.object(os.path, 'dirname')
+ @mock.patch.object(update_chromeos_llvm_next_hash, '_CreateRepo')
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'UpdateBuildLLVMNextHash')
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'UprevEbuild')
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'UploadChanges')
+ @mock.patch.object(update_chromeos_llvm_next_hash, '_DeleteRepo')
+ def testExceptionRaisedWhenUpdatingPackages(
+ self, mock_delete_repo, mock_upload_changes, mock_uprev_ebuild,
+ mock_update_llvm_next, mock_create_repo, mock_dirname):
+
+ # Test function to simulate 'os.path.dirname' returning a path to the
+ # directory of an ebuild file.
+ @CallCountsToMockFunctions
+ def SuccessfullyGetDirectoryPath(call_count, ebuild_path):
+ # Returns the absolute path to the directory of the ebuild file.
+ #
+ # 'os.path.dirname()' is expected to be called 2 times.
+ if call_count == 0 or call_count == 1:
+ return '/some/path/to/chroot/src/path/to'
+
+ # 'os.path.dirname()' was called more than 2 times.
+ assert False, 'os.path.dirname() was called more than 2 times.'
+
+ # Test function to simulate '_CreateRepo' when successfully created the
+ # repo on a valid repo path.
+ def SuccessfullyCreateRepoForChanges(repo_path, llvm_hash):
+ self.assertEqual(llvm_hash, 'a123testhash4')
+ return
+
+ # Test function to simulate 'UpdateBuildLLVMNextHash' when successfully
+ # updated the ebuild's 'LLVM_NEXT_HASH'.
+ def SuccessfullyUpdatedLLVMNextHash(ebuild_path, llvm_hash, llvm_version):
+ self.assertEqual(ebuild_path,
+ '/some/path/to/chroot/src/path/to/package.ebuild')
+ self.assertEqual(llvm_hash, 'a123testhash4')
+ self.assertEqual(llvm_version, 1000)
+ return
+
+ # Test function to simulate 'UprevEbuild' when the symlink to the ebuild
+ # does not have a revision number.
+ def FailedToUprevEbuild(symlink_path):
+ # 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(repo_path, llvm_hash, commit_messages):
+ # Test function should not be called (i.e. execution should resume in the
+ # 'finally' block) because 'UprevEbuild()' raised an
+ # exception.
+ assert False, 'Failed to go to \'finally\' block ' \
+ 'after the exception was raised.'
+
+ # Use test function to simulate behavior.
+ mock_dirname.side_effect = SuccessfullyGetDirectoryPath
+ mock_create_repo.side_effect = SuccessfullyCreateRepoForChanges
+ mock_update_llvm_next.side_effect = SuccessfullyUpdatedLLVMNextHash
+ mock_uprev_ebuild.side_effect = FailedToUprevEbuild
+ mock_upload_changes.side_effect = ShouldNotExecuteUploadChanges
+
+ # Verify exception is raised when an exception is thrown within
+ # the 'try' block by UprevEbuild function.
+ with self.assertRaises(ValueError) as err:
+ update_chromeos_llvm_next_hash.UpdatePackages({
+ '/some/path/to/chroot/src/path/to/package-r1.ebuild':
+ '/some/path/to/chroot/src/path/to/package.ebuild'
+ }, 'a123testhash4', 1000)
+
+ self.assertEqual(err.exception.message, 'Failed to uprev the ebuild.')
+
+ self.assertEqual(mock_dirname.call_count, 2)
+
+ mock_create_repo.assert_called_once_with('/some/path/to/chroot/src/path/to',
+ 'a123testhash4')
+
+ mock_update_llvm_next.assert_called_once_with(
+ '/some/path/to/chroot/src/path/to/package.ebuild', 'a123testhash4',
+ 1000)
+
+ mock_uprev_ebuild.assert_called_once_with(
+ '/some/path/to/chroot/src/path/to/package-r1.ebuild')
+
+ mock_upload_changes.assert_not_called()
+
+ mock_delete_repo.assert_called_once_with('/some/path/to/chroot/src/path/to',
+ 'a123testhash4')
+
+ @mock.patch.object(os.path, 'dirname')
+ @mock.patch.object(update_chromeos_llvm_next_hash, '_CreateRepo')
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'UpdateBuildLLVMNextHash')
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'UprevEbuild')
+ @mock.patch.object(os.path, 'basename')
+ @mock.patch.object(update_chromeos_llvm_next_hash, 'UploadChanges')
+ @mock.patch.object(update_chromeos_llvm_next_hash, '_DeleteRepo')
+ def testSuccessfullyUpdatedPackages(
+ self, mock_delete_repo, mock_upload_changes, mock_basename,
+ mock_uprev_ebuild, mock_update_llvm_next, mock_create_repo, mock_dirname):
+
+ # Test function to simulate 'os.path.dirname' on a valid ebuild path.
+ @CallCountsToMockFunctions
+ def SuccessfullyGetDirectoryPath(call_count, ebuild_path):
+ # Returns the absolute path to the directory of the ebuild file.
+ #
+ # 'os.path.dirname()' is expected to be called 3 times.
+ if call_count == 0 or call_count == 1:
+ return '/some/path/to/chroot/src/path/to'
+ if call_count == 2:
+ return '/some/path/to/chroot/src/path'
+
+ # 'os.path.dirname()' was called more than 3 times.
+ assert False, 'os.path.dirname() was called more than 3 times.'
+
+ # Test function to simulate '_CreateRepo' when successfully created the repo
+ # for the changes to be made to the ebuild files.
+ def SuccessfullyCreateRepoForChanges(repo_path, llvm_hash):
+ self.assertEqual(llvm_hash, 'a123testhash5')
+ return
+
+ # Test function to simulate 'os.path.basename' when called on the ebuild
+ # path.
+ @CallCountsToMockFunctions
+ def SuccessfullyGetBaseNameOfDirectory(call_count, path_to_ebuild_dir):
+ if call_count == 0:
+ self.assertEqual(path_to_ebuild_dir, '/some/path/to/chroot/src/path/to')
+
+ return 'to'
+ if call_count == 1:
+ self.assertEqual(path_to_ebuild_dir, '/some/path/to/chroot/src/path')
+
+ return 'path'
+
+ # Test function was called more times than expected (2 times).
+ assert False, 'os.path.basename() was called more than 2 times.'
+
+ # Test function to simulate 'UploadChanges' after a successfull update of
+ # 'LLVM_NEXT_HASH" of the ebuild file.
+ def SuccessfullyUpdatedLLVMNextHash(ebuild_path, llvm_hash, llvm_version):
+ self.assertEqual(ebuild_path,
+ '/some/path/to/chroot/src/path/to/package.ebuild')
+ self.assertEqual(llvm_hash, 'a123testhash5')
+ self.assertEqual(llvm_version, 1000)
+ return
+
+ # Test function to simulate 'UprevEbuild' when successfully incremented
+ # the revision number by 1.
+ def SuccessfullyUprevedEbuild(symlink_path):
+ self.assertEqual(symlink_path,
+ '/some/path/to/chroot/src/path/to/package-r1.ebuild')
+
+ return
+
+ # Use test function to simulate behavior.
+ mock_dirname.side_effect = SuccessfullyGetDirectoryPath
+ mock_create_repo.side_effect = SuccessfullyCreateRepoForChanges
+ mock_update_llvm_next.side_effect = SuccessfullyUpdatedLLVMNextHash
+ mock_uprev_ebuild.side_effect = SuccessfullyUprevedEbuild
+ mock_basename.side_effect = SuccessfullyGetBaseNameOfDirectory
+
+ update_chromeos_llvm_next_hash.UpdatePackages({
+ '/some/path/to/chroot/src/path/to/package-r1.ebuild':
+ '/some/path/to/chroot/src/path/to/package.ebuild'
+ }, 'a123testhash5', 1000)
+
+ self.assertEqual(mock_dirname.call_count, 3)
+
+ mock_create_repo.assert_called_once_with('/some/path/to/chroot/src/path/to',
+ 'a123testhash5')
+
+ mock_update_llvm_next.assert_called_once_with(
+ '/some/path/to/chroot/src/path/to/package.ebuild', 'a123testhash5',
+ 1000)
+
+ mock_uprev_ebuild.assert_called_once_with(
+ '/some/path/to/chroot/src/path/to/package-r1.ebuild')
+
+ self.assertEqual(mock_basename.call_count, 2)
+
+ expected_commit_messages = ' '.join([
+ '-m %s' % quote('llvm-next: Update packages to r1000'),
+ '-m %s' % quote('Following packages have been updated:'),
+ '-m %s' % quote('path/to')
+ ])
+
+ mock_upload_changes.assert_called_once_with(
+ '/some/path/to/chroot/src/path/to', 'a123testhash5',
+ expected_commit_messages)
+
+ mock_delete_repo.assert_called_once_with('/some/path/to/chroot/src/path/to',
+ 'a123testhash5')
+
+
+if __name__ == '__main__':
+ unittest.main()