diff --git a/kiwi/command.py b/kiwi/command.py index ecfc2c60..1c72854b 100644 --- a/kiwi/command.py +++ b/kiwi/command.py @@ -60,6 +60,9 @@ class Command(object): """ from .logger import log from .path import Path + command_type = namedtuple( + 'command', ['output', 'error', 'returncode'] + ) log.debug('EXEC: [%s]', ' '.join(command)) environment = os.environ if custom_env: @@ -69,9 +72,16 @@ class Command(object): custom_env=environment, access_mode=os.X_OK ): - raise KiwiCommandNotFound( - 'Command %s not found in the environment' % command[0] - ) + message = 'Command %s not found in the environment' % command[0] + if not raise_on_error: + log.debug('EXEC: %s', message) + return command_type( + output=None, + error=None, + returncode=-1 + ) + else: + raise KiwiCommandNotFound(message) try: process = subprocess.Popen( command, @@ -98,10 +108,7 @@ class Command(object): command[0], error.decode(), output.decode() ) ) - command = namedtuple( - 'command', ['output', 'error', 'returncode'] - ) - return command( + return command_type( output=output.decode(), error=error.decode(), returncode=process.returncode diff --git a/kiwi/path.py b/kiwi/path.py index 19ed746c..5d28ae6b 100644 --- a/kiwi/path.py +++ b/kiwi/path.py @@ -98,7 +98,10 @@ class Path(object): ) @classmethod - def which(self, filename, alternative_lookup_paths=None, custom_env=None, access_mode=None): + def which( + self, filename, alternative_lookup_paths=None, + custom_env=None, access_mode=None + ): """ Lookup file name in PATH @@ -106,7 +109,8 @@ class Path(object): :param list alternative_lookup_paths: list of additional lookup paths :param list custom_env: a custom os.environ :param int mode: one of the os access modes or a combination of - them (os.R_OK, os.W_OK and os.X_OK) + them (os.R_OK, os.W_OK and os.X_OK). If the provided access mode + does not match the file is considered not existing """ lookup_paths = [] system_path = os.environ.get('PATH') @@ -118,7 +122,7 @@ class Path(object): lookup_paths += alternative_lookup_paths for path in lookup_paths: location = os.path.join(path, filename) - if access_mode and os.access(location, access_mode): - return location - elif not access_mode and os.path.exists(location): - return location + if access_mode: + return location if os.path.exists(location) and os.access(location, access_mode) else None + else: + return location if os.path.exists(location) else None diff --git a/test/unit/command_test.py b/test/unit/command_test.py index 05eb1037..e89e473a 100644 --- a/test/unit/command_test.py +++ b/test/unit/command_test.py @@ -8,53 +8,66 @@ import os from .test_helper import * -from kiwi.exceptions import KiwiCommandError, KiwiCommandNotFound +from kiwi.exceptions import ( + KiwiCommandError, + KiwiCommandNotFound +) from kiwi.command import Command class TestCommand(object): @raises(KiwiCommandError) - @patch('os.access') + @patch('kiwi.path.Path.which') @patch('subprocess.Popen') - def test_run_raises_error(self, mock_popen, mock_access): + def test_run_raises_error(self, mock_popen, mock_which): + mock_which.return_value = 'command' mock_process = mock.Mock() mock_process.communicate = mock.Mock( return_value=[str.encode('stdout'), str.encode('stderr')] ) mock_process.returncode = 1 mock_popen.return_value = mock_process - mock_access.return_value = True Command.run(['command', 'args']) @raises(KiwiCommandError) - @patch('os.access') + @patch('kiwi.path.Path.which') @patch('subprocess.Popen') - def test_run_failure(self, mock_popen, mock_access): + def test_run_failure(self, mock_popen, mock_which): + mock_which.return_value = 'command' mock_popen.side_effect = KiwiCommandError('Run failure') - mock_access.return_value = True Command.run(['command', 'args']) @raises(KiwiCommandError) def test_run_invalid_environment(self): Command.run(['command', 'args'], {'HOME': '/root'}) - @patch('os.access') + @patch('kiwi.path.Path.which') @patch('subprocess.Popen') - def test_run_does_not_raise_error(self, mock_popen, mock_access): + def test_run_does_not_raise_error(self, mock_popen, mock_which): + mock_which.return_value = 'command' mock_process = mock.Mock() mock_process.communicate = mock.Mock( return_value=[str.encode('stdout'), str.encode('')] ) mock_process.returncode = 1 mock_popen.return_value = mock_process - mock_access.return_value = True result = Command.run(['command', 'args'], os.environ, False) assert result.error == '(no output on stderr)' assert result.output == 'stdout' + @patch('kiwi.path.Path.which') + def test_run_does_not_raise_error_if_command_not_found(self, mock_which): + mock_which.return_value = None + result = Command.run(['command', 'args'], os.environ, False) + assert result.error == None + assert result.output == None + assert result.returncode == -1 + @patch('os.access') + @patch('os.path.exists') @patch('subprocess.Popen') - def test_run(self, mock_popen, mock_access): + def test_run(self, mock_popen, mock_exists, mock_access): + mock_exists.return_value = True command_run = namedtuple( 'command', ['output', 'error', 'returncode'] ) @@ -80,14 +93,14 @@ class TestCommand(object): def test_call_command_does_not_exist(self): Command.call(['does-not-exist'], os.environ) - @patch('os.access') + @patch('kiwi.path.Path.which') @patch('subprocess.Popen') @patch('select.select') - def test_call(self, mock_select, mock_popen, mock_access): + def test_call(self, mock_select, mock_popen, mock_which): + mock_which.return_value = 'command' mock_select.return_value = [True, False, False] mock_process = mock.Mock() mock_popen.return_value = mock_process - mock_access.return_value = True command_call = namedtuple( 'command', [ 'output', 'output_available', @@ -103,9 +116,9 @@ class TestCommand(object): assert call.process == mock_process @raises(KiwiCommandError) - @patch('os.access') + @patch('kiwi.path.Path.which') @patch('subprocess.Popen') - def test_call_failure(self,mock_popen, mock_access): + def test_call_failure(self,mock_popen, mock_which): + mock_which.return_value = 'command' mock_popen.side_effect = KiwiCommandError('Call failure') - mock_access.return_value = True call = Command.call(['command', 'args']) diff --git a/test/unit/iso_test.py b/test/unit/iso_test.py index bedd6772..a6316723 100644 --- a/test/unit/iso_test.py +++ b/test/unit/iso_test.py @@ -191,7 +191,9 @@ class TestIso(object): assert result[2158].name == 'header_end' - def test_create_header_end_block(self): + @patch('kiwi.path.Path.which') + def test_create_header_end_block(self, mock_which): + mock_which.return_value = 'isoinfo' temp_file = NamedTemporaryFile() self.iso.header_end_file = temp_file.name assert self.iso.create_header_end_block( @@ -199,7 +201,9 @@ class TestIso(object): ) == 96 @raises(KiwiIsoLoaderError) - def test_create_header_end_block_raises(self): + @patch('kiwi.path.Path.which') + def test_create_header_end_block_raises(self, mock_which): + mock_which.return_value = 'isoinfo' temp_file = NamedTemporaryFile() self.iso.header_end_file = temp_file.name self.iso.create_header_end_block( diff --git a/test/unit/shell_test.py b/test/unit/shell_test.py index 3089927d..b72ea451 100644 --- a/test/unit/shell_test.py +++ b/test/unit/shell_test.py @@ -14,7 +14,9 @@ class TestShell(object): def test_quote(self): assert Shell.quote('aa\!') == 'aa\\\\\\!' - def test_quote_key_value_file(self): + @patch('kiwi.path.Path.which') + def test_quote_key_value_file(self, mock_which): + mock_which.return_value = 'cp' assert Shell.quote_key_value_file('../data/key_value') == [ "foo='bar'", "bar='xxx'", diff --git a/test/unit/system_profile_test.py b/test/unit/system_profile_test.py index 2e9a4204..2eac1733 100644 --- a/test/unit/system_profile_test.py +++ b/test/unit/system_profile_test.py @@ -26,7 +26,9 @@ class TestProfile(object): ) @patch('kiwi.system.profile.NamedTemporaryFile') - def test_create(self, mock_temp): + @patch('kiwi.path.Path.which') + def test_create(self, mock_which, mock_temp): + mock_which.return_value = 'cp' mock_temp.return_value = self.tmpfile result = self.profile.create() os.remove(self.tmpfile.name) @@ -130,7 +132,9 @@ class TestProfile(object): ] @patch('kiwi.system.profile.NamedTemporaryFile') - def test_create_cpio(self, mock_temp): + @patch('kiwi.path.Path.which') + def test_create_cpio(self, mock_which, mock_temp): + mock_which.return_value = 'cp' mock_temp.return_value = self.tmpfile description = XMLDescription('../data/example_dot_profile_config.xml') profile = Profile( diff --git a/test/unit/utils_checksum_test.py b/test/unit/utils_checksum_test.py index a0e450d0..ea522b9d 100644 --- a/test/unit/utils_checksum_test.py +++ b/test/unit/utils_checksum_test.py @@ -34,11 +34,15 @@ class TestChecksum(object): def test_checksum_file_not_found(self): Checksum('some-file') + @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') @patch_open - def test_md5_xz(self, mock_open, mock_size, mock_md5, mock_compress): + def test_md5_xz( + self, mock_open, mock_size, mock_md5, mock_compress, mock_which + ): + mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 @@ -63,11 +67,15 @@ class TestChecksum(object): 'sum 163968 8192 163968 8192\n' ) + @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.md5') @patch('os.path.getsize') @patch_open - def test_md5(self, mock_open, mock_size, mock_md5, mock_compress): + def test_md5( + self, mock_open, mock_size, mock_md5, mock_compress, mock_which + ): + mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 @@ -92,11 +100,15 @@ class TestChecksum(object): 'sum 163968 8192\n' ) + @patch('kiwi.path.Path.which') @patch('kiwi.utils.checksum.Compress') @patch('hashlib.sha256') @patch('os.path.getsize') @patch_open - def test_sha256(self, mock_open, mock_size, mock_sha256, mock_compress): + def test_sha256( + self, mock_open, mock_size, mock_sha256, mock_compress, mock_which + ): + mock_which.return_value = 'factor' compress = mock.Mock() digest = mock.Mock() digest.block_size = 1024 diff --git a/test/unit/utils_compress_test.py b/test/unit/utils_compress_test.py index a9b53f88..9c488692 100644 --- a/test/unit/utils_compress_test.py +++ b/test/unit/utils_compress_test.py @@ -69,7 +69,9 @@ class TestCompress(object): mock_format.return_value = None self.compress.uncompress() - def test_get_format(self): + @patch('kiwi.path.Path.which') + def test_get_format(self, mock_which): + mock_which.return_value = 'ziptool' xz = Compress('../data/xz_data.xz') assert xz.get_format() == 'xz' gzip = Compress('../data/gz_data.gz')