fix(timepix_scan): reject empty on/off windows and test argument validation

This commit is contained in:
wyzula_j committed 2026-10-06 16:14:45 +02:00
1 parent 26e268aef1
commit ef8bf026f1
2 files changed
+86 -8

No files matched your search

+11 -8
View File
@@ -47,20 +47,23 @@ class TimepixScan(LineScan):
# fmt: off
time_roi_step: Annotated[int, ScanArgument(display_name="Time ROI step", description="Bin size", gt=0)],
time_roi_n: Annotated[int, ScanArgument(display_name="Time ROI n", description="Number of time points", gt=0)],
idx_off_start: Annotated[int, ScanArgument(display_name="Index Off Start", description="Start Index for Off Level", ge=0)],
idx_off_stop: Annotated[int, ScanArgument(display_name="Index Off Stop", description="Stop Index for Off Level", ge=0)],
idx_on_start: Annotated[int, ScanArgument(display_name="Index On Start", description="Start Index for On Level", ge=0)],
idx_on_stop: Annotated[int, ScanArgument(display_name="Index On Stop", description="Stop Index for On Level", ge=0)],
idx_off_start: Annotated[int, ScanArgument(display_name="Index Off Start", description="Start Index for Off Level (first time bin)", ge=0)],
idx_off_stop: Annotated[int, ScanArgument(display_name="Index Off Stop", description="Stop Index for Off Level (exclusive)", ge=0)],
idx_on_start: Annotated[int, ScanArgument(display_name="Index On Start", description="Start Index for On Level (first time bin)", ge=0)],
idx_on_stop: Annotated[int, ScanArgument(display_name="Index On Stop", description="Stop Index for On Level (exclusive)", ge=0)],
# fmt: on
**kwargs,
):
if idx_off_start > idx_off_stop:
# The on/off windows are half-open index ranges [start, stop) over the time_roi_n time
# bins; the Timepix device averages each window into xes_level_*. An empty window
# (start == stop) would only produce NaN, so it is rejected here as well.
if idx_off_start >= idx_off_stop:
raise ValueError(
f"idx_off_start ({idx_off_start}) cannot be bigger than idx_off_stop ({idx_off_stop})!"
f"idx_off_start ({idx_off_start}) must be smaller than idx_off_stop ({idx_off_stop})!"
)
if idx_on_start > idx_on_stop:
if idx_on_start >= idx_on_stop:
raise ValueError(
f"idx_on_start ({idx_on_start}) cannot be bigger than idx_on_stop ({idx_on_stop})!"
f"idx_on_start ({idx_on_start}) must be smaller than idx_on_stop ({idx_on_stop})!"
)
for name, idx in (
("idx_off_start", idx_off_start),
+75
View File
@@ -0,0 +1,75 @@
# pylint: skip-file
import pytest
from bec_lib.bec_errors import ScanInputValidationError
from bec_server.scan_server.tests.scan_fixtures import *
from superxas_bec.scans.timepix_scan import TimepixScan
TIMEPIX_PARAMETERS = {
"time_roi_step": 1,
"time_roi_n": 10,
"idx_off_start": 0,
"idx_off_stop": 4,
"idx_on_start": 5,
"idx_on_stop": 10,
}
def _assemble_timepix_scan(scan_assembler, **overrides):
params = {**TIMEPIX_PARAMETERS, **overrides}
return scan_assembler(
"timepix_scan", "samx", -1.0, 1.0, steps=3, relative=False, exp_time=0.1, **params
)
def test_timepix_scan_forwards_parameters_to_scan_info(scan_assembler):
"""The Timepix device reads the parameters from additional_scan_parameters."""
scan = _assemble_timepix_scan(scan_assembler)
assert isinstance(scan, TimepixScan)
assert scan.scan_info.scan_name == "timepix_scan"
for name, value in TIMEPIX_PARAMETERS.items():
assert scan.scan_info.additional_scan_parameters[name] == value
@pytest.mark.parametrize(
"overrides, match",
[
({"idx_off_start": 5, "idx_off_stop": 4}, "idx_off_start"),
({"idx_on_start": 9, "idx_on_stop": 6}, "idx_on_start"),
# start == stop would be an empty window
({"idx_off_start": 3, "idx_off_stop": 3}, "idx_off_start"),
({"idx_on_start": 7, "idx_on_stop": 7}, "idx_on_start"),
],
)
def test_timepix_scan_rejects_start_not_smaller_than_stop(scan_assembler, overrides, match):
with pytest.raises(ValueError, match=match):
_assemble_timepix_scan(scan_assembler, **overrides)
@pytest.mark.parametrize(
"overrides, name",
[
({"idx_off_stop": 11}, "idx_off_stop"),
({"idx_on_stop": 11}, "idx_on_stop"),
({"idx_on_start": 11, "idx_on_stop": 12}, "idx_on_start"),
],
)
def test_timepix_scan_rejects_index_above_time_roi_n(scan_assembler, overrides, name):
with pytest.raises(ValueError, match=f"{name} with value"):
_assemble_timepix_scan(scan_assembler, **overrides)
def test_timepix_scan_accepts_stop_equal_to_time_roi_n(scan_assembler):
"""stop is exclusive, so a window may end at time_roi_n."""
scan = _assemble_timepix_scan(scan_assembler, idx_on_start=9, idx_on_stop=10)
assert scan.scan_info.additional_scan_parameters["idx_on_stop"] == 10
@pytest.mark.parametrize(
"overrides", [{"idx_off_start": -1}, {"time_roi_n": 0}, {"time_roi_step": 0}]
)
def test_timepix_scan_argument_bounds_are_validated(scan_assembler, overrides):
with pytest.raises(ScanInputValidationError):
_assemble_timepix_scan(scan_assembler, **overrides)