"""TektronixOscilloscopeBase: the SCPI setters that validate their input. These run against the real class with only the socket replaced. The scan tests use FakeScope, which stubs the setters out entirely — so a setter could raise on every call and nothing in the suite would notice, which is what happened: set_channel_coupling() iterated CHANNEL_COUPLING.items() instead of .values(), called .upper() on the list half of each pair, and raised AttributeError for any coupling at all. configure_channels() runs on connect, so the scope could not be connected to. """ import pytest from core.scope_sras import SRAS_CHANNELS, configure_channels from hardware.tektronix_base import TektronixOscilloscopeBase class RecordingScope(TektronixOscilloscopeBase): """The real instrument class with the wire replaced by a list.""" def __init__(self): super().__init__(resource_name="192.0.2.1") self._connected = True self.written: list[str] = [] def write(self, command): self.written.append(command) @pytest.fixture def scope(): return RecordingScope() def test_connect_time_channel_setup_reaches_the_wire(scope): """The regression: every command configure_channels() sends must go out.""" configure_channels(scope) for ch, profile in SRAS_CHANNELS.items(): assert f"SELect:CH{ch} ON" in scope.written assert f"CH{ch}:COUPling {profile.coupling}" in scope.written assert f"CH{ch}:TERmination {profile.termination_ohm}" in scope.written assert f"CH{ch}:SCAle {profile.scale_v_div}" in scope.written @pytest.mark.parametrize("coupling", ["DC", "AC", "dc", "ac"]) def test_channel_coupling_accepts_both_modes_in_any_case(scope, coupling): scope.set_channel_coupling(1, coupling) assert scope.written == [f"CH1:COUPling {coupling}"] def test_channel_coupling_rejects_an_unknown_mode(scope): """Rejection has to be the documented ValueError, not an AttributeError raised while building the list of valid options.""" with pytest.raises(ValueError, match="Invalid coupling mode"): scope.set_channel_coupling(1, "GND") assert scope.written == [] # The other setters share this shape; a table keeps them honest together. @pytest.mark.parametrize("setter,good,bad,sent", [ ("set_acquire_mode", "AVERAGE", "SMOOTH", "ACQuire:MODe AVERAGE"), ("set_acquire_mode", "SAM", "SMOOTH", "ACQuire:MODe SAM"), ("set_trigger_slope", "RISE", "SIDEWAYS", "TRIGger:A:EDGE:SLOpe RISE"), ("set_trigger_mode", "NORMAL", "SOMETIMES", "TRIGger:A:MODe NORMAL"), ]) def test_validated_setters_take_valid_values_and_reject_the_rest( scope, setter, good, bad, sent): getattr(scope, setter)(good) assert scope.written == [sent] with pytest.raises(ValueError): getattr(scope, setter)(bad)