blob: 1456156ddc9ddc1448a1bb763b4bb723047fc074 [file] [log] [blame]
#!/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 when creating the arguments for the patch manager."""
from __future__ import print_function
from collections import namedtuple
import mock
import os
import unittest
from cros_utils import command_executer
from failure_modes import FailureModes
from test_helpers import CallCountsToMockFunctions
import llvm_patch_management
import patch_manager
class LlvmPatchManagementTest(unittest.TestCase):
"""Test class when constructing the arguments for the patch manager."""
def testInvalidChrootPathWhenGetPathToFilesDir(self):
# 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('/some/path/to/chroot',
'sys-devel/llvm')
self.assertEqual(err.exception.message,
'Invalid chroot provided: /some/path/to/chroot')
# 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(command_executer.CommandExecuter,
'ChrootRunCommandWOutput')
def testFailedToGetChrootPathToEbuildWhenGetPathToFilesDir(
self, mock_chroot_cmd, mock_isdir):
# Simulate behavior of 'ChrootRunCommandWOutput()' when failed to get the
# absolute chroot path to the package's ebuild.
#
# Returns shell error code, stdout, stderr.
mock_chroot_cmd.return_value = (1, None, 'Invalid package provided.')
# Verify the exception is raised when failed to get the absolute chroot
# path to a package's ebuild.
with self.assertRaises(ValueError) as err:
llvm_patch_management.GetPathToFilesDirectory('/some/path/to/chroot',
'test/package')
self.assertEqual(
err.exception.message,
'Failed to get the absolute chroot path of the package '
'test/package: Invalid package provided.')
mock_chroot_cmd.assert_called_once_with(
chromeos_root='/some/path/to/chroot',
command='equery w test/package',
print_to_console=False)
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(command_executer.CommandExecuter,
'ChrootRunCommandWOutput')
@mock.patch.object(llvm_patch_management, '_GetRelativePathOfChrootPath')
def testSuccessfullyGetPathToFilesDir(
self, mock_get_relative_path_of_chroot_path, mock_chroot_cmd, mock_isdir):
# Simulate behavior of 'ChrootRunCommandWOutput()' when successfully
# retrieved the absolute chroot path to the package's ebuild.
#
# Returns shell error code, stdout, stderr.
mock_chroot_cmd.return_value = (0,
'/mnt/host/source/path/to/llvm/llvm.ebuild',
0)
# 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'
self.assertEqual(
llvm_patch_management.GetPathToFilesDirectory('/some/path/to/chroot',
'sys-devel/llvm'),
'/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):
# 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('/path/to/llvm')
self.assertEqual(err.exception.message,
'Invalid prefix for the chroot path: /path/to/llvm')
def testValidPrefixForChrootPath(self):
self.assertEqual(
llvm_patch_management._GetRelativePathOfChrootPath(
'/mnt/host/source/path/to/llvm'), 'path/to/llvm')
# 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):
# 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/files/test.json')
self.assertEqual(err.exception.message,
'Invalid file provided: /abs/path/to/files/test.json')
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):
# 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/files/PATCHES')
self.assertEqual(
err.exception.message, 'File does not end in \'.json\': '
'/abs/path/to/files/PATCHES')
mock_isfile.assert_called_once()
@mock.patch.object(os.path, 'isfile')
def testValidPatchMetadataFile(self, mock_isfile):
# 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/files/PATCHES.json')
mock_isfile.assert_called_once()
@mock.patch.object(command_executer.CommandExecuter,
'ChrootRunCommandWOutput')
def testFailedToUnpackPackage(self, mock_chroot_cmd):
# Simulate the behavior of 'ChrootRunCommandWOutput()' when unpacking fails
# on a package.
@CallCountsToMockFunctions
def MultipleCallsToGetSrcPath(call_count,
chromeos_root,
command,
print_to_console,
env=None):
# First call to 'ChrootRunCommandWOutput()' which would successfully
# get the ebuild path of the package.
if call_count == 0:
# Returns shell error code, stdout, stderr.
return 0, '/mount/host/source/path/to/package/test-r1.ebuild', 0
# Second call to 'ChrootRunCommandWOutput()' which failed to unpack the
# package.
if call_count == 1:
# Returns shell error code, stdout, stderr.
return 1, None, 'Invalid package provided.'
# 'ChrootRunCommandWOutput()' was called more times than expected (2
# times).
assert False, ('Unexpectedly called more than 2 times.')
# Use test function to simulate 'ChrootRunCommandWOutput()' behavior.
mock_chroot_cmd.side_effect = MultipleCallsToGetSrcPath
# Verify the exception is raised when failed to unpack a package.
with self.assertRaises(ValueError) as err:
llvm_patch_management.UnpackLLVMPackage('/some/path/to/chroot',
'test/package')
self.assertEqual(
err.exception.message, 'Failed to unpack the package test/package: '
'Invalid package provided.')
self.assertEqual(mock_chroot_cmd.call_count, 2)
@mock.patch.object(command_executer.CommandExecuter,
'ChrootRunCommandWOutput')
def testFailedToGetChrootPathToEbuild(self, mock_chroot_cmd):
# Simulate the behavior of 'ChrootRunCommandWOutput()' when failed to get
# the absolute chroot path to the package's ebuild.
mock_chroot_cmd.return_value = (1, None, 'Invalid package provided.')
# Verify the exception is raised when failed to get the absolute chroot
# path to the package's ebuild.
with self.assertRaises(ValueError) as err:
llvm_patch_management.UnpackLLVMPackage('/some/path/to/chroot',
'test/package')
self.assertEqual(
err.exception.message,
'Failed to get the absolute chroot path to the ebuild of '
'test/package: Invalid package provided.')
mock_chroot_cmd.assert_called_once_with(
chromeos_root='/some/path/to/chroot',
command='equery w test/package',
print_to_console=False)
@mock.patch.object(command_executer.CommandExecuter,
'ChrootRunCommandWOutput')
@mock.patch.object(llvm_patch_management, '_ConstructPathToSources')
def testSuccessfullyGetSrcPath(self, mock_construct_src_path,
mock_chroot_cmd):
# Simulate the behavior of 'ChrootRunCommandWOutput()' when successfully
# get the absolute chroot ebuild path to the package and successfully
# unpacked the package.
@CallCountsToMockFunctions
def MultipleCallsToGetSrcPath(call_count,
chromeos_root,
command,
print_to_console,
env=None):
# First call to 'ChrootRunCommandWOutput()' which would successfully
# get the absolute chroot path to the package's ebuild.
if call_count == 0:
# Returns shell error code, stdout, stderr.
return 0, '/mount/host/source/path/to/package/test-r1.ebuild', 0
# Second call to 'ChrootRunCommandWOutput()' which would successfully
# unpack the package.
if call_count == 1:
# Returns shell error code, stdout, stderr.
return 0, None, 0
# 'ChrootRunCommandWOutput()' was called more times than expected (2
# times).
assert False, ('Unexpectedly called more than 2 times.')
# Use the test function to simulate 'ChrootRunCommandWOutput()' behavior.
mock_chroot_cmd.side_effect = MultipleCallsToGetSrcPath
# Simulate the behavior of '_ConstructPathToSources()' when the ebuild name
# has a revision number and '.ebuild' and the absolute path to the src
# directory is valid.
mock_construct_src_path.return_value = ('/some/path/to/chroot/chroot/var'
'/tmp/portage/to/test-r1/work/'
'test')
self.assertEqual(
llvm_patch_management.UnpackLLVMPackage('/some/path/to/chroot',
'package/test'),
'/some/path/to/chroot/chroot/var/tmp/portage/to/test-r1/work/test')
self.assertEqual(mock_chroot_cmd.call_count, 2)
mock_construct_src_path.assert_called_once_with('/some/path/to/chroot',
'test-r1.ebuild', 'to')
def testFailedToRemoveEbuildPartFromTheEbuildName(self):
# Verify the exception is raised when the ebuild name with the revision
# number does not have '.ebuild' in the name.
#
# Ex: llvm-9.0_pre361749_p20190714-r4
#
# Does not have a '.ebuild' in the ebuild name.
with self.assertRaises(ValueError) as err:
llvm_patch_management._ConstructPathToSources('/some/path/to/chroot',
'test-r1', 'test-packages')
self.assertEqual(err.exception.message,
'Failed to remove \'.ebuild\' from test-r1.')
def testFailedToRemoveTheRevisionNumberFromTheEbuildName(self):
# Verify the exception is raised when the ebuild name with the revision
# number does not have the revision number in the name.
#
# Ex: llvm-9.0_pre361749_p20190714.ebuild
#
# Does not have a revision number in the ebuild name.
with self.assertRaises(ValueError) as err:
llvm_patch_management._ConstructPathToSources(
'/some/path/to/chroot', 'test.ebuild', 'test-packages')
self.assertEqual(err.exception.message,
'Failed to remove the revision number from test.')
# Simulate behavior of 'os.path.isdir()' when the constructed absolute path to
# the unpacked sources does not exist.
@mock.patch.object(os.path, 'isdir', return_value=False)
def testInvalidPathToUnpackedSources(self, mock_isdir):
# Verify the exception is raised when the absolute path to the unpacked
# sources is constructed, but the path is invalid.
with self.assertRaises(ValueError) as err:
llvm_patch_management._ConstructPathToSources(
'/some/path/to/chroot', 'test-r1.ebuild', 'test-packages')
self.assertEqual(
err.exception.message,
'Failed to construct the absolute path to the unpacked '
'sources of the package test: '
'/some/path/to/chroot/chroot/var/tmp/portage/test-packages'
'/test-r1/work/test')
mock_isdir.assert_called_once()
# Simulate the behavior of 'os.path.isdir()' when the absolute path to the
# src directory exists.
@mock.patch.object(os.path, 'isdir', return_value=True)
def testSuccessfullyConstructedSrcPath(self, mock_isdir):
self.assertEqual(
llvm_patch_management._ConstructPathToSources(
'/some/path/to/chroot', 'test-r1.ebuild', 'test-packages'),
'/some/path/to/chroot/chroot/var/tmp/portage/test-packages/'
'test-r1/work/test')
mock_isdir.assert_called_once()
@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):
# 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,
'/some/path/to/chroot/some/path/to/filesdir/PATCHES')
raise ValueError('File does not end in \'.json\': '
'/some/path/to/chroot/some/path/to/filesdir/PATCHES')
# Use the test function to simulate behavior of '_CheckPatchMetadataPath()'.
mock_check_patch_metadata_path.side_effect = InvalidPatchMetadataFile
# Simulate the behavior of 'GetPathToFilesDirectory()' when successfully
# constructed the absolute path to $FILESDIR of a package.
mock_get_filesdir_path.return_value = ('/some/path/to/chroot/some/path/'
'to/filesdir')
# 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(
'/some/path/to/chroot', 1000, 'PATCHES', ['test-packages/package1'],
FailureModes.FAIL)
self.assertEqual(
err.exception.message, 'File does not end in \'.json\': '
'/some/path/to/chroot/some/path/to/filesdir/PATCHES')
mock_get_filesdir_path.assert_called_once_with('/some/path/to/chroot',
'test-packages/package1')
mock_check_patch_metadata_path.assert_called_once()
@mock.patch.object(llvm_patch_management, 'GetPathToFilesDirectory')
@mock.patch.object(llvm_patch_management, '_CheckPatchMetadataPath')
@mock.patch.object(llvm_patch_management, 'UnpackLLVMPackage')
@mock.patch.object(patch_manager, 'HandlePatches')
def testSuccessfullyRetrievedPatchResults(
self, mock_handle_patches, mock_unpack_package,
mock_check_patch_metadata_path, mock_get_filesdir_path):
# Simulate the behavior of 'GetPathToFilesDirectory()' when successfully
# constructed the absolute path to $FILESDIR of a package.
mock_get_filesdir_path.return_value = ('/some/path/to/chroot/some/path/'
'to/filesdir')
# Simulate the behavior of 'UnpackLLVMPackage()' when successfully unpacked
# the package and constructed the absolute path to the unpacked sources.
mock_unpack_package.return_value = ('/some/path/to/chroot/chroot/var/tmp/'
'portage/test-packages/package2-r1/work'
'/package2')
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)
expected_patch_results = {
'applied_patches': ['fixes_something.patch'],
'failed_patches': ['disables_output.patch'],
'non_applicable_patches': [],
'disabled_patches': [],
'removed_patches': [],
'modified_metadata': None
}
patch_info = llvm_patch_management.UpdatePackagesPatchMetadataFile(
'/some/path/to/chroot', 1000, 'PATCHES.json',
['test-packages/package2'], FailureModes.CONTINUE)
self.assertDictEqual(patch_info,
{'test-packages/package2': expected_patch_results})
mock_get_filesdir_path.assert_called_once_with('/some/path/to/chroot',
'test-packages/package2')
mock_check_patch_metadata_path.assert_called_once_with(
'/some/path/to/chroot/some/path/to/filesdir/PATCHES.json')
mock_unpack_package.assert_called_once_with('/some/path/to/chroot',
'test-packages/package2')
mock_handle_patches.assert_called_once_with(
1000, '/some/path/to/chroot/some/path/to/filesdir/PATCHES.json',
'/some/path/to/chroot/some/path/to/filesdir',
'/some/path/to/chroot/chroot/var/tmp/portage/test-packages/'
'package2-r1/work/package2', FailureModes.CONTINUE)
if __name__ == '__main__':
unittest.main()