Add tests for the globalvar scope around boot entries: a failing boot
script that changes, creates and removes global.bootm.* and
global.linux.bootargs.* variables and changes global.of.overlay.path and
global.linux.bootargs_append, where the configuration has them, must
leave them as they were before, while globalvars outside the scope keep
their new values. A
second test nests boot entries like devboot does and checks that the
outer entry's variables are visible in the inner one and survive it.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Sascha Hauer <[email protected]>
---
 test/py/test_boot_globalvar_scope.py | 117 +++++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/test/py/test_boot_globalvar_scope.py 
b/test/py/test_boot_globalvar_scope.py
new file mode 100644
index 0000000000..041d38c814
--- /dev/null
+++ b/test/py/test_boot_globalvar_scope.py
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from .helper import devinfo, skip_disabled
+
+
+def write_bootscript(barebox, name, lines):
+    barebox.run_check(f"echo -o /env/boot/{name} '#!/bin/sh'")
+    for line in lines:
+        # Variable references must reach the script unexpanded. Neither
+        # quoting nor a backslash achieves that here, so splice in the
+        # dollar sign from a variable.
+        line = line.replace("$", "${dollar}")
+        barebox.run_check(f"dollar='$'; echo -a /env/boot/{name} \"{line}\"")
+
+
+def globals_now(barebox):
+    params = devinfo(barebox, "global")["Parameters"]
+    # devinfo prints "value (type: ...)", strip the annotation
+    return {k: v.split(" (type:")[0].strip('"') for k, v in params.items()}
+
+
[email protected](scope="function")
+def scope_globals(barebox, barebox_config):
+    skip_disabled(barebox_config, "CONFIG_CMD_BOOT", "CONFIG_CMD_GLOBAL",
+                  "CONFIG_CMD_ECHO")
+
+    barebox.run_check("global.bootm.image=/preset/image")
+    barebox.run_check("global linux.bootargs.preset=preset=1")
+    barebox.run_check("global linux.bootargs.removed=removed=1")
+    barebox.run_check("global.bootm.appendroot=0")
+    old_user = barebox.run_check("echo ${global.user}")[0]
+
+    yield old_user
+
+    barebox.run_check(f"global.user={old_user}")
+    barebox.run("global -r linux.bootargs.preset")
+    barebox.run("global -r linux.bootargs.removed")
+    barebox.run_check("global.bootm.image=")
+
+
+def test_boot_entry_reverts_globalvars(barebox, scope_globals):
+    old_user = scope_globals
+    before = globals_now(barebox)
+
+    # boot related globalvars outside of bootm.* and linux.bootargs.*,
+    # where the configuration has them
+    others = {var: val for var, val in (("of.overlay.path", "/does/not/exist"),
+                                        ("linux.bootargs_append", "1"))
+              if var in before}
+
+    write_bootscript(barebox, "scope", [
+        *(f"global.{var}={val}" for var, val in others.items()),
+        "global.bootm.image=/does/not/exist",
+        "global.bootm.oftree=/does/not/exist.dtb",
+        "global.bootm.appendroot=1",
+        "global.linux.bootargs.dyn.root=root=/dev/leak",
+        "global linux.bootargs.leak=leak=1",
+        "global.linux.bootargs.preset=preset=2",
+        "global -r linux.bootargs.removed",
+        "global.user=scope-test",
+    ])
+
+    # bootm fails on the nonexistent image, so boot returns
+    barebox.run("boot scope")
+
+    after = globals_now(barebox)
+
+    # values within the boot scope are restored...
+    assert after["bootm.image"] == "/preset/image"
+    assert after["bootm.oftree"] == before["bootm.oftree"]
+    assert after["bootm.appendroot"] == "0"
+    assert after["linux.bootargs.preset"] == "preset=1"
+    for var in others:
+        assert after[var] == before[var]
+
+    # ...vars created by the boot entry are gone...
+    assert "linux.bootargs.leak" not in after
+    assert "linux.bootargs.dyn.root" not in after
+    assert "linux.bootargs.dyn.ip" not in after
+
+    # ...vars removed by the boot entry are back...
+    assert after["linux.bootargs.removed"] == "removed=1"
+
+    # ...and the loadaddrs don't come back as 0xfffffffe after a failed boot
+    assert after["bootm.image.loadaddr"] == before["bootm.image.loadaddr"]
+
+    # globalvars outside the boot scope are left alone
+    assert after["user"] == "scope-test"
+    assert old_user != "scope-test"
+
+
[email protected]("scope_globals")
+def test_boot_entry_scope_nests(barebox):
+    # devboot-style: an outer entry sets up bootargs that must be visible
+    # in, and survive, the inner boot entry
+    write_bootscript(barebox, "scope-inner", [
+        "global.bootm.image=/does/not/exist",
+        "echo inner-sees ${global.linux.bootargs.outer}",
+        "global.linux.bootargs.outer=outer=clobbered",
+        "global linux.bootargs.inner=inner=1",
+    ])
+    write_bootscript(barebox, "scope-outer", [
+        "global linux.bootargs.outer=outer=1",
+        "boot scope-inner",
+        "echo outer-sees ${global.linux.bootargs.outer} "
+        "inner=${global.linux.bootargs.inner}",
+    ])
+
+    stdout, _, _ = barebox.run("boot scope-outer")
+    assert "inner-sees outer=1" in stdout
+    assert "outer-sees outer=1 inner=" in stdout
+
+    after = globals_now(barebox)
+    assert "linux.bootargs.outer" not in after
+    assert "linux.bootargs.inner" not in after
+    assert after["bootm.image"] == "/preset/image"

-- 
2.47.3


Reply via email to