Files
2026-10-23 05:56:55 -05:00

152 lines
6.3 KiB
Python

"""Evaluate the actual SCons graph with fake nodes; never execute compilers."""
import ctypes.util
import shlex
from pathlib import Path
import sys
from types import ModuleType, SimpleNamespace
from unittest.mock import MagicMock, Mock
import pytest
SOURCE = Path(__file__).resolve().parents[3] / "selfdrive/modeld/SConscript"
EXPECTED_TARGETS = {
"dmonitoring_model_tinygrad.pkl",
"driving_warp_1344x760_tinygrad.pkl", "driving_warp_1928x1208_tinygrad.pkl",
"dm_warp_1344x760_tinygrad.pkl", "dm_warp_1928x1208_tinygrad.pkl",
}
class Returned(Exception):
pass
@pytest.fixture
def graph(monkeypatch, tmp_path):
script = ModuleType("SCons.Script")
script.Action = lambda command, description: command
script.Value = lambda value: value
monkeypatch.setitem(sys.modules, script.__name__, script)
helpers = ModuleType("openpilot.selfdrive.modeld.helpers")
helpers.chestnut_present = Mock(return_value=False)
monkeypatch.setitem(sys.modules, helpers.__name__, helpers)
monkeypatch.delenv("COMMA_LAPTOP_CROSS_BUILD", raising=False)
env = MagicMock()
build_env = {"ENV": {}}
env.__getitem__.side_effect = build_env.__getitem__
env.Clone.return_value = env
def node(path):
relative = path.removeprefix("#")
return SimpleNamespace(abspath=str(tmp_path / relative), relpath=relative)
env.Dir.side_effect = node
for compiler in ("tinygrad_repo", "tinygrad_model_runtime/legacy9d/tinygrad_repo"):
marker = tmp_path / compiler / "compiler_dependency.py"
marker.parent.mkdir(parents=True)
marker.write_text("# compiler dependency witness\n")
def stop():
raise Returned()
def evaluate(arch):
scope = {"env": env, "arch": arch, "Import": Mock(), "Return": stop, "Dir": node, "File": node}
try:
exec(compile(SOURCE.read_text(), str(SOURCE), "exec"), scope)
except Returned:
pass
return scope
return evaluate, env, helpers.chestnut_present
def test_missing_llvm_pc_skips_graph_before_probe_or_compilation(graph, monkeypatch, capsys):
evaluate, env, present = graph
llvm = Mock(return_value=None)
monkeypatch.setattr(ctypes.util, "find_library", llvm)
evaluate("aarch64")
llvm.assert_called_once_with("LLVM")
env.Clone.assert_not_called()
env.Command.assert_not_called()
present.assert_not_called()
assert "Install LLVM to compile models" in capsys.readouterr().out
@pytest.mark.parametrize("arch,llvm,backend", [
("aarch64", "libLLVM.so", "DEV=CPU:LLVM"),
("Darwin", None, "DEV=METAL HCQ_RUNTIME_DEV=CPU:CLANG JIT=2"),
("comma_arm64", None, "DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1"),
])
def test_supported_backend_retains_models_and_camera_warp_graph(graph, monkeypatch, arch, llvm, backend):
evaluate, env, present = graph
library = Mock(return_value=llvm)
monkeypatch.setattr(ctypes.util, "find_library", library)
evaluate(arch)
assert library.call_count == (1 if arch == "aarch64" else 0)
present.assert_not_called()
calls = env.Command.call_args_list
assert len(calls) == 5
targets = [Path(call.args[0]).name for call in calls]
assert set(targets) == EXPECTED_TARGETS
assert targets[0] == "dmonitoring_model_tinygrad.pkl"
assert "driving_tinygrad.pkl" not in targets
assert sum(name.startswith("driving_warp_") for name in targets) == 2
assert sum(name.startswith("dm_warp_") for name in targets) == 2
compiler = "tinygrad_model_runtime/legacy9d/tinygrad_repo" if arch == "comma_arm64" else "tinygrad_repo"
compiler_root = Path(calls[0].args[0]).parent.parent / compiler
# Commands and their dependency Values must describe the same selected compiler.
# Token checks retain every required flag without requiring adjacent assignments.
for call in calls:
target, dependencies, command = call.args
tokens = shlex.split(command)
assert set(shlex.split(backend)) <= set(tokens)
assert str(compiler_root / "examples/openpilot" / ("compile_onnx.py" if target.endswith("dmonitoring_model_tinygrad.pkl")
else "compile_warp.py")) in tokens
assert "#" + str(Path(compiler) / "compiler_dependency.py") in dependencies
assert command in dependencies
if "_warp_" in target:
warp_dependency = "openpilot/system/camerad/cameras/nv12_info.py"
assert any(getattr(source, "relpath", None) == warp_dependency and
getattr(source, "abspath", None) == str(Path(target).parent.parent / warp_dependency)
for source in dependencies)
assert "--frame" in tokens and "--warp-to" in tokens
dimensions = Path(target).name.split("_warp_", 1)[1].split("_tinygrad.pkl", 1)[0]
assert tokens[tokens.index("--frame") + 1].split(",")[:2] == dimensions.split("x")
assert tokens[tokens.index("--output") + 1] == target
assert tokens[tokens.index("--layout") + 1] == ("luma" if Path(target).name.startswith("dm_") else "yuv420")
else:
assert str(Path(target).parent / "dmonitoring_model.onnx") in dependencies
env.PrependENVPath.assert_called_once_with("PYTHONPATH", str(compiler_root))
if arch == "comma_arm64":
assert env["ENV"]["PYTHONDONTWRITEBYTECODE"] == "1"
assert env["ENV"]["CACHEDB"].endswith("/9d0446a4ba8a532c8b674fb6ad795af015cd9dcf/tinygrad.db")
assert env["ENV"]["XDG_CACHE_HOME"].endswith("/9d0446a4ba8a532c8b674fb6ad795af015cd9dcf/xdg")
else:
assert env["ENV"] == {}
env.Execute.assert_not_called()
def test_llvm_probe_error_does_not_schedule_compilation(graph, monkeypatch):
evaluate, env, present = graph
monkeypatch.setattr(ctypes.util, "find_library", Mock(side_effect=OSError("probe failed")))
with pytest.raises(OSError, match="probe failed"):
evaluate("aarch64")
env.Command.assert_not_called()
present.assert_not_called()
def test_build_does_not_probe_chestnut_or_regenerate_shipped_big_artifacts(graph, monkeypatch):
evaluate, env, present = graph
present.return_value = True
library = Mock()
monkeypatch.setattr(ctypes.util, "find_library", library)
evaluate("Darwin")
library.assert_not_called()
present.assert_not_called()
assert env.Command.call_count == 5
assert {Path(call.args[0]).name for call in env.Command.call_args_list} == EXPECTED_TARGETS
assert not any(Path(call.args[0]).name.startswith("big_driving_") for call in env.Command.call_args_list)
env.SideEffect.assert_not_called()
env.Execute.assert_not_called()