mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-01 23:13:40 +02:00
GDBusProxy: Correctly handle unknown members when having an expected interface
Since it is valid for a D-Bus interface / service to add new methods, signals or properties we must NEVER warn about unknown properties or drop unknown signals or disallow unknown method invocations when we have an expected interface. So this means that the expected_interface machinery is only useful for checking that the service didn't break ABI. Also update the docs so it is clear exactly what it means to have an expected interface. https://bugzilla.gnome.org/show_bug.cgi?id=660886 Signed-off-by: David Zeuthen <davidz@redhat.com>
This commit is contained in:
@@ -421,6 +421,8 @@ test_signals (GDBusProxy *proxy)
|
||||
g_string_free (s, TRUE);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
test_bogus_method_return (GDBusProxy *proxy)
|
||||
{
|
||||
@@ -439,12 +441,88 @@ test_bogus_method_return (GDBusProxy *proxy)
|
||||
g_assert (result == NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_bogus_signal (GDBusProxy *proxy)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *result;
|
||||
GDBusInterfaceInfo *old_iface_info;
|
||||
|
||||
result = g_dbus_proxy_call_sync (proxy,
|
||||
"EmitSignal2",
|
||||
NULL,
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (result != NULL);
|
||||
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
|
||||
g_variant_unref (result);
|
||||
|
||||
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
|
||||
{
|
||||
/* and now wait for the signal that will never arrive */
|
||||
_g_assert_signal_received (proxy, "g-signal");
|
||||
}
|
||||
g_test_trap_assert_stderr ("*Dropping signal TestSignal2 of type (i) since the type from the expected interface is (u)*");
|
||||
g_test_trap_assert_failed();
|
||||
|
||||
/* Our main branch will also do g_warning() when running the mainloop so
|
||||
* temporarily remove the expected interface
|
||||
*/
|
||||
old_iface_info = g_dbus_proxy_get_interface_info (proxy);
|
||||
g_dbus_proxy_set_interface_info (proxy, NULL);
|
||||
_g_assert_signal_received (proxy, "g-signal");
|
||||
g_dbus_proxy_set_interface_info (proxy, old_iface_info);
|
||||
}
|
||||
|
||||
static void
|
||||
test_bogus_property (GDBusProxy *proxy)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GVariant *result;
|
||||
GDBusInterfaceInfo *old_iface_info;
|
||||
|
||||
/* Make the service emit a PropertiesChanged signal for property 'i' of type 'i' - since
|
||||
* our introspection data has this as type 'u' we should get a warning on stderr.
|
||||
*/
|
||||
result = g_dbus_proxy_call_sync (proxy,
|
||||
"FrobSetProperty",
|
||||
g_variant_new ("(sv)",
|
||||
"i", g_variant_new_int32 (42)),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (result != NULL);
|
||||
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
|
||||
g_variant_unref (result);
|
||||
|
||||
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
|
||||
{
|
||||
/* and now wait for the signal that will never arrive */
|
||||
_g_assert_signal_received (proxy, "g-properties-changed");
|
||||
}
|
||||
g_test_trap_assert_stderr ("*Received property i with type i does not match expected type u in the expected interface*");
|
||||
g_test_trap_assert_failed();
|
||||
|
||||
/* Our main branch will also do g_warning() when running the mainloop so
|
||||
* temporarily remove the expected interface
|
||||
*/
|
||||
old_iface_info = g_dbus_proxy_get_interface_info (proxy);
|
||||
g_dbus_proxy_set_interface_info (proxy, NULL);
|
||||
_g_assert_signal_received (proxy, "g-properties-changed");
|
||||
g_dbus_proxy_set_interface_info (proxy, old_iface_info);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static const gchar *frob_dbus_interface_xml =
|
||||
"<node>"
|
||||
" <interface name='com.example.Frob'>"
|
||||
/* Deliberately different from gdbus-testserver.py's definition */
|
||||
/* PairReturn() is deliberately different from gdbus-testserver.py's definition */
|
||||
" <method name='PairReturn'>"
|
||||
" <arg type='u' name='somenumber' direction='in'/>"
|
||||
" <arg type='s' name='somestring' direction='out'/>"
|
||||
@@ -456,7 +534,14 @@ static const gchar *frob_dbus_interface_xml =
|
||||
" <method name='Sleep'>"
|
||||
" <arg type='i' name='timeout' direction='in'/>"
|
||||
" </method>"
|
||||
/* We deliberately only mention a single property here */
|
||||
" <property name='y' type='y' access='readwrite'/>"
|
||||
/* The 'i' property is deliberately different from gdbus-testserver.py's definition */
|
||||
" <property name='i' type='u' access='readwrite'/>"
|
||||
/* ::TestSignal2 is deliberately different from gdbus-testserver.py's definition */
|
||||
" <signal name='TestSignal2'>"
|
||||
" <arg type='u' name='somenumber'/>"
|
||||
" </signal>"
|
||||
" </interface>"
|
||||
"</node>";
|
||||
static GDBusInterfaceInfo *frob_dbus_interface_info;
|
||||
@@ -464,6 +549,9 @@ static GDBusInterfaceInfo *frob_dbus_interface_info;
|
||||
static void
|
||||
test_expected_interface (GDBusProxy *proxy)
|
||||
{
|
||||
GVariant *value;
|
||||
GError *error;
|
||||
|
||||
/* This is obviously wrong but expected interface is not set so we don't fail... */
|
||||
g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!"));
|
||||
g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
|
||||
@@ -473,11 +561,12 @@ test_expected_interface (GDBusProxy *proxy)
|
||||
/* Now repeat the method tests, with an expected interface set */
|
||||
g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info);
|
||||
test_methods (proxy);
|
||||
test_signals (proxy);
|
||||
|
||||
/* And now one more test where we deliberately set the expected
|
||||
* interface definition incorrectly
|
||||
*/
|
||||
/* And also where we deliberately set the expected interface definition incorrectly */
|
||||
test_bogus_method_return (proxy);
|
||||
test_bogus_signal (proxy);
|
||||
test_bogus_property (proxy);
|
||||
|
||||
/* Also check that we complain if setting a cached property of the wrong type */
|
||||
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
|
||||
@@ -487,15 +576,57 @@ test_expected_interface (GDBusProxy *proxy)
|
||||
g_test_trap_assert_stderr ("*Trying to set property y of type s but according to the expected interface the type is y*");
|
||||
g_test_trap_assert_failed();
|
||||
|
||||
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
|
||||
{
|
||||
g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something"));
|
||||
}
|
||||
g_test_trap_assert_stderr ("*Trying to lookup property does-not-exist which isn't in expected interface com.example.Frob*");
|
||||
g_test_trap_assert_failed();
|
||||
|
||||
/* this should work, however (since the type is correct) */
|
||||
g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
|
||||
|
||||
/* Try to get the value of a property where the type we expect is different from
|
||||
* what we have in our cache (e.g. what the service returned)
|
||||
*/
|
||||
if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
|
||||
{
|
||||
value = g_dbus_proxy_get_cached_property (proxy, "i");
|
||||
}
|
||||
g_test_trap_assert_stderr ("*Trying to get property i with type i but according to the expected interface the type is u*");
|
||||
g_test_trap_assert_failed();
|
||||
|
||||
/* Even if a property does not exist in expected_interface, looking it
|
||||
* up, or setting it, should never fail. Because it could be that the
|
||||
* property has been added to the service but the GDBusInterfaceInfo*
|
||||
* passed to g_dbus_proxy_set_interface_info() just haven't been updated.
|
||||
*
|
||||
* See https://bugzilla.gnome.org/show_bug.cgi?id=660886
|
||||
*/
|
||||
value = g_dbus_proxy_get_cached_property (proxy, "d");
|
||||
g_assert (value != NULL);
|
||||
g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
|
||||
g_assert_cmpfloat (g_variant_get_double (value), ==, 7.5);
|
||||
g_variant_unref (value);
|
||||
/* update it via the cached property... */
|
||||
g_dbus_proxy_set_cached_property (proxy, "d", g_variant_new_double (75.0));
|
||||
/* ... and finally check that it has changed */
|
||||
value = g_dbus_proxy_get_cached_property (proxy, "d");
|
||||
g_assert (value != NULL);
|
||||
g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
|
||||
g_assert_cmpfloat (g_variant_get_double (value), ==, 75.0);
|
||||
g_variant_unref (value);
|
||||
/* now update it via the D-Bus interface... */
|
||||
error = NULL;
|
||||
value = g_dbus_proxy_call_sync (proxy, "FrobSetProperty",
|
||||
g_variant_new ("(sv)", "d", g_variant_new_double (85.0)),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (value != NULL);
|
||||
g_assert_cmpstr (g_variant_get_type_string (value), ==, "()");
|
||||
g_variant_unref (value);
|
||||
/* ...ensure we receive the ::PropertiesChanged signal... */
|
||||
_g_assert_signal_received (proxy, "g-properties-changed");
|
||||
/* ... and finally check that it has changed */
|
||||
value = g_dbus_proxy_get_cached_property (proxy, "d");
|
||||
g_assert (value != NULL);
|
||||
g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
|
||||
g_assert_cmpfloat (g_variant_get_double (value), ==, 85.0);
|
||||
g_variant_unref (value);
|
||||
}
|
||||
|
||||
static void
|
||||
|
Reference in New Issue
Block a user