Files
slic/tests/test_utils_dbusnotify.py
T
tligui_y 60211ca1b2
Run CI Tests / test (push) Successful in 1m25s
Update tests/test_utils_dbusnotify.py
2025-08-18 17:50:10 +02:00

268 lines
10 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
import time
import pytest
import dbus
@pytest.fixture(scope="session")
def notifier():
"""Importe DBusNotify avec un SessionBus actif (dunst -print)."""
import slic.utils.dbusnotify as dbn
return dbn.DBusNotify()
def test_notify_create(notifier):
"""Test la création d'une notification simple."""
from slic.utils.dbusnotify import Notification
notification = notifier.notify("Test Notification", "This is a test notification.")
assert notification.nid is not None
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 DBus."""
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 DBus."""
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."
# on sait que dunst expose typiquement "body", "actions", "icon", "sound"
expected = {"actions", "body", "icon", "sound"}
assert expected.intersection(capabilities), "Capabilities don't contain expected values"
for cap in capabilities:
assert isinstance(cap, str), f"Capability should be str, got {type(cap)}"
def test_notify_and_close(notifier):
"""Test envoi + fermeture d'une notification."""
notification = notifier.notify("Test Close", "This notification will be closed.")
time.sleep(0.2) # petite marge
try:
notification.close()
except Exception as e:
pytest.fail(f"close() raised an exception: {e}")
def test_notify_invalid_value(notifier):
"""On attend une exception si le body n'est pas une string."""
with pytest.raises(Exception):
notifier.notify("Invalid Test", 1234)
def test_convert_dbus_strings():
"""Vérifie la conversion dbus.String -> 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'
'''