#include #include #include /* Test the DBus communicaton code. */ #include "../engine/dconf-engine.h" static gboolean okay_in_main; static GThread *main_thread; static GThread *dbus_thread; static GQueue async_call_success_queue; static GQueue async_call_error_queue; static GMutex async_call_queue_lock; static gboolean signal_was_received; static void wait_for_queue_to_empty (GQueue *queue) { okay_in_main = TRUE; while (TRUE) { gboolean is_empty; g_mutex_lock (&async_call_queue_lock); is_empty = g_queue_is_empty (queue); g_mutex_unlock (&async_call_queue_lock); if (is_empty) return; g_main_context_iteration (NULL, TRUE); } okay_in_main = FALSE; } static gboolean just_wake (gpointer user_data) { return G_SOURCE_REMOVE; } static void signal_if_queue_is_empty (GQueue *queue) { gboolean is_empty; g_mutex_lock (&async_call_queue_lock); is_empty = g_queue_is_empty (queue); g_mutex_unlock (&async_call_queue_lock); if (is_empty) g_idle_add (just_wake, NULL); } const GVariantType * dconf_engine_call_handle_get_expected_type (DConfEngineCallHandle *handle) { return (GVariantType *) handle; } void dconf_engine_call_handle_reply (DConfEngineCallHandle *handle, GVariant *parameters, const GError *error) { DConfEngineCallHandle *expected_handle; /* Ensure that messages are never delivered in the main thread except * by way of a mainloop (ie: not during sync calls). * * It's okay if they are delivered in another thread at the same time * as a sync call is happening in the main thread, though... */ g_assert (g_thread_self () != main_thread || okay_in_main); /* Make sure that we only ever receive D-Bus calls from a single * thread. */ if (!dbus_thread) dbus_thread = g_thread_self (); g_assert (g_thread_self () == dbus_thread); /* This is the passing case. */ if (parameters != NULL) { g_mutex_lock (&async_call_queue_lock); g_assert (g_queue_is_empty (&async_call_error_queue)); expected_handle = g_queue_pop_head (&async_call_success_queue); g_mutex_unlock (&async_call_queue_lock); g_assert (parameters != NULL); g_assert (error == NULL); g_assert (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(s)"))); g_assert (expected_handle == handle); g_variant_type_free ((GVariantType *) handle); signal_if_queue_is_empty (&async_call_success_queue); } else { g_mutex_lock (&async_call_queue_lock); g_assert (g_queue_is_empty (&async_call_success_queue)); expected_handle = g_queue_pop_head (&async_call_error_queue); g_mutex_unlock (&async_call_queue_lock); g_assert (parameters == NULL); g_assert (error != NULL); g_assert (expected_handle == handle); g_variant_type_free ((GVariantType *) handle); signal_if_queue_is_empty (&async_call_error_queue); } } void dconf_engine_handle_dbus_signal (GBusType bus_type, const gchar *bus_name, const gchar *object_path, const gchar *signal_name, GVariant *parameters) { g_assert (g_thread_self () != main_thread || okay_in_main); if (!dbus_thread) dbus_thread = g_thread_self (); g_assert (g_thread_self () == dbus_thread); if (g_str_equal (signal_name, "TestSignal")) { GVariant *expected; expected = g_variant_parse (NULL, "('1', ['2', '3'])", NULL, NULL, NULL); g_assert (g_variant_equal (parameters, expected)); g_variant_unref (expected); signal_was_received = TRUE; g_idle_add (just_wake, NULL); } } static void test_creation_error_sync_with_error (void) { if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } /* Sync with 'error' */ if (g_test_subprocess ()) { GError *error = NULL; GVariant *reply; g_setenv ("DBUS_SESSION_BUS_ADDRESS", "some nonsense", 1); reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), G_VARIANT_TYPE ("(as)"), &error); g_assert (reply == NULL); g_assert (error != NULL); g_assert (strstr (error->message, "some nonsense")); return; } g_test_trap_subprocess (NULL, 0, 0); g_test_trap_assert_passed (); } static void test_creation_error_sync_without_error (void) { if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } /* Sync without 'error' */ if (g_test_subprocess ()) { GVariant *reply; g_setenv ("DBUS_SESSION_BUS_ADDRESS", "some nonsense", 1); reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), G_VARIANT_TYPE ("(as)"), NULL); g_assert (reply == NULL); return; } g_test_trap_subprocess (NULL, 0, 0); g_test_trap_assert_passed (); } static void test_creation_error_async (void) { if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } /* Async */ if (g_test_subprocess ()) { DConfEngineCallHandle *handle; GError *error = NULL; gboolean success; g_setenv ("DBUS_SESSION_BUS_ADDRESS", "some nonsense", 1); handle = (gpointer) g_variant_type_new ("(s)"); g_mutex_lock (&async_call_queue_lock); g_queue_push_tail (&async_call_error_queue, handle); g_mutex_unlock (&async_call_queue_lock); success = dconf_engine_dbus_call_async_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), handle, &error); /* This could either fail immediately or asynchronously, depending * on how the backend is setup. */ if (success) { g_assert_no_error (error); wait_for_queue_to_empty (&async_call_error_queue); } else g_assert (error != NULL); return; } g_test_trap_subprocess (NULL, 0, 0); g_test_trap_assert_passed (); } static void test_sync_call_success (void) { GError *error = NULL; gchar *session_id; gchar *system_id; GVariant *reply; if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames", g_variant_new ("()"), G_VARIANT_TYPE ("(as)"), &error); g_assert_no_error (error); g_assert (reply != NULL); g_assert (g_variant_is_of_type (reply, G_VARIANT_TYPE ("(as)"))); g_variant_unref (reply); reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), G_VARIANT_TYPE ("(s)"), &error); g_assert_no_error (error); g_assert (reply != NULL); g_assert (g_variant_is_of_type (reply, G_VARIANT_TYPE ("(s)"))); g_variant_get (reply, "(s)", &session_id); g_variant_unref (reply); reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SYSTEM, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), G_VARIANT_TYPE ("(s)"), &error); g_assert_no_error (error); g_assert (reply != NULL); g_assert (g_variant_is_of_type (reply, G_VARIANT_TYPE ("(s)"))); g_variant_get (reply, "(s)", &system_id); g_variant_unref (reply); /* Make sure we actually saw two separate buses */ g_assert_cmpstr (session_id, !=, system_id); g_free (session_id); g_free (system_id); } static void test_sync_call_error (void) { GError *error = NULL; GVariant *reply; if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } /* Test receiving errors from the other side */ reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("(s)", ""), G_VARIANT_TYPE_UNIT, &error); g_assert (reply == NULL); g_assert (error != NULL); g_assert (strstr (error->message, "org.freedesktop.DBus.Error.InvalidArgs")); g_clear_error (&error); /* Test with 'ay' to make sure transmitting that works as well */ reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("(ay)", NULL), G_VARIANT_TYPE_UNIT, &error); g_assert (reply == NULL); g_assert (error != NULL); g_assert (strstr (error->message, "org.freedesktop.DBus.Error.InvalidArgs")); g_clear_error (&error); /* Test reply type errors */ reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), G_VARIANT_TYPE ("(u)"), &error); g_assert (reply == NULL); g_assert (error != NULL); g_assert (strstr (error->message, " type ")); g_clear_error (&error); /* Test two oddities: * * - first, the dbus-1 backend can't handle return types other than * 's' and 'as', so we do a method call that will get something * else in order that we can check that the failure is treated * properly * * - next, we want to make sure that the filter function for * gdbus-filter doesn't block incoming method calls */ reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "RequestName", g_variant_new_parsed ("('ca.desrt.dconf.testsuite', uint32 0)"), G_VARIANT_TYPE ("(u)"), &error); if (reply != NULL) { guint s; /* It worked, so we must be on gdbus... */ g_assert_no_error (error); g_variant_get (reply, "(u)", &s); g_assert_cmpuint (s, ==, 1); g_variant_unref (reply); /* Ping ourselves... */ reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "ca.desrt.dconf.testsuite", "/", "org.freedesktop.DBus.Peer", "Ping", g_variant_new ("()"), G_VARIANT_TYPE_UNIT, &error); g_assert (reply != NULL); g_assert_no_error (error); g_variant_unref (reply); } else { /* Else, we're on dbus1... * * Check that the error was emitted correctly. */ g_assert_cmpstr (error->message, ==, "unable to handle message type '(u)'"); g_clear_error (&error); } } static void test_async_call_success (void) { gint i; if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } for (i = 0; i < 1000; i++) { DConfEngineCallHandle *handle; GError *error = NULL; gboolean success; handle = (gpointer) g_variant_type_new ("(s)"); g_mutex_lock (&async_call_queue_lock); g_queue_push_tail (&async_call_success_queue, handle); g_mutex_unlock (&async_call_queue_lock); success = dconf_engine_dbus_call_async_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), handle, &error); g_assert_no_error (error); g_assert (success); } wait_for_queue_to_empty (&async_call_success_queue); } static void test_async_call_error (void) { DConfEngineCallHandle *handle; GError *error = NULL; gboolean success; if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } handle = (gpointer) g_variant_type_new ("(s)"); g_mutex_lock (&async_call_queue_lock); g_queue_push_tail (&async_call_error_queue, handle); g_mutex_unlock (&async_call_queue_lock); success = dconf_engine_dbus_call_async_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("(s)", ""), handle, &error); g_assert_no_error (error); g_assert (success); wait_for_queue_to_empty (&async_call_error_queue); } static void test_sync_during_async (void) { DConfEngineCallHandle *handle; GError *error = NULL; gboolean success; GVariant *reply; if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } handle = (gpointer) g_variant_type_new ("(s)"); g_mutex_lock (&async_call_queue_lock); g_queue_push_tail (&async_call_success_queue, handle); g_mutex_unlock (&async_call_queue_lock); success = dconf_engine_dbus_call_async_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetId", g_variant_new ("()"), handle, &error); g_assert_no_error (error); g_assert (success); reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames", g_variant_new ("()"), G_VARIANT_TYPE ("(as)"), &error); g_assert_no_error (error); g_assert (reply != NULL); g_variant_unref (reply); wait_for_queue_to_empty (&async_call_success_queue); } static gboolean did_not_receive_signal (gpointer user_data) { g_assert_not_reached (); } static void test_signal_receipt (void) { GError *error = NULL; GVariant *reply; gint status; guint id; if (g_getenv ("DISPLAY") == NULL || g_strcmp0 (g_getenv ("DISPLAY"), "") == 0) { g_test_skip ("FIXME: D-Bus tests do not work on CI at the moment"); return; } reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "AddMatch", g_variant_new ("(s)", "type='signal',interface='ca.desrt.dconf.Writer'"), G_VARIANT_TYPE_UNIT, &error); g_assert_no_error (error); g_assert (reply != NULL); g_variant_unref (reply); status = system ("gdbus emit --session " "--object-path /ca/desrt/dconf/Writer/testcase " "--signal ca.desrt.dconf.Writer.TestSignal " "\"'1'\" \"['2', '3']\""); g_assert_cmpint (status, ==, 0); id = g_timeout_add (30000, did_not_receive_signal, NULL); while (!signal_was_received) g_main_context_iteration (NULL, FALSE); g_source_remove (id); } int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); main_thread = g_thread_self (); dconf_engine_dbus_init_for_testing (); /* test_creation_error absolutely must come first */ if (!g_str_equal (DBUS_BACKEND, "/libdbus-1")) { g_test_add_func (DBUS_BACKEND "/creation/error/sync-with-error", test_creation_error_sync_with_error); g_test_add_func (DBUS_BACKEND "/creation/error/sync-without-error", test_creation_error_sync_without_error); g_test_add_func (DBUS_BACKEND "/creation/error/async", test_creation_error_async); } g_test_add_func (DBUS_BACKEND "/sync-call/success", test_sync_call_success); g_test_add_func (DBUS_BACKEND "/sync-call/error", test_sync_call_error); g_test_add_func (DBUS_BACKEND "/async-call/success", test_async_call_success); g_test_add_func (DBUS_BACKEND "/async-call/error", test_async_call_error); g_test_add_func (DBUS_BACKEND "/sync-call/during-async", test_sync_during_async); g_test_add_func (DBUS_BACKEND "/signal/receipt", test_signal_receipt); return g_test_run (); }