From b7d3e3f16edb2e6dc5a8ecbac837a258928987ea Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 10 Feb 2022 19:24:42 +0000 Subject: [PATCH] tests: Add tests for GDebugController Signed-off-by: Philip Withnall Fixes: #1190 --- gio/tests/debugcontroller.c | 396 ++++++++++++++++++++++++++++++++++++ gio/tests/meson.build | 1 + 2 files changed, 397 insertions(+) create mode 100644 gio/tests/debugcontroller.c diff --git a/gio/tests/debugcontroller.c b/gio/tests/debugcontroller.c new file mode 100644 index 000000000..c20acd659 --- /dev/null +++ b/gio/tests/debugcontroller.c @@ -0,0 +1,396 @@ +/* GLib testing framework examples and tests + * + * Copyright © 2022 Endless OS Foundation, LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Author: Philip Withnall + */ + +#include +#include + + +static void +test_dbus_basic (void) +{ + GTestDBus *bus; + GDBusConnection *connection = NULL, *connection2 = NULL; + GDebugControllerDBus *controller = NULL; + gboolean old_value; + gboolean debug_enabled; + GError *local_error = NULL; + + g_test_summary ("Smoketest for construction and setting of a #GDebugControllerDBus."); + + /* Set up a test session bus and connection. */ + bus = g_test_dbus_new (G_TEST_DBUS_NONE); + g_test_dbus_up (bus); + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error); + g_assert_no_error (local_error); + + /* Create a controller for this process. */ + controller = g_debug_controller_dbus_new (connection, NULL, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (controller); + g_assert_true (G_IS_DEBUG_CONTROLLER_DBUS (controller)); + + /* Try enabling and disabling debug output from within the process. */ + old_value = g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)); + + g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), TRUE); + g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller))); + + g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), FALSE); + g_assert_false (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller))); + + /* Reset the debug state and check using g_object_get(), to exercise that. */ + g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), old_value); + + g_object_get (G_OBJECT (controller), + "debug-enabled", &debug_enabled, + "connection", &connection2, + NULL); + g_assert_true (debug_enabled == old_value); + g_assert_true (connection2 == connection); + g_clear_object (&connection2); + + g_debug_controller_dbus_stop (controller); + while (g_main_context_iteration (NULL, FALSE)); + g_assert_finalize_object (controller); + g_clear_object (&connection); + + g_test_dbus_down (bus); + g_clear_object (&bus); +} + +static void +test_dbus_duplicate (void) +{ + GTestDBus *bus; + GDBusConnection *connection = NULL; + GDebugControllerDBus *controller1 = NULL, *controller2 = NULL; + GError *local_error = NULL; + + g_test_summary ("Test that creating a second #GDebugControllerDBus on the same D-Bus connection fails."); + + /* Set up a test session bus and connection. */ + bus = g_test_dbus_new (G_TEST_DBUS_NONE); + g_test_dbus_up (bus); + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error); + g_assert_no_error (local_error); + + /* Create a controller for this process. */ + controller1 = g_debug_controller_dbus_new (connection, NULL, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (controller1); + + /* And try creating a second one. */ + controller2 = g_debug_controller_dbus_new (connection, NULL, &local_error); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_assert_null (controller2); + g_clear_error (&local_error); + + g_debug_controller_dbus_stop (controller1); + while (g_main_context_iteration (NULL, FALSE)); + g_assert_finalize_object (controller1); + g_clear_object (&connection); + + g_test_dbus_down (bus); + g_clear_object (&bus); +} + +static void +async_result_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GAsyncResult **result_out = user_data; + + g_assert_null (*result_out); + *result_out = g_object_ref (result); + + g_main_context_wakeup (g_main_context_get_thread_default ()); +} + +static gboolean +authorize_false_cb (GDebugControllerDBus *debug_controller, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + return FALSE; +} + +static gboolean +authorize_true_cb (GDebugControllerDBus *debug_controller, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + return TRUE; +} + +static void +notify_debug_enabled_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + guint *notify_count_out = user_data; + + *notify_count_out = *notify_count_out + 1; +} + +static void +properties_changed_cb (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + guint *properties_changed_count_out = user_data; + + *properties_changed_count_out = *properties_changed_count_out + 1; + g_main_context_wakeup (g_main_context_get_thread_default ()); +} + +static void +test_dbus_properties (void) +{ + GTestDBus *bus; + GDBusConnection *controller_connection = NULL; + GDBusConnection *remote_connection = NULL; + GDebugControllerDBus *controller = NULL; + gboolean old_value; + GAsyncResult *result = NULL; + GVariant *reply = NULL; + GVariant *debug_enabled_variant = NULL; + gboolean debug_enabled; + GError *local_error = NULL; + gulong handler_id; + gulong notify_id; + guint notify_count = 0; + guint properties_changed_id; + guint properties_changed_count = 0; + + g_test_summary ("Test getting and setting properties on a #GDebugControllerDBus."); + + /* Set up a test session bus and connection. Set up a separate second + * connection to simulate a remote peer. */ + bus = g_test_dbus_new (G_TEST_DBUS_NONE); + g_test_dbus_up (bus); + + controller_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error); + g_assert_no_error (local_error); + + remote_connection = g_dbus_connection_new_for_address_sync (g_test_dbus_get_bus_address (bus), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, + NULL, + &local_error); + g_assert_no_error (local_error); + + /* Create a controller for this process. */ + controller = g_debug_controller_dbus_new (controller_connection, NULL, &local_error); + g_assert_no_error (local_error); + g_assert_nonnull (controller); + g_assert_true (G_IS_DEBUG_CONTROLLER_DBUS (controller)); + + old_value = g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)); + notify_id = g_signal_connect (controller, "notify::debug-enabled", G_CALLBACK (notify_debug_enabled_cb), ¬ify_count); + + properties_changed_id = g_dbus_connection_signal_subscribe (remote_connection, + g_dbus_connection_get_unique_name (controller_connection), + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + "/org/gtk/Debugging", + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + properties_changed_cb, + &properties_changed_count, + NULL); + + /* Get the debug status remotely. */ + g_dbus_connection_call (remote_connection, + g_dbus_connection_get_unique_name (controller_connection), + "/org/gtk/Debugging", + "org.freedesktop.DBus.Properties", + "Get", + g_variant_new ("(ss)", "org.gtk.Debugging", "DebugEnabled"), + G_VARIANT_TYPE ("(v)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + async_result_cb, + &result); + g_assert_no_error (local_error); + + while (result == NULL) + g_main_context_iteration (NULL, TRUE); + + reply = g_dbus_connection_call_finish (remote_connection, result, &local_error); + g_assert_no_error (local_error); + g_clear_object (&result); + + g_variant_get (reply, "(v)", &debug_enabled_variant); + debug_enabled = g_variant_get_boolean (debug_enabled_variant); + g_assert_true (debug_enabled == old_value); + g_assert_cmpuint (notify_count, ==, 0); + g_assert_cmpuint (properties_changed_count, ==, 0); + + g_clear_pointer (&debug_enabled_variant, g_variant_unref); + g_clear_pointer (&reply, g_variant_unref); + + /* Set the debug status remotely. The first attempt should fail due to no + * authorisation handler being connected. The second should fail due to the + * now-connected handler returning %FALSE. The third attempt should + * succeed. */ + g_dbus_connection_call (remote_connection, + g_dbus_connection_get_unique_name (controller_connection), + "/org/gtk/Debugging", + "org.gtk.Debugging", + "SetDebugEnabled", + g_variant_new ("(b)", !old_value), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + async_result_cb, + &result); + + while (result == NULL) + g_main_context_iteration (NULL, TRUE); + + reply = g_dbus_connection_call_finish (remote_connection, result, &local_error); + g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); + g_clear_object (&result); + g_clear_error (&local_error); + + g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value); + g_assert_cmpuint (notify_count, ==, 0); + g_assert_cmpuint (properties_changed_count, ==, 0); + + g_clear_pointer (&debug_enabled_variant, g_variant_unref); + g_clear_pointer (&reply, g_variant_unref); + + /* Attach an authorisation handler and try again. */ + handler_id = g_signal_connect (controller, "authorize", G_CALLBACK (authorize_false_cb), NULL); + + g_dbus_connection_call (remote_connection, + g_dbus_connection_get_unique_name (controller_connection), + "/org/gtk/Debugging", + "org.gtk.Debugging", + "SetDebugEnabled", + g_variant_new ("(b)", !old_value), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + async_result_cb, + &result); + + while (result == NULL) + g_main_context_iteration (NULL, TRUE); + + reply = g_dbus_connection_call_finish (remote_connection, result, &local_error); + g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); + g_clear_object (&result); + g_clear_error (&local_error); + + g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value); + g_assert_cmpuint (notify_count, ==, 0); + g_assert_cmpuint (properties_changed_count, ==, 0); + + g_clear_pointer (&debug_enabled_variant, g_variant_unref); + g_clear_pointer (&reply, g_variant_unref); + + g_signal_handler_disconnect (controller, handler_id); + handler_id = 0; + + /* Attach another signal handler which will grant access, and try again. */ + handler_id = g_signal_connect (controller, "authorize", G_CALLBACK (authorize_true_cb), NULL); + + g_dbus_connection_call (remote_connection, + g_dbus_connection_get_unique_name (controller_connection), + "/org/gtk/Debugging", + "org.gtk.Debugging", + "SetDebugEnabled", + g_variant_new ("(b)", !old_value), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + async_result_cb, + &result); + + while (result == NULL) + g_main_context_iteration (NULL, TRUE); + + reply = g_dbus_connection_call_finish (remote_connection, result, &local_error); + g_assert_no_error (local_error); + g_clear_object (&result); + + g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == !old_value); + g_assert_cmpuint (notify_count, ==, 1); + g_assert_cmpuint (properties_changed_count, ==, 1); + + g_clear_pointer (&debug_enabled_variant, g_variant_unref); + g_clear_pointer (&reply, g_variant_unref); + + g_signal_handler_disconnect (controller, handler_id); + handler_id = 0; + + /* Set the debug status locally. */ + g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), old_value); + g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value); + g_assert_cmpuint (notify_count, ==, 2); + + while (properties_changed_count != 2) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (properties_changed_count, ==, 2); + + g_signal_handler_disconnect (controller, notify_id); + notify_id = 0; + + g_dbus_connection_signal_unsubscribe (remote_connection, properties_changed_id); + properties_changed_id = 0; + + g_debug_controller_dbus_stop (controller); + while (g_main_context_iteration (NULL, FALSE)); + g_assert_finalize_object (controller); + g_clear_object (&controller_connection); + g_clear_object (&remote_connection); + + g_test_dbus_down (bus); + g_clear_object (&bus); +} + +int +main (int argc, + char *argv[]) +{ + setlocale (LC_ALL, ""); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/debug-controller/dbus/basic", test_dbus_basic); + g_test_add_func ("/debug-controller/dbus/duplicate", test_dbus_duplicate); + g_test_add_func ("/debug-controller/dbus/properties", test_dbus_properties); + + return g_test_run (); +} diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 017749c1c..43bc9d76f 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -57,6 +57,7 @@ gio_tests = { }, 'data-input-stream' : {}, 'data-output-stream' : {}, + 'debugcontroller' : {}, 'defaultvalue' : {'extra_sources' : [giotypefuncs_inc]}, 'fileattributematcher' : {}, 'filter-streams' : {},