async def test_sending_messages_between_buses(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: msg = Message(destination=bus1.unique_name, path='/org/test/path', interface='org.test.iface', member='SomeMember', serial=bus2.next_serial()) def message_handler(sent): if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member bus1.send(Message.new_method_return(sent, 's', ['got it'])) bus1.remove_message_handler(message_handler) return True bus1.add_message_handler(message_handler) reply = await bus2.call(msg) assert reply.message_type == MessageType.METHOD_RETURN assert reply.sender == bus1.unique_name assert reply.signature == 's' assert reply.body == ['got it'] assert reply.reply_serial == msg.serial def message_handler_error(sent): if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member bus1.send( Message.new_error(sent, 'org.test.Error', 'throwing an error')) bus1.remove_message_handler(message_handler_error) return True bus1.add_message_handler(message_handler_error) msg.serial = bus2.next_serial() reply = await bus2.call(msg) assert reply.message_type == MessageType.ERROR assert reply.sender == bus1.unique_name assert reply.reply_serial == msg.serial assert reply.error_name == 'org.test.Error' assert reply.signature == 's' assert reply.body == ['throwing an error'] msg.serial = bus2.next_serial() msg.flags = MessageFlag.NO_REPLY_EXPECTED reply = await bus2.call(msg) assert reply is None
async def test_object_manager(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: expected_reply = { '/test/path/deeper': { 'test.interface2': { 'Bar': Variant('s', 'str'), 'Foo': Variant('y', 42) } } } reply_ext = { '/test/path': { 'test.interface1': {}, 'test.interface2': { 'Bar': Variant('s', 'str'), 'Foo': Variant('y', 42) } } } interface = ExampleInterface('test.interface1') interface2 = ExampleComplexInterface('test.interface2') export_path = '/test/path' bus1.export(export_path, interface) bus1.export(export_path, interface2) bus1.export(export_path + '/deeper', interface2) reply_root = await bus2.call( Message(destination=bus1.unique_name, path='/', interface='org.freedesktop.DBus.ObjectManager', member='GetManagedObjects')) reply_level1 = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.ObjectManager', member='GetManagedObjects')) reply_level2 = await bus2.call( Message(destination=bus1.unique_name, path=export_path + '/deeper', interface='org.freedesktop.DBus.ObjectManager', member='GetManagedObjects')) assert reply_root.signature == 'a{oa{sa{sv}}}' assert reply_level1.signature == 'a{oa{sa{sv}}}' assert reply_level2.signature == 'a{oa{sa{sv}}}' assert reply_level2.body == [{}] assert reply_level1.body == [expected_reply] expected_reply.update(reply_ext) assert reply_root.body == [expected_reply]
async def test_bus_disconnect_before_reply(): '''In this test, the bus disconnects before the reply comes in. Make sure the caller receives a reply with the error instead of hanging.''' bus = MessageBus() assert not bus.connected async with bus.connect(): assert bus.connected await bus.disconnect() # This actually cancels the current scope. assert False, "Not called"
async def test_signals(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: 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()
async def test_export_alias(): async with MessageBus().connect() as bus: interface = ExampleInterface('test.interface') export_path = '/test/path' export_path2 = '/test/path/child' bus.export(export_path, interface) bus.export(export_path2, interface) result = await bus.call( Message(destination=bus.unique_name, path=export_path, interface='test.interface', member='some_method')) assert result.message_type is MessageType.METHOD_RETURN, result.body[0] assert interface._method_called interface._method_called = False result = await bus.call( Message(destination=bus.unique_name, path=export_path2, interface='test.interface', member='some_method')) assert result.message_type is MessageType.METHOD_RETURN, result.body[0] assert interface._method_called
async def test_standard_interfaces(): async with MessageBus().connect() as bus: msg = Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='ListNames', serial=bus.next_serial()) reply = await bus.call(msg) assert reply.message_type == MessageType.METHOD_RETURN assert reply.reply_serial == msg.serial assert reply.signature == 'as' assert bus.unique_name in reply.body[0] msg.interface = 'org.freedesktop.DBus.Introspectable' msg.member = 'Introspect' msg.serial = bus.next_serial() reply = await bus.call(msg) assert reply.message_type == MessageType.METHOD_RETURN assert reply.reply_serial == msg.serial assert reply.signature == 's' assert type(reply.body[0]) is str msg.member = 'MemberDoesNotExist' msg.serial = bus.next_serial() reply = await bus.call(msg) assert reply.message_type == MessageType.ERROR assert reply.reply_serial == msg.serial assert reply.error_name assert reply.signature == 's' assert type(reply.body[0]) is str
async def test_standard_interface_properties(): # standard interfaces have no properties, but should still behave correctly # when you try to call the methods anyway (#49) async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: interface = ExampleInterface('test.interface1') export_path = '/test/path' bus1.export(export_path, interface) for iface in [ 'org.freedesktop.DBus.Properties', 'org.freedesktop.DBus.Introspectable', 'org.freedesktop.DBus.Peer', 'org.freedesktop.DBus.ObjectManager' ]: result = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member='Get', signature='ss', body=[iface, 'anything'])) assert result.message_type is MessageType.ERROR assert result.error_name == ErrorType.UNKNOWN_PROPERTY.value result = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member='Set', signature='ssv', body=[iface, 'anything', Variant('s', 'new thing')])) assert result.message_type is MessageType.ERROR assert result.error_name == ErrorType.UNKNOWN_PROPERTY.value result = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member='GetAll', signature='s', body=[iface])) assert result.message_type is MessageType.METHOD_RETURN assert result.body == [{}]
async def test_unexpected_disconnect(): bus = MessageBus() assert not bus.connected with pytest.raises((anyio.BrokenResourceError, OSError, anyio.ExceptionGroup)): async with bus.connect(): assert bus.connected ping = bus.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='Ping')) os.close(bus._fd) # The actual async call will ecancel this scope # and re-raise the error when leaving the context await ping
async def test_property_changed_signal(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: 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}'])) interface = ExampleInterface('test.interface') export_path = '/test/path' bus1.export(export_path, interface) async def wait_for_message(): # TODO timeout future = ValueEvent() def message_handler(signal): if signal.interface == 'org.freedesktop.DBus.Properties': bus2.remove_message_handler(message_handler) future.set(signal) bus2.add_message_handler(message_handler) return await future bus2.send( Message(destination=bus1.unique_name, interface=interface.name, path=export_path, member='do_emit_properties_changed')) signal = await wait_for_message() assert signal.interface == 'org.freedesktop.DBus.Properties' assert signal.member == 'PropertiesChanged' assert signal.signature == 'sa{sv}as' assert signal.body == [ interface.name, { 'string_prop': Variant('s', 'asdf') }, ['container_prop'] ]
async def test_sending_file_descriptor_low_level(): async with MessageBus(negotiate_unix_fd=True).connect() as bus1, \ MessageBus(negotiate_unix_fd=True).connect() as bus2: fd_before = open_file() fd_after = None msg = Message(destination=bus1.unique_name, path='/org/test/path', interface='org.test.iface', member='SomeMember', body=[0], signature='h', unix_fds=[fd_before]) def message_handler(sent): nonlocal fd_after if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member assert sent.body == [0] assert len(sent.unix_fds) == 1 fd_after = sent.unix_fds[0] bus1.send(Message.new_method_return(sent, 's', ['got it'])) bus1.remove_message_handler(message_handler) return True bus1.add_message_handler(message_handler) reply = await bus2.call(msg) assert reply.body == ['got it'] assert fd_after is not None assert_fds_equal(fd_before, fd_after) for fd in [fd_before, fd_after]: os.close(fd) for bus in [bus1, bus2]: bus.disconnect()
async def test_sending_file_descriptor_with_proxy(): name = 'dbus.next.test.service' path = '/test/path' interface_name = 'test.interface' async with MessageBus(negotiate_unix_fd=True).connect() as bus: interface = ExampleInterface(interface_name) bus.export(path, interface) await bus.request_name(name) intr = await bus.introspect(name, path) proxy = bus.get_proxy_object(name, path, intr) proxy_interface = proxy.get_interface(interface_name) # test fds are replaced correctly in all high level interfaces fd = await proxy_interface.call_returns_fd() assert_fds_equal(interface.get_last_fd(), fd) interface.cleanup() os.close(fd) fd = open_file() await proxy_interface.call_accepts_fd(fd) assert_fds_equal(interface.get_last_fd(), fd) interface.cleanup() os.close(fd) fd = await proxy_interface.get_prop_fd() assert_fds_equal(interface.get_last_fd(), fd) interface.cleanup() os.close(fd) fd = open_file() await proxy_interface.set_prop_fd(fd) assert_fds_equal(interface.get_last_fd(), fd) interface.cleanup() os.close(fd) fut = ValueEvent() def on_signal_fd(fd): fut.set_result(fd) proxy_interface.off_signal_fd(on_signal_fd) proxy_interface.on_signal_fd(on_signal_fd) await anyio.sleep(0.1) # TODO we have a timing problem here interface.SignalFd() fd = await fut assert_fds_equal(interface.get_last_fd(), fd) interface.cleanup() os.close(fd)
async def test_peer_interface(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: reply = await bus2.call( Message(destination=bus1.unique_name, path='/path/doesnt/exist', interface='org.freedesktop.DBus.Peer', member='Ping')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == '' reply = await bus2.call( Message(destination=bus1.unique_name, path='/path/doesnt/exist', interface='org.freedesktop.DBus.Peer', member='GetMachineId', signature='')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 's'
async def test_export_introspection(): interface = ExampleInterface('test.interface') interface2 = ExampleInterface('test.interface2') export_path = '/test/path' export_path2 = '/test/path/child' async with MessageBus().connect() as bus: bus.export(export_path, interface) bus.export(export_path2, interface2) root = bus._introspect_export_path('/') assert len(root.nodes) == 1
async def test_introspectable_interface(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: interface = ExampleInterface('test.interface') interface2 = ExampleInterface('test.interface2') export_path = '/test/path' bus1.export(export_path, interface) bus1.export(export_path, interface2) reply = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Introspectable', member='Introspect')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 's' node = intr.Node.parse(reply.body[0]) assert len(node.interfaces) == standard_interfaces_count + 2 assert node.interfaces[-1].name == 'test.interface2' assert node.interfaces[-2].name == 'test.interface' assert not node.nodes # introspect works on every path reply = await bus2.call( Message(destination=bus1.unique_name, path='/path/doesnt/exist', interface='org.freedesktop.DBus.Introspectable', member='Introspect')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 's' node = intr.Node.parse(reply.body[0]) assert not node.interfaces assert not node.nodes
async def test_sending_signals_between_buses(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: add_match_msg = Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='AddMatch', signature='s', body=[f'sender={bus2.unique_name}']) await bus1.call(add_match_msg) async def wait_for_message(): future = ValueEvent() def message_handler(signal): if signal.sender == bus2.unique_name: bus1.remove_message_handler(message_handler) future.set_result(signal) bus1.add_message_handler(message_handler) return await future bus2.send( Message.new_signal('/org/test/path', 'org.test.interface', 'SomeSignal', 's', ['a signal'])) signal = await wait_for_message() assert signal.message_type == MessageType.SIGNAL assert signal.path == '/org/test/path' assert signal.interface == 'org.test.interface' assert signal.member == 'SomeSignal' assert signal.signature == 's' assert signal.body == ['a signal']
async def test_export_unexport(): interface = ExampleInterface('test.interface') interface2 = ExampleInterface('test.interface2') export_path = '/test/path' export_path2 = '/test/path/child' async with MessageBus().connect() as bus: bus.export(export_path, interface) assert export_path in bus._path_exports assert len(bus._path_exports[export_path]) == 1 assert bus._path_exports[export_path][0] is interface assert len(ServiceInterface._get_buses(interface)) == 1 bus.export(export_path2, interface2) node = bus._introspect_export_path(export_path) assert len(node.interfaces) == standard_interfaces_count + 1 assert len(node.nodes) == 1 # relative path assert node.nodes[0].name == 'child' bus.unexport(export_path, interface) assert export_path not in bus._path_exports assert len(ServiceInterface._get_buses(interface)) == 0 bus.export(export_path2, interface) assert len(bus._path_exports[export_path2]) == 2 # test unexporting the whole path bus.unexport(export_path2) assert not bus._path_exports assert not ServiceInterface._get_buses(interface) assert not ServiceInterface._get_buses(interface2) # test unexporting by name bus.export(export_path, interface) bus.unexport(export_path, interface.name) assert not bus._path_exports assert not ServiceInterface._get_buses(interface) node = bus._introspect_export_path('/path/doesnt/exist') assert type(node) is intr.Node assert not node.interfaces assert not node.nodes
async def test_signals_with_changing_owners(): well_known_name = 'test.signals.changing.name' async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2, \ MessageBus().connect() as bus3: async def ping(): await bus1.call( Message(destination=bus1.unique_name, interface='org.freedesktop.DBus.Peer', path='/test/path', member='Ping')) service_interface = ExampleInterface() introspection = Node.default() introspection.interfaces.append(service_interface.introspect()) # get the interface before export obj = bus1.get_proxy_object(well_known_name, '/test/path', introspection) iface = obj.get_interface('test.interface') counter = 0 def handler(what): nonlocal counter counter += 1 iface.on_some_signal(handler) await ping() # now export and get the name bus2.export('/test/path', service_interface) result = await bus2.request_name(well_known_name) assert result is RequestNameReply.PRIMARY_OWNER # the signal should work service_interface.SomeSignal() await ping() assert counter == 1 counter = 0 # now queue up a transfer of the name service_interface2 = ExampleInterface() bus3.export('/test/path', service_interface2) result = await bus3.request_name(well_known_name) assert result is RequestNameReply.IN_QUEUE # if it doesn't own the name, the signal shouldn't work here service_interface2.SomeSignal() await ping() assert counter == 0 # now transfer over the name and it should work bus2.disconnect() await ping() service_interface2.SomeSignal() await ping() assert counter == 1 counter = 0 bus1.disconnect() bus2.disconnect() bus3.disconnect()
async def test_interface_add_remove_signal(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: 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']])
async def test_methods(interface_class): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: interface = interface_class('test.interface') export_path = '/test/path' async def call(member, signature='', body=[], flags=MessageFlag.NONE): return await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface=interface.name, member=member, signature=signature, body=body, flags=flags)) bus1.export(export_path, interface) body = ['hello world'] reply = await call('echo', 's', body) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 's' assert reply.body == body body = ['hello', 'world'] reply = await call('echo_multiple', 'ss', body) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 'ss' assert reply.body == body body = [['hello', 'world'], Variant('v', Variant('(ss)', ['hello', 'world'])), { 'foo': Variant('t', 100) }, ['one', ['two', [Variant('s', 'three')]]]] signature = 'asva{sv}(s(s(v)))' SignatureTree(signature).verify(body) reply = await call('echo_containers', signature, body) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == signature assert reply.body == body reply = await call('ping') assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == '' assert reply.body == [] reply = await call('throws_unexpected_error') assert reply.message_type == MessageType.ERROR, reply.body[0] assert reply.error_name == ErrorType.SERVICE_ERROR.value, reply.body[0] reply = await call('throws_dbus_error') assert reply.message_type == MessageType.ERROR, reply.body[0] assert reply.error_name == 'test.error', reply.body[0] assert reply.body == ['an error ocurred'] reply = await call('ping', flags=MessageFlag.NO_REPLY_EXPECTED) assert reply is None reply = await call('throws_unexpected_error', flags=MessageFlag.NO_REPLY_EXPECTED) assert reply is None reply = await call('throws_dbus_error', flags=MessageFlag.NO_REPLY_EXPECTED) assert reply is None
async def test_signals(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: bus_intr = await bus1.introspect('org.freedesktop.DBus', '/org/freedesktop/DBus') bus_obj = bus1.get_proxy_object('org.freedesktop.DBus', '/org/freedesktop/DBus', bus_intr) stats = bus_obj.get_interface('org.freedesktop.DBus.Debug.Stats') await bus1.request_name('test.signals.name') service_interface = ExampleInterface() bus1.export('/test/path', service_interface) obj = bus2.get_proxy_object('test.signals.name', '/test/path', bus1._introspect_export_path('/test/path')) interface = obj.get_interface(service_interface.name) async def ping(): await bus2.call( Message(destination=bus1.unique_name, interface='org.freedesktop.DBus.Peer', path='/test/path', member='Ping')) err = None single_counter = 0 def single_handler(value): try: nonlocal single_counter nonlocal err assert value == 'hello' single_counter += 1 except Exception as e: err = e multiple_counter = 0 def multiple_handler(value1, value2): nonlocal multiple_counter nonlocal err try: assert value1 == 'hello' assert value2 == 'world' multiple_counter += 1 except Exception as e: err = e await ping() match_rules = await stats.call_get_all_match_rules() assert bus2.unique_name in match_rules bus_match_rules = match_rules[bus2.unique_name] # the bus connection itself takes a rule on NameOwnerChange after the high # level client is initialized assert len(bus_match_rules) == 1 assert len(bus2._user_message_handlers) == 0 interface.on_some_signal(single_handler) interface.on_signal_multiple(multiple_handler) # Interlude: adding a signal handler with `on_[signal]` should add a match rule and # message handler. Removing a signal handler with `off_[signal]` should # remove the match rule and message handler to avoid memory leaks. await ping() match_rules = await stats.call_get_all_match_rules() assert bus2.unique_name in match_rules bus_match_rules = match_rules[bus2.unique_name] # test the match rule and user handler has been added assert len(bus_match_rules) == 2 assert "type='signal',interface='test.interface',path='/test/path',sender='test.signals.name'" in bus_match_rules assert len(bus2._user_message_handlers) == 1 service_interface.SomeSignal() await ping() assert err is None assert single_counter == 1 service_interface.SignalMultiple() await ping() assert err is None assert multiple_counter == 1 # special case: another bus with the same path and interface but on a # different name and connection will trigger the match rule of the first # (happens with mpris) async with MessageBus().connect() as bus3: await bus3.request_name('test.signals.name2') service_interface2 = ExampleInterface() bus3.export('/test/path', service_interface2) obj = bus2.get_proxy_object( 'test.signals.name2', '/test/path', bus3._introspect_export_path('/test/path')) # we have to add a dummy handler to add the match rule iface2 = obj.get_interface(service_interface2.name) def dummy_signal_handler(what): pass iface2.on_some_signal(dummy_signal_handler) await ping() service_interface2.SomeSignal() await ping() # single_counter is not incremented for signals of the second interface assert single_counter == 1 interface.off_some_signal(single_handler) interface.off_signal_multiple(multiple_handler) iface2.off_some_signal(dummy_signal_handler) # After `off_[signal]`, the match rule and user handler should be removed await ping() match_rules = await stats.call_get_all_match_rules() assert bus2.unique_name in match_rules bus_match_rules = match_rules[bus2.unique_name] assert len(bus_match_rules) == 1 assert "type='signal',interface='test.interface',path='/test/path',sender='test.signals.name'" not in bus_match_rules assert len(bus2._user_message_handlers) == 0
async def test_property_methods(): async with MessageBus().connect() as bus1, \ MessageBus().connect() as bus2: interface = ExampleInterface('test.interface') export_path = '/test/path' bus1.export(export_path, interface) async def call_properties(member, signature, body): return await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member=member, signature=signature, body=body)) result = await call_properties('GetAll', 's', [interface.name]) assert result.message_type == MessageType.METHOD_RETURN, result.body[0] assert result.signature == 'a{sv}' assert result.body == [{ 'string_prop': Variant('s', interface._string_prop), 'readonly_prop': Variant('t', interface._readonly_prop), 'container_prop': Variant('a(ss)', interface._container_prop), 'renamed_prop': Variant('s', interface._renamed_prop) }] result = await call_properties('Get', 'ss', [interface.name, 'string_prop']) assert result.message_type == MessageType.METHOD_RETURN, result.body[0] assert result.signature == 'v' assert result.body == [Variant('s', 'hi')] result = await call_properties( 'Set', 'ssv', [interface.name, 'string_prop', Variant('s', 'ho')]) assert result.message_type == MessageType.METHOD_RETURN, result.body[0] assert interface.string_prop == 'ho' result = await call_properties( 'Set', 'ssv', [interface.name, 'readonly_prop', Variant('t', 100)]) assert result.message_type == MessageType.ERROR, result.body[0] assert result.error_name == ErrorType.PROPERTY_READ_ONLY.value, result.body[ 0] result = await call_properties( 'Set', 'ssv', [interface.name, 'disabled_prop', Variant('s', 'asdf')]) assert result.message_type == MessageType.ERROR, result.body[0] assert result.error_name == ErrorType.UNKNOWN_PROPERTY.value result = await call_properties( 'Set', 'ssv', [interface.name, 'not_a_prop', Variant('s', 'asdf')]) assert result.message_type == MessageType.ERROR, result.body[0] assert result.error_name == ErrorType.UNKNOWN_PROPERTY.value # wrong type result = await call_properties( 'Set', 'ssv', [interface.name, 'string_prop', Variant('t', 100)]) assert result.message_type == MessageType.ERROR assert result.error_name == ErrorType.INVALID_SIGNATURE.value # enable the erroring property so we can test it for prop in ServiceInterface._get_properties(interface): if prop.name == 'throws_error': prop.disabled = False result = await call_properties( 'Set', 'ssv', [interface.name, 'throws_error', Variant('s', 'ho')]) assert result.message_type == MessageType.ERROR, result.body[0] assert result.error_name == 'test.error' assert result.body == ['told you so'] result = await call_properties('Get', 'ss', [interface.name, 'throws_error']) assert result.message_type == MessageType.ERROR, result.body[0] assert result.error_name == 'test.error' assert result.body == ['told you so'] result = await call_properties('GetAll', 's', [interface.name]) assert result.message_type == MessageType.ERROR, result.body[0] assert result.error_name == 'test.error' assert result.body == ['told you so']
async def test_tcp_connection_with_forwarding(): async with anyio.create_task_group() as tg: closables = [] host = '127.0.0.1' port = '55556' addr_info = parse_address(os.environ.get('DBUS_SESSION_BUS_ADDRESS')) assert addr_info assert 'abstract' in addr_info[0][1] path = f'\0{addr_info[0][1]["abstract"]}' async def handle_connection(tcp_sock): async with await anyio.connect_unix(path) as unix_sock: async with anyio.create_task_group() as tg: async def handle_read(): try: while True: data = await tcp_sock.receive() await unix_sock.send(data) except anyio.ClosedResourceError: return async def handle_write(): try: while True: data = await unix_sock.receive() await tcp_sock.send(data) except anyio.ClosedResourceError: return tg.spawn(handle_read) tg.spawn(handle_write) listener = await anyio.create_tcp_listener(local_port=port, local_host=host) tg.spawn(listener.serve, handle_connection) await anyio.sleep(0.1) try: async with MessageBus(bus_address=f'tcp:host={host},port={port}' ).connect() as bus: # basic tests to see if it works result = await bus.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus.Peer', member='Ping')) assert result intr = await bus.introspect('org.freedesktop.DBus', '/org/freedesktop/DBus') obj = bus.get_proxy_object('org.freedesktop.DBus', '/org/freedesktop/DBus', intr) iface = obj.get_interface('org.freedesktop.DBus.Peer') await iface.call_ping() sock = bus._sock.extra(anyio.abc.SocketAttribute.raw_socket) \ if hasattr(bus._sock,'extra') else bus._sock assert sock.getpeername()[0] == host assert sock.getsockname()[0] == host assert sock.gettimeout() == 0 pass # A finally: tg.cancel_scope.cancel()
async def test_high_level_service_fd_passing(): async with MessageBus(negotiate_unix_fd=True).connect() as bus1, \ MessageBus(negotiate_unix_fd=True).connect() as bus2: interface_name = 'test.interface' interface = ExampleInterface(interface_name) export_path = '/test/path' async def call(member, signature='', body=[], unix_fds=[], iface=interface.name): return await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface=iface, member=member, signature=signature, body=body, unix_fds=unix_fds)) bus1.export(export_path, interface) # test that an fd can be returned by the service reply = await call('ReturnsFd') assert reply.message_type == MessageType.METHOD_RETURN, reply.body assert reply.signature == 'h' assert len(reply.unix_fds) == 1 assert_fds_equal(interface.get_last_fd(), reply.unix_fds[0]) interface.cleanup() os.close(reply.unix_fds[0]) # test that an fd can be sent to the service fd = open_file() reply = await call('AcceptsFd', signature='h', body=[0], unix_fds=[fd]) assert reply.message_type == MessageType.METHOD_RETURN, reply.body assert_fds_equal(interface.get_last_fd(), fd) interface.cleanup() os.close(fd) # signals fut = ValueEvent() def fd_listener(msg): if msg.sender == bus1.unique_name and msg.message_type == MessageType.SIGNAL: fut.set_result(msg) reply = await bus2.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', member='AddMatch', signature='s', body=[f"sender='{bus1.unique_name}'"])) assert reply.message_type == MessageType.METHOD_RETURN bus2.add_message_handler(fd_listener) interface.SignalFd() reply = await fut assert len(reply.unix_fds) == 1, (reply.unix_fds, interface.get_last_fd()) assert reply.body == [0] assert_fds_equal(reply.unix_fds[0], interface.get_last_fd()) interface.cleanup() os.close(reply.unix_fds[0]) # properties reply = await call('Get', 'ss', [interface_name, 'PropFd'], iface='org.freedesktop.DBus.Properties') assert reply.message_type == MessageType.METHOD_RETURN, reply.body assert reply.body[0].signature == 'h' assert reply.body[0].value == 0 assert len(reply.unix_fds) == 1 assert_fds_equal(interface.get_last_fd(), reply.unix_fds[0]) interface.cleanup() os.close(reply.unix_fds[0]) fd = open_file() reply = await call('Set', 'ssv', [interface_name, 'PropFd', Variant('h', 0)], iface='org.freedesktop.DBus.Properties', unix_fds=[fd]) assert reply.message_type == MessageType.METHOD_RETURN, reply.body assert_fds_equal(interface.get_last_fd(), fd) interface.cleanup() os.close(fd) reply = await call('GetAll', 's', [interface_name], iface='org.freedesktop.DBus.Properties') assert reply.message_type == MessageType.METHOD_RETURN, reply.body assert reply.body[0]['PropFd'].signature == 'h' assert reply.body[0]['PropFd'].value == 0 assert len(reply.unix_fds) == 1 assert_fds_equal(interface.get_last_fd(), reply.unix_fds[0]) interface.cleanup() os.close(reply.unix_fds[0]) for bus in [bus1, bus2]: bus.disconnect()