mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-24 19:22:11 +01:00
Merge branch '2011-add-dbus-watch-name-tests' into 'master'
Resolve "Add additional unit tests for D-Bus name watching" Closes #2011 See merge request GNOME/glib!1904
This commit is contained in:
commit
2fd21175e7
@ -1,6 +1,7 @@
|
||||
/* GLib testing framework examples and tests
|
||||
*
|
||||
* Copyright (C) 2008-2010 Red Hat, Inc.
|
||||
* Copyright (C) 2021 Frederic Martinsons
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -16,6 +17,7 @@
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: David Zeuthen <davidz@redhat.com>
|
||||
* Author: Frederic Martinsons <frederic.martinsons@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
@ -495,6 +497,20 @@ typedef struct
|
||||
guint num_free_func;
|
||||
} WatchNameData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WatchNameData data;
|
||||
GDBusConnection *connection;
|
||||
GMutex cond_mutex;
|
||||
GCond cond;
|
||||
gboolean started;
|
||||
gboolean name_acquired;
|
||||
gboolean ended;
|
||||
gboolean unwatch_early;
|
||||
GMutex mutex;
|
||||
guint watch_id;
|
||||
} WatchNameThreadData;
|
||||
|
||||
static void
|
||||
watch_name_data_free_func (WatchNameData *data)
|
||||
{
|
||||
@ -514,7 +530,7 @@ w_name_acquired_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
WatchNameData *data = user_data;
|
||||
OwnNameData *data = user_data;
|
||||
data->num_acquired += 1;
|
||||
g_main_loop_quit (loop);
|
||||
}
|
||||
@ -524,7 +540,7 @@ w_name_lost_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
WatchNameData *data = user_data;
|
||||
OwnNameData *data = user_data;
|
||||
data->num_lost += 1;
|
||||
g_main_loop_quit (loop);
|
||||
}
|
||||
@ -613,6 +629,7 @@ stop_service (GDBusConnection *connection,
|
||||
{
|
||||
GError *error = NULL;
|
||||
GDBusProxy *proxy = NULL;
|
||||
GVariant *result = NULL;
|
||||
|
||||
data->num_vanished = 0;
|
||||
|
||||
@ -626,15 +643,17 @@ stop_service (GDBusConnection *connection,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
g_dbus_proxy_call_sync (proxy,
|
||||
"Quit",
|
||||
NULL,
|
||||
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||
100,
|
||||
NULL,
|
||||
&error);
|
||||
result = g_dbus_proxy_call_sync (proxy,
|
||||
"Quit",
|
||||
NULL,
|
||||
G_DBUS_CALL_FLAGS_NO_AUTO_START,
|
||||
100,
|
||||
NULL,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_object_unref (proxy);
|
||||
if (result)
|
||||
g_variant_unref (result);
|
||||
while (data->num_vanished == 0)
|
||||
g_main_loop_run (loop);
|
||||
}
|
||||
@ -643,6 +662,7 @@ static void
|
||||
test_bus_watch_name (gconstpointer d)
|
||||
{
|
||||
WatchNameData data;
|
||||
OwnNameData own_data;
|
||||
guint id;
|
||||
guint owner_id;
|
||||
GDBusConnection *connection;
|
||||
@ -685,15 +705,16 @@ test_bus_watch_name (gconstpointer d)
|
||||
g_assert_cmpint (data.num_appeared, ==, 0);
|
||||
g_assert_cmpint (data.num_vanished, ==, 1);
|
||||
g_assert_cmpint (data.num_free_func, ==, 1);
|
||||
data.num_free_func = 0;
|
||||
|
||||
/*
|
||||
* Now bring up a bus, own a name, and then start watching it.
|
||||
*/
|
||||
session_bus_up ();
|
||||
/* own the name */
|
||||
data.num_free_func = 0;
|
||||
data.num_acquired = 0;
|
||||
data.num_lost = 0;
|
||||
own_data.num_free_func = 0;
|
||||
own_data.num_acquired = 0;
|
||||
own_data.num_lost = 0;
|
||||
data.expect_null_connection = FALSE;
|
||||
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
|
||||
name,
|
||||
@ -701,11 +722,11 @@ test_bus_watch_name (gconstpointer d)
|
||||
w_bus_acquired_handler,
|
||||
w_name_acquired_handler,
|
||||
w_name_lost_handler,
|
||||
&data,
|
||||
(GDestroyNotify) watch_name_data_free_func);
|
||||
&own_data,
|
||||
(GDestroyNotify) own_name_data_free_func);
|
||||
g_main_loop_run (loop);
|
||||
g_assert_cmpint (data.num_acquired, ==, 1);
|
||||
g_assert_cmpint (data.num_lost, ==, 0);
|
||||
g_assert_cmpint (own_data.num_acquired, ==, 1);
|
||||
g_assert_cmpint (own_data.num_lost, ==, 0);
|
||||
|
||||
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
g_assert (connection != NULL);
|
||||
@ -750,9 +771,9 @@ test_bus_watch_name (gconstpointer d)
|
||||
/* unown the name */
|
||||
g_bus_unown_name (owner_id);
|
||||
g_main_loop_run (loop);
|
||||
g_assert_cmpint (data.num_acquired, ==, 1);
|
||||
g_assert_cmpint (data.num_free_func, ==, 2);
|
||||
|
||||
g_assert_cmpint (own_data.num_acquired, ==, 1);
|
||||
g_assert_cmpint (own_data.num_free_func, ==, 1);
|
||||
own_data.num_free_func = 0;
|
||||
/*
|
||||
* Create a watcher and then make a name be owned.
|
||||
*
|
||||
@ -802,21 +823,21 @@ test_bus_watch_name (gconstpointer d)
|
||||
if (!watch_name_test->existing_service)
|
||||
{
|
||||
/* own the name */
|
||||
data.num_acquired = 0;
|
||||
data.num_lost = 0;
|
||||
data.expect_null_connection = FALSE;
|
||||
own_data.num_acquired = 0;
|
||||
own_data.num_lost = 0;
|
||||
own_data.expect_null_connection = FALSE;
|
||||
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
|
||||
name,
|
||||
G_BUS_NAME_OWNER_FLAGS_NONE,
|
||||
w_bus_acquired_handler,
|
||||
w_name_acquired_handler,
|
||||
w_name_lost_handler,
|
||||
&data,
|
||||
(GDestroyNotify) watch_name_data_free_func);
|
||||
while (data.num_acquired == 0 || data.num_appeared == 0)
|
||||
&own_data,
|
||||
(GDestroyNotify) own_name_data_free_func);
|
||||
while (own_data.num_acquired == 0 || data.num_appeared == 0)
|
||||
g_main_loop_run (loop);
|
||||
g_assert_cmpint (data.num_acquired, ==, 1);
|
||||
g_assert_cmpint (data.num_lost, ==, 0);
|
||||
g_assert_cmpint (own_data.num_acquired, ==, 1);
|
||||
g_assert_cmpint (own_data.num_lost, ==, 0);
|
||||
g_assert_cmpint (data.num_appeared, ==, 1);
|
||||
g_assert_cmpint (data.num_vanished, ==, 1);
|
||||
}
|
||||
@ -835,12 +856,12 @@ test_bus_watch_name (gconstpointer d)
|
||||
if (!watch_name_test->existing_service)
|
||||
{
|
||||
g_main_loop_run (loop);
|
||||
g_assert_cmpint (data.num_lost, ==, 1);
|
||||
g_assert_cmpint (own_data.num_lost, ==, 1);
|
||||
g_assert_cmpint (data.num_vanished, ==, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpint (data.num_lost, ==, 0);
|
||||
g_assert_cmpint (own_data.num_lost, ==, 0);
|
||||
g_assert_cmpint (data.num_vanished, ==, 1);
|
||||
}
|
||||
g_bus_unwatch_name (id);
|
||||
@ -850,13 +871,255 @@ test_bus_watch_name (gconstpointer d)
|
||||
{
|
||||
g_bus_unown_name (owner_id);
|
||||
g_main_loop_run (loop);
|
||||
g_assert_cmpint (data.num_free_func, ==, 2);
|
||||
g_assert_cmpint (own_data.num_free_func, ==, 1);
|
||||
}
|
||||
session_bus_down ();
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
/* Called in the same thread as watcher_thread() */
|
||||
static void
|
||||
t_watch_name_data_free_func (WatchNameThreadData *thread_data)
|
||||
{
|
||||
thread_data->data.num_free_func++;
|
||||
}
|
||||
|
||||
/* Called in the same thread as watcher_thread() */
|
||||
static void
|
||||
t_name_appeared_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
const gchar *name_owner,
|
||||
gpointer user_data)
|
||||
{
|
||||
WatchNameThreadData *thread_data = user_data;
|
||||
thread_data->data.num_appeared += 1;
|
||||
}
|
||||
|
||||
/* Called in the same thread as watcher_thread() */
|
||||
static void
|
||||
t_name_vanished_handler (GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
WatchNameThreadData *thread_data = user_data;
|
||||
thread_data->data.num_vanished += 1;
|
||||
}
|
||||
|
||||
/* Called in the thread which constructed the GDBusConnection */
|
||||
static void
|
||||
connection_closed_cb (GDBusConnection *connection,
|
||||
gboolean remote_peer_vanished,
|
||||
GError *error,
|
||||
gpointer user_data)
|
||||
{
|
||||
WatchNameThreadData *thread_data = (WatchNameThreadData *) user_data;
|
||||
if (thread_data->unwatch_early)
|
||||
{
|
||||
g_mutex_lock (&thread_data->mutex);
|
||||
g_bus_unwatch_name (g_atomic_int_get (&thread_data->watch_id));
|
||||
g_atomic_int_set (&thread_data->watch_id, 0);
|
||||
g_cond_signal (&thread_data->cond);
|
||||
g_mutex_unlock (&thread_data->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
watcher_thread (gpointer user_data)
|
||||
{
|
||||
WatchNameThreadData *thread_data = user_data;
|
||||
GMainContext *thread_context;
|
||||
|
||||
thread_context = g_main_context_new ();
|
||||
g_main_context_push_thread_default (thread_context);
|
||||
|
||||
// Notify that the thread has started
|
||||
g_mutex_lock (&thread_data->cond_mutex);
|
||||
g_atomic_int_set (&thread_data->started, TRUE);
|
||||
g_cond_signal (&thread_data->cond);
|
||||
g_mutex_unlock (&thread_data->cond_mutex);
|
||||
|
||||
// Wait for the main thread to own the name before watching it
|
||||
g_mutex_lock (&thread_data->cond_mutex);
|
||||
while (!g_atomic_int_get (&thread_data->name_acquired))
|
||||
g_cond_wait (&thread_data->cond, &thread_data->cond_mutex);
|
||||
g_mutex_unlock (&thread_data->cond_mutex);
|
||||
|
||||
thread_data->data.num_appeared = 0;
|
||||
thread_data->data.num_vanished = 0;
|
||||
thread_data->data.num_free_func = 0;
|
||||
// g_signal_connect_after is important to have default handler be called before our code
|
||||
g_signal_connect_after (thread_data->connection, "closed", G_CALLBACK (connection_closed_cb), thread_data);
|
||||
|
||||
g_mutex_lock (&thread_data->mutex);
|
||||
thread_data->watch_id = g_bus_watch_name_on_connection (thread_data->connection,
|
||||
"org.gtk.GDBus.Name1",
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
t_name_appeared_handler,
|
||||
t_name_vanished_handler,
|
||||
thread_data,
|
||||
(GDestroyNotify) t_watch_name_data_free_func);
|
||||
g_mutex_unlock (&thread_data->mutex);
|
||||
|
||||
g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
|
||||
g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
|
||||
while (thread_data->data.num_appeared == 0)
|
||||
g_main_context_iteration (thread_context, TRUE);
|
||||
g_assert_cmpint (thread_data->data.num_appeared, ==, 1);
|
||||
g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
|
||||
thread_data->data.num_appeared = 0;
|
||||
|
||||
/* Close the connection and:
|
||||
* - check that we had received a vanished event even begin in different thread
|
||||
* - or check that unwatching the bus when a vanished had been scheduled
|
||||
* make it correctly unscheduled (unwatch_early condition)
|
||||
*/
|
||||
g_dbus_connection_close_sync (thread_data->connection, NULL, NULL);
|
||||
if (thread_data->unwatch_early)
|
||||
{
|
||||
// Wait for the main thread to iterate in order to have close connection handled
|
||||
g_mutex_lock (&thread_data->mutex);
|
||||
while (g_atomic_int_get (&thread_data->watch_id) != 0)
|
||||
g_cond_wait (&thread_data->cond, &thread_data->mutex);
|
||||
g_mutex_unlock (&thread_data->mutex);
|
||||
|
||||
while (thread_data->data.num_free_func == 0)
|
||||
g_main_context_iteration (thread_context, TRUE);
|
||||
g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
|
||||
g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
|
||||
g_assert_cmpint (thread_data->data.num_free_func, ==, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (thread_data->data.num_vanished == 0)
|
||||
{
|
||||
/*
|
||||
* Close of connection is treated in the context of the thread which
|
||||
* creates the connection. We must run iteration on it (to have the 'closed'
|
||||
* signal handled) and also run current thread loop to have name_vanished
|
||||
* callback handled.
|
||||
*/
|
||||
g_main_context_iteration (thread_context, TRUE);
|
||||
}
|
||||
g_assert_cmpint (thread_data->data.num_vanished, ==, 1);
|
||||
g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
|
||||
g_mutex_lock (&thread_data->mutex);
|
||||
g_bus_unwatch_name (g_atomic_int_get (&thread_data->watch_id));
|
||||
g_atomic_int_set (&thread_data->watch_id, 0);
|
||||
g_mutex_unlock (&thread_data->mutex);
|
||||
while (thread_data->data.num_free_func == 0)
|
||||
g_main_context_iteration (thread_context, TRUE);
|
||||
g_assert_cmpint (thread_data->data.num_free_func, ==, 1);
|
||||
}
|
||||
|
||||
g_mutex_lock (&thread_data->cond_mutex);
|
||||
thread_data->ended = TRUE;
|
||||
g_main_context_wakeup (NULL);
|
||||
g_cond_signal (&thread_data->cond);
|
||||
g_mutex_unlock (&thread_data->cond_mutex);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (thread_data->connection, connection_closed_cb, thread_data);
|
||||
g_object_unref (thread_data->connection);
|
||||
g_main_context_pop_thread_default (thread_context);
|
||||
g_main_context_unref (thread_context);
|
||||
|
||||
g_mutex_lock (&thread_data->mutex);
|
||||
g_assert_cmpint (thread_data->watch_id, ==, 0);
|
||||
g_mutex_unlock (&thread_data->mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
watch_with_different_context (gboolean unwatch_early)
|
||||
{
|
||||
OwnNameData own_data;
|
||||
WatchNameThreadData thread_data;
|
||||
GDBusConnection *connection;
|
||||
GThread *watcher;
|
||||
guint id;
|
||||
|
||||
session_bus_up ();
|
||||
|
||||
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
g_assert (connection != NULL);
|
||||
|
||||
g_mutex_init (&thread_data.mutex);
|
||||
g_mutex_init (&thread_data.cond_mutex);
|
||||
g_cond_init (&thread_data.cond);
|
||||
thread_data.started = FALSE;
|
||||
thread_data.name_acquired = FALSE;
|
||||
thread_data.ended = FALSE;
|
||||
thread_data.connection = g_object_ref (connection);
|
||||
thread_data.unwatch_early = unwatch_early;
|
||||
|
||||
// Create a thread which will watch a name and wait for it to be ready
|
||||
g_mutex_lock (&thread_data.cond_mutex);
|
||||
watcher = g_thread_new ("watcher", watcher_thread, &thread_data);
|
||||
while (!g_atomic_int_get (&thread_data.started))
|
||||
g_cond_wait (&thread_data.cond, &thread_data.cond_mutex);
|
||||
g_mutex_unlock (&thread_data.cond_mutex);
|
||||
|
||||
own_data.num_acquired = 0;
|
||||
own_data.num_lost = 0;
|
||||
own_data.num_free_func = 0;
|
||||
own_data.expect_null_connection = FALSE;
|
||||
// Own the name to avoid direct name vanished in watcher thread
|
||||
id = g_bus_own_name_on_connection (connection,
|
||||
"org.gtk.GDBus.Name1",
|
||||
G_BUS_NAME_OWNER_FLAGS_REPLACE,
|
||||
w_name_acquired_handler,
|
||||
w_name_lost_handler,
|
||||
&own_data,
|
||||
(GDestroyNotify) own_name_data_free_func);
|
||||
while (own_data.num_acquired == 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
g_assert_cmpint (own_data.num_acquired, ==, 1);
|
||||
g_assert_cmpint (own_data.num_lost, ==, 0);
|
||||
|
||||
// Wake the thread for it to begin watch
|
||||
g_mutex_lock (&thread_data.cond_mutex);
|
||||
g_atomic_int_set (&thread_data.name_acquired, TRUE);
|
||||
g_cond_signal (&thread_data.cond);
|
||||
g_mutex_unlock (&thread_data.cond_mutex);
|
||||
|
||||
// Iterate the loop until thread is waking us up
|
||||
while (!thread_data.ended)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
g_thread_join (watcher);
|
||||
|
||||
g_bus_unown_name (id);
|
||||
while (own_data.num_free_func == 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
g_assert_cmpint (own_data.num_free_func, ==, 1);
|
||||
|
||||
g_mutex_clear (&thread_data.mutex);
|
||||
g_mutex_clear (&thread_data.cond_mutex);
|
||||
g_cond_clear (&thread_data.cond);
|
||||
|
||||
session_bus_stop ();
|
||||
g_assert_true (g_dbus_connection_is_closed (connection));
|
||||
g_object_unref (connection);
|
||||
session_bus_down ();
|
||||
}
|
||||
|
||||
static void
|
||||
test_bus_watch_different_context (void)
|
||||
{
|
||||
watch_with_different_context (FALSE);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
test_bus_unwatch_early (void)
|
||||
{
|
||||
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/604");
|
||||
watch_with_different_context (TRUE);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
test_validate_names (void)
|
||||
{
|
||||
@ -979,6 +1242,8 @@ main (int argc,
|
||||
g_test_add_data_func ("/gdbus/bus-watch-name-closures-auto-start",
|
||||
&watch_closures_flags_auto_start,
|
||||
test_bus_watch_name);
|
||||
g_test_add_func ("/gdbus/bus-watch-different-context", test_bus_watch_different_context);
|
||||
g_test_add_func ("/gdbus/bus-unwatch-early", test_bus_unwatch_early);
|
||||
g_test_add_func ("/gdbus/escape-object-path", test_escape_object_path);
|
||||
ret = g_test_run();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user