Files
StarPilot/tools/ci/process_replay_coverage.py
T
2026-10-19 00:47:35 -05:00

72 lines
3.8 KiB
Python

"""Account for replay inputs separately from test execution and vehicle qualification."""
import json
from pathlib import Path
REQUIRED_REPLAY_LABELS = frozenset({
"HYUNDAI", "HYUNDAI2", "TOYOTA", "TOYOTA3", "HONDA", "HONDA2", "CHRYSLER", "RAM",
"SUBARU", "GM", "NISSAN", "VOLKSWAGEN", "MAZDA", "FORD", "RIVIAN", "TESLA",
})
VOLVO_UNRECORDED_PLATFORMS = frozenset({
"VOLVO_V40", "VOLVO_XC40_RECHARGE", "VOLVO_S60_RECHARGE", "POLESTAR_2",
})
VOLVO_REQUIRED_TESTS = {
"interfaces": ("opendbc_repo/opendbc/car/volvo/tests/test_c1.py",
"opendbc_repo/opendbc/car/volvo/tests/test_cma.py"),
"safety-debug": ("opendbc_repo/opendbc/safety/tests/test_volvo_c1.py",
"opendbc_repo/opendbc/safety/tests/test_volvo_cma.py"),
"safety-release": ("opendbc_repo/opendbc/safety/tests/test_volvo_c1.py",
"opendbc_repo/opendbc/safety/tests/test_volvo_cma.py"),
}
def replay_coverage(active_platforms, labels, suite_targets, *, full_test):
labels = set(labels)
recorded_brands = {label.lower() for label in labels}
errors = []
gaps = []
missing = set(active_platforms) - recorded_brands - {"body"}
if full_test:
absent_labels = REQUIRED_REPLAY_LABELS - labels
if absent_labels:
errors.append(f"Required replay inputs missing: {sorted(absent_labels)}")
if set(active_platforms.get("volvo", ())) != VOLVO_UNRECORDED_PLATFORMS:
errors.append("Volvo recording classification requires exactly the four known active platforms; review changed scope")
if "volvo" not in missing:
errors.append("Volvo recording classification is stale: Volvo now has a recording or is no longer active")
for suite, paths in VOLVO_REQUIRED_TESTS.items():
for path in paths:
if path not in suite_targets.get(suite, ()):
errors.append(f"Required Volvo source/native suite no longer selected: {suite}: {path}")
unaccounted = missing - {"volvo"}
if unaccounted:
errors.append(f"Active brands missing unaccounted replay inputs: {sorted(unaccounted)}")
if "volvo" in missing:
for platform in sorted(set(active_platforms.get("volvo", ())) & VOLVO_UNRECORDED_PLATFORMS):
path_index = 0 if platform == "VOLVO_V40" else 1
gaps.append({"brand": "volvo", "platforms": [platform], "status": "unrecorded",
"reason": f"No {platform} recording is available in the fixture catalog.",
"required_source_native_suites": {suite: paths[path_index] for suite, paths in VOLVO_REQUIRED_TESTS.items()}})
return {"schema_version": 1, "scope": "process_replay_input_inventory", "full_test": full_test,
"recorded_input_labels": sorted(labels), "active_platforms": active_platforms,
"unrecorded_gaps": gaps, "errors": errors,
"evidence_limit": "Input accounting only. Required source/native suites have independent execution results. This does not qualify the fleet."}
def write_coverage(report, destination, summary=None):
destination = Path(destination)
destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_text(json.dumps(report, sort_keys=True, indent=2) + "\n")
if summary:
with Path(summary).open("a") as output:
output.write("\n### Process replay input coverage\n\n")
output.write(f"Full input inventory: {report['full_test']}; recorded inputs: {len(report['recorded_input_labels'])}.\n\n")
for gap in report["unrecorded_gaps"]:
output.write(f"- {gap['brand']} / {', '.join(gap['platforms'])}: **unrecorded**. {gap['reason']}\n")
for suite, path in gap["required_source_native_suites"].items():
output.write(f" Required independent source/native check: {suite}: `{path}`.\n")
for error in report["errors"]:
output.write(f"- Coverage error: {error}\n")
output.write(f"\n{report['evidence_limit']}\n")