Files

135 lines
6.0 KiB
Python

import json
import subprocess
import sys
from pathlib import Path
import pytest
SCRIPTS = Path(__file__).parents[1]
sys.path.insert(0, str(SCRIPTS))
import run # noqa: E402
BOX = [{"command": "solid.box", "params": {"length": 10, "width": 10, "height": 10, "body_name": "Cube"}}]
def report(tmp_path, commands, *args):
script = tmp_path / "script.json"
script.write_text(json.dumps({"commands": commands}))
return subprocess.run(
[sys.executable, SCRIPTS / "run.py", script, *args], capture_output=True, text=True
)
def test_reports_bodies(tmp_path):
result = report(tmp_path, BOX)
assert result.returncode == 0, result.stderr
assert "BODY Cube: volume 1000 mm3, area 600 mm2, bbox (0, 0, 0)..(10, 10, 10), com (5, 5, 5)" in result.stdout
def test_flags_underconstrained_sketch(tmp_path):
result = report(tmp_path, [
{"command": "sketch.create", "params": {"plane": "XY", "name": "Loose"}},
{"command": "sketch.circle.center", "params": {"center": [5, 5], "radius": 5}},
{"command": "sketch.finish", "params": {}},
])
assert result.returncode == 0, result.stderr
assert 'SKETCH "Loose": dof 3, solved' in result.stdout
def test_reports_step_warnings(tmp_path):
result = report(tmp_path, [
{"command": "sketch.create", "params": {"plane": "XY", "offset": "12 mm", "name": "Plate"}},
{"command": "sketch.rectangle.center", "params": {"center": [0, 0], "corner": [40, 40]}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.extrude", "params": {"sketch": "Plate", "distance": "12 mm", "direction": "negative",
"body_name": "P"}},
{"command": "sketch.create", "params": {"plane": "XY", "name": "Hole"}},
{"command": "sketch.circle.center", "params": {"center": [0, 0], "diameter": 20}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.extrude", "params": {"sketch": "Hole", "distance": "12 mm", "taper": "-20 deg",
"operation": "cut", "targets": ["P"], "name": "Flush cut"}},
])
assert result.returncode == 0, result.stderr
assert 'WARN "Flush cut": Boolean retry' in result.stdout
def test_fails_on_a_bad_step(tmp_path):
result = report(tmp_path, [{"command": "solid.box", "params": {"width": 10}}])
assert result.returncode == 1
assert result.stdout.startswith("FAIL") and "step 1 (solid.box)" in result.stdout
def test_applies_a_script_to_a_saved_design(tmp_path):
design = tmp_path / "cube.json"
built = report(tmp_path, [
{"command": "parameters.add", "params": {"name": "size", "expression": "10 mm"}},
{"command": "solid.box", "params": {"length": "size", "width": "size", "height": "size", "body_name": "Cube"}},
], "--save", design)
assert built.returncode == 0, built.stderr
changed = report(tmp_path, [
{"command": "parameters.change", "params": {"name": "size", "expression": "20 mm"}},
], "--in", design)
assert changed.returncode == 0, changed.stderr
assert "BODY Cube: volume 8000 mm3" in changed.stdout
def test_execute_returns_the_final_inspection():
inspection = run.execute(BOX)
assert [b["name"] for b in inspection["bodies"]] == ["Cube"]
def test_execute_raises_on_failure():
with pytest.raises(run.SolidCraftError, match="step 1"):
run.execute([{"command": "solid.box", "params": {"width": 10}}])
@pytest.fixture
def flush_design(tmp_path):
"""A cut 10 mm deep into a 12 mm plate; at depth 13 mm its tapered end lies on the plate top."""
design = tmp_path / "plate.json"
built = report(tmp_path, [
{"command": "parameters.add", "params": {"name": "depth", "expression": "10 mm"}},
{"command": "sketch.create", "params": {"plane": "XY", "name": "Plate sketch"}},
{"command": "sketch.rectangle.center", "params": {"center": [0, 0], "corner": [40, 40]}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.extrude", "params": {"sketch": "Plate sketch", "distance": "12 mm", "body_name": "P"}},
{"command": "sketch.create", "params": {"plane": "XY", "offset": "-1 mm", "name": "Cut sketch"}},
{"command": "sketch.circle.center", "params": {"center": [5, 5], "diameter": 20}},
{"command": "sketch.finish", "params": {}},
{"command": "solid.extrude", "params": {"sketch": "Cut sketch", "distance": "depth", "taper": "10 deg",
"operation": "cut", "targets": ["P"], "name": "Cut"}},
], "--save", design)
assert built.returncode == 0 and "WARN" not in built.stdout, built.stdout + built.stderr
return design
def depth(value: str) -> dict:
return {"command": "parameters.change", "params": {"name": "depth", "expression": value}}
def test_reports_warnings_after_a_parameter_change(tmp_path, flush_design):
flush = report(tmp_path, [depth("13 mm")], "--in", flush_design)
assert 'WARN "Cut": Boolean retry' in flush.stdout
def test_reports_every_inspection_in_the_script(tmp_path, flush_design):
walk = report(tmp_path, [depth("13 mm"), {"command": "document.inspect", "params": {"measure": True}},
depth("10 mm")], "--in", flush_design)
lines = walk.stdout.splitlines()
assert any(line.startswith('step 2 WARN "Cut": Boolean retry') for line in lines), walk.stdout
assert any(line.startswith("step 2 BODY P: volume") for line in lines)
assert not any(line.startswith("WARN") for line in lines)
def test_reports_bodies_inspected_without_measurements(tmp_path):
result = report(tmp_path, [*BOX, {"command": "document.inspect", "params": {}}])
assert "step 2 BODY Cube\n" in result.stdout
assert "BODY Cube: volume 1000 mm3" in result.stdout
def test_says_so_when_there_is_nothing_to_report(tmp_path):
result = report(tmp_path, [{"command": "parameters.add", "params": {"name": "size", "expression": "10 mm"}}])
assert result.returncode == 0, result.stderr
assert result.stdout.strip() == "no bodies"