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:
Philip Withnall 2022-02-15 12:47:50 +00:00
commit b5873878cd
8 changed files with 586 additions and 36 deletions

View File

@ -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

View File

@ -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 thats 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)
{

View File

@ -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

View File

@ -42,6 +42,105 @@
* #GDebugControllerDBus:connection once its 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 its 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 ();
}

View File

@ -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__ */

View File

@ -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
View 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), &notify_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 ();
}

View File

@ -57,6 +57,7 @@ gio_tests = {
},
'data-input-stream' : {},
'data-output-stream' : {},
'debugcontroller' : {},
'defaultvalue' : {'extra_sources' : [giotypefuncs_inc]},
'fileattributematcher' : {},
'filter-streams' : {},