From 76124ee726a108ae52cb003f11a8ef873e559021 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 21 Oct 2013 12:55:18 +0100 Subject: [PATCH 09/11] Add a test for DBusGProxy --- test/core/Makefile.am | 8 + test/core/my-object.c | 17 ++ test/core/my-object.h | 2 + test/core/proxy-bus.c | 414 ++++++++++++++++++++++++++++++++++++++++ test/core/run-test.sh | 1 + test/core/test-service-glib.xml | 4 + 6 files changed, 446 insertions(+) create mode 100644 test/core/proxy-bus.c diff --git a/test/core/Makefile.am b/test/core/Makefile.am index 04981b3..0f7a722 100644 --- a/test/core/Makefile.am +++ b/test/core/Makefile.am @@ -60,6 +60,7 @@ noinst_PROGRAMS = \ test-not-quite-gdbus \ test-private \ test-peer-on-bus \ + test-proxy-bus \ test-proxy-noc \ test-proxy-peer \ test-registrations \ @@ -84,6 +85,13 @@ test_private_SOURCES = \ my-object-marshal.c \ private.c +test_proxy_bus_SOURCES = \ + my-object-marshal.c \ + my-object.c \ + my-object.h \ + proxy-bus.c \ + $(NULL) + test_proxy_noc_SOURCES = \ proxy-noc.c diff --git a/test/core/my-object.c b/test/core/my-object.c index e31dca3..5ebf8f4 100644 --- a/test/core/my-object.c +++ b/test/core/my-object.c @@ -31,6 +31,7 @@ enum { FROBNICATE, OBJECTIFIED, + STRING_SIGNAL, SIG0, SIG1, SIG2, @@ -193,6 +194,15 @@ my_object_class_init (MyObjectClass *mobject_class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); + signals[STRING_SIGNAL] = + g_signal_new ("string-signal", + G_OBJECT_CLASS_TYPE (mobject_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals[SIG0] = g_signal_new ("sig0", G_OBJECT_CLASS_TYPE (mobject_class), @@ -882,3 +892,10 @@ my_object_emit_objectified (MyObject *obj, { g_signal_emit (obj, signals[OBJECTIFIED], 0, other); } + +void +my_object_emit_string_signal (MyObject *obj, + const gchar *string) +{ + g_signal_emit (obj, signals[STRING_SIGNAL], 0, string); +} diff --git a/test/core/my-object.h b/test/core/my-object.h index 6f76c81..2278915 100644 --- a/test/core/my-object.h +++ b/test/core/my-object.h @@ -126,5 +126,7 @@ void my_object_emit_objectified (MyObject *obj, GObject *other); /* Not a D-Bus method. */ void my_object_save_error (MyObject *obj, GQuark domain, gint code, const gchar *message); +void my_object_emit_string_signal (MyObject *obj, + const gchar *string); #endif diff --git a/test/core/proxy-bus.c b/test/core/proxy-bus.c new file mode 100644 index 0000000..149e99c --- /dev/null +++ b/test/core/proxy-bus.c @@ -0,0 +1,414 @@ +/* Regression test for a DBusGProxy on a bus, without using extra services. + * + * Copyright © 2009-2012 Collabora Ltd. + * Copyright © 2009-2011 Nokia Corporation + * + * In preparation for dbus-glib relicensing (if it ever happens), this file is + * licensed under (at your option) either the AFL v2.1, the GPL v2 or later, + * or an MIT/X11-style license: + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +#include +#include + +#include "my-object.h" +#include "test/lib/util.h" + +GMainLoop *loop = NULL; + +typedef struct { + DBusError dbus_error; + GError *error; + + DBusGConnection *bus; + DBusConnection *libdbus; + + GObject *object; + + DBusGProxy *by_well_known; + DBusGProxy *by_owner; + DBusGProxy *by_unique; + DBusGProxy *dangling_well_known; + DBusGProxy *dangling_unique; + + guint frobnicate_emissions; + guint string_signal_emissions; + guint user_data_freed; +} Fixture; + +#define OWNED_NAME "org.freedesktop.DBus.GLib.Tests.ProxyBus.Owned" +#define DANGLING_WELL_KNOWN_NAME \ + "org.freedesktop.DBus.GLib.Tests.ProxyBus.DoesNotExist" +#define DANGLING_UNIQUE_NAME ":123456789.987654321" + +static void +forbid_signal_cb (DBusGProxy *proxy, + const gchar *sender, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + g_assert_not_reached (); +} + +static void +setup (Fixture *f, + gconstpointer test_data) +{ + int request_result; + + dbus_error_init (&f->dbus_error); + + f->bus = dbus_g_bus_get_private (DBUS_BUS_SESSION, NULL, NULL); + g_assert (f->bus != NULL); + + f->libdbus = dbus_g_connection_get_connection (f->bus); + dbus_connection_ref (f->libdbus); + + f->object = g_object_new (MY_TYPE_OBJECT, NULL); + g_assert (MY_IS_OBJECT (f->object)); + dbus_g_connection_register_g_object (f->bus, "/foo", f->object); + + request_result = dbus_bus_request_name (f->libdbus, OWNED_NAME, + DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->dbus_error); + test_assert_no_error (&f->dbus_error); + g_assert_cmpuint (request_result, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); +} + +static void +setup_with_proxies (Fixture *f, + gconstpointer test_data) +{ + setup (f, test_data); + + f->by_owner = dbus_g_proxy_new_for_name_owner (f->bus, OWNED_NAME, + "/foo", MY_OBJECT_IFACE, &f->error); + g_assert_no_error (f->error); + g_assert (f->by_owner != NULL); + + f->by_well_known = dbus_g_proxy_new_for_name (f->bus, OWNED_NAME, + "/foo", MY_OBJECT_IFACE); + g_assert (f->by_well_known != NULL); + + f->by_unique = dbus_g_proxy_new_for_name (f->bus, + dbus_bus_get_unique_name (f->libdbus), "/foo", MY_OBJECT_IFACE); + g_assert (f->by_unique != NULL); + + f->dangling_well_known = dbus_g_proxy_new_for_name (f->bus, + DANGLING_WELL_KNOWN_NAME, "/foo", MY_OBJECT_IFACE); + g_assert (f->dangling_well_known != NULL); + + f->dangling_unique = dbus_g_proxy_new_for_name (f->bus, DANGLING_UNIQUE_NAME, + "/foo", MY_OBJECT_IFACE); + g_assert (f->dangling_unique != NULL); +} + +static void +test_dangling_owner (Fixture *f, + gconstpointer test_data) +{ + DBusGProxy *dangling_owner; + + dangling_owner = dbus_g_proxy_new_for_name_owner (f->bus, + DANGLING_WELL_KNOWN_NAME, "/foo", MY_OBJECT_IFACE, &f->error); + g_assert_error (f->error, DBUS_GERROR, DBUS_GERROR_NAME_HAS_NO_OWNER); + g_assert (dangling_owner == NULL); + g_clear_error (&f->error); + + dangling_owner = dbus_g_proxy_new_for_name_owner (f->bus, + DANGLING_UNIQUE_NAME, "/foo", MY_OBJECT_IFACE, &f->error); + g_assert_error (f->error, DBUS_GERROR, DBUS_GERROR_NAME_HAS_NO_OWNER); + g_assert (dangling_owner == NULL); + g_clear_error (&f->error); +} + +static void +test_immediate_free (Fixture *f, + gconstpointer test_data) +{ + /* do nothing and let them be freed */ +} + +static void +test_free_after_get_name_owner (Fixture *f, + gconstpointer test_data) +{ + /* We make one round trip to ourselves, to guarantee that by the time the + * reply arrives, we will have had all the GetNameOwner results. */ + test_run_until_round_trip (f->bus, NULL); +} + +static void +test_free_after_owner_appears (Fixture *f, + gconstpointer test_data) +{ + int request_result; + + if (test_data == NULL || strcmp (test_data, "out-of-order") != 0) + { + /* Make one round trip to ourselves, to guarantee that by the time the + * reply arrives, we will have had all the GetNameOwner results. */ + test_run_until_round_trip (f->bus, NULL); + } + + /* Now take the well-known name that we didn't previously have. */ + request_result = dbus_bus_request_name (f->libdbus, DANGLING_WELL_KNOWN_NAME, + DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->dbus_error); + test_assert_no_error (&f->dbus_error); + g_assert_cmpuint (request_result, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); + + /* We make one round trip to ourselves, to guarantee that by the time the + * reply arrives, we will have had the NameOwnerChanged signal. */ + test_run_until_round_trip (f->bus, NULL); +} + +static void +frobnicate_cb (DBusGProxy *proxy, + const gchar *sender, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + Fixture *f = user_data; + + g_assert_cmpstr (sender, ==, dbus_bus_get_unique_name (f->libdbus)); + g_assert_cmpstr (signal_name, ==, "Frobnicate"); + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(i)")) || + g_variant_get_int32 (g_variant_get_child_value (parameters, 0)) != 42) + { + /* this leaks the string but we're crashing anyway */ + g_error ("unexpected Frobnicate argument: expected (int32 42), got %s", + g_variant_print (parameters, TRUE)); + } + + f->frobnicate_emissions++; +} + +static void +string_signal_cb (DBusGProxy *proxy, + const gchar *sender, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + Fixture *f = user_data; + + g_assert_cmpstr (sender, ==, dbus_bus_get_unique_name (f->libdbus)); + g_assert_cmpstr (signal_name, ==, "StringSignal"); + + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(s)")) || + g_strcmp0 (g_variant_get_string (g_variant_get_child_value ( + parameters, 0), NULL), "hello") != 0) + { + /* this leaks the string but we're crashing anyway */ + g_error ("unexpected StringSignal argument: expected ('hello'), got %s", + g_variant_print (parameters, TRUE)); + } + + f->string_signal_emissions++; +} + +static void +any_cb (DBusGProxy *proxy, + const gchar *sender, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + if (g_strcmp0 ("Frobnicate", signal_name) == 0) + frobnicate_cb (proxy, sender, signal_name, parameters, user_data); + else if (g_strcmp0 ("StringSignal", signal_name) == 0) + string_signal_cb (proxy, sender, signal_name, parameters, user_data); +} + +static void +free_user_data_cb (gpointer user_data) +{ + Fixture *f = user_data; + + f->user_data_freed++; +} + +static void +test_variant_signal (Fixture *f, + gconstpointer test_data) +{ + guint signal_id; + + /* normal signal connection, unspecified type */ + dbus_g_proxy_signal_subscribe (f->by_unique, "Frobnicate", NULL, + G_VARIANT_TYPE ("(i)"), DBUS_G_SIGNAL_FLAGS_NONE, frobnicate_cb, + f, free_user_data_cb); + + /* normal signal connection, specifying wrong type */ + dbus_g_proxy_signal_subscribe (f->by_unique, "Frobnicate", NULL, + G_VARIANT_TYPE ("(u)"), DBUS_G_SIGNAL_FLAGS_NONE, forbid_signal_cb, + f, free_user_data_cb); + + /* normal signal connection, specifying wrong name */ + dbus_g_proxy_signal_subscribe (f->by_unique, "NotARealSignal", NULL, + NULL, DBUS_G_SIGNAL_FLAGS_NONE, forbid_signal_cb, + f, free_user_data_cb); + + /* leaving signal and signature unspecified, but matching by first + * argument */ + signal_id = dbus_g_proxy_signal_subscribe (f->by_unique, NULL, "hello", + NULL, DBUS_G_SIGNAL_FLAGS_NONE, any_cb, + f, free_user_data_cb); + + /* OK, let's have some signals! */ + my_object_emit_frobnicate ((MyObject *) f->object, NULL); + test_run_until_round_trip (f->bus, NULL); + + g_assert_cmpuint (f->user_data_freed, ==, 0); + g_assert_cmpuint (f->frobnicate_emissions, ==, 1); + g_assert_cmpuint (f->string_signal_emissions, ==, 0); + + f->frobnicate_emissions = 0; + + my_object_emit_string_signal ((MyObject *) f->object, "hello"); + my_object_emit_string_signal ((MyObject *) f->object, "goodbye"); + test_run_until_round_trip (f->bus, NULL); + + g_assert_cmpuint (f->user_data_freed, ==, 0); + g_assert_cmpuint (f->frobnicate_emissions, ==, 0); + /* We matched this one, but only the "hello" version */ + g_assert_cmpuint (f->string_signal_emissions, ==, 1); + + f->string_signal_emissions = 0; + + dbus_g_proxy_signal_unsubscribe (f->by_unique, signal_id); + + my_object_emit_frobnicate ((MyObject *) f->object, NULL); + my_object_emit_string_signal ((MyObject *) f->object, "hello"); + my_object_emit_string_signal ((MyObject *) f->object, "goodbye"); + test_run_until_round_trip (f->bus, NULL); + + /* One set of user_data freed, because we disconnected the signal */ + g_assert_cmpuint (f->user_data_freed, ==, 1); + /* This one is still connected */ + g_assert_cmpuint (f->frobnicate_emissions, ==, 1); + /* This one isn't */ + g_assert_cmpuint (f->string_signal_emissions, ==, 0); + + g_object_unref (f->by_unique); + f->by_unique = NULL; + /* All user data was freed */ + g_assert_cmpuint (f->user_data_freed, ==, 4); +} + +static void +teardown (Fixture *f, + gconstpointer test_data) +{ + if (f->bus != NULL) + { + dbus_connection_close (dbus_g_connection_get_connection (f->bus)); + dbus_g_connection_unref (f->bus); + } + + if (f->libdbus != NULL) + { + dbus_connection_close (f->libdbus); + dbus_connection_unref (f->libdbus); + } + + if (f->by_well_known != NULL) + { + g_object_unref (f->by_well_known); + } + + if (f->by_owner != NULL) + { + g_object_unref (f->by_owner); + } + + if (f->by_unique != NULL) + { + g_object_unref (f->by_unique); + } + + if (f->dangling_well_known != NULL) + { + g_object_unref (f->dangling_well_known); + } + + if (f->dangling_unique != NULL) + { + g_object_unref (f->dangling_unique); + } + + if (f->object != NULL) + { + g_object_unref (f->object); + } + + /* This is safe to call on an initialized-but-unset DBusError, a bit like + * g_clear_error */ + dbus_error_free (&f->dbus_error); + g_clear_error (&f->error); +} + +int +main (int argc, char **argv) +{ + loop = g_main_loop_new (NULL, FALSE); + + g_type_init (); + g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL); + dbus_g_type_specialized_init (); + g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id="); + g_test_init (&argc, &argv, NULL); + + g_test_add ("/proxy/bus/dangling-owner", Fixture, NULL, + setup, test_dangling_owner, teardown); + + g_test_add ("/proxy/bus/free-after-get-name-owner", Fixture, NULL, + setup_with_proxies, test_free_after_get_name_owner, teardown); + g_test_add ("/proxy/bus/free-after-owner-appears", Fixture, NULL, + setup_with_proxies, test_free_after_owner_appears, teardown); + /* This version tests what happens when sync calls re-order messages. */ + g_test_add ("/proxy/bus/free-after-owner-appears/out-of-order", Fixture, + "out-of-order", setup_with_proxies, test_free_after_owner_appears, + teardown); + g_test_add ("/proxy/bus/immediate-free", Fixture, NULL, + setup_with_proxies, test_immediate_free, teardown); + + /* GDBus-style signals */ + g_test_add ("/proxy/bus/variant-signal", Fixture, NULL, + setup_with_proxies, test_variant_signal, teardown); + + return g_test_run (); +} diff --git a/test/core/run-test.sh b/test/core/run-test.sh index de89cfd..b7c55d6 100755 --- a/test/core/run-test.sh +++ b/test/core/run-test.sh @@ -53,4 +53,5 @@ else ${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/core/test-error-mapping || die "test-error-mapping failed" ${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/core/test-peer-on-bus || die "test-peer-on-bus failed" ${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/core/test-not-quite-gdbus || die "test-not-quite-gdbus failed" + ${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/core/test-proxy-bus || die "test-proxy-bus failed" fi diff --git a/test/core/test-service-glib.xml b/test/core/test-service-glib.xml index d91620c..8058fe6 100644 --- a/test/core/test-service-glib.xml +++ b/test/core/test-service-glib.xml @@ -179,6 +179,10 @@ + + + + -- 1.8.4.rc3