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
def test_unmarshalling_with_table(): for item in table: unmarshaller = Unmarshaller() unmarshaller.feed(bytes.fromhex(item['data'])) try: unmarshaller.unmarshall() except Exception as e: print('message failed to unmarshall:') print(json_dump(item['message'])) raise e message = Message(**item['message']) body = [] for i, type_ in enumerate(message.signature_tree.types): body.append(replace_variants(type_, message.body[i])) message.body = body for attr in [ 'body', 'signature', 'message_type', 'destination', 'path', 'interface', 'member', 'flags', 'serial' ]: assert getattr(unmarshaller.message, attr) == getattr(message, attr), f'attr doesnt match: {attr}'
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
def test_ay_buffer(): body = [bytes(10000)] msg = Message(path='/test', member='test', signature='ay', body=body) marshalled = msg._marshall() unmarshaller = Unmarshaller() unmarshaller.feed(marshalled) unmarshalled_msg = unmarshaller.unmarshall() assert unmarshalled_msg.body[0] == body[0]
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_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 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))
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
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
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
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))
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 get_name_owner(name): reply = await bus1.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='GetNameOwner', signature='s', body=[name])) assert reply.message_type == MessageType.METHOD_RETURN return reply.body[0]
def test_sending_signals_between_buses(): bus1 = MessageBus().connect_sync() bus2 = MessageBus().connect_sync() 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}']) bus1.call_sync(add_match_msg) main = GLib.MainLoop() def wait_for_message(): ret = None def message_handler(signal): nonlocal ret if signal.sender == bus2.unique_name: ret = signal bus1.remove_message_handler(message_handler) main.quit() bus1.add_message_handler(message_handler) main.run() return ret bus2.send( Message.new_signal('/org/test/path', 'org.test.interface', 'SomeSignal', 's', ['a signal'])) signal = 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_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'
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
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))
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()
def test_marshalling_with_table(): for item in table: message = Message(**item['message']) body = [] for i, type_ in enumerate(message.signature_tree.types): body.append(replace_variants(type_, message.body[i])) message.body = body buf = message._marshall() data = bytes.fromhex(item['data']) if buf != data: print('message:') print(json_dump(item['message'])) print('') print('mine:') print_buf(bytes(buf)) print('') print('theirs:') print_buf(data) assert buf == data
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']
def test_glib_big_message(): 'this tests that nonblocking reads and writes actually work for glib' bus1 = glib.MessageBus().connect_sync() bus2 = glib.MessageBus().connect_sync() interface = ExampleInterface() bus1.export('/test/path', interface) # two megabytes big_body = [bytes(1000000) * 2] result = bus2.call_sync( Message(destination=bus1.unique_name, path='/test/path', interface=interface.name, member='echo_bytes', signature='ay', body=big_body)) assert result.message_type == MessageType.METHOD_RETURN, result.body[0] assert result.body[0] == big_body[0]
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_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_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 ping(): await bus2.call( Message(destination=bus1.unique_name, interface='org.freedesktop.DBus.Peer', path='/test/path', member='Ping'))
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()