1 | /* GLib testing framework examples and tests |
2 | * |
3 | * Copyright (C) 2008-2010 Red Hat, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Author: David Zeuthen <davidz@redhat.com> |
19 | */ |
20 | |
21 | #include <gio/gio.h> |
22 | #include <unistd.h> |
23 | #include <string.h> |
24 | |
25 | #include "gdbus-tests.h" |
26 | |
27 | /* all tests rely on a global connection */ |
28 | static GDBusConnection *c = NULL; |
29 | |
30 | typedef struct |
31 | { |
32 | GMainContext *context; |
33 | gboolean timed_out; |
34 | } TimeoutData; |
35 | |
36 | static gboolean |
37 | timeout_cb (gpointer user_data) |
38 | { |
39 | TimeoutData *data = user_data; |
40 | |
41 | data->timed_out = TRUE; |
42 | g_main_context_wakeup (context: data->context); |
43 | |
44 | return G_SOURCE_REMOVE; |
45 | } |
46 | |
47 | /* Check that the given @connection has only one ref, waiting to let any pending |
48 | * unrefs complete first. This is typically used on the shared connection, to |
49 | * ensure it’s in a correct state before beginning the next test. */ |
50 | static void |
51 | assert_connection_has_one_ref (GDBusConnection *connection, |
52 | GMainContext *context) |
53 | { |
54 | GSource *timeout_source = NULL; |
55 | TimeoutData data = { context, FALSE }; |
56 | |
57 | if (g_atomic_int_get (&G_OBJECT (connection)->ref_count) == 1) |
58 | return; |
59 | |
60 | timeout_source = g_timeout_source_new_seconds (interval: 3); |
61 | g_source_set_callback (source: timeout_source, func: timeout_cb, data: &data, NULL); |
62 | g_source_attach (source: timeout_source, context); |
63 | |
64 | while (g_atomic_int_get (&G_OBJECT (connection)->ref_count) != 1 && !data.timed_out) |
65 | { |
66 | g_debug ("refcount of %p is not right, sleeping" , connection); |
67 | g_main_context_iteration (NULL, TRUE); |
68 | } |
69 | |
70 | g_source_destroy (source: timeout_source); |
71 | g_source_unref (source: timeout_source); |
72 | |
73 | if (g_atomic_int_get (&G_OBJECT (connection)->ref_count) != 1) |
74 | g_error ("connection %p had too many refs" , connection); |
75 | } |
76 | |
77 | /* ---------------------------------------------------------------------------------------------------- */ |
78 | /* Ensure that signal and method replies are delivered in the right thread */ |
79 | /* ---------------------------------------------------------------------------------------------------- */ |
80 | |
81 | typedef struct { |
82 | GThread *thread; |
83 | GMainContext *context; |
84 | guint signal_count; |
85 | gboolean unsubscribe_complete; |
86 | GAsyncResult *async_result; |
87 | } DeliveryData; |
88 | |
89 | static void |
90 | async_result_cb (GDBusConnection *connection, |
91 | GAsyncResult *res, |
92 | gpointer user_data) |
93 | { |
94 | DeliveryData *data = user_data; |
95 | |
96 | data->async_result = g_object_ref (res); |
97 | |
98 | g_assert_true (g_thread_self () == data->thread); |
99 | |
100 | g_main_context_wakeup (context: data->context); |
101 | } |
102 | |
103 | static void |
104 | signal_handler (GDBusConnection *connection, |
105 | const gchar *sender_name, |
106 | const gchar *object_path, |
107 | const gchar *interface_name, |
108 | const gchar *signal_name, |
109 | GVariant *parameters, |
110 | gpointer user_data) |
111 | { |
112 | DeliveryData *data = user_data; |
113 | |
114 | g_assert_true (g_thread_self () == data->thread); |
115 | |
116 | data->signal_count++; |
117 | |
118 | g_main_context_wakeup (context: data->context); |
119 | } |
120 | |
121 | static void |
122 | signal_data_free_cb (gpointer user_data) |
123 | { |
124 | DeliveryData *data = user_data; |
125 | |
126 | g_assert_true (g_thread_self () == data->thread); |
127 | |
128 | data->unsubscribe_complete = TRUE; |
129 | |
130 | g_main_context_wakeup (context: data->context); |
131 | } |
132 | |
133 | static gpointer |
134 | test_delivery_in_thread_func (gpointer _data) |
135 | { |
136 | GMainContext *thread_context; |
137 | DeliveryData data; |
138 | GCancellable *ca; |
139 | guint subscription_id; |
140 | GError *error = NULL; |
141 | GVariant *result_variant = NULL; |
142 | |
143 | thread_context = g_main_context_new (); |
144 | g_main_context_push_thread_default (context: thread_context); |
145 | |
146 | data.thread = g_thread_self (); |
147 | data.context = thread_context; |
148 | data.signal_count = 0; |
149 | data.unsubscribe_complete = FALSE; |
150 | data.async_result = NULL; |
151 | |
152 | /* ---------------------------------------------------------------------------------------------------- */ |
153 | |
154 | /* |
155 | * Check that we get a reply to the GetId() method call. |
156 | */ |
157 | g_dbus_connection_call (connection: c, |
158 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
159 | object_path: "/org/freedesktop/DBus" , /* object path */ |
160 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
161 | method_name: "GetId" , /* method name */ |
162 | NULL, NULL, |
163 | flags: G_DBUS_CALL_FLAGS_NONE, |
164 | timeout_msec: -1, |
165 | NULL, |
166 | callback: (GAsyncReadyCallback) async_result_cb, |
167 | user_data: &data); |
168 | while (data.async_result == NULL) |
169 | g_main_context_iteration (context: thread_context, TRUE); |
170 | |
171 | result_variant = g_dbus_connection_call_finish (connection: c, res: data.async_result, error: &error); |
172 | g_assert_no_error (error); |
173 | g_assert_nonnull (result_variant); |
174 | g_clear_pointer (&result_variant, g_variant_unref); |
175 | g_clear_object (&data.async_result); |
176 | |
177 | /* |
178 | * Check that we never actually send a message if the GCancellable |
179 | * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED |
180 | * when the actual connection is not up. |
181 | */ |
182 | ca = g_cancellable_new (); |
183 | g_cancellable_cancel (cancellable: ca); |
184 | g_dbus_connection_call (connection: c, |
185 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
186 | object_path: "/org/freedesktop/DBus" , /* object path */ |
187 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
188 | method_name: "GetId" , /* method name */ |
189 | NULL, NULL, |
190 | flags: G_DBUS_CALL_FLAGS_NONE, |
191 | timeout_msec: -1, |
192 | cancellable: ca, |
193 | callback: (GAsyncReadyCallback) async_result_cb, |
194 | user_data: &data); |
195 | while (data.async_result == NULL) |
196 | g_main_context_iteration (context: thread_context, TRUE); |
197 | |
198 | result_variant = g_dbus_connection_call_finish (connection: c, res: data.async_result, error: &error); |
199 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
200 | g_assert_false (g_dbus_error_is_remote_error (error)); |
201 | g_clear_error (err: &error); |
202 | g_assert_null (result_variant); |
203 | g_clear_object (&data.async_result); |
204 | |
205 | g_object_unref (object: ca); |
206 | |
207 | /* |
208 | * Check that cancellation works when the message is already in flight. |
209 | */ |
210 | ca = g_cancellable_new (); |
211 | g_dbus_connection_call (connection: c, |
212 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
213 | object_path: "/org/freedesktop/DBus" , /* object path */ |
214 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
215 | method_name: "GetId" , /* method name */ |
216 | NULL, NULL, |
217 | flags: G_DBUS_CALL_FLAGS_NONE, |
218 | timeout_msec: -1, |
219 | cancellable: ca, |
220 | callback: (GAsyncReadyCallback) async_result_cb, |
221 | user_data: &data); |
222 | g_cancellable_cancel (cancellable: ca); |
223 | |
224 | while (data.async_result == NULL) |
225 | g_main_context_iteration (context: thread_context, TRUE); |
226 | |
227 | result_variant = g_dbus_connection_call_finish (connection: c, res: data.async_result, error: &error); |
228 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
229 | g_assert_false (g_dbus_error_is_remote_error (error)); |
230 | g_clear_error (err: &error); |
231 | g_assert_null (result_variant); |
232 | g_clear_object (&data.async_result); |
233 | |
234 | g_object_unref (object: ca); |
235 | |
236 | /* |
237 | * Check that signals are delivered to the correct thread. |
238 | * |
239 | * First we subscribe to the signal, then we call EmitSignal(). This should |
240 | * cause a TestSignal emission from the testserver. |
241 | */ |
242 | subscription_id = g_dbus_connection_signal_subscribe (connection: c, |
243 | sender: "com.example.TestService" , /* sender */ |
244 | interface_name: "com.example.Frob" , /* interface */ |
245 | member: "TestSignal" , /* member */ |
246 | object_path: "/com/example/TestObject" , /* path */ |
247 | NULL, |
248 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
249 | callback: signal_handler, |
250 | user_data: &data, |
251 | user_data_free_func: signal_data_free_cb); |
252 | g_assert_cmpuint (subscription_id, !=, 0); |
253 | g_assert_cmpuint (data.signal_count, ==, 0); |
254 | |
255 | g_dbus_connection_call (connection: c, |
256 | bus_name: "com.example.TestService" , /* bus_name */ |
257 | object_path: "/com/example/TestObject" , /* object path */ |
258 | interface_name: "com.example.Frob" , /* interface name */ |
259 | method_name: "EmitSignal" , /* method name */ |
260 | parameters: g_variant_new_parsed (format: "('hello', @o '/com/example/TestObject')" ), |
261 | NULL, |
262 | flags: G_DBUS_CALL_FLAGS_NONE, |
263 | timeout_msec: -1, |
264 | NULL, |
265 | callback: (GAsyncReadyCallback) async_result_cb, |
266 | user_data: &data); |
267 | while (data.async_result == NULL || data.signal_count < 1) |
268 | g_main_context_iteration (context: thread_context, TRUE); |
269 | |
270 | result_variant = g_dbus_connection_call_finish (connection: c, res: data.async_result, error: &error); |
271 | g_assert_no_error (error); |
272 | g_assert_nonnull (result_variant); |
273 | g_clear_pointer (&result_variant, g_variant_unref); |
274 | g_clear_object (&data.async_result); |
275 | |
276 | g_assert_cmpuint (data.signal_count, ==, 1); |
277 | |
278 | g_dbus_connection_signal_unsubscribe (connection: c, subscription_id); |
279 | subscription_id = 0; |
280 | |
281 | while (!data.unsubscribe_complete) |
282 | g_main_context_iteration (context: thread_context, TRUE); |
283 | g_assert_true (data.unsubscribe_complete); |
284 | |
285 | /* ---------------------------------------------------------------------------------------------------- */ |
286 | |
287 | g_main_context_pop_thread_default (context: thread_context); |
288 | g_main_context_unref (context: thread_context); |
289 | |
290 | return NULL; |
291 | } |
292 | |
293 | static void |
294 | test_delivery_in_thread (void) |
295 | { |
296 | GThread *thread; |
297 | |
298 | thread = g_thread_new (name: "deliver" , |
299 | func: test_delivery_in_thread_func, |
300 | NULL); |
301 | |
302 | g_thread_join (thread); |
303 | |
304 | assert_connection_has_one_ref (connection: c, NULL); |
305 | } |
306 | |
307 | /* ---------------------------------------------------------------------------------------------------- */ |
308 | |
309 | typedef struct { |
310 | GDBusProxy *proxy; |
311 | gint msec; |
312 | guint num; |
313 | gboolean async; |
314 | |
315 | GMainLoop *thread_loop; |
316 | GThread *thread; |
317 | } SyncThreadData; |
318 | |
319 | static void |
320 | sleep_cb (GDBusProxy *proxy, |
321 | GAsyncResult *res, |
322 | gpointer user_data) |
323 | { |
324 | SyncThreadData *data = user_data; |
325 | GError *error; |
326 | GVariant *result; |
327 | |
328 | error = NULL; |
329 | result = g_dbus_proxy_call_finish (proxy, |
330 | res, |
331 | error: &error); |
332 | g_assert_no_error (error); |
333 | g_assert_nonnull (result); |
334 | g_assert_cmpstr (g_variant_get_type_string (result), ==, "()" ); |
335 | g_variant_unref (value: result); |
336 | |
337 | g_assert_true (data->thread == g_thread_self ()); |
338 | |
339 | g_main_loop_quit (loop: data->thread_loop); |
340 | |
341 | //g_debug ("async cb (%p)", g_thread_self ()); |
342 | } |
343 | |
344 | static gpointer |
345 | test_sleep_in_thread_func (gpointer _data) |
346 | { |
347 | SyncThreadData *data = _data; |
348 | GMainContext *thread_context; |
349 | guint n; |
350 | |
351 | thread_context = g_main_context_new (); |
352 | data->thread_loop = g_main_loop_new (context: thread_context, FALSE); |
353 | g_main_context_push_thread_default (context: thread_context); |
354 | |
355 | data->thread = g_thread_self (); |
356 | |
357 | for (n = 0; n < data->num; n++) |
358 | { |
359 | if (data->async) |
360 | { |
361 | //g_debug ("invoking async (%p)", g_thread_self ()); |
362 | g_dbus_proxy_call (proxy: data->proxy, |
363 | method_name: "Sleep" , |
364 | parameters: g_variant_new (format_string: "(i)" , data->msec), |
365 | flags: G_DBUS_CALL_FLAGS_NONE, |
366 | timeout_msec: -1, |
367 | NULL, |
368 | callback: (GAsyncReadyCallback) sleep_cb, |
369 | user_data: data); |
370 | g_main_loop_run (loop: data->thread_loop); |
371 | if (g_test_verbose ()) |
372 | g_printerr (format: "A" ); |
373 | //g_debug ("done invoking async (%p)", g_thread_self ()); |
374 | } |
375 | else |
376 | { |
377 | GError *error; |
378 | GVariant *result; |
379 | |
380 | error = NULL; |
381 | //g_debug ("invoking sync (%p)", g_thread_self ()); |
382 | result = g_dbus_proxy_call_sync (proxy: data->proxy, |
383 | method_name: "Sleep" , |
384 | parameters: g_variant_new (format_string: "(i)" , data->msec), |
385 | flags: G_DBUS_CALL_FLAGS_NONE, |
386 | timeout_msec: -1, |
387 | NULL, |
388 | error: &error); |
389 | if (g_test_verbose ()) |
390 | g_printerr (format: "S" ); |
391 | //g_debug ("done invoking sync (%p)", g_thread_self ()); |
392 | g_assert_no_error (error); |
393 | g_assert_nonnull (result); |
394 | g_assert_cmpstr (g_variant_get_type_string (result), ==, "()" ); |
395 | g_variant_unref (value: result); |
396 | } |
397 | } |
398 | |
399 | g_main_context_pop_thread_default (context: thread_context); |
400 | g_main_loop_unref (loop: data->thread_loop); |
401 | g_main_context_unref (context: thread_context); |
402 | |
403 | return NULL; |
404 | } |
405 | |
406 | static void |
407 | test_method_calls_on_proxy (GDBusProxy *proxy) |
408 | { |
409 | guint n, divisor; |
410 | |
411 | /* |
412 | * Check that multiple threads can do calls without interfering with |
413 | * each other. We do this by creating three threads that call the |
414 | * Sleep() method on the server (which handles it asynchronously, e.g. |
415 | * it won't block other requests) with different sleep durations and |
416 | * a number of times. We do this so each set of calls add up to 4000 |
417 | * milliseconds. |
418 | * |
419 | * The dbus test server that this code calls into uses glib timeouts |
420 | * to do the sleeping which have only a granularity of 1ms. It is |
421 | * therefore possible to lose as much as 40ms; the test could finish |
422 | * in slightly less than 4 seconds. |
423 | * |
424 | * We run this test twice - first with async calls in each thread, then |
425 | * again with sync calls |
426 | */ |
427 | |
428 | if (g_test_thorough ()) |
429 | divisor = 1; |
430 | else |
431 | divisor = 10; |
432 | |
433 | for (n = 0; n < 2; n++) |
434 | { |
435 | gboolean do_async; |
436 | GThread *thread1; |
437 | GThread *thread2; |
438 | GThread *thread3; |
439 | SyncThreadData data1; |
440 | SyncThreadData data2; |
441 | SyncThreadData data3; |
442 | gint64 start_time, end_time; |
443 | guint elapsed_msec; |
444 | |
445 | do_async = (n == 0); |
446 | |
447 | start_time = g_get_real_time (); |
448 | |
449 | data1.proxy = proxy; |
450 | data1.msec = 40; |
451 | data1.num = 100 / divisor; |
452 | data1.async = do_async; |
453 | thread1 = g_thread_new (name: "sleep" , |
454 | func: test_sleep_in_thread_func, |
455 | data: &data1); |
456 | |
457 | data2.proxy = proxy; |
458 | data2.msec = 20; |
459 | data2.num = 200 / divisor; |
460 | data2.async = do_async; |
461 | thread2 = g_thread_new (name: "sleep2" , |
462 | func: test_sleep_in_thread_func, |
463 | data: &data2); |
464 | |
465 | data3.proxy = proxy; |
466 | data3.msec = 100; |
467 | data3.num = 40 / divisor; |
468 | data3.async = do_async; |
469 | thread3 = g_thread_new (name: "sleep3" , |
470 | func: test_sleep_in_thread_func, |
471 | data: &data3); |
472 | |
473 | g_thread_join (thread: thread1); |
474 | g_thread_join (thread: thread2); |
475 | g_thread_join (thread: thread3); |
476 | |
477 | end_time = g_get_real_time (); |
478 | |
479 | elapsed_msec = (end_time - start_time) / 1000; |
480 | |
481 | //g_debug ("Elapsed time for %s = %d msec", n == 0 ? "async" : "sync", elapsed_msec); |
482 | |
483 | /* elapsed_msec should be 4000 msec +/- change for overhead/inaccuracy */ |
484 | g_assert_cmpint (elapsed_msec, >=, 3950 / divisor); |
485 | g_assert_cmpint (elapsed_msec, <, 30000 / divisor); |
486 | |
487 | if (g_test_verbose ()) |
488 | g_printerr (format: " " ); |
489 | } |
490 | } |
491 | |
492 | static void |
493 | test_method_calls_in_thread (void) |
494 | { |
495 | GDBusProxy *proxy; |
496 | GDBusConnection *connection; |
497 | GError *error; |
498 | |
499 | error = NULL; |
500 | connection = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, |
501 | NULL, |
502 | error: &error); |
503 | g_assert_no_error (error); |
504 | error = NULL; |
505 | proxy = g_dbus_proxy_new_sync (connection, |
506 | flags: G_DBUS_PROXY_FLAGS_NONE, |
507 | NULL, /* GDBusInterfaceInfo */ |
508 | name: "com.example.TestService" , /* name */ |
509 | object_path: "/com/example/TestObject" , /* object path */ |
510 | interface_name: "com.example.Frob" , /* interface */ |
511 | NULL, /* GCancellable */ |
512 | error: &error); |
513 | g_assert_no_error (error); |
514 | |
515 | test_method_calls_on_proxy (proxy); |
516 | |
517 | g_object_unref (object: proxy); |
518 | g_object_unref (object: connection); |
519 | |
520 | if (g_test_verbose ()) |
521 | g_printerr (format: "\n" ); |
522 | |
523 | assert_connection_has_one_ref (connection: c, NULL); |
524 | } |
525 | |
526 | #define SLEEP_MIN_USEC 1 |
527 | #define SLEEP_MAX_USEC 10 |
528 | |
529 | /* Can run in any thread */ |
530 | static void |
531 | ensure_connection_works (GDBusConnection *conn) |
532 | { |
533 | GVariant *v; |
534 | GError *error = NULL; |
535 | |
536 | v = g_dbus_connection_call_sync (connection: conn, bus_name: "org.freedesktop.DBus" , |
537 | object_path: "/org/freedesktop/DBus" , interface_name: "org.freedesktop.DBus" , method_name: "GetId" , NULL, NULL, flags: 0, timeout_msec: -1, |
538 | NULL, error: &error); |
539 | g_assert_no_error (error); |
540 | g_assert_nonnull (v); |
541 | g_assert_true (g_variant_is_of_type (v, G_VARIANT_TYPE ("(s)" ))); |
542 | g_variant_unref (value: v); |
543 | } |
544 | |
545 | /** |
546 | * get_sync_in_thread: |
547 | * @data: (type guint): delay in microseconds |
548 | * |
549 | * Sleep for a short time, then get a session bus connection and call |
550 | * a method on it. |
551 | * |
552 | * Runs in a non-main thread. |
553 | * |
554 | * Returns: (transfer full): the connection |
555 | */ |
556 | static gpointer |
557 | get_sync_in_thread (gpointer data) |
558 | { |
559 | guint delay = GPOINTER_TO_UINT (data); |
560 | GError *error = NULL; |
561 | GDBusConnection *conn; |
562 | |
563 | g_usleep (microseconds: delay); |
564 | |
565 | conn = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
566 | g_assert_no_error (error); |
567 | |
568 | ensure_connection_works (conn); |
569 | |
570 | return conn; |
571 | } |
572 | |
573 | static void |
574 | test_threaded_singleton (void) |
575 | { |
576 | guint i, n; |
577 | guint unref_wins = 0; |
578 | guint get_wins = 0; |
579 | |
580 | if (g_test_thorough ()) |
581 | n = 100000; |
582 | else |
583 | n = 1000; |
584 | |
585 | for (i = 0; i < n; i++) |
586 | { |
587 | GThread *thread; |
588 | guint unref_delay, get_delay; |
589 | GDBusConnection *new_conn; |
590 | |
591 | /* We want to be the last ref, so let it finish setting up */ |
592 | assert_connection_has_one_ref (connection: c, NULL); |
593 | |
594 | if (g_test_verbose () && (i % (n/50)) == 0) |
595 | g_printerr (format: "%u%%\n" , ((i * 100) / n)); |
596 | |
597 | /* Delay for a random time on each side of the race, to perturb the |
598 | * timing. Ideally, we want each side to win half the races; these |
599 | * timings are about right on smcv's laptop. |
600 | */ |
601 | unref_delay = g_random_int_range (SLEEP_MIN_USEC, SLEEP_MAX_USEC); |
602 | get_delay = g_random_int_range (SLEEP_MIN_USEC / 2, SLEEP_MAX_USEC / 2); |
603 | |
604 | /* One half of the race is to call g_bus_get_sync... */ |
605 | thread = g_thread_new (name: "get_sync_in_thread" , func: get_sync_in_thread, |
606 | GUINT_TO_POINTER (get_delay)); |
607 | |
608 | /* ... and the other half is to unref the shared connection, which must |
609 | * have exactly one ref at this point |
610 | */ |
611 | g_usleep (microseconds: unref_delay); |
612 | g_object_unref (object: c); |
613 | |
614 | /* Wait for the thread to run; see what it got */ |
615 | new_conn = g_thread_join (thread); |
616 | |
617 | /* If the thread won the race, it will have kept the same connection, |
618 | * and it'll have one ref |
619 | */ |
620 | if (new_conn == c) |
621 | { |
622 | get_wins++; |
623 | } |
624 | else |
625 | { |
626 | unref_wins++; |
627 | /* c is invalid now, but new_conn is suitable for the |
628 | * next round |
629 | */ |
630 | c = new_conn; |
631 | } |
632 | |
633 | ensure_connection_works (conn: c); |
634 | } |
635 | |
636 | if (g_test_verbose ()) |
637 | g_printerr (format: "Unref won %u races; Get won %u races\n" , unref_wins, get_wins); |
638 | } |
639 | |
640 | /* ---------------------------------------------------------------------------------------------------- */ |
641 | |
642 | int |
643 | main (int argc, |
644 | char *argv[]) |
645 | { |
646 | GError *error; |
647 | gint ret; |
648 | gchar *path; |
649 | |
650 | g_test_init (argc: &argc, argv: &argv, NULL); |
651 | |
652 | session_bus_up (); |
653 | |
654 | /* this is safe; testserver will exit once the bus goes away */ |
655 | path = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "gdbus-testserver" , NULL); |
656 | g_assert_true (g_spawn_command_line_async (path, NULL)); |
657 | g_free (mem: path); |
658 | |
659 | /* Create the connection in the main thread */ |
660 | error = NULL; |
661 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
662 | g_assert_no_error (error); |
663 | g_assert_nonnull (c); |
664 | |
665 | ensure_gdbus_testserver_up (connection: c, NULL); |
666 | |
667 | g_test_add_func (testpath: "/gdbus/delivery-in-thread" , test_func: test_delivery_in_thread); |
668 | g_test_add_func (testpath: "/gdbus/method-calls-in-thread" , test_func: test_method_calls_in_thread); |
669 | g_test_add_func (testpath: "/gdbus/threaded-singleton" , test_func: test_threaded_singleton); |
670 | |
671 | ret = g_test_run(); |
672 | |
673 | g_object_unref (object: c); |
674 | |
675 | /* tear down bus */ |
676 | session_bus_down (); |
677 | |
678 | return ret; |
679 | } |
680 | |