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]
Exemple #3
0
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"
Exemple #4
0
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()
Exemple #5
0
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 == [{}]
Exemple #8
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
Exemple #9
0
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']
        ]
Exemple #10
0
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()
Exemple #11
0
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'
Exemple #13
0
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']
Exemple #16
0
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
Exemple #17
0
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()
Exemple #18
0
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']])
Exemple #19
0
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
Exemple #20
0
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
Exemple #21
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']
Exemple #22
0
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()
Exemple #23
0
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()