Voltage-changing tests can't sleep a fixed amount and assume the
rail is there — Owon settling is bench-dependent and typically
asymmetric (up-step ≠ down-step). New shared helpers and tests use
the rail's measured value to drive timing.
- tests/hardware/psu_helpers.py:
wait_until_settled(psu, target_v, ...)
polls measure_voltage_v() until within tol, returns
(elapsed_s, trace) or (None, trace) on timeout
apply_voltage_and_settle(psu, target_v, validation_time, ...)
composite: set setpoint → wait until measured matches →
sleep validation_time so the firmware-side observer can
detect and republish status. Raises on settle timeout.
downsample_trace, plus DEFAULT_VOLTAGE_TOL_V (0.10),
DEFAULT_POLL_INTERVAL_S (0.05), DEFAULT_SETTLE_TIMEOUT_S (10.0),
DEFAULT_VALIDATION_TIME_S (1.0).
- test_overvolt.py: voltage-tolerance suite. Each test (over,
under, parametrized sweep) uses apply_voltage_and_settle for the
procedure, the autouse _park_at_nominal fixture (also via the
helper), and a single deterministic ALM_Status read after the
validation hold instead of polling-the-bus.
- test_psu_voltage_settling.py: characterization test, opt-in via
the new psu_settling marker. Walks four (start_v, target_v)
transitions and records settling_time_s + voltage_trace per case.
Values feed directly into test_overvolt's ECU_VALIDATION_TIME_S
budgeting.
- pytest.ini:
junit_family = legacy → record_property() entries now actually
appear in reports/junit.xml (the default xunit2 silently
dropped them with a collect-time warning, breaking the
conftest plugin's metadata round-trip)
psu_settling marker registered
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On benches where the Owon PSU powers the ECU, every per-file PSU
fixture that closed the port (sending 'output 0' on close) browned
out the bench between modules — every MUM test that ran after a
closed PSU connection failed with "ECU not responding".
New tests/hardware/conftest.py provides three session-scoped
fixtures:
- _psu_or_none: tolerant. Opens the Owon PSU once via resolve_port,
parks at config.power_supply.set_voltage / set_current, enables
output. Yields the live OwonPSU or None. Closes (with
safe_off_on_close=True) at session end — the bench ends safely
de-energized.
- _psu_powers_bench: autouse=True. Realizes _psu_or_none so even
tests that don't request `psu` by name benefit from the
session-level power-up. No-op if PSU isn't configured.
- psu: public. Skips cleanly when the PSU isn't reachable.
Contract for tests:
- request `psu` if you need to read measurements or change voltage
- restore nominal voltage in your finally block
- MUST NOT call psu.set_output(False) (would brown out the bench)
- MUST NOT call psu.close() (the session fixture owns it)
test_owon_psu.py becomes read-only:
- removed the local module-scoped psu fixture
- removed the set_output toggle (would have killed the session)
- now validates IDN, output_is_on(), and parsed measurements
against the always-on PSU. Renamed to
test_owon_psu_idn_and_measurements to reflect the new shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits hardware-test concerns into two reusable modules and rebuilds
test_mum_alm_animation.py on top of them.
- frame_io.py — generic LDF-driven I/O class. Knows nothing about
ALM. Three access levels:
high: send/receive/read_signal by frame and signal name
mid: pack/unpack — bytes ↔ signals without I/O
low: send_raw/receive_raw — bypass the LDF entirely
Plus introspection: frame, frame_id, frame_length. Frame lookups
are cached per FrameIO instance.
- alm_helpers.py — ALM_Node domain helpers built on FrameIO.
AlmTester class bound to (fio, nad) exposes:
force_off, read_led_state, wait_for_state,
measure_animating_window, assert_pwm_matches_rgb,
assert_pwm_wo_comp_matches_rgb
Plus pure utilities (ntc_kelvin_to_celsius, pwm_within_tol) and
the LED-state / pacing / PWM-tolerance constants. PWM assertions
use vendor/rgb_to_pwm.py (compute_pwm) at the runtime
Tj_Frame_NTC temperature.
- test_mum_alm_animation.py rewritten:
* fio + alm fixtures replace the previous dict-based _ctx
* SETUP / PROCEDURE / ASSERT / TEARDOWN section markers
* test_mode1_fade now wraps its ConfigFrame change in
try/finally so EnableCompensation is restored even on
assertion failure (was leaking state into later tests)
* test_disable_compensation_pwm_wo_comp uses the four-phase
pattern explicitly
Sibling imports work because pytest's default rootdir mode puts the
test file's directory on sys.path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The MUM (Melexis Universal Master) adapter is the current default; the
BabyLIN SDK adapter is retained only for backward compatibility with
existing rigs.
Code:
- Emit DeprecationWarning when BabyLinInterface is instantiated and
when tests/conftest.py routes interface.type=='babylin' to it.
- Update module/class docstrings in ecu_framework/{__init__,config,
lin/__init__,lin/babylin}.py to label BabyLIN-specific fields and
paths as deprecated.
Config / scripts / pytest:
- pytest.ini: relabel the babylin marker as deprecated.
- config/{babylin.example,examples,test_config}.yaml: add deprecation
banners and field comments.
- scripts/99-babylin.rules and scripts/pi_install.sh: annotate the
udev-rule install block as legacy-only.
Documentation:
- TESTING_FRAMEWORK_GUIDE.md, docs/08_babylin_internals.md, and
vendor/README.md: prepend explicit "DEPRECATED" banners.
- docs/{README,01,02,04,05,07,09,10,12,13,14,15,18,DEVELOPER_COMMIT_
GUIDE}.md: relabel "legacy" to "deprecated" where babylin is
mentioned, present MUM as the primary path, and steer new work
toward the MUM examples.
No tests, configs, or modules were deleted; existing BabyLIN setups
keep working but now produce a clear DeprecationWarning at runtime.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>