actions.c 25 KB
Newer Older
1 2 3
#include <gio/gio.h>
#include <stdlib.h>

4 5
#include "gdbus-sessionbus.h"

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
typedef struct
{
  GVariant *params;
  gboolean did_run;
} Activation;

static void
activate (GAction  *action,
          GVariant *parameter,
          gpointer  user_data)
{
  Activation *activation = user_data;

  if (parameter)
    activation->params = g_variant_ref (parameter);
  else
    activation->params = NULL;
  activation->did_run = TRUE;
}

static void
test_basic (void)
{
  Activation a = { 0, };
30 31
  GSimpleAction *action;
  gchar *name;
32 33 34 35
  GVariantType *parameter_type;
  gboolean enabled;
  GVariantType *state_type;
  GVariant *state;
36

37 38 39 40 41 42
  action = g_simple_action_new ("foo", NULL);
  g_assert (g_action_get_enabled (G_ACTION (action)));
  g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL);
  g_assert (g_action_get_state_type (G_ACTION (action)) == NULL);
  g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
  g_assert (g_action_get_state (G_ACTION (action)) == NULL);
43 44 45 46 47 48 49 50 51 52 53 54
  g_object_get (action,
                "name", &name,
                "parameter-type", &parameter_type,
                "enabled", &enabled,
                "state-type", &state_type,
                "state", &state,
                 NULL);
  g_assert_cmpstr (name, ==, "foo");
  g_assert (parameter_type == NULL);
  g_assert (enabled);
  g_assert (state_type == NULL);
  g_assert (state == NULL);
55
  g_free (name);
56

57 58
  g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
  g_assert (!a.did_run);
59
  g_action_activate (G_ACTION (action), NULL);
60 61 62
  g_assert (a.did_run);
  a.did_run = FALSE;

63 64
  g_simple_action_set_enabled (action, FALSE);
  g_action_activate (G_ACTION (action), NULL);
65 66
  g_assert (!a.did_run);

67
  if (g_test_undefined ())
68
    {
69 70 71 72
      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
                             "*assertion*g_variant_is_of_type*failed*");
      g_action_activate (G_ACTION (action), g_variant_new_string ("xxx"));
      g_test_assert_expected_messages ();
73 74 75 76 77
    }

  g_object_unref (action);
  g_assert (!a.did_run);

78 79 80 81 82 83
  action = g_simple_action_new ("foo", G_VARIANT_TYPE_STRING);
  g_assert (g_action_get_enabled (G_ACTION (action)));
  g_assert (g_variant_type_equal (g_action_get_parameter_type (G_ACTION (action)), G_VARIANT_TYPE_STRING));
  g_assert (g_action_get_state_type (G_ACTION (action)) == NULL);
  g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
  g_assert (g_action_get_state (G_ACTION (action)) == NULL);
84

85 86
  g_signal_connect (action, "activate", G_CALLBACK (activate), &a);
  g_assert (!a.did_run);
87
  g_action_activate (G_ACTION (action), g_variant_new_string ("Hello world"));
88 89 90 91 92
  g_assert (a.did_run);
  g_assert_cmpstr (g_variant_get_string (a.params, NULL), ==, "Hello world");
  g_variant_unref (a.params);
  a.did_run = FALSE;

93
  if (g_test_undefined ())
94
    {
95 96 97 98
      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
                             "*assertion*!= NULL*failed*");
      g_action_activate (G_ACTION (action), NULL);
      g_test_assert_expected_messages ();
99
    }
100 101 102 103 104

  g_object_unref (action);
  g_assert (!a.did_run);
}

105
static gboolean
106
strv_has_string (gchar       **haystack,
107 108 109 110 111 112 113 114 115 116 117 118
                 const gchar  *needle)
{
  guint n;

  for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
    {
      if (g_strcmp0 (haystack[n], needle) == 0)
        return TRUE;
    }
  return FALSE;
}

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
static gboolean
strv_strv_cmp (gchar **a, gchar **b)
{
  guint n;

  for (n = 0; a[n] != NULL; n++)
    {
       if (!strv_has_string (b, a[n]))
         return FALSE;
    }

  for (n = 0; b[n] != NULL; n++)
    {
       if (!strv_has_string (a, b[n]))
         return FALSE;
    }

  return TRUE;
}

139
static gboolean
140
strv_set_equal (gchar **strv, ...)
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
{
  gint count;
  va_list list;
  const gchar *str;
  gboolean res;

  res = TRUE;
  count = 0;
  va_start (list, strv);
  while (1)
    {
      str = va_arg (list, const gchar *);
      if (str == NULL)
        break;
      if (!strv_has_string (strv, str))
        {
          res = FALSE;
          break;
        }
      count++;
    }
  va_end (list);

  if (res)
    res = g_strv_length ((gchar**)strv) == count;

  return res;
}

Allison Karlitskaya's avatar
Allison Karlitskaya committed
170 171 172 173 174
static void
test_simple_group (void)
{
  GSimpleActionGroup *group;
  Activation a = { 0, };
175
  GSimpleAction *simple;
Allison Karlitskaya's avatar
Allison Karlitskaya committed
176
  GAction *action;
177 178
  gchar **actions;
  GVariant *state;
Allison Karlitskaya's avatar
Allison Karlitskaya committed
179

180 181
  simple = g_simple_action_new ("foo", NULL);
  g_signal_connect (simple, "activate", G_CALLBACK (activate), &a);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
182
  g_assert (!a.did_run);
183
  g_action_activate (G_ACTION (simple), NULL);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
184 185 186 187
  g_assert (a.did_run);
  a.did_run = FALSE;

  group = g_simple_action_group_new ();
188 189
  g_simple_action_group_insert (group, G_ACTION (simple));
  g_object_unref (simple);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
190 191

  g_assert (!a.did_run);
192
  g_action_group_activate_action (G_ACTION_GROUP (group), "foo", NULL);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
193 194
  g_assert (a.did_run);

195 196 197
  simple = g_simple_action_new_stateful ("bar", G_VARIANT_TYPE_STRING, g_variant_new_string ("hihi"));
  g_simple_action_group_insert (group, G_ACTION (simple));
  g_object_unref (simple);
198 199 200 201 202 203 204 205

  g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "foo"));
  g_assert (g_action_group_has_action (G_ACTION_GROUP (group), "bar"));
  g_assert (!g_action_group_has_action (G_ACTION_GROUP (group), "baz"));
  actions = g_action_group_list_actions (G_ACTION_GROUP (group));
  g_assert_cmpint (g_strv_length (actions), ==, 2);
  g_assert (strv_set_equal (actions, "foo", "bar", NULL));
  g_strfreev (actions);
206 207 208 209 210 211 212 213 214 215
  g_assert (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "foo"));
  g_assert (g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
  g_assert (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "foo") == NULL);
  g_assert (g_variant_type_equal (g_action_group_get_action_parameter_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
  g_assert (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "foo") == NULL);
  g_assert (g_variant_type_equal (g_action_group_get_action_state_type (G_ACTION_GROUP (group), "bar"), G_VARIANT_TYPE_STRING));
  g_assert (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "foo") == NULL);
  g_assert (g_action_group_get_action_state_hint (G_ACTION_GROUP (group), "bar") == NULL);
  g_assert (g_action_group_get_action_state (G_ACTION_GROUP (group), "foo") == NULL);
  state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
216 217
  g_assert (g_variant_type_equal (g_variant_get_type (state), G_VARIANT_TYPE_STRING));
  g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
218
  g_variant_unref (state);
219

220 221
  g_action_group_change_action_state (G_ACTION_GROUP (group), "bar", g_variant_new_string ("boo"));
  state = g_action_group_get_action_state (G_ACTION_GROUP (group), "bar");
222
  g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "boo");
223
  g_variant_unref (state);
224

225
  action = g_simple_action_group_lookup (group, "bar");
226
  g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
227
  g_assert (!g_action_group_get_action_enabled (G_ACTION_GROUP (group), "bar"));
228 229 230 231 232 233 234

  g_simple_action_group_remove (group, "bar");
  action = g_simple_action_group_lookup (group, "foo");
  g_assert_cmpstr (g_action_get_name (action), ==, "foo");
  action = g_simple_action_group_lookup (group, "bar");
  g_assert (action == NULL);

Allison Karlitskaya's avatar
Allison Karlitskaya committed
235 236 237 238 239
  a.did_run = FALSE;
  g_object_unref (group);
  g_assert (!a.did_run);
}

240 241 242
static void
test_stateful (void)
{
243
  GSimpleAction *action;
244
  GVariant *state;
245

246 247 248 249 250
  action = g_simple_action_new_stateful ("foo", NULL, g_variant_new_string ("hihi"));
  g_assert (g_action_get_enabled (G_ACTION (action)));
  g_assert (g_action_get_parameter_type (G_ACTION (action)) == NULL);
  g_assert (g_action_get_state_hint (G_ACTION (action)) == NULL);
  g_assert (g_variant_type_equal (g_action_get_state_type (G_ACTION (action)),
251
                                  G_VARIANT_TYPE_STRING));
252
  state = g_action_get_state (G_ACTION (action));
253 254
  g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hihi");
  g_variant_unref (state);
255

256
  if (g_test_undefined ())
257
    {
258 259 260 261
      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
                             "*assertion*g_variant_is_of_type*failed*");
      g_simple_action_set_state (action, g_variant_new_int32 (123));
      g_test_assert_expected_messages ();
262 263
    }

264
  g_simple_action_set_state (action, g_variant_new_string ("hello"));
265
  state = g_action_get_state (G_ACTION (action));
266 267
  g_assert_cmpstr (g_variant_get_string (state, NULL), ==, "hello");
  g_variant_unref (state);
268 269 270

  g_object_unref (action);

271
  action = g_simple_action_new ("foo", NULL);
272 273

  if (g_test_undefined ())
274
    {
275 276 277 278
      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
                             "*assertion*!= NULL*failed*");
      g_simple_action_set_state (action, g_variant_new_int32 (123));
      g_test_assert_expected_messages ();
279
    }
280

281 282 283
  g_object_unref (action);
}

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
static gboolean foo_activated = FALSE;
static gboolean bar_activated = FALSE;

static void
activate_foo (GSimpleAction *simple,
              GVariant      *parameter,
              gpointer       user_data)
{
  g_assert (user_data == GINT_TO_POINTER (123));
  g_assert (parameter == NULL);
  foo_activated = TRUE;
}

static void
activate_bar (GSimpleAction *simple,
              GVariant      *parameter,
              gpointer       user_data)
{
  g_assert (user_data == GINT_TO_POINTER (123));
  g_assert_cmpstr (g_variant_get_string (parameter, NULL), ==, "param");
  bar_activated = TRUE;
}

307 308 309 310 311 312 313 314 315 316 317 318 319 320
static void
change_volume_state (GSimpleAction *action,
                     GVariant      *value,
                     gpointer       user_data)
{
  gint requested;

  requested = g_variant_get_int32 (value);

  /* Volume only goes from 0 to 10 */
  if (0 <= requested && requested <= 10)
    g_simple_action_set_state (action, value);
}

321 322 323 324
static void
test_entries (void)
{
  const GActionEntry entries[] = {
325 326 327 328
    { "foo",    activate_foo                                     },
    { "bar",    activate_bar, "s"                                },
    { "toggle", NULL,         NULL, "false"                      },
    { "volume", NULL,         NULL, "0",     change_volume_state }
329 330
  };
  GSimpleActionGroup *actions;
331
  GVariant *state;
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348

  actions = g_simple_action_group_new ();
  g_simple_action_group_add_entries (actions, entries,
                                     G_N_ELEMENTS (entries),
                                     GINT_TO_POINTER (123));

  g_assert (!foo_activated);
  g_action_group_activate_action (G_ACTION_GROUP (actions), "foo", NULL);
  g_assert (foo_activated);
  foo_activated = FALSE;

  g_assert (!bar_activated);
  g_action_group_activate_action (G_ACTION_GROUP (actions), "bar",
                                  g_variant_new_string ("param"));
  g_assert (bar_activated);
  g_assert (!foo_activated);

349
  if (g_test_undefined ())
350
    {
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
      const GActionEntry bad_type = {
        "bad-type", NULL, "ss"
      };
      const GActionEntry bad_state = {
        "bad-state", NULL, NULL, "flse"
      };

      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
                             "*not a valid GVariant type string*");
      g_simple_action_group_add_entries (actions, &bad_type, 1, NULL);
      g_test_assert_expected_messages ();

      g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
                             "*could not parse*");
      g_simple_action_group_add_entries (actions, &bad_state, 1, NULL);
      g_test_assert_expected_messages ();
367 368
    }

369
  state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
370 371 372 373
  g_assert_cmpint (g_variant_get_int32 (state), ==, 0);
  g_variant_unref (state);

  /* should change */
374 375 376
  g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
                                      g_variant_new_int32 (7));
  state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
377 378 379 380
  g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
  g_variant_unref (state);

  /* should not change */
381 382 383
  g_action_group_change_action_state (G_ACTION_GROUP (actions), "volume",
                                      g_variant_new_int32 (11));
  state = g_action_group_get_action_state (G_ACTION_GROUP (actions), "volume");
384 385
  g_assert_cmpint (g_variant_get_int32 (state), ==, 7);
  g_variant_unref (state);
386 387

  g_object_unref (actions);
388
}
389

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413

GHashTable *activation_counts;

static void
count_activation (const gchar *action)
{
  gint count;

  if (activation_counts == NULL)
    activation_counts = g_hash_table_new (g_str_hash, g_str_equal);
  count = GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
  count++;
  g_hash_table_insert (activation_counts, (gpointer)action, GINT_TO_POINTER (count));
}

static gint
activation_count (const gchar *action)
{
  if (activation_counts == NULL)
    return 0;

  return GPOINTER_TO_INT (g_hash_table_lookup (activation_counts, action));
}

414 415 416
static void
activate_action (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
417
  count_activation (g_action_get_name (G_ACTION (action)));
418 419 420 421 422 423 424
}

static void
activate_toggle (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
  GVariant *old_state, *new_state;

425 426
  count_activation (g_action_get_name (G_ACTION (action)));

427 428 429 430 431 432 433 434 435
  old_state = g_action_get_state (G_ACTION (action));
  new_state = g_variant_new_boolean (!g_variant_get_boolean (old_state));
  g_simple_action_set_state (action, new_state);
  g_variant_unref (old_state);
}

static void
activate_radio (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
436
  GVariant *new_state;
437

438
  count_activation (g_action_get_name (G_ACTION (action)));
439

440
  new_state = g_variant_new_string (g_variant_get_string (parameter, NULL));
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
  g_simple_action_set_state (action, new_state);
}

static gboolean
compare_action_groups (GActionGroup *a, GActionGroup *b)
{
  gchar **alist;
  gchar **blist;
  gint i;
  gboolean equal;
  gboolean ares, bres;
  gboolean aenabled, benabled;
  const GVariantType *aparameter_type, *bparameter_type;
  const GVariantType *astate_type, *bstate_type;
  GVariant *astate_hint, *bstate_hint;
  GVariant *astate, *bstate;

  alist = g_action_group_list_actions (a);
  blist = g_action_group_list_actions (b);
  equal = strv_strv_cmp (alist, blist);

  for (i = 0; equal && alist[i]; i++)
    {
      ares = g_action_group_query_action (a, alist[i], &aenabled, &aparameter_type, &astate_type, &astate_hint, &astate);
      bres = g_action_group_query_action (b, alist[i], &benabled, &bparameter_type, &bstate_type, &bstate_hint, &bstate);

      if (ares && bres)
        {
          equal = equal && (aenabled == benabled);
          equal = equal && ((!aparameter_type && !bparameter_type) || g_variant_type_equal (aparameter_type, bparameter_type));
          equal = equal && ((!astate_type && !bstate_type) || g_variant_type_equal (astate_type, bstate_type));
          equal = equal && ((!astate_hint && !bstate_hint) || g_variant_equal (astate_hint, bstate_hint));
          equal = equal && ((!astate && !bstate) || g_variant_equal (astate, bstate));

          if (astate_hint)
            g_variant_unref (astate_hint);
          if (bstate_hint)
            g_variant_unref (bstate_hint);
          if (astate)
            g_variant_unref (astate);
          if (bstate)
            g_variant_unref (bstate);
        }
      else
        equal = FALSE;
    }

  g_strfreev (alist);
  g_strfreev (blist);

  return equal;
}

static gboolean
stop_loop (gpointer data)
{
  GMainLoop *loop = data;

  g_main_loop_quit (loop);

  return G_SOURCE_REMOVE;
}

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
static GActionEntry exported_entries[] = {
  { "undo",  activate_action, NULL, NULL,      NULL },
  { "redo",  activate_action, NULL, NULL,      NULL },
  { "cut",   activate_action, NULL, NULL,      NULL },
  { "copy",  activate_action, NULL, NULL,      NULL },
  { "paste", activate_action, NULL, NULL,      NULL },
  { "bold",  activate_toggle, NULL, "true",    NULL },
  { "lang",  activate_radio,  "s",  "'latin'", NULL },
};

static void
list_cb (GObject      *source,
         GAsyncResult *res,
         gpointer      user_data)
{
  GDBusConnection *bus = G_DBUS_CONNECTION (source);
  GMainLoop *loop = user_data;
  GError *error = NULL;
  GVariant *v;
  gchar **actions;

  v = g_dbus_connection_call_finish (bus, res, &error);
  g_assert (v);
  g_variant_get (v, "(^a&s)", &actions);
  g_assert_cmpint (g_strv_length (actions), ==, G_N_ELEMENTS (exported_entries));
Colin Walters's avatar
Colin Walters committed
529
  g_free (actions);
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
  g_variant_unref (v);
  g_main_loop_quit (loop);
}

static gboolean
call_list (gpointer user_data)
{
  GDBusConnection *bus;

  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
  g_dbus_connection_call (bus,
                          g_dbus_connection_get_unique_name (bus),
                          "/",
                          "org.gtk.Actions",
                          "List",
                          NULL,
                          NULL,
                          0,
                          G_MAXINT,
                          NULL,
                          list_cb,
                          user_data);
  g_object_unref (bus);

  return G_SOURCE_REMOVE;
}

static void
describe_cb (GObject      *source,
             GAsyncResult *res,
             gpointer      user_data)
{
  GDBusConnection *bus = G_DBUS_CONNECTION (source);
  GMainLoop *loop = user_data;
  GError *error = NULL;
  GVariant *v;
  gboolean enabled;
  gchar *param;
  GVariantIter *iter;

  v = g_dbus_connection_call_finish (bus, res, &error);
  g_assert (v);
  /* FIXME: there's an extra level of tuplelization in here */
  g_variant_get (v, "((bgav))", &enabled, &param, &iter);
  g_assert (enabled == TRUE);
  g_assert_cmpstr (param, ==, "");
  g_assert_cmpint (g_variant_iter_n_children (iter), ==, 0);
  g_free (param);
  g_variant_iter_free (iter);
  g_variant_unref (v);

  g_main_loop_quit (loop);
}

static gboolean
call_describe (gpointer user_data)
{
  GDBusConnection *bus;

  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
  g_dbus_connection_call (bus,
                          g_dbus_connection_get_unique_name (bus),
                          "/",
                          "org.gtk.Actions",
                          "Describe",
                          g_variant_new ("(s)", "copy"),
                          NULL,
                          0,
                          G_MAXINT,
                          NULL,
                          describe_cb,
                          user_data);
  g_object_unref (bus);

  return G_SOURCE_REMOVE;
}

607 608 609 610 611
static void
test_dbus_export (void)
{
  GDBusConnection *bus;
  GSimpleActionGroup *group;
612
  GDBusActionGroup *proxy;
613 614 615
  GSimpleAction *action;
  GMainLoop *loop;
  GError *error = NULL;
616
  GVariant *v;
617
  guint id;
618
  gchar **actions;
619 620 621

  loop = g_main_loop_new (NULL, FALSE);

622
  session_bus_up ();
623 624 625
  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);

  group = g_simple_action_group_new ();
626 627 628 629
  g_simple_action_group_add_entries (group,
                                     exported_entries,
                                     G_N_ELEMENTS (exported_entries),
                                     NULL);
630

631
  id = g_dbus_connection_export_action_group (bus, "/", G_ACTION_GROUP (group), &error);
632 633
  g_assert_no_error (error);

634
  proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
635 636 637 638

  actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
  g_assert_cmpint (g_strv_length (actions), ==, 0);
  g_strfreev (actions);
639 640 641 642

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

643 644 645 646 647 648 649 650 651 652 653 654
  actions = g_action_group_list_actions (G_ACTION_GROUP (proxy));
  g_assert_cmpint (g_strv_length (actions), ==, G_N_ELEMENTS (exported_entries));
  g_strfreev (actions);

  /* check that calling "List" works too */
  g_idle_add (call_list, loop);
  g_main_loop_run (loop);

  /* check that calling "Describe" works */
  g_idle_add (call_describe, loop);
  g_main_loop_run (loop);

655
  /* test that the initial transfer works */
656 657 658
  g_assert (G_IS_DBUS_ACTION_GROUP (proxy));
  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));

659
  /* test that various changes get propagated from group to proxy */
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
  action = g_simple_action_new_stateful ("italic", NULL, g_variant_new_boolean (FALSE));
  g_simple_action_group_insert (group, G_ACTION (action));
  g_object_unref (action);

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));

  action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "cut"));
  g_simple_action_set_enabled (action, FALSE);

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));

  action = G_SIMPLE_ACTION (g_simple_action_group_lookup (group, "bold"));
  g_simple_action_set_state (action, g_variant_new_boolean (FALSE));

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));

  g_simple_action_group_remove (group, "italic");

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));

692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
  /* test that activations and state changes propagate the other way */

  g_assert_cmpint (activation_count ("copy"), ==, 0);
  g_action_group_activate_action (G_ACTION_GROUP (proxy), "copy", NULL);

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_assert_cmpint (activation_count ("copy"), ==, 1);
  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));

  g_assert_cmpint (activation_count ("bold"), ==, 0);
  g_action_group_activate_action (G_ACTION_GROUP (proxy), "bold", NULL);

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_assert_cmpint (activation_count ("bold"), ==, 1);
  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
  v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
  g_assert (g_variant_get_boolean (v));
  g_variant_unref (v);

  g_action_group_change_action_state (G_ACTION_GROUP (proxy), "bold", g_variant_new_boolean (FALSE));

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_assert_cmpint (activation_count ("bold"), ==, 1);
  g_assert (compare_action_groups (G_ACTION_GROUP (group), G_ACTION_GROUP (proxy)));
  v = g_action_group_get_action_state (G_ACTION_GROUP (group), "bold");
  g_assert (!g_variant_get_boolean (v));
  g_variant_unref (v);

726
  g_dbus_connection_unexport_action_group (bus, id);
727 728 729 730

  g_object_unref (proxy);
  g_object_unref (group);
  g_main_loop_unref (loop);
731
  g_object_unref (bus);
732 733

  session_bus_down ();
734 735
}

736 737 738 739
static gpointer
do_export (gpointer data)
{
  GActionGroup *group = data;
740
  GMainContext *ctx;
741 742 743 744
  gint i;
  GError *error = NULL;
  guint id;
  GDBusConnection *bus;
745
  GAction *action;
746 747
  gchar *path;

748 749 750 751
  ctx = g_main_context_new ();

  g_main_context_push_thread_default (ctx);

752 753 754 755 756 757 758
  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
  path = g_strdup_printf("/%p", data);

  for (i = 0; i < 100000; i++)
    {
      id = g_dbus_connection_export_action_group (bus, path, G_ACTION_GROUP (group), &error);
      g_assert_no_error (error);
759 760 761 762 763

      action = g_simple_action_group_lookup (G_SIMPLE_ACTION_GROUP (group), "a");
      g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
                                   !g_action_get_enabled (action));

764
      g_dbus_connection_unexport_action_group (bus, id);
765 766

      while (g_main_context_iteration (ctx, FALSE));
767 768 769 770 771
    }

  g_free (path);
  g_object_unref (bus);

772 773 774 775
  g_main_context_pop_thread_default (ctx);

  g_main_context_unref (ctx);

776 777 778 779 780 781 782 783 784 785 786 787 788 789
  return NULL;
}

static void
test_dbus_threaded (void)
{
  GSimpleActionGroup *group[10];
  GThread *export[10];
  static GActionEntry entries[] = {
    { "a",  activate_action, NULL, NULL, NULL },
    { "b",  activate_action, NULL, NULL, NULL },
  };
  gint i;

790 791
  session_bus_up ();

792 793 794 795 796 797 798 799
  for (i = 0; i < 10; i++)
    {
      group[i] = g_simple_action_group_new ();
      g_simple_action_group_add_entries (group[i], entries, G_N_ELEMENTS (entries), NULL);
      export[i] = g_thread_new ("export", do_export, group[i]);
    }

  for (i = 0; i < 10; i++)
800
    g_thread_join (export[i]);
801 802 803

  for (i = 0; i < 10; i++)
    g_object_unref (group[i]);
804 805

  session_bus_down ();
806 807
}

808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
static void
test_bug679509 (void)
{
  GDBusConnection *bus;
  GDBusActionGroup *proxy;
  GMainLoop *loop;

  loop = g_main_loop_new (NULL, FALSE);

  session_bus_up ();
  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);

  proxy = g_dbus_action_group_get (bus, g_dbus_connection_get_unique_name (bus), "/");
  g_strfreev (g_action_group_list_actions (G_ACTION_GROUP (proxy)));
  g_object_unref (proxy);

  g_timeout_add (100, stop_loop, loop);
  g_main_loop_run (loop);

  g_main_loop_unref (loop);
  g_object_unref (bus);

  session_bus_down ();
}

833 834 835 836 837 838
int
main (int argc, char **argv)
{
  g_test_init (&argc, &argv, NULL);

  g_test_add_func ("/actions/basic", test_basic);
Allison Karlitskaya's avatar
Allison Karlitskaya committed
839
  g_test_add_func ("/actions/simplegroup", test_simple_group);
840
  g_test_add_func ("/actions/stateful", test_stateful);
841
  g_test_add_func ("/actions/entries", test_entries);
842
  g_test_add_func ("/actions/dbus/export", test_dbus_export);
843
  g_test_add_func ("/actions/dbus/threaded", test_dbus_threaded);
844
  g_test_add_func ("/actions/dbus/bug679509", test_bug679509);
845 846 847

  return g_test_run ();
}