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