Add a test to verify that counting down attempts work and that the new locking feature indeed inhibits it.
Cc: Lars Schmidt <l.schm...@pengutronix.de> Signed-off-by: Ahmad Fatoum <a.fat...@barebox.org> --- common/boards/qemu-virt/qemu-virt-flash.dtso | 5 + test/arm/virt@multi_v7_defconfig.yaml | 1 + test/arm/virt@multi_v8_defconfig.yaml | 1 + test/py/helper.py | 63 ++++++++ test/py/test_bootchooser.py | 158 +++++++++++++++++++ test/riscv/qemu-virt64@rv64i_defconfig.yaml | 1 + test/riscv/qemu@virt32_defconfig.yaml | 1 + 7 files changed, 230 insertions(+) create mode 100644 test/py/test_bootchooser.py diff --git a/common/boards/qemu-virt/qemu-virt-flash.dtso b/common/boards/qemu-virt/qemu-virt-flash.dtso index 087568a26d2a..77b17456496d 100644 --- a/common/boards/qemu-virt/qemu-virt-flash.dtso +++ b/common/boards/qemu-virt/qemu-virt-flash.dtso @@ -94,6 +94,11 @@ last_chosen@10 { reg = <0x10 0x4>; type = "uint32"; }; + + attempts_locked@14 { + reg = <0x14 0x4>; + type = "uint32"; + }; }; }; }; diff --git a/test/arm/virt@multi_v7_defconfig.yaml b/test/arm/virt@multi_v7_defconfig.yaml index 637d132d3967..2f68e35d69a8 100644 --- a/test/arm/virt@multi_v7_defconfig.yaml +++ b/test/arm/virt@multi_v7_defconfig.yaml @@ -15,6 +15,7 @@ targets: features: - virtio-mmio - testfs + - barebox-state images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" imports: diff --git a/test/arm/virt@multi_v8_defconfig.yaml b/test/arm/virt@multi_v8_defconfig.yaml index d018af06aa53..f49dbce1b631 100644 --- a/test/arm/virt@multi_v8_defconfig.yaml +++ b/test/arm/virt@multi_v8_defconfig.yaml @@ -16,6 +16,7 @@ targets: - virtio-mmio - network - testfs + - barebox-state runner: tuxmake_arch: arm64 images: diff --git a/test/py/helper.py b/test/py/helper.py index cfcc9c337fec..04c2de9bc0da 100644 --- a/test/py/helper.py +++ b/test/py/helper.py @@ -1,6 +1,8 @@ from labgrid.driver import BareboxDriver import pytest import os +import re +import shlex from itertools import filterfalse @@ -45,6 +47,67 @@ def of_get_property(barebox, path): return False +def devinfo(barebox, device): + info = {} + section = None + pattern = r'^\s*([^:]+):\s*(.*)$' + + for line in barebox.run_check(f"devinfo {device}"): + line = line.rstrip() + if match := re.match(r"^([^: ]+):$", line): + section = match.group(1) + if section in ["Parameters"]: + info[section] = {} + else: + info[section] = [] + continue + + line = line.strip() + if section is None or isinstance(info[section], dict): + if match := re.match(pattern, line): + key = match.group(1).strip() + value = match.group(2).strip() + # TODO: coerce to type? + if section is None: + info[section] = line + else: + info[section][key] = value + elif section: + info[section].append(line) + + return info + + +def format_dict_with_prefix(varset: dict, prefix: str) -> str: + parts = [] + for k, v in varset.items(): + escaped_val = shlex.quote(str(v)) + parts.append(f"{prefix}{k}={escaped_val}") + return " ".join(parts) + + +def globalvars_set(barebox, varset: dict, create=True): + cmd, prefix = ("global ", "") if create else ("", "global.") + barebox.run_check(cmd + format_dict_with_prefix(varset, prefix)) + + +def nvvars_set(barebox, varset: dict, create=True): + cmd, prefix = ("nv ", "") if create else ("", "nv.") + barebox.run_check(cmd + format_dict_with_prefix(varset, prefix)) + + +def getenv_int(barebox, var): + return int(barebox.run_check(f"echo ${var}")[0]) + + +def getstate_int(barebox, var, prefix="state.bootstate"): + return getenv_int(barebox, f"{prefix}.{var}") + + +def getparam_int(info, var): + return int(info["Parameters"][var].split()[0]) + + def skip_disabled(config, *options): if bool(config): undefined = list(filterfalse(config.__contains__, options)) diff --git a/test/py/test_bootchooser.py b/test/py/test_bootchooser.py new file mode 100644 index 000000000000..a5f2b25d6550 --- /dev/null +++ b/test/py/test_bootchooser.py @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +import pytest +from .helper import globalvars_set, devinfo, getparam_int, \ + getstate_int, getenv_int + + +def fit_name(suffix): + return f"/mnt/9p/testfs/barebox-{suffix}.fit" + + +def generate_bootscript(barebox, command, name="test"): + barebox.run_check(f"echo -o /env/boot/{name} '#!/bin/sh'") + barebox.run_check(f"echo -a /env/boot/{name} '{command}'") + return name + + +def assert_state(barebox, s0p, s0a, s1p, s1a): + info = devinfo(barebox, "state") + + assert getparam_int(info, "bootstate.system0.priority") == s0p + assert getparam_int(info, "bootstate.system0.remaining_attempts") == s0a + assert getparam_int(info, "bootstate.system1.priority") == s1p + assert getparam_int(info, "bootstate.system1.remaining_attempts") == s1a + + +def assert_boot(barebox, system_num, s0p, s0a, s1p, s1a): + expected = system_num + unexpected = system_num ^ 1 + + stdout, _, _ = barebox.run("boot bootchooser") + assert f"Test is booting system{expected}" in stdout + assert f"Test is booting system{unexpected}" not in stdout + + assert_state(barebox, s0p, s0a, s1p, s1a) + + +@pytest.fixture(scope="function") +def bootchooser(barebox, env): + if 'barebox-state' not in env.get_target_features(): + return None + + system0 = generate_bootscript(barebox, "echo Test is booting system0", + "system0") + system1 = generate_bootscript(barebox, "echo Test is booting system1", + "system1") + + globals_chg = { + "bootchooser.default_attempts": "3", + "bootchooser.retry": "0", + "bootchooser.state_prefix": "state.bootstate", + "bootchooser.targets": "system0 system1", + } + + globals_new = { + "bootchooser.system0.boot": system0, + "bootchooser.system0.default_priority": "20", + "bootchooser.system1.boot": system1, + "bootchooser.system1.default_priority": "21" + } + + globalvars_set(barebox, globals_chg) + globalvars_set(barebox, globals_new, create=True) + + barebox.run_check("bootchooser -a default -p default") + return True + + +def test_bootchooser(barebox, env, bootchooser): + if bootchooser is None: + pytest.xfail("barebox-state feature not specified") + + assert_state(barebox, s0p=20, s0a=3, s1p=21, s1a=3) + + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=2) + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=1) + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=2, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=1, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=0, s1p=21, s1a=0) + + barebox.run_check("bootchooser -a default -p default") + + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=2) + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=1) + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=2, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=1, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=0, s1p=21, s1a=0) + + +def test_bootchooser_lock_command(barebox, env, bootchooser): + if bootchooser is None: + pytest.xfail("barebox-state feature not specified") + + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=2) + + stdout = barebox.run_check("bootchooser -i") + assert 'Locking of boot attempt counter: disabled' in stdout[-3:] + assert getstate_int(barebox, "attempts_locked") == 0 + + barebox.run_check("bootchooser -l") + + stdout = barebox.run_check("bootchooser -i") + assert 'Locking of boot attempt counter: enabled' in stdout[-3:] + assert getstate_int(barebox, "attempts_locked") == 0 + + for i in range(4): + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=2) + + barebox.run_check("bootchooser -L") + + stdout = barebox.run_check("bootchooser -i") + assert 'Locking of boot attempt counter: disabled' in stdout[-3:] + assert getstate_int(barebox, "attempts_locked") == 0 + + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=1) + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=2, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=1, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=0, s1p=21, s1a=0) + + +def test_bootchooser_lock_state(barebox, env, bootchooser): + if bootchooser is None: + pytest.xfail("barebox-state feature not specified") + + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=2) + + stdout = barebox.run_check("bootchooser -i") + assert 'Locking of boot attempt counter: disabled' in stdout[-3:] + assert getstate_int(barebox, "attempts_locked") == 0 + + barebox.run_check("state.bootstate.attempts_locked=1") + assert getenv_int(barebox, "state.dirty") == 1 + stdout = barebox.run_check("state -s") + assert getenv_int(barebox, "state.dirty") == 0 + + stdout = barebox.run_check("bootchooser -i") + assert 'Locking of boot attempt counter: enabled' in stdout[-3:] + assert getstate_int(barebox, "attempts_locked") == 1 + + for i in range(4): + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=2) + + assert getenv_int(barebox, "state.dirty") == 0 + + barebox.run_check("state.bootstate.attempts_locked=0") + + stdout = barebox.run_check("bootchooser -i") + assert 'Locking of boot attempt counter: disabled' in stdout[-3:] + assert getstate_int(barebox, "attempts_locked") == 0 + + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=1) + assert_boot(barebox, 1, s0p=20, s0a=3, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=2, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=1, s1p=21, s1a=0) + assert_boot(barebox, 0, s0p=20, s0a=0, s1p=21, s1a=0) diff --git a/test/riscv/qemu-virt64@rv64i_defconfig.yaml b/test/riscv/qemu-virt64@rv64i_defconfig.yaml index c920413a17bf..83cdac6c8b9d 100644 --- a/test/riscv/qemu-virt64@rv64i_defconfig.yaml +++ b/test/riscv/qemu-virt64@rv64i_defconfig.yaml @@ -14,6 +14,7 @@ targets: BareboxTestStrategy: {} features: - virtio-mmio + - barebox-state images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" imports: diff --git a/test/riscv/qemu@virt32_defconfig.yaml b/test/riscv/qemu@virt32_defconfig.yaml index da1bb5bddd3e..03d5ef281592 100644 --- a/test/riscv/qemu@virt32_defconfig.yaml +++ b/test/riscv/qemu@virt32_defconfig.yaml @@ -15,6 +15,7 @@ targets: BareboxTestStrategy: {} features: - virtio-mmio + - barebox-state runner: download: opensbi-riscv32-generic-fw_dynamic.bin: https://github.com/qemu/qemu/blob/v5.2.0/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin?raw=true -- 2.39.5