(root)/
glib-2.79.0/
gio/
tests/
actions.c
       1  /*
       2   * Copyright © 2010, 2011, 2013, 2014 Codethink Limited
       3   * Copyright © 2010, 2011, 2012, 2013, 2015 Red Hat, Inc.
       4   * Copyright © 2012 Pavel Vasin
       5   * Copyright © 2022 Endless OS Foundation, LLC
       6   *
       7   * SPDX-License-Identifier: LGPL-2.1-or-later
       8   *
       9   * This library is free software; you can redistribute it and/or
      10   * modify it under the terms of the GNU Lesser General Public
      11   * License as published by the Free Software Foundation; either
      12   * version 2.1 of the License, or (at your option) any later version.
      13   *
      14   * This library is distributed in the hope that it will be useful,
      15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17   * Lesser General Public License for more details.
      18   *
      19   * You should have received a copy of the GNU Lesser General
      20   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      21   *
      22   * Authors: Ryan Lortie <desrt@desrt.ca>
      23   */
      24  
      25  #include <gio/gio.h>
      26  #include <stdlib.h>
      27  #include <string.h>
      28  
      29  #include "gdbus-sessionbus.h"
      30  
      31  typedef struct
      32  {
      33    GVariant *params;
      34    gboolean did_run;
      35  } Activation;
      36  
      37  static void
      38  activate (GAction  *action,
      39            GVariant *parameter,
      40            gpointer  user_data)
      41  {
      42    Activation *activation = user_data;
      43  
      44    if (parameter)
      45      activation->params = g_variant_ref (parameter);
      46    else
      47      activation->params = NULL;
      48    activation->did_run = TRUE;
      49  }
      50  
      51  static void
      52  test_basic (void)
      53  {
      54    Activation a = { 0, };
      55    GSimpleAction *action;
      56    gchar *name;
      57    GVariantType *parameter_type;
      58    gboolean enabled;
      59    GVariantType *state_type;
      60    GVariant *state;
      61  
      62    action = g_simple_action_new ("foo", NULL);
      63    g_assert_true (g_action_get_enabled (G_ACTION (action)));
      64    g_assert_null (g_action_get_parameter_type (G_ACTION (action)));
      65    g_assert_null (g_action_get_state_type (G_ACTION (action)));
      66    g_assert_null (g_action_get_state_hint (G_ACTION (action)));
      67    g_assert_null (g_action_get_state (G_ACTION (action)));
      68    g_object_get (action,
      69                  "name", &name,
      70                  "parameter-type", &parameter_type,
      71                  "enabled", &enabled,
      72                  "state-type", &state_type,
      73                  "state", &state,
      74                   NULL);
      75    g_assert_cmpstr (name, ==, "foo");
      76    g_assert_null (parameter_type);
      77    g_assert_true (enabled);
      78    g_assert_null (state_type);
      79    g_assert_null (state);
      80    g_free (name);
      81  
      82    g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
      83    g_assert_false (a.did_run);
      84    g_action_activate (G_ACTION (action), NULL);
      85    g_assert_true (a.did_run);
      86    a.did_run = FALSE;
      87  
      88    g_simple_action_set_enabled (action, FALSE);
      89    g_action_activate (G_ACTION (action), NULL);
      90    g_assert_false (a.did_run);
      91  
      92    if (g_test_undefined ())
      93      {
      94        g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
      95                               "*assertion*g_variant_is_of_type*failed*");
      96        g_action_activate (G_ACTION (action), g_variant_new_string ("xxx"));
      97        g_test_assert_expected_messages ();
      98      }
      99  
     100    g_object_unref (action);
     101    g_assert_false (a.did_run);
     102  
     103    action = g_simple_action_new ("foo", G_VARIANT_TYPE_STRING);
     104    g_assert_true (g_action_get_enabled (G_ACTION (action)));
     105    g_assert_true (g_variant_type_equal (g_action_get_parameter_type (G_ACTION (action)), G_VARIANT_TYPE_STRING));
     106    g_assert_null (g_action_get_state_type (G_ACTION (action)));
     107    g_assert_null (g_action_get_state_hint (G_ACTION (action)));
     108    g_assert_null (g_action_get_state (G_ACTION (action)));
     109  
     110    g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
     111    g_assert_false (a.did_run);
     112    g_action_activate (G_ACTION (action), g_variant_new_string ("Hello world"));
     113    g_assert_true (a.did_run);
     114    g_assert_cmpstr (g_variant_get_string (a.params, NULL), ==, "Hello world");
     115    g_variant_unref (a.params);
     116    a.did_run = FALSE;
     117  
     118    if (g_test_undefined ())
     119      {
     120        g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     121                               "*assertion*!= NULL*failed*");
     122        g_action_activate (G_ACTION (action), NULL);
     123        g_test_assert_expected_messages ();
     124      }
     125  
     126    g_object_unref (action);
     127    g_assert_false (a.did_run);
     128  }
     129  
     130  static void
     131  test_name (void)
     132  {
     133    g_assert_false (g_action_name_is_valid (""));
     134    g_assert_false (g_action_name_is_valid ("("));
     135    g_assert_false (g_action_name_is_valid ("%abc"));
     136    g_assert_false (g_action_name_is_valid ("$x1"));
     137    g_assert_true (g_action_name_is_valid ("abc.def"));
     138    g_assert_true (g_action_name_is_valid ("ABC-DEF"));
     139  }
     140  
     141  static gboolean
     142  strv_strv_cmp (const gchar * const *a,
     143                 const gchar * const *b)
     144  {
     145    guint n;
     146  
     147    for (n = 0; a[n] != NULL; n++)
     148      {
     149         if (!g_strv_contains (b, a[n]))
     150           return FALSE;
     151      }
     152  
     153    for (n = 0; b[n] != NULL; n++)
     154      {
     155         if (!g_strv_contains (a, b[n]))
     156           return FALSE;
     157      }
     158  
     159    return TRUE;
     160  }
     161  
     162  static gboolean
     163  strv_set_equal (const gchar * const *strv, ...)
     164  {
     165    guint count;
     166    va_list list;
     167    const gchar *str;
     168    gboolean res;
     169  
     170    res = TRUE;
     171    count = 0;
     172    va_start (list, strv);
     173    while (1)
     174      {
     175        str = va_arg (list, const gchar *);
     176        if (str == NULL)
     177          break;
     178        if (!g_strv_contains (strv, str))
     179          {
     180            res = FALSE;
     181            break;
     182          }
     183        count++;
     184      }
     185    va_end (list);
     186  
     187    if (res)
     188      res = g_strv_length ((gchar**)strv) == count;
     189  
     190    return res;
     191  }
     192  
     193  static void
     194  ensure_state (GActionGroup *group,
     195                const gchar  *action_name,
     196                const gchar  *expected)
     197  {
     198    GVariant *value;
     199    gchar *printed;
     200  
     201    value = g_action_group_get_action_state (group, action_name);
     202    printed = g_variant_print (value, TRUE);
     203    g_variant_unref (value);
     204  
     205    g_assert_cmpstr (printed, ==, expected);
     206    g_free (printed);
     207  }
     208  
     209  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     210  
     211  static void
     212  test_simple_group (void)
     213  {
     214    GSimpleActionGroup *group;
     215    Activation a = { 0, };
     216    GSimpleAction *simple;
     217    GAction *action;
     218    gchar **actions;
     219    GVariant *state;
     220  
     221    simple = g_simple_action_new ("foo", NULL);
     222    g_signal_connect (simple, "activate", G_CALLBACK (activate), &a);
     223    g_assert_false (a.did_run);
     224    g_action_activate (G_ACTION (simple), NULL);
     225    g_assert_true (a.did_run);
     226    a.did_run = FALSE;
     227  
     228    group = g_simple_action_group_new ();
     229    g_simple_action_group_insert (group, G_ACTION (simple));
     230    g_object_unref (simple);
     231  
     232    g_assert_false (a.did_run);
     233    g_action_group_activate_action (G_ACTION_GROUP (group), "foo", NULL);
     234    g_assert_true (a.did_run);
     235  
     236    simple = g_simple_action_new_stateful ("bar", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi"));
     237    g_simple_action_group_insert (group, G_ACTION (simple));
     238    g_object_unref (simple);
     239  
     240    g_assert_true (g_action_group_has_action (G_ACTION_GROUP (group), "foo"));
     241    g_assert_true (g_action_group_has_action (G_ACTION_GROUP (group), "bar"));
     242    g_assert_false (g_action_group_has_action (G_ACTION_GROUP (group), "baz"));
     243    actions = g_action_group_list_actions (G_ACTION_GROUP (group));
     244    g_assert_cmpint (g_strv_length (actions), ==, 2);
     245    g_assert_true (strv_set_equal ((const gchar * const *) actions, "foo", "bar", NULL));
     246    g_strfreev (actions);
     247    g_assert_true (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "foo"));
     248    g_assert_true (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
     249    g_assert_null (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "foo"));
     250    g_assert_true (g_variant_type_equal (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
     251    g_assert_null (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "foo"));
     252    g_assert_true (g_variant_type_equal (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
     253    g_assert_null (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "foo"));
     254    g_assert_null (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "bar"));
     255    g_assert_null (g_action_group_get_action_state (G_ACTION_GROUP (group), "foo"));
     256    state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
     257    g_assert_true (g_variant_type_equal (g_variant_get_type (state), G_VARIANT_TYPE_STRING));
     258    g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
     259    g_variant_unref (state);
     260  
     261    g_action_group_change_action_state (G_ACTION_GROUP (group), "bar", g_variant_new_string ("boo"));
     262    state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
     263    g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "boo");
     264    g_variant_unref (state);
     265  
     266    action = g_simple_action_group_lookup (group, "bar");
     267    g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
     268    g_assert_false (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
     269  
     270    g_simple_action_group_remove (group, "bar");
     271    action = g_simple_action_group_lookup (group, "foo");
     272    g_assert_cmpstr (g_action_get_name (action), ==, "foo");
     273    action = g_simple_action_group_lookup (group, "bar");
     274    g_assert_null (action);
     275  
     276    simple = g_simple_action_new ("foo", NULL);
     277    g_simple_action_group_insert (group, G_ACTION (simple));
     278    g_object_unref (simple);
     279  
     280    a.did_run = FALSE;
     281    g_object_unref (group);
     282    g_assert_false (a.did_run);
     283  }
     284  
     285  G_GNUC_END_IGNORE_DEPRECATIONS
     286  
     287  static void
     288  test_stateful (void)
     289  {
     290    GSimpleAction *action;
     291    GVariant *state;
     292  
     293    action = g_simple_action_new_stateful ("foo", NULL, g_variant_new_string ("hihi"));
     294    g_assert_true (g_action_get_enabled (G_ACTION (action)));
     295    g_assert_null (g_action_get_parameter_type (G_ACTION (action)));
     296    g_assert_null (g_action_get_state_hint (G_ACTION (action)));
     297    g_assert_true (g_variant_type_equal (g_action_get_state_type (G_ACTION (action)),
     298                                         G_VARIANT_TYPE_STRING));
     299    state = g_action_get_state (G_ACTION (action));
     300    g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
     301    g_variant_unref (state);
     302  
     303    if (g_test_undefined ())
     304      {
     305        GVariant *new_state = g_variant_ref_sink (g_variant_new_int32 (123));
     306        g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     307                               "*assertion*g_variant_is_of_type*failed*");
     308        g_simple_action_set_state (action, new_state);
     309        g_test_assert_expected_messages ();
     310        g_variant_unref (new_state);
     311      }
     312  
     313    g_simple_action_set_state (action, g_variant_new_string ("hello"));
     314    state = g_action_get_state (G_ACTION (action));
     315    g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hello");
     316    g_variant_unref (state);
     317  
     318    g_object_unref (action);
     319  
     320    action = g_simple_action_new ("foo", NULL);
     321  
     322    if (g_test_undefined ())
     323      {
     324        GVariant *new_state = g_variant_ref_sink (g_variant_new_int32 (123));
     325        g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     326                               "*assertion*!= NULL*failed*");
     327        g_simple_action_set_state (action, new_state);
     328        g_test_assert_expected_messages ();
     329        g_variant_unref (new_state);
     330      }
     331  
     332    g_object_unref (action);
     333  }
     334  
     335  static void
     336  test_default_activate (void)
     337  {
     338    GSimpleAction *action;
     339    GVariant *state;
     340  
     341    /* Test changing state via activation with parameter */
     342    action = g_simple_action_new_stateful ("foo", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi"));
     343    g_action_activate (G_ACTION (action), g_variant_new_string ("bye"));
     344    state = g_action_get_state (G_ACTION (action));
     345    g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "bye");
     346    g_variant_unref (state);
     347    g_object_unref (action);
     348  
     349    /* Test toggling a boolean action via activation with no parameter */
     350    action = g_simple_action_new_stateful ("foo", NULL, g_variant_new_boolean (FALSE));
     351    g_action_activate (G_ACTION (action), NULL);
     352    state = g_action_get_state (G_ACTION (action));
     353    g_assert_true (g_variant_get_boolean (state));
     354    g_variant_unref (state);
     355    /* and back again */
     356    g_action_activate (G_ACTION (action), NULL);
     357    state = g_action_get_state (G_ACTION (action));
     358    g_assert_false (g_variant_get_boolean (state));
     359    g_variant_unref (state);
     360    g_object_unref (action);
     361  }
     362  
     363  static gboolean foo_activated = FALSE;
     364  static gboolean bar_activated = FALSE;
     365  
     366  static void
     367  activate_foo (GSimpleAction *simple,
     368                GVariant      *parameter,
     369                gpointer       user_data)
     370  {
     371    g_assert_true (user_data == GINT_TO_POINTER (123));
     372    g_assert_null (parameter);
     373    foo_activated = TRUE;
     374  }
     375  
     376  static void
     377  activate_bar (GSimpleAction *simple,
     378                GVariant      *parameter,
     379                gpointer       user_data)
     380  {
     381    g_assert_true (user_data == GINT_TO_POINTER (123));
     382    g_assert_cmpstr (g_variant_get_string (parameter, NULL), ==, "param");
     383    bar_activated = TRUE;
     384  }
     385  
     386  static void
     387  change_volume_state (GSimpleAction *action,
     388                       GVariant      *value,
     389                       gpointer       user_data)
     390  {
     391    gint requested;
     392  
     393    requested = g_variant_get_int32 (value);
     394  
     395    /* Volume only goes from 0 to 10 */
     396    if (0 <= requested && requested <= 10)
     397      g_simple_action_set_state (action, value);
     398  }
     399  
     400  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     401  
     402  static void
     403  test_entries (void)
     404  {
     405    const GActionEntry entries[] = {
     406      { "foo",    activate_foo, NULL, NULL,    NULL,                { 0 } },
     407      { "bar",    activate_bar, "s",  NULL,    NULL,                { 0 } },
     408      { "toggle", NULL,         NULL, "false", NULL,                { 0 } },
     409      { "volume", NULL,         NULL, "0",     change_volume_state, { 0 } },
     410    };
     411    const GActionEntry entries2[] = {
     412      { "foo",    activate_foo, NULL, NULL,    NULL,                { 0 } },
     413      { "bar",    activate_bar, "s",  NULL,    NULL,                { 0 } },
     414      { NULL },
     415    };
     416    GSimpleActionGroup *actions;
     417    GVariant *state;
     418    GStrv names;
     419  
     420    actions = g_simple_action_group_new ();
     421    g_simple_action_group_add_entries (actions, entries,
     422                                       G_N_ELEMENTS (entries),
     423                                       GINT_TO_POINTER (123));
     424  
     425    g_assert_false (foo_activated);
     426    g_action_group_activate_action (G_ACTION_GROUP (actions), "foo", NULL);
     427    g_assert_true (foo_activated);
     428    foo_activated = FALSE;
     429  
     430    g_assert_false (bar_activated);
     431    g_action_group_activate_action (G_ACTION_GROUP (actions), "bar",
     432                                    g_variant_new_string ("param"));
     433    g_assert_true (bar_activated);
     434    g_assert_false (foo_activated);
     435  
     436    if (g_test_undefined ())
     437      {
     438        const GActionEntry bad_type = {
     439          "bad-type", NULL, "ss", NULL, NULL, { 0 }
     440        };
     441        const GActionEntry bad_state = {
     442          "bad-state", NULL, NULL, "flse", NULL, { 0 }
     443        };
     444  
     445        g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     446                               "*not a valid GVariant type string*");
     447        g_simple_action_group_add_entries (actions, &bad_type, 1, NULL);
     448        g_test_assert_expected_messages ();
     449  
     450        g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
     451                               "*could not parse*");
     452        g_simple_action_group_add_entries (actions, &bad_state, 1, NULL);
     453        g_test_assert_expected_messages ();
     454      }
     455  
     456    state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
     457    g_assert_cmpint (g_variant_get_int32 (state), ==, 0);
     458    g_variant_unref (state);
     459  
     460    /* should change */
     461    g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
     462                                        g_variant_new_int32 (7));
     463    state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
     464    g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
     465    g_variant_unref (state);
     466  
     467    /* should not change */
     468    g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
     469                                        g_variant_new_int32 (11));
     470    state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
     471    g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
     472    g_variant_unref (state);
     473  
     474    names = g_action_group_list_actions (G_ACTION_GROUP (actions));
     475    g_assert_cmpuint (g_strv_length (names), ==, G_N_ELEMENTS (entries));
     476    g_strfreev (names);
     477  
     478    g_action_map_remove_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries));
     479    names = g_action_group_list_actions (G_ACTION_GROUP (actions));
     480    g_assert_cmpuint (g_strv_length (names), ==, 0);
     481    g_strfreev (names);
     482  
     483    /* Check addition and removal of %NULL terminated array */
     484    g_action_map_add_action_entries (G_ACTION_MAP (actions), entries2, -1, NULL);
     485    names = g_action_group_list_actions (G_ACTION_GROUP (actions));
     486    g_assert_cmpuint (g_strv_length (names), ==, G_N_ELEMENTS (entries2) - 1);
     487    g_strfreev (names);
     488    g_action_map_remove_action_entries (G_ACTION_MAP (actions), entries2, -1);
     489    names = g_action_group_list_actions (G_ACTION_GROUP (actions));
     490    g_assert_cmpuint (g_strv_length (names), ==, 0);
     491    g_strfreev (names);
     492  
     493    g_object_unref (actions);
     494  }
     495  
     496  G_GNUC_END_IGNORE_DEPRECATIONS
     497  
     498  static void
     499  test_parse_detailed (void)
     500  {
     501    struct {
     502      const gchar *detailed;
     503      const gchar *expected_name;
     504      const gchar *expected_target;
     505      const gchar *expected_error;
     506      const gchar *detailed_roundtrip;
     507    } testcases[] = {
     508      { "abc",              "abc",    NULL,       NULL,             "abc" },
     509      { " abc",             NULL,     NULL,       "invalid format", NULL },
     510      { " abc",             NULL,     NULL,       "invalid format", NULL },
     511      { "abc:",             NULL,     NULL,       "invalid format", NULL },
     512      { ":abc",             NULL,     NULL,       "invalid format", NULL },
     513      { "abc(",             NULL,     NULL,       "invalid format", NULL },
     514      { "abc)",             NULL,     NULL,       "invalid format", NULL },
     515      { "(abc",             NULL,     NULL,       "invalid format", NULL },
     516      { ")abc",             NULL,     NULL,       "invalid format", NULL },
     517      { "abc::xyz",         "abc",    "'xyz'",    NULL,             "abc::xyz" },
     518      { "abc('xyz')",       "abc",    "'xyz'",    NULL,             "abc::xyz" },
     519      { "abc(42)",          "abc",    "42",       NULL,             "abc(42)" },
     520      { "abc(int32 42)",    "abc",    "42",       NULL,             "abc(42)" },
     521      { "abc(@i 42)",       "abc",    "42",       NULL,             "abc(42)" },
     522      { "abc (42)",         NULL,     NULL,       "invalid format", NULL },
     523      { "abc(42abc)",       NULL,     NULL,       "invalid character in number", NULL },
     524      { "abc(42, 4)",       "abc",    "(42, 4)",  "expected end of input", NULL },
     525      { "abc(42,)",         "abc",    "(42,)",    "expected end of input", NULL }
     526    };
     527    gsize i;
     528  
     529    for (i = 0; i < G_N_ELEMENTS (testcases); i++)
     530      {
     531        GError *error = NULL;
     532        GVariant *target;
     533        gboolean success;
     534        gchar *name;
     535  
     536        success = g_action_parse_detailed_name (testcases[i].detailed, &name, &target, &error);
     537        g_assert_true (success == (error == NULL));
     538        if (success && testcases[i].expected_error)
     539          g_error ("Unexpected success on '%s'.  Expected error containing '%s'",
     540                   testcases[i].detailed, testcases[i].expected_error);
     541  
     542        if (!success && !testcases[i].expected_error)
     543          g_error ("Unexpected failure on '%s': %s", testcases[i].detailed, error->message);
     544  
     545        if (!success)
     546          {
     547            if (!strstr (error->message, testcases[i].expected_error))
     548              g_error ("Failure message '%s' for string '%s' did not contained expected substring '%s'",
     549                       error->message, testcases[i].detailed, testcases[i].expected_error);
     550  
     551            g_error_free (error);
     552            continue;
     553          }
     554  
     555        g_assert_cmpstr (name, ==, testcases[i].expected_name);
     556        g_assert_true ((target == NULL) == (testcases[i].expected_target == NULL));
     557  
     558        if (success)
     559          {
     560            gchar *detailed;
     561  
     562            detailed = g_action_print_detailed_name (name, target);
     563            g_assert_cmpstr (detailed, ==, testcases[i].detailed_roundtrip);
     564            g_free (detailed);
     565          }
     566  
     567        if (target)
     568          {
     569            GVariant *expected;
     570  
     571            expected = g_variant_parse (NULL, testcases[i].expected_target, NULL, NULL, NULL);
     572            g_assert_true (expected);
     573  
     574            g_assert_cmpvariant (expected, target);
     575            g_variant_unref (expected);
     576            g_variant_unref (target);
     577          }
     578  
     579        g_free (name);
     580      }
     581  }
     582  
     583  GHashTable *activation_counts;
     584  
     585  static void
     586  count_activation (const gchar *action)
     587  {
     588    gint count;
     589  
     590    if (activation_counts == NULL)
     591      activation_counts = g_hash_table_new (g_str_hash, g_str_equal);
     592    count = GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
     593    count++;
     594    g_hash_table_insert (activation_counts, (gpointer)action, GINT_TO_POINTER (count));
     595  
     596    g_main_context_wakeup (NULL);
     597  }
     598  
     599  static gint
     600  activation_count (const gchar *action)
     601  {
     602    if (activation_counts == NULL)
     603      return 0;
     604  
     605    return GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
     606  }
     607  
     608  static void
     609  activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
     610  {
     611    count_activation (g_action_get_name (G_ACTION (action)));
     612  }
     613  
     614  static void
     615  activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
     616  {
     617    GVariant *old_state, *new_state;
     618  
     619    count_activation (g_action_get_name (G_ACTION (action)));
     620  
     621    old_state = g_action_get_state (G_ACTION (action));
     622    new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
     623    g_simple_action_set_state (action, new_state);
     624    g_variant_unref (old_state);
     625  }
     626  
     627  static void
     628  activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
     629  {
     630    GVariant *new_state;
     631  
     632    count_activation (g_action_get_name (G_ACTION (action)));
     633  
     634    new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
     635    g_simple_action_set_state (action, new_state);
     636  }
     637  
     638  static gboolean
     639  compare_action_groups (GActionGroup *a, GActionGroup *b)
     640  {
     641    gchar **alist;
     642    gchar **blist;
     643    gint i;
     644    gboolean equal;
     645    gboolean ares, bres;
     646    gboolean aenabled, benabled;
     647    const GVariantType *aparameter_type, *bparameter_type;
     648    const GVariantType *astate_type, *bstate_type;
     649    GVariant *astate_hint, *bstate_hint;
     650    GVariant *astate, *bstate;
     651  
     652    alist = g_action_group_list_actions (a);
     653    blist = g_action_group_list_actions (b);
     654    equal = strv_strv_cmp ((const gchar * const *) alist, (const gchar * const *) blist);
     655  
     656    for (i = 0; equal && alist[i]; i++)
     657      {
     658        ares = g_action_group_query_action (a, alist[i], &aenabled, &aparameter_type, &astate_type, &astate_hint, &astate);
     659        bres = g_action_group_query_action (b, alist[i], &benabled, &bparameter_type, &bstate_type, &bstate_hint, &bstate);
     660  
     661        if (ares && bres)
     662          {
     663            equal = equal && (aenabled == benabled);
     664            equal = equal && ((!aparameter_type && !bparameter_type) || g_variant_type_equal (aparameter_type, bparameter_type));
     665            equal = equal && ((!astate_type && !bstate_type) || g_variant_type_equal (astate_type, bstate_type));
     666            equal = equal && ((!astate_hint && !bstate_hint) || g_variant_equal (astate_hint, bstate_hint));
     667            equal = equal && ((!astate && !bstate) || g_variant_equal (astate, bstate));
     668  
     669            if (astate_hint)
     670              g_variant_unref (astate_hint);
     671            if (bstate_hint)
     672              g_variant_unref (bstate_hint);
     673            if (astate)
     674              g_variant_unref (astate);
     675            if (bstate)
     676              g_variant_unref (bstate);
     677          }
     678        else
     679          equal = FALSE;
     680      }
     681  
     682    g_strfreev (alist);
     683    g_strfreev (blist);
     684  
     685    return equal;
     686  }
     687  
     688  static gboolean
     689  timeout_cb (gpointer user_data)
     690  {
     691    gboolean *timed_out = user_data;
     692  
     693    g_assert_false (*timed_out);
     694    *timed_out = TRUE;
     695    g_main_context_wakeup (NULL);
     696  
     697    return G_SOURCE_REMOVE;
     698  }
     699  
     700  static GActionEntry exported_entries[] = {
     701    { "undo",  activate_action, NULL, NULL,      NULL, { 0 } },
     702    { "redo",  activate_action, NULL, NULL,      NULL, { 0 } },
     703    { "cut",   activate_action, NULL, NULL,      NULL, { 0 } },
     704    { "copy",  activate_action, NULL, NULL,      NULL, { 0 } },
     705    { "paste", activate_action, NULL, NULL,      NULL, { 0 } },
     706    { "bold",  activate_toggle, NULL, "true",    NULL, { 0 } },
     707    { "lang",  activate_radio,  "s",  "'latin'", NULL, { 0 } },
     708  };
     709  
     710  static void
     711  async_result_cb (GObject      *source,
     712                   GAsyncResult *res,
     713                   gpointer      user_data)
     714  {
     715    GAsyncResult **result_out = user_data;
     716  
     717    g_assert_null (*result_out);
     718    *result_out = g_object_ref (res);
     719  
     720    g_main_context_wakeup (NULL);
     721  }
     722  
     723  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     724  
     725  static void
     726  action_added_removed_cb (GActionGroup *action_group,
     727                           char         *action_name,
     728                           gpointer      user_data)
     729  {
     730    guint *counter = user_data;
     731  
     732    *counter = *counter + 1;
     733    g_main_context_wakeup (NULL);
     734  }
     735  
     736  static void
     737  action_enabled_changed_cb (GActionGroup *action_group,
     738                             char         *action_name,
     739                             gboolean      enabled,
     740                             gpointer      user_data)
     741  {
     742    guint *counter = user_data;
     743  
     744    *counter = *counter + 1;
     745    g_main_context_wakeup (NULL);
     746  }
     747  
     748  static void
     749  action_state_changed_cb (GActionGroup *action_group,
     750                           char         *action_name,
     751                           GVariant     *value,
     752                           gpointer      user_data)
     753  {
     754    guint *counter = user_data;
     755  
     756    *counter = *counter + 1;
     757    g_main_context_wakeup (NULL);
     758  }
     759  
     760  static void
     761  test_dbus_export (void)
     762  {
     763    GDBusConnection *bus;
     764    GSimpleActionGroup *group;
     765    GDBusActionGroup *proxy;
     766    GSimpleAction *action;
     767    GError *error = NULL;
     768    GVariant *v;
     769    guint id;
     770    gchar **actions;
     771    guint n_actions_added = 0, n_actions_enabled_changed = 0, n_actions_removed = 0, n_actions_state_changed = 0;
     772    gulong added_signal_id, enabled_changed_signal_id, removed_signal_id, state_changed_signal_id;
     773    gboolean enabled;
     774    gchar *param;
     775    GVariantIter *iter;
     776    GAsyncResult *async_result = NULL;
     777  
     778    session_bus_up ();
     779    bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
     780  
     781    group = g_simple_action_group_new ();
     782    g_simple_action_group_add_entries (group,
     783                                       exported_entries,
     784                                       G_N_ELEMENTS (exported_entries),
     785                                       NULL);
     786  
     787    id = g_dbus_connection_export_action_group (bus, "/", G_ACTION_GROUP (group), &error);
     788    g_assert_no_error (error);
     789  
     790    proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
     791    added_signal_id = g_signal_connect (proxy, "action-added", G_CALLBACK (action_added_removed_cb), &n_actions_added);
     792    enabled_changed_signal_id = g_signal_connect (proxy, "action-enabled-changed", G_CALLBACK (action_enabled_changed_cb), &n_actions_enabled_changed);
     793    removed_signal_id = g_signal_connect (proxy, "action-removed", G_CALLBACK (action_added_removed_cb), &n_actions_removed);
     794    state_changed_signal_id = g_signal_connect (proxy, "action-state-changed", G_CALLBACK (action_state_changed_cb), &n_actions_state_changed);
     795  
     796    actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
     797    g_assert_cmpint (g_strv_length (actions), ==, 0);
     798    g_strfreev (actions);
     799  
     800    /* Actions are queried from the bus asynchronously after the first
     801     * list_actions() call. Wait for the expected signals then check again. */
     802    while (n_actions_added < G_N_ELEMENTS (exported_entries))
     803      g_main_context_iteration (NULL, TRUE);
     804  
     805    actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
     806    g_assert_cmpint (g_strv_length (actions), ==, G_N_ELEMENTS (exported_entries));
     807    g_strfreev (actions);
     808  
     809    /* check that calling "List" works too */
     810    g_dbus_connection_call (bus,
     811                            g_dbus_connection_get_unique_name (bus),
     812                            "/",
     813                            "org.gtk.Actions",
     814                            "List",
     815                            NULL,
     816                            NULL,
     817                            0,
     818                            G_MAXINT,
     819                            NULL,
     820                            async_result_cb,
     821                            &async_result);
     822  
     823    while (async_result == NULL)
     824      g_main_context_iteration (NULL, TRUE);
     825  
     826    v = g_dbus_connection_call_finish (bus, async_result, &error);
     827    g_assert_no_error (error);
     828    g_assert_nonnull (v);
     829    g_variant_get (v, "(^a&s)", &actions);
     830    g_assert_cmpuint (g_strv_length (actions), ==, G_N_ELEMENTS (exported_entries));
     831    g_free (actions);
     832    g_variant_unref (v);
     833    g_clear_object (&async_result);
     834  
     835    /* check that calling "Describe" works */
     836    g_dbus_connection_call (bus,
     837                            g_dbus_connection_get_unique_name (bus),
     838                            "/",
     839                            "org.gtk.Actions",
     840                            "Describe",
     841                            g_variant_new ("(s)", "copy"),
     842                            NULL,
     843                            0,
     844                            G_MAXINT,
     845                            NULL,
     846                            async_result_cb,
     847                            &async_result);
     848  
     849    while (async_result == NULL)
     850      g_main_context_iteration (NULL, TRUE);
     851  
     852    v = g_dbus_connection_call_finish (bus, async_result, &error);
     853    g_assert_no_error (error);
     854    g_assert_nonnull (v);
     855    /* FIXME: there's an extra level of tuplelization in here */
     856    g_variant_get (v, "((bgav))", &enabled, &param, &iter);
     857    g_assert_true (enabled);
     858    g_assert_cmpstr (param, ==, "");
     859    g_assert_cmpint (g_variant_iter_n_children (iter), ==, 0);
     860    g_free (param);
     861    g_variant_iter_free (iter);
     862    g_variant_unref (v);
     863    g_clear_object (&async_result);
     864  
     865    /* check that activating a parameterless action over D-Bus works */
     866    g_assert_cmpint (activation_count ("undo"), ==, 0);
     867  
     868    g_dbus_connection_call (bus,
     869                            g_dbus_connection_get_unique_name (bus),
     870                            "/",
     871                            "org.gtk.Actions",
     872                            "Activate",
     873                            g_variant_new ("(sava{sv})", "undo", NULL, NULL),
     874                            NULL,
     875                            0,
     876                            G_MAXINT,
     877                            NULL,
     878                            async_result_cb,
     879                            &async_result);
     880  
     881    while (async_result == NULL)
     882      g_main_context_iteration (NULL, TRUE);
     883  
     884    v = g_dbus_connection_call_finish (bus, async_result, &error);
     885    g_assert_no_error (error);
     886    g_assert_nonnull (v);
     887    g_assert_true (g_variant_is_of_type (v, G_VARIANT_TYPE_UNIT));
     888    g_variant_unref (v);
     889    g_clear_object (&async_result);
     890  
     891    g_assert_cmpint (activation_count ("undo"), ==, 1);
     892  
     893    /* check that activating a parameterful action over D-Bus works */
     894    g_assert_cmpint (activation_count ("lang"), ==, 0);
     895    ensure_state (G_ACTION_GROUP (group), "lang", "'latin'");
     896  
     897    g_dbus_connection_call (bus,
     898                            g_dbus_connection_get_unique_name (bus),
     899                            "/",
     900                            "org.gtk.Actions",
     901                            "Activate",
     902                            g_variant_new ("(s@ava{sv})", "lang", g_variant_new_parsed ("[<'spanish'>]"), NULL),
     903                            NULL,
     904                            0,
     905                            G_MAXINT,
     906                            NULL,
     907                            async_result_cb,
     908                            &async_result);
     909  
     910    while (async_result == NULL)
     911      g_main_context_iteration (NULL, TRUE);
     912  
     913    v = g_dbus_connection_call_finish (bus, async_result, &error);
     914    g_assert_no_error (error);
     915    g_assert_nonnull (v);
     916    g_assert_true (g_variant_is_of_type (v, G_VARIANT_TYPE_UNIT));
     917    g_variant_unref (v);
     918    g_clear_object (&async_result);
     919  
     920    g_assert_cmpint (activation_count ("lang"), ==, 1);
     921    ensure_state (G_ACTION_GROUP (group), "lang", "'spanish'");
     922  
     923    /* check that various error conditions are rejected */
     924    struct
     925      {
     926        const gchar *action_name;
     927        GVariant *parameter;  /* (owned floating) (nullable) */
     928      }
     929    activate_error_conditions[] =
     930      {
     931        { "nope", NULL },  /* non-existent action */
     932        { "lang", g_variant_new_parsed ("[<@u 4>]") },  /* wrong parameter type */
     933        { "lang", NULL },  /* parameter missing */
     934        { "undo", g_variant_new_parsed ("[<'silly'>]") },  /* extraneous parameter */
     935      };
     936  
     937    for (gsize i = 0; i < G_N_ELEMENTS (activate_error_conditions); i++)
     938      {
     939        GVariant *parameter = g_steal_pointer (&activate_error_conditions[i].parameter);
     940        const gchar *type_string = (parameter != NULL) ? "(s@ava{sv})" : "(sava{sv})";
     941  
     942        g_dbus_connection_call (bus,
     943                                g_dbus_connection_get_unique_name (bus),
     944                                "/",
     945                                "org.gtk.Actions",
     946                                "Activate",
     947                                g_variant_new (type_string,
     948                                               activate_error_conditions[i].action_name,
     949                                               g_steal_pointer (&parameter),
     950                                               NULL),
     951                                NULL,
     952                                0,
     953                                G_MAXINT,
     954                                NULL,
     955                                async_result_cb,
     956                                &async_result);
     957  
     958        while (async_result == NULL)
     959          g_main_context_iteration (NULL, TRUE);
     960  
     961        v = g_dbus_connection_call_finish (bus, async_result, &error);
     962        g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
     963        g_assert_null (v);
     964        g_clear_error (&error);
     965        g_clear_object (&async_result);
     966      }
     967  
     968    /* check that setting an action’s state over D-Bus works */
     969    g_assert_cmpint (activation_count ("lang"), ==, 1);
     970    ensure_state (G_ACTION_GROUP (group), "lang", "'spanish'");
     971  
     972    g_dbus_connection_call (bus,
     973                            g_dbus_connection_get_unique_name (bus),
     974                            "/",
     975                            "org.gtk.Actions",
     976                            "SetState",
     977                            g_variant_new ("(sva{sv})", "lang", g_variant_new_string ("portuguese"), NULL),
     978                            NULL,
     979                            0,
     980                            G_MAXINT,
     981                            NULL,
     982                            async_result_cb,
     983                            &async_result);
     984  
     985    while (async_result == NULL)
     986      g_main_context_iteration (NULL, TRUE);
     987  
     988    v = g_dbus_connection_call_finish (bus, async_result, &error);
     989    g_assert_no_error (error);
     990    g_assert_nonnull (v);
     991    g_assert_true (g_variant_is_of_type (v, G_VARIANT_TYPE_UNIT));
     992    g_variant_unref (v);
     993    g_clear_object (&async_result);
     994  
     995    g_assert_cmpint (activation_count ("lang"), ==, 1);
     996    ensure_state (G_ACTION_GROUP (group), "lang", "'portuguese'");
     997  
     998    /* check that various error conditions are rejected */
     999    struct
    1000      {
    1001        const gchar *action_name;
    1002        GVariant *state;  /* (owned floating) (not nullable) */
    1003      }
    1004    set_state_error_conditions[] =
    1005      {
    1006        { "nope", g_variant_new_string ("hello") },  /* non-existent action */
    1007        { "undo", g_variant_new_string ("not stateful") },  /* not a stateful action */
    1008        { "lang", g_variant_new_uint32 (3) },  /* wrong state type */
    1009      };
    1010  
    1011    for (gsize i = 0; i < G_N_ELEMENTS (set_state_error_conditions); i++)
    1012      {
    1013        g_dbus_connection_call (bus,
    1014                                g_dbus_connection_get_unique_name (bus),
    1015                                "/",
    1016                                "org.gtk.Actions",
    1017                                "SetState",
    1018                                g_variant_new ("(s@va{sv})",
    1019                                               set_state_error_conditions[i].action_name,
    1020                                               g_variant_new_variant (g_steal_pointer (&set_state_error_conditions[i].state)),
    1021                                               NULL),
    1022                                NULL,
    1023                                0,
    1024                                G_MAXINT,
    1025                                NULL,
    1026                                async_result_cb,
    1027                                &async_result);
    1028  
    1029        while (async_result == NULL)
    1030          g_main_context_iteration (NULL, TRUE);
    1031  
    1032        v = g_dbus_connection_call_finish (bus, async_result, &error);
    1033        g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
    1034        g_assert_null (v);
    1035        g_clear_error (&error);
    1036        g_clear_object (&async_result);
    1037      }
    1038  
    1039    /* test that the initial transfer works */
    1040    g_assert_true (G_IS_DBUS_ACTION_GROUP (proxy));
    1041    while (!compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)))
    1042      g_main_context_iteration (NULL, TRUE);
    1043    n_actions_state_changed = 0;
    1044  
    1045    /* test that various changes get propagated from group to proxy */
    1046    n_actions_added = 0;
    1047    action = g_simple_action_new_stateful ("italic", NULL, g_variant_new_boolean (FALSE));
    1048    g_simple_action_group_insert (group, G_ACTION (action));
    1049    g_object_unref (action);
    1050  
    1051    while (n_actions_added == 0)
    1052      g_main_context_iteration (NULL, TRUE);
    1053  
    1054    g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
    1055  
    1056    action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "cut"));
    1057    g_simple_action_set_enabled (action, FALSE);
    1058  
    1059    while (n_actions_enabled_changed == 0)
    1060      g_main_context_iteration (NULL, TRUE);
    1061  
    1062    g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
    1063  
    1064    action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "bold"));
    1065    g_simple_action_set_state (action, g_variant_new_boolean (FALSE));
    1066  
    1067    while (n_actions_state_changed == 0)
    1068      g_main_context_iteration (NULL, TRUE);
    1069  
    1070    g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
    1071  
    1072    g_simple_action_group_remove (group, "italic");
    1073  
    1074    while (n_actions_removed == 0)
    1075      g_main_context_iteration (NULL, TRUE);
    1076  
    1077    g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
    1078  
    1079    /* test that activations and state changes propagate the other way */
    1080    n_actions_state_changed = 0;
    1081    g_assert_cmpint (activation_count ("copy"), ==, 0);
    1082    g_action_group_activate_action (G_ACTION_GROUP (proxy), "copy", NULL);
    1083  
    1084    while (activation_count ("copy") == 0)
    1085      g_main_context_iteration (NULL, TRUE);
    1086  
    1087    g_assert_cmpint (activation_count ("copy"), ==, 1);
    1088    g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
    1089  
    1090    n_actions_state_changed = 0;
    1091    g_assert_cmpint (activation_count ("bold"), ==, 0);
    1092    g_action_group_activate_action (G_ACTION_GROUP (proxy), "bold", NULL);
    1093  
    1094    while (n_actions_state_changed == 0)
    1095      g_main_context_iteration (NULL, TRUE);
    1096  
    1097    g_assert_cmpint (activation_count ("bold"), ==, 1);
    1098    g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
    1099    v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
    1100    g_assert_true (g_variant_get_boolean (v));
    1101    g_variant_unref (v);
    1102  
    1103    n_actions_state_changed = 0;
    1104    g_action_group_change_action_state (G_ACTION_GROUP (proxy), "bold", g_variant_new_boolean (FALSE));
    1105  
    1106    while (n_actions_state_changed == 0)
    1107      g_main_context_iteration (NULL, TRUE);
    1108  
    1109    g_assert_cmpint (activation_count ("bold"), ==, 1);
    1110    g_assert_true (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
    1111    v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
    1112    g_assert_false (g_variant_get_boolean (v));
    1113    g_variant_unref (v);
    1114  
    1115    g_dbus_connection_unexport_action_group (bus, id);
    1116  
    1117    g_signal_handler_disconnect (proxy, added_signal_id);
    1118    g_signal_handler_disconnect (proxy, enabled_changed_signal_id);
    1119    g_signal_handler_disconnect (proxy, removed_signal_id);
    1120    g_signal_handler_disconnect (proxy, state_changed_signal_id);
    1121    g_object_unref (proxy);
    1122    g_object_unref (group);
    1123    g_object_unref (bus);
    1124  
    1125    session_bus_down ();
    1126  }
    1127  
    1128  static gpointer
    1129  do_export (gpointer data)
    1130  {
    1131    GActionGroup *group = data;
    1132    GMainContext *ctx;
    1133    gint i;
    1134    GError *error = NULL;
    1135    guint id;
    1136    GDBusConnection *bus;
    1137    GAction *action;
    1138    gchar *path;
    1139  
    1140    ctx = g_main_context_new ();
    1141  
    1142    g_main_context_push_thread_default (ctx);
    1143  
    1144    bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
    1145    path = g_strdup_printf("/%p", data);
    1146  
    1147    for (i = 0; i < 10000; i++)
    1148      {
    1149        id = g_dbus_connection_export_action_group (bus, path, G_ACTION_GROUP (group), &error);
    1150        g_assert_no_error (error);
    1151  
    1152        action = g_simple_action_group_lookup (G_SIMPLE_ACTION_GROUP (group), "a");
    1153        g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
    1154                                     !g_action_get_enabled (action));
    1155  
    1156        g_dbus_connection_unexport_action_group (bus, id);
    1157  
    1158        while (g_main_context_iteration (ctx, FALSE));
    1159      }
    1160  
    1161    g_free (path);
    1162    g_object_unref (bus);
    1163  
    1164    g_main_context_pop_thread_default (ctx);
    1165  
    1166    g_main_context_unref (ctx);
    1167  
    1168    return NULL;
    1169  }
    1170  
    1171  static void
    1172  test_dbus_threaded (void)
    1173  {
    1174    GSimpleActionGroup *group[10];
    1175    GThread *export[10];
    1176    static GActionEntry entries[] = {
    1177      { "a",  activate_action, NULL, NULL, NULL, { 0 } },
    1178      { "b",  activate_action, NULL, NULL, NULL, { 0 } },
    1179    };
    1180    gint i;
    1181  
    1182    session_bus_up ();
    1183  
    1184    for (i = 0; i < 10; i++)
    1185      {
    1186        group[i] = g_simple_action_group_new ();
    1187        g_simple_action_group_add_entries (group[i], entries, G_N_ELEMENTS (entries), NULL);
    1188        export[i] = g_thread_new ("export", do_export, group[i]);
    1189      }
    1190  
    1191    for (i = 0; i < 10; i++)
    1192      g_thread_join (export[i]);
    1193  
    1194    for (i = 0; i < 10; i++)
    1195      g_object_unref (group[i]);
    1196  
    1197    session_bus_down ();
    1198  }
    1199  
    1200  G_GNUC_END_IGNORE_DEPRECATIONS
    1201  
    1202  static void
    1203  test_bug679509 (void)
    1204  {
    1205    GDBusConnection *bus;
    1206    GDBusActionGroup *proxy;
    1207    gboolean timed_out = FALSE;
    1208  
    1209    session_bus_up ();
    1210    bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
    1211  
    1212    proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
    1213    g_strfreev (g_action_group_list_actions (G_ACTION_GROUP (proxy)));
    1214    g_object_unref (proxy);
    1215  
    1216    g_timeout_add (100, timeout_cb, &timed_out);
    1217    while (!timed_out)
    1218      g_main_context_iteration (NULL, TRUE);
    1219  
    1220    g_object_unref (bus);
    1221  
    1222    session_bus_down ();
    1223  }
    1224  
    1225  static gchar *state_change_log;
    1226  
    1227  static void
    1228  state_changed (GActionGroup *group,
    1229                 const gchar  *action_name,
    1230                 GVariant     *value,
    1231                 gpointer      user_data)
    1232  {
    1233    GString *string;
    1234  
    1235    g_assert_false (state_change_log);
    1236  
    1237    string = g_string_new (action_name);
    1238    g_string_append_c (string, ':');
    1239    g_variant_print_string (value, string, TRUE);
    1240    state_change_log = g_string_free (string, FALSE);
    1241  }
    1242  
    1243  static void
    1244  verify_changed (const gchar *log_entry)
    1245  {
    1246    g_assert_cmpstr (state_change_log, ==, log_entry);
    1247    g_clear_pointer (&state_change_log, g_free);
    1248  }
    1249  
    1250  static void
    1251  test_property_actions (void)
    1252  {
    1253    GSimpleActionGroup *group;
    1254    GPropertyAction *action;
    1255    GSocketClient *client;
    1256    GApplication *app;
    1257    gchar *name;
    1258    GVariantType *ptype, *stype;
    1259    gboolean enabled;
    1260    GVariant *state;
    1261  
    1262    group = g_simple_action_group_new ();
    1263    g_signal_connect (group, "action-state-changed", G_CALLBACK (state_changed), NULL);
    1264  
    1265    client = g_socket_client_new ();
    1266    app = g_application_new ("org.gtk.test", 0);
    1267  
    1268    /* string... */
    1269    action = g_property_action_new ("app-id", app, "application-id");
    1270    g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
    1271    g_object_unref (action);
    1272  
    1273    /* uint... */
    1274    action = g_property_action_new ("keepalive", app, "inactivity-timeout");
    1275    g_object_get (action, "name", &name, "parameter-type", &ptype, "enabled", &enabled, "state-type", &stype, "state", &state, NULL);
    1276    g_assert_cmpstr (name, ==, "keepalive");
    1277    g_assert_true (enabled);
    1278    g_free (name);
    1279    g_variant_type_free (ptype);
    1280    g_variant_type_free (stype);
    1281    g_variant_unref (state);
    1282  
    1283    g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
    1284    g_object_unref (action);
    1285  
    1286    /* bool... */
    1287    action = g_property_action_new ("tls", client, "tls");
    1288    g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
    1289    g_object_unref (action);
    1290  
    1291    /* inverted */
    1292    action = g_object_new (G_TYPE_PROPERTY_ACTION,
    1293                           "name", "disable-proxy",
    1294                           "object", client,
    1295                           "property-name", "enable-proxy",
    1296                           "invert-boolean", TRUE,
    1297                           NULL);
    1298    g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
    1299    g_object_unref (action);
    1300  
    1301    /* enum... */
    1302    action = g_property_action_new ("type", client, "type");
    1303    g_action_map_add_action (G_ACTION_MAP (group), G_ACTION (action));
    1304    g_object_unref (action);
    1305  
    1306    /* the objects should be held alive by the actions... */
    1307    g_object_unref (client);
    1308    g_object_unref (app);
    1309  
    1310    ensure_state (G_ACTION_GROUP (group), "app-id", "'org.gtk.test'");
    1311    ensure_state (G_ACTION_GROUP (group), "keepalive", "uint32 0");
    1312    ensure_state (G_ACTION_GROUP (group), "tls", "false");
    1313    ensure_state (G_ACTION_GROUP (group), "disable-proxy", "false");
    1314    ensure_state (G_ACTION_GROUP (group), "type", "'stream'");
    1315  
    1316    verify_changed (NULL);
    1317  
    1318    /* some string tests... */
    1319    g_action_group_change_action_state (G_ACTION_GROUP (group), "app-id", g_variant_new ("s", "org.gtk.test2"));
    1320    verify_changed ("app-id:'org.gtk.test2'");
    1321    g_assert_cmpstr (g_application_get_application_id (app), ==, "org.gtk.test2");
    1322    ensure_state (G_ACTION_GROUP (group), "app-id", "'org.gtk.test2'");
    1323  
    1324    g_action_group_activate_action (G_ACTION_GROUP (group), "app-id", g_variant_new ("s", "org.gtk.test3"));
    1325    verify_changed ("app-id:'org.gtk.test3'");
    1326    g_assert_cmpstr (g_application_get_application_id (app), ==, "org.gtk.test3");
    1327    ensure_state (G_ACTION_GROUP (group), "app-id", "'org.gtk.test3'");
    1328  
    1329    g_application_set_application_id (app, "org.gtk.test");
    1330    verify_changed ("app-id:'org.gtk.test'");
    1331    ensure_state (G_ACTION_GROUP (group), "app-id", "'org.gtk.test'");
    1332  
    1333    /* uint tests */
    1334    g_action_group_change_action_state (G_ACTION_GROUP (group), "keepalive", g_variant_new ("u", 1234));
    1335    verify_changed ("keepalive:uint32 1234");
    1336    g_assert_cmpuint (g_application_get_inactivity_timeout (app), ==, 1234);
    1337    ensure_state (G_ACTION_GROUP (group), "keepalive", "uint32 1234");
    1338  
    1339    g_action_group_activate_action (G_ACTION_GROUP (group), "keepalive", g_variant_new ("u", 5678));
    1340    verify_changed ("keepalive:uint32 5678");
    1341    g_assert_cmpuint (g_application_get_inactivity_timeout (app), ==, 5678);
    1342    ensure_state (G_ACTION_GROUP (group), "keepalive", "uint32 5678");
    1343  
    1344    g_application_set_inactivity_timeout (app, 0);
    1345    verify_changed ("keepalive:uint32 0");
    1346    ensure_state (G_ACTION_GROUP (group), "keepalive", "uint32 0");
    1347  
    1348    /* bool tests */
    1349    g_action_group_change_action_state (G_ACTION_GROUP (group), "tls", g_variant_new ("b", TRUE));
    1350    verify_changed ("tls:true");
    1351    g_assert_true (g_socket_client_get_tls (client));
    1352    ensure_state (G_ACTION_GROUP (group), "tls", "true");
    1353  
    1354    g_action_group_change_action_state (G_ACTION_GROUP (group), "disable-proxy", g_variant_new ("b", TRUE));
    1355    verify_changed ("disable-proxy:true");
    1356    ensure_state (G_ACTION_GROUP (group), "disable-proxy", "true");
    1357    g_assert_false (g_socket_client_get_enable_proxy (client));
    1358  
    1359    /* test toggle true->false */
    1360    g_action_group_activate_action (G_ACTION_GROUP (group), "tls", NULL);
    1361    verify_changed ("tls:false");
    1362    g_assert_false (g_socket_client_get_tls (client));
    1363    ensure_state (G_ACTION_GROUP (group), "tls", "false");
    1364  
    1365    /* and now back false->true */
    1366    g_action_group_activate_action (G_ACTION_GROUP (group), "tls", NULL);
    1367    verify_changed ("tls:true");
    1368    g_assert_true (g_socket_client_get_tls (client));
    1369    ensure_state (G_ACTION_GROUP (group), "tls", "true");
    1370  
    1371    g_socket_client_set_tls (client, FALSE);
    1372    verify_changed ("tls:false");
    1373    ensure_state (G_ACTION_GROUP (group), "tls", "false");
    1374  
    1375    /* now do the same for the inverted action */
    1376    g_action_group_activate_action (G_ACTION_GROUP (group), "disable-proxy", NULL);
    1377    verify_changed ("disable-proxy:false");
    1378    g_assert_true (g_socket_client_get_enable_proxy (client));
    1379    ensure_state (G_ACTION_GROUP (group), "disable-proxy", "false");
    1380  
    1381    g_action_group_activate_action (G_ACTION_GROUP (group), "disable-proxy", NULL);
    1382    verify_changed ("disable-proxy:true");
    1383    g_assert_false (g_socket_client_get_enable_proxy (client));
    1384    ensure_state (G_ACTION_GROUP (group), "disable-proxy", "true");
    1385  
    1386    g_socket_client_set_enable_proxy (client, TRUE);
    1387    verify_changed ("disable-proxy:false");
    1388    ensure_state (G_ACTION_GROUP (group), "disable-proxy", "false");
    1389  
    1390    /* enum tests */
    1391    g_action_group_change_action_state (G_ACTION_GROUP (group), "type", g_variant_new ("s", "datagram"));
    1392    verify_changed ("type:'datagram'");
    1393    g_assert_cmpint (g_socket_client_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM);
    1394    ensure_state (G_ACTION_GROUP (group), "type", "'datagram'");
    1395  
    1396    g_action_group_activate_action (G_ACTION_GROUP (group), "type", g_variant_new ("s", "stream"));
    1397    verify_changed ("type:'stream'");
    1398    g_assert_cmpint (g_socket_client_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM);
    1399    ensure_state (G_ACTION_GROUP (group), "type", "'stream'");
    1400  
    1401    g_socket_client_set_socket_type (client, G_SOCKET_TYPE_SEQPACKET);
    1402    verify_changed ("type:'seqpacket'");
    1403    ensure_state (G_ACTION_GROUP (group), "type", "'seqpacket'");
    1404  
    1405    /* Check some error cases... */
    1406    g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_CRITICAL, "*non-existent*");
    1407    action = g_property_action_new ("foo", app, "xyz");
    1408    g_test_assert_expected_messages ();
    1409    g_object_unref (action);
    1410  
    1411    g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_CRITICAL, "*writable*");
    1412    action = g_property_action_new ("foo", app, "is-registered");
    1413    g_test_assert_expected_messages ();
    1414    g_object_unref (action);
    1415  
    1416    g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_CRITICAL, "*type 'GSocketAddress'*");
    1417    action = g_property_action_new ("foo", client, "local-address");
    1418    g_test_assert_expected_messages ();
    1419    g_object_unref (action);
    1420  
    1421    g_object_unref (group);
    1422  }
    1423  
    1424  static void
    1425  test_property_actions_no_properties (void)
    1426  {
    1427    GPropertyAction *action;
    1428  
    1429    g_test_expect_message ("GLib-GIO", G_LOG_LEVEL_CRITICAL, "*Attempted to use an empty property name for GPropertyAction*");
    1430    action = (GPropertyAction*) g_object_new_with_properties (G_TYPE_PROPERTY_ACTION, 0, NULL, NULL);
    1431  
    1432    g_test_assert_expected_messages ();
    1433    g_assert_true (G_IS_PROPERTY_ACTION (action));
    1434  
    1435    g_object_unref (action);
    1436  }
    1437  
    1438  int
    1439  main (int argc, char **argv)
    1440  {
    1441    g_test_init (&argc, &argv, NULL);
    1442  
    1443    g_test_add_func ("/actions/basic", test_basic);
    1444    g_test_add_func ("/actions/name", test_name);
    1445    g_test_add_func ("/actions/simplegroup", test_simple_group);
    1446    g_test_add_func ("/actions/stateful", test_stateful);
    1447    g_test_add_func ("/actions/default-activate", test_default_activate);
    1448    g_test_add_func ("/actions/entries", test_entries);
    1449    g_test_add_func ("/actions/parse-detailed", test_parse_detailed);
    1450    g_test_add_func ("/actions/dbus/export", test_dbus_export);
    1451    g_test_add_func ("/actions/dbus/threaded", test_dbus_threaded);
    1452    g_test_add_func ("/actions/dbus/bug679509", test_bug679509);
    1453    g_test_add_func ("/actions/property", test_property_actions);
    1454    g_test_add_func ("/actions/no-properties", test_property_actions_no_properties);
    1455  
    1456    return g_test_run ();
    1457  }