import io
import os
import logging
from collections import namedtuple
from unittest.mock import (
    patch, call, MagicMock, Mock
)
from pytest import (
    raises, fixture
)
import pytest

import kiwi

from kiwi.defaults import Defaults
from kiwi.defaults import grub_loader_type
from kiwi.xml_state import XMLState
from kiwi.xml_description import XMLDescription
from kiwi.bootloader.config.grub2 import BootLoaderConfigGrub2
from kiwi.utils.sysconfig import SysConfig
from kiwi.xml_state import DracutT

from kiwi.exceptions import (
    KiwiBootLoaderGrubPlatformError,
    KiwiBootLoaderGrubSecureBootError,
    KiwiFileNotFound,
    KiwiTemplateError,
    KiwiBootLoaderGrubDataError,
    KiwiBootLoaderGrubFontError,
    KiwiBootLoaderGrubModulesError,
    KiwiDiskGeometryError,
    KiwiCommandNotFound
)


class TestBootLoaderConfigGrub2:
    @fixture(autouse=True)
    def inject_fixtures(self, caplog):
        self._caplog = caplog

    @patch('kiwi.bootloader.config.grub2.FirmWare')
    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_theme')
    def setup(self, mock_theme, mock_firmware):
        Defaults.set_platform_name('x86_64')
        self.command_type = namedtuple(
            'command_return_type', ['output']
        )
        self.find_grub = {}
        self.os_exists = {
            'root_dir/boot/grub2/fonts/unicode.pf2': True,
            'root_dir/boot/x86_64/loader/grub2/fonts/unicode.pf2': True,
            'root_dir/boot/grub2/themes/some-theme/background.png': True,
            'root_dir/usr/share/grub2': True,
            'root_dir/usr/share/grub': False,
            'root_dir/boot/grub2/themes': False,
            'root_dir/boot/grub/themes': False,
            'root_dir/boot/grub/fonts/unicode.pf2': False,
            'root_dir/usr/lib/grub2': True,
            'root_dir/usr/lib/grub': False,
            'root_dir/boot/grub2/x86_64-efi': False,
            'root_dir/boot/grub2/i386-pc': False,
            'root_dir/boot/grub2/x86_64-xen': False,
            'root_dir/usr/lib64/efi/shim.efi': True,
            'root_dir/usr/lib64/efi/grub.efi': True,
            'root_dir/usr/lib64/efi/does-not-exist': False,
            'root_dir/boot/efi/': True,
            'root_dir/usr/share/grub2/powerpc-ieee1275': True
        }
        self.glob_iglob_shim_fallback = [
            # get_mok_manager
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/MokManager.efi'],
            # get_shim_loader
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/shim.efi'],
            # get_signed_grub_loader(disk)
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/grub.efi'],
            # get_grub_efi_font_directory
            ['root_dir/boot/efi/EFI/DIST/fonts']
        ]
        self.glob_iglob_noshim_fallback = [
            # get_mok_manager
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/MokManager.efi'],
            # get_signed_grub_loader(disk)
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/grub.efi'],
            # get_grub_efi_font_directory
            ['root_dir/boot/efi/EFI/DIST/fonts']
        ]
        self.glob_iglob_shim_iso = [
            # get_mok_manager
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/MokManager.efi'],
            # get_shim_loader
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/shim.efi'],
            # get_signed_grub_loader(iso)
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            [],
            ['root_dir/usr/lib64/efi/grub.efi'],
            # get_grub_efi_font_directory
            ['root_dir/boot/efi/EFI/DIST/fonts']
        ]

        mock_theme.return_value = None
        kiwi.bootloader.config.grub2.Path = Mock()
        kiwi.bootloader.config.base.Path = Mock()

        self.firmware = Mock()
        self.firmware.ec2_mode = Mock(
            return_value=None
        )
        self.firmware.efi_mode = Mock(
            return_value=''
        )
        mock_firmware.return_value = self.firmware

        self.mbrid = Mock()
        self.mbrid.get_id = Mock(
            return_value='0xffffffff'
        )

        self.grub2 = Mock()
        kiwi.bootloader.config.grub2.BootLoaderTemplateGrub2 = Mock(
            return_value=self.grub2
        )

        self.state = XMLState(
            XMLDescription('../data/example_config.xml').load()
        )
        self.state.is_xen_server = Mock(
            return_value=False
        )
        self.state.is_xen_guest = Mock(
            return_value=False
        )
        self.state.get_build_type_bootloader_serial_line_setup = Mock(
            return_value='serial --speed=38400'
        )
        self.state.get_build_type_bootloader_timeout_style = Mock(
            return_value='countdown'
        )
        self.bootloader = BootLoaderConfigGrub2(
            self.state, 'root_dir', None, {
                'grub_directory_name': 'grub2',
                'grub_load_command': 'source',
                'boot_is_crypto': True,
                'crypto_disk': True,
                'targetbase': 'rootdev',
                'config_options': ['--set-trusted-boot']
            }
        )
        self.bootloader.cmdline = 'some-cmdline root=UUID=foo'
        self.bootloader.cmdline_failsafe = ' '.join(
            [self.bootloader.cmdline, 'failsafe-options']
        )

    @patch('kiwi.bootloader.config.grub2.FirmWare')
    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_theme')
    def setup_method(self, cls, mock_theme, mock_firmware):
        self.setup()

    def test_setup_sysconfig_bootloader_not_impl(self):
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )

        with pytest.raises(NotImplementedError):
            BootLoaderConfigGrub2(
                xml_state=xml_state, root_dir='root_dir'
            ).setup_sysconfig_bootloader()

    @patch('kiwi.bootloader.config.grub2.Path.which')
    def test_post_init_grub2_boot_directory(self, mock_which):
        Defaults.set_platform_name('i686')
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        mock_which.return_value = None
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.boot_directory_name == 'grub'

    @patch('kiwi.bootloader.config.grub2.Path.which')
    def test_post_init_grub2_load_command(self, mock_which):
        Defaults.set_platform_name('i686')
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        mock_which.return_value = None
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.grub_load == 'source'

    def test_post_init_invalid_platform(self):
        Defaults.set_platform_name('unsupported-arch')
        with raises(KiwiBootLoaderGrubPlatformError):
            BootLoaderConfigGrub2(Mock(), 'root_dir')

    @patch('kiwi.defaults.Defaults.get_shim_loader')
    @patch('kiwi.defaults.Defaults.get_signed_grub_loader')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('os.path.exists')
    @patch('shutil.copy2')
    def test_setup_install_boot_images_raises_no_efigrub(
        self, mock_shutil_copy2, mock_exists, mock_Path_which,
        mock_sync, mock_command, mock_grub, mock_shim
    ):
        Defaults.set_platform_name('x86_64')
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        mock_Path_which.return_value = '/path/to/grub2-mkimage'
        mock_shim.return_value = 'shim.efi'
        mock_grub.return_value = None
        self.bootloader.theme = 'some-theme'
        self.os_exists['root_dir/usr/share/grub2/themes/some-theme'] = False
        self.os_exists['root_dir/boot/grub2/themes/some-theme'] = False
        self.os_exists['root_dir/usr/lib/grub2/themes/some-theme'] = True
        self.os_exists['root_dir/usr/lib/grub/themes/some-theme'] = False
        self.os_exists['root_dir/boot/grub2/themes/some-theme'] = False
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['/usr/share/grub2/i386-pc'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect
        with patch('builtins.open'):
            with raises(KiwiBootLoaderGrubSecureBootError):
                self.bootloader.setup_install_boot_images(self.mbrid)

    def test_post_init_ix86_platform(self):
        Defaults.set_platform_name('i686')
        xml_state = MagicMock()
        xml_state.get_initrd_system = Mock(
            return_value='dracut'
        )
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.arch == 'ix86'

    def test_post_init_ppc_platform(self):
        Defaults.set_platform_name('ppc64')
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.arch == 'ppc64'

    def test_post_init_s390_platform(self):
        Defaults.set_platform_name('s390x')
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.arch == 's390x'

    def test_post_init_arm64_platform(self):
        Defaults.set_platform_name('arm64')
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.arch == 'arm64'

    def test_post_init_riscv64_platform(self):
        Defaults.set_platform_name('riscv64')
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.arch == 'riscv64'

    def test_post_init_loongarch64_platform(self):
        Defaults.set_platform_name('loongarch64')
        xml_state = MagicMock()
        xml_state.build_type.get_firmware = Mock(
            return_value=None
        )
        bootloader = BootLoaderConfigGrub2(xml_state, 'root_dir')
        assert bootloader.arch == 'loongarch64'

    @patch('os.path.exists')
    def test_post_init_dom0(self, mock_exists):
        Defaults.set_platform_name('x86_64')
        self.state.is_xen_server = Mock(
            return_value=True
        )
        self.state.is_xen_guest = Mock(
            return_value=False
        )
        mock_exists.return_value = True
        self.bootloader.post_init(None)
        assert self.bootloader.multiboot is True
        assert self.bootloader.xen_guest is False

    @patch('os.path.exists')
    def test_post_init_domU(self, mock_exists):
        Defaults.set_platform_name('x86_64')
        self.state.is_xen_server = Mock(
            return_value=False
        )
        self.state.is_xen_guest = Mock(
            return_value=True
        )
        mock_exists.return_value = True
        self.bootloader.post_init(None)
        assert self.bootloader.multiboot is False
        assert self.bootloader.xen_guest is True

    @patch.object(BootLoaderConfigGrub2, '_setup_default_grub')
    @patch.object(BootLoaderConfigGrub2, '_setup_sysconfig_bootloader')
    def test_write_meta_data(
        self, mock_setup_sysconfig_bootloader,
        mock_setup_default_grub
    ):
        self.bootloader.write_meta_data()
        mock_setup_default_grub.assert_called_once_with()
        mock_setup_sysconfig_bootloader.assert_called_once_with()

    @patch.object(BootLoaderConfigGrub2, '_setup_default_grub')
    @patch.object(BootLoaderConfigGrub2, '_setup_sysconfig_bootloader')
    @patch.object(BootLoaderConfigGrub2, '_setup_zipl2grub_conf')
    def test_write_meta_data_s390(
        self, mock_setup_zipl2grub_conf, mock_setup_sysconfig_bootloader,
        mock_setup_default_grub
    ):
        self.bootloader.arch = 's390x'
        self.bootloader.write_meta_data()
        mock_setup_default_grub.assert_called_once_with()
        mock_setup_sysconfig_bootloader.assert_called_once_with()
        mock_setup_zipl2grub_conf.assert_called_once_with()

    @patch('os.path.exists')
    @patch.object(BootLoaderConfigGrub2, '_copy_grub_config_to_efi_path')
    def test_write(
        self, mock_copy_grub_config_to_efi_path, mock_exists
    ):
        mock_exists.return_value = True
        self.bootloader.config = 'some-data'
        self.bootloader.iso_boot = True
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader.write()

            mock_open.assert_called_once_with(
                'root_dir/boot/grub2/grub.cfg', 'w'
            )
            file_handle.write.assert_called_once_with(
                'some-data'
            )

    @patch('kiwi.bootloader.config.grub2.DataSync.sync_data')
    @patch('kiwi.bootloader.config.grub2.Temporary.new_dir')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_create_embedded_fat_efi_image(self, mock_command, mock_tmpdir, mock_syncdata):
        tmpdir = Mock()
        tmpdir.name = 'tmpdir'
        mock_tmpdir.return_value = tmpdir
        self.bootloader._create_embedded_fat_efi_image('tmp-esp-image')
        assert mock_command.call_args_list == [
            call(
                [
                    'qemu-img', 'create', 'tmp-esp-image', '20M'
                ]
            ),
            call(
                [
                    'mkdosfs', '-n', 'BOOT', 'tmp-esp-image'
                ]
            ),
            call(
                [
                    'mcopy', '-Do', '-s', '-i', 'tmp-esp-image',
                    'tmpdir', '::EFI'
                ]
            )
        ]

    @patch('glob.iglob')
    @patch('shutil.copy2')
    @patch('kiwi.bootloader.config.grub2.Path.create')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.Defaults.get_signed_grub_loader')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    def test_copy_grub_config_to_efi_path(
        self, mock_Path_which, mock_get_signed_grub_loader, mock_Command,
        mock_Path_create, mock_shutil_copy2, mock_glob
    ):
        mock_get_signed_grub_loader.return_value = []
        mock_glob.return_value = []

        # 1. run: check strings command not found
        mock_Path_which.return_value = ''
        mock_get_signed_grub_loader.return_value = [
            grub_loader_type(
                'some-loader', 'some-binaryname', 'some-targetname'
            )
        ]
        with raises(KiwiCommandNotFound):
            self.bootloader._copy_grub_config_to_efi_path(
                'root_dir', 'config_file'
            )

        # 2. run: check default handling
        mock_get_signed_grub_loader.return_value = []
        mock_Path_which.return_value = '/usr/bin/strings'
        self.bootloader._copy_grub_config_to_efi_path(
            'root_dir', 'config_file'
        )

        mock_Path_create.assert_called_once_with(
            'root_dir/EFI/BOOT'
        )
        mock_shutil_copy2.assert_called_once_with(
            'config_file', 'root_dir/EFI/BOOT/grub.cfg'
        )

        # 3. run: check with vendor directory read from grub binary
        mock_shutil_copy2.reset_mock()
        mock_Path_create.reset_mock()
        strings_out = Mock()
        strings_out.output = '/EFI/ubuntu'
        mock_get_signed_grub_loader.return_value = [
            grub_loader_type(
                'some-loader', 'some-binaryname', 'some-targetname'
            )
        ]
        mock_Command.return_value = strings_out
        self.bootloader._copy_grub_config_to_efi_path(
            'root_dir', 'config_file'
        )

        mock_Path_create.assert_called_once_with(
            'root_dir/EFI/ubuntu'
        )
        mock_shutil_copy2.assert_called_once_with(
            'config_file', 'root_dir/EFI/ubuntu/grub.cfg'
        )

        # 4. run: check with installed vendor directory
        mock_shutil_copy2.reset_mock()
        mock_Path_create.reset_mock()
        mock_get_signed_grub_loader.return_value = []
        mock_glob.return_value = ['root_dir/EFI/fedora/shim.efi']
        self.bootloader._copy_grub_config_to_efi_path(
            'root_dir', 'config_file'
        )

        mock_Path_create.assert_called_once_with(
            'root_dir/EFI/fedora'
        )
        mock_shutil_copy2.assert_called_once_with(
            'config_file', 'root_dir/EFI/fedora/grub.cfg'
        )

    @patch('shutil.copy2')
    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_zipl2grub_conf_512_byte_target(
        self, mock_Command_run, mock_exists, mock_shutil_copy2
    ):
        path_return_values = [True, False]

        def path_exists(arg):
            return path_return_values.pop(0)

        command = Mock()
        command.output = '  2048'
        self.bootloader.target_table_type = 'msdos'
        mock_Command_run.return_value = command
        mock_exists.side_effect = path_exists
        xml_state = MagicMock()
        xml_state.get_build_type_bootloader_targettype = Mock(
            return_value='FBA'
        )
        xml_state.build_type.get_target_blocksize = Mock(
            return_value=None
        )
        self.bootloader.xml_state = xml_state
        with open('../data/etc/default/zipl2grub.conf.in') as zipl_grub:
            zipl_config = zipl_grub.read()
        with patch('builtins.open', create=True) as mock_open:
            file_handle = mock_open.return_value.__enter__.return_value
            file_handle.read.return_value = zipl_config
            self.bootloader._setup_zipl2grub_conf()
            assert \
                '    targettype = FBA\n' \
                '    targetbase = rootdev\n' \
                '    targetblocksize = 512\n' \
                '    targetoffset = 2048' \
                in file_handle.write.call_args[0][0]
        mock_shutil_copy2.assert_called_once_with(
            'root_dir/etc/default/zipl2grub.conf.in',
            'root_dir/etc/default/zipl2grub.conf.in.orig'
        )
        path_return_values = [True, True]
        mock_shutil_copy2.reset_mock()
        with patch('builtins.open', create=True) as mock_open:
            file_handle = mock_open.return_value.__enter__.return_value
            file_handle.read.return_value = zipl_config
            self.bootloader._setup_zipl2grub_conf()
        mock_shutil_copy2.assert_called_once_with(
            'root_dir/etc/default/zipl2grub.conf.in.orig',
            'root_dir/etc/default/zipl2grub.conf.in'
        )

    @patch('shutil.copy2')
    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_zipl2grub_conf_4096_byte_target(
        self, mock_Command_run, mock_exists, mock_shutil_copy2
    ):
        path_return_values = [True, False]
        command_return_values = [
            self.command_type(
                output='  blocks per track .....: 12\n'
            ),
            self.command_type(
                output=' /dev/loop01 2 6401 6400 1 Linux native\n'
            ),
            self.command_type(
                output='  cylinders ............: 10017\n'
            ),
            self.command_type(
                output='  tracks per cylinder ..: 15\n'
            ),
            self.command_type(
                output='  blocks per track .....: 12\n'
            )
        ]

        def path_exists(arg):
            return path_return_values.pop(0)

        def command_run(arg):
            return command_return_values.pop(0)

        self.bootloader.target_table_type = 'dasd'
        mock_Command_run.side_effect = command_run
        mock_exists.side_effect = path_exists
        xml_state = MagicMock()
        xml_state.get_build_type_bootloader_targettype = Mock(
            return_value='CDL'
        )
        xml_state.build_type.get_target_blocksize = Mock(
            return_value=4096
        )
        self.bootloader.xml_state = xml_state
        with open('../data/etc/default/zipl2grub.conf.in') as zipl_grub:
            zipl_config = zipl_grub.read()
        with patch('builtins.open', create=True) as mock_open:
            file_handle = mock_open.return_value.__enter__.return_value
            file_handle.read.return_value = zipl_config
            self.bootloader._setup_zipl2grub_conf()
            assert \
                '    targettype = CDL\n' \
                '    targetbase = rootdev\n' \
                '    targetblocksize = 4096\n' \
                '    targetoffset = 24\n' \
                '    targetgeometry = 10017,15,12' \
                in file_handle.write.call_args[0][0]

        assert mock_Command_run.call_args_list == [
            call(
                [
                    'bash', '-c',
                    'fdasd -f -p rootdev | grep "blocks per track"'
                ]
            ),
            call(
                [
                    'bash', '-c',
                    'fdasd -f -s -p rootdev | grep "^ " | '
                    'head -n 1 | tr -s " "'
                ]
            ),
            call(
                [
                    'bash', '-c', 'fdasd -f -p rootdev | grep "cylinders"'
                ]
            ),
            call(
                [
                    'bash', '-c',
                    'fdasd -f -p rootdev | grep "tracks per cylinder"'
                ]
            ),
            call(
                [
                    'bash', '-c',
                    'fdasd -f -p rootdev | grep "blocks per track"'
                ]
            )
        ]

    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch.object(BootLoaderConfigGrub2, '_get_dasd_disk_geometry_element')
    def test_get_partition_start_raises(
        self, mock_get_dasd_disk_geometry_element, mock_Command_run
    ):
        self.bootloader.target_table_type = 'dasd'
        mock_Command_run.return_value = self.command_type(
            output='bogus data'
        )
        with raises(KiwiDiskGeometryError):
            self.bootloader._get_partition_start('/dev/disk')

    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_get_dasd_disk_geometry_element_raises(
        self, mock_Command_run
    ):
        self.bootloader.target_table_type = 'dasd'
        mock_Command_run.return_value = self.command_type(
            output='bogus data'
        )
        with raises(KiwiDiskGeometryError):
            self.bootloader._get_dasd_disk_geometry_element(
                '/dev/disk', 'tracks per cylinder'
            )

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_default_grub_s390_secure_execution(
        self, mock_Command_run, mock_sysconfig, mock_exists
    ):
        grep_grub_option = Mock()
        grep_grub_option.returncode = 0
        mock_Command_run.return_value = grep_grub_option
        mock_exists.return_value = False
        grub_default = SysConfig('some-file')
        grub_default.write = Mock()
        mock_sysconfig.return_value = grub_default
        mock_exists.return_value = True
        self.bootloader.bls = True
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'gfxterm'
        self.bootloader.gfxmode = '800x600'
        self.bootloader.theme = 'openSUSE'
        self.bootloader.displayname = 'Bob'
        self.bootloader.arch = 's390x'
        self.bootloader.host_key_certificates = [
            {
                'hkd_cert': ['/path/to/host.crt'],
                'hkd_revocation_list': ['some1.crl', 'some2.crl'],
                'hkd_ca_cert': '/path/to/DigiCertCA.crt',
                'hkd_sign_cert': '/path/to/ibm-z-host-key-signing.crt'
            }
        ]
        self.firmware.efi_mode.return_value = 'efi'

        self.bootloader._setup_default_grub()

        mock_sysconfig.assert_called_once_with('root_dir/etc/default/grub')
        grub_default.write.assert_called_once_with()

        assert grub_default.data_dict == {
            'GRUB_BACKGROUND': '/boot/grub2/themes/openSUSE/background.png',
            'GRUB_CMDLINE_LINUX_DEFAULT': '"some-cmdline"',
            'GRUB_DISTRIBUTOR': '"Bob"',
            'GRUB_ENABLE_BLSCFG': 'true',
            'GRUB_ENABLE_CRYPTODISK': 'y',
            'GRUB_GFXMODE': '800x600',
            'GRUB_SERIAL_COMMAND': '"serial --speed=38400"',
            'GRUB_TERMINAL_INPUT': '"serial"',
            'GRUB_TERMINAL_OUTPUT': '"gfxterm"',
            'GRUB_THEME': '/boot/grub2/themes/openSUSE/theme.txt',
            'GRUB_TIMEOUT': 10,
            'GRUB_TIMEOUT_STYLE': 'countdown',
            'SUSE_BTRFS_SNAPSHOT_BOOTING': 'true',
            'SUSE_S390_SE_CA_CERT': '/path/to/DigiCertCA.crt',
            'SUSE_S390_SE_ENABLE': 'true',
            'SUSE_S390_SE_HOST_KEY': '/path/to/host.crt',
            'SUSE_S390_SE_HOST_KEY_SIGNING_KEY':
                '/path/to/ibm-z-host-key-signing.crt',
            'SUSE_S390_SE_REVOCATION_LIST': 'some1.crl,some2.crl',
            'GRUB_DEFAULT': 'saved'
        }

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_default_grub(
        self, mock_Command_run, mock_sysconfig, mock_exists
    ):
        grep_grub_option = Mock()
        grep_grub_option.returncode = 0
        mock_Command_run.return_value = grep_grub_option
        mock_exists.return_value = False
        grub_default = SysConfig('some-file')
        grub_default.write = Mock()
        mock_sysconfig.return_value = grub_default
        mock_exists.return_value = True
        self.bootloader.bls = True
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'gfxterm'
        self.bootloader.gfxmode = '800x600'
        self.bootloader.theme = 'openSUSE'
        self.bootloader.displayname = 'Bob'
        self.firmware.efi_mode.return_value = 'efi'

        self.bootloader._setup_default_grub()

        mock_sysconfig.assert_called_once_with('root_dir/etc/default/grub')
        grub_default.write.assert_called_once_with()

        assert grub_default.data_dict == {
            'GRUB_BACKGROUND': '/boot/grub2/themes/openSUSE/background.png',
            'GRUB_CMDLINE_LINUX_DEFAULT': '"some-cmdline"',
            'GRUB_DISTRIBUTOR': '"Bob"',
            'GRUB_ENABLE_BLSCFG': 'true',
            'GRUB_ENABLE_CRYPTODISK': 'y',
            'GRUB_GFXMODE': '800x600',
            'GRUB_SERIAL_COMMAND': '"serial --speed=38400"',
            'GRUB_TERMINAL_INPUT': '"serial"',
            'GRUB_TERMINAL_OUTPUT': '"gfxterm"',
            'GRUB_THEME': '/boot/grub2/themes/openSUSE/theme.txt',
            'GRUB_TIMEOUT': 10,
            'GRUB_TIMEOUT_STYLE': 'countdown',
            'SUSE_BTRFS_SNAPSHOT_BOOTING': 'true',
            'GRUB_DEFAULT': 'saved'
        }

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_default_grub_empty_kernelcmdline(
        self, mock_Command_run, mock_sysconfig, mock_exists
    ):
        grep_grub_option = Mock()
        grep_grub_option.returncode = 0
        mock_Command_run.return_value = grep_grub_option
        grub_default = MagicMock()
        mock_sysconfig.return_value = grub_default
        mock_exists.return_value = True
        self.bootloader.bls = True
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'serial'
        self.bootloader.gfxmode = '800x600'
        self.bootloader.theme = 'openSUSE'
        self.bootloader.displayname = 'Bob'
        self.bootloader.cmdline = 'root=LABEL=some-label'
        self.bootloader.persistency_type = 'by-label'

        self.bootloader._setup_default_grub()

        # Must not contain GRUB_CMDLINE_LINUX_DEFAULT
        assert grub_default.__setitem__.call_args_list == [
            call(
                'GRUB_BACKGROUND',
                '/boot/grub2/themes/openSUSE/background.png'
            ),
            call('GRUB_CMDLINE_LINUX', '"root=LABEL=some-label"'),
            call('GRUB_DISABLE_LINUX_UUID', 'true'),
            call('GRUB_DISTRIBUTOR', '"Bob"'),
            call('GRUB_ENABLE_BLSCFG', 'true'),
            call('GRUB_ENABLE_CRYPTODISK', 'y'),
            call('GRUB_ENABLE_LINUX_LABEL', 'true'),
            call('GRUB_GFXMODE', '800x600'),
            call(
                'GRUB_SERIAL_COMMAND', '"serial --speed=38400"'
            ),
            call('GRUB_TERMINAL_INPUT', '"serial"'),
            call('GRUB_TERMINAL_OUTPUT', '"serial"'),
            call('GRUB_THEME', '/boot/grub2/themes/openSUSE/theme.txt'),
            call('GRUB_TIMEOUT', 10),
            call('GRUB_TIMEOUT_STYLE', 'countdown'),
            call('SUSE_BTRFS_SNAPSHOT_BOOTING', 'true'),
            call('SUSE_REMOVE_LINUX_ROOT_PARAM', 'true')
        ]

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_default_grub_use_of_by_partuuid(
        self, mock_Command_run, mock_sysconfig, mock_exists
    ):
        grep_grub_option = Mock()
        grep_grub_option.returncode = 0
        mock_Command_run.return_value = grep_grub_option
        grub_default = MagicMock()
        mock_sysconfig.return_value = grub_default
        mock_exists.return_value = True
        self.bootloader.bls = True
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'serial'
        self.bootloader.gfxmode = '800x600'
        self.bootloader.theme = 'openSUSE'
        self.bootloader.displayname = 'Bob'
        self.bootloader.cmdline = 'root=UUID=foo'
        self.bootloader.persistency_type = 'by-partuuid'

        self.bootloader._setup_default_grub()

        assert grub_default.__setitem__.call_args_list == [
            call(
                'GRUB_BACKGROUND',
                '/boot/grub2/themes/openSUSE/background.png'
            ),
            call('GRUB_DISABLE_LINUX_PARTUUID', 'false'),
            call('GRUB_DISABLE_LINUX_UUID', 'true'),
            call('GRUB_DISTRIBUTOR', '"Bob"'),
            call('GRUB_ENABLE_BLSCFG', 'true'),
            call('GRUB_ENABLE_CRYPTODISK', 'y'),
            call('GRUB_GFXMODE', '800x600'),
            call(
                'GRUB_SERIAL_COMMAND', '"serial --speed=38400"'
            ),
            call('GRUB_TERMINAL_INPUT', '"serial"'),
            call('GRUB_TERMINAL_OUTPUT', '"serial"'),
            call('GRUB_THEME', '/boot/grub2/themes/openSUSE/theme.txt'),
            call('GRUB_TIMEOUT', 10),
            call('GRUB_TIMEOUT_STYLE', 'countdown'),
            call('SUSE_BTRFS_SNAPSHOT_BOOTING', 'true')
        ]

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_default_grub_use_of_by_label(
        self, mock_Command_run, mock_sysconfig, mock_exists
    ):
        grep_grub_option = Mock()
        grep_grub_option.returncode = 0
        mock_Command_run.return_value = grep_grub_option
        grub_default = MagicMock()
        mock_sysconfig.return_value = grub_default
        mock_exists.return_value = True
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'serial'
        self.bootloader.gfxmode = '800x600'
        self.bootloader.theme = 'openSUSE'
        self.bootloader.displayname = 'Bob'
        self.bootloader.cmdline = 'abcd root=LABEL=foo console=tty0'
        self.bootloader.persistency_type = 'by-label'
        self.bootloader.bls = True

        self.bootloader._setup_default_grub()

        assert grub_default.__setitem__.call_args_list == [
            call(
                'GRUB_BACKGROUND',
                '/boot/grub2/themes/openSUSE/background.png'
            ),
            call('GRUB_CMDLINE_LINUX', '"root=LABEL=foo"'),
            call('GRUB_CMDLINE_LINUX_DEFAULT', '"abcd console=tty0"'),
            call('GRUB_DISABLE_LINUX_UUID', 'true'),
            call('GRUB_DISTRIBUTOR', '"Bob"'),
            call('GRUB_ENABLE_BLSCFG', 'true'),
            call('GRUB_ENABLE_CRYPTODISK', 'y'),
            call('GRUB_ENABLE_LINUX_LABEL', 'true'),
            call('GRUB_GFXMODE', '800x600'),
            call(
                'GRUB_SERIAL_COMMAND', '"serial --speed=38400"'
            ),
            call('GRUB_TERMINAL_INPUT', '"serial"'),
            call('GRUB_TERMINAL_OUTPUT', '"serial"'),
            call('GRUB_THEME', '/boot/grub2/themes/openSUSE/theme.txt'),
            call('GRUB_TIMEOUT', 10),
            call('GRUB_TIMEOUT_STYLE', 'countdown'),
            call('SUSE_BTRFS_SNAPSHOT_BOOTING', 'true'),
            call('SUSE_REMOVE_LINUX_ROOT_PARAM', 'true'),
        ]

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    def test_setup_default_grub_use_of_bls(
        self, mock_Command_run, mock_sysconfig, mock_exists
    ):
        grep_grub_option = Mock()
        grep_grub_option.returncode = 0
        mock_Command_run.return_value = grep_grub_option
        mock_exists.return_value = False
        grub_default = SysConfig('some-file')
        grub_default.write = Mock()
        mock_sysconfig.return_value = grub_default
        mock_exists.return_value = True
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'gfxterm'
        self.bootloader.gfxmode = '800x600'
        self.bootloader.theme = 'openSUSE'
        self.bootloader.displayname = 'Bob'
        self.bootloader.bls = True
        self.firmware.efi_mode.return_value = 'efi'

        self.bootloader._setup_default_grub()

        mock_sysconfig.assert_called_once_with('root_dir/etc/default/grub')
        grub_default.write.assert_called_once_with()

        assert grub_default.data_dict == {
            'GRUB_BACKGROUND': '/boot/grub2/themes/openSUSE/background.png',
            'GRUB_CMDLINE_LINUX_DEFAULT': '"some-cmdline"',
            'GRUB_DISTRIBUTOR': '"Bob"',
            'GRUB_ENABLE_BLSCFG': 'true',
            'GRUB_ENABLE_CRYPTODISK': 'y',
            'GRUB_GFXMODE': '800x600',
            'GRUB_SERIAL_COMMAND': '"serial --speed=38400"',
            'GRUB_TERMINAL_INPUT': '"serial"',
            'GRUB_TERMINAL_OUTPUT': '"gfxterm"',
            'GRUB_THEME': '/boot/grub2/themes/openSUSE/theme.txt',
            'GRUB_TIMEOUT': 10,
            'GRUB_TIMEOUT_STYLE': 'countdown',
            'SUSE_BTRFS_SNAPSHOT_BOOTING': 'true',
            'GRUB_DEFAULT': 'saved'
        }

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    def test_setup_sysconfig_bootloader(self, mock_sysconfig, mock_exists):
        sysconfig_bootloader = MagicMock()
        mock_sysconfig.return_value = sysconfig_bootloader
        mock_exists.return_value = True
        self.bootloader.bls = False
        self.bootloader._setup_sysconfig_bootloader()
        mock_sysconfig.assert_called_once_with(
            'root_dir/etc/sysconfig/bootloader'
        )
        sysconfig_bootloader.write.assert_called_once_with()
        assert sysconfig_bootloader.__setitem__.call_args_list == [
            call('DEFAULT_APPEND', '"some-cmdline root=UUID=foo"'),
            call(
                'FAILSAFE_APPEND',
                '"some-cmdline root=UUID=foo failsafe-options"'
            ),
            call('LOADER_LOCATION', 'mbr'),
            call('LOADER_TYPE', 'grub2'),
            call('TRUSTED_BOOT', 'yes')
        ]
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        sysconfig_bootloader.__setitem__.reset_mock()
        self.bootloader._setup_sysconfig_bootloader()
        assert sysconfig_bootloader.__setitem__.call_args_list == [
            call('DEFAULT_APPEND', '"some-cmdline root=UUID=foo"'),
            call(
                'FAILSAFE_APPEND',
                '"some-cmdline root=UUID=foo failsafe-options"'
            ),
            call('LOADER_LOCATION', 'none'),
            call('LOADER_TYPE', 'grub2-efi'),
            call('SECURE_BOOT', 'yes'),
            call('TRUSTED_BOOT', 'yes')
        ]

    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.grub2.SysConfig')
    def test_setup_sysconfig_bootloader_no_secure(
        self, mock_sysconfig, mock_exists
    ):
        sysconfig_bootloader = MagicMock()
        mock_sysconfig.return_value = sysconfig_bootloader
        mock_exists.return_value = True
        self.bootloader.bls = False
        self.bootloader._setup_sysconfig_bootloader()
        mock_sysconfig.assert_called_once_with(
            'root_dir/etc/sysconfig/bootloader'
        )
        sysconfig_bootloader.write.assert_called_once_with()
        assert sysconfig_bootloader.__setitem__.call_args_list == [
            call('DEFAULT_APPEND', '"some-cmdline root=UUID=foo"'),
            call(
                'FAILSAFE_APPEND',
                '"some-cmdline root=UUID=foo failsafe-options"'
            ),
            call('LOADER_LOCATION', 'mbr'),
            call('LOADER_TYPE', 'grub2'),
            call('TRUSTED_BOOT', 'yes')
        ]
        self.firmware.efi_mode = Mock(
            return_value='efi'
        )
        sysconfig_bootloader.__setitem__.reset_mock()
        self.bootloader._setup_sysconfig_bootloader()
        assert sysconfig_bootloader.__setitem__.call_args_list == [
            call('DEFAULT_APPEND', '"some-cmdline root=UUID=foo"'),
            call(
                'FAILSAFE_APPEND',
                '"some-cmdline root=UUID=foo failsafe-options"'
            ),
            call('LOADER_LOCATION', 'none'),
            call('LOADER_TYPE', 'grub2-efi'),
            call('SECURE_BOOT', 'no'),
            call('TRUSTED_BOOT', 'yes')
        ]
        sysconfig_bootloader.__setitem__.reset_mock()
        self.bootloader.bls = True
        self.bootloader._setup_sysconfig_bootloader()
        assert sysconfig_bootloader.__setitem__.call_args_list == [
            call('DEFAULT_APPEND', '"some-cmdline root=UUID=foo"'),
            call(
                'FAILSAFE_APPEND',
                '"some-cmdline root=UUID=foo failsafe-options"'
            ),
            call('LOADER_LOCATION', 'none'),
            call('LOADER_TYPE', 'grub2-bls'),
            call('SECURE_BOOT', 'no'),
            call('TRUSTED_BOOT', 'yes')
        ]

    @patch('os.path.exists')
    def test_setup_live_image_config_custom_template(self, mock_exists):
        bootloader = Mock()
        bootloader.get_grub_template.return_value = "example.template"
        self.bootloader.xml_state.build_type.bootloader.append(bootloader)
        mock_exists.return_value = True
        self.bootloader.multiboot = False
        with patch('builtins.open') as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            file_handle.read.return_value = "example template contents"
            self.bootloader.setup_live_image_config(self.mbrid)
        assert self.bootloader.config == "example template contents"

    @patch('os.path.exists')
    def test_setup_live_image_config_custom_template_file_does_not_exist(self, mock_exists):
        bootloader = Mock()
        bootloader.get_grub_template.return_value = "example.template"
        self.bootloader.xml_state.build_type.bootloader.append(bootloader)
        mock_exists.return_value = False
        self.bootloader.multiboot = False
        with raises(KiwiFileNotFound):
            self.bootloader.setup_live_image_config(self.mbrid)

    def test_setup_live_image_config_custom_template_not_set(self):
        bootloader = Mock()
        bootloader.get_grub_template.return_value = ""
        self.bootloader.xml_state.build_type.bootloader.append(bootloader)
        self.bootloader.multiboot = False
        self.bootloader.setup_live_image_config(self.mbrid)
        self.grub2.get_iso_template.assert_called_once()

    @patch('os.path.exists')
    def test_setup_install_image_config_custom_template(self, mock_exists):
        bootloader = Mock()
        bootloader.get_grub_template.return_value = "example.template"
        self.bootloader.xml_state.build_type.bootloader.append(bootloader)
        mock_exists.return_value = True
        self.bootloader.multiboot = False
        with patch('builtins.open') as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            file_handle.read.return_value = "example template contents"
            self.bootloader.setup_install_image_config(self.mbrid)
        assert self.bootloader.config == "example template contents"

    @patch('os.path.exists')
    def test_setup_install_image_config_custom_template_file_does_not_exist(self, mock_exists):
        bootloader = Mock()
        bootloader.get_grub_template.return_value = "example.template"
        self.bootloader.xml_state.build_type.bootloader.append(bootloader)
        mock_exists.return_value = False
        self.bootloader.multiboot = False
        with raises(KiwiFileNotFound):
            self.bootloader.setup_install_image_config(self.mbrid)

    def test_setup_install_image_config_custom_template_not_set(self):
        bootloader = Mock()
        bootloader.get_grub_template.return_value = ""
        self.bootloader.xml_state.build_type.bootloader.append(bootloader)
        self.bootloader.multiboot = False
        self.bootloader.setup_install_image_config(self.mbrid)
        self.grub2.get_install_template.assert_called_once()

    def test_setup_live_image_config_multiboot(self):
        self.bootloader.multiboot = True
        self.state.build_type.get_btrfs_set_default_volume = Mock(
            return_value=False
        )
        self.state.build_type.get_btrfs_root_is_snapper_snapshot = Mock(
            return_value=False
        )
        self.bootloader.setup_live_image_config(self.mbrid)
        self.grub2.get_multiboot_iso_template.assert_called_once_with(
            True, True, False, None, False
        )

    @patch.object(BootLoaderConfigGrub2, '_copy_grub_config_to_efi_path')
    def test_setup_live_image_config_standard(
        self, mock_copy_grub_config_to_efi_path
    ):
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'gfxterm'
        self.bootloader.early_boot_script_efi = 'earlyboot.cfg'
        self.bootloader.multiboot = False
        self.bootloader.setup_live_image_config(self.mbrid)
        self.grub2.get_iso_template.assert_called_once_with(
            True, True, True, None, True
        )
        mock_copy_grub_config_to_efi_path.assert_called_once_with(
            'root_dir', 'earlyboot.cfg', 'iso'
        )

    def test_setup_install_image_config_multiboot(self):
        self.bootloader.terminal_input = 'serial'
        self.bootloader.terminal_output = 'gfxterm'
        self.bootloader.multiboot = True
        self.bootloader.setup_install_image_config(self.mbrid)
        self.grub2.get_multiboot_install_template.assert_called_once_with(
            True, True, True, True, True
        )

    @patch.object(BootLoaderConfigGrub2, '_mount_system')
    @patch.object(BootLoaderConfigGrub2, '_copy_grub_config_to_efi_path')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.Path.create')
    @patch('kiwi.defaults.Defaults.get_vendor_grubenv')
    @patch('glob.iglob')
    def test_setup_disk_image_config(
        self, mock_iglob, mock_get_vendor_grubenv,
        mock_Path_create, mock_Path_which,
        mock_Command_run, mock_copy_grub_config_to_efi_path,
        mock_mount_system
    ):
        self.state.get_dracut_config = Mock(
            return_value=DracutT(uefi=False, modules=[], drivers=[])
        )
        mock_iglob.return_value = ['some_entry.conf']
        mock_get_vendor_grubenv.return_value = 'grubenv'
        mock_Path_which.return_value = '/path/to/grub2-mkconfig'
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        self.bootloader.root_filesystem_is_overlay = True
        self.bootloader.root_reference = 'root=overlay:UUID=ID'
        self.bootloader.root_mount = Mock()
        self.bootloader.root_mount.mountpoint = 'root_mount_point'
        self.bootloader.efi_mount = Mock()
        self.bootloader.efi_mount.mountpoint = 'efi_mount_point'
        self.bootloader.early_boot_script_efi = 'earlyboot.cfg'
        with patch('builtins.open', create=True) as mock_open:
            mock_open_grub = MagicMock(spec=io.IOBase)
            mock_open_menu = MagicMock(spec=io.IOBase)

            def open_file(filename, mode=None):
                if filename == 'root_mount_point/boot/grub2/grub.cfg':
                    return mock_open_grub.return_value
                elif filename == 'some_entry.conf':
                    return mock_open_menu.return_value

            mock_open.side_effect = open_file

            file_handle_grub = \
                mock_open_grub.return_value.__enter__.return_value
            file_handle_menu = \
                mock_open_menu.return_value.__enter__.return_value

            file_handle_grub.read.return_value = \
                'root=rootdev nomodeset console=ttyS0 console=tty0\n' \
                'root=PARTUUID=xx'
            file_handle_menu.read.return_value = \
                'options foo\nlinux unexpected/boot/vmlinuz\ninitrd /boot/initrd'
            self.bootloader.xml_state.\
                get_build_type_bootloader_environment_variables = Mock(
                    return_value=['menu_auto_hide=1']
                )
            self.bootloader.setup_disk_image_config(
                boot_options={
                    'root_device': 'rootdev', 'boot_device': 'bootdev'
                }
            )
            mock_mount_system.assert_called_once_with(
                None, 'rootdev', 'bootdev', None, None, None, None
            )
            os.environ.update({'GRUB_DISABLE_OS_PROBER': 'true'})
            assert mock_Command_run.call_args_list == [
                call(
                    [
                        'chroot', self.bootloader.root_mount.mountpoint,
                        'grub2-editenv', '-', 'set', 'menu_auto_hide=1'
                    ]
                ),
                call(
                    [
                        'chroot', self.bootloader.root_mount.mountpoint,
                        'grub2-mkconfig', '-o', '/boot/grub2/grub.cfg'
                    ], os.environ
                ),
                call(
                    [
                        'bash', '-c',
                        'cd root_mount_point/boot && rm -f boot && ln -s . boot'
                    ], raise_on_error=False
                ),
                call(
                    [
                        'chroot', self.bootloader.root_mount.mountpoint,
                        'grub2-editenv', '-', 'set', 'root=overlay:UUID=ID'
                    ]
                )
            ]
            mock_copy_grub_config_to_efi_path.assert_called_once_with(
                'efi_mount_point', 'earlyboot.cfg'
            )
            file_handle_grub.write.assert_called_once_with == (
                'root=overlay:UUID=ID nomodeset console=ttyS0 console=tty0'
                '\n'
                'root=overlay:UUID=ID'
            )
            assert 'options some-cmdline root=UUID=foo' in \
                file_handle_menu.write.call_args_list[0][0][0].split(os.linesep)
            assert 'linux /boot/vmlinuz' in \
                file_handle_menu.write.call_args_list[1][0][0].split(os.linesep)
            assert 'initrd /boot/initrd' in \
                file_handle_menu.write.call_args_list[1][0][0].split(os.linesep)

            self.bootloader.bootpartition = True
            file_handle_menu.reset_mock()

            self.bootloader.setup_disk_image_config(
                boot_options={
                    'root_device': 'rootdev', 'boot_device': 'bootdev'
                }
            )

            assert 'linux /vmlinuz' in \
                file_handle_menu.write.call_args_list[1][0][0].split(os.linesep)
            assert 'initrd /initrd' in \
                file_handle_menu.write.call_args_list[1][0][0].split(os.linesep)

            # test UKI setup
            self.state.get_dracut_config.return_value = DracutT(
                uefi=True, modules=[], drivers=[]
            )
            boot_image = Mock()
            self.bootloader.setup_disk_image_config(
                boot_options={
                    'root_device': 'rootdev',
                    'boot_device': 'bootdev',
                    'boot_image': boot_image
                }
            )
            boot_image.create_uki.assert_called_once_with(
                'some-cmdline root=UUID=foo'
            )

            # test read-only device
            file_handle_grub.write.side_effect = OSError('readonly system')
            file_handle_menu.write.side_effect = OSError('readonly system')

            with self._caplog.at_level(logging.INFO):
                self.bootloader.setup_disk_image_config(
                    boot_options={
                        'root_device': 'rootdev', 'boot_device': 'bootdev'
                    }
                )
                assert 'readonly system' in self._caplog.text

    @patch.object(BootLoaderConfigGrub2, '_copy_grub_config_to_efi_path')
    def test_setup_install_image_config_standard(
        self, mock_copy_grub_config_to_efi_path
    ):
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        self.bootloader.early_boot_script_efi = 'earlyboot.cfg'
        self.bootloader.multiboot = False
        self.bootloader.setup_install_image_config(self.mbrid)
        self.grub2.get_install_template.assert_called_once_with(
            True, True, False, True, True
        )
        mock_copy_grub_config_to_efi_path.assert_called_once_with(
            'root_dir', 'earlyboot.cfg', 'iso'
        )

    def test_setup_iso_image_config_substitute_error(self):
        self.bootloader.multiboot = True
        template = Mock()
        template.substitute = Mock()
        template.substitute.side_effect = Exception
        self.grub2.get_multiboot_iso_template = Mock(
            return_value=template
        )
        with raises(KiwiTemplateError):
            self.bootloader.setup_live_image_config(self.mbrid)

    def test_setup_install_image_config_substitute_error(self):
        self.bootloader.multiboot = True
        template = Mock()
        template.substitute = Mock()
        template.substitute.side_effect = Exception
        self.grub2.get_multiboot_install_template = Mock(
            return_value=template
        )
        with raises(KiwiTemplateError):
            self.bootloader.setup_install_image_config(self.mbrid)

    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('os.path.exists')
    def test_no_grub_installation_found(
        self, mock_exists, mock_get_boot_path, mock_command
    ):
        mock_get_boot_path.return_value = '/boot'
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = False
        self.os_exists['root_dir/usr/lib/grub2/i386-pc'] = False
        self.os_exists['root_dir/usr/share/grub/i386-pc'] = False
        self.os_exists['root_dir/usr/lib/grub/i386-pc'] = False
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect
        with raises(KiwiBootLoaderGrubDataError):
            self.bootloader.setup_disk_boot_images('0815')

    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('os.path.exists')
    @patch('kiwi.defaults.Defaults.get_grub_path')
    def test_setup_disk_boot_images_raises_font_does_not_exist(
        self, mock_get_grub_path, mock_exists, mock_get_boot_path, mock_command
    ):
        mock_get_boot_path.return_value = '/boot'
        self.os_exists['root_dir/boot/grub2/fonts/unicode.pf2'] = False
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = False
        self.os_exists['root_dir/usr/share/grub/unicode.pf2'] = False
        self.os_exists['root_dir/usr/lib/grub2/unicode.pf2'] = False
        self.os_exists['root_dir/usr/lib/grub/unicode.pf2'] = False

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect
        mock_command.side_effect = Exception
        with raises(KiwiBootLoaderGrubFontError):
            self.bootloader.setup_disk_boot_images('0815')

    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('os.path.exists')
    @patch.object(BootLoaderConfigGrub2, '_copy_theme_data_to_boot_directory')
    def test_setup_disk_boot_images_raises_grub_modules_does_not_exist(
        self, mock_copy_theme_data, mock_exists, mock_command
    ):
        Defaults.set_platform_name('x86_64')
        mock_exists.return_value = True
        self.firmware.efi_mode = Mock(
            return_value=False
        )
        mock_command.side_effect = Exception
        with raises(KiwiBootLoaderGrubModulesError):
            self.bootloader.setup_disk_boot_images('0815')

    @patch('kiwi.bootloader.config.grub2.Defaults.get_unsigned_grub_loader')
    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    def test_setup_disk_boot_images_xen_guest_efi_image_needs_multiboot(
        self, mock_exists, mock_sync, mock_Path_which,
        mock_command, mock_get_boot_path, mock_get_unsigned_grub_loader
    ):
        Defaults.set_platform_name('x86_64')
        mock_Path_which.return_value = '/path/to/grub2-mkimage'
        mock_get_boot_path.return_value = '/boot'
        mock_get_unsigned_grub_loader.return_value = []
        self.firmware.efi_mode = Mock(
            return_value='efi'
        )
        self.bootloader.xen_guest = True
        self.os_exists['root_dir/boot/grub2/fonts/unicode.pf2'] = False
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/i386-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-xen'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi/linuxefi.mod'] = \
            True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        with patch('builtins.open'):
            self.bootloader.setup_disk_boot_images('0815')

        assert mock_command.call_args_list == [
            call(
                [
                    'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                    'root_dir/boot/grub2/fonts'
                ]
            ),
            call(
                [
                    'chroot', 'root_dir', 'grub2-mkimage',
                    '-O', 'x86_64-efi',
                    '-o', '/boot/efi/EFI/BOOT/bootx64.efi',
                    '-c', '/boot/efi/EFI/BOOT/earlyboot.cfg',
                    '-p', '/boot/grub2',
                    '-d', '/usr/share/grub2/x86_64-efi',
                    'ext2', 'iso9660', 'linux', 'echo', 'configfile',
                    'search_label', 'search_fs_file', 'search',
                    'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat',
                    'gettext', 'font', 'minicmd', 'gfxterm', 'gfxmenu',
                    'all_video', 'xfs', 'btrfs', 'squash4', 'lvm', 'luks',
                    'gcry_rijndael', 'gcry_sha256', 'gcry_sha512', 'crypto',
                    'cryptodisk', 'test', 'true', 'loadenv', 'multiboot',
                    'part_gpt', 'part_msdos', 'efi_gop', 'linuxefi'
                ]
            )
        ]

    @patch('kiwi.bootloader.config.grub2.Defaults.get_unsigned_grub_loader')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    @patch('kiwi.bootloader.config.base.DiskSetup')
    def test_setup_disk_boot_images_bios_plus_efi(
        self,
        mock_DiskSetup,
        mock_exists,
        mock_sync,
        mock_Path_which,
        mock_command,
        mock_get_unsigned_grub_loader
    ):
        mock_DiskSetup.return_value.need_boot_partition.return_value = True
        Defaults.set_platform_name('x86_64')
        mock_Path_which.return_value = '/path/to/grub2-mkimage'
        mock_get_unsigned_grub_loader.return_value = []
        data = Mock()
        mock_sync.return_value = data
        self.firmware.efi_mode = Mock(
            return_value='efi'
        )
        self.os_exists['root_dir/grub2/fonts/unicode.pf2'] = False
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/i386-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi/linuxefi.mod'] = \
            True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader.setup_disk_boot_images('0815')

            mock_open.assert_called_once_with(
                'root_dir/boot/efi/EFI/BOOT/earlyboot.cfg', 'w'
            )
            assert file_handle.write.call_args_list == [
                call('set btrfs_relative_path="yes"\n'),
                call('insmod cryptodisk\n'),
                call('insmod luks\n'),
                call('cryptomount -u 0815\n'),
                call('set root="cryptouuid/0815"\n'),
                call('search --fs-uuid --set=root 0815\n'),
                call('set prefix=($root)/grub2\n'),
                call('source ($root)/grub2/grub.cfg\n')
            ]
        assert mock_command.call_args_list == [
            call(
                [
                    'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                    'root_dir/grub2/fonts'
                ]
            ),
            call(
                [
                    'chroot', 'root_dir', 'grub2-mkimage',
                    '-O', 'x86_64-efi',
                    '-o', '/boot/efi/EFI/BOOT/bootx64.efi',
                    '-c', '/boot/efi/EFI/BOOT/earlyboot.cfg',
                    '-p', '/grub2',
                    '-d', '/usr/share/grub2/x86_64-efi',
                    'ext2', 'iso9660', 'linux', 'echo', 'configfile',
                    'search_label', 'search_fs_file', 'search',
                    'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat',
                    'gettext', 'font', 'minicmd', 'gfxterm', 'gfxmenu',
                    'all_video', 'xfs', 'btrfs', 'squash4', 'lvm', 'luks',
                    'gcry_rijndael', 'gcry_sha256', 'gcry_sha512', 'crypto',
                    'cryptodisk', 'test', 'true', 'loadenv', 'part_gpt',
                    'part_msdos', 'efi_gop', 'linuxefi'
                ]
            )
        ]
        assert mock_sync.call_args_list == [
            call(
                'root_dir/usr/share/grub2/i386-pc/',
                'root_dir/boot/grub2/i386-pc'
            ),
            call(
                'root_dir/usr/share/grub2/x86_64-efi/',
                'root_dir/boot/grub2/x86_64-efi'
            ),
            call(
                'root_dir/usr/share/grub2/i386-efi/',
                'root_dir/boot/grub2/i386-efi'
            )
        ]
        assert data.sync_data.call_args_list == [
            call(exclude=['*.module'], options=['-a']),
            call(exclude=['*.module'], options=['-a']),
            call(exclude=['*.module'], options=['-a'])
        ]

        mock_get_unsigned_grub_loader.return_value = [
            grub_loader_type(
                'custom_grub_image', 'some-binaryname', 'bootx64.efi'
            )
        ]
        mock_command.reset_mock()

        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader.setup_disk_boot_images('0815')

            assert file_handle.write.call_args_list == [
                call('set btrfs_relative_path="yes"\n'),
                call('insmod cryptodisk\n'),
                call('insmod luks\n'),
                call('cryptomount -u 0815\n'),
                call('set root="cryptouuid/0815"\n'),
                call('search --fs-uuid --set=root 0815\n'),
                call('set prefix=($root)/grub2\n'),
                call('source ($root)/grub2/grub.cfg\n')
            ]
            mock_open.assert_called_once_with(
                'root_dir/boot/efi/EFI/BOOT/grub.cfg', 'w'
            )

        assert mock_command.call_args_list == [
            call(
                [
                    'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                    'root_dir/grub2/fonts'
                ]
            ),
            call(
                [
                    'cp', 'custom_grub_image',
                    'root_dir/boot/efi/EFI/BOOT/bootx64.efi'
                ]
            )
        ]

        self.state.get_dracut_config = Mock(
            return_value=DracutT(uefi=True, modules=[], drivers=[])
        )
        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader.setup_disk_boot_images('0815', '0815')

            assert file_handle.write.call_args_list == [
                call('search --no-floppy --set=root --fs-uuid 0815\n'),
                call('chainloader ($root)/EFI/Linux/kiwi.efi\n'),
                call('boot\n')
            ]
            mock_open.assert_called_once_with(
                'root_dir/boot/efi/EFI/BOOT/grub.cfg', 'w'
            )

    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    def test_setup_disk_boot_images_xen_guest(
        self, mock_exists, mock_sync,
        mock_command, mock_get_boot_path
    ):
        Defaults.set_platform_name('x86_64')
        mock_get_boot_path.return_value = '/boot'
        self.firmware.efi_mode = Mock(
            return_value=''
        )
        self.bootloader.xen_guest = True
        self.os_exists['root_dir/boot/grub2/fonts/unicode.pf2'] = False
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-xen'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        self.bootloader.setup_disk_boot_images('0815')

        mock_command.assert_called_once_with(
            [
                'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                'root_dir/boot/grub2/fonts'
            ]
        )
        mock_sync.assert_called_once_with(
            'root_dir/usr/share/grub2/x86_64-xen/',
            'root_dir/boot/grub2/x86_64-xen'
        )

    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    def test_setup_disk_boot_images_ppc(
        self, mock_exists, mock_sync, mock_command, mock_get_boot_path
    ):
        Defaults.set_platform_name('ppc64le')
        mock_get_boot_path.return_value = '/boot'
        self.bootloader.arch = 'ppc64le'
        self.firmware.efi_mode = Mock(
            return_value=''
        )
        self.bootloader.xen_guest = False
        self.os_exists['root_dir/boot/grub2/fonts/unicode.pf2'] = False
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        self.bootloader.setup_disk_boot_images('0815')

        mock_command.assert_called_once_with(
            [
                'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                'root_dir/boot/grub2/fonts'
            ]
        )

    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    def test_setup_disk_boot_images_s390(
        self, mock_exists, mock_sync, mock_command, mock_get_boot_path
    ):
        Defaults.set_platform_name('s390x')
        mock_get_boot_path.return_value = '/boot'
        self.bootloader.arch = 's390x'
        self.firmware.efi_mode = Mock(
            return_value=''
        )
        self.bootloader.xen_guest = False
        self.os_exists['root_dir/boot/grub2/fonts/unicode.pf2'] = False
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        self.bootloader.setup_disk_boot_images('0815')

        mock_command.assert_called_once_with(
            [
                'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                'root_dir/boot/grub2/fonts'
            ]
        )

    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('os.path.exists')
    @patch('os.chmod')
    @patch('os.stat')
    def test_setup_disk_boot_images_bios_plus_efi_secure_boot(
        self, mock_stat, mock_chmod, mock_exists,
        mock_command, mock_get_boot_path
    ):
        Defaults.set_platform_name('x86_64')
        mock_get_boot_path.return_value = '/boot'
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect
        self.bootloader.setup_disk_boot_images('uuid')
        with self._caplog.at_level(logging.INFO):
            assert mock_command.call_args_list == [
                call(
                    [
                        'rsync', '-a', '--exclude', '/*.module',
                        'root_dir/usr/share/grub2/i386-pc/',
                        'root_dir/boot/grub2/i386-pc'
                    ]
                )
            ]

    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('os.path.exists')
    @patch('glob.iglob')
    @patch('os.chmod')
    @patch('os.stat')
    @patch('os.path.isfile')
    def test_setup_disk_boot_images_bios_plus_efi_secure_boot_no_shim_install(
        self, mock_isfile, mock_stat, mock_chmod, mock_glob,
        mock_exists, mock_command, mock_which, mock_get_boot_path
    ):
        # we expect the copy of shim.efi and grub.efi from the fallback
        # code if no shim_install was found for building the disk image
        Defaults.set_platform_name('x86_64')
        mock_get_boot_path.return_value = '/boot'
        mock_which.return_value = None
        mock_isfile.return_value = False
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        def side_effect_glob(arg):
            return self.glob_iglob_shim_fallback.pop()

        mock_glob.side_effect = side_effect_glob
        mock_exists.side_effect = side_effect
        with self._caplog.at_level(logging.WARNING):
            with patch('builtins.open', create=True) as mock_open:
                mock_open.return_value = MagicMock(spec=io.IOBase)
                file_handle = mock_open.return_value.__enter__.return_value
                self.bootloader.setup_disk_boot_images('uuid')

                assert file_handle.write.call_args_list == [
                    call('set btrfs_relative_path="yes"\n'),
                    call('insmod cryptodisk\n'),
                    call('insmod luks\n'),
                    call('cryptomount -u uuid\n'),
                    call('set root="cryptouuid/uuid"\n'),
                    call('search --fs-uuid --set=root uuid\n'),
                    call('set prefix=($root)/boot/grub2\n'),
                    call('source ($root)/boot/grub2/grub.cfg\n')
                ]
                mock_open.assert_called_once_with(
                    'root_dir/boot/efi/EFI/BOOT/grub.cfg', 'w'
                )
                assert mock_command.call_args_list == [
                    call(
                        [
                            'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                            'root_dir/boot/efi/EFI/DIST/fonts'
                        ]
                    ),
                    call(
                        [
                            'rsync', '-a', '--exclude', '/*.module',
                            'root_dir/usr/share/grub2/i386-pc/',
                            'root_dir/boot/grub2/i386-pc'
                        ]
                    ),
                    call(
                        [
                            'cp', 'root_dir/usr/lib64/efi/grub.efi',
                            'root_dir/boot/efi/EFI/BOOT/grub.efi'
                        ]
                    ),
                    call(
                        [
                            'cp', 'root_dir/usr/lib64/efi/shim.efi',
                            'root_dir/boot/efi/EFI/BOOT/bootx64.efi'
                        ]
                    ),
                    call(
                        [
                            'cp', 'root_dir/usr/lib64/efi/MokManager.efi',
                            'root_dir/boot/efi/EFI/BOOT'
                        ]
                    )
                ]

    @patch('kiwi.bootloader.config.grub2.Defaults.get_shim_loader')
    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('os.path.exists')
    @patch('glob.iglob')
    @patch('os.chmod')
    @patch('os.stat')
    @patch('os.path.isfile')
    def test_setup_disk_boot_images_bios_plus_efi_secure_boot_no_shim_at_all(
        self, mock_isfile, mock_stat, mock_chmod, mock_glob,
        mock_exists, mock_command, mock_which, mock_get_boot_path,
        mock_get_shim_loader
    ):
        # we expect the copy of grub.efi from the fallback
        # code if no shim was found at all
        mock_get_shim_loader.return_value = []

        Defaults.set_platform_name('x86_64')
        mock_get_boot_path.return_value = '/boot'
        mock_which.return_value = None
        mock_isfile.return_value = False
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        def side_effect_glob(arg):
            return self.glob_iglob_noshim_fallback.pop()

        mock_glob.side_effect = side_effect_glob
        mock_exists.side_effect = side_effect
        with self._caplog.at_level(logging.WARNING):
            with patch('builtins.open', create=True) as mock_open:
                mock_open.return_value = MagicMock(spec=io.IOBase)
                file_handle = mock_open.return_value.__enter__.return_value
                self.bootloader.setup_disk_boot_images('uuid')

                assert file_handle.write.call_args_list == [
                    call('set btrfs_relative_path="yes"\n'),
                    call('insmod cryptodisk\n'),
                    call('insmod luks\n'),
                    call('cryptomount -u uuid\n'),
                    call('set root="cryptouuid/uuid"\n'),
                    call('search --fs-uuid --set=root uuid\n'),
                    call('set prefix=($root)/boot/grub2\n'),
                    call('source ($root)/boot/grub2/grub.cfg\n')
                ]
                mock_open.assert_called_once_with(
                    'root_dir/boot/efi/EFI/BOOT/grub.cfg', 'w'
                )
                assert mock_command.call_args_list == [
                    call(
                        [
                            'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                            'root_dir/boot/efi/EFI/DIST/fonts'
                        ]
                    ),
                    call(
                        [
                            'rsync', '-a', '--exclude', '/*.module',
                            'root_dir/usr/share/grub2/i386-pc/',
                            'root_dir/boot/grub2/i386-pc'
                        ]
                    ),
                    call(
                        [
                            'cp', 'root_dir/usr/lib64/efi/grub.efi',
                            'root_dir/boot/efi/EFI/BOOT/bootx64.efi'
                        ]
                    )
                ]

    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Defaults.get_unsigned_grub_loader')
    @patch('kiwi.bootloader.config.grub2.Defaults.get_grub_platform_core_loader')
    @patch('kiwi.bootloader.config.grub2.Defaults.get_grub_chrp_loader')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.Path.create')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    @patch('shutil.copy2')
    def test_setup_install_boot_images_ppc(
        self, mock_shutil_copy2, mock_exists, mock_sync,
        mock_Path_create, mock_Path_which, mock_command,
        mock_get_grub_chrp_loader, mock_get_grub_platform_core_loader,
        mock_get_unsigned_grub_loader, mock_get_boot_path
    ):
        Defaults.set_platform_name('ppc64le')
        mock_get_grub_chrp_loader.return_value = 'grub.elf'
        mock_get_grub_platform_core_loader.return_value = None
        mock_Path_which.return_value = '/path/to/grub2-mkimage'
        mock_get_boot_path.return_value = '/boot'
        self.bootloader.arch = 'ppc64le'
        data = Mock()
        mock_sync.return_value = data
        self.firmware.efi_mode = Mock(
            return_value=''
        )

        # simulate alternative boot directory to reach all code paths
        self.bootloader.boot_dir = 'boot_dir'

        self.os_exists['lookup_dir/usr/share/grub2/powerpc-ieee1275'] = True
        self.os_exists['lookup_dir/usr/share/grub2/unicode.pf2'] = True
        self.os_exists['boot_dir/boot/grub2/fonts/unicode.pf2'] = False
        self.os_exists['boot_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        with open('../data/bootinfo.txt') as chrp:
            grub2_test_chrp_boot = chrp.read()
        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader.setup_install_boot_images(
                mbrid=self.mbrid, lookup_path="lookup_dir"
            )

            assert mock_open.call_args_list == [
                call('boot_dir/boot/grub2/earlyboot.cfg', 'w'),
                call('boot_dir/boot/grub2/powerpc-ieee1275/grub.cfg', 'w'),
                call('boot_dir/ppc/bootinfo.txt', 'w'),
                call('boot_dir/boot/grub2/loopback.cfg', 'w')
            ]

            assert file_handle.write.call_args_list == [
                call('set btrfs_relative_path="yes"\n'),
                call('search --file --set=root /boot/0xffffffff\n'),
                call('set prefix=($root)/boot/grub2\n'),
                call('source ($root)/boot/grub2/grub.cfg\n'),
                call('set btrfs_relative_path="yes"\n'),
                call('search --file --set=root /boot/0xffffffff\n'),
                call('set prefix=($root)/boot/grub2\n'),
                call('source ($root)/boot/grub2/grub.cfg\n'),
                call(grub2_test_chrp_boot),
                call('source /boot/grub2/grub.cfg\n')
            ]

        assert mock_command.call_args_list == [
            call(
                [
                    'cp', 'lookup_dir/usr/share/grub2/unicode.pf2',
                    'boot_dir/boot/grub2/fonts'
                ]
            ),
            call(
                [
                    'chroot', 'root_dir', 'grub2-mkimage',
                    '-O', 'powerpc-ieee1275',
                    '-o', '/usr/share/grub2/powerpc-ieee1275/grub.elf',
                    '-c', '/boot/grub2/earlyboot.cfg',
                    '-p', '/boot/grub2',
                    '-d', '/usr/share/grub2/powerpc-ieee1275',
                    'ext2', 'iso9660', 'linux', 'echo', 'configfile',
                    'search_label', 'search_fs_file', 'search',
                    'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat',
                    'gettext', 'font', 'minicmd', 'gfxterm', 'gfxmenu',
                    'all_video', 'xfs', 'btrfs', 'squash4', 'lvm', 'luks',
                    'gcry_rijndael', 'gcry_sha256', 'gcry_sha512',
                    'crypto', 'cryptodisk', 'test', 'true', 'loadenv',
                    'part_gpt', 'part_msdos', 'boot'
                ]
            )
        ]

    @patch('kiwi.bootloader.config.base.BootLoaderConfigBase.get_boot_path')
    @patch('kiwi.bootloader.config.grub2.Defaults.get_unsigned_grub_loader')
    @patch('kiwi.bootloader.config.grub2.Defaults.get_grub_platform_core_loader')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.Path.create')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    @patch('shutil.copy2')
    def test_setup_install_boot_images_efi(
        self, mock_shutil_copy2, mock_exists, mock_sync,
        mock_Path_create, mock_Path_which, mock_command,
        mock_get_grub_platform_core_loader, mock_get_unsigned_grub_loader,
        mock_get_boot_path
    ):
        Defaults.set_platform_name('x86_64')
        mock_Path_which.return_value = '/path/to/grub2-mkimage'
        mock_get_boot_path.return_value = '/boot'
        mock_get_unsigned_grub_loader.return_value = []
        mock_get_grub_platform_core_loader.return_value = None
        data = Mock()
        mock_sync.return_value = data
        self.firmware.efi_mode = Mock(
            return_value='efi'
        )

        # simulate alternative boot directory to reach all code paths
        self.bootloader.boot_dir = 'boot_dir'

        self.os_exists['boot_dir/boot/grub2/fonts/unicode.pf2'] = False
        self.os_exists['boot_dir/usr/share/grub2/unicode.pf2'] = True
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['boot_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['boot_dir/usr/share/grub2/i386-efi'] = True
        self.os_exists['/usr/share/grub2/i386-pc'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi/linuxefi.mod'] = \
            True
        self.os_exists['root_dir/boot/efi/'] = False
        self.os_exists['boot_dir/boot/efi/'] = False

        self.os_exists['lookup_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['lookup_dir/usr/share/grub2/i386-efi'] = True
        self.os_exists['lookup_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['lookup_dir/usr/share/grub2/unicode.pf2'] = True
        self.os_exists['lookup_dir/boot/efi/'] = False

        self.os_exists['boot_dir/usr/share/grub2/x86_64-efi'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader.setup_install_boot_images(
                mbrid=self.mbrid, lookup_path="lookup_dir"
            )

            assert mock_open.call_args_list == [
                call('boot_dir/boot/grub2/earlyboot.cfg', 'w'),
                call('boot_dir/EFI/BOOT/earlyboot.cfg', 'w'),
                call('boot_dir/boot/grub2/loopback.cfg', 'w')
            ]
            assert file_handle.write.call_args_list == [
                call('set btrfs_relative_path="yes"\n'),
                call('search --file --set=root /boot/0xffffffff\n'),
                call('set prefix=($root)/boot/grub2\n'),
                call('source ($root)/boot/grub2/grub.cfg\n'),
                call('set btrfs_relative_path="yes"\n'),
                call('search --file --set=root /boot/0xffffffff\n'),
                call('set prefix=($root)/boot/grub2\n'),
                call('source ($root)/boot/grub2/grub.cfg\n'),
                call('source /boot/grub2/grub.cfg\n')
            ]
        assert mock_Path_create.call_args_list == [
            call('boot_dir/boot/grub2'),
            call('boot_dir/boot/grub2/fonts'),
            call('boot_dir/boot/grub2/themes'),
            call('root_dir/boot/grub2'),
            call('lookup_dir/usr/share/grub2/i386-pc'),
            call('root_dir/boot/efi/EFI/BOOT/'),
            call('boot_dir/EFI/BOOT')
        ]
        assert mock_command.call_args_list == [
            call(
                [
                    'cp', 'lookup_dir/usr/share/grub2/unicode.pf2',
                    'boot_dir/boot/grub2/fonts'
                ]
            ),
            call(
                [
                    'chroot', 'root_dir', 'grub2-mkimage',
                    '-O', 'i386-pc',
                    '-o', '/usr/share/grub2/i386-pc/core.img',
                    '-c', '/boot/grub2/earlyboot.cfg',
                    '-p', '/boot/grub2',
                    '-d', '/usr/share/grub2/i386-pc',
                    'ext2', 'iso9660', 'linux', 'echo', 'configfile',
                    'search_label', 'search_fs_file', 'search',
                    'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat',
                    'gettext', 'font', 'minicmd', 'gfxterm', 'gfxmenu',
                    'all_video', 'xfs', 'btrfs', 'squash4', 'lvm', 'luks',
                    'gcry_rijndael', 'gcry_sha256', 'gcry_sha512',
                    'crypto', 'cryptodisk', 'test', 'true', 'loadenv',
                    'part_gpt', 'part_msdos', 'biosdisk', 'vga', 'vbe',
                    'chain', 'boot'
                ]
            ),
            call(
                [
                    'bash', '-c', 'cat lookup_dir/usr/share/grub2/i386-pc/'
                    'cdboot.img lookup_dir/usr/share/grub2/i386-pc/core.img > '
                    'lookup_dir/usr/share/grub2/i386-pc/eltorito.img'
                ]
            ),
            call(
                [
                    'chroot', 'root_dir', 'grub2-mkimage',
                    '-O', 'x86_64-efi',
                    '-o', '/boot/efi/EFI/BOOT/bootx64.efi',
                    '-c', '/boot/efi/EFI/BOOT/earlyboot.cfg',
                    '-p', '/boot/grub2',
                    '-d', '/usr/share/grub2/x86_64-efi',
                    'ext2', 'iso9660', 'linux', 'echo', 'configfile',
                    'search_label', 'search_fs_file', 'search',
                    'search_fs_uuid', 'ls', 'normal', 'gzio', 'png', 'fat',
                    'gettext', 'font', 'minicmd', 'gfxterm', 'gfxmenu',
                    'all_video', 'xfs', 'btrfs', 'squash4', 'lvm', 'luks',
                    'gcry_rijndael', 'gcry_sha256', 'gcry_sha512',
                    'crypto', 'cryptodisk', 'test', 'true', 'loadenv',
                    'part_gpt', 'part_msdos', 'efi_gop', 'linuxefi'
                ]
            )
        ]
        assert mock_sync.call_args_list == [
            call(
                'lookup_dir/usr/share/grub2/i386-pc/',
                'boot_dir/boot/grub2/i386-pc'
            ),
            call(
                'lookup_dir/usr/share/grub2/x86_64-efi/',
                'boot_dir/boot/grub2/x86_64-efi'
            ),
            call(
                'lookup_dir/usr/share/grub2/i386-efi/',
                'boot_dir/boot/grub2/i386-efi'
            )
        ]
        assert data.sync_data.call_args_list == [
            call(exclude=['*.module'], options=['-a']),
            call(exclude=['*.module'], options=['-a']),
            call(exclude=['*.module'], options=['-a'])
        ]
        assert mock_shutil_copy2.call_args_list == [
            call(
                'boot_dir/boot/grub2/earlyboot.cfg',
                'root_dir/boot/grub2/earlyboot.cfg'
            ),
            call(
                'root_dir/usr/share/grub2/i386-pc/core.img',
                'lookup_dir/usr/share/grub2/i386-pc/core.img'
            ),
            call(
                'boot_dir/EFI/BOOT/earlyboot.cfg',
                'root_dir/boot/efi/EFI/BOOT/earlyboot.cfg'
            ),
            call(
                'root_dir/boot/efi/EFI/BOOT/bootx64.efi',
                'boot_dir/EFI/BOOT/bootx64.efi'
            )
        ]
        mock_get_unsigned_grub_loader.return_value = [
            grub_loader_type(
                'custom_grub_image', 'some-binaryname', 'bootx64.efi'
            )
        ]
        mock_get_grub_platform_core_loader.return_value = \
            'custom_bios_grub_image'
        mock_command.reset_mock()

        with patch('builtins.open', create=True) as mock_open:
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader.setup_install_boot_images(self.mbrid)

            assert file_handle.write.call_args_list == [
                call('set btrfs_relative_path="yes"\n'),
                call('search --file --set=root /boot/0xffffffff\n'),
                call('set prefix=($root)/boot/grub2\n'),
                call('source ($root)/boot/grub2/grub.cfg\n'),
                call('source /boot/grub2/grub.cfg\n')
            ]
            assert mock_open.call_args_list == [
                call('boot_dir/EFI/BOOT/grub.cfg', 'w'),
                call('boot_dir/boot/grub2/loopback.cfg', 'w')
            ]

        assert mock_command.call_args_list == [
            call(
                [
                    'cp', 'boot_dir/usr/share/grub2/unicode.pf2',
                    'boot_dir/boot/grub2/fonts'
                ]
            ),
            call(
                [
                    'bash', '-c', 'cat boot_dir/usr/share/grub2/i386-pc/'
                    'cdboot.img custom_bios_grub_image > '
                    'boot_dir/usr/share/grub2/i386-pc/eltorito.img'
                ]
            ),
            call(
                [
                    'cp', 'custom_grub_image', 'boot_dir/EFI/BOOT/bootx64.efi'
                ]
            )
        ]

        with patch('builtins.open', create=True) as mock_open:
            # Test fallback search for earlyboot if UUID and MBR ID are missing
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader._setup_efi_image(uuid=None, mbrid=None)

            assert file_handle.write.call_args_list == [
                call('set btrfs_relative_path="yes"\n'),
                call('search --file --set=root /boot/mbrid\n'),
                call('set prefix=($root)/boot/grub2\n'),
                call('source ($root)/boot/grub2/grub.cfg\n'),
            ]

        mock_get_unsigned_grub_loader.return_value = []
        self.state.get_dracut_config = Mock(
            return_value=DracutT(uefi=True, modules=[], drivers=[])
        )
        with patch('builtins.open', create=True) as mock_open:
            # Test UKI chainloader for earlyboot
            mock_open.return_value = MagicMock(spec=io.IOBase)
            file_handle = mock_open.return_value.__enter__.return_value
            self.bootloader._setup_efi_image(efi_uuid='0815')

            assert file_handle.write.call_args_list == [
                call('search --no-floppy --set=root --fs-uuid 0815\n'),
                call('chainloader ($root)/EFI/Linux/kiwi.efi\n'),
                call('boot\n')
            ]

    @patch.object(BootLoaderConfigGrub2, '_supports_platform_modules')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('os.path.exists')
    @patch('glob.iglob')
    @patch('os.chmod')
    @patch('os.stat')
    @patch('os.path.isfile')
    def test_setup_install_boot_images_efi_secure_boot(
        self, mock_isfile, mock_stat, mock_chmod, mock_glob,
        mock_exists, mock_command, mock_supports_platform_modules
    ):
        Defaults.set_platform_name('x86_64')
        mock_supports_platform_modules.return_value = False
        mock_isfile.return_value = False
        self.os_exists['root_dir'] = True
        self.firmware.efi_mode = Mock(
            return_value='uefi'
        )
        self.os_exists['root_dir/usr/share/grub2/i386-pc'] = True
        self.os_exists['root_dir/usr/share/grub2/x86_64-efi'] = True
        self.os_exists['root_dir/usr/share/grub2/unicode.pf2'] = True

        def side_effect_exists(arg):
            return self.os_exists[arg]

        def side_effect_glob(arg):
            return self.glob_iglob_shim_iso.pop()

        mock_glob.side_effect = side_effect_glob
        mock_exists.side_effect = side_effect_exists
        with self._caplog.at_level(logging.INFO):
            with patch('builtins.open', create=True) as mock_open:
                mock_open.return_value = MagicMock(spec=io.IOBase)
                file_handle = mock_open.return_value.__enter__.return_value
                self.bootloader.setup_install_boot_images(
                    self.mbrid, 'root_dir'
                )
                assert file_handle.write.call_args_list == [
                    call('set btrfs_relative_path="yes"\n'),
                    call('search --file --set=root /boot/0xffffffff\n'),
                    call('set prefix=($root)/boot/grub2\n'),
                    call('source ($root)/boot/grub2/grub.cfg\n'),
                    call('source /boot/grub2/grub.cfg\n')
                ]
                assert mock_open.call_args_list == [
                    call('root_dir/EFI/BOOT/grub.cfg', 'w'),
                    call('root_dir/boot/grub2/loopback.cfg', 'w')
                ]
                assert mock_command.call_args_list == [
                    call(
                        [
                            'cp', 'root_dir/usr/share/grub2/unicode.pf2',
                            'root_dir/boot/efi/EFI/DIST/fonts'
                        ]
                    ),
                    call(
                        [
                            'rsync', '-a', 'root_dir/boot/efi/', 'root_dir'
                        ]
                    ),
                    call(
                        [
                            'cp', 'root_dir/usr/lib64/efi/grub.efi',
                            'root_dir/EFI/BOOT/grubx64.efi'
                        ]
                    ),
                    call(
                        [
                            'cp', 'root_dir/usr/lib64/efi/shim.efi',
                            'root_dir/EFI/BOOT/bootx64.efi'
                        ]
                    ),
                    call(
                        [
                            'cp', 'root_dir/usr/lib64/efi/MokManager.efi',
                            'root_dir/EFI/BOOT'
                        ]
                    )
                ]

    @patch('kiwi.defaults.Defaults.get_grub_efi_font_directory')
    @patch.object(BootLoaderConfigGrub2, '_supports_platform_modules')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    @patch('glob.iglob')
    def test_setup_install_boot_images_with_theme_from_usr_share(
        self, mock_glob, mock_exists,
        mock_sync, mock_command, mock_supports_platform_modules,
        mock_get_grub_efi_font_directory
    ):
        Defaults.set_platform_name('x86_64')
        mock_get_grub_efi_font_directory.return_value = None
        mock_supports_platform_modules.return_value = False
        mock_glob.return_value = [
            'root_dir/boot/grub2/themes/some-theme/background.png'
        ]
        data = Mock()
        mock_sync.return_value = data
        self.bootloader.theme = 'some-theme'
        self.os_exists['lookup_path/usr/share/grub2'] = True
        self.os_exists['lookup_path/usr/lib/grub2'] = True
        self.os_exists['lookup_path/usr/share/grub2/i386-pc'] = True
        self.os_exists['lookup_path/usr/share/grub2/themes/some-theme'] = True
        self.os_exists['lookup_path/boot/grub2/themes/some-theme'] = True
        self.os_exists['root_dir/boot/grub2/themes/some-theme'] = True
        self.os_exists['lookup_path/usr/share/grub2/unicode.pf2'] = True

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        with patch('builtins.open'):
            self.bootloader.setup_install_boot_images(
                self.mbrid, lookup_path='lookup_path'
            )

        assert mock_command.call_args_list == [
            call(
                [
                    'cp',
                    'root_dir/boot/grub2/themes/some-theme/background.png',
                    'root_dir/background.png'
                ]
            ),
            call(
                [
                    'mv', 'root_dir/background.png',
                    'root_dir/boot/grub2/themes/some-theme'
                ]
            )
        ]
        assert mock_sync.call_args_list[0] == call(
            'lookup_path/usr/share/grub2/themes/some-theme',
            'root_dir/boot/grub2/themes'
        )
        assert data.sync_data.call_args_list[0] == call(
            options=['-a']
        )

    @patch('kiwi.defaults.Defaults.get_grub_efi_font_directory')
    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    @patch('glob.iglob')
    @patch('kiwi.defaults.Defaults.get_grub_path')
    def test_setup_install_boot_images_with_theme_from_boot(
        self, mock_get_grub_path, mock_glob,
        mock_exists, mock_sync, mock_command,
        mock_get_grub_efi_font_directory
    ):
        Defaults.set_platform_name('x86_64')
        mock_get_grub_efi_font_directory.return_value = None
        mock_glob.return_value = [
            'lookup_path/boot/grub2/themes/some-theme/background.png'
        ]
        data = Mock()
        mock_sync.return_value = data
        self.bootloader.theme = 'some-theme'

        self.os_exists['root_dir/boot/grub2/themes/some-theme'] = True
        self.os_exists['some-theme'] = False

        self.find_grub['themes/some-theme'] = 'some-theme'
        self.find_grub['i386-pc'] = 'i386-pc'
        self.find_grub['unicode.pf2'] = True

        def find_grub_data_side_effect(
            root_path, filename, raise_on_error=True
        ):
            return self.find_grub[filename]

        def side_effect(arg):
            return self.os_exists[arg]

        mock_get_grub_path.side_effect = find_grub_data_side_effect
        mock_exists.side_effect = side_effect

        with patch('builtins.open'):
            self.bootloader.setup_install_boot_images(
                self.mbrid, lookup_path='lookup_path'
            )

        assert mock_sync.call_args_list[0] == call(
            'lookup_path/boot/grub2/themes/some-theme',
            'root_dir/boot/grub2/themes'
        )
        assert data.sync_data.call_args_list[0] == call(
            options=['-a']
        )

    @patch('kiwi.bootloader.config.grub2.Command.run')
    @patch('kiwi.bootloader.config.grub2.Path.which')
    @patch('kiwi.bootloader.config.grub2.DataSync')
    @patch('os.path.exists')
    @patch('kiwi.defaults.Defaults.get_grub_path')
    @patch('shutil.copy2')
    def test_setup_install_boot_images_with_theme_not_existing(
        self, mock_shutil_copy2, mock_get_grub_path,
        mock_exists, mock_sync, mock_Path_which, mock_command
    ):
        Defaults.set_platform_name('x86_64')
        mock_Path_which.return_value = '/path/to/grub2-mkimage'
        self.bootloader.theme = 'some-theme'

        mock_get_grub_path.return_value = 'theme-dir'

        self.os_exists['theme-dir'] = False
        self.os_exists['root_dir/boot/grub2/themes/some-theme'] = False

        def side_effect(arg):
            return self.os_exists[arg]

        mock_exists.side_effect = side_effect

        with patch('builtins.open'):
            with self._caplog.at_level(logging.WARNING):
                self.bootloader.setup_install_boot_images(self.mbrid)
                assert self.bootloader.terminal_input == 'console'
                assert self.bootloader.terminal_output == 'console'

    @patch.object(BootLoaderConfigGrub2, 'setup_install_boot_images')
    def test_setup_live_boot_images(self, mock_setup_install_boot_images):
        self.bootloader.setup_live_boot_images(self.mbrid)
        mock_setup_install_boot_images.assert_called_once_with(
            self.mbrid, None
        )
