Add tests for new GAction api
[platform/upstream/glib.git] / gio / tests / actions.c
1 #include <gio/gio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "gdbus-sessionbus.h"
6
7 typedef struct
8 {
9   GVariant *params;
10   gboolean did_run;
11 } Activation;
12
13 static void
14 activate (GAction  *action,
15           GVariant *parameter,
16           gpointer  user_data)
17 {
18   Activation *activation = user_data;
19
20   if (parameter)
21     activation->params = g_variant_ref (parameter);
22   else
23     activation->params = NULL;
24   activation->did_run = TRUE;
25 }
26
27 static void
28 test_basic (void)
29 {
30   Activation a = { 0, };
31   GSimpleAction *action;
32   gchar *name;
33   GVariantType *parameter_type;
34   gboolean enabled;
35   GVariantType *state_type;
36   GVariant *state;
37
38   action = g_simple_action_new ("foo", NULL);
39   g_assert (g_action_get_enabled (G_ACTION (action)));
40   g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL);
41   g_assert (g_action_get_state_type (G_ACTION (action)) == NULL);
42   g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
43   g_assert (g_action_get_state (G_ACTION (action)) == NULL);
44   g_object_get (action,
45                 "name", &name,
46                 "parameter-type", &parameter_type,
47                 "enabled", &enabled,
48                 "state-type", &state_type,
49                 "state", &state,
50                  NULL);
51   g_assert_cmpstr (name, ==, "foo");
52   g_assert (parameter_type == NULL);
53   g_assert (enabled);
54   g_assert (state_type == NULL);
55   g_assert (state == NULL);
56   g_free (name);
57
58   g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
59   g_assert (!a.did_run);
60   g_action_activate (G_ACTION (action), NULL);
61   g_assert (a.did_run);
62   a.did_run = FALSE;
63
64   g_simple_action_set_enabled (action, FALSE);
65   g_action_activate (G_ACTION (action), NULL);
66   g_assert (!a.did_run);
67
68   if (g_test_undefined ())
69     {
70       g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
71                              "*assertion*g_variant_is_of_type*failed*");
72       g_action_activate (G_ACTION (action), g_variant_new_string ("xxx"));
73       g_test_assert_expected_messages ();
74     }
75
76   g_object_unref (action);
77   g_assert (!a.did_run);
78
79   action = g_simple_action_new ("foo", G_VARIANT_TYPE_STRING);
80   g_assert (g_action_get_enabled (G_ACTION (action)));
81   g_assert (g_variant_type_equal (g_action_get_parameter_type (G_ACTION (action)), G_VARIANT_TYPE_STRING));
82   g_assert (g_action_get_state_type (G_ACTION (action)) == NULL);
83   g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
84   g_assert (g_action_get_state (G_ACTION (action)) == NULL);
85
86   g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
87   g_assert (!a.did_run);
88   g_action_activate (G_ACTION (action), g_variant_new_string ("Hello world"));
89   g_assert (a.did_run);
90   g_assert_cmpstr (g_variant_get_string (a.params, NULL), ==, "Hello world");
91   g_variant_unref (a.params);
92   a.did_run = FALSE;
93
94   if (g_test_undefined ())
95     {
96       g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
97                              "*assertion*!= NULL*failed*");
98       g_action_activate (G_ACTION (action), NULL);
99       g_test_assert_expected_messages ();
100     }
101
102   g_object_unref (action);
103   g_assert (!a.did_run);
104 }
105
106 static void
107 test_name (void)
108 {
109   g_assert (!g_action_name_is_valid (""));
110   g_assert (!g_action_name_is_valid ("("));
111   g_assert (!g_action_name_is_valid ("%abc"));
112   g_assert (!g_action_name_is_valid ("$x1"));
113   g_assert (g_action_name_is_valid ("abc.def"));
114   g_assert (g_action_name_is_valid ("ABC-DEF"));
115 }
116
117 static gboolean
118 strv_has_string (gchar       **haystack,
119                  const gchar  *needle)
120 {
121   guint n;
122
123   for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
124     {
125       if (g_strcmp0 (haystack[n], needle) == 0)
126         return TRUE;
127     }
128   return FALSE;
129 }
130
131 static gboolean
132 strv_strv_cmp (gchar **a, gchar **b)
133 {
134   guint n;
135
136   for (n = 0; a[n] != NULL; n++)
137     {
138        if (!strv_has_string (b, a[n]))
139          return FALSE;
140     }
141
142   for (n = 0; b[n] != NULL; n++)
143     {
144        if (!strv_has_string (a, b[n]))
145          return FALSE;
146     }
147
148   return TRUE;
149 }
150
151 static gboolean
152 strv_set_equal (gchar **strv, ...)
153 {
154   gint count;
155   va_list list;
156   const gchar *str;
157   gboolean res;
158
159   res = TRUE;
160   count = 0;
161   va_start (list, strv);
162   while (1)
163     {
164       str = va_arg (list, const gchar *);
165       if (str == NULL)
166         break;
167       if (!strv_has_string (strv, str))
168         {
169           res = FALSE;
170           break;
171         }
172       count++;
173     }
174   va_end (list);
175
176   if (res)
177     res = g_strv_length ((gchar**)strv) == count;
178
179   return res;
180 }
181
182 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
183
184 static void
185 test_simple_group (void)
186 {
187   GSimpleActionGroup *group;
188   Activation a = { 0, };
189   GSimpleAction *simple;
190   GAction *action;
191   gchar **actions;
192   GVariant *state;
193
194   simple = g_simple_action_new ("foo", NULL);
195   g_signal_connect (simple, "activate", G_CALLBACK (activate), &a);
196   g_assert (!a.did_run);
197   g_action_activate (G_ACTION (simple), NULL);
198   g_assert (a.did_run);
199   a.did_run = FALSE;
200
201   group = g_simple_action_group_new ();
202   g_simple_action_group_insert (group, G_ACTION (simple));
203   g_object_unref (simple);
204
205   g_assert (!a.did_run);
206   g_action_group_activate_action (G_ACTION_GROUP (group), "foo", NULL);
207   g_assert (a.did_run);
208
209   simple = g_simple_action_new_stateful ("bar", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi"));
210   g_simple_action_group_insert (group, G_ACTION (simple));
211   g_object_unref (simple);
212
213   g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "foo"));
214   g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "bar"));
215   g_assert (!g_action_group_has_action (G_ACTION_GROUP (group), "baz"));
216   actions = g_action_group_list_actions (G_ACTION_GROUP (group));
217   g_assert_cmpint (g_strv_length (actions), ==, 2);
218   g_assert (strv_set_equal (actions, "foo", "bar", NULL));
219   g_strfreev (actions);
220   g_assert (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "foo"));
221   g_assert (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
222   g_assert (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "foo") == NULL);
223   g_assert (g_variant_type_equal (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
224   g_assert (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "foo") == NULL);
225   g_assert (g_variant_type_equal (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
226   g_assert (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "foo") == NULL);
227   g_assert (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "bar") == NULL);
228   g_assert (g_action_group_get_action_state (G_ACTION_GROUP (group), "foo") == NULL);
229   state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
230   g_assert (g_variant_type_equal (g_variant_get_type (state), G_VARIANT_TYPE_STRING));
231   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
232   g_variant_unref (state);
233
234   g_action_group_change_action_state (G_ACTION_GROUP (group), "bar", g_variant_new_string ("boo"));
235   state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
236   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "boo");
237   g_variant_unref (state);
238
239   action = g_simple_action_group_lookup (group, "bar");
240   g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
241   g_assert (!g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
242
243   g_simple_action_group_remove (group, "bar");
244   action = g_simple_action_group_lookup (group, "foo");
245   g_assert_cmpstr (g_action_get_name (action), ==, "foo");
246   action = g_simple_action_group_lookup (group, "bar");
247   g_assert (action == NULL);
248
249   a.did_run = FALSE;
250   g_object_unref (group);
251   g_assert (!a.did_run);
252 }
253
254 G_GNUC_END_IGNORE_DEPRECATIONS
255
256 static void
257 test_stateful (void)
258 {
259   GSimpleAction *action;
260   GVariant *state;
261
262   action = g_simple_action_new_stateful ("foo", NULL, g_variant_new_string ("hihi"));
263   g_assert (g_action_get_enabled (G_ACTION (action)));
264   g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL);
265   g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
266   g_assert (g_variant_type_equal (g_action_get_state_type (G_ACTION (action)),
267                                   G_VARIANT_TYPE_STRING));
268   state = g_action_get_state (G_ACTION (action));
269   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
270   g_variant_unref (state);
271
272   if (g_test_undefined ())
273     {
274       g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
275                              "*assertion*g_variant_is_of_type*failed*");
276       g_simple_action_set_state (action, g_variant_new_int32 (123));
277       g_test_assert_expected_messages ();
278     }
279
280   g_simple_action_set_state (action, g_variant_new_string ("hello"));
281   state = g_action_get_state (G_ACTION (action));
282   g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hello");
283   g_variant_unref (state);
284
285   g_object_unref (action);
286
287   action = g_simple_action_new ("foo", NULL);
288
289   if (g_test_undefined ())
290     {
291       g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
292                              "*assertion*!= NULL*failed*");
293       g_simple_action_set_state (action, g_variant_new_int32 (123));
294       g_test_assert_expected_messages ();
295     }
296
297   g_object_unref (action);
298 }
299
300 static gboolean foo_activated = FALSE;
301 static gboolean bar_activated = FALSE;
302
303 static void
304 activate_foo (GSimpleAction *simple,
305               GVariant      *parameter,
306               gpointer       user_data)
307 {
308   g_assert (user_data == GINT_TO_POINTER (123));
309   g_assert (parameter == NULL);
310   foo_activated = TRUE;
311 }
312
313 static void
314 activate_bar (GSimpleAction *simple,
315               GVariant      *parameter,
316               gpointer       user_data)
317 {
318   g_assert (user_data == GINT_TO_POINTER (123));
319   g_assert_cmpstr (g_variant_get_string (parameter, NULL), ==, "param");
320   bar_activated = TRUE;
321 }
322
323 static void
324 change_volume_state (GSimpleAction *action,
325                      GVariant      *value,
326                      gpointer       user_data)
327 {
328   gint requested;
329
330   requested = g_variant_get_int32 (value);
331
332   /* Volume only goes from 0 to 10 */
333   if (0 <= requested && requested <= 10)
334     g_simple_action_set_state (action, value);
335 }
336
337 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
338
339 static void
340 test_entries (void)
341 {
342   const GActionEntry entries[] = {
343     { "foo",    activate_foo                                     },
344     { "bar",    activate_bar, "s"                                },
345     { "toggle", NULL,         NULL, "false"                      },
346     { "volume", NULL,         NULL, "0",     change_volume_state }
347   };
348   GSimpleActionGroup *actions;
349   GVariant *state;
350
351   actions = g_simple_action_group_new ();
352   g_simple_action_group_add_entries (actions, entries,
353                                      G_N_ELEMENTS (entries),
354                                      GINT_TO_POINTER (123));
355
356   g_assert (!foo_activated);
357   g_action_group_activate_action (G_ACTION_GROUP (actions), "foo", NULL);
358   g_assert (foo_activated);
359   foo_activated = FALSE;
360
361   g_assert (!bar_activated);
362   g_action_group_activate_action (G_ACTION_GROUP (actions), "bar",
363                                   g_variant_new_string ("param"));
364   g_assert (bar_activated);
365   g_assert (!foo_activated);
366
367   if (g_test_undefined ())
368     {
369       const GActionEntry bad_type = {
370         "bad-type", NULL, "ss"
371       };
372       const GActionEntry bad_state = {
373         "bad-state", NULL, NULL, "flse"
374       };
375
376       g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
377                              "*not a valid GVariant type string*");
378       g_simple_action_group_add_entries (actions, &bad_type, 1, NULL);
379       g_test_assert_expected_messages ();
380
381       g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
382                              "*could not parse*");
383       g_simple_action_group_add_entries (actions, &bad_state, 1, NULL);
384       g_test_assert_expected_messages ();
385     }
386
387   state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
388   g_assert_cmpint (g_variant_get_int32 (state), ==, 0);
389   g_variant_unref (state);
390
391   /* should change */
392   g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
393                                       g_variant_new_int32 (7));
394   state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
395   g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
396   g_variant_unref (state);
397
398   /* should not change */
399   g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
400                                       g_variant_new_int32 (11));
401   state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
402   g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
403   g_variant_unref (state);
404
405   g_object_unref (actions);
406 }
407
408 G_GNUC_END_IGNORE_DEPRECATIONS
409
410 static void
411 test_parse_detailed (void)
412 {
413   struct {
414     const gchar *detailed;
415     const gchar *expected_name;
416     const gchar *expected_target;
417     const gchar *expected_error;
418     const gchar *detailed_roundtrip;
419   } testcases[] = {
420     { "abc",              "abc",    NULL,       NULL,             "abc" },
421     { " abc",             NULL,     NULL,       "invalid format", NULL },
422     { " abc",             NULL,     NULL,       "invalid format", NULL },
423     { "abc:",             NULL,     NULL,       "invalid format", NULL },
424     { ":abc",             NULL,     NULL,       "invalid format", NULL },
425     { "abc(",             NULL,     NULL,       "invalid format", NULL },
426     { "abc)",             NULL,     NULL,       "invalid format", NULL },
427     { "(abc",             NULL,     NULL,       "invalid format", NULL },
428     { ")abc",             NULL,     NULL,       "invalid format", NULL },
429     { "abc::xyz",         "abc",    "'xyz'",    NULL,             "abc::xyz" },
430     { "abc('xyz')",       "abc",    "'xyz'",    NULL,             "abc::xyz" },
431     { "abc(42)",          "abc",    "42",       NULL,             "abc(42)" },
432     { "abc(int32 42)",    "abc",    "42",       NULL,             "abc(42)" },
433     { "abc(@i 42)",       "abc",    "42",       NULL,             "abc(42)" },
434     { "abc (42)",         NULL,     NULL,       "invalid format", NULL },
435     { "abc(42abc)",       NULL,     NULL,       "invalid character in number", NULL },
436     { "abc(42, 4)",       "abc",    "(42, 4)",  "expected end of input", NULL },
437     { "abc(42,)",         "abc",    "(42,)",    "expected end of input", NULL }
438   };
439   gint i;
440
441   for (i = 0; i < G_N_ELEMENTS (testcases); i++)
442     {
443       GError *error = NULL;
444       GVariant *target;
445       gboolean success;
446       gchar *name;
447
448       success = g_action_parse_detailed_name (testcases[i].detailed, &name, &target, &error);
449       g_assert (success == (error == NULL));
450       if (success && testcases[i].expected_error)
451         g_error ("Unexpected success on '%s'.  Expected error containing '%s'",
452                  testcases[i].detailed, testcases[i].expected_error);
453
454       if (!success && !testcases[i].expected_error)
455         g_error ("Unexpected failure on '%s': %s", testcases[i].detailed, error->message);
456
457       if (!success)
458         {
459           if (!strstr (error->message, testcases[i].expected_error))
460             g_error ("Failure message '%s' for string '%s' did not contained expected substring '%s'",
461                      error->message, testcases[i].detailed, testcases[i].expected_error);
462
463           g_error_free (error);
464           continue;
465         }
466
467       g_assert_cmpstr (name, ==, testcases[i].expected_name);
468       g_assert ((target == NULL) == (testcases[i].expected_target == NULL));
469
470       if (success)
471         {
472           gchar *detailed;
473
474           detailed = g_action_print_detailed_name (name, target);
475           g_assert_cmpstr (detailed, ==, testcases[i].detailed_roundtrip);
476           g_free (detailed);
477         }
478
479       if (target)
480         {
481           GVariant *expected;
482
483           expected = g_variant_parse (NULL, testcases[i].expected_target, NULL, NULL, NULL);
484           g_assert (expected);
485
486           g_assert (g_variant_equal (expected, target));
487           g_variant_unref (expected);
488           g_variant_unref (target);
489         }
490
491       g_free (name);
492     }
493 }
494
495 GHashTable *activation_counts;
496
497 static void
498 count_activation (const gchar *action)
499 {
500   gint count;
501
502   if (activation_counts == NULL)
503     activation_counts = g_hash_table_new (g_str_hash, g_str_equal);
504   count = GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
505   count++;
506   g_hash_table_insert (activation_counts, (gpointer)action, GINT_TO_POINTER (count));
507 }
508
509 static gint
510 activation_count (const gchar *action)
511 {
512   if (activation_counts == NULL)
513     return 0;
514
515   return GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
516 }
517
518 static void
519 activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
520 {
521   count_activation (g_action_get_name (G_ACTION (action)));
522 }
523
524 static void
525 activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
526 {
527   GVariant *old_state, *new_state;
528
529   count_activation (g_action_get_name (G_ACTION (action)));
530
531   old_state = g_action_get_state (G_ACTION (action));
532   new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
533   g_simple_action_set_state (action, new_state);
534   g_variant_unref (old_state);
535 }
536
537 static void
538 activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
539 {
540   GVariant *new_state;
541
542   count_activation (g_action_get_name (G_ACTION (action)));
543
544   new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
545   g_simple_action_set_state (action, new_state);
546 }
547
548 static gboolean
549 compare_action_groups (GActionGroup *a, GActionGroup *b)
550 {
551   gchar **alist;
552   gchar **blist;
553   gint i;
554   gboolean equal;
555   gboolean ares, bres;
556   gboolean aenabled, benabled;
557   const GVariantType *aparameter_type, *bparameter_type;
558   const GVariantType *astate_type, *bstate_type;
559   GVariant *astate_hint, *bstate_hint;
560   GVariant *astate, *bstate;
561
562   alist = g_action_group_list_actions (a);
563   blist = g_action_group_list_actions (b);
564   equal = strv_strv_cmp (alist, blist);
565
566   for (i = 0; equal && alist[i]; i++)
567     {
568       ares = g_action_group_query_action (a, alist[i], &aenabled, &aparameter_type, &astate_type, &astate_hint, &astate);
569       bres = g_action_group_query_action (b, alist[i], &benabled, &bparameter_type, &bstate_type, &bstate_hint, &bstate);
570
571       if (ares && bres)
572         {
573           equal = equal && (aenabled == benabled);
574           equal = equal && ((!aparameter_type && !bparameter_type) || g_variant_type_equal (aparameter_type, bparameter_type));
575           equal = equal && ((!astate_type && !bstate_type) || g_variant_type_equal (astate_type, bstate_type));
576           equal = equal && ((!astate_hint && !bstate_hint) || g_variant_equal (astate_hint, bstate_hint));
577           equal = equal && ((!astate && !bstate) || g_variant_equal (astate, bstate));
578
579           if (astate_hint)
580             g_variant_unref (astate_hint);
581           if (bstate_hint)
582             g_variant_unref (bstate_hint);
583           if (astate)
584             g_variant_unref (astate);
585           if (bstate)
586             g_variant_unref (bstate);
587         }
588       else
589         equal = FALSE;
590     }
591
592   g_strfreev (alist);
593   g_strfreev (blist);
594
595   return equal;
596 }
597
598 static gboolean
599 stop_loop (gpointer data)
600 {
601   GMainLoop *loop = data;
602
603   g_main_loop_quit (loop);
604
605   return G_SOURCE_REMOVE;
606 }
607
608 static GActionEntry exported_entries[] = {
609   { "undo",  activate_action, NULL, NULL,      NULL },
610   { "redo",  activate_action, NULL, NULL,      NULL },
611   { "cut",   activate_action, NULL, NULL,      NULL },
612   { "copy",  activate_action, NULL, NULL,      NULL },
613   { "paste", activate_action, NULL, NULL,      NULL },
614   { "bold",  activate_toggle, NULL, "true",    NULL },
615   { "lang",  activate_radio,  "s",  "'latin'", NULL },
616 };
617
618 static void
619 list_cb (GObject      *source,
620          GAsyncResult *res,
621          gpointer      user_data)
622 {
623   GDBusConnection *bus = G_DBUS_CONNECTION (source);
624   GMainLoop *loop = user_data;
625   GError *error = NULL;
626   GVariant *v;
627   gchar **actions;
628
629   v = g_dbus_connection_call_finish (bus, res, &error);
630   g_assert (v);
631   g_variant_get (v, "(^a&s)", &actions);
632   g_assert_cmpint (g_strv_length (actions), ==, G_N_ELEMENTS (exported_entries));
633   g_free (actions);
634   g_variant_unref (v);
635   g_main_loop_quit (loop);
636 }
637
638 static gboolean
639 call_list (gpointer user_data)
640 {
641   GDBusConnection *bus;
642
643   bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
644   g_dbus_connection_call (bus,
645                           g_dbus_connection_get_unique_name (bus),
646                           "/",
647                           "org.gtk.Actions",
648                           "List",
649                           NULL,
650                           NULL,
651                           0,
652                           G_MAXINT,
653                           NULL,
654                           list_cb,
655                           user_data);
656   g_object_unref (bus);
657
658   return G_SOURCE_REMOVE;
659 }
660
661 static void
662 describe_cb (GObject      *source,
663              GAsyncResult *res,
664              gpointer      user_data)
665 {
666   GDBusConnection *bus = G_DBUS_CONNECTION (source);
667   GMainLoop *loop = user_data;
668   GError *error = NULL;
669   GVariant *v;
670   gboolean enabled;
671   gchar *param;
672   GVariantIter *iter;
673
674   v = g_dbus_connection_call_finish (bus, res, &error);
675   g_assert (v);
676   /* FIXME: there's an extra level of tuplelization in here */
677   g_variant_get (v, "((bgav))", &enabled, &param, &iter);
678   g_assert (enabled == TRUE);
679   g_assert_cmpstr (param, ==, "");
680   g_assert_cmpint (g_variant_iter_n_children (iter), ==, 0);
681   g_free (param);
682   g_variant_iter_free (iter);
683   g_variant_unref (v);
684
685   g_main_loop_quit (loop);
686 }
687
688 static gboolean
689 call_describe (gpointer user_data)
690 {
691   GDBusConnection *bus;
692
693   bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
694   g_dbus_connection_call (bus,
695                           g_dbus_connection_get_unique_name (bus),
696                           "/",
697                           "org.gtk.Actions",
698                           "Describe",
699                           g_variant_new ("(s)", "copy"),
700                           NULL,
701                           0,
702                           G_MAXINT,
703                           NULL,
704                           describe_cb,
705                           user_data);
706   g_object_unref (bus);
707
708   return G_SOURCE_REMOVE;
709 }
710
711 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
712
713 static void
714 test_dbus_export (void)
715 {
716   GDBusConnection *bus;
717   GSimpleActionGroup *group;
718   GDBusActionGroup *proxy;
719   GSimpleAction *action;
720   GMainLoop *loop;
721   GError *error = NULL;
722   GVariant *v;
723   guint id;
724   gchar **actions;
725
726   loop = g_main_loop_new (NULL, FALSE);
727
728   session_bus_up ();
729   bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
730
731   group = g_simple_action_group_new ();
732   g_simple_action_group_add_entries (group,
733                                      exported_entries,
734                                      G_N_ELEMENTS (exported_entries),
735                                      NULL);
736
737   id = g_dbus_connection_export_action_group (bus, "/", G_ACTION_GROUP (group), &error);
738   g_assert_no_error (error);
739
740   proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
741
742   actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
743   g_assert_cmpint (g_strv_length (actions), ==, 0);
744   g_strfreev (actions);
745
746   g_timeout_add (100, stop_loop, loop);
747   g_main_loop_run (loop);
748
749   actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
750   g_assert_cmpint (g_strv_length (actions), ==, G_N_ELEMENTS (exported_entries));
751   g_strfreev (actions);
752
753   /* check that calling "List" works too */
754   g_idle_add (call_list, loop);
755   g_main_loop_run (loop);
756
757   /* check that calling "Describe" works */
758   g_idle_add (call_describe, loop);
759   g_main_loop_run (loop);
760
761   /* test that the initial transfer works */
762   g_assert (G_IS_DBUS_ACTION_GROUP (proxy));
763   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
764
765   /* test that various changes get propagated from group to proxy */
766   action = g_simple_action_new_stateful ("italic", NULL, g_variant_new_boolean (FALSE));
767   g_simple_action_group_insert (group, G_ACTION (action));
768   g_object_unref (action);
769
770   g_timeout_add (100, stop_loop, loop);
771   g_main_loop_run (loop);
772
773   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
774
775   action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "cut"));
776   g_simple_action_set_enabled (action, FALSE);
777
778   g_timeout_add (100, stop_loop, loop);
779   g_main_loop_run (loop);
780
781   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
782
783   action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "bold"));
784   g_simple_action_set_state (action, g_variant_new_boolean (FALSE));
785
786   g_timeout_add (100, stop_loop, loop);
787   g_main_loop_run (loop);
788
789   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
790
791   g_simple_action_group_remove (group, "italic");
792
793   g_timeout_add (100, stop_loop, loop);
794   g_main_loop_run (loop);
795
796   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
797
798   /* test that activations and state changes propagate the other way */
799
800   g_assert_cmpint (activation_count ("copy"), ==, 0);
801   g_action_group_activate_action (G_ACTION_GROUP (proxy), "copy", NULL);
802
803   g_timeout_add (100, stop_loop, loop);
804   g_main_loop_run (loop);
805
806   g_assert_cmpint (activation_count ("copy"), ==, 1);
807   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
808
809   g_assert_cmpint (activation_count ("bold"), ==, 0);
810   g_action_group_activate_action (G_ACTION_GROUP (proxy), "bold", NULL);
811
812   g_timeout_add (100, stop_loop, loop);
813   g_main_loop_run (loop);
814
815   g_assert_cmpint (activation_count ("bold"), ==, 1);
816   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
817   v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
818   g_assert (g_variant_get_boolean (v));
819   g_variant_unref (v);
820
821   g_action_group_change_action_state (G_ACTION_GROUP (proxy), "bold", g_variant_new_boolean (FALSE));
822
823   g_timeout_add (100, stop_loop, loop);
824   g_main_loop_run (loop);
825
826   g_assert_cmpint (activation_count ("bold"), ==, 1);
827   g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
828   v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
829   g_assert (!g_variant_get_boolean (v));
830   g_variant_unref (v);
831
832   g_dbus_connection_unexport_action_group (bus, id);
833
834   g_object_unref (proxy);
835   g_object_unref (group);
836   g_main_loop_unref (loop);
837   g_object_unref (bus);
838
839   session_bus_down ();
840 }
841
842 static gpointer
843 do_export (gpointer data)
844 {
845   GActionGroup *group = data;
846   GMainContext *ctx;
847   gint i;
848   GError *error = NULL;
849   guint id;
850   GDBusConnection *bus;
851   GAction *action;
852   gchar *path;
853
854   ctx = g_main_context_new ();
855
856   g_main_context_push_thread_default (ctx);
857
858   bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
859   path = g_strdup_printf("/%p", data);
860
861   for (i = 0; i < 100000; i++)
862     {
863       id = g_dbus_connection_export_action_group (bus, path, G_ACTION_GROUP (group), &error);
864       g_assert_no_error (error);
865
866       action = g_simple_action_group_lookup (G_SIMPLE_ACTION_GROUP (group), "a");
867       g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
868                                    !g_action_get_enabled (action));
869
870       g_dbus_connection_unexport_action_group (bus, id);
871
872       while (g_main_context_iteration (ctx, FALSE));
873     }
874
875   g_free (path);
876   g_object_unref (bus);
877
878   g_main_context_pop_thread_default (ctx);
879
880   g_main_context_unref (ctx);
881
882   return NULL;
883 }
884
885 static void
886 test_dbus_threaded (void)
887 {
888   GSimpleActionGroup *group[10];
889   GThread *export[10];
890   static GActionEntry entries[] = {
891     { "a",  activate_action, NULL, NULL, NULL },
892     { "b",  activate_action, NULL, NULL, NULL },
893   };
894   gint i;
895
896   session_bus_up ();
897
898   for (i = 0; i < 10; i++)
899     {
900       group[i] = g_simple_action_group_new ();
901       g_simple_action_group_add_entries (group[i], entries, G_N_ELEMENTS (entries), NULL);
902       export[i] = g_thread_new ("export", do_export, group[i]);
903     }
904
905   for (i = 0; i < 10; i++)
906     g_thread_join (export[i]);
907
908   for (i = 0; i < 10; i++)
909     g_object_unref (group[i]);
910
911   session_bus_down ();
912 }
913
914 G_GNUC_END_IGNORE_DEPRECATIONS
915
916 static void
917 test_bug679509 (void)
918 {
919   GDBusConnection *bus;
920   GDBusActionGroup *proxy;
921   GMainLoop *loop;
922
923   loop = g_main_loop_new (NULL, FALSE);
924
925   session_bus_up ();
926   bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
927
928   proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
929   g_strfreev (g_action_group_list_actions (G_ACTION_GROUP (proxy)));
930   g_object_unref (proxy);
931
932   g_timeout_add (100, stop_loop, loop);
933   g_main_loop_run (loop);
934
935   g_main_loop_unref (loop);
936   g_object_unref (bus);
937
938   session_bus_down ();
939 }
940
941 static gchar *state_change_log;
942
943 static void
944 state_changed (GActionGroup *group,
945                const gchar  *action_name,
946                GVariant     *value,
947                gpointer      user_data)
948 {
949   GString *string;
950
951   g_assert (!state_change_log);
952
953   string = g_string_new (action_name);
954   g_string_append_c (string, ':');
955   g_variant_print_string (value, string, TRUE);
956   state_change_log = g_string_free (string, FALSE);
957 }
958
959 static void
960 verify_changed (const gchar *log_entry)
961 {
962   g_assert_cmpstr (state_change_log, ==, log_entry);
963   g_clear_pointer (&state_change_log, g_free);
964 }
965
966 static void
967 ensure_state (GSimpleActionGroup *group,
968               const gchar        *action_name,
969               const gchar        *expected)
970 {
971   GVariant *value;
972   gchar *printed;
973
974   value = g_action_group_get_action_state (G_ACTION_GROUP (group), action_name);
975   printed = g_variant_print (value, TRUE);
976   g_variant_unref (value);
977
978   g_assert_cmpstr (printed, ==, expected);
979   g_free (printed);
980 }
981
982 static void
983 test_property_actions (void)
984 {
985   GSimpleActionGroup *group;
986   GPropertyAction *action;
987   GSocketClient *client;
988   GApplication *app;
989
990   group = g_simple_action_group_new ();
991   g_signal_connect (group, "action-state-changed", G_CALLBACK (state_changed), NULL);
992
993   client = g_socket_client_new ();
994   app = g_application_new ("org.gtk.test", 0);
995
996   /* string... */
997   action = g_property_action_new ("app-id", app, "application-id");
998   g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
999   g_object_unref (action);
1000
1001   /* uint... */
1002   action = g_property_action_new ("keepalive", app, "inactivity-timeout");
1003   g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
1004   g_object_unref (action);
1005
1006   /* bool... */
1007   action = g_property_action_new ("tls", client, "tls");
1008   g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
1009   g_object_unref (action);
1010
1011   /* enum... */
1012   action = g_property_action_new ("type", client, "type");
1013   g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
1014   g_object_unref (action);
1015
1016   /* the objects should be held alive by the actions... */
1017   g_object_unref (client);
1018   g_object_unref (app);
1019
1020   ensure_state (group, "app-id", "'org.gtk.test'");
1021   ensure_state (group, "keepalive", "uint32 0");
1022   ensure_state (group, "tls", "false");
1023   ensure_state (group, "type", "'stream'");
1024
1025   verify_changed (NULL);
1026
1027   /* some string tests... */
1028   g_action_group_change_action_state (G_ACTION_GROUP (group), "app-id", g_variant_new ("s", "org.gtk.test2"));
1029   verify_changed ("app-id:'org.gtk.test2'");
1030   g_assert_cmpstr (g_application_get_application_id (app), ==, "org.gtk.test2");
1031   ensure_state (group, "app-id", "'org.gtk.test2'");
1032
1033   g_action_group_activate_action (G_ACTION_GROUP (group), "app-id", g_variant_new ("s", "org.gtk.test3"));
1034   verify_changed ("app-id:'org.gtk.test3'");
1035   g_assert_cmpstr (g_application_get_application_id (app), ==, "org.gtk.test3");
1036   ensure_state (group, "app-id", "'org.gtk.test3'");
1037
1038   g_application_set_application_id (app, "org.gtk.test");
1039   verify_changed ("app-id:'org.gtk.test'");
1040   ensure_state (group, "app-id", "'org.gtk.test'");
1041
1042   /* uint tests */
1043   g_action_group_change_action_state (G_ACTION_GROUP (group), "keepalive", g_variant_new ("u", 1234));
1044   verify_changed ("keepalive:uint32 1234");
1045   g_assert_cmpuint (g_application_get_inactivity_timeout (app), ==, 1234);
1046   ensure_state (group, "keepalive", "uint32 1234");
1047
1048   g_action_group_activate_action (G_ACTION_GROUP (group), "keepalive", g_variant_new ("u", 5678));
1049   verify_changed ("keepalive:uint32 5678");
1050   g_assert_cmpuint (g_application_get_inactivity_timeout (app), ==, 5678);
1051   ensure_state (group, "keepalive", "uint32 5678");
1052
1053   g_application_set_inactivity_timeout (app, 0);
1054   verify_changed ("keepalive:uint32 0");
1055   ensure_state (group, "keepalive", "uint32 0");
1056
1057   /* bool tests */
1058   g_action_group_change_action_state (G_ACTION_GROUP (group), "tls", g_variant_new ("b", TRUE));
1059   verify_changed ("tls:true");
1060   g_assert (g_socket_client_get_tls (client));
1061   ensure_state (group, "tls", "true");
1062
1063   /* test toggle true->false */
1064   g_action_group_activate_action (G_ACTION_GROUP (group), "tls", NULL);
1065   verify_changed ("tls:false");
1066   g_assert (!g_socket_client_get_tls (client));
1067   ensure_state (group, "tls", "false");
1068
1069   /* and now back false->true */
1070   g_action_group_activate_action (G_ACTION_GROUP (group), "tls", NULL);
1071   verify_changed ("tls:true");
1072   g_assert (g_socket_client_get_tls (client));
1073   ensure_state (group, "tls", "true");
1074
1075   g_socket_client_set_tls (client, FALSE);
1076   verify_changed ("tls:false");
1077   ensure_state (group, "tls", "false");
1078
1079   /* enum tests */
1080   g_action_group_change_action_state (G_ACTION_GROUP (group), "type", g_variant_new ("s", "datagram"));
1081   verify_changed ("type:'datagram'");
1082   g_assert_cmpint (g_socket_client_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM);
1083   ensure_state (group, "type", "'datagram'");
1084
1085   g_action_group_activate_action (G_ACTION_GROUP (group), "type", g_variant_new ("s", "stream"));
1086   verify_changed ("type:'stream'");
1087   g_assert_cmpint (g_socket_client_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM);
1088   ensure_state (group, "type", "'stream'");
1089
1090   g_socket_client_set_socket_type (client, G_SOCKET_TYPE_SEQPACKET);
1091   verify_changed ("type:'seqpacket'");
1092   ensure_state (group, "type", "'seqpacket'");
1093
1094   /* Check some error cases... */
1095   g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_CRITICAL, "*non-existent*");
1096   action = g_property_action_new ("foo", app, "xyz");
1097   g_test_assert_expected_messages ();
1098   g_object_unref (action);
1099
1100   g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_CRITICAL, "*writable*");
1101   action = g_property_action_new ("foo", app, "is-registered");
1102   g_test_assert_expected_messages ();
1103   g_object_unref (action);
1104
1105   g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_CRITICAL, "*type 'GSocketAddress'*");
1106   action = g_property_action_new ("foo", client, "local-address");
1107   g_test_assert_expected_messages ();
1108   g_object_unref (action);
1109
1110   g_object_unref (group);
1111 }
1112
1113 int
1114 main (int argc, char **argv)
1115 {
1116   g_test_init (&argc, &argv, NULL);
1117
1118   g_test_add_func ("/actions/basic", test_basic);
1119   g_test_add_func ("/actions/name", test_name);
1120   g_test_add_func ("/actions/simplegroup", test_simple_group);
1121   g_test_add_func ("/actions/stateful", test_stateful);
1122   g_test_add_func ("/actions/entries", test_entries);
1123   g_test_add_func ("/actions/parse-detailed", test_parse_detailed);
1124   g_test_add_func ("/actions/dbus/export", test_dbus_export);
1125   g_test_add_func ("/actions/dbus/threaded", test_dbus_threaded);
1126   g_test_add_func ("/actions/dbus/bug679509", test_bug679509);
1127   g_test_add_func ("/actions/property", test_property_actions);
1128
1129   return g_test_run ();
1130 }