102 lines
4.2 KiB
Python
102 lines
4.2 KiB
Python
"""The module boundaries the responsibility split is supposed to create.
|
|
|
|
A pure move is only worth anything if the dependency directions actually change:
|
|
the finger worker must not need the box worker to describe a packet, the session
|
|
file format must not need the sampling state machine, and the models must not
|
|
import either. These assertions are cheap and they fail loudly if someone
|
|
re-introduces the cycle.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
from pathlib import Path
|
|
|
|
SHARED = Path(__file__).resolve().parents[1] / "src"
|
|
ACQUISITION = SHARED / "orisys_app_shared" / "acquisition"
|
|
FORCE = SHARED / "orisys_app_shared" / "force"
|
|
RECORD = SHARED / "orisys_app_shared" / "record"
|
|
|
|
|
|
def _imports(path: Path) -> set[str]:
|
|
"""Every module name imported by ``path`` (``from x import y`` counts as x)."""
|
|
tree = ast.parse(path.read_text(encoding="utf-8"))
|
|
found: set[str] = set()
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.Import):
|
|
for alias in node.names:
|
|
found.add(alias.name)
|
|
elif isinstance(node, ast.ImportFrom):
|
|
if node.module:
|
|
found.add(node.module)
|
|
# ``from . import models`` also names the submodule.
|
|
for alias in node.names:
|
|
found.add(f"{node.module}.{alias.name}" if node.module else alias.name)
|
|
return found
|
|
|
|
|
|
def _imports_any(path: Path, needles) -> bool:
|
|
found = _imports(path)
|
|
for needle in needles:
|
|
if any(entry == needle or entry.endswith(f".{needle}") for entry in found):
|
|
return True
|
|
return False
|
|
|
|
|
|
def test_the_packet_model_does_not_import_a_worker():
|
|
assert not _imports_any(
|
|
ACQUISITION / "models.py", {"sdk_worker", "finger_worker"}
|
|
), "acquisition.models depends on a worker"
|
|
|
|
|
|
def test_the_finger_worker_takes_the_packet_from_models():
|
|
"""It used to import the box worker just to describe a frame."""
|
|
source = (ACQUISITION / "finger_worker.py").read_text(encoding="utf-8")
|
|
tree = ast.parse(source)
|
|
from_sdk_worker: set[str] = set()
|
|
for node in ast.walk(tree):
|
|
if isinstance(node, ast.ImportFrom) and (node.module or "").endswith("sdk_worker"):
|
|
from_sdk_worker.update(alias.name for alias in node.names)
|
|
assert "SdkPacket" not in from_sdk_worker, "the finger worker still borrows the packet"
|
|
assert "FixedCameraMode" not in from_sdk_worker
|
|
assert "load_fixed_mode" not in from_sdk_worker
|
|
# ...and the helpers it genuinely shares still come from there.
|
|
assert "process_chain" in from_sdk_worker
|
|
|
|
|
|
def test_the_calibration_models_do_not_import_the_sampler_or_the_session_file():
|
|
for path in (FORCE / "models.py",):
|
|
assert not _imports_any(
|
|
path, {"calibration_recorder", "calibration_session", "session"}
|
|
), f"{path.name} depends on a higher layer"
|
|
|
|
|
|
def test_the_session_format_does_not_import_the_sampling_state_machine():
|
|
assert not _imports_any(
|
|
FORCE / "calibration_session.py", {"calibration_recorder"}
|
|
), "the session file format still depends on the sampler"
|
|
|
|
|
|
def test_the_moved_models_are_the_same_objects_everywhere():
|
|
from orisys_app_shared.acquisition import finger_worker, models, sdk_worker
|
|
from orisys_app_shared.force import calibration_recorder, calibration_session
|
|
from orisys_app_shared.force import models as force_models
|
|
|
|
assert sdk_worker.SdkPacket is models.SdkPacket is finger_worker.SdkPacket
|
|
assert sdk_worker.FixedCameraMode is models.FixedCameraMode
|
|
assert calibration_recorder.CalibrationPoint is force_models.CalibrationPoint
|
|
assert calibration_recorder.AxisStats is force_models.AxisStats
|
|
assert calibration_session.SAMPLE_FIELDS is force_models.SAMPLE_FIELDS
|
|
# The recorder still offers the trace helpers it used to own.
|
|
assert callable(calibration_recorder.trace_from)
|
|
assert callable(calibration_recorder.trace_dt)
|
|
|
|
|
|
def test_the_recording_pieces_are_separate_modules():
|
|
from orisys_app_shared.record import experiment
|
|
from orisys_app_shared.record.video_writer import VideoSegments
|
|
|
|
# The writer delegates video encoding to its own component.
|
|
assert not hasattr(experiment, "_open_segment")
|
|
assert callable(VideoSegments.write) and callable(VideoSegments.close)
|