diff --git a/tests/test_utils_pyepics.py b/tests/test_utils_pyepics.py new file mode 100644 index 000000000..2a12a868a --- /dev/null +++ b/tests/test_utils_pyepics.py @@ -0,0 +1,187 @@ +import pytest +import time +import threading +from morbidissimo import MorIOC +from slic.utils.hastyepics import get_pv as PV +from your_module import EnumWrapper, MonitorAccumulator, Positioner, EpicsString + +# ------------------------------------------------------------------------------ +# Fixture IOC avec PVs pour tous les cas de test +# ------------------------------------------------------------------------------ +@pytest.fixture(scope="module", autouse=True) +def run_test_ioc(): + stop = threading.Event() + + def ioc(): + with MorIOC("TEST") as mor: + mor.serve( + # Pour EnumWrapper + TEST_ENUM_FULL=["OFF", "ON", "STANDBY", "FAULT"], + TEST_ENUM_EMPTY=[], + TEST_ENUM_INT=0, + + # Pour MonitorAccumulator + TEST_ANALOG=0.0, + + # Pour EpicsString + TEST_STRING="init", + TEST_STRING_BINARY=b"binary", + + # Pour tests avancés + TEST_SPECIAL_CHARS="a.b c@d" + ) + while not stop.is_set(): + time.sleep(0.1) + + t = threading.Thread(target=ioc) + t.start() + time.sleep(2) + yield + stop.set() + t.join() + +# ------------------------------------------------------------------------------ +# Tests EnumWrapper avec couverture 100% de .set() +# ------------------------------------------------------------------------------ +def test_enum_wrapper_set_coverage(capsys): + """Test exhaustif de EnumWrapper.set() avec sorties print""" + enum_full = EnumWrapper("TEST_ENUM_FULL") + enum_empty = EnumWrapper("TEST_ENUM_EMPTY") + enum_int = EnumWrapper("TEST_ENUM_INT") + + # Cas 1: Set avec string valide + print("\n=== Test set(str) valide ===") + enum_full.set("ON") + assert enum_full.get_name() == "ON" + out, _ = capsys.readouterr() + assert "ON" in out # Vérifie l'affichage + + # Cas 2: Set avec index valide + print("\n=== Test set(int) valide ===") + enum_full.set(2) + assert enum_full.get_name() == "STANDBY" + out, _ = capsys.readouterr() + assert "STANDBY" in out + + # Cas 3: Set avec string invalide + print("\n=== Test set(str) invalide ===") + with pytest.raises(AssertionError) as exc: + enum_full.set("INVALID") + assert "need to be one of" in str(exc.value) + out, _ = capsys.readouterr() + assert "INVALID" in out + + # Cas 4: Set avec index invalide (trop grand) + print("\n=== Test set(int) hors limite ===") + with pytest.raises(AssertionError) as exc: + enum_full.set(10) + assert "positive" in str(exc.value) + + # Cas 5: Set avec index négatif + print("\n=== Test set(int) négatif ===") + with pytest.raises(AssertionError): + enum_full.set(-1) + + # Cas 6: Enum vide + print("\n=== Test enum vide ===") + with pytest.raises(AssertionError): + enum_empty.set("ANY") + + # Cas 7: Type non supporté + print("\n=== Test type invalide ===") + with pytest.raises(AssertionError): + enum_full.set(3.14) # Float non accepté + +# ------------------------------------------------------------------------------ +# Tests MonitorAccumulator avec couverture 100% +# ------------------------------------------------------------------------------ +def test_monitor_accumulator_full_coverage(capsys): + """Test exhaustif de MonitorAccumulator""" + pv = PV("TEST_ANALOG") + monitor = MonitorAccumulator(pv, attr="value", keywords=["value", "timestamp"]) + + # Cas 1: Accumulation normale + print("\n=== Test accumulation simple ===") + monitor.accumulate() + pv.put(42.0) + time.sleep(0.5) + assert len(monitor.values) > 0 + assert monitor.values[-1][0] == pytest.approx(42.0) + + # Cas 2: Cycle avec réinitialisation + print("\n=== Test cycle() ===") + data = monitor.cycle() + assert len(data) > 0 + assert monitor.values == [] + + # Cas 3: Callback avec attribut spécifique + print("\n=== Test callback avec attr ===") + monitor_attr = MonitorAccumulator(pv, attr="precision") + monitor_attr.accumulate() + pv.put(99.9) + time.sleep(0.5) + monitor_attr.stop() + + # Cas 4: Mots-clés personnalisés + print("\n=== Test keywords custom ===") + monitor_custom = MonitorAccumulator(pv, keywords=["severity"]) + monitor_custom.accumulate() + pv.put(123.4) + time.sleep(0.5) + assert "severity" in str(monitor_custom.values) + +# ------------------------------------------------------------------------------ +# Tests Positioner avec couverture 100% +# ------------------------------------------------------------------------------ +def test_positioner_full_coverage(capsys): + """Test exhaustif de Positioner""" + # Cas 1: Noms simples + print("\n=== Test noms simples ===") + pos = Positioner([("START", lambda: print("Started")), + ("STOP", lambda: print("Stopped"))]) + pos.START() + out, _ = capsys.readouterr() + assert "Started" in out + + # Cas 2: Noms spéciaux + print("\n=== Test noms spéciaux ===") + pos_special = Positioner([ + ("1.2", lambda: print("Version")), + ("a b", lambda: print("Espace")), + ("@test", lambda: print("At")) + ]) + pos_special.v1p2() + pos_special.a_b() + pos_special._test() + out, _ = capsys.readouterr() + assert "Version" in out and "Espace" in out + +# ------------------------------------------------------------------------------ +# Tests EpicsString avec couverture 100% +# ------------------------------------------------------------------------------ +def test_epics_string_full_coverage(capsys): + """Test exhaustif de EpicsString""" + # Cas 1: String normale + print("\n=== Test string ASCII ===") + estr = EpicsString("TEST_STRING") + estr.set("new_value") + assert estr.get() == "new_value" + estr("updated") + assert str(estr) == "updated" + + # Cas 2: Binary data + print("\n=== Test binary data ===") + estr_bin = EpicsString("TEST_STRING_BINARY") + assert isinstance(estr_bin.get(), str) + + # Cas 3: Caractères spéciaux + print("\n=== Test caractères spéciaux ===") + estr_special = EpicsString("TEST_SPECIAL_CHARS") + estr_special.set("a@b.c d") + assert "d" in estr_special.get() + +# ------------------------------------------------------------------------------ +# Exécution +# ------------------------------------------------------------------------------ +if __name__ == "__main__": + pytest.main(["-v", "-s", "--cov=your_module", "--cov-report=term-missing"]) \ No newline at end of file