test: py: Add run_ut() helper for manual unit tests

Running manual unit tests (those with _norun suffix) involves a common
pattern: building the ut command with the -f flag, running it, and
checking for failures. This is verbose and error-prone.

Add a run_ut() method to ConsoleBase that simplifies this. It handles
the command construction, test arguments and failure checking
automatically.

Before:
    output = ubman.run_command(
        f'ut -f fs fs_test_ext4l_probe_norun fs_image={ext4_image}')
    assert 'failures: 0' in output

After:
    ubman.run_ut('fs', 'fs_test_ext4l_probe', fs_image=ext4_image)

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2025-12-28 20:47:58 -07:00
committed by Simon Glass
parent 1dd04b3fcb
commit aa1ada905b
5 changed files with 58 additions and 75 deletions

View File

@@ -485,6 +485,33 @@ class ConsoleBase():
output.append(self.run_command(cmd))
return output
def run_ut(self, suite, test, **kwargs):
"""Run a manual unit test
Run a unit test that has the _norun suffix, meaning it requires
external setup (like creating a disk image) before it can run.
Args:
suite (str): Test suite name (e.g., 'fs')
test (str): Test name without _norun suffix
(e.g., 'fs_test_ext4l_probe')
**kwargs: Test arguments passed as key=value
(e.g., fs_image='/path/to/img')
Returns:
str: Command output
Raises:
AssertionError: If test reports failures
"""
args = ' '.join(f'{k}={v}' for k, v in kwargs.items())
cmd = f'ut -f {suite} {test}_norun'
if args:
cmd += f' {args}'
output = self.run_command(cmd)
assert 'failures: 0' in output, f'Test {test} failed'
return output
def send(self, msg):
"""Send characters without waiting for echo, etc."""
self.run_command(msg, wait_for_prompt=False, wait_for_echo=False,

View File

@@ -261,9 +261,7 @@ def test_fit_print(ubman):
build_test_fit(ubman, fit)
# Run the C test which will load and verify this FIT
ubman.run_command('ut -f bootstd test_fit_print_norun')
result = ubman.run_command('echo $?')
assert '0' == result
ubman.run_ut('bootstd', 'test_fit_print')
@pytest.mark.boardspec('sandbox')
@@ -279,9 +277,7 @@ def test_fit_print_no_desc(ubman):
utils.run_and_log(ubman, ['fdtput', '-d', fit, '/', 'description'])
# Run the C test to check the missing description
ubman.run_command('ut -f bootstd test_fit_print_no_desc_norun')
result = ubman.run_command('echo $?')
assert '0' == result
ubman.run_ut('bootstd', 'test_fit_print_no_desc')
@pytest.mark.boardspec('sandbox')
@pytest.mark.buildconfigspec('fit_print')

View File

@@ -16,39 +16,6 @@ from fstest_defs import SMALL_FILE, BIG_FILE
from fstest_helpers import assert_fs_integrity
def run_c_test(ubman, fs_type, fs_img, test_name, small=None, big=None,
md5val=None):
"""Run a C unit test with proper setup.
Args:
ubman (ConsoleBase): U-Boot console manager
fs_type (str): Filesystem type (ext4, fat, fs_generic, exfat)
fs_img (str): Path to filesystem image
test_name (str): Name of C test function (without _norun suffix)
small (str): Filename of small test file (optional)
big (str): Filename of big test file (optional)
md5val (str): Expected MD5 value for verification (optional)
Returns:
bool: True if test passed, False otherwise
"""
# Build the command with arguments
cmd = f'ut -f fs {test_name}_norun fs_type={fs_type} fs_image={fs_img}'
if small:
cmd += f' small={small}'
if big:
cmd += f' big={big}'
if md5val:
cmd += f' md5val={md5val}'
# Run the C test
ubman.run_command(cmd)
# Check result
result = ubman.run_command('echo $?')
return result.strip() == '0'
@pytest.mark.boardspec('sandbox')
@pytest.mark.slow
class TestFsBasic:
@@ -58,94 +25,92 @@ class TestFsBasic:
"""Test Case 1 - ls command, listing root and invalid directories"""
fs_type, fs_img, _ = fs_obj_basic
with ubman.log.section('Test Case 1 - ls'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_ls',
small=SMALL_FILE, big=BIG_FILE)
ubman.run_ut('fs', 'fs_test_ls', fs_type=fs_type, fs_image=fs_img,
small=SMALL_FILE, big=BIG_FILE)
def test_fs2(self, ubman, fs_obj_basic):
"""Test Case 2 - size command for a small file"""
fs_type, fs_img, _ = fs_obj_basic
with ubman.log.section('Test Case 2 - size (small)'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_size_small',
small=SMALL_FILE)
ubman.run_ut('fs', 'fs_test_size_small', fs_type=fs_type,
fs_image=fs_img, small=SMALL_FILE)
def test_fs3(self, ubman, fs_obj_basic):
"""Test Case 3 - size command for a large file"""
fs_type, fs_img, _ = fs_obj_basic
with ubman.log.section('Test Case 3 - size (large)'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_size_big',
big=BIG_FILE)
ubman.run_ut('fs', 'fs_test_size_big', fs_type=fs_type,
fs_image=fs_img, big=BIG_FILE)
def test_fs4(self, ubman, fs_obj_basic):
"""Test Case 4 - load a small file, 1MB"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 4 - load (small)'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_load_small',
small=SMALL_FILE, md5val=md5val[0])
ubman.run_ut('fs', 'fs_test_load_small', fs_type=fs_type,
fs_image=fs_img, small=SMALL_FILE, md5val=md5val[0])
def test_fs5(self, ubman, fs_obj_basic):
"""Test Case 5 - load, reading first 1MB of 3GB file"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 5 - load (first 1MB)'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_load_big_first',
big=BIG_FILE, md5val=md5val[1])
ubman.run_ut('fs', 'fs_test_load_big_first', fs_type=fs_type,
fs_image=fs_img, big=BIG_FILE, md5val=md5val[1])
def test_fs6(self, ubman, fs_obj_basic):
"""Test Case 6 - load, reading last 1MB of 3GB file"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 6 - load (last 1MB)'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_load_big_last',
big=BIG_FILE, md5val=md5val[2])
ubman.run_ut('fs', 'fs_test_load_big_last', fs_type=fs_type,
fs_image=fs_img, big=BIG_FILE, md5val=md5val[2])
def test_fs7(self, ubman, fs_obj_basic):
"""Test Case 7 - load, 1MB from the last 1MB in 2GB"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 7 - load (last 1MB in 2GB)'):
assert run_c_test(ubman, fs_type, fs_img,
'fs_test_load_big_2g_last',
big=BIG_FILE, md5val=md5val[3])
ubman.run_ut('fs', 'fs_test_load_big_2g_last', fs_type=fs_type,
fs_image=fs_img, big=BIG_FILE, md5val=md5val[3])
def test_fs8(self, ubman, fs_obj_basic):
"""Test Case 8 - load, reading first 1MB in 2GB"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 8 - load (first 1MB in 2GB)'):
assert run_c_test(ubman, fs_type, fs_img,
'fs_test_load_big_2g_first',
big=BIG_FILE, md5val=md5val[4])
ubman.run_ut('fs', 'fs_test_load_big_2g_first', fs_type=fs_type,
fs_image=fs_img, big=BIG_FILE, md5val=md5val[4])
def test_fs9(self, ubman, fs_obj_basic):
"""Test Case 9 - load, 1MB crossing 2GB boundary"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 9 - load (crossing 2GB boundary)'):
assert run_c_test(ubman, fs_type, fs_img,
'fs_test_load_big_2g_cross',
big=BIG_FILE, md5val=md5val[5])
ubman.run_ut('fs', 'fs_test_load_big_2g_cross', fs_type=fs_type,
fs_image=fs_img, big=BIG_FILE, md5val=md5val[5])
def test_fs10(self, ubman, fs_obj_basic):
"""Test Case 10 - load, reading beyond file end"""
fs_type, fs_img, _ = fs_obj_basic
with ubman.log.section('Test Case 10 - load (beyond file end)'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_load_beyond',
big=BIG_FILE)
ubman.run_ut('fs', 'fs_test_load_beyond', fs_type=fs_type,
fs_image=fs_img, big=BIG_FILE)
def test_fs11(self, ubman, fs_obj_basic):
"""Test Case 11 - write"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 11 - write'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_write',
small=SMALL_FILE, md5val=md5val[0])
ubman.run_ut('fs', 'fs_test_write', fs_type=fs_type,
fs_image=fs_img, small=SMALL_FILE, md5val=md5val[0])
assert_fs_integrity(fs_type, fs_img)
def test_fs12(self, ubman, fs_obj_basic):
"""Test Case 12 - write to "." directory"""
fs_type, fs_img, _ = fs_obj_basic
with ubman.log.section('Test Case 12 - write (".")'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_write_dot')
ubman.run_ut('fs', 'fs_test_write_dot', fs_type=fs_type,
fs_image=fs_img)
assert_fs_integrity(fs_type, fs_img)
def test_fs13(self, ubman, fs_obj_basic):
"""Test Case 13 - write to a file with '/./<filename>'"""
fs_type, fs_img, md5val = fs_obj_basic
with ubman.log.section('Test Case 13 - write ("./<file>")'):
assert run_c_test(ubman, fs_type, fs_img, 'fs_test_write_dotpath',
small=SMALL_FILE, md5val=md5val[0])
ubman.run_ut('fs', 'fs_test_write_dotpath', fs_type=fs_type,
fs_image=fs_img, small=SMALL_FILE, md5val=md5val[0])
assert_fs_integrity(fs_type, fs_img)

View File

@@ -33,5 +33,4 @@ def test_upl_handoff(ubman):
assert 'UPL state: active' == output
# Check the FIT offsets look correct
output = ubman.run_command('ut upl -f upl_test_info_norun')
assert 'failures: 0' in output
ubman.run_ut('upl', 'upl_test_info')

View File

@@ -127,9 +127,7 @@ def test_vbe_extlinux_fit_no_oem(ubman):
fname = os.path.join(ubman.config.persistent_data_dir, 'vbe0.img')
ubman.run_command(f'host bind 0 {fname}')
ubman.run_command('ut -f bootstd vbe_test_abrec_no_oem_norun')
result = ubman.run_command('echo $?')
assert '0' == result
ubman.run_ut('bootstd', 'vbe_test_abrec_no_oem')
@pytest.mark.boardspec('sandbox')
def test_vbe_extlinux_fit_oem(ubman):
@@ -137,6 +135,4 @@ def test_vbe_extlinux_fit_oem(ubman):
fname = os.path.join(ubman.config.persistent_data_dir, 'vbe1.img')
ubman.run_command(f'host bind 0 {fname}')
ubman.run_command('ut -f bootstd vbe_test_abrec_oem_norun')
result = ubman.run_command('echo $?')
assert '0' == result
ubman.run_ut('bootstd', 'vbe_test_abrec_oem')