glib/gio/tests/gmenumodel.c
Simon McVittie 8fef0a9cd3 gmenumodel test: Wait for the expected events to happen
Previously, we waited an arbitrary 100ms or 200ms and then asserted
that the events had happened, but that might fail if the machine is
slow or heavily loaded.

We still wait for an arbitrary time for negative tests (asserting
that no more signals are received) because we don't have any way
to do better here.

Signed-off-by: Simon McVittie <smcv@collabora.com>
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=884661

https://bugzilla.gnome.org/show_bug.cgi?id=791744
2017-12-18 18:56:35 +00:00

1509 lines
39 KiB
C

#include <gio/gio.h>
#include <gio/gunixsocketaddress.h>
#include <glib/gstdio.h>
#include <string.h>
#include "gdbus-sessionbus.h"
static gboolean
time_out (gpointer unused G_GNUC_UNUSED)
{
g_error ("Timed out");
/* not reached */
return FALSE;
}
static guint
add_timeout (guint seconds)
{
#ifdef G_OS_UNIX
/* Safety-catch against the main loop having blocked */
alarm (seconds + 5);
#endif
return g_timeout_add_seconds (seconds, time_out, NULL);
}
static void
cancel_timeout (guint timeout_id)
{
#ifdef G_OS_UNIX
alarm (0);
#endif
g_source_remove (timeout_id);
}
/* Markup printing {{{1 */
/* This used to be part of GLib, but it was removed before the stable
* release because it wasn't generally useful. We want it here, though.
*/
static void
indent_string (GString *string,
gint indent)
{
while (indent--)
g_string_append_c (string, ' ');
}
static GString *
g_menu_markup_print_string (GString *string,
GMenuModel *model,
gint indent,
gint tabstop)
{
gboolean need_nl = FALSE;
gint i, n;
if G_UNLIKELY (string == NULL)
string = g_string_new (NULL);
n = g_menu_model_get_n_items (model);
for (i = 0; i < n; i++)
{
GMenuAttributeIter *attr_iter;
GMenuLinkIter *link_iter;
GString *contents;
GString *attrs;
attr_iter = g_menu_model_iterate_item_attributes (model, i);
link_iter = g_menu_model_iterate_item_links (model, i);
contents = g_string_new (NULL);
attrs = g_string_new (NULL);
while (g_menu_attribute_iter_next (attr_iter))
{
const char *name = g_menu_attribute_iter_get_name (attr_iter);
GVariant *value = g_menu_attribute_iter_get_value (attr_iter);
if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
{
gchar *str;
str = g_markup_printf_escaped (" %s='%s'", name, g_variant_get_string (value, NULL));
g_string_append (attrs, str);
g_free (str);
}
else
{
gchar *printed;
gchar *str;
const gchar *type;
printed = g_variant_print (value, TRUE);
type = g_variant_type_peek_string (g_variant_get_type (value));
str = g_markup_printf_escaped ("<attribute name='%s' type='%s'>%s</attribute>\n", name, type, printed);
indent_string (contents, indent + tabstop);
g_string_append (contents, str);
g_free (printed);
g_free (str);
}
g_variant_unref (value);
}
g_object_unref (attr_iter);
while (g_menu_link_iter_next (link_iter))
{
const gchar *name = g_menu_link_iter_get_name (link_iter);
GMenuModel *menu = g_menu_link_iter_get_value (link_iter);
gchar *str;
if (contents->str[0])
g_string_append_c (contents, '\n');
str = g_markup_printf_escaped ("<link name='%s'>\n", name);
indent_string (contents, indent + tabstop);
g_string_append (contents, str);
g_free (str);
g_menu_markup_print_string (contents, menu, indent + 2 * tabstop, tabstop);
indent_string (contents, indent + tabstop);
g_string_append (contents, "</link>\n");
g_object_unref (menu);
}
g_object_unref (link_iter);
if (contents->str[0])
{
indent_string (string, indent);
g_string_append_printf (string, "<item%s>\n", attrs->str);
g_string_append (string, contents->str);
indent_string (string, indent);
g_string_append (string, "</item>\n");
need_nl = TRUE;
}
else
{
if (need_nl)
g_string_append_c (string, '\n');
indent_string (string, indent);
g_string_append_printf (string, "<item%s/>\n", attrs->str);
need_nl = FALSE;
}
g_string_free (contents, TRUE);
g_string_free (attrs, TRUE);
}
return string;
}
/* TestItem {{{1 */
/* This utility struct is used by both the RandomMenu and MirrorMenu
* class implementations below.
*/
typedef struct {
GHashTable *attributes;
GHashTable *links;
} TestItem;
static TestItem *
test_item_new (GHashTable *attributes,
GHashTable *links)
{
TestItem *item;
item = g_slice_new (TestItem);
item->attributes = g_hash_table_ref (attributes);
item->links = g_hash_table_ref (links);
return item;
}
static void
test_item_free (gpointer data)
{
TestItem *item = data;
g_hash_table_unref (item->attributes);
g_hash_table_unref (item->links);
g_slice_free (TestItem, item);
}
/* RandomMenu {{{1 */
#define MAX_ITEMS 5
#define TOP_ORDER 4
typedef struct {
GMenuModel parent_instance;
GSequence *items;
gint order;
} RandomMenu;
typedef GMenuModelClass RandomMenuClass;
static GType random_menu_get_type (void);
G_DEFINE_TYPE (RandomMenu, random_menu, G_TYPE_MENU_MODEL)
static gboolean
random_menu_is_mutable (GMenuModel *model)
{
return TRUE;
}
static gint
random_menu_get_n_items (GMenuModel *model)
{
RandomMenu *menu = (RandomMenu *) model;
return g_sequence_get_length (menu->items);
}
static void
random_menu_get_item_attributes (GMenuModel *model,
gint position,
GHashTable **table)
{
RandomMenu *menu = (RandomMenu *) model;
TestItem *item;
item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
*table = g_hash_table_ref (item->attributes);
}
static void
random_menu_get_item_links (GMenuModel *model,
gint position,
GHashTable **table)
{
RandomMenu *menu = (RandomMenu *) model;
TestItem *item;
item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
*table = g_hash_table_ref (item->links);
}
static void
random_menu_finalize (GObject *object)
{
RandomMenu *menu = (RandomMenu *) object;
g_sequence_free (menu->items);
G_OBJECT_CLASS (random_menu_parent_class)
->finalize (object);
}
static void
random_menu_init (RandomMenu *menu)
{
}
static void
random_menu_class_init (GMenuModelClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
class->is_mutable = random_menu_is_mutable;
class->get_n_items = random_menu_get_n_items;
class->get_item_attributes = random_menu_get_item_attributes;
class->get_item_links = random_menu_get_item_links;
object_class->finalize = random_menu_finalize;
}
static RandomMenu * random_menu_new (GRand *rand, gint order);
static void
random_menu_change (RandomMenu *menu,
GRand *rand)
{
gint position, removes, adds;
GSequenceIter *point;
gint n_items;
gint i;
n_items = g_sequence_get_length (menu->items);
do
{
position = g_rand_int_range (rand, 0, n_items + 1);
removes = g_rand_int_range (rand, 0, n_items - position + 1);
adds = g_rand_int_range (rand, 0, MAX_ITEMS - (n_items - removes) + 1);
}
while (removes == 0 && adds == 0);
point = g_sequence_get_iter_at_pos (menu->items, position + removes);
if (removes)
{
GSequenceIter *start;
start = g_sequence_get_iter_at_pos (menu->items, position);
g_sequence_remove_range (start, point);
}
for (i = 0; i < adds; i++)
{
const gchar *label;
GHashTable *links;
GHashTable *attributes;
attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
if (menu->order > 0 && g_rand_boolean (rand))
{
RandomMenu *child;
const gchar *subtype;
child = random_menu_new (rand, menu->order - 1);
if (g_rand_boolean (rand))
{
subtype = G_MENU_LINK_SECTION;
/* label some section headers */
if (g_rand_boolean (rand))
label = "Section";
else
label = NULL;
}
else
{
/* label all submenus */
subtype = G_MENU_LINK_SUBMENU;
label = "Submenu";
}
g_hash_table_insert (links, g_strdup (subtype), child);
}
else
/* label all terminals */
label = "Menu Item";
if (label)
g_hash_table_insert (attributes, g_strdup ("label"), g_variant_ref_sink (g_variant_new_string (label)));
g_sequence_insert_before (point, test_item_new (attributes, links));
g_hash_table_unref (links);
g_hash_table_unref (attributes);
}
g_menu_model_items_changed (G_MENU_MODEL (menu), position, removes, adds);
}
static RandomMenu *
random_menu_new (GRand *rand,
gint order)
{
RandomMenu *menu;
menu = g_object_new (random_menu_get_type (), NULL);
menu->items = g_sequence_new (test_item_free);
menu->order = order;
random_menu_change (menu, rand);
return menu;
}
/* MirrorMenu {{{1 */
typedef struct {
GMenuModel parent_instance;
GMenuModel *clone_of;
GSequence *items;
gulong handler_id;
} MirrorMenu;
typedef GMenuModelClass MirrorMenuClass;
static GType mirror_menu_get_type (void);
G_DEFINE_TYPE (MirrorMenu, mirror_menu, G_TYPE_MENU_MODEL)
static gboolean
mirror_menu_is_mutable (GMenuModel *model)
{
MirrorMenu *menu = (MirrorMenu *) model;
return menu->handler_id != 0;
}
static gint
mirror_menu_get_n_items (GMenuModel *model)
{
MirrorMenu *menu = (MirrorMenu *) model;
return g_sequence_get_length (menu->items);
}
static void
mirror_menu_get_item_attributes (GMenuModel *model,
gint position,
GHashTable **table)
{
MirrorMenu *menu = (MirrorMenu *) model;
TestItem *item;
item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
*table = g_hash_table_ref (item->attributes);
}
static void
mirror_menu_get_item_links (GMenuModel *model,
gint position,
GHashTable **table)
{
MirrorMenu *menu = (MirrorMenu *) model;
TestItem *item;
item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
*table = g_hash_table_ref (item->links);
}
static void
mirror_menu_finalize (GObject *object)
{
MirrorMenu *menu = (MirrorMenu *) object;
if (menu->handler_id)
g_signal_handler_disconnect (menu->clone_of, menu->handler_id);
g_sequence_free (menu->items);
g_object_unref (menu->clone_of);
G_OBJECT_CLASS (mirror_menu_parent_class)
->finalize (object);
}
static void
mirror_menu_init (MirrorMenu *menu)
{
}
static void
mirror_menu_class_init (GMenuModelClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
class->is_mutable = mirror_menu_is_mutable;
class->get_n_items = mirror_menu_get_n_items;
class->get_item_attributes = mirror_menu_get_item_attributes;
class->get_item_links = mirror_menu_get_item_links;
object_class->finalize = mirror_menu_finalize;
}
static MirrorMenu * mirror_menu_new (GMenuModel *clone_of);
static void
mirror_menu_changed (GMenuModel *model,
gint position,
gint removed,
gint added,
gpointer user_data)
{
MirrorMenu *menu = user_data;
GSequenceIter *point;
gint i;
g_assert (model == menu->clone_of);
point = g_sequence_get_iter_at_pos (menu->items, position + removed);
if (removed)
{
GSequenceIter *start;
start = g_sequence_get_iter_at_pos (menu->items, position);
g_sequence_remove_range (start, point);
}
for (i = position; i < position + added; i++)
{
GMenuAttributeIter *attr_iter;
GMenuLinkIter *link_iter;
GHashTable *links;
GHashTable *attributes;
const gchar *name;
GMenuModel *child;
GVariant *value;
attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
attr_iter = g_menu_model_iterate_item_attributes (model, i);
while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
{
g_hash_table_insert (attributes, g_strdup (name), value);
}
g_object_unref (attr_iter);
link_iter = g_menu_model_iterate_item_links (model, i);
while (g_menu_link_iter_get_next (link_iter, &name, &child))
{
g_hash_table_insert (links, g_strdup (name), mirror_menu_new (child));
g_object_unref (child);
}
g_object_unref (link_iter);
g_sequence_insert_before (point, test_item_new (attributes, links));
g_hash_table_unref (attributes);
g_hash_table_unref (links);
}
g_menu_model_items_changed (G_MENU_MODEL (menu), position, removed, added);
}
static MirrorMenu *
mirror_menu_new (GMenuModel *clone_of)
{
MirrorMenu *menu;
menu = g_object_new (mirror_menu_get_type (), NULL);
menu->items = g_sequence_new (test_item_free);
menu->clone_of = g_object_ref (clone_of);
if (g_menu_model_is_mutable (clone_of))
menu->handler_id = g_signal_connect (clone_of, "items-changed", G_CALLBACK (mirror_menu_changed), menu);
mirror_menu_changed (clone_of, 0, 0, g_menu_model_get_n_items (clone_of), menu);
return menu;
}
/* check_menus_equal(), assert_menus_equal() {{{1 */
static gboolean
check_menus_equal (GMenuModel *a,
GMenuModel *b)
{
gboolean equal = TRUE;
gint a_n, b_n;
gint i;
a_n = g_menu_model_get_n_items (a);
b_n = g_menu_model_get_n_items (b);
if (a_n != b_n)
return FALSE;
for (i = 0; i < a_n; i++)
{
GMenuAttributeIter *attr_iter;
GVariant *a_value, *b_value;
GMenuLinkIter *link_iter;
GMenuModel *a_menu, *b_menu;
const gchar *name;
attr_iter = g_menu_model_iterate_item_attributes (a, i);
while (g_menu_attribute_iter_get_next (attr_iter, &name, &a_value))
{
b_value = g_menu_model_get_item_attribute_value (b, i, name, NULL);
equal &= b_value && g_variant_equal (a_value, b_value);
if (b_value)
g_variant_unref (b_value);
g_variant_unref (a_value);
}
g_object_unref (attr_iter);
attr_iter = g_menu_model_iterate_item_attributes (b, i);
while (g_menu_attribute_iter_get_next (attr_iter, &name, &b_value))
{
a_value = g_menu_model_get_item_attribute_value (a, i, name, NULL);
equal &= a_value && g_variant_equal (a_value, b_value);
if (a_value)
g_variant_unref (a_value);
g_variant_unref (b_value);
}
g_object_unref (attr_iter);
link_iter = g_menu_model_iterate_item_links (a, i);
while (g_menu_link_iter_get_next (link_iter, &name, &a_menu))
{
b_menu = g_menu_model_get_item_link (b, i, name);
equal &= b_menu && check_menus_equal (a_menu, b_menu);
if (b_menu)
g_object_unref (b_menu);
g_object_unref (a_menu);
}
g_object_unref (link_iter);
link_iter = g_menu_model_iterate_item_links (b, i);
while (g_menu_link_iter_get_next (link_iter, &name, &b_menu))
{
a_menu = g_menu_model_get_item_link (a, i, name);
equal &= a_menu && check_menus_equal (a_menu, b_menu);
if (a_menu)
g_object_unref (a_menu);
g_object_unref (b_menu);
}
g_object_unref (link_iter);
}
return equal;
}
static void
assert_menus_equal (GMenuModel *a,
GMenuModel *b)
{
if (!check_menus_equal (a, b))
{
GString *string;
string = g_string_new ("\n <a>\n");
g_menu_markup_print_string (string, G_MENU_MODEL (a), 4, 2);
g_string_append (string, " </a>\n\n-------------\n <b>\n");
g_menu_markup_print_string (string, G_MENU_MODEL (b), 4, 2);
g_string_append (string, " </b>\n");
g_error ("%s", string->str);
}
}
static void
assert_menuitem_equal (GMenuItem *item,
GMenuModel *model,
gint index)
{
GMenuAttributeIter *attr_iter;
GMenuLinkIter *link_iter;
const gchar *name;
GVariant *value;
GMenuModel *linked_model;
/* NOTE we can't yet test whether item has attributes or links that
* are not in the model, because there's no iterator API for menu
* items */
attr_iter = g_menu_model_iterate_item_attributes (model, index);
while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
{
GVariant *item_value;
item_value = g_menu_item_get_attribute_value (item, name, g_variant_get_type (value));
g_assert (item_value && g_variant_equal (item_value, value));
g_variant_unref (item_value);
g_variant_unref (value);
}
link_iter = g_menu_model_iterate_item_links (model, index);
while (g_menu_link_iter_get_next (link_iter, &name, &linked_model))
{
GMenuModel *item_linked_model;
item_linked_model = g_menu_item_get_link (item, name);
g_assert (linked_model == item_linked_model);
g_object_unref (item_linked_model);
g_object_unref (linked_model);
}
g_object_unref (attr_iter);
g_object_unref (link_iter);
}
/* Test cases {{{1 */
static void
test_equality (void)
{
GRand *randa, *randb;
guint32 seed;
gint i;
seed = g_test_rand_int ();
randa = g_rand_new_with_seed (seed);
randb = g_rand_new_with_seed (seed);
for (i = 0; i < 500; i++)
{
RandomMenu *a, *b;
a = random_menu_new (randa, TOP_ORDER);
b = random_menu_new (randb, TOP_ORDER);
assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
g_object_unref (b);
g_object_unref (a);
}
g_rand_int (randa);
for (i = 0; i < 500;)
{
RandomMenu *a, *b;
a = random_menu_new (randa, TOP_ORDER);
b = random_menu_new (randb, TOP_ORDER);
if (check_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b)))
{
/* by chance, they may really be equal. double check. */
GString *as, *bs;
as = g_menu_markup_print_string (NULL, G_MENU_MODEL (a), 4, 2);
bs = g_menu_markup_print_string (NULL, G_MENU_MODEL (b), 4, 2);
g_assert_cmpstr (as->str, ==, bs->str);
g_string_free (bs, TRUE);
g_string_free (as, TRUE);
/* we're here because randa and randb just generated equal
* menus. they may do it again, so throw away randb and make
* a fresh one.
*/
g_rand_free (randb);
randb = g_rand_new_with_seed (g_rand_int (randa));
}
else
/* make sure we get enough unequals (ie: no GRand failure) */
i++;
g_object_unref (b);
g_object_unref (a);
}
g_rand_free (randb);
g_rand_free (randa);
}
static void
test_random (void)
{
RandomMenu *random;
MirrorMenu *mirror;
GRand *rand;
gint i;
rand = g_rand_new_with_seed (g_test_rand_int ());
random = random_menu_new (rand, TOP_ORDER);
mirror = mirror_menu_new (G_MENU_MODEL (random));
for (i = 0; i < 500; i++)
{
assert_menus_equal (G_MENU_MODEL (random), G_MENU_MODEL (mirror));
random_menu_change (random, rand);
}
g_object_unref (mirror);
g_object_unref (random);
g_rand_free (rand);
}
typedef struct
{
GDBusConnection *client_connection;
GDBusConnection *server_connection;
GDBusServer *server;
GThread *service_thread;
GMutex service_loop_lock;
GCond service_loop_cond;
GMainLoop *service_loop;
GMainLoop *loop;
} PeerConnection;
static gboolean
on_new_connection (GDBusServer *server,
GDBusConnection *connection,
gpointer user_data)
{
PeerConnection *data = user_data;
data->server_connection = g_object_ref (connection);
g_main_loop_quit (data->loop);
return TRUE;
}
static void
create_service_loop (GMainContext *service_context,
PeerConnection *data)
{
g_assert (data->service_loop == NULL);
g_mutex_lock (&data->service_loop_lock);
data->service_loop = g_main_loop_new (service_context, FALSE);
g_cond_broadcast (&data->service_loop_cond);
g_mutex_unlock (&data->service_loop_lock);
}
static void
teardown_service_loop (PeerConnection *data)
{
g_mutex_lock (&data->service_loop_lock);
g_clear_pointer (&data->service_loop, g_main_loop_unref);
g_mutex_unlock (&data->service_loop_lock);
}
static void
await_service_loop (PeerConnection *data)
{
g_mutex_lock (&data->service_loop_lock);
while (data->service_loop == NULL)
g_cond_wait (&data->service_loop_cond, &data->service_loop_lock);
g_mutex_unlock (&data->service_loop_lock);
}
static gpointer
service_thread_func (gpointer user_data)
{
PeerConnection *data = user_data;
GMainContext *service_context;
GError *error;
gchar *address;
gchar *tmpdir;
GDBusServerFlags flags;
gchar *guid;
service_context = g_main_context_new ();
g_main_context_push_thread_default (service_context);
tmpdir = NULL;
flags = G_DBUS_SERVER_FLAGS_NONE;
#ifdef G_OS_UNIX
if (g_unix_socket_address_abstract_names_supported ())
address = g_strdup ("unix:tmpdir=/tmp/test-dbus-peer");
else
{
tmpdir = g_dir_make_tmp ("test-dbus-peer-XXXXXX", NULL);
address = g_strdup_printf ("unix:tmpdir=%s", tmpdir);
}
#else
address = g_strdup ("nonce-tcp:");
flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
#endif
guid = g_dbus_generate_guid ();
error = NULL;
data->server = g_dbus_server_new_sync (address,
flags,
guid,
NULL,
NULL,
&error);
g_assert_no_error (error);
g_free (address);
g_free (guid);
g_signal_connect (data->server,
"new-connection",
G_CALLBACK (on_new_connection),
data);
g_dbus_server_start (data->server);
create_service_loop (service_context, data);
g_main_loop_run (data->service_loop);
g_main_context_pop_thread_default (service_context);
teardown_service_loop (data);
g_main_context_unref (service_context);
if (tmpdir)
{
g_rmdir (tmpdir);
g_free (tmpdir);
}
return NULL;
}
static void
peer_connection_up (PeerConnection *data)
{
GError *error;
memset (data, '\0', sizeof (PeerConnection));
data->loop = g_main_loop_new (NULL, FALSE);
g_mutex_init (&data->service_loop_lock);
g_cond_init (&data->service_loop_cond);
/* bring up a server - we run the server in a different thread to
avoid deadlocks */
data->service_thread = g_thread_new ("test_dbus_peer",
service_thread_func,
data);
await_service_loop (data);
g_assert (data->server != NULL);
/* bring up a connection and accept it */
error = NULL;
data->client_connection =
g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (data->server),
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
NULL, /* GDBusAuthObserver */
NULL, /* cancellable */
&error);
g_assert_no_error (error);
g_assert (data->client_connection != NULL);
while (data->server_connection == NULL)
g_main_loop_run (data->loop);
}
static void
peer_connection_down (PeerConnection *data)
{
g_object_unref (data->client_connection);
g_object_unref (data->server_connection);
g_dbus_server_stop (data->server);
g_object_unref (data->server);
g_main_loop_quit (data->service_loop);
g_thread_join (data->service_thread);
g_mutex_clear (&data->service_loop_lock);
g_cond_clear (&data->service_loop_cond);
g_main_loop_unref (data->loop);
}
struct roundtrip_state
{
RandomMenu *random;
MirrorMenu *proxy_mirror;
GDBusMenuModel *proxy;
GMainLoop *loop;
GRand *rand;
gint success;
gint count;
};
static gboolean
roundtrip_step (gpointer data)
{
struct roundtrip_state *state = data;
if (check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy)) &&
check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy_mirror)))
{
state->success++;
state->count = 0;
if (state->success < 100)
random_menu_change (state->random, state->rand);
else
g_main_loop_quit (state->loop);
}
else if (state->count == 100)
{
assert_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy));
g_assert_not_reached ();
}
else
state->count++;
return G_SOURCE_CONTINUE;
}
static void
do_roundtrip (GDBusConnection *exporter_connection,
GDBusConnection *proxy_connection)
{
struct roundtrip_state state;
guint export_id;
guint id;
state.rand = g_rand_new_with_seed (g_test_rand_int ());
state.random = random_menu_new (state.rand, 2);
export_id = g_dbus_connection_export_menu_model (exporter_connection,
"/",
G_MENU_MODEL (state.random),
NULL);
state.proxy = g_dbus_menu_model_get (proxy_connection,
g_dbus_connection_get_unique_name (proxy_connection),
"/");
state.proxy_mirror = mirror_menu_new (G_MENU_MODEL (state.proxy));
state.count = 0;
state.success = 0;
id = g_timeout_add (10, roundtrip_step, &state);
state.loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (state.loop);
g_main_loop_unref (state.loop);
g_source_remove (id);
g_object_unref (state.proxy);
g_dbus_connection_unexport_menu_model (exporter_connection, export_id);
g_object_unref (state.random);
g_object_unref (state.proxy_mirror);
g_rand_free (state.rand);
}
static void
test_dbus_roundtrip (void)
{
GDBusConnection *bus;
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
do_roundtrip (bus, bus);
g_object_unref (bus);
}
static void
test_dbus_peer_roundtrip (void)
{
PeerConnection peer;
peer_connection_up (&peer);
do_roundtrip (peer.server_connection, peer.client_connection);
peer_connection_down (&peer);
}
static gint items_changed_count;
static void
items_changed (GMenuModel *model,
gint position,
gint removed,
gint added,
gpointer data)
{
items_changed_count++;
}
static gboolean
stop_loop (gpointer data)
{
GMainLoop *loop = data;
g_main_loop_quit (loop);
return G_SOURCE_REMOVE;
}
static void
do_subscriptions (GDBusConnection *exporter_connection,
GDBusConnection *proxy_connection)
{
GMenu *menu;
GDBusMenuModel *proxy;
GMainLoop *loop;
GError *error = NULL;
guint export_id;
guint timeout_id;
timeout_id = add_timeout (60);
loop = g_main_loop_new (NULL, FALSE);
menu = g_menu_new ();
export_id = g_dbus_connection_export_menu_model (exporter_connection,
"/",
G_MENU_MODEL (menu),
&error);
g_assert_no_error (error);
proxy = g_dbus_menu_model_get (proxy_connection,
g_dbus_connection_get_unique_name (proxy_connection),
"/");
items_changed_count = 0;
g_signal_connect (proxy, "items-changed",
G_CALLBACK (items_changed), NULL);
g_menu_append (menu, "item1", NULL);
g_menu_append (menu, "item2", NULL);
g_menu_append (menu, "item3", NULL);
g_assert_cmpint (items_changed_count, ==, 0);
/* We don't subscribe to change-notification until we look at the items */
g_timeout_add (100, stop_loop, loop);
g_main_loop_run (loop);
/* Looking at the items triggers subscription */
g_menu_model_get_n_items (G_MENU_MODEL (proxy));
while (items_changed_count < 1)
g_main_context_iteration (NULL, TRUE);
/* We get all three items in one batch */
g_assert_cmpint (items_changed_count, ==, 1);
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
/* If we wait, we don't get any more */
g_timeout_add (100, stop_loop, loop);
g_main_loop_run (loop);
g_assert_cmpint (items_changed_count, ==, 1);
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
/* Now we're subscribed, we get changes individually */
g_menu_append (menu, "item4", NULL);
g_menu_append (menu, "item5", NULL);
g_menu_append (menu, "item6", NULL);
g_menu_remove (menu, 0);
g_menu_remove (menu, 0);
while (items_changed_count < 6)
g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (items_changed_count, ==, 6);
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 4);
/* After destroying the proxy and waiting a bit, we don't get any more
* items-changed signals */
g_object_unref (proxy);
g_timeout_add (100, stop_loop, loop);
g_main_loop_run (loop);
g_menu_remove (menu, 0);
g_menu_remove (menu, 0);
g_timeout_add (100, stop_loop, loop);
g_main_loop_run (loop);
g_assert_cmpint (items_changed_count, ==, 6);
g_dbus_connection_unexport_menu_model (exporter_connection, export_id);
g_object_unref (menu);
g_main_loop_unref (loop);
cancel_timeout (timeout_id);
}
static void
test_dbus_subscriptions (void)
{
GDBusConnection *bus;
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
do_subscriptions (bus, bus);
g_object_unref (bus);
}
static void
test_dbus_peer_subscriptions (void)
{
PeerConnection peer;
peer_connection_up (&peer);
do_subscriptions (peer.server_connection, peer.client_connection);
peer_connection_down (&peer);
}
static gpointer
do_modify (gpointer data)
{
RandomMenu *menu = data;
GRand *rand;
gint i;
rand = g_rand_new_with_seed (g_test_rand_int ());
for (i = 0; i < 10000; i++)
{
random_menu_change (menu, rand);
}
return NULL;
}
static gpointer
do_export (gpointer data)
{
GMenuModel *menu = data;
gint i;
GDBusConnection *bus;
gchar *path;
GError *error = NULL;
guint id;
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
path = g_strdup_printf ("/%p", data);
for (i = 0; i < 10000; i++)
{
id = g_dbus_connection_export_menu_model (bus, path, menu, &error);
g_assert_no_error (error);
g_dbus_connection_unexport_menu_model (bus, id);
while (g_main_context_iteration (NULL, FALSE));
}
g_free (path);
g_object_unref (bus);
return NULL;
}
static void
test_dbus_threaded (void)
{
RandomMenu *menu[10];
GThread *call[10];
GThread *export[10];
gint i;
for (i = 0; i < 10; i++)
{
menu[i] = random_menu_new (g_rand_new_with_seed (g_test_rand_int ()), 2);
call[i] = g_thread_new ("call", do_modify, menu[i]);
export[i] = g_thread_new ("export", do_export, menu[i]);
}
for (i = 0; i < 10; i++)
{
g_thread_join (call[i]);
g_thread_join (export[i]);
}
for (i = 0; i < 10; i++)
g_object_unref (menu[i]);
}
static void
test_attributes (void)
{
GMenu *menu;
GMenuItem *item;
GVariant *v;
menu = g_menu_new ();
item = g_menu_item_new ("test", NULL);
g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
g_menu_item_set_attribute (item, "double", "d", 1.5);
v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
g_menu_item_set_attribute_value (item, "complex", v);
g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
g_menu_append_item (menu, item);
g_menu_item_set_attribute (item, "double", "d", G_PI);
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "boolean", NULL);
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
g_variant_unref (v);
v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "string", NULL);
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
g_variant_unref (v);
v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "double", NULL);
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
g_variant_unref (v);
v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "complex", NULL);
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
g_variant_unref (v);
g_menu_remove_all (menu);
g_object_unref (menu);
g_object_unref (item);
}
static void
test_attribute_iter (void)
{
GMenu *menu;
GMenuItem *item;
const gchar *name;
GVariant *v;
GMenuAttributeIter *iter;
GHashTable *found;
menu = g_menu_new ();
item = g_menu_item_new ("test", NULL);
g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
g_menu_item_set_attribute (item, "double", "d", 1.5);
v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
g_menu_item_set_attribute_value (item, "complex", v);
g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
g_menu_append_item (menu, item);
g_menu_item_set_attribute (item, "double", "d", G_PI);
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
found = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
iter = g_menu_model_iterate_item_attributes (G_MENU_MODEL (menu), 0);
while (g_menu_attribute_iter_get_next (iter, &name, &v))
g_hash_table_insert (found, g_strdup (name), v);
g_assert_cmpint (g_hash_table_size (found), ==, 6);
v = g_hash_table_lookup (found, "label");
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
v = g_hash_table_lookup (found, "boolean");
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
v = g_hash_table_lookup (found, "string");
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
v = g_hash_table_lookup (found, "double");
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
v = g_hash_table_lookup (found, "complex");
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
v = g_hash_table_lookup (found, "test-123");
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
g_hash_table_unref (found);
g_menu_remove_all (menu);
g_object_unref (menu);
g_object_unref (item);
}
static void
test_links (void)
{
GMenu *menu;
GMenuModel *m;
GMenuModel *x;
GMenuItem *item;
m = G_MENU_MODEL (g_menu_new ());
g_menu_append (G_MENU (m), "test", NULL);
menu = g_menu_new ();
item = g_menu_item_new ("test2", NULL);
g_menu_item_set_link (item, "submenu", m);
g_menu_prepend_item (menu, item);
item = g_menu_item_new ("test1", NULL);
g_menu_item_set_link (item, "section", m);
g_menu_insert_item (menu, 0, item);
item = g_menu_item_new ("test3", NULL);
g_menu_item_set_link (item, "wallet", m);
g_menu_insert_item (menu, 1000, item);
item = g_menu_item_new ("test4", NULL);
g_menu_item_set_link (item, "purse", m);
g_menu_item_set_link (item, "purse", NULL);
g_menu_append_item (menu, item);
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 4);
x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, "section");
g_assert (x == m);
g_object_unref (x);
x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 1, "submenu");
g_assert (x == m);
g_object_unref (x);
x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 2, "wallet");
g_assert (x == m);
g_object_unref (x);
x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 3, "purse");
g_assert (x == NULL);
g_object_unref (m);
g_object_unref (menu);
}
static void
test_mutable (void)
{
GMenu *menu;
menu = g_menu_new ();
g_menu_append (menu, "test", "test");
g_assert (g_menu_model_is_mutable (G_MENU_MODEL (menu)));
g_menu_freeze (menu);
g_assert (!g_menu_model_is_mutable (G_MENU_MODEL (menu)));
g_object_unref (menu);
}
static void
test_convenience (void)
{
GMenu *m1, *m2;
GMenu *sub;
GMenuItem *item;
m1 = g_menu_new ();
m2 = g_menu_new ();
sub = g_menu_new ();
g_menu_prepend (m1, "label1", "do::something");
g_menu_insert (m2, 0, "label1", "do::something");
g_menu_append (m1, "label2", "do::somethingelse");
g_menu_insert (m2, -1, "label2", "do::somethingelse");
g_menu_insert_section (m1, 10, "label3", G_MENU_MODEL (sub));
item = g_menu_item_new_section ("label3", G_MENU_MODEL (sub));
g_menu_insert_item (m2, 10, item);
g_object_unref (item);
g_menu_prepend_section (m1, "label4", G_MENU_MODEL (sub));
g_menu_insert_section (m2, 0, "label4", G_MENU_MODEL (sub));
g_menu_append_section (m1, "label5", G_MENU_MODEL (sub));
g_menu_insert_section (m2, -1, "label5", G_MENU_MODEL (sub));
g_menu_insert_submenu (m1, 5, "label6", G_MENU_MODEL (sub));
item = g_menu_item_new_submenu ("label6", G_MENU_MODEL (sub));
g_menu_insert_item (m2, 5, item);
g_object_unref (item);
g_menu_prepend_submenu (m1, "label7", G_MENU_MODEL (sub));
g_menu_insert_submenu (m2, 0, "label7", G_MENU_MODEL (sub));
g_menu_append_submenu (m1, "label8", G_MENU_MODEL (sub));
g_menu_insert_submenu (m2, -1, "label8", G_MENU_MODEL (sub));
assert_menus_equal (G_MENU_MODEL (m1), G_MENU_MODEL (m2));
g_object_unref (m1);
g_object_unref (m2);
}
static void
test_menuitem (void)
{
GMenu *menu;
GMenu *submenu;
GMenuItem *item;
GIcon *icon;
gboolean b;
gchar *s;
menu = g_menu_new ();
submenu = g_menu_new ();
item = g_menu_item_new ("label", "action");
g_menu_item_set_attribute (item, "attribute", "b", TRUE);
g_menu_item_set_link (item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu));
g_menu_append_item (menu, item);
icon = g_themed_icon_new ("bla");
g_menu_item_set_icon (item, icon);
g_object_unref (icon);
g_assert (g_menu_item_get_attribute (item, "attribute", "b", &b));
g_assert (b);
g_menu_item_set_action_and_target (item, "action", "(bs)", TRUE, "string");
g_assert (g_menu_item_get_attribute (item, "target", "(bs)", &b, &s));
g_assert (b);
g_assert_cmpstr (s, ==, "string");
g_free (s);
g_object_unref (item);
item = g_menu_item_new_from_model (G_MENU_MODEL (menu), 0);
assert_menuitem_equal (item, G_MENU_MODEL (menu), 0);
g_object_unref (item);
g_object_unref (menu);
g_object_unref (submenu);
}
/* Epilogue {{{1 */
int
main (int argc, char **argv)
{
gboolean ret;
g_test_init (&argc, &argv, NULL);
session_bus_up ();
g_test_add_func ("/gmenu/equality", test_equality);
g_test_add_func ("/gmenu/random", test_random);
g_test_add_func ("/gmenu/dbus/roundtrip", test_dbus_roundtrip);
g_test_add_func ("/gmenu/dbus/subscriptions", test_dbus_subscriptions);
g_test_add_func ("/gmenu/dbus/threaded", test_dbus_threaded);
g_test_add_func ("/gmenu/dbus/peer/roundtrip", test_dbus_peer_roundtrip);
g_test_add_func ("/gmenu/dbus/peer/subscriptions", test_dbus_peer_subscriptions);
g_test_add_func ("/gmenu/attributes", test_attributes);
g_test_add_func ("/gmenu/attributes/iterate", test_attribute_iter);
g_test_add_func ("/gmenu/links", test_links);
g_test_add_func ("/gmenu/mutable", test_mutable);
g_test_add_func ("/gmenu/convenience", test_convenience);
g_test_add_func ("/gmenu/menuitem", test_menuitem);
ret = g_test_run ();
session_bus_down ();
return ret;
}
/* vim:set foldmethod=marker: */