Add GMenuModel testcases

This commit is contained in:
Ryan Lortie 2011-10-21 23:51:48 -04:00
parent d110fd9202
commit 6dfb9d583f
3 changed files with 629 additions and 0 deletions

View File

@ -57,6 +57,7 @@ gdbus-serialization
gdbus-test-codegen
gdbus-test-codegen-generated*
gdbus-threading
gmenumodel
g-file
g-file-info
g-icon

View File

@ -58,6 +58,7 @@ TEST_PROGS += \
vfs \
network-monitor \
fileattributematcher \
gmenumodel \
$(NULL)
if OS_UNIX
@ -426,6 +427,8 @@ gapplication_example_cmdline3_LDADD = $(progs_ldadd)
gapplication_example_actions_SOURCES = gapplication-example-actions.c
gapplication_example_actions_LDADD = $(progs_ldadd)
gmenumodel_LDADD = $(progs_ldadd)
schema_tests = \
schema-tests/array-default-not-in-choices.gschema.xml \
schema-tests/bad-choice.gschema.xml \

625
gio/tests/gmenumodel.c Normal file
View File

@ -0,0 +1,625 @@
#include <gio/gio.h>
/* 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);
}
}
/* 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);
}
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);
}
struct roundtrip_state
{
RandomMenu *random;
GMenuProxy *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)))
{
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
test_roundtrip (void)
{
struct roundtrip_state state;
GDBusConnection *bus;
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
state.rand = g_rand_new_with_seed (g_test_rand_int ());
state.random = random_menu_new (state.rand, TOP_ORDER);
g_menu_exporter_export (bus, "/", G_MENU_MODEL (state.random), NULL);
state.proxy = g_menu_proxy_get (bus, g_dbus_connection_get_unique_name (bus), "/");
state.count = 0;
state.success = 0;
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_object_unref (state.proxy);
g_menu_exporter_stop (G_MENU_MODEL (state.random));
g_object_unref (state.random);
g_rand_free (state.rand);
g_object_unref (bus);
}
/* Epilogue {{{1 */
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_type_init ();
g_test_add_func ("/gmenu/equality", test_equality);
g_test_add_func ("/gmenu/random", test_random);
g_test_add_func ("/gmenu/roundtrip", test_roundtrip);
return g_test_run ();
}
/* vim:set foldmethod=marker: */