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