Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright © 2022 Endless OS Foundation, LLC
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * SPDX-License-Identifier: LGPL-2.1-or-later
21 : : * Author: Philip Withnall <pwithnall@endlessos.org>
22 : : */
23 : :
24 : : #include <gio/gio.h>
25 : : #include <locale.h>
26 : :
27 : :
28 : : static void
29 : 1 : test_dbus_basic (void)
30 : : {
31 : : GTestDBus *bus;
32 : 1 : GDBusConnection *connection = NULL, *connection2 = NULL;
33 : 1 : GDebugControllerDBus *controller = NULL;
34 : : gboolean old_value;
35 : : gboolean debug_enabled;
36 : 1 : GError *local_error = NULL;
37 : :
38 : 1 : g_test_summary ("Smoketest for construction and setting of a #GDebugControllerDBus.");
39 : :
40 : : /* Set up a test session bus and connection. */
41 : 1 : bus = g_test_dbus_new (G_TEST_DBUS_NONE);
42 : 1 : g_test_dbus_up (bus);
43 : :
44 : 1 : connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
45 : 1 : g_assert_no_error (local_error);
46 : :
47 : : /* Create a controller for this process. */
48 : 1 : controller = g_debug_controller_dbus_new (connection, NULL, &local_error);
49 : 1 : g_assert_no_error (local_error);
50 : 1 : g_assert_nonnull (controller);
51 : 1 : g_assert_true (G_IS_DEBUG_CONTROLLER_DBUS (controller));
52 : :
53 : : /* Try enabling and disabling debug output from within the process. */
54 : 1 : old_value = g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller));
55 : :
56 : 1 : g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), TRUE);
57 : 1 : g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)));
58 : :
59 : 1 : g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), FALSE);
60 : 1 : g_assert_false (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)));
61 : :
62 : : /* Reset the debug state and check using g_object_get(), to exercise that. */
63 : 1 : g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), old_value);
64 : :
65 : 1 : g_object_get (G_OBJECT (controller),
66 : : "debug-enabled", &debug_enabled,
67 : : "connection", &connection2,
68 : : NULL);
69 : 1 : g_assert_true (debug_enabled == old_value);
70 : 1 : g_assert_true (connection2 == connection);
71 : 1 : g_clear_object (&connection2);
72 : :
73 : 1 : g_debug_controller_dbus_stop (controller);
74 [ - + ]: 1 : while (g_main_context_iteration (NULL, FALSE));
75 : 1 : g_assert_finalize_object (controller);
76 : 1 : g_clear_object (&connection);
77 : :
78 : 1 : g_test_dbus_down (bus);
79 : 1 : g_clear_object (&bus);
80 : 1 : }
81 : :
82 : : static void
83 : 1 : test_dbus_duplicate (void)
84 : : {
85 : : GTestDBus *bus;
86 : 1 : GDBusConnection *connection = NULL;
87 : 1 : GDebugControllerDBus *controller1 = NULL, *controller2 = NULL;
88 : 1 : GError *local_error = NULL;
89 : :
90 : 1 : g_test_summary ("Test that creating a second #GDebugControllerDBus on the same D-Bus connection fails.");
91 : :
92 : : /* Set up a test session bus and connection. */
93 : 1 : bus = g_test_dbus_new (G_TEST_DBUS_NONE);
94 : 1 : g_test_dbus_up (bus);
95 : :
96 : 1 : connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
97 : 1 : g_assert_no_error (local_error);
98 : :
99 : : /* Create a controller for this process. */
100 : 1 : controller1 = g_debug_controller_dbus_new (connection, NULL, &local_error);
101 : 1 : g_assert_no_error (local_error);
102 : 1 : g_assert_nonnull (controller1);
103 : :
104 : : /* And try creating a second one. */
105 : 1 : controller2 = g_debug_controller_dbus_new (connection, NULL, &local_error);
106 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS);
107 : 1 : g_assert_null (controller2);
108 : 1 : g_clear_error (&local_error);
109 : :
110 : 1 : g_debug_controller_dbus_stop (controller1);
111 [ - + ]: 1 : while (g_main_context_iteration (NULL, FALSE));
112 : 1 : g_assert_finalize_object (controller1);
113 : 1 : g_clear_object (&connection);
114 : :
115 : 1 : g_test_dbus_down (bus);
116 : 1 : g_clear_object (&bus);
117 : 1 : }
118 : :
119 : : static void
120 : 4 : async_result_cb (GObject *source_object,
121 : : GAsyncResult *result,
122 : : gpointer user_data)
123 : : {
124 : 4 : GAsyncResult **result_out = user_data;
125 : :
126 : 4 : g_assert_null (*result_out);
127 : 4 : *result_out = g_object_ref (result);
128 : :
129 : 4 : g_main_context_wakeup (g_main_context_get_thread_default ());
130 : 4 : }
131 : :
132 : : static gboolean
133 : 1 : authorize_false_cb (GDebugControllerDBus *debug_controller,
134 : : GDBusMethodInvocation *invocation,
135 : : gpointer user_data)
136 : : {
137 : 1 : return FALSE;
138 : : }
139 : :
140 : : static gboolean
141 : 1 : authorize_true_cb (GDebugControllerDBus *debug_controller,
142 : : GDBusMethodInvocation *invocation,
143 : : gpointer user_data)
144 : : {
145 : 1 : return TRUE;
146 : : }
147 : :
148 : : static void
149 : 2 : notify_debug_enabled_cb (GObject *object,
150 : : GParamSpec *pspec,
151 : : gpointer user_data)
152 : : {
153 : 2 : guint *notify_count_out = user_data;
154 : :
155 : 2 : *notify_count_out = *notify_count_out + 1;
156 : 2 : }
157 : :
158 : : static void
159 : 2 : properties_changed_cb (GDBusConnection *connection,
160 : : const gchar *sender_name,
161 : : const gchar *object_path,
162 : : const gchar *interface_name,
163 : : const gchar *signal_name,
164 : : GVariant *parameters,
165 : : gpointer user_data)
166 : : {
167 : 2 : guint *properties_changed_count_out = user_data;
168 : :
169 : 2 : *properties_changed_count_out = *properties_changed_count_out + 1;
170 : 2 : g_main_context_wakeup (g_main_context_get_thread_default ());
171 : 2 : }
172 : :
173 : : static void
174 : 1 : test_dbus_properties (void)
175 : : {
176 : : GTestDBus *bus;
177 : 1 : GDBusConnection *controller_connection = NULL;
178 : 1 : GDBusConnection *remote_connection = NULL;
179 : 1 : GDebugControllerDBus *controller = NULL;
180 : : gboolean old_value;
181 : 1 : GAsyncResult *result = NULL;
182 : 1 : GVariant *reply = NULL;
183 : 1 : GVariant *debug_enabled_variant = NULL;
184 : : gboolean debug_enabled;
185 : 1 : GError *local_error = NULL;
186 : : gulong handler_id;
187 : : gulong notify_id;
188 : 1 : guint notify_count = 0;
189 : : guint properties_changed_id;
190 : 1 : guint properties_changed_count = 0;
191 : :
192 : 1 : g_test_summary ("Test getting and setting properties on a #GDebugControllerDBus.");
193 : :
194 : : /* Set up a test session bus and connection. Set up a separate second
195 : : * connection to simulate a remote peer. */
196 : 1 : bus = g_test_dbus_new (G_TEST_DBUS_NONE);
197 : 1 : g_test_dbus_up (bus);
198 : :
199 : 1 : controller_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
200 : 1 : g_assert_no_error (local_error);
201 : :
202 : 1 : remote_connection = g_dbus_connection_new_for_address_sync (g_test_dbus_get_bus_address (bus),
203 : : G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
204 : : G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
205 : : NULL,
206 : : NULL,
207 : : &local_error);
208 : 1 : g_assert_no_error (local_error);
209 : :
210 : : /* Create a controller for this process. */
211 : 1 : controller = g_debug_controller_dbus_new (controller_connection, NULL, &local_error);
212 : 1 : g_assert_no_error (local_error);
213 : 1 : g_assert_nonnull (controller);
214 : 1 : g_assert_true (G_IS_DEBUG_CONTROLLER_DBUS (controller));
215 : :
216 : 1 : old_value = g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller));
217 : 1 : notify_id = g_signal_connect (controller, "notify::debug-enabled", G_CALLBACK (notify_debug_enabled_cb), ¬ify_count);
218 : :
219 : 1 : properties_changed_id = g_dbus_connection_signal_subscribe (remote_connection,
220 : : g_dbus_connection_get_unique_name (controller_connection),
221 : : "org.freedesktop.DBus.Properties",
222 : : "PropertiesChanged",
223 : : "/org/gtk/Debugging",
224 : : NULL,
225 : : G_DBUS_SIGNAL_FLAGS_NONE,
226 : : properties_changed_cb,
227 : : &properties_changed_count,
228 : : NULL);
229 : :
230 : : /* Get the debug status remotely. */
231 : 1 : g_dbus_connection_call (remote_connection,
232 : : g_dbus_connection_get_unique_name (controller_connection),
233 : : "/org/gtk/Debugging",
234 : : "org.freedesktop.DBus.Properties",
235 : : "Get",
236 : : g_variant_new ("(ss)", "org.gtk.Debugging", "DebugEnabled"),
237 : : G_VARIANT_TYPE ("(v)"),
238 : : G_DBUS_CALL_FLAGS_NONE,
239 : : -1,
240 : : NULL,
241 : : async_result_cb,
242 : : &result);
243 : 1 : g_assert_no_error (local_error);
244 : :
245 [ + + ]: 3 : while (result == NULL)
246 : 2 : g_main_context_iteration (NULL, TRUE);
247 : :
248 : 1 : reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
249 : 1 : g_assert_no_error (local_error);
250 : 1 : g_clear_object (&result);
251 : :
252 : 1 : g_variant_get (reply, "(v)", &debug_enabled_variant);
253 : 1 : debug_enabled = g_variant_get_boolean (debug_enabled_variant);
254 : 1 : g_assert_true (debug_enabled == old_value);
255 : 1 : g_assert_cmpuint (notify_count, ==, 0);
256 : 1 : g_assert_cmpuint (properties_changed_count, ==, 0);
257 : :
258 : 1 : g_clear_pointer (&debug_enabled_variant, g_variant_unref);
259 : 1 : g_clear_pointer (&reply, g_variant_unref);
260 : :
261 : : /* Set the debug status remotely. The first attempt should fail due to no
262 : : * authorisation handler being connected. The second should fail due to the
263 : : * now-connected handler returning %FALSE. The third attempt should
264 : : * succeed. */
265 : 1 : g_dbus_connection_call (remote_connection,
266 : : g_dbus_connection_get_unique_name (controller_connection),
267 : : "/org/gtk/Debugging",
268 : : "org.gtk.Debugging",
269 : : "SetDebugEnabled",
270 : : g_variant_new ("(b)", !old_value),
271 : : NULL,
272 : : G_DBUS_CALL_FLAGS_NONE,
273 : : -1,
274 : : NULL,
275 : : async_result_cb,
276 : : &result);
277 : :
278 [ + + ]: 5 : while (result == NULL)
279 : 4 : g_main_context_iteration (NULL, TRUE);
280 : :
281 : 1 : reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
282 : 1 : g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED);
283 : 1 : g_clear_object (&result);
284 : 1 : g_clear_error (&local_error);
285 : :
286 : 1 : g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value);
287 : 1 : g_assert_cmpuint (notify_count, ==, 0);
288 : 1 : g_assert_cmpuint (properties_changed_count, ==, 0);
289 : :
290 : 1 : g_clear_pointer (&debug_enabled_variant, g_variant_unref);
291 : 1 : g_clear_pointer (&reply, g_variant_unref);
292 : :
293 : : /* Attach an authorisation handler and try again. */
294 : 1 : handler_id = g_signal_connect (controller, "authorize", G_CALLBACK (authorize_false_cb), NULL);
295 : :
296 : 1 : g_dbus_connection_call (remote_connection,
297 : : g_dbus_connection_get_unique_name (controller_connection),
298 : : "/org/gtk/Debugging",
299 : : "org.gtk.Debugging",
300 : : "SetDebugEnabled",
301 : : g_variant_new ("(b)", !old_value),
302 : : NULL,
303 : : G_DBUS_CALL_FLAGS_NONE,
304 : : -1,
305 : : NULL,
306 : : async_result_cb,
307 : : &result);
308 : :
309 [ + + ]: 5 : while (result == NULL)
310 : 4 : g_main_context_iteration (NULL, TRUE);
311 : :
312 : 1 : reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
313 : 1 : g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED);
314 : 1 : g_clear_object (&result);
315 : 1 : g_clear_error (&local_error);
316 : :
317 : 1 : g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value);
318 : 1 : g_assert_cmpuint (notify_count, ==, 0);
319 : 1 : g_assert_cmpuint (properties_changed_count, ==, 0);
320 : :
321 : 1 : g_clear_pointer (&debug_enabled_variant, g_variant_unref);
322 : 1 : g_clear_pointer (&reply, g_variant_unref);
323 : :
324 : 1 : g_signal_handler_disconnect (controller, handler_id);
325 : 1 : handler_id = 0;
326 : :
327 : : /* Attach another signal handler which will grant access, and try again. */
328 : 1 : handler_id = g_signal_connect (controller, "authorize", G_CALLBACK (authorize_true_cb), NULL);
329 : :
330 : 1 : g_dbus_connection_call (remote_connection,
331 : : g_dbus_connection_get_unique_name (controller_connection),
332 : : "/org/gtk/Debugging",
333 : : "org.gtk.Debugging",
334 : : "SetDebugEnabled",
335 : : g_variant_new ("(b)", !old_value),
336 : : NULL,
337 : : G_DBUS_CALL_FLAGS_NONE,
338 : : -1,
339 : : NULL,
340 : : async_result_cb,
341 : : &result);
342 : :
343 [ + + ]: 7 : while (result == NULL)
344 : 6 : g_main_context_iteration (NULL, TRUE);
345 : :
346 : 1 : reply = g_dbus_connection_call_finish (remote_connection, result, &local_error);
347 : 1 : g_assert_no_error (local_error);
348 : 1 : g_clear_object (&result);
349 : :
350 : 1 : g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == !old_value);
351 : 1 : g_assert_cmpuint (notify_count, ==, 1);
352 : 1 : g_assert_cmpuint (properties_changed_count, ==, 1);
353 : :
354 : 1 : g_clear_pointer (&debug_enabled_variant, g_variant_unref);
355 : 1 : g_clear_pointer (&reply, g_variant_unref);
356 : :
357 : 1 : g_signal_handler_disconnect (controller, handler_id);
358 : 1 : handler_id = 0;
359 : :
360 : : /* Set the debug status locally. */
361 : 1 : g_debug_controller_set_debug_enabled (G_DEBUG_CONTROLLER (controller), old_value);
362 : 1 : g_assert_true (g_debug_controller_get_debug_enabled (G_DEBUG_CONTROLLER (controller)) == old_value);
363 : 1 : g_assert_cmpuint (notify_count, ==, 2);
364 : :
365 [ + + ]: 3 : while (properties_changed_count != 2)
366 : 2 : g_main_context_iteration (NULL, TRUE);
367 : :
368 : 1 : g_assert_cmpuint (properties_changed_count, ==, 2);
369 : :
370 : 1 : g_signal_handler_disconnect (controller, notify_id);
371 : 1 : notify_id = 0;
372 : :
373 : 1 : g_dbus_connection_signal_unsubscribe (remote_connection, properties_changed_id);
374 : 1 : properties_changed_id = 0;
375 : :
376 : 1 : g_debug_controller_dbus_stop (controller);
377 [ - + ]: 1 : while (g_main_context_iteration (NULL, FALSE));
378 : 1 : g_assert_finalize_object (controller);
379 : 1 : g_clear_object (&controller_connection);
380 : 1 : g_clear_object (&remote_connection);
381 : :
382 : 1 : g_test_dbus_down (bus);
383 : 1 : g_clear_object (&bus);
384 : 1 : }
385 : :
386 : : static GLogWriterOutput
387 : 0 : noop_log_writer_cb (GLogLevelFlags log_level,
388 : : const GLogField *fields,
389 : : gsize n_fields,
390 : : gpointer user_data)
391 : : {
392 : 0 : return G_LOG_WRITER_HANDLED;
393 : : }
394 : :
395 : : int
396 : 1 : main (int argc,
397 : : char *argv[])
398 : : {
399 : 1 : setlocale (LC_ALL, "");
400 : 1 : g_test_init (&argc, &argv, NULL);
401 : :
402 : : /* Ignore the log messages, as the debug controller prints one when debug is
403 : : * enabled/disabled, and if debug is enabled then that will escape to stdout. */
404 : 1 : g_log_set_writer_func (noop_log_writer_cb, NULL, NULL);
405 : :
406 : 1 : g_test_add_func ("/debug-controller/dbus/basic", test_dbus_basic);
407 : 1 : g_test_add_func ("/debug-controller/dbus/duplicate", test_dbus_duplicate);
408 : 1 : g_test_add_func ("/debug-controller/dbus/properties", test_dbus_properties);
409 : :
410 : 1 : return g_test_run ();
411 : : }
|