Files
magnus919_agent-skills/fpga-development/scripts/test_fixture.py
T
Magnus HedemarkandGitHub f605e807f3 feat(fpga-development): add design and verification methodology (#512)
* feat(fpga-development): add design and verification methodology

* feat(fpga-development): integrate electronics verification routing
2026-09-14 18:37:01 -04:00

91 lines
4.2 KiB
Python
Executable File

#!/usr/bin/env python3
import importlib.util
import json
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
import pytest
RUNNER = Path(__file__).with_name("fpga_fixture.py")
spec = importlib.util.spec_from_file_location("fpga_fixture", RUNNER)
fixture = importlib.util.module_from_spec(spec)
assert spec.loader
spec.loader.exec_module(fixture)
def test_runner_help():
assert subprocess.run([sys.executable, str(RUNNER), "--help"], capture_output=True, text=True).returncode == 0
def test_runner_simulation_and_synthesis():
if not (shutil.which("iverilog") and shutil.which("vvp") and shutil.which("yosys")):
pytest.skip("integration tools unavailable")
with tempfile.TemporaryDirectory() as d:
p = subprocess.run([sys.executable, str(RUNNER), "--json", "--output", d], capture_output=True, text=True)
data = json.loads(p.stdout)
assert p.returncode == 0
assert data["status"] == "passed"
assert {x["width"] for x in data["simulation"]} == {1, 8}
assert all(x["status"] == "passed" for x in data["simulation"])
assert all(x["status"] == "passed" for x in data["synthesis"])
def test_checker_rejects_data_loss_mutation():
if not (shutil.which("iverilog") and shutil.which("vvp")):
pytest.skip("simulation tools unavailable")
asset = RUNNER.parents[1] / "assets" / "rv_buffer.sv"
original = asset.read_text()
mutated = original.replace("full <= 1'b1; data_reg <= in_data;", "full <= 1'b1; data_reg <= data_reg;")
assert mutated != original
with tempfile.TemporaryDirectory() as d:
root = Path(d)
(root / "rv_buffer.sv").write_text(mutated)
(root / "tb_rv_buffer.sv").write_text((asset.parent / "tb_rv_buffer.sv").read_text())
p = subprocess.run(["iverilog", "-g2012", "-s", "tb_rv_buffer", "-o", str(root / "a.vvp"), "-Ptb_rv_buffer.DATA_WIDTH=8", str(root / "rv_buffer.sv"), str(root / "tb_rv_buffer.sv")], capture_output=True, text=True)
assert p.returncode == 0
q = subprocess.run(["vvp", str(root / "a.vvp"), "+SEED=20260914"], capture_output=True, text=True)
assert q.returncode != 0
assert "FAIL" in q.stdout
def test_missing_tools_are_reported_separately(monkeypatch, tmp_path, capsys):
monkeypatch.setattr(fixture.shutil, "which", lambda _: None)
monkeypatch.setattr(sys, "argv", [str(RUNNER), "--json", "--output", str(tmp_path / "missing")])
assert fixture.main() == 1
data = json.loads(capsys.readouterr().out)
assert data["status"] == "incomplete"
assert all(x["status"] == "skipped" for x in data["simulation"] + data["synthesis"])
def test_timeout_bytes_are_json_safe(monkeypatch, tmp_path):
def timed_out(*args, **kwargs):
raise subprocess.TimeoutExpired(args[0], 30, output=b"partial output", stderr=b"partial error")
monkeypatch.setattr(fixture.subprocess, "run", timed_out)
result = fixture.run(["example-tool"], tmp_path)
assert result["status"] == "timeout"
assert result["stdout"] == "partial output"
assert result["stderr"] == "partial error"
json.dumps(result)
def test_existing_output_is_preserved(tmp_path):
output = tmp_path / "keep.txt"
output.write_text("preserve me")
p = subprocess.run([sys.executable, str(RUNNER), "--output", str(output)], capture_output=True, text=True)
assert p.returncode == 2
assert output.read_text() == "preserve me"
def test_relative_output_from_other_cwd(tmp_path):
if not all(shutil.which(t) for t in ("iverilog", "vvp", "yosys")):
pytest.skip("integration tools unavailable")
p = subprocess.run([sys.executable, str(RUNNER), "--json", "--output", "results with spaces"], cwd=tmp_path, capture_output=True, text=True)
assert p.returncode == 0, p.stderr
data = json.loads(p.stdout)
assert data["status"] == "passed"
assert Path(data["output"]) == tmp_path / "results with spaces"
assert (Path(data["output"]) / "result.json").is_file()
def test_timeout_is_json_safe():
with tempfile.TemporaryDirectory() as d:
result = fixture.run([sys.executable, "-c", "import time; print('before'); time.sleep(1)"], Path(d), timeout=0.01)
assert result["status"] == "timeout"
json.dumps(result)