from __future__ import annotations
import hashlib
import tarfile
from camstack_train.manifest import MLPACKAGE_FILES, catalog_draft, file_rows, write_bundle
from camstack_train.spec import parse_job
CID = "custom-yolov9m-640-ab12cd34"
def stage(tmp_path, with_int8=True, with_coreml=True):
s = tmp_path / "artifacts"
s.mkdir()
(s / f"{CID}.pt").write_bytes(b"pt")
(s / f"{CID}.onnx").write_bytes(b"onnx")
(s / f"{CID}-fp16.xml").write_bytes(b"")
(s / f"{CID}-fp16.bin").write_bytes(b"fp16w")
if with_int8:
(s / f"{CID}-int8.xml").write_bytes(b"")
(s / f"{CID}-int8.bin").write_bytes(b"int8w")
if with_coreml:
for m in MLPACKAGE_FILES:
p = s / f"{CID}.mlpackage" / m
p.parent.mkdir(parents=True, exist_ok=True)
p.write_bytes(m.encode())
return s
def sha(b: bytes) -> str:
return hashlib.sha256(b).hexdigest()
def test_rows_classify_every_file(tmp_path):
rows = {r["path"]: r for r in file_rows(stage(tmp_path), CID)}
assert rows[f"{CID}.onnx"] == {"path": f"{CID}.onnx", "bytes": 4, "sha256": sha(b"onnx"), "format": "onnx", "variant": "base"}
assert rows[f"{CID}-int8.bin"]["variant"] == "int8" and rows[f"{CID}-int8.bin"]["format"] == "openvino"
assert rows[f"{CID}-fp16.bin"]["variant"] == "base"
assert rows[f"{CID}.mlpackage/Data/com.apple.CoreML/weights/weight.bin"]["format"] == "coreml"
assert rows[f"{CID}.pt"]["format"] == "pt"
assert len(rows) == 9
def test_bundle_holds_exactly_the_rows(tmp_path):
s = stage(tmp_path)
rows = file_rows(s, CID)
bundle = write_bundle(s, rows, tmp_path / "out" / "bundle.tar")
assert bundle["path"] == "bundle.tar" and bundle["sha256"] == sha((tmp_path / "out" / "bundle.tar").read_bytes())
with tarfile.open(tmp_path / "out" / "bundle.tar") as tar:
assert sorted(tar.getnames()) == sorted(r["path"] for r in rows)
again = write_bundle(s, rows, tmp_path / "out2" / "bundle.tar")
assert again["sha256"] == bundle["sha256"] # deterministic
def test_catalog_draft_formats_and_revisions(tmp_path, job_dict):
job = parse_job(job_dict)
rows = file_rows(stage(tmp_path), CID)
floors = {"candidate": {"person": 0.5}, "candidateInt8": {"person": 0.45}}
draft = catalog_draft(job, rows, floors)
base = draft["base"]
assert base["id"] == CID and base["defaultClassFloors"] == {"person": 0.5}
assert base["inputSize"] == {"width": 640, "height": 640} and base["preprocessMode"] == "letterbox"
assert base["formats"]["onnx"] == {"url": f"{CID}.onnx", "sizeMB": 0.0, "revision": sha(b"onnx")}
assert base["formats"]["openvino"]["url"] == f"{CID}-fp16.xml"
assert base["formats"]["openvino"]["files"] == [f"{CID}-fp16.bin"]
assert base["formats"]["openvino"]["revision"] == sha(b"fp16w")
cm = base["formats"]["coreml"]
assert cm["url"] == f"{CID}.mlpackage" and cm["isDirectory"] and cm["files"] == list(MLPACKAGE_FILES)
assert cm["revision"] == sha(b"Data/com.apple.CoreML/weights/weight.bin")
assert draft["int8"]["id"] == f"{CID}-int8"
assert list(draft["int8"]["formats"]) == ["openvino"]
assert draft["int8"]["defaultClassFloors"] == {"person": 0.45}
def test_draft_without_int8_or_coreml(tmp_path, job_dict):
job = parse_job(job_dict)
rows = file_rows(stage(tmp_path, with_int8=False, with_coreml=False), CID)
draft = catalog_draft(job, rows, {"candidate": {}, "candidateInt8": None})
assert draft["int8"] is None and "coreml" not in draft["base"]["formats"]