Add a test for multithreaded action group exporter use
[platform/upstream/glib.git] / gio / tests / actions.c
1 #include <gio/gio.h>
2 #include <stdlib.h>
3
4 typedef struct
5 {
6   GVariant *params;
7   gboolean did_run;
8 } Activation;
9
10 static void
11 activate (GAction  *action,
12           GVariant *parameter,
13           gpointer  user_data)
14 {
15   Activation *activation = user_data;
16
17   if (parameter)
18     activation->params = g_variant_ref (parameter);
19   else
20     activation->params = NULL;
21   activation->did_run = TRUE;
22 }
23
24 static void
25 test_basic (void)
26 {
27   Activation a = { 0, };
28   GSimpleAction *action;
29   gchar *name;
30   GVariantType *parameter_type;
31   gboolean enabled;
32   GVariantType *state_type;
33   GVariant *state;
34
35   action = g_simple_action_new ("foo", NULL);
36   g_assert (g_action_get_enabled (G_ACTION (action)));
37   g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL);
38   g_assert (g_action_get_state_type (G_ACTION (action)) == NULL);
39   g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
40   g_assert (g_action_get_state (G_ACTION (action)) == NULL);
41   g_object_get (action,
42                 "name", &name,
43                 "parameter-type", &parameter_type,
44                 "enabled", &enabled,
45                 "state-type", &state_type,
46                 "state", &state,
47                  NULL);
48   g_assert_cmpstr (name, ==, "foo");
49   g_assert (parameter_type == NULL);
50   g_assert (enabled);
51   g_assert (state_type == NULL);
52   g_assert (state == NULL);
53   g_free (name);
54
55   g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
56   g_assert (!a.did_run);
57   g_action_activate (G_ACTION (action), NULL);
58   g_assert (a.did_run);
59   a.did_run = FALSE;
60
61   g_simple_action_set_enabled (action, FALSE);
62   g_action_activate (G_ACTION (action), NULL);
63   g_assert (!a.did_run);
64
65   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
66     {
67       g_action_activate (G_ACTION (action), g_variant_new_string ("xxx"));
68       exit (0);
69     }
70   g_test_trap_assert_failed ();
71
72   g_object_unref (action);
73   g_assert (!a.did_run);
74
75   action = g_simple_action_new ("foo", G_VARIANT_TYPE_STRING);
76   g_assert (g_action_get_enabled (G_ACTION (action)));
77   g_assert (g_variant_type_equal (g_action_get_parameter_type (G_ACTION (action)), G_VARIANT_TYPE_STRING));
78   g_assert (g_action_get_state_type (G_ACTION (action)) == NULL);
79   g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
80   g_assert (g_action_get_state (G_ACTION (action)) == NULL);
81
82   g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
83   g_assert (!a.did_run);
84   g_action_activate (G_ACTION (action), g_variant_new_string ("Hello world"));
85   g_assert (a.did_run);
86   g_assert_cmpstr (g_variant_get_string (a.params, NULL), ==, "Hello world");
87   g_variant_unref (a.params);
88   a.did_run = FALSE;
89
90   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
91     {
92       g_action_activate (G_ACTION (action), NULL);
93       exit (0);
94     }
95
96   g_test_trap_assert_failed ();
97
98   g_object_unref (action);
99   g_assert (!a.did_run);
100 }
101
102 static gboolean
103 strv_has_string (gchar       **haystack,
104                  const gchar  *needle)
105 {
106   guint n;
107
108   for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
109     {
110       if (g_strcmp0 (haystack[n], needle) == 0)
111         return TRUE;
112     }
113   return FALSE;
114 }
115
116 static gboolean
117 strv_strv_cmp (gchar **a, gchar **b)
118 {
119   guint n;
120
121   for (n = 0; a[n] != NULL; n++)
122     {
123        if (!strv_has_string (b, a[n]))
124          return FALSE;
125     }
126
127   for (n = 0; b[n] != NULL; n++)
128     {
129        if (!strv_has_string (a, b[n]))
130          return FALSE;
131     }
132
133   return TRUE;
134 }
135
136 static gboolean
137 strv_set_equal (gchar **strv, ...)
138 {
139   gint count;
140   va_list list;
141   const gchar *str;
142   gboolean res;
143
144   res = TRUE;
145   count = 0;
146   va_start (list, strv);
147   while (1)
148     {
149       str = va_arg (list, const gchar *);
150       if (str == NULL)
151         break;
152       if (!strv_has_string (strv, str))
153         {
154           res = FALSE;
155           break;
156         }
157       count++;
158     }
159   va_end (list);
160
161   if (res)
162     res = g_strv_length ((gchar**)strv) == count;
163
164   return res;
165 }
166
167 static void
168 test_simple_group (void)
169 {
170   GSimpleActionGroup *group;
171   Activation a = { 0, };
172   GSimpleAction *simple;
173   GAction *action;
174   gchar **actions;
175   GVariant *state;
176
177   simple = g_simple_action_new ("foo", NULL);
178   g_signal_connect (simple, "activate", G_CALLBACK (activate), &a);
179   g_assert (!a.did_run);
180   g_action_activate (G_ACTION (simple), NULL);
181   g_assert (a.did_run);
182   a.did_run = FALSE;
183
184   group = g_simple_action_group_new ();
185   g_simple_action_group_insert (group, G_ACTION (simple));
186   g_object_unref (simple);
187
188   g_assert (!a.did_run);
189   g_action_group_activate_action (G_ACTION_GROUP (group), "foo", NULL);
190   g_assert (a.did_run);
191
192   simple = g_simple_action_new_stateful ("bar", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi"));
193   g_simple_action_group_insert (group, G_ACTION (simple));
194   g_object_unref (simple);
195
196   g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "foo"));
197   g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "bar"));
198   g_assert (!g_action_group_has_action (G_ACTION_GROUP (group), "baz"));
199   actions = g_action_group_list_actions (G_ACTION_GROUP (group));
200   g_assert_cmpint (g_strv_length (actions), ==, 2);
201   g_assert (strv_set_equal (actions, "foo", "bar", NULL));
202   g_strfreev (actions);
203   g_assert (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "foo"));
204   g_assert (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
205   g_assert (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "foo") == NULL);
206   g_assert (g_variant_type_equal (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
207   g_assert (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "foo") == NULL);
208   g_assert (g_variant_type_equal (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
209   g_assert (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "foo") == NULL);
210   g_assert (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "bar") == NULL);
211   g_assert (g_action_group_get_action_state (G_ACTION_GROUP (group), "foo") == NULL);
212   state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
213   g_assert (g_variant_type_equal (g_variant_get_type (state), G_VARIANT_TYPE_STRING));
214   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
215   g_variant_unref (state);
216
217   g_action_group_change_action_state (G_ACTION_GROUP (group), "bar", g_variant_new_string ("boo"));
218   state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
219   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "boo");
220   g_variant_unref (state);
221
222   action = g_simple_action_group_lookup (group, "bar");
223   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
224   g_assert (!g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
225
226   g_simple_action_group_remove (group, "bar");
227   action = g_simple_action_group_lookup (group, "foo");
228   g_assert_cmpstr (g_action_get_name (action), ==, "foo");
229   action = g_simple_action_group_lookup (group, "bar");
230   g_assert (action == NULL);
231
232   a.did_run = FALSE;
233   g_object_unref (group);
234   g_assert (!a.did_run);
235 }
236
237 static void
238 test_stateful (void)
239 {
240   GSimpleAction *action;
241   GVariant *state;
242
243   action = g_simple_action_new_stateful ("foo", NULL, g_variant_new_string ("hihi"));
244   g_assert (g_action_get_enabled (G_ACTION (action)));
245   g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL);
246   g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
247   g_assert (g_variant_type_equal (g_action_get_state_type (G_ACTION (action)),
248                                   G_VARIANT_TYPE_STRING));
249   state = g_action_get_state (G_ACTION (action));
250   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
251   g_variant_unref (state);
252
253   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
254     {
255       g_simple_action_set_state (action, g_variant_new_int32 (123));
256       exit (0);
257     }
258   g_test_trap_assert_failed ();
259
260   g_simple_action_set_state (action, g_variant_new_string ("hello"));
261   state = g_action_get_state (G_ACTION (action));
262   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hello");
263   g_variant_unref (state);
264
265   g_object_unref (action);
266
267   action = g_simple_action_new ("foo", NULL);
268   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
269     {
270       g_simple_action_set_state (action, g_variant_new_int32 (123));
271       exit (0);
272     }
273   g_test_trap_assert_failed ();
274   g_object_unref (action);
275 }
276
277 static gboolean foo_activated = FALSE;
278 static gboolean bar_activated = FALSE;
279
280 static void
281 activate_foo (GSimpleAction *simple,
282               GVariant      *parameter,
283               gpointer       user_data)
284 {
285   g_assert (user_data == GINT_TO_POINTER (123));
286   g_assert (parameter == NULL);
287   foo_activated = TRUE;
288 }
289
290 static void
291 activate_bar (GSimpleAction *simple,
292               GVariant      *parameter,
293               gpointer       user_data)
294 {
295   g_assert (user_data == GINT_TO_POINTER (123));
296   g_assert_cmpstr (g_variant_get_string (parameter, NULL), ==, "param");
297   bar_activated = TRUE;
298 }
299
300 static void
301 change_volume_state (GSimpleAction *action,
302                      GVariant      *value,
303                      gpointer       user_data)
304 {
305   gint requested;
306
307   requested = g_variant_get_int32 (value);
308
309   /* Volume only goes from 0 to 10 */
310   if (0 <= requested && requested <= 10)
311     g_simple_action_set_state (action, value);
312 }
313
314 static void
315 test_entries (void)
316 {
317   const GActionEntry entries[] = {
318     { "foo",    activate_foo                                     },
319     { "bar",    activate_bar, "s"                                },
320     { "toggle", NULL,         NULL, "false"                      },
321     { "volume", NULL,         NULL, "0",     change_volume_state }
322   };
323   GSimpleActionGroup *actions;
324   GVariant *state;
325
326   actions = g_simple_action_group_new ();
327   g_simple_action_group_add_entries (actions, entries,
328                                      G_N_ELEMENTS (entries),
329                                      GINT_TO_POINTER (123));
330
331   g_assert (!foo_activated);
332   g_action_group_activate_action (G_ACTION_GROUP (actions), "foo", NULL);
333   g_assert (foo_activated);
334   foo_activated = FALSE;
335
336   g_assert (!bar_activated);
337   g_action_group_activate_action (G_ACTION_GROUP (actions), "bar",
338                                   g_variant_new_string ("param"));
339   g_assert (bar_activated);
340   g_assert (!foo_activated);
341
342   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
343     {
344       const GActionEntry bad_type = {
345         "bad-type", NULL, "ss"
346       };
347
348       g_simple_action_group_add_entries (actions, &bad_type, 1, NULL);
349       exit (0);
350     }
351   g_test_trap_assert_failed ();
352
353   if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
354     {
355       const GActionEntry bad_state = {
356         "bad-state", NULL, NULL, "flse"
357       };
358
359       g_simple_action_group_add_entries (actions, &bad_state, 1, NULL);
360       exit (0);
361     }
362   g_test_trap_assert_failed ();
363
364   state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
365   g_assert_cmpint (g_variant_get_int32 (state), ==, 0);
366   g_variant_unref (state);
367
368   /* should change */
369   g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
370                                       g_variant_new_int32 (7));
371   state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
372   g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
373   g_variant_unref (state);
374
375   /* should not change */
376   g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
377                                       g_variant_new_int32 (11));
378   state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
379   g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
380   g_variant_unref (state);
381
382   g_object_unref (actions);
383 }
384
385
386 GHashTable *activation_counts;
387
388 static void
389 count_activation (const gchar *action)
390 {
391   gint count;
392
393   if (activation_counts == NULL)
394     activation_counts = g_hash_table_new (g_str_hash, g_str_equal);
395   count = GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
396   count++;
397   g_hash_table_insert (activation_counts, (gpointer)action, GINT_TO_POINTER (count));
398 }
399
400 static gint
401 activation_count (const gchar *action)
402 {
403   if (activation_counts == NULL)
404     return 0;
405
406   return GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
407 }
408
409 static void
410 activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
411 {
412   count_activation (g_action_get_name (G_ACTION (action)));
413 }
414
415 static void
416 activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
417 {
418   GVariant *old_state, *new_state;
419
420   count_activation (g_action_get_name (G_ACTION (action)));
421
422   old_state = g_action_get_state (G_ACTION (action));
423   new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
424   g_simple_action_set_state (action, new_state);
425   g_variant_unref (old_state);
426 }
427
428 static void
429 activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
430 {
431   GVariant *new_state;
432
433   count_activation (g_action_get_name (G_ACTION (action)));
434
435   new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
436   g_simple_action_set_state (action, new_state);
437 }
438
439 static gboolean
440 compare_action_groups (GActionGroup *a, GActionGroup *b)
441 {
442   gchar **alist;
443   gchar **blist;
444   gint i;
445   gboolean equal;
446   gboolean ares, bres;
447   gboolean aenabled, benabled;
448   const GVariantType *aparameter_type, *bparameter_type;
449   const GVariantType *astate_type, *bstate_type;
450   GVariant *astate_hint, *bstate_hint;
451   GVariant *astate, *bstate;
452
453   alist = g_action_group_list_actions (a);
454   blist = g_action_group_list_actions (b);
455   equal = strv_strv_cmp (alist, blist);
456
457   for (i = 0; equal && alist[i]; i++)
458     {
459       ares = g_action_group_query_action (a, alist[i], &aenabled, &aparameter_type, &astate_type, &astate_hint, &astate);
460       bres = g_action_group_query_action (b, alist[i], &benabled, &bparameter_type, &bstate_type, &bstate_hint, &bstate);
461
462       if (ares && bres)
463         {
464           equal = equal && (aenabled == benabled);
465           equal = equal && ((!aparameter_type && !bparameter_type) || g_variant_type_equal (aparameter_type, bparameter_type));
466           equal = equal && ((!astate_type && !bstate_type) || g_variant_type_equal (astate_type, bstate_type));
467           equal = equal && ((!astate_hint && !bstate_hint) || g_variant_equal (astate_hint, bstate_hint));
468           equal = equal && ((!astate && !bstate) || g_variant_equal (astate, bstate));
469
470           if (astate_hint)
471             g_variant_unref (astate_hint);
472           if (bstate_hint)
473             g_variant_unref (bstate_hint);
474           if (astate)
475             g_variant_unref (astate);
476           if (bstate)
477             g_variant_unref (bstate);
478         }
479       else
480         equal = FALSE;
481     }
482
483   g_strfreev (alist);
484   g_strfreev (blist);
485
486   return equal;
487 }
488
489 static gboolean
490 stop_loop (gpointer data)
491 {
492   GMainLoop *loop = data;
493
494   g_main_loop_quit (loop);
495
496   return G_SOURCE_REMOVE;
497 }
498
499 GDBusActionGroup *proxy;
500
501 static void
502 got_proxy (GObject      *source,
503            GAsyncResult *res,
504            gpointer      user_data)
505 {
506   GError *error = NULL;
507
508   proxy = g_dbus_action_group_new_finish (res, &error);
509   g_assert_no_error (error);
510 }
511
512 static void
513 test_dbus_export (void)
514 {
515   GDBusConnection *bus;
516   GSimpleActionGroup *group;
517   GSimpleAction *action;
518   GMainLoop *loop;
519   static GActionEntry entries[] = {
520     { "undo",  activate_action, NULL, NULL,      NULL },
521     { "redo",  activate_action, NULL, NULL,      NULL },
522     { "cut",   activate_action, NULL, NULL,      NULL },
523     { "copy",  activate_action, NULL, NULL,      NULL },
524     { "paste", activate_action, NULL, NULL,      NULL },
525     { "bold",  activate_toggle, NULL, "true",    NULL },
526     { "lang",  activate_radio,  "s",  "'latin'", NULL },
527   };
528   GError *error = NULL;
529   GVariant *v;
530   guint id;
531
532   loop = g_main_loop_new (NULL, FALSE);
533
534   bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
535
536   group = g_simple_action_group_new ();
537   g_simple_action_group_add_entries (group, entries, G_N_ELEMENTS (entries), NULL);
538
539   id = g_dbus_connection_export_action_group (bus, "/", G_ACTION_GROUP (group), &error);
540   g_assert_no_error (error);
541
542   g_dbus_action_group_new (bus, g_dbus_connection_get_unique_name (bus), "/", 0, NULL, got_proxy, NULL);
543   g_assert_no_error (error);
544
545   g_timeout_add (100, stop_loop, loop);
546   g_main_loop_run (loop);
547
548   /* test that the initial transfer works */
549   g_assert (G_IS_DBUS_ACTION_GROUP (proxy));
550   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
551
552   /* test that various changes get propagated from group to proxy */
553   action = g_simple_action_new_stateful ("italic", NULL, g_variant_new_boolean (FALSE));
554   g_simple_action_group_insert (group, G_ACTION (action));
555   g_object_unref (action);
556
557   g_timeout_add (100, stop_loop, loop);
558   g_main_loop_run (loop);
559
560   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
561
562   action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "cut"));
563   g_simple_action_set_enabled (action, FALSE);
564
565   g_timeout_add (100, stop_loop, loop);
566   g_main_loop_run (loop);
567
568   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
569
570   action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "bold"));
571   g_simple_action_set_state (action, g_variant_new_boolean (FALSE));
572
573   g_timeout_add (100, stop_loop, loop);
574   g_main_loop_run (loop);
575
576   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
577
578   g_simple_action_group_remove (group, "italic");
579
580   g_timeout_add (100, stop_loop, loop);
581   g_main_loop_run (loop);
582
583   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
584
585   /* test that activations and state changes propagate the other way */
586
587   g_assert_cmpint (activation_count ("copy"), ==, 0);
588   g_action_group_activate_action (G_ACTION_GROUP (proxy), "copy", NULL);
589
590   g_timeout_add (100, stop_loop, loop);
591   g_main_loop_run (loop);
592
593   g_assert_cmpint (activation_count ("copy"), ==, 1);
594   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
595
596   g_assert_cmpint (activation_count ("bold"), ==, 0);
597   g_action_group_activate_action (G_ACTION_GROUP (proxy), "bold", NULL);
598
599   g_timeout_add (100, stop_loop, loop);
600   g_main_loop_run (loop);
601
602   g_assert_cmpint (activation_count ("bold"), ==, 1);
603   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
604   v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
605   g_assert (g_variant_get_boolean (v));
606   g_variant_unref (v);
607
608   g_action_group_change_action_state (G_ACTION_GROUP (proxy), "bold", g_variant_new_boolean (FALSE));
609
610   g_timeout_add (100, stop_loop, loop);
611   g_main_loop_run (loop);
612
613   g_assert_cmpint (activation_count ("bold"), ==, 1);
614   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
615   v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
616   g_assert (!g_variant_get_boolean (v));
617   g_variant_unref (v);
618
619   g_dbus_connection_unexport_action_group (bus, id);
620
621   g_object_unref (proxy);
622   g_object_unref (group);
623   g_main_loop_unref (loop);
624   g_object_unref (bus);
625 }
626
627 static gpointer
628 do_activate (gpointer data)
629 {
630   GSimpleActionGroup *group = data;
631   gint i;
632   GAction *action;
633
634   for (i = 0; i < 1000000; i++)
635     {
636       action = g_simple_action_group_lookup (group, "a");
637       g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
638                                    !g_action_get_enabled (action));
639     }
640
641   return NULL;
642 }
643
644 static gpointer
645 do_export (gpointer data)
646 {
647   GActionGroup *group = data;
648   gint i;
649   GError *error = NULL;
650   guint id;
651   GDBusConnection *bus;
652   gchar *path;
653
654   bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
655   path = g_strdup_printf("/%p", data);
656
657   for (i = 0; i < 100000; i++)
658     {
659       id = g_dbus_connection_export_action_group (bus, path, G_ACTION_GROUP (group), &error);
660       g_assert_no_error (error);
661       g_dbus_connection_unexport_action_group (bus, id);
662     }
663
664   g_free (path);
665   g_object_unref (bus);
666
667   return NULL;
668 }
669
670 static void
671 test_dbus_threaded (void)
672 {
673   GSimpleActionGroup *group[10];
674   GThread *call[10];
675   GThread *export[10];
676   static GActionEntry entries[] = {
677     { "a",  activate_action, NULL, NULL, NULL },
678     { "b",  activate_action, NULL, NULL, NULL },
679   };
680   gint i;
681
682   for (i = 0; i < 10; i++)
683     {
684       group[i] = g_simple_action_group_new ();
685       g_simple_action_group_add_entries (group[i], entries, G_N_ELEMENTS (entries), NULL);
686       call[i] = g_thread_new ("call", do_activate, group[i]);
687       export[i] = g_thread_new ("export", do_export, group[i]);
688     }
689
690   for (i = 0; i < 10; i++)
691     {
692       g_thread_join (call[i]);
693       g_thread_join (export[i]);
694     }
695
696   for (i = 0; i < 10; i++)
697     g_object_unref (group[i]);
698 }
699
700 int
701 main (int argc, char **argv)
702 {
703   g_type_init ();
704   g_test_init (&argc, &argv, NULL);
705
706   g_test_add_func ("/actions/basic", test_basic);
707   g_test_add_func ("/actions/simplegroup", test_simple_group);
708   g_test_add_func ("/actions/stateful", test_stateful);
709   g_test_add_func ("/actions/entries", test_entries);
710   g_test_add_func ("/actions/dbus/export", test_dbus_export);
711   g_test_add_func ("/actions/dbus/threaded", test_dbus_threaded);
712
713   return g_test_run ();
714 }