Files
OBLITERATUS/tests/test_jetson_support_tooling.py
T

259 lines
8.8 KiB
Python

"""CPU-testable contracts for the experimental Jetson support path."""
from __future__ import annotations
import importlib.metadata
import json
import sys
from pathlib import Path
from types import SimpleNamespace
import pytest
import yaml
try:
import tomllib
except ModuleNotFoundError: # pragma: no cover - exercised by the Python 3.10 CI lane
import tomli as tomllib
from scripts import jetson_support
from scripts import run_conditional_gate
from scripts import setup_jetson
ROOT = Path(__file__).parents[1]
def test_host_evidence_is_allow_listed(monkeypatch, tmp_path):
tegra = tmp_path / "nv_tegra_release"
tegra.write_text("# R36 (release), REVISION: 4.3\nserial=secret\n")
os_release = tmp_path / "os-release"
os_release.write_text(
'ID=ubuntu\nVERSION_ID="22.04"\nPRETTY_NAME="Ubuntu 22.04"\nSECRET=value\n'
)
monkeypatch.setattr(jetson_support.platform, "machine", lambda: "aarch64")
monkeypatch.setattr(jetson_support.platform, "python_version", lambda: "3.10.12")
monkeypatch.setattr(jetson_support, "_capture", lambda command, **kwargs: "6.2+b17")
facts = jetson_support.collect_host_facts(
tegra_release=tegra,
os_release=os_release,
)
assert facts == {
"architecture": "aarch64",
"python_version": "3.10.12",
"os": {
"id": "ubuntu",
"version_id": "22.04",
"pretty_name": "Ubuntu 22.04",
},
"l4t_release": "# R36 (release), REVISION: 4.3",
"jetpack_package": "6.2+b17",
}
assert "secret" not in json.dumps(facts).lower()
def test_runtime_evidence_reports_cuda_without_device_identity(monkeypatch):
properties = SimpleNamespace(name="Orin", total_memory=64 * 1024**3)
fake_cuda = SimpleNamespace(
is_available=lambda: True,
device_count=lambda: 1,
get_device_properties=lambda _index: properties,
get_device_capability=lambda _index: (8, 7),
)
fake_torch = SimpleNamespace(
__version__="2.8.0a0+nv25.06",
version=SimpleNamespace(cuda="12.6"),
cuda=fake_cuda,
)
monkeypatch.setitem(sys.modules, "torch", fake_torch)
def missing_package(_name):
raise importlib.metadata.PackageNotFoundError
monkeypatch.setattr(jetson_support.importlib.metadata, "version", missing_package)
facts = jetson_support.collect_runtime_facts()
assert facts["cuda_available"] is True
assert facts["device_name"] == "Orin"
assert facts["compute_capability"] == [8, 7]
assert facts["total_memory_gb"] == 64.0
assert set(facts) == {
"torch_imported",
"torch_version",
"torch_cuda_version",
"cuda_available",
"cuda_device_count",
"device_name",
"compute_capability",
"total_memory_gb",
"bitsandbytes_version",
}
def test_report_validation_blocks_non_jetson_or_non_cuda_and_warns_on_bnb():
report = {
"host": {"architecture": "x86_64", "l4t_release": None},
"runtime": {
"torch_imported": True,
"torch_cuda_version": None,
"cuda_available": False,
"bitsandbytes_version": "0.47.0",
},
}
errors, warnings = jetson_support.validate_report(report)
assert len(errors) == 3
assert any("ARM64" in error for error in errors)
assert any("Jetson L4T" in error for error in errors)
assert any("not a CUDA build" in error for error in errors)
assert warnings and "unsupported" in warnings[0]
def test_gate_evidence_and_issue_body_cannot_copy_arbitrary_fields(tmp_path):
evidence = tmp_path / "gate.json"
evidence.write_text(json.dumps({
"gate": "jetson-runtime",
"status": "passed",
"git_sha": "a" * 40,
"counts": {"tests": 1, "token": "nested-secret"},
"token": "must-not-escape",
"hostname": "must-not-escape",
}))
summary = jetson_support._gate_summary(evidence)
body = jetson_support.issue_body({"gate_evidence": summary})
assert summary == {
"gate": "jetson-runtime",
"status": "passed",
"git_sha": "a" * 40,
"counts": {"tests": 1},
}
assert "must-not-escape" not in body
assert "nested-secret" not in body
assert "excludes environment variables" in body
def test_dependency_export_rejects_torch_and_bitsandbytes(tmp_path):
requirements = tmp_path / "requirements.txt"
requirements.write_text("transformers==4.56.0\npytest==8.4.1\n")
setup_jetson._require_exclusions(requirements)
for forbidden in (
"torch==2.8.0",
"torch @ https://example.invalid/torch.whl",
"bitsandbytes[diagnostics]==0.47.0",
):
requirements.write_text(forbidden + "\n")
with pytest.raises(RuntimeError, match="forbidden packages"):
setup_jetson._require_exclusions(requirements)
def test_jetson_bootstrap_preserves_vendor_runtime_and_uses_locked_no_deps(
monkeypatch,
tmp_path,
):
project = tmp_path / "project"
(project / "scripts").mkdir(parents=True)
(project / "scripts" / "jetson_support.py").write_text("# fixture\n")
(project / "uv.lock").write_text("# fixture\n")
venv = tmp_path / "jetson-venv"
commands: list[list[str]] = []
def fake_run(command, *, cwd):
command = list(command)
commands.append(command)
if command[1:4] == ["-m", "venv", "--system-site-packages"]:
(venv / "bin").mkdir(parents=True)
(venv / "pyvenv.cfg").write_text("include-system-site-packages = true\n")
if "--output-file" in command:
output = Path(command[command.index("--output-file") + 1])
output.write_text("transformers==4.56.0\n")
monkeypatch.setattr(setup_jetson, "_run", fake_run)
setup_jetson.prepare(
project=project,
venv=venv,
python="vendor-python",
uv_python="tool-python",
reuse=False,
)
assert commands[0][0] == "vendor-python"
assert "--check" in commands[0]
export = next(command for command in commands if "export" in command)
assert export.count("--no-emit-package") == 2
assert "torch" in export and "bitsandbytes" in export
installs = [command for command in commands if "install" in command]
assert len(installs) == 2
assert all("--no-deps" in command for command in installs)
assert any("check" in command for command in commands)
def test_bootstrap_rejects_unsafe_or_non_vendor_reusable_targets(tmp_path):
project = tmp_path / "project"
project.mkdir()
with pytest.raises(ValueError, match="unsafe"):
setup_jetson._require_safe_target(project, project)
existing = tmp_path / "existing"
existing.mkdir()
(existing / "pyvenv.cfg").write_text("include-system-site-packages = false\n")
with pytest.raises(ValueError, match="does not expose JetPack"):
setup_jetson._require_new_or_reusable_venv(existing, reuse=True)
def test_bitsandbytes_is_opt_in_for_quantization_only():
metadata = tomllib.loads((ROOT / "pyproject.toml").read_text())
base = metadata["project"]["dependencies"]
extras = metadata["project"]["optional-dependencies"]
assert not any(value.startswith("bitsandbytes") for value in base)
assert extras["quantization"] == ["bitsandbytes>=0.46.1"]
def test_jetson_issue_form_requires_reproducible_sanitized_hardware_evidence():
form = yaml.safe_load(
(ROOT / ".github" / "ISSUE_TEMPLATE" / "jetson-runtime.yml").read_text(),
)
fields = {value.get("id"): value for value in form["body"] if value.get("id")}
assert set(fields) == {
"device",
"jetpack",
"commit",
"reproduction",
"expected",
"actual",
"evidence",
"confirmations",
}
assert all(value.get("validations", {}).get("required") for value in fields.values())
confirmations = fields["confirmations"]["attributes"]["options"]
assert all(option["required"] for option in confirmations)
assert any("secrets" in option["label"] for option in confirmations)
def test_jetson_conditional_prerequisites_are_physical_and_cuda(monkeypatch, tmp_path):
fake_torch = SimpleNamespace(cuda=SimpleNamespace(is_available=lambda: True))
monkeypatch.setitem(sys.modules, "torch", fake_torch)
monkeypatch.setattr(run_conditional_gate.platform, "machine", lambda: "aarch64")
tegra = tmp_path / "nv_tegra_release"
tegra.write_text("# R36\n")
monkeypatch.setattr(run_conditional_gate, "JETSON_RELEASE", tegra)
assert run_conditional_gate.missing_prerequisites("jetson-runtime") == []
monkeypatch.setattr(run_conditional_gate.platform, "machine", lambda: "x86_64")
fake_torch.cuda.is_available = lambda: False
tegra.unlink()
assert run_conditional_gate.missing_prerequisites("jetson-runtime") == [
"a CUDA-capable PyTorch runtime",
"an ARM64 host",
"a Jetson L4T runtime",
]