261 lines
7.4 KiB
Python
261 lines
7.4 KiB
Python
import asyncio
|
|
|
|
import pytest
|
|
from dbus_fast import Message, MessageType
|
|
from dbus_fast.aio import MessageBus
|
|
from dbus_fast.constants import PropertyAccess
|
|
from dbus_fast.service import (
|
|
ServiceInterface,
|
|
SignalDisabledError,
|
|
dbus_property,
|
|
signal,
|
|
)
|
|
from dbus_fast.signature import Variant
|
|
|
|
|
|
class ExampleInterface(ServiceInterface):
|
|
def __init__(self, name):
|
|
super().__init__(name)
|
|
|
|
@signal()
|
|
def signal_empty(self):
|
|
assert type(self) is ExampleInterface
|
|
|
|
@signal()
|
|
def signal_simple(self) -> "s":
|
|
assert type(self) is ExampleInterface
|
|
return "hello"
|
|
|
|
@signal()
|
|
def signal_multiple(self) -> "ss":
|
|
assert type(self) is ExampleInterface
|
|
return ["hello", "world"]
|
|
|
|
@signal(name="renamed")
|
|
def original_name(self):
|
|
assert type(self) is ExampleInterface
|
|
|
|
@signal(disabled=True)
|
|
def signal_disabled(self):
|
|
assert type(self) is ExampleInterface
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def test_prop(self) -> "i":
|
|
return 42
|
|
|
|
|
|
class SecondExampleInterface(ServiceInterface):
|
|
def __init__(self, name):
|
|
super().__init__(name)
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def str_prop(self) -> "s":
|
|
return "abc"
|
|
|
|
@dbus_property(access=PropertyAccess.READ)
|
|
def list_prop(self) -> "ai":
|
|
return [1, 2, 3]
|
|
|
|
|
|
class ExpectMessage:
|
|
def __init__(self, bus1, bus2, interface_name, timeout=1):
|
|
self.future = asyncio.get_running_loop().create_future()
|
|
self.bus1 = bus1
|
|
self.bus2 = bus2
|
|
self.interface_name = interface_name
|
|
self.timeout = timeout
|
|
self.timeout_task = None
|
|
|
|
def message_handler(self, msg):
|
|
if msg.sender == self.bus1.unique_name and msg.interface == self.interface_name:
|
|
self.timeout_task.cancel()
|
|
self.future.set_result(msg)
|
|
return True
|
|
|
|
def timeout_cb(self):
|
|
self.future.set_exception(TimeoutError)
|
|
|
|
async def __aenter__(self):
|
|
self.bus2.add_message_handler(self.message_handler)
|
|
self.timeout_task = asyncio.get_running_loop().call_later(
|
|
self.timeout, self.timeout_cb
|
|
)
|
|
|
|
return self.future
|
|
|
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
self.bus2.remove_message_handler(self.message_handler)
|
|
|
|
|
|
def assert_signal_ok(signal, export_path, member, signature, body):
|
|
assert signal.message_type == MessageType.SIGNAL
|
|
assert signal.path == export_path
|
|
assert signal.member == member
|
|
assert signal.signature == signature
|
|
assert signal.body == body
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_signals():
|
|
bus1 = await MessageBus().connect()
|
|
bus2 = await MessageBus().connect()
|
|
|
|
interface = ExampleInterface("test.interface")
|
|
export_path = "/test/path"
|
|
bus1.export(export_path, interface)
|
|
|
|
await bus2.call(
|
|
Message(
|
|
destination="org.freedesktop.DBus",
|
|
path="/org/freedesktop/DBus",
|
|
interface="org.freedesktop.DBus",
|
|
member="AddMatch",
|
|
signature="s",
|
|
body=[f"sender={bus1.unique_name}"],
|
|
)
|
|
)
|
|
|
|
async with ExpectMessage(bus1, bus2, interface.name) as expected_signal:
|
|
interface.signal_empty()
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="signal_empty",
|
|
signature="",
|
|
body=[],
|
|
)
|
|
|
|
async with ExpectMessage(bus1, bus2, interface.name) as expected_signal:
|
|
interface.original_name()
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="renamed",
|
|
signature="",
|
|
body=[],
|
|
)
|
|
|
|
async with ExpectMessage(bus1, bus2, interface.name) as expected_signal:
|
|
interface.signal_simple()
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="signal_simple",
|
|
signature="s",
|
|
body=["hello"],
|
|
)
|
|
|
|
async with ExpectMessage(bus1, bus2, interface.name) as expected_signal:
|
|
interface.signal_multiple()
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="signal_multiple",
|
|
signature="ss",
|
|
body=["hello", "world"],
|
|
)
|
|
|
|
with pytest.raises(SignalDisabledError):
|
|
interface.signal_disabled()
|
|
|
|
bus1.disconnect()
|
|
bus2.disconnect()
|
|
bus1._sock.close()
|
|
bus2._sock.close()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_interface_add_remove_signal():
|
|
bus1 = await MessageBus().connect()
|
|
bus2 = await MessageBus().connect()
|
|
|
|
await bus2.call(
|
|
Message(
|
|
destination="org.freedesktop.DBus",
|
|
path="/org/freedesktop/DBus",
|
|
interface="org.freedesktop.DBus",
|
|
member="AddMatch",
|
|
signature="s",
|
|
body=[f"sender={bus1.unique_name}"],
|
|
)
|
|
)
|
|
|
|
first_interface = ExampleInterface("test.interface.first")
|
|
second_interface = SecondExampleInterface("test.interface.second")
|
|
export_path = "/test/path"
|
|
|
|
# add first interface
|
|
async with ExpectMessage(
|
|
bus1, bus2, "org.freedesktop.DBus.ObjectManager"
|
|
) as expected_signal:
|
|
bus1.export(export_path, first_interface)
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="InterfacesAdded",
|
|
signature="oa{sa{sv}}",
|
|
body=[
|
|
export_path,
|
|
{"test.interface.first": {"test_prop": Variant("i", 42)}},
|
|
],
|
|
)
|
|
|
|
# add second interface
|
|
async with ExpectMessage(
|
|
bus1, bus2, "org.freedesktop.DBus.ObjectManager"
|
|
) as expected_signal:
|
|
bus1.export(export_path, second_interface)
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="InterfacesAdded",
|
|
signature="oa{sa{sv}}",
|
|
body=[
|
|
export_path,
|
|
{
|
|
"test.interface.second": {
|
|
"str_prop": Variant("s", "abc"),
|
|
"list_prop": Variant("ai", [1, 2, 3]),
|
|
}
|
|
},
|
|
],
|
|
)
|
|
|
|
# remove single interface
|
|
async with ExpectMessage(
|
|
bus1, bus2, "org.freedesktop.DBus.ObjectManager"
|
|
) as expected_signal:
|
|
bus1.unexport(export_path, second_interface)
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="InterfacesRemoved",
|
|
signature="oas",
|
|
body=[export_path, ["test.interface.second"]],
|
|
)
|
|
|
|
# add second interface again
|
|
async with ExpectMessage(
|
|
bus1, bus2, "org.freedesktop.DBus.ObjectManager"
|
|
) as expected_signal:
|
|
bus1.export(export_path, second_interface)
|
|
await expected_signal
|
|
|
|
# remove multiple interfaces
|
|
async with ExpectMessage(
|
|
bus1, bus2, "org.freedesktop.DBus.ObjectManager"
|
|
) as expected_signal:
|
|
bus1.unexport(export_path)
|
|
assert_signal_ok(
|
|
signal=await expected_signal,
|
|
export_path=export_path,
|
|
member="InterfacesRemoved",
|
|
signature="oas",
|
|
body=[export_path, ["test.interface.first", "test.interface.second"]],
|
|
)
|
|
|
|
bus1.disconnect()
|
|
bus2.disconnect()
|
|
bus1._sock.close()
|
|
bus2._sock.close()
|