Add a test for ids on submenu and section elements
[glib.git] / gio / tests / gmenumodel.c
blob4253f99261dd978dccc12af2873c1667a057ec19
1 #include <gio/gio.h>
3 /* TestItem {{{1 */
5 /* This utility struct is used by both the RandomMenu and MirrorMenu
6 * class implementations below.
7 */
8 typedef struct {
9 GHashTable *attributes;
10 GHashTable *links;
11 } TestItem;
13 static TestItem *
14 test_item_new (GHashTable *attributes,
15 GHashTable *links)
17 TestItem *item;
19 item = g_slice_new (TestItem);
20 item->attributes = g_hash_table_ref (attributes);
21 item->links = g_hash_table_ref (links);
23 return item;
26 static void
27 test_item_free (gpointer data)
29 TestItem *item = data;
31 g_hash_table_unref (item->attributes);
32 g_hash_table_unref (item->links);
34 g_slice_free (TestItem, item);
37 /* RandomMenu {{{1 */
38 #define MAX_ITEMS 5
39 #define TOP_ORDER 4
41 typedef struct {
42 GMenuModel parent_instance;
44 GSequence *items;
45 gint order;
46 } RandomMenu;
48 typedef GMenuModelClass RandomMenuClass;
50 static GType random_menu_get_type (void);
51 G_DEFINE_TYPE (RandomMenu, random_menu, G_TYPE_MENU_MODEL);
53 static gboolean
54 random_menu_is_mutable (GMenuModel *model)
56 return TRUE;
59 static gint
60 random_menu_get_n_items (GMenuModel *model)
62 RandomMenu *menu = (RandomMenu *) model;
64 return g_sequence_get_length (menu->items);
67 static void
68 random_menu_get_item_attributes (GMenuModel *model,
69 gint position,
70 GHashTable **table)
72 RandomMenu *menu = (RandomMenu *) model;
73 TestItem *item;
75 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
76 *table = g_hash_table_ref (item->attributes);
79 static void
80 random_menu_get_item_links (GMenuModel *model,
81 gint position,
82 GHashTable **table)
84 RandomMenu *menu = (RandomMenu *) model;
85 TestItem *item;
87 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
88 *table = g_hash_table_ref (item->links);
91 static void
92 random_menu_finalize (GObject *object)
94 RandomMenu *menu = (RandomMenu *) object;
96 g_sequence_free (menu->items);
98 G_OBJECT_CLASS (random_menu_parent_class)
99 ->finalize (object);
102 static void
103 random_menu_init (RandomMenu *menu)
107 static void
108 random_menu_class_init (GMenuModelClass *class)
110 GObjectClass *object_class = G_OBJECT_CLASS (class);
112 class->is_mutable = random_menu_is_mutable;
113 class->get_n_items = random_menu_get_n_items;
114 class->get_item_attributes = random_menu_get_item_attributes;
115 class->get_item_links = random_menu_get_item_links;
117 object_class->finalize = random_menu_finalize;
120 static RandomMenu * random_menu_new (GRand *rand, gint order);
122 static void
123 random_menu_change (RandomMenu *menu,
124 GRand *rand)
126 gint position, removes, adds;
127 GSequenceIter *point;
128 gint n_items;
129 gint i;
131 n_items = g_sequence_get_length (menu->items);
135 position = g_rand_int_range (rand, 0, n_items + 1);
136 removes = g_rand_int_range (rand, 0, n_items - position + 1);
137 adds = g_rand_int_range (rand, 0, MAX_ITEMS - (n_items - removes) + 1);
139 while (removes == 0 && adds == 0);
141 point = g_sequence_get_iter_at_pos (menu->items, position + removes);
143 if (removes)
145 GSequenceIter *start;
147 start = g_sequence_get_iter_at_pos (menu->items, position);
148 g_sequence_remove_range (start, point);
151 for (i = 0; i < adds; i++)
153 const gchar *label;
154 GHashTable *links;
155 GHashTable *attributes;
157 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
158 links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
160 if (menu->order > 0 && g_rand_boolean (rand))
162 RandomMenu *child;
163 const gchar *subtype;
165 child = random_menu_new (rand, menu->order - 1);
167 if (g_rand_boolean (rand))
169 subtype = G_MENU_LINK_SECTION;
170 /* label some section headers */
171 if (g_rand_boolean (rand))
172 label = "Section";
173 else
174 label = NULL;
176 else
178 /* label all submenus */
179 subtype = G_MENU_LINK_SUBMENU;
180 label = "Submenu";
183 g_hash_table_insert (links, g_strdup (subtype), child);
185 else
186 /* label all terminals */
187 label = "Menu Item";
189 if (label)
190 g_hash_table_insert (attributes, g_strdup ("label"), g_variant_ref_sink (g_variant_new_string (label)));
192 g_sequence_insert_before (point, test_item_new (attributes, links));
193 g_hash_table_unref (links);
194 g_hash_table_unref (attributes);
197 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removes, adds);
200 static RandomMenu *
201 random_menu_new (GRand *rand,
202 gint order)
204 RandomMenu *menu;
206 menu = g_object_new (random_menu_get_type (), NULL);
207 menu->items = g_sequence_new (test_item_free);
208 menu->order = order;
210 random_menu_change (menu, rand);
212 return menu;
215 /* MirrorMenu {{{1 */
216 typedef struct {
217 GMenuModel parent_instance;
219 GMenuModel *clone_of;
220 GSequence *items;
221 gulong handler_id;
222 } MirrorMenu;
224 typedef GMenuModelClass MirrorMenuClass;
226 static GType mirror_menu_get_type (void);
227 G_DEFINE_TYPE (MirrorMenu, mirror_menu, G_TYPE_MENU_MODEL);
229 static gboolean
230 mirror_menu_is_mutable (GMenuModel *model)
232 MirrorMenu *menu = (MirrorMenu *) model;
234 return menu->handler_id != 0;
237 static gint
238 mirror_menu_get_n_items (GMenuModel *model)
240 MirrorMenu *menu = (MirrorMenu *) model;
242 return g_sequence_get_length (menu->items);
245 static void
246 mirror_menu_get_item_attributes (GMenuModel *model,
247 gint position,
248 GHashTable **table)
250 MirrorMenu *menu = (MirrorMenu *) model;
251 TestItem *item;
253 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
254 *table = g_hash_table_ref (item->attributes);
257 static void
258 mirror_menu_get_item_links (GMenuModel *model,
259 gint position,
260 GHashTable **table)
262 MirrorMenu *menu = (MirrorMenu *) model;
263 TestItem *item;
265 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
266 *table = g_hash_table_ref (item->links);
269 static void
270 mirror_menu_finalize (GObject *object)
272 MirrorMenu *menu = (MirrorMenu *) object;
274 if (menu->handler_id)
275 g_signal_handler_disconnect (menu->clone_of, menu->handler_id);
277 g_sequence_free (menu->items);
278 g_object_unref (menu->clone_of);
280 G_OBJECT_CLASS (mirror_menu_parent_class)
281 ->finalize (object);
284 static void
285 mirror_menu_init (MirrorMenu *menu)
289 static void
290 mirror_menu_class_init (GMenuModelClass *class)
292 GObjectClass *object_class = G_OBJECT_CLASS (class);
294 class->is_mutable = mirror_menu_is_mutable;
295 class->get_n_items = mirror_menu_get_n_items;
296 class->get_item_attributes = mirror_menu_get_item_attributes;
297 class->get_item_links = mirror_menu_get_item_links;
299 object_class->finalize = mirror_menu_finalize;
302 static MirrorMenu * mirror_menu_new (GMenuModel *clone_of);
304 static void
305 mirror_menu_changed (GMenuModel *model,
306 gint position,
307 gint removed,
308 gint added,
309 gpointer user_data)
311 MirrorMenu *menu = user_data;
312 GSequenceIter *point;
313 gint i;
315 g_assert (model == menu->clone_of);
317 point = g_sequence_get_iter_at_pos (menu->items, position + removed);
319 if (removed)
321 GSequenceIter *start;
323 start = g_sequence_get_iter_at_pos (menu->items, position);
324 g_sequence_remove_range (start, point);
327 for (i = position; i < position + added; i++)
329 GMenuAttributeIter *attr_iter;
330 GMenuLinkIter *link_iter;
331 GHashTable *links;
332 GHashTable *attributes;
333 const gchar *name;
334 GMenuModel *child;
335 GVariant *value;
337 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
338 links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
340 attr_iter = g_menu_model_iterate_item_attributes (model, i);
341 while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
343 g_hash_table_insert (attributes, g_strdup (name), value);
345 g_object_unref (attr_iter);
347 link_iter = g_menu_model_iterate_item_links (model, i);
348 while (g_menu_link_iter_get_next (link_iter, &name, &child))
350 g_hash_table_insert (links, g_strdup (name), mirror_menu_new (child));
351 g_object_unref (child);
353 g_object_unref (link_iter);
355 g_sequence_insert_before (point, test_item_new (attributes, links));
356 g_hash_table_unref (attributes);
357 g_hash_table_unref (links);
360 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removed, added);
363 static MirrorMenu *
364 mirror_menu_new (GMenuModel *clone_of)
366 MirrorMenu *menu;
368 menu = g_object_new (mirror_menu_get_type (), NULL);
369 menu->items = g_sequence_new (test_item_free);
370 menu->clone_of = g_object_ref (clone_of);
372 if (g_menu_model_is_mutable (clone_of))
373 menu->handler_id = g_signal_connect (clone_of, "items-changed", G_CALLBACK (mirror_menu_changed), menu);
374 mirror_menu_changed (clone_of, 0, 0, g_menu_model_get_n_items (clone_of), menu);
376 return menu;
379 /* check_menus_equal(), assert_menus_equal() {{{1 */
380 static gboolean
381 check_menus_equal (GMenuModel *a,
382 GMenuModel *b)
384 gboolean equal = TRUE;
385 gint a_n, b_n;
386 gint i;
388 a_n = g_menu_model_get_n_items (a);
389 b_n = g_menu_model_get_n_items (b);
391 if (a_n != b_n)
392 return FALSE;
394 for (i = 0; i < a_n; i++)
396 GMenuAttributeIter *attr_iter;
397 GVariant *a_value, *b_value;
398 GMenuLinkIter *link_iter;
399 GMenuModel *a_menu, *b_menu;
400 const gchar *name;
402 attr_iter = g_menu_model_iterate_item_attributes (a, i);
403 while (g_menu_attribute_iter_get_next (attr_iter, &name, &a_value))
405 b_value = g_menu_model_get_item_attribute_value (b, i, name, NULL);
406 equal &= b_value && g_variant_equal (a_value, b_value);
407 if (b_value)
408 g_variant_unref (b_value);
409 g_variant_unref (a_value);
411 g_object_unref (attr_iter);
413 attr_iter = g_menu_model_iterate_item_attributes (b, i);
414 while (g_menu_attribute_iter_get_next (attr_iter, &name, &b_value))
416 a_value = g_menu_model_get_item_attribute_value (a, i, name, NULL);
417 equal &= a_value && g_variant_equal (a_value, b_value);
418 if (a_value)
419 g_variant_unref (a_value);
420 g_variant_unref (b_value);
422 g_object_unref (attr_iter);
424 link_iter = g_menu_model_iterate_item_links (a, i);
425 while (g_menu_link_iter_get_next (link_iter, &name, &a_menu))
427 b_menu = g_menu_model_get_item_link (b, i, name);
428 equal &= b_menu && check_menus_equal (a_menu, b_menu);
429 if (b_menu)
430 g_object_unref (b_menu);
431 g_object_unref (a_menu);
433 g_object_unref (link_iter);
435 link_iter = g_menu_model_iterate_item_links (b, i);
436 while (g_menu_link_iter_get_next (link_iter, &name, &b_menu))
438 a_menu = g_menu_model_get_item_link (a, i, name);
439 equal &= a_menu && check_menus_equal (a_menu, b_menu);
440 if (a_menu)
441 g_object_unref (a_menu);
442 g_object_unref (b_menu);
444 g_object_unref (link_iter);
447 return equal;
450 static void
451 assert_menus_equal (GMenuModel *a,
452 GMenuModel *b)
454 if (!check_menus_equal (a, b))
456 GString *string;
458 string = g_string_new ("\n <a>\n");
459 g_menu_markup_print_string (string, G_MENU_MODEL (a), 4, 2);
460 g_string_append (string, " </a>\n\n-------------\n <b>\n");
461 g_menu_markup_print_string (string, G_MENU_MODEL (b), 4, 2);
462 g_string_append (string, " </b>\n");
463 g_error ("%s", string->str);
467 /* Test cases {{{1 */
468 static void
469 test_equality (void)
471 GRand *randa, *randb;
472 guint32 seed;
473 gint i;
475 seed = g_test_rand_int ();
477 randa = g_rand_new_with_seed (seed);
478 randb = g_rand_new_with_seed (seed);
480 for (i = 0; i < 500; i++)
482 RandomMenu *a, *b;
484 a = random_menu_new (randa, TOP_ORDER);
485 b = random_menu_new (randb, TOP_ORDER);
486 assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
487 g_object_unref (b);
488 g_object_unref (a);
491 g_rand_int (randa);
493 for (i = 0; i < 500;)
495 RandomMenu *a, *b;
497 a = random_menu_new (randa, TOP_ORDER);
498 b = random_menu_new (randb, TOP_ORDER);
499 if (check_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b)))
501 /* by chance, they may really be equal. double check. */
502 GString *as, *bs;
504 as = g_menu_markup_print_string (NULL, G_MENU_MODEL (a), 4, 2);
505 bs = g_menu_markup_print_string (NULL, G_MENU_MODEL (b), 4, 2);
506 g_assert_cmpstr (as->str, ==, bs->str);
507 g_string_free (bs, TRUE);
508 g_string_free (as, TRUE);
510 /* we're here because randa and randb just generated equal
511 * menus. they may do it again, so throw away randb and make
512 * a fresh one.
514 g_rand_free (randb);
515 randb = g_rand_new_with_seed (g_rand_int (randa));
517 else
518 /* make sure we get enough unequals (ie: no GRand failure) */
519 i++;
521 g_object_unref (b);
522 g_object_unref (a);
525 g_rand_free (randb);
526 g_rand_free (randa);
529 static void
530 test_random (void)
532 RandomMenu *random;
533 MirrorMenu *mirror;
534 GRand *rand;
535 gint i;
537 rand = g_rand_new_with_seed (g_test_rand_int ());
538 random = random_menu_new (rand, TOP_ORDER);
539 mirror = mirror_menu_new (G_MENU_MODEL (random));
541 for (i = 0; i < 500; i++)
543 assert_menus_equal (G_MENU_MODEL (random), G_MENU_MODEL (mirror));
544 random_menu_change (random, rand);
547 g_object_unref (mirror);
548 g_object_unref (random);
550 g_rand_free (rand);
553 struct roundtrip_state
555 RandomMenu *random;
556 MirrorMenu *proxy_mirror;
557 GDBusMenuModel *proxy;
558 GMainLoop *loop;
559 GRand *rand;
560 gint success;
561 gint count;
564 static gboolean
565 roundtrip_step (gpointer data)
567 struct roundtrip_state *state = data;
569 if (check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy)) &&
570 check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy_mirror)))
572 state->success++;
573 state->count = 0;
575 if (state->success < 100)
576 random_menu_change (state->random, state->rand);
577 else
578 g_main_loop_quit (state->loop);
580 else if (state->count == 100)
582 assert_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy));
583 g_assert_not_reached ();
585 else
586 state->count++;
588 return G_SOURCE_CONTINUE;
591 static void
592 test_dbus_roundtrip (void)
594 struct roundtrip_state state;
595 GDBusConnection *bus;
596 guint export_id;
597 guint id;
599 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
601 state.rand = g_rand_new_with_seed (g_test_rand_int ());
603 state.random = random_menu_new (state.rand, 2);
604 export_id = g_dbus_connection_export_menu_model (bus, "/", G_MENU_MODEL (state.random), NULL);
605 state.proxy = g_dbus_menu_model_get (bus, g_dbus_connection_get_unique_name (bus), "/");
606 state.proxy_mirror = mirror_menu_new (G_MENU_MODEL (state.proxy));
607 state.count = 0;
608 state.success = 0;
610 id = g_timeout_add (10, roundtrip_step, &state);
612 state.loop = g_main_loop_new (NULL, FALSE);
613 g_main_loop_run (state.loop);
615 g_main_loop_unref (state.loop);
616 g_source_remove (id);
617 g_object_unref (state.proxy);
618 g_dbus_connection_unexport_menu_model (bus, export_id);
619 g_object_unref (state.random);
620 g_object_unref (state.proxy_mirror);
621 g_rand_free (state.rand);
622 g_object_unref (bus);
625 static gint items_changed_count;
627 static void
628 items_changed (GMenuModel *model,
629 gint position,
630 gint removed,
631 gint added,
632 gpointer data)
634 items_changed_count++;
637 static gboolean
638 stop_loop (gpointer data)
640 GMainLoop *loop = data;
642 g_main_loop_quit (loop);
644 return G_SOURCE_REMOVE;
647 static void
648 test_dbus_subscriptions (void)
650 GDBusConnection *bus;
651 GMenu *menu;
652 GDBusMenuModel *proxy;
653 GMainLoop *loop;
654 GError *error = NULL;
655 guint export_id;
657 loop = g_main_loop_new (NULL, FALSE);
659 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
661 menu = g_menu_new ();
663 export_id = g_dbus_connection_export_menu_model (bus, "/", G_MENU_MODEL (menu), &error);
664 g_assert_no_error (error);
666 proxy = g_dbus_menu_model_get (bus, g_dbus_connection_get_unique_name (bus), "/");
667 items_changed_count = 0;
668 g_signal_connect (proxy, "items-changed",
669 G_CALLBACK (items_changed), NULL);
671 g_menu_append (menu, "item1", NULL);
672 g_menu_append (menu, "item2", NULL);
673 g_menu_append (menu, "item3", NULL);
675 g_assert_cmpint (items_changed_count, ==, 0);
677 g_timeout_add (100, stop_loop, loop);
678 g_main_loop_run (loop);
680 g_menu_model_get_n_items (G_MENU_MODEL (proxy));
682 g_timeout_add (100, stop_loop, loop);
683 g_main_loop_run (loop);
685 g_assert_cmpint (items_changed_count, ==, 1);
686 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
688 g_timeout_add (100, stop_loop, loop);
689 g_main_loop_run (loop);
691 g_menu_append (menu, "item4", NULL);
692 g_menu_append (menu, "item5", NULL);
693 g_menu_append (menu, "item6", NULL);
694 g_menu_remove (menu, 0);
695 g_menu_remove (menu, 0);
697 g_timeout_add (200, stop_loop, loop);
698 g_main_loop_run (loop);
700 g_assert_cmpint (items_changed_count, ==, 6);
702 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 4);
703 g_object_unref (proxy);
705 g_timeout_add (100, stop_loop, loop);
706 g_main_loop_run (loop);
708 g_menu_remove (menu, 0);
709 g_menu_remove (menu, 0);
711 g_timeout_add (100, stop_loop, loop);
712 g_main_loop_run (loop);
714 g_assert_cmpint (items_changed_count, ==, 6);
716 g_dbus_connection_unexport_menu_model (bus, export_id);
717 g_object_unref (menu);
719 g_main_loop_unref (loop);
722 static gpointer
723 do_modify (gpointer data)
725 RandomMenu *menu = data;
726 GRand *rand;
727 gint i;
729 rand = g_rand_new_with_seed (g_test_rand_int ());
731 for (i = 0; i < 10000; i++)
733 random_menu_change (menu, rand);
736 return NULL;
739 static gpointer
740 do_export (gpointer data)
742 GMenuModel *menu = data;
743 gint i;
744 GDBusConnection *bus;
745 gchar *path;
746 GError *error = NULL;
747 guint id;
749 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
750 path = g_strdup_printf ("/%p", data);
752 for (i = 0; i < 10000; i++)
754 id = g_dbus_connection_export_menu_model (bus, path, menu, &error);
755 g_assert_no_error (error);
756 g_dbus_connection_unexport_menu_model (bus, id);
757 while (g_main_context_iteration (NULL, FALSE));
760 g_free (path);
762 g_object_unref (bus);
764 return NULL;
767 static void
768 test_dbus_threaded (void)
770 RandomMenu *menu[10];
771 GThread *call[10];
772 GThread *export[10];
773 gint i;
775 for (i = 0; i < 10; i++)
777 menu[i] = random_menu_new (g_rand_new_with_seed (g_test_rand_int ()), 2);
778 call[i] = g_thread_new ("call", do_modify, menu[i]);
779 export[i] = g_thread_new ("export", do_export, menu[i]);
782 for (i = 0; i < 10; i++)
784 g_thread_join (call[i]);
785 g_thread_join (export[i]);
788 for (i = 0; i < 10; i++)
789 g_object_unref (menu[i]);
792 typedef struct {
793 GMenu *menu;
794 GHashTable *objects;
795 } ParserData;
797 static void
798 start_element (GMarkupParseContext *context,
799 const gchar *element_name,
800 const gchar **attribute_names,
801 const gchar **attribute_values,
802 gpointer user_data,
803 GError **error)
805 ParserData *data = user_data;
807 if (g_strcmp0 (element_name, "menu") == 0)
808 g_menu_markup_parser_start_menu (context, "domain", data->objects);
811 static void
812 end_element (GMarkupParseContext *context,
813 const gchar *element_name,
814 gpointer user_data,
815 GError **error)
817 ParserData *data = user_data;
819 if (g_strcmp0 (element_name, "menu") == 0)
820 data->menu = g_menu_markup_parser_end_menu (context);
823 static GMenuModel *
824 parse_menu_string (const gchar *string, GHashTable *objects, GError **error)
826 const GMarkupParser parser = {
827 start_element, end_element, NULL, NULL, NULL
829 GMarkupParseContext *context;
830 ParserData data;
832 data.menu = NULL;
833 data.objects = objects;
835 context = g_markup_parse_context_new (&parser, 0, &data, NULL);
836 g_markup_parse_context_parse (context, string, -1, error);
837 g_markup_parse_context_free (context);
839 return (GMenuModel*)data.menu;
842 static gchar *
843 menu_to_string (GMenuModel *menu)
845 GString *s;
847 s = g_string_new ("<menu>\n");
848 g_menu_markup_print_string (s, menu, 2, 2);
849 g_string_append (s, "</menu>\n");
851 return g_string_free (s, FALSE);
854 const gchar menu_data[] =
855 "<menu id='edit-menu'>\n"
856 " <section>\n"
857 " <item action='undo'>\n"
858 " <attribute name='label' translatable='yes' context='Stock label'>'_Undo'</attribute>\n"
859 " </item>\n"
860 " <item label='Redo' action='redo'/>\n"
861 " </section>\n"
862 " <section></section>\n"
863 " <section label='Copy &amp; Paste'>\n"
864 " <item label='Cut' action='cut'/>\n"
865 " <item label='Copy' action='copy'/>\n"
866 " <item label='Paste' action='paste'/>\n"
867 " </section>\n"
868 " <item><link name='section' id='blargh'>\n"
869 " <item label='Bold' action='bold'/>\n"
870 " <submenu label='Language'>\n"
871 " <item label='Latin' action='lang' target='latin'/>\n"
872 " <item label='Greek' action='lang' target='greek'/>\n"
873 " <item label='Urdu' action='lang' target='urdu'/>\n"
874 " </submenu>\n"
875 " <item name='test unusual attributes'>\n"
876 " <attribute name='action' type='s'>'quite-some-action'</attribute>\n"
877 " <attribute name='target' type='i'>36</attribute>\n"
878 " <attribute name='chocolate-thunda' type='as'>['a','b']</attribute>\n"
879 " <attribute name='thing1' type='g'>'s(uu)'</attribute>\n"
880 " <attribute name='icon' type='s'>'small blue thing'</attribute>\n"
881 " </item>\n"
882 " </link></item>\n"
883 "</menu>\n";
885 static void
886 test_markup_roundtrip (void)
888 GError *error = NULL;
889 GMenuModel *a;
890 GMenuModel *b;
891 gchar *s;
892 gchar *s2;
894 a = parse_menu_string (menu_data, NULL, &error);
895 g_assert_no_error (error);
896 g_assert (G_IS_MENU_MODEL (a));
898 /* normalized representation */
899 s = menu_to_string (a);
901 b = parse_menu_string (s, NULL, &error);
902 g_assert_no_error (error);
903 g_assert (G_IS_MENU_MODEL (b));
905 assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
907 s2 = menu_to_string (b);
909 g_assert_cmpstr (s, ==, s2);
911 g_object_unref (a);
912 g_object_unref (b);
913 g_free (s);
914 g_free (s2);
917 static void
918 test_markup_objects (void)
920 GMenuModel *a, *b;
921 GHashTable *objects;
922 GError *error = NULL;
924 objects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
925 a = parse_menu_string (menu_data, objects, &error);
926 g_assert_no_error (error);
927 g_assert (G_IS_MENU_MODEL (a));
928 g_assert_cmpint (g_hash_table_size (objects), ==, 1);
929 b = g_hash_table_lookup (objects, "blargh");
930 g_assert (G_IS_MENU_MODEL (b));
931 g_object_unref (a);
932 g_hash_table_unref (objects);
935 const gchar menu_data2[] =
936 "<menu>"
937 " <section>"
938 " <item label='Redo' action='redo'/>"
939 " </section>"
940 " <section></section>\n"
941 " <section label='Copy &amp; Paste'>"
942 " <item label='Cut' action='cut'/>"
943 " </section>"
944 " <section id='section1'>"
945 " <item label='Bold' action='bold'/>"
946 " <submenu label='Language' id='submenu1'>"
947 " <section id='section2'>"
948 " <item label='Urdu' action='lang' target='urdu'/>"
949 " </section>"
950 " </submenu>"
951 " </section>"
952 "</menu>";
953 static void
954 test_markup_ids (void)
956 GMenuModel *a, *b;
957 GHashTable *objects;
958 GError *error = NULL;
960 objects = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
961 a = parse_menu_string (menu_data2, objects, &error);
962 g_assert_no_error (error);
963 g_assert (G_IS_MENU_MODEL (a));
964 g_assert_cmpint (g_hash_table_size (objects), ==, 3);
965 b = g_hash_table_lookup (objects, "section1");
966 g_assert (G_IS_MENU_MODEL (b));
967 b = g_hash_table_lookup (objects, "section2");
968 g_assert (G_IS_MENU_MODEL (b));
969 b = g_hash_table_lookup (objects, "submenu1");
970 g_assert (G_IS_MENU_MODEL (b));
971 g_object_unref (a);
972 g_hash_table_unref (objects);
975 static void
976 test_attributes (void)
978 GMenu *menu;
979 GMenuItem *item;
980 GVariant *v;
982 menu = g_menu_new ();
984 item = g_menu_item_new ("test", NULL);
985 g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
986 g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
987 g_menu_item_set_attribute_value (item, "double", g_variant_new_double (1.5));
988 v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
989 g_menu_item_set_attribute_value (item, "complex", v);
990 g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
992 g_menu_append_item (menu, item);
994 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
996 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "boolean", NULL);
997 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
998 g_variant_unref (v);
1000 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "string", NULL);
1001 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1002 g_variant_unref (v);
1004 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "double", NULL);
1005 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1006 g_variant_unref (v);
1008 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "complex", NULL);
1009 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1010 g_variant_unref (v);
1012 g_object_unref (menu);
1015 static void
1016 test_links (void)
1018 GMenu *menu;
1019 GMenuModel *m;
1020 GMenuModel *x;
1021 GMenuItem *item;
1023 m = G_MENU_MODEL (g_menu_new ());
1024 g_menu_append (G_MENU (m), "test", NULL);
1026 menu = g_menu_new ();
1028 item = g_menu_item_new ("test1", NULL);
1029 g_menu_item_set_link (item, "section", m);
1030 g_menu_append_item (menu, item);
1032 item = g_menu_item_new ("test2", NULL);
1033 g_menu_item_set_link (item, "submenu", m);
1034 g_menu_append_item (menu, item);
1036 item = g_menu_item_new ("test3", NULL);
1037 g_menu_item_set_link (item, "wallet", m);
1038 g_menu_append_item (menu, item);
1040 item = g_menu_item_new ("test4", NULL);
1041 g_menu_item_set_link (item, "purse", m);
1042 g_menu_item_set_link (item, "purse", NULL);
1043 g_menu_append_item (menu, item);
1045 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 4);
1047 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, "section");
1048 g_assert (x == m);
1049 g_object_unref (x);
1051 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 1, "submenu");
1052 g_assert (x == m);
1053 g_object_unref (x);
1055 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 2, "wallet");
1056 g_assert (x == m);
1057 g_object_unref (x);
1059 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 3, "purse");
1060 g_assert (x == NULL);
1062 g_object_unref (m);
1063 g_object_unref (menu);
1066 static void
1067 test_mutable (void)
1069 GMenu *menu;
1071 menu = g_menu_new ();
1072 g_menu_append (menu, "test", "test");
1074 g_assert (g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1075 g_menu_freeze (menu);
1076 g_assert (!g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1078 g_object_unref (menu);
1081 static void
1082 test_misc (void)
1084 /* trying to use most of the GMenu api for constructing the
1085 * same menu two different ways
1087 GMenu *a, *m, *m2;
1088 GMenuModel *b;
1089 GMenuItem *item;
1090 const gchar *s;
1092 a = g_menu_new ();
1093 item = g_menu_item_new ("test1", "action1::target1");
1094 g_menu_prepend_item (a, item);
1095 g_object_unref (item);
1097 m = g_menu_new ();
1098 g_menu_prepend (m, "test2a", "action2");
1099 g_menu_append (m, "test2c", NULL);
1100 g_menu_insert (m, 1, "test2b", NULL);
1102 item = g_menu_item_new_submenu ("test2", G_MENU_MODEL (m));
1103 g_menu_append_item (a, item);
1104 g_object_unref (item);
1105 g_object_unref (m);
1107 m = g_menu_new ();
1109 m2 = g_menu_new ();
1110 g_menu_append (m2, "x", NULL);
1111 g_menu_prepend_section (m, "test3a", G_MENU_MODEL (m2));
1112 g_object_unref (m2);
1114 item = g_menu_item_new_section ("test3", G_MENU_MODEL (m));
1115 g_menu_insert_item (a, -1, item);
1116 g_object_unref (item);
1117 g_object_unref (m);
1119 s = ""
1120 "<menu>"
1121 " <item target='target1' action='action1' label='test1'/>"
1122 " <item label='test2'>"
1123 " <link name='submenu'>"
1124 " <item action='action2' label='test2a'/>"
1125 " <item label='test2b'/>"
1126 " <item label='test2c'/>"
1127 " </link>"
1128 " </item>"
1129 " <item label='test3'>"
1130 " <link name='section'>"
1131 " <item label='test3a'>"
1132 " <link name='section'>"
1133 " <item label='x'/>"
1134 " </link>"
1135 " </item>"
1136 " </link>"
1137 " </item>"
1138 "</menu>";
1140 b = parse_menu_string (s, NULL, NULL);
1142 assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
1143 g_object_unref (a);
1144 g_object_unref (b);
1147 /* Epilogue {{{1 */
1149 main (int argc, char **argv)
1151 g_test_init (&argc, &argv, NULL);
1153 g_type_init ();
1155 g_test_add_func ("/gmenu/equality", test_equality);
1156 g_test_add_func ("/gmenu/random", test_random);
1157 g_test_add_func ("/gmenu/dbus/roundtrip", test_dbus_roundtrip);
1158 g_test_add_func ("/gmenu/dbus/subscriptions", test_dbus_subscriptions);
1159 g_test_add_func ("/gmenu/dbus/threaded", test_dbus_threaded);
1160 g_test_add_func ("/gmenu/markup/roundtrip", test_markup_roundtrip);
1161 g_test_add_func ("/gmenu/markup/objects", test_markup_objects);
1162 g_test_add_func ("/gmenu/markup/ids", test_markup_ids);
1163 g_test_add_func ("/gmenu/attributes", test_attributes);
1164 g_test_add_func ("/gmenu/links", test_links);
1165 g_test_add_func ("/gmenu/mutable", test_mutable);
1166 g_test_add_func ("/gmenu/misc", test_misc);
1168 return g_test_run ();
1170 /* vim:set foldmethod=marker: */