Files
ninlight-w600/tools/mkimage/test_nl_mkimage.py
T
Michael SauerandClaude Haiku 4.5 0ba269a39a feat: nl_mkimage prüft Images mit --verify
deploy.sh soll kein Image auf die Station schieben, das jede Lampe
ablehnen würde. Die Prüfung sitzt in nl_mkimage, weil dort das
Trailer-Layout ohnehin steht - eine zweite Kopie derselben Offsets
wäre genau die Doppelung, die beim Abweichen jedes Update killt.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-09-12 12:25:02 +02:00

103 lines
3.1 KiB
Python

import hashlib
import subprocess
import sys
from pathlib import Path
SCRIPT = Path(__file__).with_name("nl_mkimage.py")
def build(tmp_path, payload=b"\x01\x02\x03" * 100, device="evk", version="1.2.3"):
src = tmp_path / "fw_gz.img"
src.write_bytes(payload)
out = tmp_path / "fw_ota.img"
subprocess.run(
[sys.executable, str(SCRIPT), "--image", str(src), "--device", device,
"--version", version, "--out", str(out)],
check=True,
)
return src, out
def test_appends_512_byte_trailer(tmp_path):
src, out = build(tmp_path)
data = out.read_bytes()
assert len(data) == len(src.read_bytes()) + 512
assert data[: len(src.read_bytes())] == src.read_bytes()
trailer = data[-512:]
assert trailer[0:4] == b"NINL"
assert trailer[4] == 1
assert trailer[5:21].rstrip(b"\x00") == b"evk"
assert trailer[21:37].rstrip(b"\x00") == b"1.2.3"
assert trailer[37:69] == hashlib.sha256(src.read_bytes()).digest()
assert set(trailer[69:]) == {0xFF}
def test_rejects_long_fields(tmp_path):
src = tmp_path / "fw_gz.img"
src.write_bytes(b"x")
r = subprocess.run(
[sys.executable, str(SCRIPT), "--image", str(src), "--device", "0123456789abcdef",
"--version", "1", "--out", str(tmp_path / "o.img")],
capture_output=True, text=True,
)
assert r.returncode != 0
assert "device" in r.stderr
def test_refuses_image_that_already_has_a_trailer(tmp_path):
_, out = build(tmp_path)
r = subprocess.run(
[sys.executable, str(SCRIPT), "--image", str(out), "--device", "evk",
"--version", "1.2.3", "--out", str(tmp_path / "again.img")],
capture_output=True, text=True,
)
assert r.returncode != 0
assert "trailer" in r.stderr
def verify(path, *extra):
return subprocess.run(
[sys.executable, str(SCRIPT), "--verify", str(path), *extra],
capture_output=True, text=True,
)
def test_verify_accepts_a_good_image(tmp_path):
_, out = build(tmp_path, device="e27_rgb", version="1.2.3")
r = verify(out)
assert r.returncode == 0, r.stderr
assert r.stdout.split()[:2] == ["e27_rgb", "1.2.3"]
def test_verify_rejects_a_file_without_trailer(tmp_path):
p = tmp_path / "plain.img"
p.write_bytes(b"x" * 5000)
r = verify(p)
assert r.returncode == 1
assert "trailer" in r.stderr.lower()
def test_verify_rejects_a_corrupted_payload(tmp_path):
_, out = build(tmp_path)
d = bytearray(out.read_bytes())
d[100] ^= 0x01
out.write_bytes(d)
r = verify(out)
assert r.returncode == 1
assert "sha256" in r.stderr.lower()
def test_verify_checks_the_expected_device(tmp_path):
_, out = build(tmp_path, device="evk")
assert verify(out, "--expect-device", "evk").returncode == 0
r = verify(out, "--expect-device", "g95_cw")
assert r.returncode == 1
assert "evk" in r.stderr and "g95_cw" in r.stderr
def test_verify_rejects_a_file_shorter_than_the_trailer(tmp_path):
p = tmp_path / "tiny.img"
p.write_bytes(b"NINL" + b"\x00" * 10)
r = verify(p)
assert r.returncode == 1