VoxIP Kiosk OS 1.0.0: Raspberry Pi kiosk with web management
CI / test-bookworm (push) Failing after 11s
CI / test-latest (push) Successful in 1m27s

Chromium kiosk without a desktop for the Raspberry Pi 3, a Flask web
interface on port 8080 (kiosk, system and network management), a
restricted root helper, network changes with automatic rollback, a
healthcheck timer, install/update/uninstall scripts and a pi-gen image
build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rothmanandClaude Opus 5.5 committed 2026-11-04 21:38:49 +01:00
commit a33c4822bf
96 files changed
+7535

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
import json
from pathlib import Path
from voxip_kiosk import browser as b
from voxip_kiosk.config import DEFAULTS
XRANDR = """Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 7680 x 7680
HDMI-1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 531mm x 299mm
1920x1080 60.00*+ 50.00 59.94
1280x720 60.00 50.00 59.94
720x576 50.00
HDMI-2 disconnected (normal left inverted right x axis y axis)
"""
def test_build_args_never_weakens_tls_and_binds_devtools_locally(tmp_path):
args = b.build_args("/usr/bin/chromium", dict(DEFAULTS), tmp_path / "p", tmp_path / "c", "file:///start.html", (1920, 1080))
joined = " ".join(args)
for flag in b.FORBIDDEN_FLAGS:
assert flag not in joined
assert "--remote-debugging-address=127.0.0.1" in args
assert "--kiosk" in args and "--noerrdialogs" in args and "--disable-session-crashed-bubble" in args
assert "--window-size=1920,1080" in args
assert args[-1] == "file:///start.html"
assert not any(a.startswith("--force-device-scale-factor") for a in args)
def test_build_args_zoom(tmp_path):
args = b.build_args("/usr/bin/chromium", dict(DEFAULTS, zoom=1.5), tmp_path, tmp_path, "x")
assert "--force-device-scale-factor=1.5" in args
def test_parse_xrandr():
outputs = b.parse_xrandr(XRANDR)
assert len(outputs) == 1
out = outputs[0]
assert out["name"] == "HDMI-1"
assert out["modes"] == ["1920x1080", "1280x720", "720x576"]
assert out["current"] == "1920x1080@60.00"
assert out["preferred"] == "1920x1080"
assert out["size"] == (1920, 1080)
def test_xrandr_args_rotation_and_resolution():
out = b.parse_xrandr(XRANDR)[0]
assert b.xrandr_args(out, dict(DEFAULTS, rotation=90)) == ["xrandr", "--output", "HDMI-1", "--auto", "--rotate", "right"]
assert b.xrandr_args(out, dict(DEFAULTS, rotation=270, resolution="1280x720@50")) == [
"xrandr", "--output", "HDMI-1", "--mode", "1280x720", "--rate", "50", "--rotate", "left"]
# unsupported mode falls back to auto instead of failing
assert "--auto" in b.xrandr_args(out, dict(DEFAULTS, resolution="3840x2160"))
def test_xset_commands():
assert ["xset", "-dpms"] in b.xset_commands(dict(DEFAULTS))
cmds = b.xset_commands(dict(DEFAULTS, screen_always_on=False, screen_off_minutes=15))
assert ["xset", "+dpms"] in cmds and ["xset", "dpms", "0", "0", "900"] in cmds
def test_clean_profile_removes_locks_and_crash_flag(tmp_path):
profile = tmp_path / "profile"
(profile / "Default").mkdir(parents=True)
(profile / "SingletonLock").symlink_to("kiosk-1234")
(profile / "Default" / "Preferences").write_text(json.dumps({"profile": {"exit_type": "Crashed", "exited_cleanly": False}, "x": 1}))
b.clean_profile(profile)
assert not (profile / "SingletonLock").is_symlink()
prefs = json.loads((profile / "Default" / "Preferences").read_text())
assert prefs["profile"] == {"exit_type": "Normal", "exited_cleanly": True}
assert prefs["x"] == 1
def test_start_page_url_contains_target(paths):
url = b.start_page_url(paths, dict(DEFAULTS, url="https://e.org/x?token=abc", device_name="Hal 1"))
assert url.startswith(f"file://{paths.www_dir}/start.html?")
assert "next=https%3A%2F%2Fe.org%2Fx%3Ftoken%3Dabc" in url and "name=Hal+1" in url
def test_local_pages_exist():
www = Path(__file__).resolve().parents[1] / "www"
for page in ("start.html", "offline.html", "setup.html", "kiosk.css", "logo.svg"):
assert (www / page).exists()