#include <gio/gio.h>
+#include "gdbus-sessionbus.h"
+
+/* 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
}
}
+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)
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) */
struct roundtrip_state
{
RandomMenu *random;
- GMenuProxy *proxy;
+ MirrorMenu *proxy_mirror;
+ GDBusMenuModel *proxy;
GMainLoop *loop;
GRand *rand;
gint success;
{
struct roundtrip_state *state = data;
- if (check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy)))
+ 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;
}
static void
-test_roundtrip (void)
+test_dbus_roundtrip (void)
{
struct roundtrip_state state;
GDBusConnection *bus;
+ guint export_id;
+ 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, 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.random = random_menu_new (state.rand, 2);
+ export_id = g_dbus_connection_export_menu_model (bus, "/", G_MENU_MODEL (state.random), NULL);
+ state.proxy = g_dbus_menu_model_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;
- g_timeout_add (10, roundtrip_step, &state);
+ 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_exporter_stop (G_MENU_MODEL (state.random));
+ g_dbus_connection_unexport_menu_model (bus, export_id);
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;
+ GDBusMenuModel *proxy;
+ GMainLoop *loop;
+ GError *error = NULL;
+ guint export_id;
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+ menu = g_menu_new ();
+
+ export_id = g_dbus_connection_export_menu_model (bus, "/", G_MENU_MODEL (menu), &error);
+ g_assert_no_error (error);
+
+ proxy = g_dbus_menu_model_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_dbus_connection_unexport_menu_model (bus, export_id);
+ g_object_unref (menu);
+
+ g_main_loop_unref (loop);
+ g_object_unref (bus);
+}
+
+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_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);
- g_type_init ();
+ session_bus_up ();
g_test_add_func ("/gmenu/equality", test_equality);
g_test_add_func ("/gmenu/random", test_random);
- g_test_add_func ("/gmenu/roundtrip", test_roundtrip);
+ 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/attributes", test_attributes);
+ 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 g_test_run ();
+ return ret;
}
/* vim:set foldmethod=marker: */