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