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