# Simulator Harness The pymodbus DL205 simulator stands in for real DL205/DL260 hardware in the E2E test suite. This document describes the launcher, the xUnit fixture, the skip policy, the per-test timeout discipline, and the pymodbus 3.13.0 framer quirk that the test strategy works around. ## Why a Simulator `mbproxy` targets a fleet of AutomationDirect DL205/DL260 controllers that test machines do not have. The pymodbus profile at [`../../tests/sim/dl205.json`](../../tests/sim/dl205.json) already models the device-side quirks (BCD nibbles at known holding-register addresses, CDAB-ordered 32-bit values, C-relay/Y-output coil mappings) as concrete register seeds. The harness wraps that profile in an xUnit `IAsyncLifetime` fixture so every E2E test class opens against a fresh known-good DL-series target without manual setup. The device-side rationale for each seed (why HR 1072 is `0x1234`, why FC03 caps at 128, etc.) lives in [`../Reference/dl205.md`](../Reference/dl205.md). The harness exists to make that profile addressable from xUnit tests; it does not duplicate the device documentation. ## Harness Layout Three files form the harness contract: | Path | Role | |------|------| | `tests/sim/run-dl205-sim.ps1` | PowerShell launcher. Provisions a Python venv under `tests/sim/.venv` on first run (`python -m venv` + `pip install pymodbus`) and execs `pymodbus.simulator` against `dl205.json` on the requested port. Idempotent — re-runs reuse the venv. | | `tests/Mbproxy.Tests/Sim/DL205SimulatorFixture.cs` | `IAsyncLifetime` fixture that picks a free port, spawns the launcher, polls for TCP readiness, and tears the process tree down on dispose. | | `tests/Mbproxy.Tests/Sim/DL205SimulatorCollection.cs` | `[CollectionDefinition(nameof(DL205SimulatorCollection))]` that exposes the fixture as an xUnit `ICollectionFixture`. | The launcher is a PowerShell 7+ script; the fixture invokes it via `pwsh -NoProfile -File