Merge patch series "Improve pytest runtime"

Tom Rini <trini@konsulko.com> says:

One thing that Simon Glass has noted is that our pytest run time keeps
getting longer. Looking at:
https://source.denx.de/u-boot/u-boot/-/pipelines/25011/test_report?job_name=sandbox%20test.py%3A%20%5Bfast%20amd64%5D
we can see that some of the longest running tests are a little puzzling.
It turns out that we have two ways of making filesystem images without
requiring root access and one of them is significantly slower than the
other. This series changes us from using virt-make-fs to only using the
mk_fs helper that currently resides in test_ut.py which uses standard
userspace tools. The final result can be seen at:
https://source.denx.de/u-boot/u-boot/-/pipelines/25015/test_report?job_name=sandbox%20test.py%3A%20%5Bfast%20amd64%5D
and the tests changed here now run much quicker.

Link: https://lore.kernel.org/r/20250320140030.2052434-1-trini@konsulko.com
This commit is contained in:
Tom Rini 2025-04-08 13:54:50 -06:00
commit a30b544628
18 changed files with 288 additions and 350 deletions

View file

@ -41,13 +41,11 @@ will be required. The following is an incomplete list:
* dfu-util
* dtc
* openssl
* sudo OR guestmount
* e2fsprogs
* util-linux
* coreutils
* dosfstools
* efitools
* guestfs-tools
* mount
* mtools
* sbsigntool
@ -64,23 +62,12 @@ The test script supports either:
physical board, attach to the board's console stream, and reset the board.
Further details are described later.
The usage of command 'sudo' should be avoided in tests. To create disk images
use command virt-make-fs which is provided by package guestfs-tools. This
command creates a virtual machine with QEMU in which the disk image is
generated.
Command virt-make-fs needs read access to the current kernel. On Ubuntu only
root has this privilege. You can add a script /etc/initramfs-tools/hooks/vmlinuz
with the following content to overcome the problem:
.. code-block:: bash
#!/bin/sh
echo "chmod a+r vmlinuz-*"
chmod a+r /boot/vmlinuz-*
The script should be chmod 755. It will be invoked whenever the initial RAM file
system is updated.
The usage of the command 'sudo' is not allowed in tests. Using elevated
priviledges can lead to security concerns. Furthermore not all users may have
administrator rights. Therefore the command 'sudo' must not be used in tests.
To create disk images we have helper functions located in
`test/py/tests/fs_helper.py` which shall be used in any tests that require
creating disk images.
Using `virtualenv` to provide requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -73,6 +73,40 @@ def mk_fs(config, fs_type, size, prefix, src_dir=None, size_gran = 0x100000):
call(f'rm -f {fs_img}', shell=True)
raise
def setup_image(ubman, devnum, part_type, img_size=20, second_part=False,
basename='mmc'):
"""Create a disk image with a single partition
Args:
ubman (ConsoleBase): Console to use
devnum (int): Device number to use, e.g. 1
part_type (int): Partition type, e.g. 0xc for FAT32
img_size (int): Image size in MiB
second_part (bool): True to contain a small second partition
basename (str): Base name to use in the filename, e.g. 'mmc'
Returns:
tuple:
str: Filename of MMC image
str: Directory name of scratch directory
"""
fname = os.path.join(ubman.config.source_dir, f'{basename}{devnum}.img')
mnt = os.path.join(ubman.config.persistent_data_dir, 'scratch')
spec = f'type={part_type:x}, size={img_size - 2}M, start=1M, bootable'
if second_part:
spec += '\ntype=c'
try:
check_call(f'mkdir -p {mnt}', shell=True)
check_call(f'qemu-img create {fname} {img_size}M', shell=True)
check_call(f'printf "{spec}" | sfdisk {fname}', shell=True)
except CalledProcessError:
call(f'rm -f {fname}', shell=True)
raise
return fname, mnt
# Just for trying out
if __name__ == "__main__":
import collections

37
test/py/tests/test_cat.py Normal file
View file

@ -0,0 +1,37 @@
# SPDX-License-Identifier: GPL-2.0+
""" Unit test for cat command
"""
import pytest
from subprocess import call, check_call, CalledProcessError
from tests import fs_helper
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_cat')
def test_cat(ubman):
""" Unit test for cat
Args:
ubman -- U-Boot console
"""
try:
scratch_dir = ubman.config.persistent_data_dir + '/scratch'
check_call('mkdir -p %s' % scratch_dir, shell=True)
with open(scratch_dir + '/hello', 'w', encoding = 'ascii') as file:
file.write('hello world\n')
cat_data = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000,
'test_cat', scratch_dir)
response = ubman.run_command_list([ f'host bind 0 {cat_data}',
'cat host 0 hello'])
assert 'hello world' in response
except CalledProcessError as err:
pytest.skip('Preparing test_cat image failed')
call('rm -f %s' % cat_data, shell=True)
return
finally:
call('rm -rf %s' % scratch_dir, shell=True)
call('rm -f %s' % cat_data, shell=True)

View file

@ -1,36 +0,0 @@
# SPDX-License-Identifier: GPL-2.0+
"""Fixture for cat command test
"""
import os
import shutil
from subprocess import check_call, CalledProcessError
import pytest
@pytest.fixture(scope='session')
def cat_data(u_boot_config):
"""Set up a file system to be used in cat tests
Args:
u_boot_config -- U-Boot configuration.
"""
mnt_point = u_boot_config.persistent_data_dir + '/test_cat'
image_path = u_boot_config.persistent_data_dir + '/cat.img'
try:
os.mkdir(mnt_point, mode = 0o755)
with open(mnt_point + '/hello', 'w', encoding = 'ascii') as file:
file.write('hello world\n')
check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
shell=True)
yield image_path
except CalledProcessError:
pytest.skip('Setup failed')
finally:
shutil.rmtree(mnt_point)
if os.path.exists(image_path):
os.remove(image_path)

View file

@ -1,20 +0,0 @@
# SPDX-License-Identifier: GPL-2.0+
""" Unit test for cat command
"""
import pytest
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_cat')
def test_cat(ubman, cat_data):
""" Unit test for cat
Args:
ubman -- U-Boot console
cat_data -- Path to the disk image used for testing.
"""
response = ubman.run_command_list([
f'host bind 0 {cat_data}',
'cat host 0 hello'])
assert 'hello world' in response

View file

@ -0,0 +1,70 @@
# SPDX-License-Identifier: GPL-2.0+
""" Unit test for UEFI bootmanager
"""
import shutil
import pytest
from subprocess import call, check_call, CalledProcessError
from tests import fs_helper
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_efidebug')
@pytest.mark.buildconfigspec('cmd_bootefi_bootmgr')
@pytest.mark.singlethread
def test_efi_bootmgr(ubman):
""" Unit test for UEFI bootmanager
The efidebug command is used to set up UEFI load options.
The bootefi bootmgr loads initrddump.efi as a payload.
The crc32 of the loaded initrd.img is checked
Args:
ubman -- U-Boot console
"""
try:
efi_bootmgr_data, mnt = fs_helper.setup_image(ubman, 0, 0xc,
basename='test_efi_bootmgr')
with open(mnt + '/initrd-1.img', 'w', encoding = 'ascii') as file:
file.write("initrd 1")
with open(mnt + '/initrd-2.img', 'w', encoding = 'ascii') as file:
file.write("initrd 2")
shutil.copyfile(ubman.config.build_dir + '/lib/efi_loader/initrddump.efi',
mnt + '/initrddump.efi')
fsfile = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000,
'test_efi_bootmgr', mnt)
check_call(f'dd if={fsfile} of={efi_bootmgr_data} bs=1M seek=1', shell=True)
ubman.run_command(cmd = f'host bind 0 {efi_bootmgr_data}')
ubman.run_command(cmd = 'efidebug boot add ' \
'-b 0001 label-1 host 0:1 initrddump.efi ' \
'-i host 0:1 initrd-1.img -s nocolor')
ubman.run_command(cmd = 'efidebug boot dump')
ubman.run_command(cmd = 'efidebug boot order 0001')
ubman.run_command(cmd = 'bootefi bootmgr')
response = ubman.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x181464af' in response
ubman.run_command(cmd = 'exit', wait_for_echo=False)
ubman.run_command(cmd = 'efidebug boot add ' \
'-B 0002 label-2 host 0:1 initrddump.efi ' \
'-I host 0:1 initrd-2.img -s nocolor')
ubman.run_command(cmd = 'efidebug boot dump')
ubman.run_command(cmd = 'efidebug boot order 0002')
ubman.run_command(cmd = 'bootefi bootmgr')
response = ubman.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x811d3515' in response
ubman.run_command(cmd = 'exit', wait_for_echo=False)
ubman.run_command(cmd = 'efidebug boot rm 0001')
ubman.run_command(cmd = 'efidebug boot rm 0002')
except CalledProcessError as err:
pytest.skip('Preparing test_efi_bootmgr image failed')
call('rm -f %s' % efi_bootmgr_data, shell=True)
return
finally:
call('rm -rf %s' % mnt, shell=True)
call('rm -f %s' % efi_bootmgr_data, shell=True)

View file

@ -1,38 +0,0 @@
# SPDX-License-Identifier: GPL-2.0+
"""Fixture for UEFI bootmanager test."""
import os
import shutil
from subprocess import check_call
import pytest
@pytest.fixture(scope='session')
def efi_bootmgr_data(u_boot_config):
"""Set up a file system to be used in UEFI bootmanager tests.
Args:
u_boot_config -- U-Boot configuration.
Return:
A path to disk image to be used for testing
"""
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_bootmgr'
image_path = u_boot_config.persistent_data_dir + '/efi_bootmgr.img'
shutil.rmtree(mnt_point, ignore_errors=True)
os.mkdir(mnt_point, mode = 0o755)
with open(mnt_point + '/initrd-1.img', 'w', encoding = 'ascii') as file:
file.write("initrd 1")
with open(mnt_point + '/initrd-2.img', 'w', encoding = 'ascii') as file:
file.write("initrd 2")
shutil.copyfile(u_boot_config.build_dir + '/lib/efi_loader/initrddump.efi',
mnt_point + '/initrddump.efi')
check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
shell=True)
return image_path

View file

@ -1,44 +0,0 @@
# SPDX-License-Identifier: GPL-2.0+
""" Unit test for UEFI bootmanager
"""
import pytest
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_efidebug')
@pytest.mark.buildconfigspec('cmd_bootefi_bootmgr')
@pytest.mark.singlethread
def test_efi_bootmgr(ubman, efi_bootmgr_data):
""" Unit test for UEFI bootmanager
The efidebug command is used to set up UEFI load options.
The bootefi bootmgr loads initrddump.efi as a payload.
The crc32 of the loaded initrd.img is checked
Args:
ubman -- U-Boot console
efi_bootmgr_data -- Path to the disk image used for testing.
"""
ubman.run_command(cmd = f'host bind 0 {efi_bootmgr_data}')
ubman.run_command(cmd = 'efidebug boot add ' \
'-b 0001 label-1 host 0:1 initrddump.efi ' \
'-i host 0:1 initrd-1.img -s nocolor')
ubman.run_command(cmd = 'efidebug boot dump')
ubman.run_command(cmd = 'efidebug boot order 0001')
ubman.run_command(cmd = 'bootefi bootmgr')
response = ubman.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x181464af' in response
ubman.run_command(cmd = 'exit', wait_for_echo=False)
ubman.run_command(cmd = 'efidebug boot add ' \
'-B 0002 label-2 host 0:1 initrddump.efi ' \
'-I host 0:1 initrd-2.img -s nocolor')
ubman.run_command(cmd = 'efidebug boot dump')
ubman.run_command(cmd = 'efidebug boot order 0002')
ubman.run_command(cmd = 'bootefi bootmgr')
response = ubman.run_command(cmd = 'load', wait_for_echo=False)
assert 'crc32: 0x811d3515' in response
ubman.run_command(cmd = 'exit', wait_for_echo=False)
ubman.run_command(cmd = 'efidebug boot rm 0001')
ubman.run_command(cmd = 'efidebug boot rm 0002')

View file

@ -136,7 +136,7 @@ def do_reboot_dtb_specified(u_boot_config, ubman, dtb_filename):
ubman -- A console connection to U-Boot.
dtb_filename -- DTB file name.
"""
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
mnt_point = u_boot_config.persistent_data_dir + '/scratch'
ubman.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+ f'/{dtb_filename}'
ubman.restart_uboot()

View file

@ -7,11 +7,12 @@
import os
from subprocess import call, check_call, CalledProcessError
from tests import fs_helper
import pytest
from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR, EFITOOLS_PATH
@pytest.fixture(scope='session')
def efi_capsule_data(request, u_boot_config):
@pytest.fixture(scope='function')
def efi_capsule_data(request, ubman):
"""Set up a file system and return path to image.
The function sets up a file system to be used in UEFI capsule and
@ -19,14 +20,14 @@ def efi_capsule_data(request, u_boot_config):
for testing.
request -- Pytest request object.
u_boot_config -- U-Boot configuration.
ubman -- U-Boot configuration.
"""
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
data_dir = mnt_point + CAPSULE_DATA_DIR
install_dir = mnt_point + CAPSULE_INSTALL_DIR
image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
try:
image_path, mnt_point = fs_helper.setup_image(ubman, 0, 0xc,
basename='test_efi_capsule')
data_dir = mnt_point + CAPSULE_DATA_DIR
install_dir = mnt_point + CAPSULE_INSTALL_DIR
# Create a target device
check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
@ -34,9 +35,9 @@ def efi_capsule_data(request, u_boot_config):
check_call('mkdir -p %s' % data_dir, shell=True)
check_call('mkdir -p %s' % install_dir, shell=True)
capsule_auth_enabled = u_boot_config.buildconfig.get(
capsule_auth_enabled = ubman.config.buildconfig.get(
'config_efi_capsule_authenticate')
key_dir = u_boot_config.source_dir + '/board/sandbox'
key_dir = ubman.config.source_dir + '/board/sandbox'
if capsule_auth_enabled:
# Get the keys from the board directory
check_call('cp %s/capsule_priv_key_good.key %s/SIGNER.key'
@ -54,12 +55,12 @@ def efi_capsule_data(request, u_boot_config):
# Update dtb to add the version information
check_call('cd %s; '
'cp %s/test/py/tests/test_efi_capsule/version.dtso .'
% (data_dir, u_boot_config.source_dir), shell=True)
% (data_dir, ubman.config.source_dir), shell=True)
if capsule_auth_enabled:
check_call('cd %s; '
'cp %s/arch/sandbox/dts/test.dtb test_sig.dtb'
% (data_dir, u_boot_config.build_dir), shell=True)
% (data_dir, ubman.config.build_dir), shell=True)
check_call('cd %s; '
'dtc -@ -I dts -O dtb -o version.dtbo version.dtso; '
'fdtoverlay -i test_sig.dtb '
@ -70,29 +71,33 @@ def efi_capsule_data(request, u_boot_config):
'dtc -@ -I dts -O dtb -o version.dtbo version.dtso; '
'fdtoverlay -i %s/arch/sandbox/dts/test.dtb '
'-o test_ver.dtb version.dtbo'
% (data_dir, u_boot_config.build_dir), shell=True)
% (data_dir, ubman.config.build_dir), shell=True)
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old > u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir,
shell=True)
pythonpath = os.environ.get('PYTHONPATH', '')
os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/scripts/dtc/pylibfdt' % u_boot_config.build_dir
os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/scripts/dtc/pylibfdt' % ubman.config.build_dir
check_call('cd %s; '
'cc -E -I %s/include -x assembler-with-cpp -o capsule_gen_tmp.dts %s/test/py/tests/test_efi_capsule/capsule_gen_binman.dts; '
'dtc -I dts -O dtb capsule_gen_tmp.dts -o capsule_binman.dtb;'
% (data_dir, u_boot_config.source_dir, u_boot_config.source_dir), shell=True)
% (data_dir, ubman.config.source_dir, ubman.config.source_dir), shell=True)
check_call('cd %s; '
'./tools/binman/binman --toolpath %s/tools build -u -d %s/capsule_binman.dtb -O %s -m --allow-missing -I %s -I ./board/sandbox -I ./arch/sandbox/dts'
% (u_boot_config.source_dir, u_boot_config.build_dir, data_dir, data_dir, data_dir), shell=True)
check_call('cp %s/Test* %s' % (u_boot_config.build_dir, data_dir), shell=True)
% (ubman.config.source_dir, ubman.config.build_dir, data_dir, data_dir, data_dir), shell=True)
check_call('cp %s/Test* %s' % (ubman.config.build_dir, data_dir), shell=True)
os.environ['PYTHONPATH'] = pythonpath
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
(mnt_point, image_path), shell=True)
# Create a 16MiB partition as the EFI system partition in the disk
# image
fsfile = fs_helper.mk_fs(ubman.config, 'vfat', 0x1000000,
'test_efi_capsule', mnt_point)
check_call(f'dd conv=notrunc if={fsfile} of={image_path} bs=1M seek=1', shell=True)
check_call('sgdisk --mbrtogpt %s' % image_path, shell=True)
check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
image_path, shell=True)
call('rm -f %s' % fsfile, shell=True)
except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)

View file

@ -5,27 +5,25 @@
"""Fixture for UEFI secure boot test."""
from subprocess import call, check_call, CalledProcessError
from tests import fs_helper
import pytest
from defs import *
@pytest.fixture(scope='session')
def efi_boot_env(request, u_boot_config):
@pytest.fixture(scope='function')
def efi_boot_env(request, ubman):
"""Set up a file system to be used in UEFI secure boot test.
Args:
request: Pytest request object.
u_boot_config: U-Boot configuration.
ubman: U-Boot configuration.
Return:
A path to disk image to be used for testing
"""
image_path = u_boot_config.persistent_data_dir
image_path = image_path + '/test_efi_secboot.img'
try:
mnt_point = u_boot_config.build_dir + '/mnt_efisecure'
check_call('rm -rf {}'.format(mnt_point), shell=True)
check_call('mkdir -p {}'.format(mnt_point), shell=True)
image_path, mnt_point = fs_helper.setup_image(ubman, 0, 0xc,
basename='test_efi_secboot')
# suffix
# *.key: RSA private key in PEM
@ -101,7 +99,7 @@ def efi_boot_env(request, u_boot_config):
# Copy image
check_call('cp %s/lib/efi_loader/helloworld.efi %s' %
(u_boot_config.build_dir, mnt_point), shell=True)
(ubman.config.build_dir, mnt_point), shell=True)
# Sign image
check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
@ -111,7 +109,7 @@ def efi_boot_env(request, u_boot_config):
% mnt_point, shell=True)
# Create a corrupted signed image
check_call('cd %s; sh %s/test/py/tests/test_efi_secboot/forge_image.sh helloworld.efi.signed helloworld_forged.efi.signed'
% (mnt_point, u_boot_config.source_dir), shell=True)
% (mnt_point, ubman.config.source_dir), shell=True)
# Digest image
check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
% (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
@ -123,9 +121,9 @@ def efi_boot_env(request, u_boot_config):
% (mnt_point, EFITOOLS_PATH),
shell=True)
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(
mnt_point, image_path), shell=True)
check_call('rm -rf {}'.format(mnt_point), shell=True)
fsfile = fs_helper.mk_fs(ubman.config, 'vfat', 0x1000000,
'test_efi_secboot', mnt_point)
check_call(f'dd if={fsfile} of={image_path} bs=1M seek=1', shell=True)
except CalledProcessError as exception:
pytest.skip('Setup failed: %s' % exception.cmd)
@ -133,15 +131,16 @@ def efi_boot_env(request, u_boot_config):
else:
yield image_path
finally:
call('rm -f %s' % image_path, shell=True)
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s %s' % (image_path, fsfile), shell=True)
#
# Fixture for UEFI secure boot test of intermediate certificates
#
@pytest.fixture(scope='session')
def efi_boot_env_intca(request, u_boot_config):
@pytest.fixture(scope='function')
def efi_boot_env_intca(request, ubman):
"""Set up file system for secure boot test.
Set up a file system to be used in UEFI secure boot test
@ -149,18 +148,15 @@ def efi_boot_env_intca(request, u_boot_config):
Args:
request: Pytest request object.
u_boot_config: U-Boot configuration.
ubman: U-Boot configuration.
Return:
A path to disk image to be used for testing
"""
image_path = u_boot_config.persistent_data_dir
image_path = image_path + '/test_efi_secboot_intca.img'
try:
mnt_point = u_boot_config.persistent_data_dir + '/mnt_efi_secboot_intca'
check_call('rm -rf {}'.format(mnt_point), shell=True)
check_call('mkdir -p {}'.format(mnt_point), shell=True)
image_path, mnt_point = fs_helper.setup_image(ubman, 0, 0xc,
basename='test_efi_secboot_intca')
# Create signature database
# PK
@ -190,7 +186,7 @@ def efi_boot_env_intca(request, u_boot_config):
# TestRoot
check_call('cp %s/test/py/tests/test_efi_secboot/openssl.cnf %s'
% (u_boot_config.source_dir, mnt_point), shell=True)
% (ubman.config.source_dir, mnt_point), shell=True)
check_call('cd %s; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestRoot.key 2048; openssl req -extensions v3_ca -new -x509 -days 365 -key TestRoot.key -out TestRoot.crt -subj "/CN=TEST_root/"; touch index.txt; touch index.txt.attr'
% mnt_point, shell=True)
# TestSub
@ -231,7 +227,7 @@ def efi_boot_env_intca(request, u_boot_config):
# in SignedData
check_call('cp %s/lib/efi_loader/helloworld.efi %s' %
(u_boot_config.build_dir, mnt_point), shell=True)
(ubman.config.build_dir, mnt_point), shell=True)
# signed by TestCert
check_call('cd %s; %ssbsign --key TestCert.key --cert TestCert.crt --out helloworld.efi.signed_a helloworld.efi'
% (mnt_point, SBSIGN_PATH), shell=True)
@ -242,8 +238,9 @@ def efi_boot_env_intca(request, u_boot_config):
check_call('cd %s; cat TestSub.crt TestRoot.crt > TestSubRoot.crt; %ssbsign --key TestCert.key --cert TestCert.crt --addcert TestSubRoot.crt --out helloworld.efi.signed_abc helloworld.efi'
% (mnt_point, SBSIGN_PATH), shell=True)
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(mnt_point, image_path), shell=True)
check_call('rm -rf {}'.format(mnt_point), shell=True)
fsfile = fs_helper.mk_fs(ubman.config, 'vfat', 0x1000000,
'test_efi_secboot_intca', mnt_point)
check_call(f'dd if={fsfile} of={image_path} bs=1M seek=1', shell=True)
except CalledProcessError as e:
pytest.skip('Setup failed: %s' % e.cmd)
@ -251,4 +248,5 @@ def efi_boot_env_intca(request, u_boot_config):
else:
yield image_path
finally:
call('rm -f %s' % image_path, shell=True)
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s %s' % (image_path, fsfile), shell=True)

View file

@ -2,13 +2,57 @@
""" Unit test for UEFI menu-driven configuration
"""
import pytest
import shutil
import pytest
import time
from subprocess import call, check_call, CalledProcessError
from tests import fs_helper
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_eficonfig')
@pytest.mark.buildconfigspec('cmd_bootefi_bootmgr')
def test_efi_eficonfig(ubman, efi_eficonfig_data):
def test_efi_eficonfig(ubman):
def prepare_image(u_boot_config):
"""Set up a file system to be used in UEFI "eficonfig" command
tests. This creates a disk image with the following files:
initrd-1.img
initrd-2.img
initrddump.efi
Args:
u_boot_config -- U-Boot configuration.
Return:
A path to disk image to be used for testing
"""
try:
image_path, mnt_point = fs_helper.setup_image(u_boot_config, 0,
0xc,
basename='test_eficonfig')
with open(mnt_point + '/initrd-1.img', 'w', encoding = 'ascii') as file:
file.write("initrd 1")
with open(mnt_point + '/initrd-2.img', 'w', encoding = 'ascii') as file:
file.write("initrd 2")
shutil.copyfile(u_boot_config.build_dir + '/lib/efi_loader/initrddump.efi',
mnt_point + '/initrddump.efi')
fsfile = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000,
'test_eficonfig', mnt_point)
check_call(f'dd if={fsfile} of={image_path} bs=1M seek=1', shell=True)
yield image_path
except CalledProcessError as err:
pytest.skip('Preparing test_eficonfig image failed')
call('rm -f %s' % image_path, shell=True)
finally:
call('rm -rf %s' % mnt_point, shell=True)
call('rm -f %s' % image_path, shell=True)
def send_user_input_and_wait(user_str, expect_str):
time.sleep(0.1) # TODO: does not work correctly without sleep
@ -57,12 +101,6 @@ def test_efi_eficonfig(ubman, efi_eficonfig_data):
Args:
ubman -- U-Boot console
efi__data -- Path to the disk image used for testing.
Test disk image has following files.
initrd-1.img
initrd-2.img
initrddump.efi
"""
# This test passes for unknown reasons in the bowels of U-Boot. It needs to
# be replaced with a unit test.
@ -71,6 +109,7 @@ def test_efi_eficonfig(ubman, efi_eficonfig_data):
# Restart the system to clean the previous state
ubman.restart_uboot()
efi_eficonfig_data = prepare_image(ubman.config)
with ubman.temporary_timeout(500):
#
# Test Case 1: Check the menu is displayed

View file

@ -1,40 +0,0 @@
# SPDX-License-Identifier: GPL-2.0+
"""Fixture for UEFI eficonfig test
"""
import os
import shutil
from subprocess import check_call
import pytest
@pytest.fixture(scope='session')
def efi_eficonfig_data(u_boot_config):
"""Set up a file system to be used in UEFI "eficonfig" command
tests
Args:
u_boot_config -- U-Boot configuration.
Return:
A path to disk image to be used for testing
"""
mnt_point = u_boot_config.persistent_data_dir + '/test_efi_eficonfig'
image_path = u_boot_config.persistent_data_dir + '/efi_eficonfig.img'
shutil.rmtree(mnt_point, ignore_errors=True)
os.mkdir(mnt_point, mode = 0o755)
with open(mnt_point + '/initrd-1.img', 'w', encoding = 'ascii') as file:
file.write("initrd 1")
with open(mnt_point + '/initrd-2.img', 'w', encoding = 'ascii') as file:
file.write("initrd 2")
shutil.copyfile(u_boot_config.build_dir + '/lib/efi_loader/initrddump.efi',
mnt_point + '/initrddump.efi')
check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
shell=True)
return image_path

View file

@ -27,36 +27,6 @@ def mkdir_cond(dirname):
if not os.path.exists(dirname):
os.mkdir(dirname)
def setup_image(ubman, devnum, part_type, img_size=20, second_part=False,
basename='mmc'):
"""Create a disk image with a single partition
Args:
ubman (ConsoleBase): Console to use
devnum (int): Device number to use, e.g. 1
part_type (int): Partition type, e.g. 0xc for FAT32
img_size (int): Image size in MiB
second_part (bool): True to contain a small second partition
basename (str): Base name to use in the filename, e.g. 'mmc'
Returns:
tuple:
str: Filename of MMC image
str: Directory name of scratch directory
"""
fname = os.path.join(ubman.config.source_dir, f'{basename}{devnum}.img')
mnt = os.path.join(ubman.config.persistent_data_dir, 'scratch')
mkdir_cond(mnt)
spec = f'type={part_type:x}, size={img_size - 2}M, start=1M, bootable'
if second_part:
spec += '\ntype=c'
utils.run_and_log(ubman, f'qemu-img create {fname} 20M')
utils.run_and_log(ubman, f'sfdisk {fname}',
stdin=spec.encode('utf-8'))
return fname, mnt
def copy_partition(ubman, fsfile, outname):
"""Copy a partition into a disk iamge
@ -74,7 +44,7 @@ def setup_bootmenu_image(ubman):
This is modelled on Armbian 22.08 Jammy
"""
mmc_dev = 4
fname, mnt = setup_image(ubman, mmc_dev, 0x83)
fname, mnt = fs_helper.setup_image(ubman, mmc_dev, 0x83)
script = '''# DO NOT EDIT THIS FILE
#
@ -190,7 +160,7 @@ booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
def setup_bootflow_image(ubman):
"""Create a 20MB disk image with a single FAT partition"""
mmc_dev = 1
fname, mnt = setup_image(ubman, mmc_dev, 0xc, second_part=True)
fname, mnt = fs_helper.setup_image(ubman, mmc_dev, 0xc, second_part=True)
vmlinux = 'vmlinuz-5.3.7-301.fc31.armv7hl'
initrd = 'initramfs-5.3.7-301.fc31.armv7hl.img'
@ -556,8 +526,8 @@ def setup_efi_image(ubman):
"""Create a 20MB disk image with an EFI app on it"""
devnum = 1
basename = 'flash'
fname, mnt = setup_image(ubman, devnum, 0xc, second_part=True,
basename=basename)
fname, mnt = fs_helper.setup_image(ubman, devnum, 0xc, second_part=True,
basename=basename)
efi_dir = os.path.join(mnt, 'EFI')
mkdir_cond(efi_dir)

40
test/py/tests/test_xxd.py Normal file
View file

@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-2.0+
""" Unit test for xxd command
"""
import pytest
from subprocess import call, check_call, CalledProcessError
from tests import fs_helper
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_xxd')
def test_xxd(ubman):
""" Unit test for xxd
Args:
ubman -- U-Boot console
"""
try:
scratch_dir = ubman.config.persistent_data_dir + '/scratch'
check_call('mkdir -p %s' % scratch_dir, shell=True)
with open(scratch_dir + '/hello', 'w', encoding = 'ascii') as file:
file.write('hello world\n\x00\x01\x02\x03\x04\x05')
xxd_data = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000,
'test_xxd', scratch_dir)
response = ubman.run_command_list([ f'host bind 0 {xxd_data}',
'xxd host 0 hello'])
assert '00000000: 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a 00 01 02 03 hello world.....\r\r\n' + \
'00000010: 04 05 ..' \
in response
except CalledProcessError as err:
pytest.skip('Preparing test_xxd image failed')
call('rm -f %s' % xxd_data, shell=True)
return
finally:
call('rm -rf %s' % scratch_dir, shell=True)
call('rm -f %s' % xxd_data, shell=True)

View file

@ -1,36 +0,0 @@
# SPDX-License-Identifier: GPL-2.0+
"""Fixture for xxd command test
"""
import os
import shutil
from subprocess import check_call, CalledProcessError
import pytest
@pytest.fixture(scope='session')
def xxd_data(u_boot_config):
"""Set up a file system to be used in xxd tests
Args:
u_boot_config -- U-Boot configuration.
"""
mnt_point = u_boot_config.persistent_data_dir + '/test_xxd'
image_path = u_boot_config.persistent_data_dir + '/xxd.img'
try:
os.mkdir(mnt_point, mode = 0o755)
with open(mnt_point + '/hello', 'w', encoding = 'ascii') as file:
file.write('hello world\n\x00\x01\x02\x03\x04\x05')
check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
shell=True)
yield image_path
except CalledProcessError:
pytest.skip('Setup failed')
finally:
shutil.rmtree(mnt_point)
if os.path.exists(image_path):
os.remove(image_path)

View file

@ -1,23 +0,0 @@
# SPDX-License-Identifier: GPL-2.0+
""" Unit test for xxd command
"""
import pytest
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('cmd_xxd')
def test_xxd(ubman, xxd_data):
""" Unit test for xxd
Args:
ubman -- U-Boot console
xxd_data -- Path to the disk image used for testing.
"""
response = ubman.run_command_list([
f'host bind 0 {xxd_data}',
'xxd host 0 hello'])
assert '00000000: 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a 00 01 02 03 hello world.....\r\r\n' + \
'00000010: 04 05 ..' \
in response

View file

@ -92,7 +92,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
libconfuse-dev \
libgit2-dev \
libjson-glib-dev \
libguestfs-tools \
libgnutls28-dev \
libgnutls30 \
liblz4-tool \
@ -106,7 +105,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
libtool \
libudev-dev \
libusb-1.0-0-dev \
linux-image-generic \
lzma-alone \
lzop \
mount \
@ -143,9 +141,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
xxd \
zip
# Make kernels readable for libguestfs tools to work correctly
RUN chmod +r /boot/vmlinu*
# Build GRUB UEFI targets for ARM & RISC-V, 32-bit and 64-bit
RUN git clone git://git.savannah.gnu.org/grub.git /tmp/grub && \
cd /tmp/grub && \