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