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"})
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." )
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) )