diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/Kconfig | 2 | ||||
-rw-r--r-- | test/boot/bootdev.c | 11 | ||||
-rw-r--r-- | test/boot/bootflow.c | 12 | ||||
-rw-r--r-- | test/boot/bootmeth.c | 47 | ||||
-rw-r--r-- | test/boot/cedit.c | 22 | ||||
-rw-r--r-- | test/boot/expo.c | 26 | ||||
-rw-r--r-- | test/boot/files/expo_ids.h | 3 | ||||
-rw-r--r-- | test/boot/files/expo_layout.dts | 5 | ||||
-rw-r--r-- | test/cmd/Makefile | 3 | ||||
-rw-r--r-- | test/cmd/cpuid.c | 22 | ||||
-rw-r--r-- | test/cmd/font.c | 21 | ||||
-rw-r--r-- | test/cmd/msr.c | 38 | ||||
-rw-r--r-- | test/dm/Makefile | 2 | ||||
-rw-r--r-- | test/py/conftest.py | 60 | ||||
-rw-r--r-- | test/py/tests/test_sleep.py | 4 | ||||
-rw-r--r-- | test/py/u_boot_console_base.py | 39 | ||||
-rw-r--r-- | test/py/u_boot_spawn.py | 88 | ||||
-rw-r--r-- | test/test-main.c | 28 |
18 files changed, 355 insertions, 78 deletions
diff --git a/test/Kconfig b/test/Kconfig index e2ec0994a2e..558a9cd49b4 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -32,7 +32,7 @@ if UT_LIB config UT_LIB_ASN1 bool "Unit test for asn1 compiler and decoder function" - depends on SANDBOX + depends on SANDBOX && !MBEDTLS_LIB_X509 default y imply ASYMMETRIC_KEY_TYPE imply ASYMMETRIC_PUBLIC_KEY_SUBTYPE diff --git a/test/boot/bootdev.c b/test/boot/bootdev.c index c635d06ec25..369105ca4cf 100644 --- a/test/boot/bootdev.c +++ b/test/boot/bootdev.c @@ -128,6 +128,7 @@ BOOTSTD_TEST(bootdev_test_labels, UTF_DM | UTF_SCAN_FDT | UTF_ETH_BOOTDEV); static int bootdev_test_any(struct unit_test_state *uts) { struct udevice *dev, *media; + char *seq; int mflags; /* @@ -147,8 +148,16 @@ static int bootdev_test_any(struct unit_test_state *uts) * 8 [ ] OK mmc mmc2.bootdev * 9 [ + ] OK mmc mmc1.bootdev * a [ ] OK mmc mmc0.bootdev + * + * However if DSA_SANDBOX is disabled the dsa-test@{0,1} devices + * are not there. */ - ut_assertok(bootdev_find_by_any("8", &dev, &mflags)); + if (CONFIG_IS_ENABLED(DSA_SANDBOX)) + seq = "8"; + else + seq = "6"; + + ut_assertok(bootdev_find_by_any(seq, &dev, &mflags)); ut_asserteq(UCLASS_BOOTDEV, device_get_uclass_id(dev)); ut_asserteq(BOOTFLOW_METHF_SINGLE_DEV, mflags); media = dev_get_parent(dev); diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 6ad63afe90a..154dea70a59 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -109,9 +109,17 @@ static int bootflow_cmd_label(struct unit_test_state *uts) * 8 [ ] OK mmc mmc2.bootdev * 9 [ + ] OK mmc mmc1.bootdev * a [ ] OK mmc mmc0.bootdev + * + * However with CONFIG_DSA_SANDBOX=n we have two fewer (dsa-test@0 and + * dsa-test@1). */ - ut_assertok(run_command("bootflow scan -lH 9", 0)); - ut_assert_nextline("Scanning for bootflows with label '9'"); + if (CONFIG_IS_ENABLED(DSA_SANDBOX)) { + ut_assertok(run_command("bootflow scan -lH 9", 0)); + ut_assert_nextline("Scanning for bootflows with label '9'"); + } else { + ut_assertok(run_command("bootflow scan -lH 7", 0)); + ut_assert_nextline("Scanning for bootflows with label '7'"); + } ut_assert_skip_to_line("(1 bootflow, 1 valid)"); ut_assertok(run_command("bootflow scan -lH 0", 0)); diff --git a/test/boot/bootmeth.c b/test/boot/bootmeth.c index 518d99c4a27..18ae6d7fe13 100644 --- a/test/boot/bootmeth.c +++ b/test/boot/bootmeth.c @@ -127,6 +127,53 @@ static int bootmeth_cmd_order_glob(struct unit_test_state *uts) } BOOTSTD_TEST(bootmeth_cmd_order_glob, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE); +/* Check 'bootmeth set' command */ +static int bootmeth_cmd_set(struct unit_test_state *uts) +{ + /* Check we can enable extlinux fallback */ + console_record_reset_enable(); + ut_assertok(run_command("bootmeth set extlinux fallback 1", 0)); + ut_assert_console_end(); + + /* Check we can disable extlinux fallback */ + console_record_reset_enable(); + ut_assertok(run_command("bootmeth set extlinux fallback 0", 0)); + ut_assert_console_end(); + + /* Check extlinux fallback unexpected value */ + console_record_reset_enable(); + ut_asserteq(1, run_command("bootmeth set extlinux fallback fred", 0)); + ut_assert_nextline("Unexpected value 'fred'"); + ut_assert_nextline("Failed (err=-22)"); + ut_assert_console_end(); + + /* Check that we need to provide right number of parameters */ + ut_asserteq(1, run_command("bootmeth set extlinux fallback", 0)); + ut_assert_nextline("Required parameters not provided"); + ut_assert_console_end(); + + /* Check that we need to provide a valid bootmethod */ + ut_asserteq(1, run_command("bootmeth set fred fallback 0", 0)); + ut_assert_nextline("Unknown bootmeth 'fred'"); + ut_assert_nextline("Failed (err=-19)"); + ut_assert_console_end(); + + /* Check that we need to provide a valid property */ + ut_asserteq(1, run_command("bootmeth set extlinux fred 0", 0)); + ut_assert_nextline("Invalid option"); + ut_assert_nextline("Failed (err=-22)"); + ut_assert_console_end(); + + /* Check that we need to provide a bootmeth that supports properties */ + ut_asserteq(1, run_command("bootmeth set efi fallback 0", 0)); + ut_assert_nextline("set_property not found"); + ut_assert_nextline("Failed (err=-19)"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootmeth_cmd_set, UTF_DM | UTF_SCAN_FDT); + /* Check 'bootmeths' env var */ static int bootmeth_env(struct unit_test_state *uts) { diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 1f7af8e5d79..4d1b99bc2ea 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -31,9 +31,11 @@ static int cedit_base(struct unit_test_state *uts) * ^N Move down to second item * ^M Select item * \e Quit + * + * cedit_run() returns -EACCESS so this command returns CMD_RET_FAILURE */ console_in_puts("\x0e\x0d\x0e\x0d\e"); - ut_assertok(run_command("cedit run", 0)); + ut_asserteq(1, run_command("cedit run", 0)); exp = cur_exp; scn = expo_lookup_scene_id(exp, exp->scene_id); @@ -94,14 +96,16 @@ static int cedit_fdt(struct unit_test_state *uts) ut_asserteq(ID_CPU_SPEED_2, ofnode_read_u32_default(node, "cpu-speed", 0)); + ut_asserteq(3, + ofnode_read_u32_default(node, "cpu-speed-value", 0)); ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str")); ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name")); - /* There should only be 5 properties */ + /* There should only be 7 properties */ for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop); i++, ofnode_next_property(&prop)) ; - ut_asserteq(5, i); + ut_asserteq(7, i); ut_assert_console_end(); @@ -147,14 +151,16 @@ static int cedit_env(struct unit_test_state *uts) strcpy(str, "my-machine"); ut_assertok(run_command("cedit write_env -v", 0)); - ut_assert_nextlinen("c.cpu-speed=7"); + ut_assert_nextlinen("c.cpu-speed=11"); ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz"); - ut_assert_nextlinen("c.power-loss=10"); + ut_assert_nextlinen("c.cpu-speed-value=3"); + ut_assert_nextlinen("c.power-loss=14"); ut_assert_nextlinen("c.power-loss-str=Always Off"); + ut_assert_nextlinen("c.power-loss-value=0"); ut_assert_nextlinen("c.machine-name=my-machine"); ut_assert_console_end(); - ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0)); + ut_asserteq(11, env_get_ulong("c.cpu-speed", 10, 0)); ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str")); ut_asserteq_str("my-machine", env_get("c.machine-name")); @@ -163,8 +169,8 @@ static int cedit_env(struct unit_test_state *uts) *str = '\0'; ut_assertok(run_command("cedit read_env -v", 0)); - ut_assert_nextlinen("c.cpu-speed=7"); - ut_assert_nextlinen("c.power-loss=10"); + ut_assert_nextlinen("c.cpu-speed=11"); + ut_assert_nextlinen("c.power-loss=14"); ut_assert_nextlinen("c.machine-name=my-machine"); ut_assert_console_end(); diff --git a/test/boot/expo.c b/test/boot/expo.c index 9b4aa803eb1..db14ff86f8b 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -91,7 +91,7 @@ static int expo_base(struct unit_test_state *uts) *name = '\0'; ut_assertnonnull(exp); ut_asserteq(0, exp->scene_id); - ut_asserteq(0, exp->next_id); + ut_asserteq(EXPOID_BASE_ID, exp->next_id); /* Make sure the name was allocated */ ut_assertnonnull(exp->name); @@ -130,7 +130,7 @@ static int expo_scene(struct unit_test_state *uts) ut_assertok(expo_new(EXPO_NAME, NULL, &exp)); scn = NULL; - ut_asserteq(0, exp->next_id); + ut_asserteq(EXPOID_BASE_ID, exp->next_id); strcpy(name, SCENE_NAME1); id = scene_new(exp, name, SCENE1, &scn); *name = '\0'; @@ -151,7 +151,7 @@ static int expo_scene(struct unit_test_state *uts) scn = NULL; id = scene_new(exp, SCENE_NAME2, 0, &scn); ut_assertnonnull(scn); - ut_assertok(scene_title_set(scn, title_id)); + scn->title_id = title_id; ut_asserteq(STR_SCENE_TITLE + 1, id); ut_asserteq(STR_SCENE_TITLE + 2, exp->next_id); ut_asserteq_ptr(exp, scn->expo); @@ -167,6 +167,25 @@ static int expo_scene(struct unit_test_state *uts) } BOOTSTD_TEST(expo_scene, UTF_DM | UTF_SCAN_FDT); +/* Check creating a scene with no ID */ +static int expo_scene_no_id(struct unit_test_state *uts) +{ + struct scene *scn; + struct expo *exp; + char name[100]; + int id; + + ut_assertok(expo_new(EXPO_NAME, NULL, &exp)); + ut_asserteq(EXPOID_BASE_ID, exp->next_id); + + strcpy(name, SCENE_NAME1); + id = scene_new(exp, SCENE_NAME1, 0, &scn); + ut_asserteq(EXPOID_BASE_ID, scn->id); + + return 0; +} +BOOTSTD_TEST(expo_scene_no_id, UTF_DM | UTF_SCAN_FDT); + /* Check creating a scene with objects */ static int expo_object(struct unit_test_state *uts) { @@ -698,6 +717,7 @@ static int expo_test_build(struct unit_test_state *uts) ut_asserteq(0, item->desc_id); ut_asserteq(0, item->preview_id); ut_asserteq(0, item->flags); + ut_asserteq(0, item->value); txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); ut_asserteq_str("2 GHz", expo_get_str(exp, txt->str_id)); diff --git a/test/boot/files/expo_ids.h b/test/boot/files/expo_ids.h index a86e0d06f6b..ffb511364b1 100644 --- a/test/boot/files/expo_ids.h +++ b/test/boot/files/expo_ids.h @@ -4,8 +4,7 @@ */ enum { - ZERO, - ID_PROMPT, + ID_PROMPT = EXPOID_BASE_ID, ID_SCENE1, ID_SCENE1_TITLE, diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts index bed552288f4..9bc1e4950b9 100644 --- a/test/boot/files/expo_layout.dts +++ b/test/boot/files/expo_layout.dts @@ -39,8 +39,11 @@ item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2 ID_CPU_SPEED_3>; + /* values for the menu items */ + item-value = <0 3 6>; + start-bit = <0x400>; - bit-length = <2>; + bit-length = <3>; }; power-loss { diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 8f2134998ad..40808350962 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -12,6 +12,7 @@ ifdef CONFIG_CONSOLE_RECORD obj-$(CONFIG_CMD_PAUSE) += test_pause.o endif obj-y += exit.o mem.o +obj-$(CONFIG_X86) += cpuid.o msr.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_FDT) += fdt.o @@ -30,7 +31,9 @@ ifdef CONFIG_SANDBOX obj-$(CONFIG_CMD_MBR) += mbr.o obj-$(CONFIG_CMD_READ) += rw.o obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +ifdef CONFIG_NET obj-$(CONFIG_CMD_WGET) += wget.o +endif obj-$(CONFIG_ARM_FFA_TRANSPORT) += armffa.o endif obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o diff --git a/test/cmd/cpuid.c b/test/cmd/cpuid.c new file mode 100644 index 00000000000..e07f5fd4696 --- /dev/null +++ b/test/cmd/cpuid.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for cpuid command + * + * Copyright 2024 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <test/cmd.h> +#include <test/ut.h> + +static int cmd_test_cpuid(struct unit_test_state *uts) +{ + ut_assertok(run_commandf("cpuid 1")); + ut_assert_nextline("eax 00060fb1"); + ut_assert_nextline("ebx 00000800"); + ut_assert_nextline("ecx 80002001"); + ut_assert_nextline("edx 078bfbfd"); + + return 0; +} +CMD_TEST(cmd_test_cpuid, UTF_CONSOLE); diff --git a/test/cmd/font.c b/test/cmd/font.c index 25d365dedd2..3335dd65bea 100644 --- a/test/cmd/font.c +++ b/test/cmd/font.c @@ -27,14 +27,20 @@ static int font_test_base(struct unit_test_state *uts) ut_assertok(uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev)); ut_assertok(run_command("font list", 0)); - ut_assert_nextline("nimbus_sans_l_regular"); + if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_NIMBUS)) + ut_assert_nextline("nimbus_sans_l_regular"); + if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_ANKACODER)) + ut_assert_nextline("ankacoder_c75_r"); if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE)) ut_assert_nextline("cantoraone_regular"); ut_assert_console_end(); ut_assertok(vidconsole_get_font_size(dev, &name, &size)); - ut_asserteq_str("nimbus_sans_l_regular", name); - ut_asserteq(18, size); + if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_ANKACODER)) + ut_asserteq_str("ankacoder_c75_r", name); + else + ut_asserteq_str("nimbus_sans_l_regular", name); + ut_asserteq(CONFIG_CONSOLE_TRUETYPE_SIZE, size); if (!IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE)) return 0; @@ -58,10 +64,19 @@ static int font_test_base(struct unit_test_state *uts) ut_assertok(vidconsole_get_font_size(dev, &name, &size)); ut_asserteq_str("cantoraone_regular", name); ut_asserteq(40, size); + ut_assertok(ut_check_console_end(uts)); + + ut_assertok(run_command("font size", 0)); + ut_assert_nextline("40"); + ut_assertok(ut_check_console_end(uts)); ut_assertok(run_command("font size 30", 0)); ut_assert_console_end(); + ut_assertok(run_command("font size", 0)); + ut_assert_nextline("30"); + ut_assertok(ut_check_console_end(uts)); + ut_assertok(vidconsole_get_font_size(dev, &name, &size)); ut_asserteq_str("cantoraone_regular", name); ut_asserteq(30, size); diff --git a/test/cmd/msr.c b/test/cmd/msr.c new file mode 100644 index 00000000000..e9a152ee5bf --- /dev/null +++ b/test/cmd/msr.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for msr command + * + * Copyright 2024 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <test/cmd.h> +#include <test/ut.h> + +static int cmd_test_msr(struct unit_test_state *uts) +{ + ut_assertok(run_commandf("msr read 200")); + ut_assert_nextline("00000000 ffe00006"); + ut_assert_console_end(); + + /* change the first variable msr and see it reflected in the mtrr cmd */ + ut_assertok(run_commandf("mtrr")); + ut_assert_nextline("CPU 65537:"); + ut_assert_nextlinen("Reg"); + ut_assert_nextlinen("0 Y Back 00000000ffe00000"); + ut_assertok(console_record_reset_enable()); + + /* change the type from 6 to 5 */ + ut_assertok(run_commandf("msr write 200 0 ffe00005")); + ut_assert_console_end(); + + /* Now it shows 'Protect' */ + ut_assertok(run_commandf("mtrr")); + ut_assert_nextline("CPU 65537:"); + ut_assert_nextlinen("Reg"); + ut_assert_nextlinen("0 Y Protect 00000000ffe00000"); + ut_assertok(console_record_reset_enable()); + + return 0; +} +CMD_TEST(cmd_test_msr, UTF_CONSOLE); diff --git a/test/dm/Makefile b/test/dm/Makefile index 6c9ebb8d07c..bcb52ef1067 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -48,7 +48,9 @@ obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi_host.o obj-$(CONFIG_DM_DSA) += dsa.o obj-$(CONFIG_ECDSA_VERIFY) += ecdsa.o obj-$(CONFIG_EFI_MEDIA_SANDBOX) += efi_media.o +ifdef CONFIG_NET obj-$(CONFIG_DM_ETH) += eth.o +endif obj-$(CONFIG_EXTCON) += extcon.o ifneq ($(CONFIG_EFI_PARTITION),) obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fastboot.o diff --git a/test/py/conftest.py b/test/py/conftest.py index fc9dd3a83f8..46a410cf268 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -24,6 +24,7 @@ import pytest import re from _pytest.runner import runtestprotocol import sys +from u_boot_spawn import BootFail, Timeout, Unexpected, handle_exception # Globals: The HTML log file, and the connection to the U-Boot console. log = None @@ -115,14 +116,36 @@ def run_build(config, source_dir, build_dir, board_type, log): runner.close() log.status_pass('OK') -def pytest_xdist_setupnodes(config, specs): - """Clear out any 'done' file from a previous build""" - global build_done_file - build_dir = config.getoption('build_dir') +def get_details(config): + """Obtain salient details about the board and directories to use + + Args: + config (pytest.Config): pytest configuration + + Returns: + tuple: + str: Board type (U-Boot build name) + str: Identity for the lab board + str: Build directory + str: Source directory + """ board_type = config.getoption('board_type') + board_identity = config.getoption('board_identity') + build_dir = config.getoption('build_dir') + source_dir = os.path.dirname(os.path.dirname(TEST_PY_DIR)) + default_build_dir = source_dir + '/build-' + board_type if not build_dir: - build_dir = source_dir + '/build-' + board_type + build_dir = default_build_dir + + return board_type, board_identity, build_dir, source_dir + +def pytest_xdist_setupnodes(config, specs): + """Clear out any 'done' file from a previous build""" + global build_done_file + + build_dir = get_details(config)[2] + build_done_file = Path(build_dir) / 'build.done' if build_done_file.exists(): os.remove(build_done_file) @@ -161,17 +184,10 @@ def pytest_configure(config): global console global ubconfig - source_dir = os.path.dirname(os.path.dirname(TEST_PY_DIR)) + board_type, board_identity, build_dir, source_dir = get_details(config) - board_type = config.getoption('board_type') board_type_filename = board_type.replace('-', '_') - - board_identity = config.getoption('board_identity') board_identity_filename = board_identity.replace('-', '_') - - build_dir = config.getoption('build_dir') - if not build_dir: - build_dir = source_dir + '/build-' + board_type mkdir_p(build_dir) result_dir = config.getoption('result_dir') @@ -239,6 +255,7 @@ def pytest_configure(config): ubconfig.board_identity = board_identity ubconfig.gdbserver = gdbserver ubconfig.dtb = build_dir + '/arch/sandbox/dts/test.dtb' + ubconfig.connection_ok = True env_vars = ( 'board_type', @@ -405,8 +422,21 @@ def u_boot_console(request): Returns: The fixture value. """ - - console.ensure_spawned() + if not ubconfig.connection_ok: + pytest.skip('Cannot get target connection') + return None + try: + console.ensure_spawned() + except OSError as err: + handle_exception(ubconfig, console, log, err, 'Lab failure', True) + except Timeout as err: + handle_exception(ubconfig, console, log, err, 'Lab timeout', True) + except BootFail as err: + handle_exception(ubconfig, console, log, err, 'Boot fail', True, + console.get_spawn_output()) + except Unexpected: + handle_exception(ubconfig, console, log, err, 'Unexpected test output', + False) return console anchors = {} diff --git a/test/py/tests/test_sleep.py b/test/py/tests/test_sleep.py index 66a57434bff..8965fc3fea9 100644 --- a/test/py/tests/test_sleep.py +++ b/test/py/tests/test_sleep.py @@ -27,7 +27,7 @@ def test_sleep(u_boot_console): if not sleep_skip: pytest.skip('sleep is not accurate') - if u_boot_console.config.buildconfig.get('config_cmd_misc', 'n') != 'y': + if u_boot_console.config.buildconfig.get('config_cmd_sleep', 'n') != 'y': pytest.skip('sleep command not supported') # 3s isn't too long, but is enough to cross a few second boundaries. @@ -42,7 +42,7 @@ def test_sleep(u_boot_console): # margin is hopefully enough to account for any system overhead. assert elapsed < (sleep_time + sleep_margin) -@pytest.mark.buildconfigspec("cmd_misc") +@pytest.mark.buildconfigspec("cmd_time") def test_time(u_boot_console): """Test the time command, and validate that it gives approximately the correct amount of command execution time.""" diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py index 76a550d45a1..d8d0bdf9fd4 100644 --- a/test/py/u_boot_console_base.py +++ b/test/py/u_boot_console_base.py @@ -14,6 +14,7 @@ import pytest import re import sys import u_boot_spawn +from u_boot_spawn import BootFail, Timeout, Unexpected, handle_exception # Regexes for text we expect U-Boot to send to the console. pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))') @@ -26,6 +27,9 @@ pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ## PAT_ID = 0 PAT_RE = 1 +# Timeout before expecting the console to be ready (in milliseconds) +TIMEOUT_MS = 30000 + bad_pattern_defs = ( ('spl_signon', pattern_u_boot_spl_signon), ('main_signon', pattern_u_boot_main_signon), @@ -109,7 +113,7 @@ class ConsoleBase(object): Can only usefully be called by sub-classes. Args: - log: A mulptiplex_log.Logfile object, to which the U-Boot output + log: A multiplexed_log.Logfile object, to which the U-Boot output will be logged. config: A configuration data structure, as built by conftest.py. max_fifo_fill: The maximum number of characters to send to U-Boot @@ -186,13 +190,13 @@ class ConsoleBase(object): m = self.p.expect([pattern_u_boot_spl_signon] + self.bad_patterns) if m != 0: - raise Exception('Bad pattern found on SPL console: ' + + raise BootFail('Bad pattern found on SPL console: ' + self.bad_pattern_ids[m - 1]) env_spl_banner_times -= 1 m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns) if m != 0: - raise Exception('Bad pattern found on console: ' + + raise BootFail('Bad pattern found on console: ' + self.bad_pattern_ids[m - 1]) self.u_boot_version_string = self.p.after while True: @@ -203,13 +207,9 @@ class ConsoleBase(object): if m == 1: self.p.send(' ') continue - raise Exception('Bad pattern found on console: ' + + raise BootFail('Bad pattern found on console: ' + self.bad_pattern_ids[m - 2]) - except Exception as ex: - self.log.error(str(ex)) - self.cleanup_spawn() - raise finally: self.log.timestamp() @@ -275,7 +275,7 @@ class ConsoleBase(object): m = self.p.expect([chunk] + self.bad_patterns) if m != 0: self.at_prompt = False - raise Exception('Bad pattern found on console: ' + + raise BootFail('Bad pattern found on console: ' + self.bad_pattern_ids[m - 1]) if not wait_for_prompt: return @@ -285,16 +285,20 @@ class ConsoleBase(object): m = self.p.expect([self.prompt_compiled] + self.bad_patterns) if m != 0: self.at_prompt = False - raise Exception('Bad pattern found on console: ' + + raise BootFail('Missing prompt on console: ' + self.bad_pattern_ids[m - 1]) self.at_prompt = True self.at_prompt_logevt = self.logstream.logfile.cur_evt # Only strip \r\n; space/TAB might be significant if testing # indentation. return self.p.before.strip('\r\n') - except Exception as ex: - self.log.error(str(ex)) - self.cleanup_spawn() + except Timeout as exc: + handle_exception(self.config, self, self.log, exc, 'Lab failure', + True) + raise + except BootFail as exc: + handle_exception(self.config, self, self.log, exc, 'Boot fail', + True, self.get_spawn_output()) raise finally: self.log.timestamp() @@ -351,8 +355,9 @@ class ConsoleBase(object): text = re.escape(text) m = self.p.expect([text] + self.bad_patterns) if m != 0: - raise Exception('Bad pattern found on console: ' + - self.bad_pattern_ids[m - 1]) + raise Unexpected( + "Unexpected pattern found on console (exp '{text}': " + + self.bad_pattern_ids[m - 1]) def drain_console(self): """Read from and log the U-Boot console for a short time. @@ -422,7 +427,7 @@ class ConsoleBase(object): # Reset the console timeout value as some tests may change # its default value during the execution if not self.config.gdbserver: - self.p.timeout = 30000 + self.p.timeout = TIMEOUT_MS return try: self.log.start_section('Starting U-Boot') @@ -433,7 +438,7 @@ class ConsoleBase(object): # future, possibly per-test to be optimal. This works for 'help' # on board 'seaboard'. if not self.config.gdbserver: - self.p.timeout = 30000 + self.p.timeout = TIMEOUT_MS self.p.logfile_read = self.logstream if expect_reset: loop_num = 2 diff --git a/test/py/u_boot_spawn.py b/test/py/u_boot_spawn.py index 97e95e07c80..24d369035e5 100644 --- a/test/py/u_boot_spawn.py +++ b/test/py/u_boot_spawn.py @@ -8,6 +8,7 @@ Logic to spawn a sub-process and interact with its stdio. import os import re import pty +import pytest import signal import select import time @@ -16,6 +17,54 @@ import traceback class Timeout(Exception): """An exception sub-class that indicates that a timeout occurred.""" +class BootFail(Exception): + """An exception sub-class that indicates that a boot failure occurred. + + This is used when a bad pattern is seen when waiting for the boot prompt. + It is regarded as fatal, to avoid trying to boot the again and again to no + avail. + """ + +class Unexpected(Exception): + """An exception sub-class that indicates that unexpected test was seen.""" + + +def handle_exception(ubconfig, console, log, err, name, fatal, output=''): + """Handle an exception from the console + + Exceptions can occur when there is unexpected output or due to the board + crashing or hanging. Some exceptions are likely fatal, where retrying will + just chew up time to no available. In those cases it is best to cause + further tests be skipped. + + Args: + ubconfig (ArbitraryAttributeContainer): ubconfig object + log (Logfile): Place to log errors + console (ConsoleBase): Console to clean up, if fatal + err (Exception): Exception which was thrown + name (str): Name of problem, to log + fatal (bool): True to abort all tests + output (str): Extra output to report on boot failure. This can show the + target's console output as it tried to boot + """ + msg = f'{name}: ' + if fatal: + msg += 'Marking connection bad - no other tests will run' + else: + msg += 'Assuming that lab is healthy' + print(msg) + log.error(msg) + log.error(f'Error: {err}') + + if output: + msg += f'; output {output}' + + if fatal: + ubconfig.connection_ok = False + console.cleanup_spawn() + pytest.exit(msg) + + class Spawn: """Represents the stdio of a freshly created sub-process. Commands may be sent to the process, and responses waited for. @@ -137,6 +186,32 @@ class Spawn: os.write(self.fd, data.encode(errors='replace')) + def receive(self, num_bytes): + """Receive data from the sub-process's stdin. + + Args: + num_bytes (int): Maximum number of bytes to read + + Returns: + str: The data received + + Raises: + ValueError if U-Boot died + """ + try: + c = os.read(self.fd, num_bytes).decode(errors='replace') + except OSError as err: + # With sandbox, try to detect when U-Boot exits when it + # shouldn't and explain why. This is much more friendly than + # just dying with an I/O error + if self.decode_signal and err.errno == 5: # I/O error + alive, _, info = self.checkalive() + if alive: + raise err + raise ValueError('U-Boot exited with %s' % info) + raise + return c + def expect(self, patterns): """Wait for the sub-process to emit specific data. @@ -193,18 +268,7 @@ class Spawn: events = self.poll.poll(poll_maxwait) if not events: raise Timeout() - try: - c = os.read(self.fd, 1024).decode(errors='replace') - except OSError as err: - # With sandbox, try to detect when U-Boot exits when it - # shouldn't and explain why. This is much more friendly than - # just dying with an I/O error - if self.decode_signal and err.errno == 5: # I/O error - alive, _, info = self.checkalive() - if alive: - raise err - raise ValueError('U-Boot exited with %s' % info) - raise + c = self.receive(1024) if self.logfile_read: self.logfile_read.write(c) self.buf += c diff --git a/test/test-main.c b/test/test-main.c index 479dbb33b72..da5b07ce00b 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -294,27 +294,27 @@ static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) * Remove any USB keyboard, so that we can add and remove USB devices * in tests. * - * For UT_TESTF_DM tests, the old driver model state is saved and + * For UTF_DM tests, the old driver model state is saved and * restored across each test. Within in each test there is therefore a * new driver model state, which means that any USB keyboard device in * stdio points to the old state. * - * This is fine in most cases. But if a non-UT_TESTF_DM test starts up + * This is fine in most cases. But if a non-UTF_DM test starts up * USB (thus creating a stdio record pointing to the USB keyboard * device) then when the test finishes, the new driver model state is * freed, meaning that there is now a stale pointer in stdio. * - * This means that any future UT_TESTF_DM test which uses stdin will + * This means that any future UTF_DM test which uses stdin will * cause the console system to call tstc() on the stale device pointer, * causing a crash. * - * We don't want to fix this by enabling UT_TESTF_DM for all tests as + * We don't want to fix this by enabling UTF_DM for all tests as * this causes other problems. For example, bootflow_efi relies on * U-Boot going through a proper init - without that we don't have the * TCG measurement working and get an error * 'tcg2 measurement fails(0x8000000000000007)'. Once we tidy up how EFI * runs tests (e.g. get rid of all the restarting of U-Boot) we could - * potentially make the bootstd tests set UT_TESTF_DM, but other tests + * potentially make the bootstd tests set UTF_DM, but other tests * might do the same thing. * * We could add a test flag to declare that USB is being used, but that @@ -323,7 +323,7 @@ static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) * pointers always. * * So just remove any USB keyboards from the console tables. This allows - * UT_TESTF_DM and non-UT_TESTF_DM tests to coexist happily. + * UTF_DM and non-UTF_DM tests to coexist happily. */ usb_kbd_remove_for_test(); @@ -486,7 +486,7 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test, static int ut_run_test_live_flat(struct unit_test_state *uts, struct unit_test *test) { - int runs; + int runs, ret; if ((test->flags & UTF_OTHER_FDT) && !IS_ENABLED(CONFIG_SANDBOX)) return skip_test(uts); @@ -496,8 +496,11 @@ static int ut_run_test_live_flat(struct unit_test_state *uts, if (CONFIG_IS_ENABLED(OF_LIVE)) { if (!(test->flags & UTF_FLAT_TREE)) { uts->of_live = true; - ut_assertok(ut_run_test(uts, test, test->name)); - runs++; + ret = ut_run_test(uts, test, test->name); + if (ret != -EAGAIN) { + ut_assertok(ret); + runs++; + } } } @@ -521,8 +524,11 @@ static int ut_run_test_live_flat(struct unit_test_state *uts, (!runs || ut_test_run_on_flattree(test)) && !(gd->flags & GD_FLG_FDT_CHANGED)) { uts->of_live = false; - ut_assertok(ut_run_test(uts, test, test->name)); - runs++; + ret = ut_run_test(uts, test, test->name); + if (ret != -EAGAIN) { + ut_assertok(ret); + runs++; + } } return 0; |