324 lines
12 KiB
Python
324 lines
12 KiB
Python
import os
|
||
import time
|
||
import subprocess
|
||
import pytest
|
||
import dbus
|
||
|
||
from dbusmock import DBusTestCase, MOCK_IFACE
|
||
|
||
import os
|
||
import time
|
||
import subprocess
|
||
import pytest
|
||
import dbus
|
||
|
||
_MAKO_PROC = None
|
||
_MAKO_LOGFILE = "/tmp/mako-test.log"
|
||
|
||
|
||
@pytest.fixture(scope="session", autouse=True)
|
||
def mako_daemon():
|
||
"""Start mako in headless logger mode and ensure cleanup."""
|
||
global _MAKO_PROC
|
||
|
||
# Ensure old log removed
|
||
if os.path.exists(_MAKO_LOGFILE):
|
||
os.remove(_MAKO_LOGFILE)
|
||
|
||
# Launch mako with --output (no graphical deps, only logs)
|
||
_MAKO_PROC = subprocess.Popen(
|
||
["mako", f"--output={_MAKO_LOGFILE}"],
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
)
|
||
|
||
# petite pause pour qu’il prenne org.freedesktop.Notifications
|
||
time.sleep(0.5)
|
||
|
||
yield
|
||
|
||
if _MAKO_PROC:
|
||
_MAKO_PROC.terminate()
|
||
try:
|
||
_MAKO_PROC.wait(timeout=2)
|
||
except subprocess.TimeoutExpired:
|
||
_MAKO_PROC.kill()
|
||
|
||
|
||
@pytest.fixture
|
||
def notifier(monkeypatch):
|
||
"""Fixture qui force ton code à utiliser le vrai bus DBus de session."""
|
||
import slic.utils.dbusnotify as dbn
|
||
bus = dbus.SessionBus()
|
||
monkeypatch.setattr(dbn.dbus, "SessionBus", lambda: bus)
|
||
monkeypatch.setattr(dbn.dbus, "SystemBus", lambda: bus)
|
||
return dbn.DBusNotify()
|
||
|
||
|
||
def _read_mako_log(deadline=2.0):
|
||
"""Helper pour lire le fichier de log de mako jusqu'à trouver une notif."""
|
||
end = time.time() + deadline
|
||
while time.time() < end:
|
||
if os.path.exists(_MAKO_LOGFILE):
|
||
with open(_MAKO_LOGFILE, "r", encoding="utf-8") as f:
|
||
content = f.read().strip()
|
||
if content:
|
||
return content
|
||
time.sleep(0.1)
|
||
return ""
|
||
|
||
|
||
# ----------------------------------------------------------------------
|
||
# == Tes tests repris intégralement, adaptés au backend mako ==
|
||
|
||
|
||
def test_notify_create(notifier):
|
||
"""Test la création d'une notification."""
|
||
from slic.utils.dbusnotify import Notification
|
||
notification = notifier.notify("Test Notification", "This is a test notification.")
|
||
|
||
assert isinstance(notification, Notification)
|
||
assert notification.summary == "Test Notification"
|
||
assert notification.body == "This is a test notification."
|
||
|
||
content = _read_mako_log()
|
||
assert "Test Notification" in content
|
||
assert "This is a test notification." in content
|
||
|
||
|
||
def test_notify_update(notifier):
|
||
"""Test la mise à jour d'une notification."""
|
||
notification = notifier.notify("Test Notification", "This is a test notification.")
|
||
notification.update(summary="Updated Test", body="This notification has been updated.")
|
||
|
||
assert notification.summary == "Updated Test"
|
||
assert notification.body == "This notification has been updated."
|
||
|
||
content = _read_mako_log()
|
||
assert "Updated Test" in content
|
||
assert "This notification has been updated." in content
|
||
|
||
|
||
def test_get_server_info(notifier):
|
||
"""Test l'obtention des informations du serveur."""
|
||
server_info = notifier.get_server_info()
|
||
|
||
assert isinstance(server_info, dict)
|
||
assert "name" in server_info and server_info["name"], "Server name is empty"
|
||
assert "vendor" in server_info and server_info["vendor"], "Server vendor is empty"
|
||
assert "version" in server_info and server_info["version"], "Server version is empty"
|
||
assert "spec" in server_info and server_info["spec"], "Server spec is empty"
|
||
|
||
|
||
def test_get_capabilities(notifier):
|
||
"""Test l'obtention des capacités du serveur."""
|
||
# Normaliser: list[str] (gère tuple/dbus.Array/dbus.String)
|
||
capabilities = [str(x) for x in list(notifier.get_capabilities())]
|
||
|
||
assert isinstance(capabilities, list), "Capabilities should be a list."
|
||
assert len(capabilities) > 0, "Capabilities list is empty."
|
||
|
||
# mako supporte au minimum "body"
|
||
assert "body" in capabilities
|
||
|
||
for cap in capabilities:
|
||
assert isinstance(cap, str), f"Each capability should be a string, but found {type(cap)}."
|
||
|
||
|
||
def test_notify_and_close(notifier):
|
||
"""Test l'envoi d'une notification et sa fermeture."""
|
||
notification = notifier.notify("Test Close", "This notification will be closed.")
|
||
time.sleep(0.2) # petite marge
|
||
notification.close()
|
||
|
||
content = _read_mako_log()
|
||
assert "Test Close" in content
|
||
# pas garanti que mako loggue "Closed", donc on vérifie juste que la notif apparaît
|
||
|
||
|
||
def test_notify_invalid_value(notifier):
|
||
"""On attend une exception si la valeur est incorrecte (body non string)."""
|
||
with pytest.raises(Exception):
|
||
notifier.notify("Invalid Test", 1234)
|
||
|
||
|
||
def test_convert_dbus_strings():
|
||
"""Vérifie la conversion de dbus.String en str dans une séquence mixte."""
|
||
from slic.utils.dbusnotify import convert_dbus_strings
|
||
test_sequence = [dbus.String('Test String 1'), 42, dbus.String('Test String 2'), 3.14]
|
||
converted = convert_dbus_strings(test_sequence)
|
||
|
||
assert isinstance(converted[0], str), f"Expected str, got {type(converted[0])}"
|
||
assert isinstance(converted[2], str), f"Expected str, got {type(converted[2])}"
|
||
assert isinstance(converted[1], int), f"Expected int, got {type(converted[1])}"
|
||
assert isinstance(converted[3], float), f"Expected float, got {type(converted[3])}"
|
||
assert converted[0] == 'Test String 1'
|
||
assert converted[2] == 'Test String 2'
|
||
|
||
'''
|
||
# Process du serveur mock Notifications (exposé aux tests)
|
||
_DBUS_NOTIFY_PROC = None
|
||
|
||
|
||
class _DBusEnv(DBusTestCase):
|
||
"""Accès aux helpers de DBusTestCase sans classe de tests."""
|
||
pass
|
||
|
||
|
||
@pytest.fixture(scope="session", autouse=True)
|
||
def _dbus_session_notifications():
|
||
"""Lance un bus de session et un service Notifications mocké pour toute la session."""
|
||
_DBusEnv.start_session_bus()
|
||
|
||
# Démarrer le mock org.freedesktop.Notifications et CAPTURER stdout
|
||
p = _DBusEnv.spawn_server(
|
||
'org.freedesktop.Notifications',
|
||
'/org/freedesktop/Notifications',
|
||
'org.freedesktop.Notifications',
|
||
False, # system_bus=False
|
||
subprocess.PIPE # stdout du mock lisible côté test
|
||
)
|
||
|
||
# Récupérer l'objet mock et injecter les méthodes nécessaires
|
||
bus = _DBusEnv.get_dbus(False) # False => session bus
|
||
obj = bus.get_object('org.freedesktop.Notifications',
|
||
'/org/freedesktop/Notifications')
|
||
mock = dbus.Interface(obj, MOCK_IFACE)
|
||
|
||
# NB: 5e argument = code Python (string) exécuté côté mock
|
||
mock.AddMethod('org.freedesktop.Notifications', 'GetServerInformation',
|
||
'', 'ssss',
|
||
'ret = ("pytest-notify","pytest-vendor","1.0","1.2")')
|
||
mock.AddMethod('org.freedesktop.Notifications', 'GetCapabilities',
|
||
'', 'as',
|
||
'ret = ["actions","body","icon","sound"]')
|
||
mock.AddMethod('org.freedesktop.Notifications', 'Notify',
|
||
'susssasa{sv}i', 'u',
|
||
'ret = 1')
|
||
mock.AddMethod('org.freedesktop.Notifications', 'CloseNotification',
|
||
'u', '',
|
||
'import sys; print("Closed %d" % args[0]); sys.stdout.flush()')
|
||
|
||
# Attendre que le nom soit effectivement enregistré sur le bus (évite la course)
|
||
dbus_daemon = dbus.Interface(
|
||
bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus'),
|
||
'org.freedesktop.DBus'
|
||
)
|
||
for _ in range(50):
|
||
if dbus_daemon.NameHasOwner('org.freedesktop.Notifications'):
|
||
break
|
||
time.sleep(0.1)
|
||
else:
|
||
pytest.fail("org.freedesktop.Notifications n'a pas pris le nom sur le bus")
|
||
|
||
# Exposer le process du mock aux tests
|
||
global _DBUS_NOTIFY_PROC
|
||
_DBUS_NOTIFY_PROC = p
|
||
|
||
yield
|
||
# Pas de teardown spécifique requis
|
||
|
||
|
||
@pytest.fixture
|
||
def notify_proc():
|
||
"""Retourne le process du serveur mock pour lire son stdout."""
|
||
return _DBUS_NOTIFY_PROC
|
||
|
||
|
||
@pytest.fixture
|
||
def notifier(monkeypatch):
|
||
"""Instancie DBusNotify en forçant l'usage du même SessionBus que la fixture."""
|
||
bus = _DBusEnv.get_dbus(False) # session bus lancé ci-dessus
|
||
import slic.utils.dbusnotify as dbn
|
||
# S'assure que DBusNotify (et code associé) utilise EXACTEMENT ce bus
|
||
monkeypatch.setattr(dbn.dbus, "SessionBus", lambda: bus)
|
||
monkeypatch.setattr(dbn.dbus, "SystemBus", lambda: bus) # garde-fou
|
||
return dbn.DBusNotify()
|
||
|
||
|
||
def test_notify_create(notifier):
|
||
"""Test la création d'une notification."""
|
||
from slic.utils.dbusnotify import Notification
|
||
notification = notifier.notify("Test Notification", "This is a test notification.")
|
||
|
||
assert isinstance(notification, Notification)
|
||
assert notification.summary == "Test Notification"
|
||
assert notification.body == "This is a test notification."
|
||
|
||
|
||
def test_notify_update(notifier):
|
||
"""Test la mise à jour d'une notification."""
|
||
notification = notifier.notify("Test Notification", "This is a test notification.")
|
||
notification.update(summary="Updated Test", body="This notification has been updated.")
|
||
|
||
assert notification.summary == "Updated Test"
|
||
assert notification.body == "This notification has been updated."
|
||
|
||
|
||
def test_get_server_info(notifier):
|
||
"""Test l'obtention des informations du serveur."""
|
||
server_info = notifier.get_server_info()
|
||
|
||
assert isinstance(server_info, dict)
|
||
assert "name" in server_info and server_info["name"], "Server name is empty"
|
||
assert "vendor" in server_info and server_info["vendor"], "Server vendor is empty"
|
||
assert "version" in server_info and server_info["version"], "Server version is empty"
|
||
assert "spec" in server_info and server_info["spec"], "Server spec is empty"
|
||
|
||
|
||
def test_get_capabilities(notifier):
|
||
"""Test l'obtention des capacités du serveur."""
|
||
# Normaliser: list[str] (gère tuple/dbus.Array/dbus.String)
|
||
capabilities = [str(x) for x in list(notifier.get_capabilities())]
|
||
|
||
assert isinstance(capabilities, list), "Capabilities should be a list."
|
||
assert len(capabilities) > 0, "Capabilities list is empty."
|
||
|
||
expected_capabilities = ['actions', 'body', 'icon', 'sound']
|
||
for cap in expected_capabilities:
|
||
assert cap in capabilities, f"Expected capability '{cap}' not found."
|
||
|
||
for cap in capabilities:
|
||
assert isinstance(cap, str), f"Each capability should be a string, but found {type(cap)}."
|
||
|
||
|
||
def test_notify_and_close(notifier, notify_proc):
|
||
"""Test l'envoi d'une notification et sa fermeture."""
|
||
notification = notifier.notify("Test Close", "This notification will be closed.")
|
||
time.sleep(0.2) # petite marge
|
||
notification.close()
|
||
|
||
# Lire la ligne imprimée par le mock ("Closed <id>")
|
||
deadline = time.time() + 2.0
|
||
seen = False
|
||
while time.time() < deadline:
|
||
line = notify_proc.stdout.readline().decode(errors="ignore").strip()
|
||
if not line:
|
||
time.sleep(0.05)
|
||
continue
|
||
if "Closed" in line:
|
||
seen = True
|
||
break
|
||
|
||
assert seen, "Notification was not closed properly."
|
||
|
||
|
||
def test_notify_invalid_value(notifier):
|
||
"""On attend une exception si la valeur est incorrecte (body non string)."""
|
||
with pytest.raises(Exception):
|
||
notifier.notify("Invalid Test", 1234)
|
||
|
||
|
||
def test_convert_dbus_strings():
|
||
"""Vérifie la conversion de dbus.String en str dans une séquence mixte."""
|
||
from slic.utils.dbusnotify import convert_dbus_strings
|
||
test_sequence = [dbus.String('Test String 1'), 42, dbus.String('Test String 2'), 3.14]
|
||
converted = convert_dbus_strings(test_sequence)
|
||
|
||
assert isinstance(converted[0], str), f"Expected str, got {type(converted[0])}"
|
||
assert isinstance(converted[2], str), f"Expected str, got {type(converted[2])}"
|
||
assert isinstance(converted[1], int), f"Expected int, got {type(converted[1])}"
|
||
assert isinstance(converted[3], float), f"Expected float, got {type(converted[3])}"
|
||
assert converted[0] == 'Test String 1'
|
||
assert converted[2] == 'Test String 2'
|
||
''' |