test: simulation-mode acquisition acceptance suite#596
Conversation
End-to-end acceptance tests that drive MultiPointController directly against simulated hardware (in-process, no GUI, no QApplication) and assert on observable artifacts only: image files, coordinates.csv rows, 'acquisition parameters.json', .done markers, the per-acquisition log, and subprocess state. New tests/acceptance/ package (pytest marker: acceptance, registered in its conftest; runs under the default pytest invocation, so CI picks it up with no workflow change): - test_full_acquisition.py — full small acquisition (2x2 FOV x 2ch x 3z = 24 images + CSV/JSON/log assertions); OME_TIFF vs ZARR_V3 format parity (16 planes, identical plane shape/dtype; zarr read back via tensorstore, skipped where not installed); 2-timepoint smoke. - test_abort.py — abort mid-acquisition: clean stop, a second acquisition succeeds in the same process, and no JobRunner subprocess survives close() (PR #582 regression surface). - test_backpressure.py — z-stack under a ~1.5-frame byte limit completes instead of deadlocking (pins the byte-release-on-job- completion design; throttling verified engaged via logs). - harness.py/conftest.py — shared AcquisitionHarness around tests/control/test_stubs.py wiring, artifact helpers, generous CI-safe timeouts, def-knob pinning (incl. patching the worker's star-imported FILE_SAVING_OPTION binding). Suite runtime: 5 passed in ~28s locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings (Opus, all nits): fix harness docstring referencing a nonexistent fixture; drop the redundant un-restored MERGE_CHANNELS module write (acquisition_defaults owns it with restore); guard the backpressure test's premise — assert on-disk acquisition bytes exceed 5x the byte limit so a shrunken simulated frame fails loudly instead of passing without exercising the deadlock path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…stant The premise guard added in the previous commit fired in CI: the pristine CI config crops the simulated camera to a quarter of the local frame (~8.3 MiB vs ~33.1 MiB), so the hardcoded frame size overstated the pressure and the guard correctly refused to pass. Snap one frame from the simulated camera at test start and size the byte limit (1.5 frames) from its actual nbytes — the 12-frame acquisition is then ~8x the limit under any machine config, and the on-disk guard still verifies the premise held. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The review-added premise guard caught a real config divergence on its first CI run — CI's pristine Note: that CI run also ended with the flaky post-green exit segfault that #595 root-causes (3 of master's last 15 runs fail the same way) and fixes via |
Summary
End-to-end acceptance suite for simulated acquisitions — the safety net for the upcoming acquisition-engine refactor and protocol-v2 port. New
software/tests/acceptance/package (markeracceptance), drivingMultiPointControllerin-process (no GUI, no QApplication — the controller uses plain threads and plain-callable callbacks) and asserting on observable artifacts only: files on disk and process state.test_full_small_acquisition0/, 12 rows in per-timepointcoordinates.csv, 4 rows in root CSV,acquisition parameters.jsoncontents,.donemarkers, no ERROR lines inacquisition.logtest_format_parity_ome_tiff_vs_zarrOME_TIFFandZARR_V3⇒ 16 planes each, identical plane shape (4168×4168) and dtype (uint16)test_abort_mid_acquisition_then_reacquireJobRunnersubprocesses surviveclose()(the #582 orphan surface)test_zstack_completes_under_tight_byte_limittest_multi_timepoint_smoke0/and1/each with correct images, CSV rows,.doneShared harness (
harness.py+conftest.py) wraps the existingtests/control/test_stubs.pywiring; timeouts are deliberately generous for CI runners.CI wiring
No workflow change needed: pytest's default collection picks up
tests/acceptance/under the existingpython3 -m pyteststep. Deselect locally with-m "not acceptance". Suite runtime: 5 passed in ~28 s locally (headless).Behavior findings (pinned, not fixed — recorded per the session brief)
acquisition parameters.jsonis written atstart_new_experiment()time, not atrun_acquisition()time — callers that configure NZ/Nt after creating the experiment get stale values in the JSON. Tests configure before creating; noted in the harness docstring.{experiment}/ome_tiff/{region}_{fov}.ome.tiff(one per FOV, series ZCYX), while individual-images mode writes under{experiment}/0/.{experiment}/zarr/{region}/fov_{n}.ome.zarr/0, 5D TCZYX), not under the per-timepoint path thatZarrWriterInfo.get_output_path's docstring suggests.tensorstore(the same library the product writes with), and the parity testimportorskips it, matchingtest_zarr_writer.py.FILE_SAVING_OPTIONis star-imported bymulti_point_worker, so runtime format switching requires patching both the_defattribute and the worker's frozen binding — the conftest helper does this; a future cleanup could move the worker to thecontrol._def.Xruntime-access pattern.Test plan
python -m pytest tests/acceptance -q→ 5 passed in 28.3 sblack --config software/pyproject.tomlcleanSession-B note: this PR is the Sync-2 gate for the parallel-session plan (Session C's code phase starts when this is merged).
🤖 Generated with Claude Code