"""Hardware test for the Owon serial PSU. Validates basic SCPI control via :class:`OwonPSU` against the **session-managed** PSU (see :mod:`tests.hardware.conftest`): - identification (`*IDN?`) - decoded output state (`output?`) - parsed measurement queries (`MEAS:VOLT?`, `MEAS:CURR?`) The session-scoped autouse fixture in ``conftest.py`` opens the PSU once at session start, parks it at the configured nominal voltage, enables output, and leaves it that way for the whole session. This test therefore does **not** toggle the output — calling ``set_output(False)`` would brown out the ECU and break every MUM test that runs afterwards. The four-phase template (SETUP / PROCEDURE / ASSERT / TEARDOWN) still applies, but TEARDOWN is empty: the test reads-only and leaves the bench exactly as it found it. """ from __future__ import annotations import pytest from ecu_framework.config import EcuTestConfig from ecu_framework.power import OwonPSU pytestmark = [pytest.mark.hardware] def test_owon_psu_idn_and_measurements(config: EcuTestConfig, psu: OwonPSU, rp): """ Title: Owon PSU — IDN, output state, and parsed measurements Description: Read-only smoke test for the Owon PSU controller. Confirms the bench PSU responds to ``*IDN?``, reports an enabled output (the session fixture parked it there), and returns parseable floats for ``MEAS:VOLT?`` and ``MEAS:CURR?``. Optionally verifies the IDN matches the configured substring. Requirements: REQ-PSU-001 Test Steps: 1. SETUP: none — the session fixture opened the port, parked the PSU at nominal, and enabled output before any test in this run started 2. PROCEDURE: query *IDN?, output?, MEAS:VOLT?, MEAS:CURR? 3. ASSERT: IDN is non-empty (and contains ``idn_substr`` if configured); output is reported ON; both measurements parse to floats 4. TEARDOWN: none — this test does not mutate bench state Expected Result: - IDN is non-empty (and contains ``idn_substr`` when set) - ``output_is_on()`` returns True (bench is powered) - ``measure_voltage_v()`` returns a float close to nominal - ``measure_current_a()`` returns a float ≥ 0 """ psu_cfg = config.power_supply want_substr = psu_cfg.idn_substr expected_v = float(psu_cfg.set_voltage) if psu_cfg.set_voltage else None # ── PROCEDURE ───────────────────────────────────────────────────── # All four queries are reads — they don't change the bench. idn = psu.idn() is_on = psu.output_is_on() measured_v = psu.measure_voltage_v() measured_i = psu.measure_current_a() print(f"PSU IDN: {idn}") print(f"Output ON: {is_on}") print(f"Measured: V={measured_v}V, I={measured_i}A " f"(nominal setpoint: {expected_v}V)") # ── ASSERT ──────────────────────────────────────────────────────── # Record diagnostics before assertions so failure investigations # have the captured values. rp("psu_idn", idn) rp("output_is_on", bool(is_on)) rp("measured_voltage_v", measured_v) rp("measured_current_a", measured_i) rp("expected_voltage_v", expected_v) assert isinstance(idn, str) and idn, "*IDN? returned empty response" if want_substr: assert str(want_substr).lower() in idn.lower(), ( f"IDN does not contain expected substring: {want_substr!r}. " f"Got: {idn!r}" ) # The session fixture parked the PSU with output enabled. If this # comes back False the bench is in an unexpected state — likely # something in a preceding test mistakenly turned the output off. assert is_on is True, ( f"PSU output is not ON ({is_on=!r}). The session fixture parks " f"output=ON at start; some earlier test or the fixture itself " f"may have disabled it. Tests must NOT call psu.set_output(False)." ) # Measurements must parse — surfaces firmware-level response # format mismatches as a clear failure. assert measured_v is not None, ( "measure_voltage_v() returned no number; " "check the firmware's MEAS:VOLT? response format" ) assert measured_i is not None, ( "measure_current_a() returned no number; " "check the firmware's MEAS:CURR? response format" ) # Sanity: measured voltage should be within ±10% of the nominal # setpoint when the bench is steady. Loose tolerance because PSU # accuracy + meter noise + cable drop all stack up. if expected_v is not None: tol = 0.10 * expected_v assert abs(measured_v - expected_v) <= tol, ( f"Measured {measured_v}V is outside ±10% of nominal {expected_v}V " f"(tolerance ±{tol:.2f}V). Bench supply may be drifting or the " f"PSU isn't connected to its measure points." )