Files
slic/tests/test_utils_dbusnotify.py
T
tligui_y e45bb4547b
Run CI Tests / test (push) Has been cancelled
Update tests/test_utils_dbusnotify.py
2025-08-18 15:42:39 +02:00

324 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 quil 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'
'''