5bd7dd7a01
Reuse colour artwork serializers and record source hashes. Verified single and multiview exports plus targeted tests.
100 lines
3.5 KiB
Python
100 lines
3.5 KiB
Python
"""The stroke CLI must preserve colour sources and verify all exported formats."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from orisys_sensor_design import cli
|
|
from orisys_sensor_design.manifest import RevisionPaths, sha256_file
|
|
|
|
REVISION = Path(__file__).resolve().parents[1] / "sensors" / "finger-xynova" / "r01"
|
|
|
|
|
|
def _args(job: Path, output_dir: Path) -> argparse.Namespace:
|
|
return argparse.Namespace(
|
|
views=False,
|
|
source_job=job,
|
|
output_dir=output_dir,
|
|
camera_width_fraction=0.5,
|
|
seed=13,
|
|
)
|
|
|
|
|
|
def _job(tmp_path: Path) -> tuple[Path, Path, Path]:
|
|
paths = RevisionPaths.from_revision_dir(REVISION)
|
|
png = tmp_path / "colour.png"
|
|
svg = tmp_path / "colour.svg"
|
|
png.write_bytes(b"colour pixels")
|
|
svg.write_bytes(b"<svg>colour geometry</svg>")
|
|
job_path = tmp_path / "_texture_job.json"
|
|
job_path.write_text(
|
|
json.dumps(
|
|
{
|
|
"schema": "orisys-sensor-design/texture-job-v3",
|
|
"revision_dir": str(paths.root),
|
|
"source_blend": str(paths.source_blend),
|
|
"outputs": [
|
|
{
|
|
"uv_map": "compute",
|
|
"artifacts": {"png": str(png), "svg": str(svg)},
|
|
}
|
|
],
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
return job_path, png, svg
|
|
|
|
|
|
def test_stroke_cli_exports_three_formats_and_manifest(tmp_path, monkeypatch):
|
|
job, png, svg = _job(tmp_path)
|
|
output_dir = tmp_path / "strokes"
|
|
calls = []
|
|
|
|
def run_blender(script, blend, args):
|
|
calls.append((script, blend, args))
|
|
output_dir.mkdir()
|
|
for fmt in ("png", "svg", "pdf"):
|
|
(output_dir / f"stroke_texture_compute.{fmt}").write_bytes(fmt.encode())
|
|
return 0
|
|
|
|
monkeypatch.setattr(cli, "_run_blender_script", run_blender)
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, output_dir)) == 0
|
|
assert calls[0][0] == "random_stroke_pattern.py"
|
|
assert calls[0][2][-4:] == ["--seed", "13", "--camera-width-fraction", "0.5"]
|
|
manifest = json.loads((output_dir / "stroke_texture_manifest.json").read_text())
|
|
assert manifest["schema"] == "orisys-sensor-design/stroke-texture-manifest-v1"
|
|
assert manifest["source_job"]["sha256"] == sha256_file(job)
|
|
assert manifest["outputs"][0]["sources"]["png"]["sha256"] == sha256_file(png)
|
|
assert manifest["outputs"][0]["sources"]["svg"]["sha256"] == sha256_file(svg)
|
|
assert set(manifest["outputs"][0]["artifacts"]) == {"png", "svg", "pdf"}
|
|
assert png.read_bytes() == b"colour pixels"
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, output_dir)) == 2
|
|
assert len(calls) == 1
|
|
|
|
|
|
def test_stroke_cli_refuses_missing_source_svg(tmp_path, monkeypatch):
|
|
job, _png, svg = _job(tmp_path)
|
|
svg.unlink()
|
|
monkeypatch.setattr(
|
|
cli,
|
|
"_run_blender_script",
|
|
lambda *_: (_ for _ in ()).throw(AssertionError("Blender must not run")),
|
|
)
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, tmp_path / "strokes")) == 3
|
|
|
|
|
|
def test_stroke_cli_rejects_incomplete_source_job(tmp_path, monkeypatch):
|
|
job, _png, _svg = _job(tmp_path)
|
|
data = json.loads(job.read_text(encoding="utf-8"))
|
|
del data["outputs"][0]["artifacts"]["svg"]
|
|
job.write_text(json.dumps(data), encoding="utf-8")
|
|
monkeypatch.setattr(
|
|
cli,
|
|
"_run_blender_script",
|
|
lambda *_: (_ for _ in ()).throw(AssertionError("Blender must not run")),
|
|
)
|
|
assert cli.cmd_stroke_texture(REVISION, _args(job, tmp_path / "strokes")) == 2
|