servomexlib.testing¶
FakeTransport, fixture loaders, the MockSlave-backed Modbus fake, and canned frames.
servomexlib.testing ¶
Public testing seam.
Re-exports the in-process :class:FakeTransport plus fixture helpers so tests —
in this package and in downstream code — can drive every protocol without
hardware. Fixtures use the human-readable > hex / < hex arrow format
shared with the sibling .testing modules.
For the Modbus path we do not hand-roll an ADU simulator: the
:func:mock_modbus_pair helper preloads the byte-accurate
anymodbus.testing.MockSlave (RTU and ASCII framing, FC01/02/04/05/08) with
the 4100's register/coil banks and binds our ModbusClient to it over an
in-process serial pair. The Modbus imports are lazy so servomexlib.testing
stays importable without the optional [modbus] extra.
CoilOp
dataclass
¶
One observed coil operation against a recording :class:MockSlave.
Used by the autocalibration coil-pulse tests to assert
the ordered 0→1→0 transition: a start_calibration pulse should record
a WRITE_SINGLE_COIL on=True (FC05), then a READ_COILS readback
(FC01), then a WRITE_SINGLE_COIL on=False. coil is the PDU address.
FakeTransport ¶
Bases: ByteStreamTransport
Scripted, in-process :class:ByteStreamTransport.
Source code in src/servomexlib/transport/fake.py
add_script ¶
emit
async
¶
Feed frames one at a time, optionally interval seconds apart.
Run as a background task to simulate a continuous broadcaster:
task_group.start_soon(lambda: fake.emit(frames, interval=0.05)).
Source code in src/servomexlib/transport/fake.py
MockChannel
dataclass
¶
MockChannel(
channel,
value,
name=b"\x00\x00\x00\x00\x00\x00",
unit=b"\x00\x00\x00",
status_bits=(False,) * 8,
)
One channel's contents to preload into a :class:MockSlave's banks.
name / unit are raw display-ROM bytes (not ASCII), so a test can
reproduce e.g. CO₂'s name 43 4F 82 20 20 20 and exercise the charset
decode. status_bits are the 8 FC02 discrete bits (Fault, Maintenance,
Calibration, WarmingUp, Alarm1..4).
ReadOp
dataclass
¶
One observed read request against a recording :class:MockSlave.
coil_ops ¶
load_4100_banks ¶
Preload slave's input-register and discrete-input banks from channels.
Values are written as IEEE-754 float32 high-word-first (the HW-confirmed word order); names/units as raw display-ROM register words; status as FC02 bits.
Source code in src/servomexlib/testing.py
load_arrow_script ¶
Parse a > hex / < hex arrow fixture into a write→reply script.
Each > line is a request (host→device) and the following < line is
the reply (device→host). # starts a comment; blank lines are ignored.
Hex tokens may be space- or colon-separated.
Source code in src/servomexlib/testing.py
load_cal_state ¶
Set group's cal-group discretes (11009-11016) on slave.
Mirrors :meth:ModbusClient._read_cal_progress: the group's pair of discretes
is [calibrating, gas2] at 2*(group-1) within the cal-group block. With
calibrating=True and gas2 selecting cal-gas-1 vs -2, a subsequent
calibration_status(group) reports active=True and the matching
:class:CalPhase.
Source code in src/servomexlib/testing.py
mock_modbus_pair
async
¶
mock_modbus_pair(
*,
framing=None,
address=1,
channels=DEFAULT_4100_BANK,
client_channels=None,
device="mock",
disabled_function_codes=None,
record_coil_ops=False,
record_read_ops=False,
valid_input_ranges=None,
valid_discrete_ranges=None,
)
Yield (ModbusClient, MockSlave) connected over an in-process serial pair.
The client is the real :class:~servomexlib.protocol.modbus.client.ModbusClient
bound through a real :class:~servomexlib.protocol.modbus.session.ModbusSession
and :class:~servomexlib.transport.serial.SerialTransport, so the full encode/
frame/decode stack is exercised against byte-accurate ADUs. The mock's banks
may be mutated mid-test; reads observe the change on the next request.
Source code in src/servomexlib/testing.py
mock_modbus_transport
async
¶
mock_modbus_transport(
*,
framing=None,
address=1,
channels=DEFAULT_4100_BANK,
disabled_function_codes=None,
record_coil_ops=False,
record_read_ops=False,
valid_input_ranges=None,
valid_discrete_ranges=None,
)
Yield (Transport, MockSlave) — the client-end transport over a serving slave.
Lower-level than :func:mock_modbus_pair: it hands back the raw
:class:~servomexlib.transport.serial.SerialTransport (the no-hardware path
for the AUTO ladder and ad-hoc binding) connected to a preloaded byte-
accurate :class:MockSlave. The transport and the slave end are closed on exit.
Source code in src/servomexlib/testing.py
read_ops ¶
split_continuous_frames ¶
Split a raw continuous capture into individual frame payloads (no CRLF).
The on-wire terminator is CR LF (design §3.1), but a capture that has been
through a text-mode transform keeps only the LF. We split on LF and
drop a trailing CR so both framings yield the same payloads — matching the
parser, which strips CR LF either way.