kiwi-el8/test/unit/iso_tools_iso_test.py
Marcus Schäfer 4899da09c6
Complete test coverage on iso tools testing
If there is no mkisofs/genisoimage present on the system, the
tests based on real example iso files are skipped. The code
tested in this case is now covered in a mock style to run
through the logic even without the tools in place
2019-03-06 17:44:45 +01:00

332 lines
11 KiB
Python

from builtins import bytes
from mock import (
call, patch
)
import mock
import struct
import pytest
import sys
from .test_helper import raises, patch_open
from kiwi.iso_tools.iso import Iso
from kiwi.path import Path
from tempfile import NamedTemporaryFile
from kiwi.exceptions import (
KiwiIsoLoaderError,
KiwiIsoMetaDataError,
KiwiCommandError
)
class TestIso(object):
@patch('platform.machine')
def setup(self, mock_machine):
mock_machine.return_value = 'x86_64'
self.context_manager_mock = mock.Mock()
self.file_mock = mock.Mock()
self.enter_mock = mock.Mock()
self.exit_mock = mock.Mock()
self.enter_mock.return_value = self.file_mock
setattr(self.context_manager_mock, '__enter__', self.enter_mock)
setattr(self.context_manager_mock, '__exit__', self.exit_mock)
self.iso = Iso('source-dir')
@patch_open
def test_create_header_end_marker(self, mock_open):
mock_open.return_value = self.context_manager_mock
self.iso.create_header_end_marker()
assert self.file_mock.write.called_once_with(
'source-dir/header_end 1000000\n'
)
@raises(KiwiIsoLoaderError)
@patch('os.path.exists')
def test_setup_isolinux_boot_path_raises(self, mock_exists):
mock_exists.return_value = False
self.iso.setup_isolinux_boot_path()
@patch('os.path.exists')
@patch('kiwi.iso_tools.iso.Command.run')
def test_setup_isolinux_boot_path(self, mock_command, mock_exists):
mock_exists.return_value = True
self.iso.setup_isolinux_boot_path()
mock_command.assert_called_once_with(
[
'isolinux-config', '--base', 'boot/x86_64/loader',
'source-dir/boot/x86_64/loader/isolinux.bin'
]
)
@patch('kiwi.iso_tools.iso.Command.run')
@patch('os.path.exists')
def test_setup_isolinux_boot_path_failed_isolinux_config(
self, mock_exists, mock_command
):
mock_exists.return_value = True
command_raises = [False, True]
def side_effect(arg):
if command_raises.pop():
raise Exception
mock_command.side_effect = side_effect
self.iso.setup_isolinux_boot_path()
assert mock_command.call_args_list[1] == call(
[
'cp', '-a', '-l',
'source-dir/boot/x86_64/loader/', 'source-dir/isolinux/'
]
)
@pytest.mark.skipif(
Path.which('isoinfo') is None, reason='requires cdrtools'
)
def test_create_header_end_block_on_test_iso(self):
temp_file = NamedTemporaryFile()
self.iso.header_end_file = temp_file.name
assert self.iso.create_header_end_block(
'../data/iso_with_marker.iso'
) == 96
@patch_open
@patch('kiwi.iso_tools.iso.IsoToolsCdrTools')
def test_create_header_end_block(self, mock_IsoToolsCdrTools, mock_open):
mock_open.return_value = self.context_manager_mock
self.file_mock.read.return_value = bytes(
b'7984fc91-a43f-4e45-bf27-6d3aa08b24cf'
)
iso_tool = mock.Mock()
iso_tool.list_iso.return_value = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
mock_IsoToolsCdrTools.return_value = iso_tool
self.iso.create_header_end_block('some-iso-file')
assert mock_open.call_args_list == [
call('some-iso-file', 'rb'),
call('source-dir/header_end', 'wb')
]
@pytest.mark.skipif(
Path.which('isoinfo') is None, reason='requires cdrtools'
)
@raises(KiwiIsoLoaderError)
def test_create_header_end_block_raises_on_test_iso(self):
temp_file = NamedTemporaryFile()
self.iso.header_end_file = temp_file.name
self.iso.create_header_end_block(
'../data/iso_no_marker.iso'
)
@patch_open
@patch('kiwi.iso_tools.iso.IsoToolsCdrTools')
@raises(KiwiIsoLoaderError)
def test_create_header_end_block_raises(
self, mock_IsoToolsCdrTools, mock_open
):
self.iso.create_header_end_block('some-iso-file')
@patch('kiwi.iso_tools.iso.Command.run')
def test_create_hybrid(self, mock_command):
command = mock.Mock()
command.error = None
mock_command.return_value = command
Iso.create_hybrid(42, '0x0815', 'some-iso', 'efi')
mock_command.assert_called_once_with(
[
'isohybrid', '--offset', '42',
'--id', '0x0815', '--type', '0x83',
'--uefi', 'some-iso'
]
)
@raises(KiwiCommandError)
@patch('kiwi.iso_tools.iso.Command.run')
def test_create_hybrid_with_error(self, mock_command):
command = mock.Mock()
command.error = 'some error message'
mock_command.return_value = command
Iso.create_hybrid(42, '0x0815', 'some-iso', 'efi')
@raises(KiwiCommandError)
@patch('kiwi.iso_tools.iso.Command.run')
def test_create_hybrid_with_multiple_errors(self, mock_command):
command = mock.Mock()
command.error = \
'isohybrid: Warning: more than 1024 cylinders: 1817\n' + \
'isohybrid: Not all BIOSes will be able to boot this device\n' + \
'isohybrid: some other error we do not ignore'
mock_command.return_value = command
Iso.create_hybrid(42, '0x0815', 'some-iso', 'efi')
@patch('kiwi.iso_tools.iso.Command.run')
def test_create_hybrid_with_cylinders_warning(self, mock_command):
command = mock.Mock()
command.error = \
'isohybrid: Warning: more than 1024 cylinders: 1817\n' + \
'isohybrid: Not all BIOSes will be able to boot this device\n'
mock_command.return_value = command
Iso.create_hybrid(42, '0x0815', 'some-iso', 'efi')
mock_command.assert_called_once_with(
[
'isohybrid', '--offset', '42',
'--id', '0x0815', '--type', '0x83',
'--uefi', 'some-iso'
]
)
@patch('kiwi.iso_tools.iso.Command.run')
def test_set_media_tag(self, mock_command):
Iso.set_media_tag('foo')
mock_command.assert_called_once_with(
['tagmedia', '--md5', '--check', '--pad', '150', 'foo']
)
@patch_open
@raises(KiwiIsoMetaDataError)
def test_iso_metadata_iso9660_invalid(self, mock_open):
mock_open.return_value = self.context_manager_mock
self.file_mock.read.return_value = bytes(b'bogus')
Iso.fix_boot_catalog('isofile')
@patch_open
@raises(KiwiIsoMetaDataError)
def test_iso_metadata_not_bootable(self, mock_open):
mock_open.return_value = self.context_manager_mock
self.file_mock.read.return_value = bytes(b'CD001')
Iso.fix_boot_catalog('isofile')
@patch_open
@raises(KiwiIsoMetaDataError)
def test_iso_metadata_path_table_sector_invalid(self, mock_open):
mock_open.return_value = self.context_manager_mock
read_results = [bytes(b'EL TORITO SPECIFICATION'), bytes(b'CD001')]
def side_effect(arg):
return read_results.pop()
self.file_mock.read.side_effect = side_effect
Iso.fix_boot_catalog('isofile')
@patch_open
@raises(KiwiIsoMetaDataError)
def test_iso_metadata_catalog_sector_invalid(self, mock_open):
mock_open.return_value = self.context_manager_mock
volume_descriptor = \
bytes(b'CD001') + bytes(b'_') * (0x08c - 0x5) + bytes(b'0x1d5f23a')
read_results = [bytes(b'EL TORITO SPECIFICATION'), volume_descriptor]
def side_effect(arg):
return read_results.pop()
self.file_mock.read.side_effect = side_effect
Iso.fix_boot_catalog('isofile')
@patch_open
@raises(KiwiIsoMetaDataError)
def test_iso_metadata_catalog_invalid(self, mock_open):
mock_open.return_value = self.context_manager_mock
volume_descriptor = \
bytes(b'CD001') + bytes(b'_') * (0x08c - 0x5) + bytes(b'0x1d5f23a')
eltorito_descriptor = \
bytes(b'EL TORITO SPECIFICATION') + \
bytes(b'_') * (0x47 - 0x17) + bytes(b'0x1d5f23a')
read_results = [eltorito_descriptor, volume_descriptor]
def side_effect(arg):
return read_results.pop()
self.file_mock.read.side_effect = side_effect
Iso.fix_boot_catalog('isofile')
@patch_open
def test_relocate_boot_catalog(self, mock_open):
mock_open.return_value = self.context_manager_mock
volume_descriptor = \
bytes(b'CD001') + bytes(b'_') * (0x08c - 0x5) + bytes(b'0x1d5f23a')
eltorito_descriptor = \
bytes(b'EL TORITO SPECIFICATION') + \
bytes(b'_') * (0x47 - 0x17) + bytes(b'0x1d5f23a')
new_volume_descriptor = \
bytes(b'bogus')
next_new_volume_descriptor = \
bytes(b'TEA01')
new_boot_catalog = bytes(b'\x00') * 0x800
read_results = [
new_boot_catalog,
next_new_volume_descriptor,
new_volume_descriptor,
bytes(b'catalog'),
eltorito_descriptor,
volume_descriptor
]
def side_effect(arg):
return read_results.pop()
self.file_mock.read.side_effect = side_effect
Iso.relocate_boot_catalog('isofile')
assert self.file_mock.write.call_args_list == [
call(bytes(b'catalog')),
call(
bytes(
b'EL TORITO SPECIFICATION'
) + bytes(
b'_'
) * (0x47 - 0x17) + bytes(b'\x13\x00\x00\x005f23a')
)
]
@patch_open
def test_fix_boot_catalog(self, mock_open):
mock_open.return_value = self.context_manager_mock
volume_descriptor = \
bytes(b'CD001') + bytes(b'_') * (0x08c - 0x5) + bytes(b'0x1d5f23a')
eltorito_descriptor = \
bytes(b'EL TORITO SPECIFICATION') + \
bytes(b'_') * (0x47 - 0x17) + bytes(b'0x1d5f23a')
boot_catalog = bytes(b'_') * 64 + struct.pack('B', 0x88) + \
bytes(b'_') * 32
read_results = [
boot_catalog,
eltorito_descriptor,
volume_descriptor
]
def side_effect(arg):
return read_results.pop()
self.file_mock.read.side_effect = side_effect
Iso.fix_boot_catalog('isofile')
if sys.byteorder == 'big':
assert self.file_mock.write.call_args_list == [
call(
bytes(
b'_'
) * 44 + bytes(
b'\x01Legacy (isolinux)\x00\x00\x91\xef\x00\x01'
) + bytes(
b'\x00'
) * 28 + bytes(
b'\x88___________\x01UEFI (grub)'
) + bytes(b'\x00') * 8
)
]
else:
assert self.file_mock.write.call_args_list == [
call(
bytes(
b'_'
) * 44 + bytes(
b'\x01Legacy (isolinux)\x00\x00\x91\xef\x01'
) + bytes(
b'\x00'
) * 29 + bytes(
b'\x88___________\x01UEFI (grub)'
) + bytes(b'\x00') * 8
)
]