gio: Add newer dbus UnknownXxxx and PropertyReadOnly errors
[platform/upstream/glib.git] / gio / tests / gmenumodel.c
index aa42943..75c70cd 100644 (file)
@@ -1,5 +1,127 @@
 #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
@@ -464,6 +586,49 @@ assert_menus_equal (GMenuModel *a,
     }
 }
 
+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)
@@ -554,7 +719,7 @@ struct roundtrip_state
 {
   RandomMenu *random;
   MirrorMenu *proxy_mirror;
-  GMenuProxy *proxy;
+  GDBusMenuModel *proxy;
   GMainLoop *loop;
   GRand *rand;
   gint success;
@@ -602,7 +767,7 @@ test_dbus_roundtrip (void)
 
   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_menu_proxy_get (bus, g_dbus_connection_get_unique_name (bus), "/");
+  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;
@@ -649,7 +814,7 @@ test_dbus_subscriptions (void)
 {
   GDBusConnection *bus;
   GMenu *menu;
-  GMenuProxy *proxy;
+  GDBusMenuModel *proxy;
   GMainLoop *loop;
   GError *error = NULL;
   guint export_id;
@@ -663,7 +828,7 @@ test_dbus_subscriptions (void)
   export_id = g_dbus_connection_export_menu_model (bus, "/", G_MENU_MODEL (menu), &error);
   g_assert_no_error (error);
 
-  proxy = g_menu_proxy_get (bus, g_dbus_connection_get_unique_name (bus), "/");
+  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);
@@ -717,120 +882,77 @@ test_dbus_subscriptions (void)
   g_object_unref (menu);
 
   g_main_loop_unref (loop);
+  g_object_unref (bus);
 }
 
-static void
-start_element (GMarkupParseContext *context,
-               const gchar         *element_name,
-               const gchar        **attribute_names,
-               const gchar        **attribute_values,
-               gpointer             user_data,
-               GError             **error)
+static gpointer
+do_modify (gpointer data)
 {
-  if (g_strcmp0 (element_name, "menu") == 0)
-    g_menu_markup_parser_start_menu (context, "domain", NULL);
-}
+  RandomMenu *menu = data;
+  GRand *rand;
+  gint i;
 
-static void
-end_element (GMarkupParseContext *context,
-             const gchar         *element_name,
-             gpointer             user_data,
-             GError             **error)
-{
-  GMenu **menu = user_data;
+  rand = g_rand_new_with_seed (g_test_rand_int ());
 
-  if (g_strcmp0 (element_name, "menu") == 0)
-    *menu = g_menu_markup_parser_end_menu (context);
+  for (i = 0; i < 10000; i++)
+    {
+      random_menu_change (menu, rand);
+    }
+
+  return NULL;
 }
 
-static GMenuModel *
-parse_menu_string (const gchar *string, GError **error)
+static gpointer
+do_export (gpointer data)
 {
-  const GMarkupParser parser = {
-    start_element, end_element, NULL, NULL, NULL
-  };
-  GMarkupParseContext *context;
-  GMenuModel *menu = NULL;
+  GMenuModel *menu = data;
+  gint i;
+  GDBusConnection *bus;
+  gchar *path;
+  GError *error = NULL;
+  guint id;
 
-  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);
+  bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+  path = g_strdup_printf ("/%p", data);
 
-  return menu;
-}
+  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));
+    }
 
-static gchar *
-menu_to_string (GMenuModel *menu)
-{
-  GString *s;
+  g_free (path);
 
-  s = g_string_new ("<menu>\n");
-  g_menu_markup_print_string (s, menu, 2, 2);
-  g_string_append (s, "</menu>\n");
+  g_object_unref (bus);
 
-  return g_string_free (s, FALSE);
+  return NULL;
 }
 
 static void
-test_markup_roundtrip (void)
+test_dbus_threaded (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 &amp; 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));
+  RandomMenu *menu[10];
+  GThread *call[10];
+  GThread *export[10];
+  gint i;
 
-  s2 = menu_to_string (b);
+  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]);
+    }
 
-  g_assert_cmpstr (s, ==, s2);
+  for (i = 0; i < 10; i++)
+    {
+      g_thread_join (call[i]);
+      g_thread_join (export[i]);
+    }
 
-  g_object_unref (a);
-  g_object_unref (b);
-  g_free (s);
-  g_free (s2);
+  for (i = 0; i < 10; i++)
+    g_object_unref (menu[i]);
 }
 
 static void
@@ -845,13 +967,16 @@ test_attributes (void)
   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));
+
+  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);
@@ -870,7 +995,10 @@ test_attributes (void)
   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
@@ -886,17 +1014,17 @@ test_links (void)
 
   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);
+  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_append_item (menu, item);
+  g_menu_insert_item (menu, 1000, item);
 
   item = g_menu_item_new ("test4", NULL);
   g_menu_item_set_link (item, "purse", m);
@@ -940,89 +1068,116 @@ test_mutable (void)
 }
 
 static void
-test_misc (void)
+test_convenience (void)
 {
-  /* trying to use most of the GMenu api for constructing the
-   * same menu two different ways
-   */
-  GMenu *a, *m, *m2;
-  GMenuModel *b;
+  GMenu *m1, *m2;
+  GMenu *sub;
   GMenuItem *item;
-  const gchar *s;
 
-  a = g_menu_new ();
-  item = g_menu_item_new ("test1", "action1::target1");
-  g_menu_prepend_item (a, 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);
 
-  m = g_menu_new ();
-  g_menu_prepend (m, "test2a", "action2");
-  g_menu_append (m, "test2c", NULL);
-  g_menu_insert (m, 1, "test2b", NULL);
+  g_menu_prepend_section (m1, "label4", G_MENU_MODEL (sub));
+  g_menu_insert_section (m2, 0, "label4", G_MENU_MODEL (sub));
 
-  item = g_menu_item_new_submenu ("test2", G_MENU_MODEL (m));
-  g_menu_append_item (a, item);
+  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_object_unref (m);
 
-  m = g_menu_new ();
+  g_menu_prepend_submenu (m1, "label7", G_MENU_MODEL (sub));
+  g_menu_insert_submenu (m2, 0, "label7", G_MENU_MODEL (sub));
 
-  m2 = g_menu_new ();
-  g_menu_append (m2, "x", NULL);
-  g_menu_prepend_section (m, "test3a", G_MENU_MODEL (m2));
+  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);
 
-  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);
+  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 */
+/* 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/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/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/misc", test_misc);
+  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: */