mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-04 02:06:18 +01:00
a0a94cd6c6
Create a 'mirror' model of the proxy for the testcase. In addition to testing that the proxy model emits the proper signals this also keeps the proxy alive (by holding references to it from the mirror). The previous code would create the submenu proxies and destroy them right away (from the recursive step in the equality comparison functions). This means that the subscription would go out over D-Bus and the proxy would be destroyed before it returned. Keeping the model alive allows it to be actually updated.
1022 lines
26 KiB
C
1022 lines
26 KiB
C
#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;
|
|
MirrorMenu *proxy_mirror;
|
|
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)) &&
|
|
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
|
|
test_dbus_roundtrip (void)
|
|
{
|
|
struct roundtrip_state state;
|
|
GDBusConnection *bus;
|
|
guint id;
|
|
|
|
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, 2);
|
|
g_menu_model_dbus_export_start (bus, "/", G_MENU_MODEL (state.random), NULL);
|
|
g_assert (g_menu_model_dbus_export_query (G_MENU_MODEL (state.random), NULL, NULL));
|
|
state.proxy = g_menu_proxy_get (bus, g_dbus_connection_get_unique_name (bus), "/");
|
|
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_menu_model_dbus_export_stop (G_MENU_MODEL (state.random));
|
|
g_assert (!g_menu_model_dbus_export_query (G_MENU_MODEL (state.random), NULL, NULL));
|
|
g_object_unref (state.random);
|
|
g_object_unref (state.proxy_mirror);
|
|
g_rand_free (state.rand);
|
|
g_object_unref (bus);
|
|
}
|
|
|
|
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
|
|
test_dbus_subscriptions (void)
|
|
{
|
|
GDBusConnection *bus;
|
|
GMenu *menu;
|
|
GMenuProxy *proxy;
|
|
GMainLoop *loop;
|
|
GError *error = NULL;
|
|
|
|
loop = g_main_loop_new (NULL, FALSE);
|
|
|
|
bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
|
|
|
menu = g_menu_new ();
|
|
|
|
g_menu_model_dbus_export_start (bus, "/", G_MENU_MODEL (menu), &error);
|
|
g_assert_no_error (error);
|
|
|
|
proxy = g_menu_proxy_get (bus, g_dbus_connection_get_unique_name (bus), "/");
|
|
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);
|
|
|
|
g_timeout_add (100, stop_loop, loop);
|
|
g_main_loop_run (loop);
|
|
|
|
g_menu_model_get_n_items (G_MENU_MODEL (proxy));
|
|
|
|
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);
|
|
|
|
g_timeout_add (100, stop_loop, loop);
|
|
g_main_loop_run (loop);
|
|
|
|
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);
|
|
|
|
g_timeout_add (200, stop_loop, loop);
|
|
g_main_loop_run (loop);
|
|
|
|
g_assert_cmpint (items_changed_count, ==, 6);
|
|
|
|
g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 4);
|
|
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_menu_model_dbus_export_stop (G_MENU_MODEL (menu));
|
|
g_object_unref (menu);
|
|
|
|
g_main_loop_unref (loop);
|
|
}
|
|
|
|
static void
|
|
start_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
const gchar **attribute_names,
|
|
const gchar **attribute_values,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
if (g_strcmp0 (element_name, "menu") == 0)
|
|
g_menu_markup_parser_start_menu (context, "domain", NULL);
|
|
}
|
|
|
|
static void
|
|
end_element (GMarkupParseContext *context,
|
|
const gchar *element_name,
|
|
gpointer user_data,
|
|
GError **error)
|
|
{
|
|
GMenu **menu = user_data;
|
|
|
|
if (g_strcmp0 (element_name, "menu") == 0)
|
|
*menu = g_menu_markup_parser_end_menu (context);
|
|
}
|
|
|
|
static GMenuModel *
|
|
parse_menu_string (const gchar *string, GError **error)
|
|
{
|
|
const GMarkupParser parser = {
|
|
start_element, end_element, NULL, NULL, NULL
|
|
};
|
|
GMarkupParseContext *context;
|
|
GMenuModel *menu = NULL;
|
|
|
|
context = g_markup_parse_context_new (&parser, 0, &menu, NULL);
|
|
g_markup_parse_context_parse (context, string, -1, error);
|
|
g_markup_parse_context_free (context);
|
|
|
|
return menu;
|
|
}
|
|
|
|
static gchar *
|
|
menu_to_string (GMenuModel *menu)
|
|
{
|
|
GString *s;
|
|
|
|
s = g_string_new ("<menu>\n");
|
|
g_menu_markup_print_string (s, menu, 2, 2);
|
|
g_string_append (s, "</menu>\n");
|
|
|
|
return g_string_free (s, FALSE);
|
|
}
|
|
|
|
static void
|
|
test_markup_roundtrip (void)
|
|
{
|
|
const gchar data[] =
|
|
"<menu id='edit-menu'>\n"
|
|
" <section>\n"
|
|
" <item action='undo'>\n"
|
|
" <attribute name='label' translatable='yes' context='Stock label'>'_Undo'</attribute>\n"
|
|
" </item>\n"
|
|
" <item label='Redo' action='redo'/>\n"
|
|
" </section>\n"
|
|
" <section></section>\n"
|
|
" <section label='Copy & Paste'>\n"
|
|
" <item label='Cut' action='cut'/>\n"
|
|
" <item label='Copy' action='copy'/>\n"
|
|
" <item label='Paste' action='paste'/>\n"
|
|
" </section>\n"
|
|
" <section>\n"
|
|
" <item label='Bold' action='bold'/>\n"
|
|
" <submenu label='Language'>\n"
|
|
" <item label='Latin' action='lang' target='latin'/>\n"
|
|
" <item label='Greek' action='lang' target='greek'/>\n"
|
|
" <item label='Urdu' action='lang' target='urdu'/>\n"
|
|
" </submenu>\n"
|
|
" <item name='test unusual attributes'>\n"
|
|
" <attribute name='action' type='s'>'quite-some-action'</attribute>\n"
|
|
" <attribute name='target' type='i'>36</attribute>\n"
|
|
" <attribute name='chocolate-thunda' type='as'>['a','b']</attribute>\n"
|
|
" <attribute name='thing1' type='g'>'s(uu)'</attribute>\n"
|
|
" <attribute name='icon' type='s'>'small blue thing'</attribute>\n"
|
|
" </item>\n"
|
|
" </section>\n"
|
|
"</menu>\n";
|
|
GError *error = NULL;
|
|
GMenuModel *a;
|
|
GMenuModel *b;
|
|
gchar *s;
|
|
gchar *s2;
|
|
|
|
a = parse_menu_string (data, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (G_IS_MENU_MODEL (a));
|
|
|
|
/* normalized representation */
|
|
s = menu_to_string (a);
|
|
|
|
b = parse_menu_string (s, &error);
|
|
g_assert_no_error (error);
|
|
g_assert (G_IS_MENU_MODEL (b));
|
|
|
|
assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
|
|
|
|
s2 = menu_to_string (b);
|
|
|
|
g_assert_cmpstr (s, ==, s2);
|
|
|
|
g_object_unref (a);
|
|
g_object_unref (b);
|
|
g_free (s);
|
|
g_free (s2);
|
|
}
|
|
|
|
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_value (item, "double", g_variant_new_double (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_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_object_unref (menu);
|
|
}
|
|
|
|
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 ("test1", NULL);
|
|
g_menu_item_set_link (item, "section", m);
|
|
g_menu_append_item (menu, item);
|
|
|
|
item = g_menu_item_new ("test2", NULL);
|
|
g_menu_item_set_link (item, "submenu", m);
|
|
g_menu_append_item (menu, item);
|
|
|
|
item = g_menu_item_new ("test3", NULL);
|
|
g_menu_item_set_link (item, "wallet", m);
|
|
g_menu_append_item (menu, 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_misc (void)
|
|
{
|
|
/* trying to use most of the GMenu api for constructing the
|
|
* same menu two different ways
|
|
*/
|
|
GMenu *a, *m, *m2;
|
|
GMenuModel *b;
|
|
GMenuItem *item;
|
|
const gchar *s;
|
|
|
|
a = g_menu_new ();
|
|
item = g_menu_item_new ("test1", "action1::target1");
|
|
g_menu_prepend_item (a, item);
|
|
g_object_unref (item);
|
|
|
|
m = g_menu_new ();
|
|
g_menu_prepend (m, "test2a", "action2");
|
|
g_menu_append (m, "test2c", NULL);
|
|
g_menu_insert (m, 1, "test2b", NULL);
|
|
|
|
item = g_menu_item_new_submenu ("test2", G_MENU_MODEL (m));
|
|
g_menu_append_item (a, item);
|
|
g_object_unref (item);
|
|
g_object_unref (m);
|
|
|
|
m = g_menu_new ();
|
|
|
|
m2 = g_menu_new ();
|
|
g_menu_append (m2, "x", NULL);
|
|
g_menu_prepend_section (m, "test3a", G_MENU_MODEL (m2));
|
|
g_object_unref (m2);
|
|
|
|
item = g_menu_item_new_section ("test3", G_MENU_MODEL (m));
|
|
g_menu_insert_item (a, -1, item);
|
|
g_object_unref (item);
|
|
g_object_unref (m);
|
|
|
|
s = ""
|
|
"<menu>"
|
|
" <item target='target1' action='action1' label='test1'/>"
|
|
" <item label='test2'>"
|
|
" <link name='submenu'>"
|
|
" <item action='action2' label='test2a'/>"
|
|
" <item label='test2b'/>"
|
|
" <item label='test2c'/>"
|
|
" </link>"
|
|
" </item>"
|
|
" <item label='test3'>"
|
|
" <link name='section'>"
|
|
" <item label='test3a'>"
|
|
" <link name='section'>"
|
|
" <item label='x'/>"
|
|
" </link>"
|
|
" </item>"
|
|
" </link>"
|
|
" </item>"
|
|
"</menu>";
|
|
|
|
b = parse_menu_string (s, NULL);
|
|
|
|
assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
|
|
g_object_unref (a);
|
|
g_object_unref (b);
|
|
}
|
|
|
|
/* 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/dbus/roundtrip", test_dbus_roundtrip);
|
|
g_test_add_func ("/gmenu/dbus/subscriptions", test_dbus_subscriptions);
|
|
g_test_add_func ("/gmenu/markup/roundtrip", test_markup_roundtrip);
|
|
g_test_add_func ("/gmenu/attributes", test_attributes);
|
|
g_test_add_func ("/gmenu/links", test_links);
|
|
g_test_add_func ("/gmenu/mutable", test_mutable);
|
|
g_test_add_func ("/gmenu/misc", test_misc);
|
|
|
|
return g_test_run ();
|
|
}
|
|
/* vim:set foldmethod=marker: */
|