From: Ahmad Fatoum <a.fatoum@barebox.org>
To: barebox@lists.infradead.org
Cc: Lars Schmidt <l.schmidt@pengutronix.de>,
Ahmad Fatoum <a.fatoum@barebox.org>
Subject: [PATCH 3/3] test: py: add bootchooser attempt decrement and locking test
Date: Mon, 23 Jun 2025 08:26:41 +0200 [thread overview]
Message-ID: <20250623062641.3529615-4-a.fatoum@barebox.org> (raw)
In-Reply-To: <20250623062641.3529615-1-a.fatoum@barebox.org>
Add a test to verify that counting down attempts work and that the new
locking feature indeed inhibits it.
Cc: Lars Schmidt <l.schmidt@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@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
next prev parent reply other threads:[~2025-06-23 6:40 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-23 6:26 [PATCH 0/3] test: test recent regressions/features Ahmad Fatoum
2025-06-23 6:26 ` [PATCH 1/3] test: self: idr: add test for idr_remove in idr_for_each_entry Ahmad Fatoum
2025-06-23 6:26 ` [PATCH 2/3] test: py: test mount/unmount of pstore file system Ahmad Fatoum
2025-06-23 8:37 ` Ahmad Fatoum
2025-06-23 9:26 ` Sascha Hauer
2025-06-23 6:26 ` Ahmad Fatoum [this message]
2025-06-23 8:35 ` [PATCH 0/3] test: test recent regressions/features Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250623062641.3529615-4-a.fatoum@barebox.org \
--to=a.fatoum@barebox.org \
--cc=barebox@lists.infradead.org \
--cc=l.schmidt@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox