[PATCH 00/19] test: Fix pytest inter-test side effects
From: Simon Glass <sjg@chromium.org> Several pytest tests leave behind state that breaks later tests in the same session. This series fixes memory leaks, stale environment variables, dangling pointers, EFI log pollution and bootstage exhaustion, and adds a 'restart' marker for tests that restart U-Boot. It also adds --malloc-dump support for debugging heap issues. Simon Glass (19): sandbox: Pass exit code through sandbox_exit() sandbox: Use sandbox_exit() for -c command path sandbox: Add a command-line option to dump malloc state sandbox: Move malloc_dump_to_file() prototype to os.h bootstd: Fix memory leak in bootflow scanning test: py: Add a marker for tests that restart U-Boot test: py: Restore default log format after test_log_format() test: pxe: Fix dangling pointer in FDT env save/restore test: pxe: Fix ipappend test to clear board env var test: py: Fix PersistentFileHelperCtxMgr with stale .pyc test: common: Fix memory leak in malloc_fill_pool test test: py: Fix test_source breaking later tests test: boot: Reset EFI log at start of bootflow_efi test: py: Mark slow EFI selftest tests test: py: Add --malloc-dump support to pytest test: py: Send poweroff on every sandbox shutdown test: boot: Restore default addr env vars in extlinux tests bootstage: Add save/restore subcommands test: Save and restore bootstage record count in FIT tests arch/sandbox/cpu/cpu.c | 5 +- arch/sandbox/cpu/start.c | 13 ++- arch/sandbox/cpu/state.c | 3 + arch/sandbox/include/asm/state.h | 1 + arch/sandbox/include/asm/u-boot-sandbox.h | 2 +- arch/sandbox/lib/interrupts.c | 2 +- boot/Kconfig | 11 +++ boot/bootflow.c | 2 + cmd/bootstage.c | 28 ++++++ doc/develop/malloc.rst | 18 ++++ doc/develop/pytest/usage.rst | 20 ++++- doc/usage/cmd/bootstage.rst | 86 +++++++++++++++++++ doc/usage/index.rst | 1 + drivers/sysreset/sysreset_sandbox.c | 4 +- include/bootstage.h | 9 ++ include/malloc.h | 11 --- include/os.h | 11 +++ include/test/test.h | 2 + test/boot/bootflow.c | 17 +++- test/boot/pxe.c | 77 +++++++++++++---- test/cmd/bootstage.c | 24 ++++++ test/common/malloc.c | 16 ++-- test/py/conftest.py | 3 + test/py/console_sandbox.py | 35 ++++++++ test/py/pytest.ini | 1 + test/py/tests/test_distro.py | 4 + .../test_capsule_firmware_fit.py | 1 + .../test_capsule_firmware_raw.py | 1 + .../test_capsule_firmware_signed_fit.py | 1 + .../test_capsule_firmware_signed_raw.py | 1 + test/py/tests/test_efi_fit.py | 1 + .../py/tests/test_efi_secboot/test_authvar.py | 1 + test/py/tests/test_efi_secboot/test_signed.py | 1 + .../test_efi_secboot/test_signed_intca.py | 1 + .../tests/test_efi_secboot/test_unsigned.py | 1 + test/py/tests/test_efi_selftest.py | 9 ++ test/py/tests/test_eficonfig.py | 1 + test/py/tests/test_fit.py | 6 ++ test/py/tests/test_fpga.py | 1 + test/py/tests/test_fs/test_erofs.py | 2 +- .../test_fs/test_squashfs/test_sqfs_ls.py | 1 + test/py/tests/test_help.py | 2 + test/py/tests/test_log.py | 3 + test/py/tests/test_reset.py | 2 + test/py/tests/test_sandbox_exit.py | 4 + test/py/tests/test_saveenv.py | 1 + test/py/tests/test_source.py | 10 +++ test/py/tests/test_spl.py | 1 + test/py/tests/test_stackprotector.py | 1 + test/py/tests/test_upl.py | 1 + test/py/tests/test_vbe_vpl.py | 1 + test/py/tests/test_vboot.py | 1 + test/py/tests/test_vpl.py | 1 + test/py/utils.py | 31 ++++++- test/test-main.c | 5 ++ 55 files changed, 452 insertions(+), 47 deletions(-) create mode 100644 doc/usage/cmd/bootstage.rst -- 2.43.0 base-commit: 3d3ef6d6720718155fcf3c30fac6ea0f4dcba898 branch: mema
From: Simon Glass <sjg@chromium.org> sandbox_exit() always exits with code 0, discarding any error from the caller. The -c command path worked around this by calling os_exit() directly, bypassing state_uninit() and losing cleanup such as writing the malloc dump. Add an exit_code parameter to sandbox_exit() so callers can propagate errors while still getting a clean shutdown. Update all callers to pass 0 except sandbox_main_loop_init() which passes the command return value. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/sandbox/cpu/cpu.c | 5 ++--- arch/sandbox/include/asm/u-boot-sandbox.h | 2 +- arch/sandbox/lib/interrupts.c | 2 +- drivers/sysreset/sysreset_sandbox.c | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index d6177fbbd14..791af49464b 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -20,7 +20,7 @@ DECLARE_GLOBAL_DATA_PTR; -void __noreturn sandbox_exit(void) +void __noreturn sandbox_exit(int exit_code) { /* Do this here while it still has an effect */ os_fd_restore(); @@ -28,8 +28,7 @@ void __noreturn sandbox_exit(void) if (state_uninit()) os_exit(2); - /* This is considered normal termination for now */ - os_exit(0); + os_exit(exit_code); } /* delay x useconds */ diff --git a/arch/sandbox/include/asm/u-boot-sandbox.h b/arch/sandbox/include/asm/u-boot-sandbox.h index 941f35f9e69..60fb300f623 100644 --- a/arch/sandbox/include/asm/u-boot-sandbox.h +++ b/arch/sandbox/include/asm/u-boot-sandbox.h @@ -41,7 +41,7 @@ struct udevice; void sandbox_reset(void); /* Exit sandbox (quit U-Boot) */ -void __noreturn sandbox_exit(void); +void __noreturn sandbox_exit(int exit_code); /** * sandbox_init() - init sandbox diff --git a/arch/sandbox/lib/interrupts.c b/arch/sandbox/lib/interrupts.c index 3f6583e11f0..e9207b82032 100644 --- a/arch/sandbox/lib/interrupts.c +++ b/arch/sandbox/lib/interrupts.c @@ -53,6 +53,6 @@ void os_signal_action(int sig, unsigned long pc) printf("resetting ...\n\n"); sandbox_reset(); } else { - sandbox_exit(); + sandbox_exit(0); } } diff --git a/drivers/sysreset/sysreset_sandbox.c b/drivers/sysreset/sysreset_sandbox.c index d126fad0372..6739ecbd92f 100644 --- a/drivers/sysreset/sysreset_sandbox.c +++ b/drivers/sysreset/sysreset_sandbox.c @@ -63,13 +63,13 @@ static int sandbox_sysreset_request(struct udevice *dev, enum sysreset_t type) state->last_sysreset = type; if (!state->sysreset_allowed[type]) return -EACCES; - sandbox_exit(); + sandbox_exit(0); case SYSRESET_POWER: case SYSRESET_HOT: case SYSRESET_TO_FIRMWARE_UI: if (!state->sysreset_allowed[type]) return -EACCES; - sandbox_exit(); + sandbox_exit(0); default: return -EPROTONOSUPPORT; } -- 2.43.0
From: Simon Glass <sjg@chromium.org> The -c command path calls os_exit() directly, bypassing state_uninit(). This means cleanup actions like writing the malloc dump are skipped. Use sandbox_exit() instead, which calls state_uninit() before exiting. Now that sandbox_exit() accepts an exit code, the command return value is still propagated correctly. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/sandbox/cpu/start.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index b9f48376d22..7c9f0a99654 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -158,7 +158,7 @@ int sandbox_main_loop_init(void) 0); #endif if (!state->interactive) - os_exit(retval); + sandbox_exit(retval); } return 0; @@ -583,7 +583,7 @@ void __efi_runtime EFIAPI efi_reset_system( unsigned long data_size, void *reset_data) { if (reset_type == EFI_RESET_SHUTDOWN) - sandbox_exit(); + sandbox_exit(0); else sandbox_reset(); } -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a --malloc_dump option that writes the heap dump to a file on exit. This is useful for debugging memory leaks in test sessions. The dump is written from state_uninit() which runs on clean shutdown (poweroff, reset). The prototype for malloc_dump_to_file() comes from os.h, avoiding the asm/malloc.h conflict. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/sandbox/cpu/start.c | 9 +++++++++ arch/sandbox/cpu/state.c | 3 +++ arch/sandbox/include/asm/state.h | 1 + 3 files changed, 13 insertions(+) diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 7c9f0a99654..caa9b61dcd9 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -553,6 +553,15 @@ static int sandbox_cmdline_cb_pager_bypass(struct sandbox_state *state, SANDBOX_CMDLINE_OPT_SHORT(pager_bypass, 'P', 0, "Enable pager bypass mode"); +static int sandbox_cmdline_cb_malloc_dump(struct sandbox_state *state, + const char *arg) +{ + state->malloc_dump_fname = arg; + + return 0; +} +SANDBOX_CMDLINE_OPT(malloc_dump, 1, "Write malloc dump to file on exit"); + static int sandbox_cmdline_cb_bind(struct sandbox_state *state, const char *arg) { if (state->num_binds >= SB_MAX_BINDS) { diff --git a/arch/sandbox/cpu/state.c b/arch/sandbox/cpu/state.c index e492855755b..fd31d93c500 100644 --- a/arch/sandbox/cpu/state.c +++ b/arch/sandbox/cpu/state.c @@ -544,6 +544,9 @@ int state_uninit(void) if (state->jumped_fname) os_unlink(state->jumped_fname); + if (state->malloc_dump_fname) + malloc_dump_to_file(state->malloc_dump_fname); + /* Disable tracing before unmapping RAM */ if (IS_ENABLED(CONFIG_TRACE)) trace_set_enabled(0); diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index 001d780aec8..8707d798d8d 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -184,6 +184,7 @@ struct sandbox_state { int video_test; /* ms to wait before next assert */ const char *video_frames_dir; /* Directory to write video frames */ int video_frame_count; /* Number of frames written */ + const char *malloc_dump_fname; /* File to write malloc dump on exit */ /* Pointer to information for each SPI bus/cs */ struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS] -- 2.43.0
From: Simon Glass <sjg@chromium.org> This function is only available in sandbox builds and uses os_open() and os_write(). Move its declaration from malloc.h (which conflicts with asm/malloc.h in sandbox files) to os.h where it fits naturally alongside other sandbox host-file operations. This allows sandbox code that includes asm/malloc.h to call malloc_dump_to_file() via os.h without a forward declaration. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/malloc.h | 11 ----------- include/os.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/malloc.h b/include/malloc.h index 3deb90b2a0b..610289f3a6c 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -712,17 +712,6 @@ void malloc_disable_testing(void); */ void malloc_dump(void); -/** - * malloc_dump_to_file() - Write heap dump to a host file - * - * @fname: Path to the output file on the host filesystem - * Return: 0 on success, negative error code on failure - * - * This is only available in sandbox builds. It writes the same information - * as malloc_dump() but to a file instead of the console. - */ -int malloc_dump_to_file(const char *fname); - /** * malloc_log_start() - Start logging malloc traffic * diff --git a/include/os.h b/include/os.h index bc556f2195f..41e3022657c 100644 --- a/include/os.h +++ b/include/os.h @@ -546,6 +546,17 @@ int os_map_file(const char *pathname, int os_flags, void **bufp, int *sizep); */ int os_unmap(void *buf, int size); +/** + * malloc_dump_to_file() - Write heap dump to a host file + * + * This is only available in sandbox builds. It writes the same information + * as malloc_dump() but to a file instead of the console. + * + * @fname: Path to the output file on the host filesystem + * Return: 0 on success, negative error code on failure + */ +int malloc_dump_to_file(const char *fname); + /* * os_find_text_base() - Find the text section in this running process * -- 2.43.0
From: Simon Glass <sjg@chromium.org> When bootflow_check() fails during scanning, bflow->name and other fields allocated by bootdev_find_in_blk() are not freed before the next iteration overwrites them. This causes a memory leak of about 1.2 MB across ~7,280 iterations in a typical sandbox test run. Add bootflow_free() calls in bootflow_scan_first() and bootflow_scan_next() to release the failed bootflow's resources before retrying. Place the free after the BOOTFLOWIF_ALL early return so that callers requesting all bootflows still receive the populated bflow. The subsequent bootflow_check() reinitialises the bflow via bootflow_init(), so there is no use-after-free risk. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/boot/bootflow.c b/boot/bootflow.c index 0511d7f6cb8..befe507af96 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -660,6 +660,7 @@ int bootflow_scan_first(struct udevice *dev, const char *label, return log_msg_ret("all", ret); } iter->err = ret; + bootflow_free(bflow); ret = bootflow_scan_next(iter, bflow); if (ret) return log_msg_ret("get", ret); @@ -693,6 +694,7 @@ int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow) if (iter->flags & BOOTFLOWIF_ALL) return log_msg_ret("all", ret); } + bootflow_free(bflow); } else { log_debug("incr failed, err=%d\n", ret); iter->err = ret; -- 2.43.0
From: Simon Glass <sjg@chromium.org> Some pytest tests restart the U-Boot process during execution via ubman.restart_uboot() or ubman.restart_uboot_with_flags() This can be undesirable when debugging the pytests, since you have to reconnect gdb many times. Add a 'restart' pytest marker to all such tests, allowing them to be excluded with some like -k 'not restart' The marker is applied to individual test functions or to test classes where all methods perform a restart. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/pytest/usage.rst | 20 ++++++++++++++++--- test/py/pytest.ini | 1 + test/py/tests/test_distro.py | 4 ++++ .../test_capsule_firmware_fit.py | 1 + .../test_capsule_firmware_raw.py | 1 + .../test_capsule_firmware_signed_fit.py | 1 + .../test_capsule_firmware_signed_raw.py | 1 + test/py/tests/test_efi_fit.py | 1 + .../py/tests/test_efi_secboot/test_authvar.py | 1 + test/py/tests/test_efi_secboot/test_signed.py | 1 + .../test_efi_secboot/test_signed_intca.py | 1 + .../tests/test_efi_secboot/test_unsigned.py | 1 + test/py/tests/test_efi_selftest.py | 6 ++++++ test/py/tests/test_eficonfig.py | 1 + test/py/tests/test_fpga.py | 1 + test/py/tests/test_fs/test_erofs.py | 2 +- .../test_fs/test_squashfs/test_sqfs_ls.py | 1 + test/py/tests/test_help.py | 2 ++ test/py/tests/test_log.py | 1 + test/py/tests/test_reset.py | 2 ++ test/py/tests/test_sandbox_exit.py | 4 ++++ test/py/tests/test_saveenv.py | 1 + test/py/tests/test_spl.py | 1 + test/py/tests/test_stackprotector.py | 1 + test/py/tests/test_upl.py | 1 + test/py/tests/test_vbe_vpl.py | 1 + test/py/tests/test_vboot.py | 1 + test/py/tests/test_vpl.py | 1 + 28 files changed, 57 insertions(+), 4 deletions(-) diff --git a/doc/develop/pytest/usage.rst b/doc/develop/pytest/usage.rst index 9e9cc38c9b2..087acded0d8 100644 --- a/doc/develop/pytest/usage.rst +++ b/doc/develop/pytest/usage.rst @@ -236,9 +236,10 @@ protocol, or any graphical wrapper around gdb. Some tests deliberately cause the sandbox process to exit, e.g. to test the reset command, or sandbox's CTRL-C handling. When this happens, you will need -to attach the debugger to the new sandbox instance. If these tests are not -relevant to your debugging session, you can skip them using pytest's ``-k`` -command-line option; see the next section. +to attach the debugger to the new sandbox instance. These tests are marked +with the ``restart`` pytest marker, so you can skip them all with:: + + test/py/test.py -B sandbox --gdbserver localhost:1234 -m "not restart" Command-line options -------------------- @@ -606,3 +607,16 @@ set), since the locally launched QEMU is not available in that environment: .. code-block:: python @pytest.mark.localqemu + +The restart marker indicates that a test restarts the U-Boot process +during execution, e.g. via ``ubman.restart_uboot()`` or +``ubman.restart_uboot_with_flags()``. This can be used to skip these tests +when a restart is not desirable: + +.. code-block:: python + + @pytest.mark.restart + +To exclude these tests from a test run:: + + test/py/test.py -B sandbox -m "not restart" diff --git a/test/py/pytest.ini b/test/py/pytest.ini index bebb22cd3d6..a3a8aaf74b5 100644 --- a/test/py/pytest.ini +++ b/test/py/pytest.ini @@ -15,3 +15,4 @@ markers = singlethread: Cannot run in parallel role: U-Boot: Indicates the lab 'role' which can execute this test localqemu: U-Boot: Test launches its own QEMU, skip in lab mode + restart: U-Boot: Test restarts U-Boot during execution diff --git a/test/py/tests/test_distro.py b/test/py/tests/test_distro.py index bc3b511af63..0a0ab8269cc 100644 --- a/test/py/tests/test_distro.py +++ b/test/py/tests/test_distro.py @@ -10,6 +10,7 @@ CONSOLE = 'earlycon=uart8250,io,0x3f8 console=uart8250,io,0x3f8' @pytest.mark.boardspec('qemu-x86_64') @pytest.mark.role('qemu-x86_64') +@pytest.mark.restart def test_distro(ubman): """Test booting into Ubuntu 24.04""" with ubman.log.section('boot'): @@ -64,6 +65,7 @@ def test_distro(ubman): @pytest.mark.boardspec('colibri-imx8x') @pytest.mark.role('colibrimx8') +@pytest.mark.restart def test_distro_script(ubman): """Test that a selected board can boot into Llinux using a script""" with ubman.log.section('boot'): @@ -79,6 +81,7 @@ def test_distro_script(ubman): @pytest.mark.boardspec('efi-arm_app64') @pytest.mark.role('efi-aarch64') +@pytest.mark.restart def test_distro_arm_app_extlinux(ubman): """Test that the ARM EFI app can boot into Ubuntu 25.04 via extlinux""" with ubman.log.section('boot'): @@ -98,6 +101,7 @@ def test_distro_arm_app_extlinux(ubman): @pytest.mark.boardspec('efi-arm_app64') @pytest.mark.role('efi-aarch64') +@pytest.mark.restart def test_distro_arm_app_efi(ubman): """Test that the ARM EFI app can boot into Ubuntu 25.04 via EFI""" with ubman.log.section('boot'): diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py index 016274533cd..338554291c1 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py @@ -28,6 +28,7 @@ from capsule_common import ( @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.buildconfigspec('cmd_sf') @pytest.mark.slow +@pytest.mark.restart class TestEfiCapsuleFirmwareFit(): """Test capsule-on-disk firmware update for FIT images """ diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py index b8cb483b380..42fb1ae9954 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py @@ -29,6 +29,7 @@ from capsule_common import ( @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.buildconfigspec('cmd_sf') @pytest.mark.slow +@pytest.mark.restart class TestEfiCapsuleFirmwareRaw: """ Tests verifying capsule-on-disk firmware update for raw images """ diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py index 29545c5080a..f0ad67e2468 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py @@ -31,6 +31,7 @@ from capsule_common import ( @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.buildconfigspec('cmd_sf') @pytest.mark.slow +@pytest.mark.restart class TestEfiCapsuleFirmwareSignedFit(): """Capsule-on-disk firmware update test """ diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py index a500c499bb9..8456f39454f 100644 --- a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py @@ -29,6 +29,7 @@ from capsule_common import ( @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.buildconfigspec('cmd_sf') @pytest.mark.slow +@pytest.mark.restart class TestEfiCapsuleFirmwareSignedRaw(): """Firmware Update (Signed capsule with raw images) Test """ diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py index 63ee8e6cef2..efa9ebf5adf 100644 --- a/test/py/tests/test_efi_fit.py +++ b/test/py/tests/test_efi_fit.py @@ -159,6 +159,7 @@ FDT_DATA = ''' @pytest.mark.buildconfigspec('fit') @pytest.mark.notbuildconfigspec('generate_acpi_table') @pytest.mark.requiredtool('dtc') +@pytest.mark.restart def test_efi_fit_launch(ubman): """Test handling of UEFI binaries inside FIT images. diff --git a/test/py/tests/test_efi_secboot/test_authvar.py b/test/py/tests/test_efi_secboot/test_authvar.py index 3750f302dba..03caec65fec 100644 --- a/test/py/tests/test_efi_secboot/test_authvar.py +++ b/test/py/tests/test_efi_secboot/test_authvar.py @@ -16,6 +16,7 @@ import pytest @pytest.mark.buildconfigspec('cmd_fat') @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.slow +@pytest.mark.restart class TestEfiAuthVar(object): def test_efi_var_auth1(self, ubman, efi_boot_env): """ diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py index e8aaef7090c..78ed2a08fe2 100644 --- a/test/py/tests/test_efi_secboot/test_signed.py +++ b/test/py/tests/test_efi_secboot/test_signed.py @@ -17,6 +17,7 @@ import pytest @pytest.mark.buildconfigspec('cmd_fat') @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.slow +@pytest.mark.restart class TestEfiSignedImage(object): def test_efi_signed_image_auth1(self, ubman, efi_boot_env): """ diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py index 58f7be03b8b..6e0d7800305 100644 --- a/test/py/tests/test_efi_secboot/test_signed_intca.py +++ b/test/py/tests/test_efi_secboot/test_signed_intca.py @@ -19,6 +19,7 @@ import pytest @pytest.mark.buildconfigspec('cmd_fat') @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.slow +@pytest.mark.restart class TestEfiSignedImageIntca(object): def test_efi_signed_image_intca1(self, ubman, efi_boot_env_intca): """ diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py index bd6e1b2dadd..f5624806d88 100644 --- a/test/py/tests/test_efi_secboot/test_unsigned.py +++ b/test/py/tests/test_efi_secboot/test_unsigned.py @@ -17,6 +17,7 @@ import pytest @pytest.mark.buildconfigspec('cmd_fat') @pytest.mark.buildconfigspec('cmd_nvedit_efi') @pytest.mark.slow +@pytest.mark.restart class TestEfiUnsignedImage(object): def test_efi_unsigned_image_auth1(self, ubman, efi_boot_env): """ diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 5f29157ae15..2a959ae3406 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -7,6 +7,7 @@ import pytest @pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.restart def test_efi_selftest_base(ubman): """Run UEFI unit tests @@ -24,6 +25,7 @@ def test_efi_selftest_base(ubman): @pytest.mark.buildconfigspec('hush_parser') @pytest.mark.buildconfigspec('of_control') @pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.restart def test_efi_selftest_device_tree(ubman): """Test the device tree support in the UEFI sub-system @@ -44,6 +46,7 @@ def test_efi_selftest_device_tree(ubman): ubman.restart_uboot() @pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.restart def test_efi_selftest_watchdog_reboot(ubman): """Test the watchdog timer @@ -61,6 +64,7 @@ def test_efi_selftest_watchdog_reboot(ubman): ubman.run_command(cmd='', send_nl=False, wait_for_reboot=True) @pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.restart def test_efi_selftest_text_input(ubman): """Test the EFI_SIMPLE_TEXT_INPUT_PROTOCOL @@ -116,6 +120,7 @@ def test_efi_selftest_text_input(ubman): ubman.restart_uboot() @pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.restart def test_efi_selftest_text_input_ex(ubman): """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL @@ -180,6 +185,7 @@ def test_efi_selftest_text_input_ex(ubman): @pytest.mark.buildconfigspec('cmd_bootefi_selftest') @pytest.mark.buildconfigspec('efi_tcg2_protocol') @pytest.mark.notbuildconfigspec('sandbox') +@pytest.mark.restart def test_efi_selftest_tcg2(ubman): """Test the EFI_TCG2 PROTOCOL diff --git a/test/py/tests/test_eficonfig.py b/test/py/tests/test_eficonfig.py index 20ea8d9f28f..171c8ec6cc8 100644 --- a/test/py/tests/test_eficonfig.py +++ b/test/py/tests/test_eficonfig.py @@ -12,6 +12,7 @@ from tests import fs_helper @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_eficonfig') @pytest.mark.buildconfigspec('cmd_bootefi_bootmgr') +@pytest.mark.restart def test_efi_eficonfig(ubman): def prepare_image(u_boot_config): diff --git a/test/py/tests/test_fpga.py b/test/py/tests/test_fpga.py index 299a8653f74..84998dc2de2 100644 --- a/test/py/tests/test_fpga.py +++ b/test/py/tests/test_fpga.py @@ -97,6 +97,7 @@ expected_usage = 'fpga - loadable FPGA image support' @pytest.mark.xfail @pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.restart def test_fpga_fail(ubman): # Test non valid fpga subcommand expected = 'fpga: non existing command' diff --git a/test/py/tests/test_fs/test_erofs.py b/test/py/tests/test_fs/test_erofs.py index a2bb6b505f2..5150cb5cb1f 100644 --- a/test/py/tests/test_fs/test_erofs.py +++ b/test/py/tests/test_fs/test_erofs.py @@ -189,7 +189,7 @@ def erofs_run_all_tests(ubman): @pytest.mark.buildconfigspec('fs_erofs') @pytest.mark.requiredtool('mkfs.erofs') @pytest.mark.requiredtool('md5sum') - +@pytest.mark.restart def test_erofs(ubman): """ Executes the erofs test suite. diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py index adda3b98cda..50522c31d02 100644 --- a/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py +++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py @@ -106,6 +106,7 @@ def sqfs_run_all_ls_tests(ubman): @pytest.mark.buildconfigspec('fs_squashfs') @pytest.mark.requiredtool('mksquashfs') @pytest.mark.singlethread +@pytest.mark.restart def test_sqfs_ls(ubman): """ Executes the sqfsls test suite. diff --git a/test/py/tests/test_help.py b/test/py/tests/test_help.py index afb57201ba3..ff8de8e8124 100644 --- a/test/py/tests/test_help.py +++ b/test/py/tests/test_help.py @@ -15,6 +15,7 @@ def test_help(ubman): assert lines.splitlines()[0] == "? - alias for 'help'" @pytest.mark.boardspec('sandbox') +@pytest.mark.restart def test_help_no_devicetree(ubman): try: ubman.restart_uboot_with_flags([], use_dtb=False) @@ -26,6 +27,7 @@ def test_help_no_devicetree(ubman): ubman.restart_uboot() @pytest.mark.boardspec('sandbox_vpl') +@pytest.mark.restart def test_vpl_help(ubman): try: ubman.restart_uboot() diff --git a/test/py/tests/test_log.py b/test/py/tests/test_log.py index 4558b037e2a..3fc61e258a0 100644 --- a/test/py/tests/test_log.py +++ b/test/py/tests/test_log.py @@ -41,6 +41,7 @@ def test_log_format(ubman): @pytest.mark.buildconfigspec('debug_uart') @pytest.mark.boardspec('sandbox') +@pytest.mark.restart def test_log_dropped(ubman): """Test dropped 'log' message when debug_uart is activated""" diff --git a/test/py/tests/test_reset.py b/test/py/tests/test_reset.py index af079a70664..4423baee9c3 100644 --- a/test/py/tests/test_reset.py +++ b/test/py/tests/test_reset.py @@ -41,6 +41,7 @@ def setup_reset_env(ubman): pytest.skip('skipping reset test due to jtag bootmode') @pytest.mark.buildconfigspec('hush_parser') +@pytest.mark.restart def test_reset(ubman): """Test the reset command in non-JTAG bootmode. It does COLD reset, which resets CPU, DDR and peripherals @@ -52,6 +53,7 @@ def test_reset(ubman): test_000_version.test_version(ubman) @pytest.mark.buildconfigspec('hush_parser') +@pytest.mark.restart def test_reset_w(ubman): """Test the reset -w command in non-JTAG bootmode. It does WARM reset, which resets CPU but keep DDR/peripherals active. diff --git a/test/py/tests/test_sandbox_exit.py b/test/py/tests/test_sandbox_exit.py index 849fd477941..eb95558f09e 100644 --- a/test/py/tests/test_sandbox_exit.py +++ b/test/py/tests/test_sandbox_exit.py @@ -7,6 +7,7 @@ import signal @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('sysreset_cmd_poweroff') +@pytest.mark.restart def test_poweroff(ubman): """Test that the "poweroff" command exits sandbox process.""" @@ -14,6 +15,7 @@ def test_poweroff(ubman): assert(ubman.validate_exited()) @pytest.mark.boardspec('sandbox') +@pytest.mark.restart def test_ctrl_c(ubman): """Test that sending SIGINT to sandbox causes it to exit.""" @@ -23,6 +25,7 @@ def test_ctrl_c(ubman): @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_exception') @pytest.mark.buildconfigspec('sandbox_crash_reset') +@pytest.mark.restart def test_exception_reset(ubman): """Test that SIGILL causes a reset.""" @@ -38,6 +41,7 @@ def test_exception_reset(ubman): @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_exception') @pytest.mark.notbuildconfigspec('sandbox_crash_reset') +@pytest.mark.restart def test_exception_exit(ubman): """Test that SIGILL causes a reset.""" diff --git a/test/py/tests/test_saveenv.py b/test/py/tests/test_saveenv.py index 555284906da..0fb8dcaf1d8 100644 --- a/test/py/tests/test_saveenv.py +++ b/test/py/tests/test_saveenv.py @@ -73,6 +73,7 @@ def set_env(ubman, var_name, var_value): @pytest.mark.buildconfigspec('cmd_saveenv') @pytest.mark.buildconfigspec('hush_parser') +@pytest.mark.restart def test_saveenv(ubman): """Test the saveenv command in non-JTAG bootmode. It saves the U-Boot environment in persistent storage. diff --git a/test/py/tests/test_spl.py b/test/py/tests/test_spl.py index 48407399039..b3a2de9801c 100644 --- a/test/py/tests/test_spl.py +++ b/test/py/tests/test_spl.py @@ -15,6 +15,7 @@ def test_ut_spl_init(ubman): with open(fn, 'wb') as fh: fh.write(data) +@pytest.mark.restart def test_spl(ubman, ut_spl_subtest): """Execute a "ut" subtest. diff --git a/test/py/tests/test_stackprotector.py b/test/py/tests/test_stackprotector.py index a7e20d6307c..2a99ba7b9c9 100644 --- a/test/py/tests/test_stackprotector.py +++ b/test/py/tests/test_stackprotector.py @@ -6,6 +6,7 @@ import signal @pytest.mark.buildconfigspec('cmd_stackprotector_test') @pytest.mark.notbuildconfigspec('asan') +@pytest.mark.restart def test_stackprotector(ubman): """Test that the stackprotector function works.""" diff --git a/test/py/tests/test_upl.py b/test/py/tests/test_upl.py index f2b69078cf1..ea921e0c005 100644 --- a/test/py/tests/test_upl.py +++ b/test/py/tests/test_upl.py @@ -9,6 +9,7 @@ import pytest import utils @pytest.mark.boardspec('sandbox_vpl') +@pytest.mark.restart def test_upl_handoff(ubman): """Test of UPL handoff diff --git a/test/py/tests/test_vbe_vpl.py b/test/py/tests/test_vbe_vpl.py index f011b034f63..6ecd03086cc 100644 --- a/test/py/tests/test_vbe_vpl.py +++ b/test/py/tests/test_vbe_vpl.py @@ -10,6 +10,7 @@ import utils @pytest.mark.boardspec('sandbox_vpl') @pytest.mark.requiredtool('dtc') +@pytest.mark.restart def test_vbe_vpl(ubman): #cmd = [ubman.config.build_dir + fname, '-v'] ram = os.path.join(ubman.config.build_dir, 'ram.bin') diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py index a2545ab3b60..820c8059be3 100644 --- a/test/py/tests/test_vboot.py +++ b/test/py/tests/test_vboot.py @@ -141,6 +141,7 @@ TESTDATA += [pytest.param(*v, marks=pytest.mark.slow) for v in TESTDATA_IN[1:]] @pytest.mark.requiredtool('fdtget') @pytest.mark.requiredtool('fdtput') @pytest.mark.requiredtool('openssl') +@pytest.mark.restart @pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg,global_sign", TESTDATA) def test_vboot_base(ubman, name, sha_algo, padding, sign_options, required, diff --git a/test/py/tests/test_vpl.py b/test/py/tests/test_vpl.py index a269c7c262e..2ea1f0c399d 100644 --- a/test/py/tests/test_vpl.py +++ b/test/py/tests/test_vpl.py @@ -5,6 +5,7 @@ import os.path import pytest +@pytest.mark.restart def test_vpl(ubman, ut_vpl_subtest): """Execute a "ut" subtest. -- 2.43.0
From: Simon Glass <sjg@chromium.org> test_log_format() changes the log format to various values but does not restore the default at the end. This can affect later tests that depend on the default format. Add 'log format default' at the end of the test to restore it. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/tests/test_log.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/py/tests/test_log.py b/test/py/tests/test_log.py index 3fc61e258a0..c1b7e1d6b1b 100644 --- a/test/py/tests/test_log.py +++ b/test/py/tests/test_log.py @@ -39,6 +39,8 @@ def test_log_format(ubman): run_with_format('lm', 'NOTICE. msg') run_with_format('m', 'msg') + ubman.run_command('log format default') + @pytest.mark.buildconfigspec('debug_uart') @pytest.mark.boardspec('sandbox') @pytest.mark.restart -- 2.43.0
From: Simon Glass <sjg@chromium.org> pxe_test_fdt_fallback() and pxe_test_alloc_norun() save the values of fdt_addr and fdtcontroladdr using env_get(), which returns a pointer into the environment hash table. When the variable is then cleared with env_set(name, NULL), the hash entry is freed, leaving the saved pointer dangling. Restoring from this pointer writes corrupt data, breaking later tests like bootflow_efi that depend on fdtcontroladdr Fix by using strdup() to save env values. Extract the save/restore logic into pxe_save_and_clear_fdt_env() and pxe_restore_fdt_env() helpers shared by both tests. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/boot/pxe.c | 70 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/test/boot/pxe.c b/test/boot/pxe.c index cd831807b94..e691ceebc61 100644 --- a/test/boot/pxe.c +++ b/test/boot/pxe.c @@ -35,6 +35,56 @@ #define PXE_ARG_FS_IMAGE 0 /* Path to filesystem image */ #define PXE_ARG_CFG_PATH 1 /* Path to config file within image */ +/** + * struct pxe_env_save - saved FDT-related environment variables + * + * @fdt_addr: Saved fdt_addr value (strdup'd, or empty string if unset) + * @fdtcontroladdr: Saved fdtcontroladdr value (strdup'd, or empty) + */ +struct pxe_env_save { + char *fdt_addr; + char *fdtcontroladdr; +}; + +/** + * pxe_save_and_clear_fdt_env() - save and clear FDT env vars + * + * Saves the current values of fdt_addr and fdtcontroladdr, then clears + * them. The values are strdup'd since env_set() frees the old value, + * which would leave saved env_get() pointers dangling. + * + * @save: Struct to save into + * Return: 0 on success, -ENOMEM on allocation failure + */ +static int pxe_save_and_clear_fdt_env(struct pxe_env_save *save) +{ + save->fdt_addr = strdup(env_get("fdt_addr") ?: ""); + save->fdtcontroladdr = strdup(env_get("fdtcontroladdr") ?: ""); + if (!save->fdt_addr || !save->fdtcontroladdr) { + free(save->fdt_addr); + free(save->fdtcontroladdr); + return -ENOMEM; + } + env_set("fdt_addr", NULL); + env_set("fdtcontroladdr", NULL); + + return 0; +} + +/** + * pxe_restore_fdt_env() - restore FDT env vars from saved state + * + * @save: Struct with saved values (freed after restore) + */ +static void pxe_restore_fdt_env(struct pxe_env_save *save) +{ + env_set("fdt_addr", *save->fdt_addr ? save->fdt_addr : NULL); + env_set("fdtcontroladdr", + *save->fdtcontroladdr ? save->fdtcontroladdr : NULL); + free(save->fdt_addr); + free(save->fdtcontroladdr); +} + /* Memory address for loading files */ #define PXE_LOAD_ADDR 0x01000000 #define PXE_KERNEL_ADDR 0x02000000 @@ -967,8 +1017,8 @@ PXE_TEST_ARGS(pxe_test_ipappend_norun, UTF_CONSOLE | UTF_MANUAL | UTF_ETH_BOOTDE */ static int pxe_test_fdt_fallback(struct unit_test_state *uts) { - const char *orig_fdt_addr, *orig_fdtcontroladdr; ulong kern_addr = 0x1000000; + struct pxe_env_save save; struct pxe_label label; void *kern_buf; @@ -980,10 +1030,7 @@ static int pxe_test_fdt_fallback(struct unit_test_state *uts) memset(&label, '\0', sizeof(label)); /* Save and clear env vars (fdtcontroladdr is set by U-Boot) */ - orig_fdt_addr = env_get("fdt_addr"); - orig_fdtcontroladdr = env_get("fdtcontroladdr"); - ut_assertok(env_set("fdt_addr", NULL)); - ut_assertok(env_set("fdtcontroladdr", NULL)); + ut_assertok(pxe_save_and_clear_fdt_env(&save)); /* Test 1: No fallback env vars set - should return NULL */ ut_assertnull(pxe_get_fdt_fallback(&label, kern_addr)); @@ -1001,8 +1048,7 @@ static int pxe_test_fdt_fallback(struct unit_test_state *uts) ut_asserteq_str("3000000", pxe_get_fdt_fallback(&label, kern_addr)); /* Restore env vars */ - ut_assertok(env_set("fdt_addr", orig_fdt_addr)); - ut_assertok(env_set("fdtcontroladdr", orig_fdtcontroladdr)); + pxe_restore_fdt_env(&save); return 0; } @@ -1144,10 +1190,10 @@ static int pxe_alloc_getfile(struct pxe_context *ctx, const char *file_path, */ static int pxe_test_alloc_norun(struct unit_test_state *uts) { - const char *orig_fdt_addr, *orig_fdtcontroladdr; const char *fs_image = ut_str(PXE_ARG_FS_IMAGE); const char *cfg_path = ut_str(PXE_ARG_CFG_PATH); struct pxe_alloc_info info; + struct pxe_env_save save; struct pxe_context ctx; ulong addr; int ret; @@ -1162,10 +1208,7 @@ static int pxe_test_alloc_norun(struct unit_test_state *uts) ut_assertok(run_commandf("host bind 0 %s", fs_image)); /* Save and clear FDT fallback env vars (fdtcontroladdr is set at boot) */ - orig_fdt_addr = env_get("fdt_addr"); - orig_fdtcontroladdr = env_get("fdtcontroladdr"); - ut_assertok(env_set("fdt_addr", NULL)); - ut_assertok(env_set("fdtcontroladdr", NULL)); + ut_assertok(pxe_save_and_clear_fdt_env(&save)); /* Ensure address env vars are NOT set */ ut_assertok(env_set("kernel_addr_r", NULL)); @@ -1238,8 +1281,7 @@ static int pxe_test_alloc_norun(struct unit_test_state *uts) pxe_menu_uninit(ctx.cfg); pxe_destroy_ctx(&ctx); ut_assertok(env_set("pxe_timeout", NULL)); - ut_assertok(env_set("fdt_addr", orig_fdt_addr)); - ut_assertok(env_set("fdtcontroladdr", orig_fdtcontroladdr)); + pxe_restore_fdt_env(&save); return 0; } -- 2.43.0
From: Simon Glass <sjg@chromium.org> pxe_test_ipappend_norun() clears fdtfile to test the fdtdir fallback path, but the fallback code in pxe_utils.c constructs the DTB filename from the board env var (which is 'sandbox'). This causes the test to try /dtb/sandbox.dtb instead of /dtb/.dtb Clear the board env var alongside fdtfile, and restore it in the cleanup section. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/boot/pxe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/boot/pxe.c b/test/boot/pxe.c index e691ceebc61..237481d5ee8 100644 --- a/test/boot/pxe.c +++ b/test/boot/pxe.c @@ -958,8 +958,12 @@ static int pxe_test_ipappend_norun(struct unit_test_state *uts) ut_assertok(env_set("gatewayip", "192.168.1.254")); ut_assertok(env_set("netmask", "255.255.255.0")); - /* Clear fdtfile to ensure rescue label's fdtdir tries /dtb/.dtb */ + /* + * Clear fdtfile and board so the rescue label's fdtdir tries + * /dtb/.dtb (the fallback constructs soc-board.dtb from env) + */ ut_assertok(env_set("fdtfile", NULL)); + ut_assertok(env_set("board", NULL)); /* Override to boot the rescue label which has ipappend=3 */ ut_assertok(env_set("pxe_label_override", "rescue")); @@ -996,6 +1000,7 @@ static int pxe_test_ipappend_norun(struct unit_test_state *uts) ut_assert_console_end(); /* Clean up */ + env_set("board", "sandbox"); env_set("ipaddr", NULL); env_set("serverip", NULL); env_set("gatewayip", NULL); -- 2.43.0
From: Simon Glass <sjg@chromium.org> inspect.getmodule() returns None when the .pyc file is compiled with a different source path, e.g. when running tests inside a container after building outside it, or vice versa. This causes an AttributeError on module.__file__ in PersistentFileHelperCtxMgr Fall back to the caller's filename from the stack frame when the module cannot be resolved. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/utils.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/py/utils.py b/test/py/utils.py index 7812b2a201b..083a3feddc2 100644 --- a/test/py/utils.py +++ b/test/py/utils.py @@ -303,8 +303,17 @@ class PersistentFileHelperCtxMgr(object): def __enter__(self): frame = inspect.stack()[1] module = inspect.getmodule(frame[0]) - self.module_filename = module.__file__ - self.module_timestamp = os.path.getmtime(self.module_filename) + if module is not None: + self.module_filename = module.__file__ + else: + self.module_filename = frame[1] + + if os.path.exists(self.module_filename): + self.module_timestamp = os.path.getmtime(self.module_filename) + else: + # The .pyc was compiled with a different source path + # (e.g. inside/outside a container). Skip staleness check. + self.module_timestamp = 0 if os.path.exists(self.filename): filename_timestamp = os.path.getmtime(self.filename) -- 2.43.0
From: Simon Glass <sjg@chromium.org> When common_test_malloc_fill_pool() fills the heap and then fails an assertion (e.g. because earlier tests leaked memory, reducing the available pool), it returns early without freeing the allocations. This leaks ~120 MB — the entire malloc pool — causing all subsequent tests that need malloc to fail. Free all allocations before checking the peak, so that a test failure does not leak the entire pool and break all subsequent tests. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/common/malloc.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test/common/malloc.c b/test/common/malloc.c index af1a7326272..c4764a51f6b 100644 --- a/test/common/malloc.c +++ b/test/common/malloc.c @@ -616,18 +616,24 @@ static int common_test_malloc_fill_pool(struct unit_test_state *uts) ptr_table_size); /* - * Should have allocated most of the pool - if we can't allocate - * 1MB, then at most 1MB is available, so we must have allocated - * at least (pool_size - 1MB) + * Should have allocated most of the pool - if we can't allocate 1MB, + * then at most 1MB is available, so we must have allocated at least + * (pool_size - 1MB). Save the peak before freeing so an assertion + * failure does not leak the entire pool. */ ut_assert(count > 0); ut_assert(count < ptr_table_size / sizeof(void *)); - ut_assert(get_alloced_size() >= TOTAL_MALLOC_LEN - SZ_1M); + alloc_size = get_alloced_size(); - /* Free all allocations */ + /* + * Free all allocations before checking the peak, so that a failure does + * not leak the entire pool and break later tests + */ for (i = 0; i < count; i++) free(ptrs[i]); + ut_assert(alloc_size >= TOTAL_MALLOC_LEN - SZ_1M); + /* Should be back to starting state */ ut_asserteq(before, get_alloced_size()); -- 2.43.0
From: Simon Glass <sjg@chromium.org> The source command's ':' and '#' variants use the global image_load_addr variable, not the loadaddr env var. Prior tests (e.g. PXE) can change image_load_addr via load commands, causing test_source to look for the FIT at the wrong address. Fix this by explicitly setting loadaddr before loading the FIT, which triggers the env callback that synchronises image_load_addr. Also restore the control FDT and clean up loadaddr at the end so that later tests are not affected by the modified FDT pointer. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/tests/test_source.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/py/tests/test_source.py b/test/py/tests/test_source.py index 970d8c79869..4b9a12890c7 100644 --- a/test/py/tests/test_source.py +++ b/test/py/tests/test_source.py @@ -15,6 +15,12 @@ def test_source(ubman): its = os.path.join(ubman.config.source_dir, 'test/py/tests/source.its') fit = os.path.join(ubman.config.build_dir, 'source.itb') utils.run_and_log(ubman, (mkimage, '-f', its, fit)) + + # Set loadaddr to match CONFIG_SYS_LOAD_ADDR, in case a previous test + # (e.g. PXE) changed image_load_addr. The 'source :' and 'source #' + # variants use image_load_addr, which is synchronised via the loadaddr + # env-var callback. + ubman.run_command('setenv loadaddr 0') ubman.run_command(f'host load hostfs - $loadaddr {fit}') assert '2' in ubman.run_command('source') @@ -34,3 +40,7 @@ def test_source(ubman): ubman.run_command('fdt rm /images default') assert 'Fail' in ubman.run_command('source || echo Fail') assert 'Fail' in ubman.run_command('source \\# || echo Fail') + + # Restore the control FDT and clean up + ubman.run_command('fdt addr $fdtcontroladdr') + ubman.run_command('setenv loadaddr') -- 2.43.0
From: Simon Glass <sjg@chromium.org> The 'host load' command calls efi_set_bootdev() which allocates EFI device-path pool memory and logs a free_pool(NULL) entry in the EFI log. If test_source (or another test using 'host load') runs first in the same session, these stale log entries cause bootflow_efi() to fail with EFI_INVALID_PARAMETER when it validates the log. Reset the EFI log at the start of bootflow_efi() so it only checks entries from its own operations. Also move the bloblist_find() call to just before the log-checking loop, since the log may not exist at function entry but may be created during the EFI boot sequence. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/boot/bootflow.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index e1e50319740..9aab3ea807a 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -1393,8 +1393,8 @@ BOOTSTD_TEST(bootflow_android_image_v2, UTF_CONSOLE | UTF_DM | UTF_SCAN_FDT); /* Test EFI bootmeth */ static int bootflow_efi(struct unit_test_state *uts) { - struct efil_hdr *hdr = bloblist_find(BLOBLISTT_EFI_LOG, 0); static const char *order[] = {"mmc1", "usb", NULL}; + struct efil_hdr *hdr; struct efil_rec_hdr *rec_hdr; struct bootstd_priv *std; struct udevice *bootstd; @@ -1402,6 +1402,10 @@ static int bootflow_efi(struct unit_test_state *uts) struct udevice *usb; int i; + /* clear stale entries left by previous tests */ + if (IS_ENABLED(CONFIG_EFI_LOG)) + efi_log_reset(); + ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); std = dev_get_priv(bootstd); old_order = std->bootdev_order; @@ -1470,6 +1474,7 @@ static int bootflow_efi(struct unit_test_state *uts) ut_assert(!device_active(usb)); /* check memory allocations are as expected */ + hdr = bloblist_find(BLOBLISTT_EFI_LOG, 0); if (!hdr) return 0; -- 2.43.0
From: Simon Glass <sjg@chromium.org> These three tests take a long time to run (2-8s each) due to the interactive nature of the EFI selftest framework and the U-Boot restart they perform: 8.4s test_efi_selftest_text_input_ex 7.4s test_efi_selftest_text_input 2.4s test_efi_selftest_base Mark them as slow so they can be excluded with '-k not slow'. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/tests/test_efi_selftest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py index 2a959ae3406..ef1301f620e 100644 --- a/test/py/tests/test_efi_selftest.py +++ b/test/py/tests/test_efi_selftest.py @@ -7,6 +7,7 @@ import pytest @pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.slow @pytest.mark.restart def test_efi_selftest_base(ubman): """Run UEFI unit tests @@ -64,6 +65,7 @@ def test_efi_selftest_watchdog_reboot(ubman): ubman.run_command(cmd='', send_nl=False, wait_for_reboot=True) @pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.slow @pytest.mark.restart def test_efi_selftest_text_input(ubman): """Test the EFI_SIMPLE_TEXT_INPUT_PROTOCOL @@ -120,6 +122,7 @@ def test_efi_selftest_text_input(ubman): ubman.restart_uboot() @pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.slow @pytest.mark.restart def test_efi_selftest_text_input_ex(ubman): """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a --malloc-dump option to the pytest framework that passes --malloc_dump to the sandbox binary. The filename may contain '%d' which is replaced with a sequence number that increments on each U-Boot restart, so each instance produces a separate dump. Override close() in ConsoleSandbox to send 'poweroff' before closing the PTY when --malloc-dump is active, so that state_uninit() runs and writes the dump file. Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/malloc.rst | 18 ++++++++++++++++++ test/py/conftest.py | 3 +++ test/py/console_sandbox.py | 26 ++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/doc/develop/malloc.rst b/doc/develop/malloc.rst index b5c84c4c3bd..7e46c05dfde 100644 --- a/doc/develop/malloc.rst +++ b/doc/develop/malloc.rst @@ -511,6 +511,24 @@ by checking ``malloc_get_info()`` before and after:: allocations during the operation 6. Fix the leak and verify the test passes +**Dumping heap state on exit** + +When running sandbox, the ``--malloc_dump`` command-line option writes a heap +dump to a file when U-Boot exits cleanly (via ``poweroff`` or ``reset``). This +is useful for capturing heap state at the end of a test session:: + + ./u-boot -Tf -c "poweroff" --malloc_dump /tmp/heap.txt + +The pytest framework also supports this via ``--malloc-dump``:: + + test/py/test.py -B sandbox --malloc-dump /tmp/heap.txt -k test_source + +The filename may contain ``%d`` which is replaced with a sequence number +that increments each time U-Boot restarts during the test session, so each +instance produces a separate dump:: + + test/py/test.py -B sandbox --malloc-dump /tmp/heap%d.txt -k test_vboot + API Reference ------------- diff --git a/test/py/conftest.py b/test/py/conftest.py index f4c5e390a93..47a0d112e51 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -106,6 +106,8 @@ def pytest_addoption(parser): help='Disable console timeout (useful for debugging)') parser.addoption('--no-full', default=False, action='store_true', help='Skip flat-tree tests (run live-tree only)') + parser.addoption('--malloc-dump', default=None, + help='Write malloc dump to file on exit') def run_build(config, source_dir, build_dir, board_type, log): @@ -362,6 +364,7 @@ def pytest_configure(config): ubconfig.allow_exceptions = config.getoption('allow_exceptions') ubconfig.no_timeout = config.getoption('no_timeout') ubconfig.no_full = config.getoption('no_full') + ubconfig.malloc_dump = config.getoption('malloc_dump') env_vars = ( 'board_type', diff --git a/test/py/console_sandbox.py b/test/py/console_sandbox.py index 3bd109acef5..424e3ad2dd7 100644 --- a/test/py/console_sandbox.py +++ b/test/py/console_sandbox.py @@ -25,6 +25,7 @@ class ConsoleSandbox(ConsoleBase): super().__init__(log, config, max_fifo_fill=1024) self.sandbox_flags = [] self.use_dtb = True + self.malloc_dump_seq = 0 def get_spawn(self): """Connect to a fresh U-Boot instance. @@ -57,6 +58,14 @@ class ConsoleSandbox(ConsoleBase): if self.config.no_full: cmd.append('-F') + if self.config.malloc_dump: + try: + fname = self.config.malloc_dump % self.malloc_dump_seq + except TypeError: + fname = self.config.malloc_dump + self.malloc_dump_seq += 1 + cmd += ['--malloc_dump', fname] + # Always disable the pager cmd.append('-P') @@ -84,6 +93,23 @@ class ConsoleSandbox(ConsoleBase): self.sandbox_flags = [] self.use_dtb = True + def close(self): + """Terminate the sandbox, using poweroff for a clean shutdown. + + When --malloc-dump is active we need state_uninit() to run, so + send 'poweroff' instead of just closing the PTY. + """ + if self.p and self.config.malloc_dump: + try: + self.p.send('poweroff\n') + for _ in range(50): + if not self.p.isalive(): + break + time.sleep(0.1) + except: + pass + super().close() + def kill(self, sig): """Send a specific Unix signal to the sandbox process. -- 2.43.0
From: Simon Glass <sjg@chromium.org> When --malloc-dump is active, only the final close() sends poweroff to trigger state_uninit(). Intermediate shutdowns from cleanup_spawn() (used by restart_uboot and failure cleanup) just close the PTY, so those sessions never write a dump file. Send poweroff before every shutdown path so each sandbox session produces a dump. This makes the dump sequence numbers sequential. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/console_sandbox.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/py/console_sandbox.py b/test/py/console_sandbox.py index 424e3ad2dd7..21625140dbb 100644 --- a/test/py/console_sandbox.py +++ b/test/py/console_sandbox.py @@ -93,8 +93,8 @@ class ConsoleSandbox(ConsoleBase): self.sandbox_flags = [] self.use_dtb = True - def close(self): - """Terminate the sandbox, using poweroff for a clean shutdown. + def _poweroff_if_needed(self): + """Send poweroff for a clean shutdown if malloc-dump is active. When --malloc-dump is active we need state_uninit() to run, so send 'poweroff' instead of just closing the PTY. @@ -108,6 +108,15 @@ class ConsoleSandbox(ConsoleBase): time.sleep(0.1) except: pass + + def cleanup_spawn(self): + """Shut down sandbox, using poweroff for a clean shutdown.""" + self._poweroff_if_needed() + super().cleanup_spawn() + + def close(self): + """Terminate sandbox, using poweroff for a clean shutdown.""" + self._poweroff_if_needed() super().close() def kill(self, sig): -- 2.43.0
From: Simon Glass <sjg@chromium.org> The PXE tests set kernel_addr_r to 0x2000000 and do not restore the original value. When bootflow_extlinux_localboot or bootflow_scan_extlinux run later, they read the wrong address from the environment and fail. Restore kernel_addr_r and ramdisk_addr_r to their default values at the start of each test so they do not depend on test ordering. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/boot/bootflow.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 9aab3ea807a..53fa88ae080 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -1677,6 +1677,11 @@ static int bootflow_scan_extlinux(struct unit_test_state *uts) const void *fdt; int node; + /* restore default addresses in case PXE tests changed them */ + ut_assertok(env_set("kernel_addr_r", "1000000")); + ut_assertok(env_set("ramdisk_addr_r", "2000000")); + ut_assertok(env_set("fdt_addr_r", "c00000")); + ut_assertok(run_command("bootflow scan", 0)); ut_assert_console_end(); ut_assertok(bootstd_get_priv(&std)); @@ -1745,6 +1750,11 @@ static int bootflow_extlinux_localboot(struct unit_test_state *uts) const char **old_order; struct bootflow *bflow; + /* restore default addresses in case PXE tests changed them */ + ut_assertok(env_set("kernel_addr_r", "1000000")); + ut_assertok(env_set("ramdisk_addr_r", "2000000")); + ut_assertok(env_set("fdt_addr_r", "c00000")); + ut_assertok(prep_mmc_bootdev(uts, "mmc9", false, &old_order)); ut_assertok(run_command("bootflow scan", 0)); -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add 'bootstage save' and 'bootstage restore' subcommands behind a new BOOTSTAGE_SAVE Kconfig (default y when UNIT_TEST is enabled). These store and retrieve the record count via $bootstage_count. Some commands (e.g. bootm) add bootstage records with unique IDs. In a pytest session with many tests, these accumulate and fill the bootstage table. The new commands allow the test framework to save the count before a test and restore it afterwards. Also add inline stubs for bootstage_get_rec_count() and bootstage_set_rec_count() when BOOTSTAGE is disabled, along with a test and command documentation. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/Kconfig | 11 +++++ cmd/bootstage.c | 28 ++++++++++++ doc/usage/cmd/bootstage.rst | 86 +++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + include/bootstage.h | 9 ++++ test/cmd/bootstage.c | 24 +++++++++++ 6 files changed, 159 insertions(+) create mode 100644 doc/usage/cmd/bootstage.rst diff --git a/boot/Kconfig b/boot/Kconfig index cd66060f4db..d9d5214ca0f 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -1479,6 +1479,17 @@ config BOOTSTAGE_FDT Code in the Linux kernel can find this in /proc/devicetree. +config BOOTSTAGE_SAVE + bool "Allow saving and restoring the bootstage record count" + depends on BOOTSTAGE + default y if UNIT_TEST + help + Provide 'bootstage save' and 'bootstage restore' subcommands + which store and retrieve the record count via the bootstage_count + environment variable. These are used by Python tests to prevent + records from accumulating across tests and filling the bootstage + table. + config BOOTSTAGE_STASH bool "Stash the boot timing information in memory before booting OS" depends on BOOTSTAGE diff --git a/cmd/bootstage.c b/cmd/bootstage.c index 5c6d5a3ab45..ce69097b96c 100644 --- a/cmd/bootstage.c +++ b/cmd/bootstage.c @@ -5,6 +5,7 @@ #include <bootstage.h> #include <command.h> +#include <env.h> #include <vsprintf.h> #include <linux/string.h> @@ -62,8 +63,31 @@ static int do_bootstage_stash(struct cmd_tbl *cmdtp, int flag, int argc, } #endif +static int __maybe_unused do_bootstage_save(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + env_set_hex("bootstage_count", bootstage_get_rec_count()); + + return 0; +} + +static int __maybe_unused do_bootstage_restore(struct cmd_tbl *cmdtp, int flag, + int argc, char *const argv[]) +{ + ulong count = env_get_hex("bootstage_count", 0); + + if (count) + bootstage_set_rec_count(count); + + return 0; +} + static struct cmd_tbl cmd_bootstage_sub[] = { U_BOOT_CMD_MKENT(report, 2, 1, do_bootstage_report, "", ""), +#if IS_ENABLED(CONFIG_BOOTSTAGE_SAVE) + U_BOOT_CMD_MKENT(save, 1, 0, do_bootstage_save, "", ""), + U_BOOT_CMD_MKENT(restore, 1, 0, do_bootstage_restore, "", ""), +#endif #if IS_ENABLED(CONFIG_BOOTSTAGE_STASH) U_BOOT_CMD_MKENT(stash, 4, 0, do_bootstage_stash, "", ""), U_BOOT_CMD_MKENT(unstash, 4, 0, do_bootstage_stash, "", ""), @@ -95,6 +119,10 @@ U_BOOT_CMD(bootstage, 4, 1, do_boostage, "Boot stage command", " - check boot progress and timing\n" "report - Print a report\n" +#if IS_ENABLED(CONFIG_BOOTSTAGE_SAVE) + "save - Save record count to $bootstage_count\n" + "restore - Restore record count from $bootstage_count\n" +#endif #if IS_ENABLED(CONFIG_BOOTSTAGE_STASH) "stash [<start> [<size>]] - Stash data into memory\n" "unstash [<start> [<size>]] - Unstash data from memory\n" diff --git a/doc/usage/cmd/bootstage.rst b/doc/usage/cmd/bootstage.rst new file mode 100644 index 00000000000..44069e6fedd --- /dev/null +++ b/doc/usage/cmd/bootstage.rst @@ -0,0 +1,86 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +.. index:: + single: bootstage (command) + +bootstage command +================= + +Synopsis +-------- + +:: + + bootstage report + bootstage save + bootstage restore + bootstage stash [<start> [<size>]] + bootstage unstash [<start> [<size>]] + +Description +----------- + +The *bootstage* command provides access to U-Boot's boot-timing records. + +bootstage report + Print a report of all bootstage records, showing the timestamp and elapsed + time for each stage. + +bootstage save + Save the current record count to the *bootstage_count* environment + variable. This can be used to snapshot the bootstage state before an + operation that adds records. + +bootstage restore + Restore the record count from *bootstage_count*, discarding any records + added since the last save. This is used by the Python test framework to + prevent records from accumulating across tests. + +bootstage stash + Stash bootstage data into memory at the given address and size. + Only available when ``CONFIG_BOOTSTAGE_STASH`` is enabled. + +bootstage unstash + Read back previously stashed bootstage data from memory. + Only available when ``CONFIG_BOOTSTAGE_STASH`` is enabled. + +Configuration +------------- + +The *bootstage* command is available when ``CONFIG_BOOTSTAGE`` is enabled. + +CONFIG_BOOTSTAGE_REPORT + Enable output of a boot-time report before booting the OS. + +CONFIG_BOOTSTAGE_RECORD_COUNT + Number of bootstage records to store (default 50). + +CONFIG_BOOTSTAGE_SAVE + Enable the *save* and *restore* subcommands. Default y when + ``CONFIG_UNIT_TEST`` is set. + +CONFIG_BOOTSTAGE_STASH + Enable the *stash* and *unstash* subcommands for passing bootstage + data to the OS via memory. + +Example +------- + +:: + + => bootstage report + Timer summary in microseconds (8 records): + Mark Elapsed Stage + 0 0 reset + 0 0 board_init_f + 29,743 29,743 board_init_r + 52,918 23,175 eth_common_init + 53,007 89 eth_initialize + + Accumulated time: + 1,235 dm_f + 670 of_live + 5,621 dm_r + + => bootstage save + => bootstage restore diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 05e1828bf01..ecf0ba16700 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -49,6 +49,7 @@ Shell commands cmd/bootm cmd/bootmenu cmd/bootmeth + cmd/bootstage cmd/bootstd cmd/bootz cmd/button diff --git a/include/bootstage.h b/include/bootstage.h index da57f9b81c5..30858cbfb3f 100644 --- a/include/bootstage.h +++ b/include/bootstage.h @@ -519,6 +519,15 @@ static inline int bootstage_init(bool first) return 0; } +static inline uint bootstage_get_rec_count(void) +{ + return 0; +} + +static inline void bootstage_set_rec_count(uint count) +{ +} + #endif /* ENABLE_BOOTSTAGE */ /* helpers for SPL */ diff --git a/test/cmd/bootstage.c b/test/cmd/bootstage.c index dee4a0671fa..29df4725d1f 100644 --- a/test/cmd/bootstage.c +++ b/test/cmd/bootstage.c @@ -30,3 +30,27 @@ static int cmd_bootstage_report(struct unit_test_state *uts) return 0; } CMD_TEST(cmd_bootstage_report, UTF_CONSOLE); + +static int cmd_bootstage_save_restore(struct unit_test_state *uts) +{ + uint count; + + count = bootstage_get_rec_count(); + ut_assert(count > 0); + + /* Save the current count */ + ut_assertok(run_command("bootstage save", 0)); + ut_assert_console_end(); + + /* Add a new record and check the count grows by one */ + bootstage_mark_name(BOOTSTAGE_ID_USER + 60, "test_save_restore"); + ut_asserteq(count + 1, bootstage_get_rec_count()); + + /* Restore should bring the count back */ + ut_assertok(run_command("bootstage restore", 0)); + ut_assert_console_end(); + ut_asserteq(count, bootstage_get_rec_count()); + + return 0; +} +CMD_TEST(cmd_bootstage_save_restore, UTF_CONSOLE); -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a preserve_bootstage() context-manager helper in the pytest utils module, and use it in TestFitImage which runs bootm and is the main consumer of bootstage records. Also save and restore the bootstage record count in test_pre_run() and test_post_run() for ut tests which run multiple tests within a single 'ut' command invocation. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/test/test.h | 2 ++ test/py/tests/test_fit.py | 6 ++++++ test/py/utils.py | 18 ++++++++++++++++++ test/test-main.c | 5 +++++ 4 files changed, 31 insertions(+) diff --git a/include/test/test.h b/include/test/test.h index c3b251e2cd4..b81cab4d7a4 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -92,6 +92,7 @@ struct ut_arg { * @force_run: true to run tests marked with the UTF_MANUAL flag * @workers: Number of parallel workers, 0 if not sharding tests * @worker_id: ID of this worker (0 to workers-1) + * @old_bootstage_count: bootstage record count saved before each test * @old_bloblist: stores the old gd->bloblist pointer * @soft_fail: continue execution of the test even after it fails * @expect_str: Temporary string used to hold expected string value @@ -128,6 +129,7 @@ struct unit_test_state { bool force_run; int workers; int worker_id; + uint old_bootstage_count; void *old_bloblist; bool soft_fail; char expect_str[1024]; diff --git a/test/py/tests/test_fit.py b/test/py/tests/test_fit.py index ed18ff68825..6fb76196e16 100755 --- a/test/py/tests/test_fit.py +++ b/test/py/tests/test_fit.py @@ -140,6 +140,12 @@ class TestFitImage: - run code coverage to make sure we are testing all the code """ + @pytest.fixture(autouse=True) + def save_bootstage(self, ubman): + """Save and restore bootstage around each test.""" + with utils.preserve_bootstage(ubman): + yield + def make_fname(self, ubman, leaf): """Make a temporary filename diff --git a/test/py/utils.py b/test/py/utils.py index 083a3feddc2..9097f158496 100644 --- a/test/py/utils.py +++ b/test/py/utils.py @@ -14,8 +14,26 @@ import signal import sys import time import re +from contextlib import contextmanager import pytest +@contextmanager +def preserve_bootstage(ubman): + """Context manager to save and restore bootstage record count. + + Some commands (e.g. bootm) add bootstage records with unique IDs. These + accumulate across tests in a pytest session and can fill the bootstage + table. Use this around tests that trigger such commands. + + Args: + ubman (ConsoleBase): U-Boot console connection + """ + ubman.run_command('bootstage save') + try: + yield + finally: + ubman.run_command('bootstage restore') + def md5sum_data(data): """Calculate the MD5 hash of some data. diff --git a/test/test-main.c b/test/test-main.c index 5db35b59760..77223cfbcb7 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -7,6 +7,7 @@ #define LOG_CATEGORY LOGC_TEST #include <blk.h> +#include <bootstage.h> #include <console.h> #include <cyclic.h> #include <dm.h> @@ -560,6 +561,8 @@ static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) gd_set_bloblist(NULL); } + uts->old_bootstage_count = bootstage_get_rec_count(); + if (!(test->flags & UTF_NO_SILENT)) ut_silence_console(uts); @@ -589,6 +592,8 @@ static int test_post_run(struct unit_test_state *uts, struct unit_test *test) log_debug("restore bloblist %p\n", gd_bloblist()); } + bootstage_set_rec_count(uts->old_bootstage_count); + blkcache_free(); return 0; -- 2.43.0
participants (1)
-
Simon Glass