100 lines
3.5 KiB
Python
100 lines
3.5 KiB
Python
"""La fabrique rend un overlay conforme au contrat, sans ouvrir de fenêtre."""
|
|
import pytest
|
|
|
|
from interface import classe_overlay_pour
|
|
|
|
|
|
def test_l_overlay_macos_declare_tout_le_contrat():
|
|
classe = classe_overlay_pour("darwin")
|
|
for methode in ("show", "hide", "set_level", "set_status", "rearmer_garde"):
|
|
assert callable(getattr(classe, methode, None)), f"{methode} manque"
|
|
|
|
|
|
def test_une_plateforme_inconnue_est_refusee_clairement():
|
|
with pytest.raises(NotImplementedError) as e:
|
|
classe_overlay_pour("haiku-os")
|
|
assert "haiku-os" in str(e.value)
|
|
|
|
|
|
def test_la_fabrique_rend_overlayqt_pour_win32():
|
|
classe = classe_overlay_pour("win32")
|
|
from interface.qt import OverlayQt
|
|
assert classe is OverlayQt
|
|
|
|
|
|
def test_l_overlay_qt_declare_tout_le_contrat():
|
|
classe = classe_overlay_pour("win32")
|
|
for methode in ("show", "hide", "set_level", "set_status", "rearmer_garde"):
|
|
assert callable(getattr(classe, methode, None)), f"{methode} manque"
|
|
|
|
|
|
# --- garde de duree de vie : annulation reelle a hide() ---------------------
|
|
#
|
|
# Exception assumee au principe "aucune fenetre" du haut de ce fichier : la
|
|
# seule facon honnete de prouver qu'une garde est REELLEMENT annulee (et pas
|
|
# seulement oubliee dans le champ `_garde`) est d'interroger l'etat vrai de la
|
|
# minuterie native — NSTimer.isValid() cote Cocoa, QTimer.isActive() cote Qt.
|
|
# Une doublure (fake) ne fournirait aucune garantie sur ce point : elle ne
|
|
# ferait que rejouer ce que le code de production affirme deja faire.
|
|
#
|
|
# Piege du 21/08 (halo fige) : une garde dereferencee (`_garde = None`) mais
|
|
# jamais invalidee continue de courir en coulisse, et peut refermer un overlay
|
|
# rouvert entre-temps, en pleine dictee. D'ou : on capture l'objet minuterie
|
|
# AVANT hide()/rearmer_garde(), pour verifier SON etat a lui, pas seulement le
|
|
# champ qui le reference.
|
|
from interface.cocoa import PulseWindow
|
|
from interface.qt import OverlayQt
|
|
|
|
|
|
def _garde_active_cocoa(garde):
|
|
return bool(garde.isValid())
|
|
|
|
|
|
def _garde_active_qt(garde):
|
|
return garde.isActive()
|
|
|
|
|
|
_IMPLEMENTATIONS_REELLES = [
|
|
(lambda: PulseWindow.alloc().init(), _garde_active_cocoa),
|
|
(OverlayQt, _garde_active_qt),
|
|
]
|
|
_IDS_IMPLEMENTATIONS = ["cocoa", "qt"]
|
|
|
|
|
|
@pytest.fixture(params=_IMPLEMENTATIONS_REELLES, ids=_IDS_IMPLEMENTATIONS)
|
|
def overlay_et_garde_active(request):
|
|
"""Une vraie instance d'overlay, plus la fonction qui lit l'etat reel de sa
|
|
minuterie de garde. Ferme toujours la fenetre a la fin, meme si le test echoue."""
|
|
construire, garde_active = request.param
|
|
ov = construire()
|
|
try:
|
|
yield ov, garde_active
|
|
finally:
|
|
ov.hide()
|
|
|
|
|
|
def test_show_arme_une_garde_valide(overlay_et_garde_active):
|
|
ov, garde_active = overlay_et_garde_active
|
|
ov.show()
|
|
assert ov._garde is not None
|
|
assert garde_active(ov._garde)
|
|
|
|
|
|
def test_hide_annule_reellement_la_garde(overlay_et_garde_active):
|
|
ov, garde_active = overlay_et_garde_active
|
|
ov.show()
|
|
garde = ov._garde # reference gardee AVANT hide()
|
|
ov.hide()
|
|
assert ov._garde is None
|
|
assert not garde_active(garde), "la minuterie doit etre invalidee, pas seulement oubliee"
|
|
|
|
|
|
def test_rearmer_garde_ne_laisse_qu_une_seule_garde_active(overlay_et_garde_active):
|
|
ov, garde_active = overlay_et_garde_active
|
|
ov.show()
|
|
ancienne_garde = ov._garde
|
|
ov.rearmer_garde(5.0)
|
|
assert ov._garde is not ancienne_garde
|
|
assert not garde_active(ancienne_garde), "l'ancienne garde doit etre invalidee au remplacement"
|
|
assert garde_active(ov._garde)
|