mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-05-12 08:53:11 +02:00
Merge branch '1190-debugging-docs' into 'main'
gdebugcontroller: Add documentation and tests Closes #1190 See merge request GNOME/glib!2486
This commit is contained in:
commit
b5873878cd
@ -4215,7 +4215,6 @@ GDBusObjectManagerServerPrivate
|
||||
GDebugController
|
||||
GDebugControllerInterface
|
||||
G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME
|
||||
g_debug_controller_dup_default
|
||||
g_debug_controller_get_debug_enabled
|
||||
g_debug_controller_set_debug_enabled
|
||||
<SUBSECTION Standard>
|
||||
@ -4231,6 +4230,7 @@ G_DEBUG_CONTROLLER_GET_INTERFACE
|
||||
<TITLE>GDebugControllerDBus</TITLE>
|
||||
GDebugControllerDBus
|
||||
g_debug_controller_dbus_new
|
||||
g_debug_controller_dbus_stop
|
||||
<SUBSECTION Standard>
|
||||
g_debug_controller_dbus_get_type
|
||||
G_TYPE_DEBUG_CONTROLLER_DBUS
|
||||
|
@ -44,29 +44,16 @@
|
||||
* default. Application code may connect to the #GObject::notify signal for it
|
||||
* to control other parts of its debug infrastructure as necessary.
|
||||
*
|
||||
* If your application or service is using the default GLib log writer function,
|
||||
* creating one of the built-in implementations of #GDebugController should be
|
||||
* all that’s needed to dynamically enable or disable debug output.
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
|
||||
G_DEFINE_INTERFACE_WITH_CODE (GDebugController, g_debug_controller, G_TYPE_OBJECT,
|
||||
g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_INITABLE))
|
||||
|
||||
/**
|
||||
* g_debug_controller_dup_default:
|
||||
*
|
||||
* Gets a reference to the default #GDebugController for the system.
|
||||
*
|
||||
* Returns: (not nullable) (transfer full): a new reference to the default #GDebugController
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
GDebugController *
|
||||
g_debug_controller_dup_default (void)
|
||||
{
|
||||
return g_object_ref (_g_io_module_get_default (G_DEBUG_CONTROLLER_EXTENSION_POINT_NAME,
|
||||
"GIO_USE_DEBUG_CONTROLLER",
|
||||
NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
g_debug_controller_default_init (GDebugControllerInterface *iface)
|
||||
{
|
||||
|
@ -68,9 +68,6 @@ struct _GDebugControllerInterface {
|
||||
GTypeInterface g_iface;
|
||||
};
|
||||
|
||||
GLIB_AVAILABLE_IN_2_72
|
||||
GDebugController *g_debug_controller_dup_default (void);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_72
|
||||
gboolean g_debug_controller_get_debug_enabled (GDebugController *self);
|
||||
GLIB_AVAILABLE_IN_2_72
|
||||
|
@ -42,6 +42,105 @@
|
||||
* #GDebugControllerDBus:connection once it’s initialized. The object will be
|
||||
* unregistered when the last reference to the #GDebugControllerDBus is dropped.
|
||||
*
|
||||
* This D-Bus object can be used by remote processes to enable or disable debug
|
||||
* output in this process. Remote processes calling
|
||||
* `org.gtk.Debugging.SetDebugEnabled()` will affect the value of
|
||||
* #GDebugController:debug-enabled and, by default, g_log_get_debug_enabled().
|
||||
* default.
|
||||
*
|
||||
* By default, all processes will be able to call `SetDebugEnabled()`. If this
|
||||
* process is privileged, or might expose sensitive information in its debug
|
||||
* output, you may want to restrict the ability to enable debug output to
|
||||
* privileged users or processes.
|
||||
*
|
||||
* One option is to install a D-Bus security policy which restricts access to
|
||||
* `SetDebugEnabled()`, installing something like the following in
|
||||
* `$datadir/dbus-1/system.d/`:
|
||||
* |[<!-- language="XML" -->
|
||||
* <?xml version="1.0"?> <!--*-nxml-*-->
|
||||
* <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
* "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
* <busconfig>
|
||||
* <policy user="root">
|
||||
* <allow send_destination="com.example.MyService" send_interface="org.gtk.Debugging"/>
|
||||
* </policy>
|
||||
* <policy context="default">
|
||||
* <deny send_destination="com.example.MyService" send_interface="org.gtk.Debugging"/>
|
||||
* </policy>
|
||||
* </busconfig>
|
||||
* ]|
|
||||
*
|
||||
* This will prevent the `SetDebugEnabled()` method from being called by all
|
||||
* except root. It will not prevent the `DebugEnabled` property from being read,
|
||||
* as it’s accessed through the `org.freedesktop.DBus.Properties` interface.
|
||||
*
|
||||
* Another option is to use polkit to allow or deny requests on a case-by-case
|
||||
* basis, allowing for the possibility of dynamic authorisation. To do this,
|
||||
* connect to the #GDebugControllerDBus::authorize signal and query polkit in
|
||||
* it:
|
||||
* |[<!-- language="C" -->
|
||||
* g_autoptr(GError) child_error = NULL;
|
||||
* g_autoptr(GDBusConnection) connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
|
||||
* gulong debug_controller_authorize_id = 0;
|
||||
*
|
||||
* // Set up the debug controller.
|
||||
* debug_controller = G_DEBUG_CONTROLLER (g_debug_controller_dbus_new (priv->connection, NULL, &child_error));
|
||||
* if (debug_controller == NULL)
|
||||
* {
|
||||
* g_error ("Could not register debug controller on bus: %s"),
|
||||
* child_error->message);
|
||||
* }
|
||||
*
|
||||
* debug_controller_authorize_id = g_signal_connect (debug_controller,
|
||||
* "authorize",
|
||||
* G_CALLBACK (debug_controller_authorize_cb),
|
||||
* self);
|
||||
*
|
||||
* static gboolean
|
||||
* debug_controller_authorize_cb (GDebugControllerDBus *debug_controller,
|
||||
* GDBusMethodInvocation *invocation,
|
||||
* gpointer user_data)
|
||||
* {
|
||||
* g_autoptr(PolkitAuthority) authority = NULL;
|
||||
* g_autoptr(PolkitSubject) subject = NULL;
|
||||
* g_autoptr(PolkitAuthorizationResult) auth_result = NULL;
|
||||
* g_autoptr(GError) local_error = NULL;
|
||||
* GDBusMessage *message;
|
||||
* GDBusMessageFlags message_flags;
|
||||
* PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
|
||||
*
|
||||
* message = g_dbus_method_invocation_get_message (invocation);
|
||||
* message_flags = g_dbus_message_get_flags (message);
|
||||
*
|
||||
* authority = polkit_authority_get_sync (NULL, &local_error);
|
||||
* if (authority == NULL)
|
||||
* {
|
||||
* g_warning ("Failed to get polkit authority: %s", local_error->message);
|
||||
* return FALSE;
|
||||
* }
|
||||
*
|
||||
* if (message_flags & G_DBUS_MESSAGE_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION)
|
||||
* flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
|
||||
*
|
||||
* subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation));
|
||||
*
|
||||
* auth_result = polkit_authority_check_authorization_sync (authority,
|
||||
* subject,
|
||||
* "com.example.MyService.set-debug-enabled",
|
||||
* NULL,
|
||||
* flags,
|
||||
* NULL,
|
||||
* &local_error);
|
||||
* if (auth_result == NULL)
|
||||
* {
|
||||
* g_warning ("Failed to get check polkit authorization: %s", local_error->message);
|
||||
* return FALSE;
|
||||
* }
|
||||
*
|
||||
* return polkit_authorization_result_get_is_authorized (auth_result);
|
||||
* }
|
||||
* ]|
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
|
||||
@ -84,8 +183,10 @@ typedef struct
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GCancellable *cancellable; /* (owned) */
|
||||
GDBusConnection *connection; /* (owned) */
|
||||
guint object_id;
|
||||
guint n_pending_authorize_tasks;
|
||||
|
||||
gboolean debug_enabled;
|
||||
} GDebugControllerDBusPrivate;
|
||||
@ -103,8 +204,11 @@ G_DEFINE_TYPE_WITH_CODE (GDebugControllerDBus, g_debug_controller_dbus, G_TYPE_O
|
||||
30))
|
||||
|
||||
static void
|
||||
g_debug_controller_dbus_init (GDebugControllerDBus *dbus)
|
||||
g_debug_controller_dbus_init (GDebugControllerDBus *self)
|
||||
{
|
||||
GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
|
||||
|
||||
priv->cancellable = g_cancellable_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -113,6 +217,9 @@ set_debug_enabled (GDebugControllerDBus *self,
|
||||
{
|
||||
GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
|
||||
|
||||
if (g_cancellable_is_cancelled (priv->cancellable))
|
||||
return;
|
||||
|
||||
if (debug_enabled != priv->debug_enabled)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
@ -131,7 +238,7 @@ set_debug_enabled (GDebugControllerDBus *self,
|
||||
g_dbus_connection_emit_signal (priv->connection,
|
||||
NULL,
|
||||
"/org/gtk/Debugging",
|
||||
"org.gtk.DBus.Properties",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged",
|
||||
g_variant_new ("(sa{sv}as)",
|
||||
"org.gtk.Debugging",
|
||||
@ -189,6 +296,7 @@ authorize_cb (GObject *object,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
|
||||
GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
|
||||
GTask *task = G_TASK (result);
|
||||
GDBusMethodInvocation *invocation = g_task_get_task_data (task);
|
||||
GVariant *parameters = g_dbus_method_invocation_get_parameters (invocation);
|
||||
@ -202,14 +310,18 @@ authorize_cb (GObject *object,
|
||||
GError *local_error = g_error_new (G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
|
||||
_("Not authorized to change debug settings"));
|
||||
g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update the property value. */
|
||||
g_variant_get (parameters, "(b)", &enabled);
|
||||
set_debug_enabled (self, enabled);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation, NULL);
|
||||
}
|
||||
|
||||
/* Update the property value. */
|
||||
g_variant_get (parameters, "(b)", &enabled);
|
||||
set_debug_enabled (self, enabled);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation, NULL);
|
||||
g_assert (priv->n_pending_authorize_tasks > 0);
|
||||
priv->n_pending_authorize_tasks--;
|
||||
}
|
||||
|
||||
/* Called in the #GMainContext which was default when the #GDebugControllerDBus
|
||||
@ -225,6 +337,7 @@ dbus_method_call (GDBusConnection *connection,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDebugControllerDBus *self = user_data;
|
||||
GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
|
||||
GDebugControllerDBusClass *klass = G_DEBUG_CONTROLLER_DBUS_GET_CLASS (self);
|
||||
|
||||
/* Only on the org.gtk.Debugging interface */
|
||||
@ -232,10 +345,13 @@ dbus_method_call (GDBusConnection *connection,
|
||||
{
|
||||
GTask *task = NULL;
|
||||
|
||||
task = g_task_new (self, NULL, authorize_cb, NULL);
|
||||
task = g_task_new (self, priv->cancellable, authorize_cb, NULL);
|
||||
g_task_set_source_tag (task, dbus_method_call);
|
||||
g_task_set_task_data (task, g_object_ref (invocation), (GDestroyNotify) g_object_unref);
|
||||
|
||||
g_assert (priv->n_pending_authorize_tasks < G_MAXUINT);
|
||||
priv->n_pending_authorize_tasks++;
|
||||
|
||||
/* Check the calling peer is authorised to change the debug mode. So that
|
||||
* the signal handler can block on checking polkit authorisation (which
|
||||
* definitely involves D-Bus calls, and might involve user interaction),
|
||||
@ -349,13 +465,10 @@ g_debug_controller_dbus_dispose (GObject *object)
|
||||
GDebugControllerDBus *self = G_DEBUG_CONTROLLER_DBUS (object);
|
||||
GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
|
||||
|
||||
if (priv->object_id != 0)
|
||||
{
|
||||
g_dbus_connection_unregister_object (priv->connection, priv->object_id);
|
||||
priv->object_id = 0;
|
||||
}
|
||||
|
||||
g_debug_controller_dbus_stop (self);
|
||||
g_assert (priv->n_pending_authorize_tasks == 0);
|
||||
g_clear_object (&priv->connection);
|
||||
g_clear_object (&priv->cancellable);
|
||||
|
||||
G_OBJECT_CLASS (g_debug_controller_dbus_parent_class)->dispose (object);
|
||||
}
|
||||
@ -488,3 +601,55 @@ g_debug_controller_dbus_new (GDBusConnection *connection,
|
||||
"connection", connection,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_debug_controller_dbus_stop:
|
||||
* @self: a #GDebugControllerDBus
|
||||
*
|
||||
* Stop the debug controller, unregistering its object from the bus.
|
||||
*
|
||||
* Any pending method calls to the object will complete successfully, but new
|
||||
* ones will return an error. This method will block until all pending
|
||||
* #GDebugControllerDBus::authorize signals have been handled. This is expected
|
||||
* to not take long, as it will just be waiting for threads to join. If any
|
||||
* #GDebugControllerDBus::authorize signal handlers are still executing in other
|
||||
* threads, this will block until after they have returned.
|
||||
*
|
||||
* This method will be called automatically when the final reference to the
|
||||
* #GDebugControllerDBus is dropped. You may want to call it explicitly to know
|
||||
* when the controller has been fully removed from the bus, or to break
|
||||
* reference count cycles.
|
||||
*
|
||||
* Calling this method from within a #GDebugControllerDBus::authorize signal
|
||||
* handler will cause a deadlock and must not be done.
|
||||
*
|
||||
* Since: 2.72
|
||||
*/
|
||||
void
|
||||
g_debug_controller_dbus_stop (GDebugControllerDBus *self)
|
||||
{
|
||||
GDebugControllerDBusPrivate *priv = g_debug_controller_dbus_get_instance_private (self);
|
||||
|
||||
g_cancellable_cancel (priv->cancellable);
|
||||
|
||||
if (priv->object_id != 0)
|
||||
{
|
||||
g_dbus_connection_unregister_object (priv->connection, priv->object_id);
|
||||
priv->object_id = 0;
|
||||
}
|
||||
|
||||
/* Wait for any pending authorize tasks to finish. These will just be waiting
|
||||
* for threads to join at this point, as the D-Bus object has been
|
||||
* unregistered and the cancellable cancelled.
|
||||
*
|
||||
* FIXME: There is still a narrow race here where (we think) the worker thread
|
||||
* briefly holds a reference to the #GTask after the task thread function has
|
||||
* returned.
|
||||
*
|
||||
* The loop will also never terminate if g_debug_controller_dbus_stop() is
|
||||
* called from within an ::authorize callback.
|
||||
*
|
||||
* See discussion in https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2486 */
|
||||
while (priv->n_pending_authorize_tasks > 0)
|
||||
g_thread_yield ();
|
||||
}
|
||||
|
@ -61,6 +61,9 @@ GDebugControllerDBus *g_debug_controller_dbus_new (GDBusConnection *connection,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_72
|
||||
void g_debug_controller_dbus_stop (GDebugControllerDBus *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_DEBUG_CONTROLLER_DBUS_H__ */
|
||||
|
@ -1343,6 +1343,7 @@ _g_io_modules_ensure_loaded (void)
|
||||
#endif
|
||||
#ifdef G_OS_UNIX
|
||||
g_type_ensure (_g_unix_volume_monitor_get_type ());
|
||||
g_type_ensure (g_debug_controller_dbus_get_type ());
|
||||
g_type_ensure (g_fdo_notification_backend_get_type ());
|
||||
g_type_ensure (g_gtk_notification_backend_get_type ());
|
||||
g_type_ensure (g_portal_notification_backend_get_type ());
|
||||
|
396
gio/tests/debugcontroller.c
Normal file
396
gio/tests/debugcontroller.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
* Author: Philip Withnall <pwithnall@endlessos.org>
|
||||
*/
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <locale.h>
|
||||
|
||||
|
||||
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 ();
|
||||
}
|
@ -57,6 +57,7 @@ gio_tests = {
|
||||
},
|
||||
'data-input-stream' : {},
|
||||
'data-output-stream' : {},
|
||||
'debugcontroller' : {},
|
||||
'defaultvalue' : {'extra_sources' : [giotypefuncs_inc]},
|
||||
'fileattributematcher' : {},
|
||||
'filter-streams' : {},
|
||||
|
Loading…
x
Reference in New Issue
Block a user