Add firmware verification harness
CI / Non-hardware verification (push) Has been cancelled

This commit is contained in:
2026-07-13 11:10:19 +02:00
parent def09160d0
commit 99c07ecda6
16 changed files with 1004 additions and 32 deletions
+131
View File
@@ -0,0 +1,131 @@
#!/usr/bin/env python3
from __future__ import annotations
import configparser
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if hasattr(sys.stderr, "reconfigure"):
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
REQUIRED_PATHS = [
"AGENTS.md",
".gitea/workflows/ci.yml",
"README.md",
"Requirements.md",
"docs/TESTS.md",
"docs/POWER_OPTIMIZATION.md",
"docs/engineering/current-state.md",
"docs/engineering/risk-register.md",
"docs/engineering/test-matrix.md",
"docs/engineering/hil-strategy.md",
"hil_tests/README.md",
]
TEST_SUITES = [
"test_html_escape",
"test_json_codec",
"test_lora_transport",
"test_meter_fault_count",
"test_payload_codec",
"test_refactor_smoke",
"test_security_fuzz",
]
TRACEABILITY_CONTRACTS = [
"LoRa frame format",
"CRC validation",
"Chunk reassembly",
"Payload schema",
"Sender-ID validation",
"ACK matching",
"Time bootstrap",
"Retry bounds",
"Queue bounds",
"Duplicate handling",
"MQTT JSON keys",
"Home Assistant discovery fields",
"CSV compatibility",
"HTML escaping",
"Web input validation",
]
def read_text(path: str) -> str:
return (ROOT / path).read_text(encoding="utf-8")
def platformio_envs() -> set[str]:
parser = configparser.RawConfigParser()
parser.optionxform = str
with (ROOT / "platformio.ini").open(encoding="utf-8") as handle:
parser.read_file(handle)
return {section.split(":", 1)[1] for section in parser.sections() if section.startswith("env:")}
def main() -> int:
errors: list[str] = []
for rel in REQUIRED_PATHS:
if not (ROOT / rel).exists():
errors.append(f"Missing required documentation or harness file: {rel}")
if errors:
for error in errors:
print(error)
return 1
envs = platformio_envs()
tests_doc = read_text("docs/TESTS.md")
readme = read_text("README.md")
requirements = read_text("Requirements.md")
current_state = read_text("docs/engineering/current-state.md")
risk_register = read_text("docs/engineering/risk-register.md")
test_matrix = read_text("docs/engineering/test-matrix.md")
agents = read_text("AGENTS.md")
for env in sorted(envs):
if f"`{env}`" not in tests_doc:
errors.append(f"docs/TESTS.md does not document environment {env}.")
for stale in ["lilygo-t3-v1-6-1", "schema v3", "schema `3`"]:
if stale in tests_doc:
errors.append(f"docs/TESTS.md still contains stale text: {stale}")
for suite in TEST_SUITES:
if suite not in tests_doc:
errors.append(f"docs/TESTS.md does not document suite {suite}.")
for contract in TRACEABILITY_CONTRACTS:
if contract not in tests_doc or contract not in test_matrix:
errors.append(f"Traceability contract missing from docs: {contract}")
schema_v4_markers = ["schema v4", "schema `4`", "schema `v4`"]
for path, text in [("README.md", readme), ("Requirements.md", requirements), ("current-state.md", current_state)]:
if not any(marker in text for marker in schema_v4_markers):
errors.append(f"{path} does not document payload schema v4.")
if "schema v3" in text or "schema `3`" in text:
errors.append(f"{path} still documents schema v3.")
for category in [
"Verified Defects",
"Probable Risks",
"Missing Tests",
"Missing Documentation",
"Security Decisions",
"Hardware-Dependent Questions",
]:
if category not in risk_register:
errors.append(f"risk-register.md missing category: {category}")
if "python tools/verify.py" not in agents:
errors.append("AGENTS.md does not name the canonical verification command.")
if errors:
for error in errors:
print(f"ERROR: {error}")
return 1
print("Documentation consistency checks passed.")
return 0
if __name__ == "__main__":
raise SystemExit(main())