import pytest from io import StringIO import colorama from slic.utils.pv import PV from epics import PV as EpicsPV @pytest.fixture def capture_stdout(monkeypatch): buf = StringIO() monkeypatch.setattr("sys.stdout", buf) return buf # Fake PV class FakeEPICS(EpicsPV): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._value = 0.0 self.units = "units" def get(self, *args, **kwargs): print(f"[DEBUG] FakeEPICS.get() returning {self._value}") return self._value def put(self, value, *args, **kwargs): self._value = value return value # Fixture that monkeypatch epics.PV → FakeEPICS @pytest.fixture def fake_epics_pv(monkeypatch): monkeypatch.setattr("epics.PV", FakeEPICS) return PV("TEST:PV") @pytest.fixture def capture_stdout(monkeypatch): buf = StringIO() monkeypatch.setattr("sys.stdout", buf) return buf @pytest.mark.parametrize("value, expected_bar, expected_color, expected_repr", [ (25.0, "██▌ ", colorama.Fore.GREEN, 'PV "TEST:PV" at 25.0 units'), (50.0, "█████ ", colorama.Fore.GREEN, 'PV "TEST:PV" at 50.0 units'), (75.0, "███████▌ ", colorama.Fore.GREEN, 'PV "TEST:PV" at 75.0 units'), (100.0, "██████████", colorama.Fore.GREEN, 'PV "TEST:PV" at 100.0 units'), (150.0, ">>>>>>>>>>", colorama.Fore.RED, 'PV "TEST:PV" at 150.0 units'), (-50.0, "<<<<<<<<<<", colorama.Fore.RED, 'PV "TEST:PV" at -50.0 units') ]) def test_progress_and_repr(fake_epics_pv, capture_stdout, value, expected_bar, expected_color, expected_repr): pv = fake_epics_pv # Test put() and progress bar pv.put(value, show_progress=True) output = capture_stdout.getvalue() # Verify progress bar display assert f"|{expected_bar}|" in output assert expected_color in output assert str(value) in output # Verify value update assert pv.get() == pytest.approx(value) # Test origin_repr assert f"PV TEST:PV at {value} units" == pv.orig_repr() capture_stdout.truncate(0) def test_use_callback_context_manager(fake_epics_pv): # Test the use_callback context manager functionality pv = fake_epics_pv callback_log = [] def test_callback(value, **kwargs): callback_log.append(value) # Verify callback is added and removed properly assert len(pv._pv._callbacks) == 0 # No callbacks initially with pv.use_callback(test_callback) as callback_id: # Callback should be added when entering context assert len(pv._pv._callbacks) == 1 assert callback_id in pv._pv._callbacks # Trigger callback pv.put(10.0) assert callback_log == [10.0] # Callback should be removed after context assert len(pv._pv._callbacks) == 0