mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-09-21 08:50:42 +02:00
Exact setuptools/wheel pins block platform-specific backend patches and make isolated pip installs fail on Android/Termux Python. Keep a setuptools lower bound only, drop the unused wheel backend pin, keep requirements.txt aligned with core package deps, and install the Spaces extra in Docker instead of pulling bitsandbytes by default. Android/Termux is still not a supported runtime; the new platform note documents host wheels, ANDROID_API_LEVEL, and --no-build-isolation so pip does not isolate a second setuptools or compile manylinux-only extensions.
279 lines
9.5 KiB
Python
279 lines
9.5 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_requirements_txt_mirrors_core_pyproject_dependencies():
|
|
metadata = tomllib.loads((ROOT / "pyproject.toml").read_text())
|
|
requirements = [
|
|
line.strip()
|
|
for line in (ROOT / "requirements.txt").read_text().splitlines()
|
|
if line.strip() and not line.startswith("#")
|
|
]
|
|
|
|
assert requirements == metadata["project"]["dependencies"]
|
|
assert not any(value.startswith("bitsandbytes") for value in requirements)
|
|
assert not any(value.startswith("gradio") for value in requirements)
|
|
|
|
|
|
def test_dockerfile_installs_spaces_extra_not_quantization():
|
|
dockerfile = (ROOT / "Dockerfile").read_text()
|
|
|
|
assert 'pip install --no-cache-dir ".[spaces]"' in dockerfile
|
|
assert "bitsandbytes" not in dockerfile
|
|
|
|
|
|
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",
|
|
]
|