Пример #1
0
class DBusServerTestCase(unittest.TestCase):
    """Test DBus server support."""

    NO_PARAMETERS = get_variant("()", tuple())

    def setUp(self):
        self.message_bus = Mock()
        self.connection = self.message_bus.connection
        self.error_mapper = ErrorMapper()
        self.object = None
        self.object_path = "/my/path"
        self.handler = None

    def _publish_object(self, xml="<node />"):
        """Publish a mocked object."""
        self.object = Mock(__dbus_xml__=dedent(xml))

        # Raise AttributeError for default methods.
        del self.object.Get
        del self.object.Set
        del self.object.GetAll

        # Create object signals.
        self.object.Signal1 = Signal()
        self.object.Signal2 = Signal()

        # Create default object signals.
        self.object.PropertiesChanged = Signal()

        self.handler = ServerObjectHandler(self.message_bus,
                                           self.object_path,
                                           self.object,
                                           error_mapper=self.error_mapper)
        self.handler.connect_object()

    def _call_method(self,
                     interface,
                     method,
                     parameters=NO_PARAMETERS,
                     reply=None):
        invocation = Mock()
        invocation.get_sender.return_value = ":1.0"

        GLibServer._object_callback(self.connection, Mock(), self.object_path,
                                    interface, method, parameters, invocation,
                                    (self.handler._method_callback, ()))

        invocation.return_dbus_error.assert_not_called()
        invocation.return_value.assert_called_once_with(reply)

    def _call_method_with_error(self,
                                interface,
                                method,
                                parameters=NO_PARAMETERS,
                                error_name=None,
                                error_message=None):
        invocation = Mock()

        with self.assertLogs(level='WARN'):
            self.handler._method_callback(invocation, interface, method,
                                          parameters)

        invocation.return_dbus_error.assert_called_once()
        invocation.return_value.assert_not_called()

        (name, msg), kwargs = invocation.return_dbus_error.call_args

        self.assertEqual(kwargs, {})
        self.assertEqual(name, error_name, "Unexpected error name.")

        if error_message is not None:
            self.assertEqual(msg, error_message, "Unexpected error message.")

    def test_register(self):
        """Test the object registration."""
        with self.assertRaises(DBusSpecificationError) as cm:
            self._publish_object("<node />")

        self.assertEqual("No DBus interfaces for registration.",
                         str(cm.exception))

        self._publish_object("""
        <node>
            <interface name="Interface" />
        </node>
        """)
        self.message_bus.connection.register_object.assert_called()

        self.handler.disconnect_object()
        self.message_bus.connection.unregister_object.assert_called()

    def test_method(self):
        """Test the method publishing."""
        self._publish_object("""
        <node>
            <interface name="Interface">
                <method name="Method1"/>
                <method name="Method2">
                    <arg direction="in" name="x" type="i"/>
                </method>
                <method name="Method3">
                    <arg direction="out" name="return" type="i"/>
                </method>
                <method name="Method4">
                    <arg direction="in" name="x" type="ad"/>
                    <arg direction="in" name="y" type="o"/>
                    <arg direction="out" name="return" type="(ib)"/>
                </method>
                <method name="Method5">
                    <arg direction="out" name="return" type="i"/>
                    <arg direction="out" name="return" type="b"/>
                </method>
            </interface>
        </node>
        """)

        self.object.Method2.return_value = None
        self._call_method("Interface",
                          "Method2",
                          parameters=get_variant("(i)", (1, )))
        self.object.Method2.assert_called_once_with(1)

        self.object.Method1.return_value = None
        self._call_method("Interface", "Method1")
        self.object.Method1.assert_called_once_with()

        self.object.Method3.return_value = 0
        self._call_method("Interface",
                          "Method3",
                          reply=get_variant("(i)", (0, )))
        self.object.Method3.assert_called_once_with()

        self.object.Method4.return_value = (1, True)
        self._call_method("Interface",
                          "Method4",
                          parameters=get_variant("(ado)",
                                                 ([1.2, 2.3], "/my/path")),
                          reply=get_variant("((ib))", ((1, True), )))
        self.object.Method4.assert_called_once_with([1.2, 2.3], "/my/path")

        self.object.Method5.return_value = (1, True)
        self._call_method("Interface",
                          "Method5",
                          reply=get_variant("(ib)", (1, True)))
        self.object.Method5.assert_called_once_with()

        self._call_method_with_error(
            "Interface",
            "MethodInvalid",
            error_name="not.known.Error.DBusSpecificationError",
            error_message="DBus specification has no member "
            "'Interface.MethodInvalid'.")

        self.error_mapper.add_rule(
            ErrorRule(exception_type=MethodFailedException,
                      error_name="MethodFailed"))

        self.object.Method1.side_effect = MethodFailedException(
            "The method has failed.")
        self._call_method_with_error("Interface",
                                     "Method1",
                                     error_name="MethodFailed",
                                     error_message="The method has failed.")

    def test_invalid_method_result(self):
        """Test a method with an invalid result."""
        self._publish_object("""
        <node>
            <interface name="Interface">
                <method name="Method">
                    <arg direction="out" name="return" type="t"/>
                </method>
            </interface>
        </node>
        """)

        self.object.Method.return_value = -1
        self._call_method_with_error(
            "Interface", "Method", error_name="not.known.Error.OverflowError")

    def test_property(self):
        """Test the property publishing."""
        self._publish_object("""
        <node>
            <interface name="Interface">
                <property name="Property1" type="i" access="readwrite" />
                <property name="Property2" type="s" access="read" />
                <property name="Property3" type="b" access="write" />
            </interface>
        </node>
        """)

        self.object.Property1 = 0
        self._call_method("org.freedesktop.DBus.Properties",
                          "Get",
                          parameters=get_variant("(ss)",
                                                 ("Interface", "Property1")),
                          reply=get_variant("(v)", (get_variant("i", 0), )))

        self._call_method(
            "org.freedesktop.DBus.Properties",
            "Set",
            parameters=get_variant(
                "(ssv)", ("Interface", "Property1", get_variant("i", 1))),
        )
        self.assertEqual(self.object.Property1, 1)

        self.object.Property2 = "Hello"
        self._call_method("org.freedesktop.DBus.Properties",
                          "Get",
                          parameters=get_variant("(ss)",
                                                 ("Interface", "Property2")),
                          reply=get_variant("(v)",
                                            (get_variant("s", "Hello"), )))
        self._call_method_with_error(
            "org.freedesktop.DBus.Properties",
            "Set",
            parameters=get_variant(
                "(ssv)",
                ("Interface", "Property2", get_variant("s", "World"))),
            error_name="not.known.Error.AttributeError",
            error_message="The property Interface.Property2 "
            "is not writable.")
        self.assertEqual(self.object.Property2, "Hello")

        self.object.Property3 = True
        self._call_method_with_error(
            "org.freedesktop.DBus.Properties",
            "Get",
            parameters=get_variant("(ss)", ("Interface", "Property3")),
            error_name="not.known.Error.AttributeError",
            error_message="The property Interface.Property3 "
            "is not readable.")
        self._call_method(
            "org.freedesktop.DBus.Properties",
            "Set",
            parameters=get_variant(
                "(ssv)", ("Interface", "Property3", get_variant("b", False))),
        )
        self.assertEqual(self.object.Property3, False)

        self._call_method(
            "org.freedesktop.DBus.Properties",
            "GetAll",
            parameters=get_variant("(s)", ("Interface", )),
            reply=get_variant("(a{sv})",
                              ({
                                  "Property1": get_variant("i", 1),
                                  "Property2": get_variant("s", "Hello")
                              }, )))

        self.object.PropertiesChanged("Interface",
                                      {"Property1": get_variant("i", 1)},
                                      ["Property2"])
        self.message_bus.connection.emit_signal.assert_called_once_with(
            None, self.object_path, "org.freedesktop.DBus.Properties",
            "PropertiesChanged",
            get_variant("(sa{sv}as)", ("Interface", {
                "Property1": get_variant("i", 1)
            }, ["Property2"])))

    def test_signal(self):
        """Test the signal publishing."""
        self._publish_object("""
        <node>
            <interface name="Interface">
                <signal name="Signal1" />
                <signal name="Signal2">
                    <arg direction="out" name="x" type="i"/>
                    <arg direction="out" name="y" type="s"/>
                </signal>
            </interface>
        </node>
        """)
        self.object.Signal1()
        self.message_bus.connection.emit_signal.assert_called_once_with(
            None, self.object_path, "Interface", "Signal1", None)

        self.message_bus.connection.emit_signal.reset_mock()

        self.object.Signal2(1, "Test")
        self.message_bus.connection.emit_signal.assert_called_once_with(
            None, self.object_path, "Interface", "Signal2",
            get_variant("(is)", (1, "Test")))

    def test_call_info(self):
        self._publish_object("""
        <node>
            <interface name="Interface">
                <method name="Method1"/>
                <method name="Method2">
                    <arg direction="in" name="x" type="i"/>
                </method>
            </interface>
        </node>
        """)

        accepts_additional_arguments(self.object.Method1)
        self._call_method("Interface", "Method1")
        self.object.Method1.assert_called_once_with(
            call_info={"sender": ":1.0"})

        accepts_additional_arguments(self.object.Method2)
        self._call_method("Interface",
                          "Method2",
                          parameters=get_variant("(i)", (1, )))
        self.object.Method2.assert_called_once_with(
            1, call_info={"sender": ":1.0"})
Пример #2
0
class DBusClientTestCase(unittest.TestCase):
    """Test DBus clinet support."""

    NO_REPLY = get_variant("()", ())

    def setUp(self):
        self.maxDiff = None
        self.message_bus = Mock()
        self.connection = self.message_bus.connection
        self.error_mapper = ErrorMapper()
        self.service_name = "my.service"
        self.object_path = "/my/object"
        self.handler = None
        self.proxy = None

        self.variant_type_factory = VariantTypeFactory()
        self.variant_type_factory.set_up()

    def tearDown(self):
        self.variant_type_factory.tear_down()

    def test_variant_type_factory(self):
        """Test the variant type factory."""
        self.assertEqual(str(get_variant_type("s")), "s")
        self.assertEqual(repr(get_variant_type("i")), "i")

        self.assertEqual(get_variant_type("s"), get_variant_type("s"))
        self.assertEqual(get_variant_type("i"), get_variant_type("i"))

        self.assertNotEqual(get_variant_type("b"), get_variant_type("i"))
        self.assertNotEqual(get_variant_type("s"), get_variant_type("u"))

    def _create_proxy(self, xml, proxy_factory=ObjectProxy):
        """Create a proxy with a mocked message bus."""
        self.proxy = proxy_factory(
            self.message_bus,
            self.service_name,
            self.object_path,
            error_mapper=self.error_mapper
        )
        self.handler = self.proxy._handler
        self.handler._specification = DBusSpecification.from_xml(xml)

    def test_introspect(self):
        """Test the introspection."""
        self._set_reply(get_variant("(s)", (dedent("""
        <node>
            <interface name="Interface">
                <method name="Method1"/>
            </interface>
        </node>
        """), )))

        self.handler = ClientObjectHandler(
            self.message_bus,
            self.service_name,
            self.object_path
        )
        self.assertIsNotNone(self.handler.specification)
        self._check_call(
            "org.freedesktop.DBus.Introspectable",
            "Introspect",
            reply_type=get_variant_type("(s)")
        )

        self.assertIn(
            DBusSpecification.Method("Method1", "Interface", None, None),
            self.handler.specification.members
        )

    def test_method(self):
        """Test the method proxy."""
        self._create_proxy("""
        <node>
            <interface name="Interface">
                <method name="Method1"/>
                <method name="Method2">
                    <arg direction="in" name="x" type="i"/>
                </method>
                <method name="Method3">
                    <arg direction="out" name="return" type="i"/>
                </method>
                <method name="Method4">
                    <arg direction="in" name="x" type="ad"/>
                    <arg direction="in" name="y" type="o"/>
                    <arg direction="out" name="return" type="(ib)"/>
                </method>
                <method name="Method5">
                    <arg direction="out" name="return_x" type="i"/>
                    <arg direction="out" name="return_y" type="i"/>
                </method>
            </interface>
        </node>
        """)

        self.assertTrue(callable(self.proxy.Method1))
        self.assertEqual(self.proxy.Method1, self.proxy.Method1)

        self._set_reply(self.NO_REPLY)
        self.assertEqual(self.proxy.Method1(), None)
        self._check_call(
            "Interface",
            "Method1"
        )

        self._set_reply(self.NO_REPLY)
        self.assertEqual(self.proxy.Method2(1), None)
        self._check_call(
            "Interface",
            "Method2",
            parameters=get_variant("(i)", (1, ))
        )

        self._set_reply(get_variant("(i)", (0, )))
        self.assertEqual(self.proxy.Method3(), 0)
        self._check_call(
            "Interface",
            "Method3",
            reply_type=get_variant_type("(i)")
        )

        self._set_reply(get_variant("((ib))", ((1, True), )))
        self.assertEqual(self.proxy.Method4([1.2, 2.3], "/my/path"),
                         (1, True))
        self._check_call(
            "Interface",
            "Method4",
            parameters=get_variant("(ado)", ([1.2, 2.3], "/my/path")),
            reply_type=get_variant_type("((ib))")
        )

        self._set_reply(get_variant("(ii)", (1, 2)))
        self.assertEqual(self.proxy.Method5(), (1, 2))
        self._check_call(
            "Interface",
            "Method5",
            reply_type=get_variant_type("(ii)")
        )

        # Handle unregistered remote exception.
        self._set_reply(Gio.DBusError.new_for_dbus_error(
            "org.test.Unknown",
            "My message."
        ))

        with self.assertRaises(DBusError) as cm:
            self.proxy.Method1()

        self.assertTrue("My message." in str(cm.exception))

        # Handle registered remote exception.
        self.error_mapper.add_rule(ErrorRule(
            exception_type=FakeException,
            error_name="org.test.Unknown"
        ))

        self._set_reply(Gio.DBusError.new_for_dbus_error(
            "org.test.Unknown",
            "My message."
        ))

        with self.assertRaises(FakeException) as cm:
            self.proxy.Method1()

        self.assertEqual(str(cm.exception), "My message.")

        # Handle local exception.
        self._set_reply(Exception("My message."))

        with self.assertRaises(Exception) as cm:
            self.proxy.Method1()

        self.assertEqual(str(cm.exception), "My message.")

        # Test invalid method.
        with self.assertRaises(AttributeError) as cm:
            self.proxy.MethodInvalid()

        self.assertEqual(
            "DBus object has no attribute 'MethodInvalid'.",
            str(cm.exception)
        )

        # Test invalid attribute.
        with self.assertRaises(AttributeError) as cm:
            self.proxy.Method1 = lambda: 1

        self.assertEqual(
            "Can't set DBus attribute 'Method1'.",
            str(cm.exception)
        )

    def _set_reply(self, reply_value):
        """Set the reply of the DBus call."""
        self.connection.call_sync.reset_mock()

        if isinstance(reply_value, Exception):
            self.connection.call_sync.side_effect = reply_value
        else:
            self.connection.call_sync.return_value = reply_value

    def _check_call(self, interface_name, method_name, parameters=None,
                    reply_type=None):
        """Check the DBus call."""
        self.connection.call_sync.assert_called_once_with(
            self.service_name,
            self.object_path,
            interface_name,
            method_name,
            parameters,
            reply_type,
            DBUS_FLAG_NONE,
            GLibClient.DBUS_TIMEOUT_NONE,
            None
        )

        self.connection.call_sync.reset_mock()

    def test_async_method(self):
        """Test asynchronous calls of a method proxy."""
        self._create_proxy("""
        <node>
            <interface name="Interface">
                <method name="Method1"/>
                <method name="Method2">
                    <arg direction="in" name="x" type="i"/>
                    <arg direction="in" name="y" type="i"/>
                    <arg direction="out" name="return" type="i"/>
                </method>
            </interface>
        </node>
        """)
        callback = Mock()
        callback_args = ("A", "B")
        self.proxy.Method1(callback=callback, callback_args=callback_args)
        self._check_async_call(
            "Interface",
            "Method1",
            callback,
            callback_args
        )

        self._finish_async_call(self.NO_REPLY, callback, callback_args)
        callback.assert_called_once_with(None, "A", "B")

        callback = Mock()
        callback_args = ("A", "B")
        self.proxy.Method2(
            1, 2, callback=callback, callback_args=callback_args
        )
        self._check_async_call(
            "Interface",
            "Method2",
            callback,
            callback_args,
            get_variant("(ii)", (1, 2)),
            get_variant_type("(i)")
        )

        self._finish_async_call(
            get_variant("(i)", (3, )),
            callback,
            callback_args
        )
        callback.assert_called_once_with(3, "A", "B")

        self.error_mapper.add_rule(ErrorRule(
            exception_type=FakeException,
            error_name="org.test.Unknown"
        ))

        callback = Mock()
        callback_args = ("A", "B")

        error = Gio.DBusError.new_for_dbus_error(
            "org.test.Unknown",
            "My message."
        )

        with self.assertRaises(FakeException) as cm:
            self._finish_async_call(error, callback, callback_args)

        self.assertEqual(str(cm.exception), "My message.")
        callback.assert_not_called()

    def _check_async_call(self, interface_name, method_name, callback,
                          callback_args, parameters=None, reply_type=None):
        """Check the asynchronous DBus call."""
        self.connection.call.assert_called_once_with(
            self.service_name,
            self.object_path,
            interface_name,
            method_name,
            parameters,
            reply_type,
            DBUS_FLAG_NONE,
            GLibClient.DBUS_TIMEOUT_NONE,
            callback=GLibClient._async_call_finish,
            user_data=(
                self.handler._method_callback,
                (callback, callback_args)
            )
        )

        self.connection.call.reset_mock()

    def _finish_async_call(self, result, callback, callback_args):
        """Finish the asynchronous call."""
        def _call_finish(result_object):
            if isinstance(result_object, Exception):
                raise result_object

            return result_object

        def _callback(finish, *args):
            callback(finish(), *args)

        GLibClient._async_call_finish(
            source_object=Mock(call_finish=_call_finish),
            result_object=result,
            user_data=(
                self.handler._method_callback,
                (_callback, callback_args)
            )
        )

    def test_property(self):
        """Test the property proxy."""
        self._create_proxy("""
        <node>
            <interface name="Interface">
                <property name="Property1" type="i" access="readwrite" />
                <property name="Property2" type="s" access="read" />
                <property name="Property3" type="b" access="write" />
            </interface>
        </node>
        """)

        self._set_reply(self.NO_REPLY)
        self.proxy.Property1 = 10
        self._check_set_property("Property1", get_variant("i", 10))

        self._set_reply(get_variant("(v)", (get_variant("i", 20), )))
        self.assertEqual(self.proxy.Property1, 20)
        self._check_get_property("Property1")

        with self.assertRaises(AttributeError) as cm:
            self.proxy.Property2 = "World"

        self.assertEqual(str(cm.exception), "Can't set DBus property.")

        self._set_reply(get_variant("(v)", (get_variant("s", "Hello"), )))
        self.assertEqual(self.proxy.Property2, "Hello")
        self._check_get_property("Property2")

        self._set_reply(self.NO_REPLY)
        self.proxy.Property3 = False
        self._check_set_property("Property3", get_variant("b", False))

        with self.assertRaises(AttributeError) as cm:
            self.fail(self.proxy.Property3)

        self.assertEqual(str(cm.exception), "Can't read DBus property.")

        with self.assertRaises(AttributeError) as cm:
            self.proxy.PropertyInvalid = 0

        self.assertEqual(
            "DBus object has no attribute 'PropertyInvalid'.",
            str(cm.exception)
        )

        with self.assertRaises(AttributeError) as cm:
            self.fail(self.proxy.PropertyInvalid)

        self.assertEqual(
            "DBus object has no attribute 'PropertyInvalid'.",
            str(cm.exception)
        )

    def _check_set_property(self, name, value):
        """Check the DBus call that sets a property."""
        self._check_call(
            "org.freedesktop.DBus.Properties",
            "Set",
            get_variant("(ssv)", ("Interface", name, value)),
            None
        )

    def _check_get_property(self, name):
        """Check the DBus call that gets a property."""
        self._check_call(
            "org.freedesktop.DBus.Properties",
            "Get",
            get_variant("(ss)", ("Interface", name)),
            get_variant_type("(v)")
        )

    def test_signal(self):
        """Test the signal publishing."""
        self._create_proxy("""
        <node>
            <interface name="Interface">
                <signal name="Signal1" />
                <signal name="Signal2">
                    <arg direction="out" name="x" type="i"/>
                    <arg direction="out" name="y" type="s"/>
                </signal>
            </interface>
        </node>
        """)

        self.assertIsInstance(self.proxy.Signal1, Signal)
        self.assertEqual(self.proxy.Signal1, self.proxy.Signal1)

        self._check_signal("Interface", "Signal1", self.proxy.Signal1.emit)
        self._emit_signal(self.NO_REPLY, self.proxy.Signal1.emit)
        self.assertEqual(len(self.handler._subscriptions), 2)

        self._check_signal("Interface", "Signal2", self.proxy.Signal2.emit)
        self._emit_signal(get_variant("(is)", (1, "Test")),
                          self.proxy.Signal2.emit)
        self.assertEqual(len(self.handler._subscriptions), 4)

        with self.assertRaises(AttributeError) as cm:
            self.fail(self.proxy.SignalInvalid)

        self.assertEqual(
            "DBus object has no attribute 'SignalInvalid'.",
            str(cm.exception)
        )

        with self.assertRaises(AttributeError) as cm:
            self.proxy.Signal1 = self.handler._signal_factory()

        self.assertEqual(
            "Can't set DBus attribute 'Signal1'.",
            str(cm.exception)
        )

        self.proxy.Signal1.connect(Mock())
        self.proxy.Signal2.connect(Mock())

        disconnect_proxy(self.proxy)
        self.assertEqual(self.connection.signal_unsubscribe.call_count, 2)
        self.assertEqual(self.handler._subscriptions, [])
        self.assertEqual(self.proxy.Signal1._callbacks, [])
        self.assertEqual(self.proxy.Signal2._callbacks, [])

    def _check_signal(self, interface_name, signal_name, signal_callback):
        """Check the DBus signal subscription."""
        self.connection.signal_subscribe.assert_called_once_with(
            self.service_name,
            interface_name,
            signal_name,
            self.object_path,
            None,
            DBUS_FLAG_NONE,
            callback=GLibClient._signal_callback,
            user_data=(self.handler._signal_callback, (signal_callback, ))
        )
        self.connection.signal_subscribe.reset_mock()

    def _emit_signal(self, parameters, signal_callback):
        """Emit a DBus signal."""
        GLibClient._signal_callback(
            self.connection,
            None,
            self.object_path,
            None,
            None,
            parameters=parameters,
            user_data=(self.handler._signal_callback, (signal_callback,))
        )

    def test_error(self):
        """Test the error handling."""
        error = Exception("My message.")
        self.assertEqual(
            GLibClient.is_remote_error(error),
            False
        )

        error = Gio.DBusError.new_for_dbus_error(
            "org.test.Error",
            "My message."
        )
        self.assertEqual(
            GLibClient.is_remote_error(error),
            True
        )
        self.assertEqual(
            GLibClient.get_remote_error_name(error),
            "org.test.Error",
        )
        self.assertEqual(
            GLibClient.get_remote_error_message(error),
            "My message."
        )
Пример #3
0
class DBusErrorTestCase(unittest.TestCase):
    """Test the DBus error register and handler."""

    def setUp(self):
        self.error_mapper = ErrorMapper()

    def _check_type(self, error_name, expected_type):
        exception_type = self.error_mapper.get_exception_type(error_name)
        self.assertEqual(exception_type, expected_type)

    def _check_name(self, exception_type, expected_name):
        error_name = self.error_mapper.get_error_name(exception_type)
        self.assertEqual(error_name, expected_name)

    def test_decorators(self):
        """Test the error decorators."""
        dbus_error = get_error_decorator(self.error_mapper)

        @dbus_error("org.test.ErrorA")
        class DecoratedA(Exception):
            pass

        @dbus_error("ErrorB", namespace=("org", "test"))
        class DecoratedB(Exception):
            pass

        self._check_name(DecoratedA, "org.test.ErrorA")
        self._check_type("org.test.ErrorA", DecoratedA)

        self._check_name(DecoratedB, "org.test.ErrorB")
        self._check_type("org.test.ErrorB", DecoratedB)

    def test_simple_rule(self):
        """Test a simple rule."""
        self.error_mapper.add_rule(ErrorRule(
            exception_type=ExceptionA,
            error_name="org.test.ErrorA"
        ))

        self._check_name(ExceptionA, "org.test.ErrorA")
        self._check_name(ExceptionA1, "not.known.Error.ExceptionA1")
        self._check_name(ExceptionA2, "not.known.Error.ExceptionA2")

        self._check_type("org.test.ErrorA", ExceptionA)
        self._check_type("org.test.ErrorA1", DBusError)
        self._check_type("org.test.ErrorA2", DBusError)

        self._check_name(ExceptionB, "not.known.Error.ExceptionB")
        self._check_type("org.test.ErrorB", DBusError)

    def test_custom_rule(self):
        """Test a custom rule."""
        self.error_mapper.add_rule(CustomRule(
            exception_type=ExceptionA,
            error_name="org.test.ErrorA"
        ))

        self._check_name(ExceptionA, "org.test.ErrorA")
        self._check_name(ExceptionA1, "org.test.ErrorA")
        self._check_name(ExceptionA2, "org.test.ErrorA")

        self._check_type("org.test.ErrorA", ExceptionA)
        self._check_type("org.test.ErrorA1", DBusError)
        self._check_type("org.test.ErrorA2", DBusError)

        self._check_name(ExceptionB, "not.known.Error.ExceptionB")
        self._check_type("org.test.ErrorB", DBusError)

    def test_several_rules(self):
        """Test several rules."""
        self.error_mapper.add_rule(ErrorRule(
            exception_type=ExceptionA,
            error_name="org.test.ErrorA"
        ))
        self.error_mapper.add_rule(ErrorRule(
            exception_type=ExceptionB,
            error_name="org.test.ErrorB"
        ))

        self._check_name(ExceptionA, "org.test.ErrorA")
        self._check_name(ExceptionB, "org.test.ErrorB")
        self._check_name(ExceptionC, "not.known.Error.ExceptionC")

        self._check_type("org.test.ErrorA", ExceptionA)
        self._check_type("org.test.ErrorB", ExceptionB)
        self._check_type("org.test.ErrorC", DBusError)

    def test_rule_priorities(self):
        """Test the priorities of the rules."""
        self.error_mapper.add_rule(ErrorRule(
            exception_type=ExceptionA,
            error_name="org.test.ErrorA1"
        ))

        self._check_name(ExceptionA, "org.test.ErrorA1")
        self._check_type("org.test.ErrorA1", ExceptionA)
        self._check_type("org.test.ErrorA2", DBusError)

        self.error_mapper.add_rule(ErrorRule(
            exception_type=ExceptionA,
            error_name="org.test.ErrorA2"
        ))

        self._check_name(ExceptionA, "org.test.ErrorA2")
        self._check_type("org.test.ErrorA1", ExceptionA)
        self._check_type("org.test.ErrorA2", ExceptionA)

    def test_default_mapping(self):
        """Test the default error mapping."""
        self._check_name(ExceptionA, "not.known.Error.ExceptionA")
        self._check_type("org.test.ErrorB", DBusError)
        self._check_type("org.test.ErrorC", DBusError)

    def test_default_class(self):
        """Test the default class."""
        self._check_type("org.test.ErrorA", DBusError)

    def test_default_namespace(self):
        """Test the default namespace."""
        self._check_name(ExceptionA, "not.known.Error.ExceptionA")

    def test_failed_mapping(self):
        """Test the failed mapping."""
        self.error_mapper._error_rules = []

        with self.assertRaises(LookupError) as cm:
            self.error_mapper.get_error_name(ExceptionA)

        self.assertEqual(
            "No name found for 'ExceptionA'.",
            str(cm.exception)
        )

        with self.assertRaises(LookupError) as cm:
            self.error_mapper.get_exception_type("org.test.ErrorA")

        self.assertEqual(
            "No type found for 'org.test.ErrorA'.",
            str(cm.exception)
        )