Add some more cases to the app-id unit tests
[glib.git] / gio / tests / gmenumodel.c
blob2aec1a91cb22099d481f0d9201dc4a6c27552426
1 #include <gio/gio.h>
3 #include "gdbus-sessionbus.h"
5 /* Markup printing {{{1 */
7 /* This used to be part of GLib, but it was removed before the stable
8 * release because it wasn't generally useful. We want it here, though.
9 */
10 static void
11 indent_string (GString *string,
12 gint indent)
14 while (indent--)
15 g_string_append_c (string, ' ');
18 static GString *
19 g_menu_markup_print_string (GString *string,
20 GMenuModel *model,
21 gint indent,
22 gint tabstop)
24 gboolean need_nl = FALSE;
25 gint i, n;
27 if G_UNLIKELY (string == NULL)
28 string = g_string_new (NULL);
30 n = g_menu_model_get_n_items (model);
32 for (i = 0; i < n; i++)
34 GMenuAttributeIter *attr_iter;
35 GMenuLinkIter *link_iter;
36 GString *contents;
37 GString *attrs;
39 attr_iter = g_menu_model_iterate_item_attributes (model, i);
40 link_iter = g_menu_model_iterate_item_links (model, i);
41 contents = g_string_new (NULL);
42 attrs = g_string_new (NULL);
44 while (g_menu_attribute_iter_next (attr_iter))
46 const char *name = g_menu_attribute_iter_get_name (attr_iter);
47 GVariant *value = g_menu_attribute_iter_get_value (attr_iter);
49 if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
51 gchar *str;
52 str = g_markup_printf_escaped (" %s='%s'", name, g_variant_get_string (value, NULL));
53 g_string_append (attrs, str);
54 g_free (str);
57 else
59 gchar *printed;
60 gchar *str;
61 const gchar *type;
63 printed = g_variant_print (value, TRUE);
64 type = g_variant_type_peek_string (g_variant_get_type (value));
65 str = g_markup_printf_escaped ("<attribute name='%s' type='%s'>%s</attribute>\n", name, type, printed);
66 indent_string (contents, indent + tabstop);
67 g_string_append (contents, str);
68 g_free (printed);
69 g_free (str);
72 g_variant_unref (value);
74 g_object_unref (attr_iter);
76 while (g_menu_link_iter_next (link_iter))
78 const gchar *name = g_menu_link_iter_get_name (link_iter);
79 GMenuModel *menu = g_menu_link_iter_get_value (link_iter);
80 gchar *str;
82 if (contents->str[0])
83 g_string_append_c (contents, '\n');
85 str = g_markup_printf_escaped ("<link name='%s'>\n", name);
86 indent_string (contents, indent + tabstop);
87 g_string_append (contents, str);
88 g_free (str);
90 g_menu_markup_print_string (contents, menu, indent + 2 * tabstop, tabstop);
92 indent_string (contents, indent + tabstop);
93 g_string_append (contents, "</link>\n");
94 g_object_unref (menu);
96 g_object_unref (link_iter);
98 if (contents->str[0])
100 indent_string (string, indent);
101 g_string_append_printf (string, "<item%s>\n", attrs->str);
102 g_string_append (string, contents->str);
103 indent_string (string, indent);
104 g_string_append (string, "</item>\n");
105 need_nl = TRUE;
108 else
110 if (need_nl)
111 g_string_append_c (string, '\n');
113 indent_string (string, indent);
114 g_string_append_printf (string, "<item%s/>\n", attrs->str);
115 need_nl = FALSE;
118 g_string_free (contents, TRUE);
119 g_string_free (attrs, TRUE);
122 return string;
125 /* TestItem {{{1 */
127 /* This utility struct is used by both the RandomMenu and MirrorMenu
128 * class implementations below.
130 typedef struct {
131 GHashTable *attributes;
132 GHashTable *links;
133 } TestItem;
135 static TestItem *
136 test_item_new (GHashTable *attributes,
137 GHashTable *links)
139 TestItem *item;
141 item = g_slice_new (TestItem);
142 item->attributes = g_hash_table_ref (attributes);
143 item->links = g_hash_table_ref (links);
145 return item;
148 static void
149 test_item_free (gpointer data)
151 TestItem *item = data;
153 g_hash_table_unref (item->attributes);
154 g_hash_table_unref (item->links);
156 g_slice_free (TestItem, item);
159 /* RandomMenu {{{1 */
160 #define MAX_ITEMS 5
161 #define TOP_ORDER 4
163 typedef struct {
164 GMenuModel parent_instance;
166 GSequence *items;
167 gint order;
168 } RandomMenu;
170 typedef GMenuModelClass RandomMenuClass;
172 static GType random_menu_get_type (void);
173 G_DEFINE_TYPE (RandomMenu, random_menu, G_TYPE_MENU_MODEL);
175 static gboolean
176 random_menu_is_mutable (GMenuModel *model)
178 return TRUE;
181 static gint
182 random_menu_get_n_items (GMenuModel *model)
184 RandomMenu *menu = (RandomMenu *) model;
186 return g_sequence_get_length (menu->items);
189 static void
190 random_menu_get_item_attributes (GMenuModel *model,
191 gint position,
192 GHashTable **table)
194 RandomMenu *menu = (RandomMenu *) model;
195 TestItem *item;
197 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
198 *table = g_hash_table_ref (item->attributes);
201 static void
202 random_menu_get_item_links (GMenuModel *model,
203 gint position,
204 GHashTable **table)
206 RandomMenu *menu = (RandomMenu *) model;
207 TestItem *item;
209 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
210 *table = g_hash_table_ref (item->links);
213 static void
214 random_menu_finalize (GObject *object)
216 RandomMenu *menu = (RandomMenu *) object;
218 g_sequence_free (menu->items);
220 G_OBJECT_CLASS (random_menu_parent_class)
221 ->finalize (object);
224 static void
225 random_menu_init (RandomMenu *menu)
229 static void
230 random_menu_class_init (GMenuModelClass *class)
232 GObjectClass *object_class = G_OBJECT_CLASS (class);
234 class->is_mutable = random_menu_is_mutable;
235 class->get_n_items = random_menu_get_n_items;
236 class->get_item_attributes = random_menu_get_item_attributes;
237 class->get_item_links = random_menu_get_item_links;
239 object_class->finalize = random_menu_finalize;
242 static RandomMenu * random_menu_new (GRand *rand, gint order);
244 static void
245 random_menu_change (RandomMenu *menu,
246 GRand *rand)
248 gint position, removes, adds;
249 GSequenceIter *point;
250 gint n_items;
251 gint i;
253 n_items = g_sequence_get_length (menu->items);
257 position = g_rand_int_range (rand, 0, n_items + 1);
258 removes = g_rand_int_range (rand, 0, n_items - position + 1);
259 adds = g_rand_int_range (rand, 0, MAX_ITEMS - (n_items - removes) + 1);
261 while (removes == 0 && adds == 0);
263 point = g_sequence_get_iter_at_pos (menu->items, position + removes);
265 if (removes)
267 GSequenceIter *start;
269 start = g_sequence_get_iter_at_pos (menu->items, position);
270 g_sequence_remove_range (start, point);
273 for (i = 0; i < adds; i++)
275 const gchar *label;
276 GHashTable *links;
277 GHashTable *attributes;
279 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
280 links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
282 if (menu->order > 0 && g_rand_boolean (rand))
284 RandomMenu *child;
285 const gchar *subtype;
287 child = random_menu_new (rand, menu->order - 1);
289 if (g_rand_boolean (rand))
291 subtype = G_MENU_LINK_SECTION;
292 /* label some section headers */
293 if (g_rand_boolean (rand))
294 label = "Section";
295 else
296 label = NULL;
298 else
300 /* label all submenus */
301 subtype = G_MENU_LINK_SUBMENU;
302 label = "Submenu";
305 g_hash_table_insert (links, g_strdup (subtype), child);
307 else
308 /* label all terminals */
309 label = "Menu Item";
311 if (label)
312 g_hash_table_insert (attributes, g_strdup ("label"), g_variant_ref_sink (g_variant_new_string (label)));
314 g_sequence_insert_before (point, test_item_new (attributes, links));
315 g_hash_table_unref (links);
316 g_hash_table_unref (attributes);
319 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removes, adds);
322 static RandomMenu *
323 random_menu_new (GRand *rand,
324 gint order)
326 RandomMenu *menu;
328 menu = g_object_new (random_menu_get_type (), NULL);
329 menu->items = g_sequence_new (test_item_free);
330 menu->order = order;
332 random_menu_change (menu, rand);
334 return menu;
337 /* MirrorMenu {{{1 */
338 typedef struct {
339 GMenuModel parent_instance;
341 GMenuModel *clone_of;
342 GSequence *items;
343 gulong handler_id;
344 } MirrorMenu;
346 typedef GMenuModelClass MirrorMenuClass;
348 static GType mirror_menu_get_type (void);
349 G_DEFINE_TYPE (MirrorMenu, mirror_menu, G_TYPE_MENU_MODEL);
351 static gboolean
352 mirror_menu_is_mutable (GMenuModel *model)
354 MirrorMenu *menu = (MirrorMenu *) model;
356 return menu->handler_id != 0;
359 static gint
360 mirror_menu_get_n_items (GMenuModel *model)
362 MirrorMenu *menu = (MirrorMenu *) model;
364 return g_sequence_get_length (menu->items);
367 static void
368 mirror_menu_get_item_attributes (GMenuModel *model,
369 gint position,
370 GHashTable **table)
372 MirrorMenu *menu = (MirrorMenu *) model;
373 TestItem *item;
375 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
376 *table = g_hash_table_ref (item->attributes);
379 static void
380 mirror_menu_get_item_links (GMenuModel *model,
381 gint position,
382 GHashTable **table)
384 MirrorMenu *menu = (MirrorMenu *) model;
385 TestItem *item;
387 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
388 *table = g_hash_table_ref (item->links);
391 static void
392 mirror_menu_finalize (GObject *object)
394 MirrorMenu *menu = (MirrorMenu *) object;
396 if (menu->handler_id)
397 g_signal_handler_disconnect (menu->clone_of, menu->handler_id);
399 g_sequence_free (menu->items);
400 g_object_unref (menu->clone_of);
402 G_OBJECT_CLASS (mirror_menu_parent_class)
403 ->finalize (object);
406 static void
407 mirror_menu_init (MirrorMenu *menu)
411 static void
412 mirror_menu_class_init (GMenuModelClass *class)
414 GObjectClass *object_class = G_OBJECT_CLASS (class);
416 class->is_mutable = mirror_menu_is_mutable;
417 class->get_n_items = mirror_menu_get_n_items;
418 class->get_item_attributes = mirror_menu_get_item_attributes;
419 class->get_item_links = mirror_menu_get_item_links;
421 object_class->finalize = mirror_menu_finalize;
424 static MirrorMenu * mirror_menu_new (GMenuModel *clone_of);
426 static void
427 mirror_menu_changed (GMenuModel *model,
428 gint position,
429 gint removed,
430 gint added,
431 gpointer user_data)
433 MirrorMenu *menu = user_data;
434 GSequenceIter *point;
435 gint i;
437 g_assert (model == menu->clone_of);
439 point = g_sequence_get_iter_at_pos (menu->items, position + removed);
441 if (removed)
443 GSequenceIter *start;
445 start = g_sequence_get_iter_at_pos (menu->items, position);
446 g_sequence_remove_range (start, point);
449 for (i = position; i < position + added; i++)
451 GMenuAttributeIter *attr_iter;
452 GMenuLinkIter *link_iter;
453 GHashTable *links;
454 GHashTable *attributes;
455 const gchar *name;
456 GMenuModel *child;
457 GVariant *value;
459 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
460 links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
462 attr_iter = g_menu_model_iterate_item_attributes (model, i);
463 while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
465 g_hash_table_insert (attributes, g_strdup (name), value);
467 g_object_unref (attr_iter);
469 link_iter = g_menu_model_iterate_item_links (model, i);
470 while (g_menu_link_iter_get_next (link_iter, &name, &child))
472 g_hash_table_insert (links, g_strdup (name), mirror_menu_new (child));
473 g_object_unref (child);
475 g_object_unref (link_iter);
477 g_sequence_insert_before (point, test_item_new (attributes, links));
478 g_hash_table_unref (attributes);
479 g_hash_table_unref (links);
482 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removed, added);
485 static MirrorMenu *
486 mirror_menu_new (GMenuModel *clone_of)
488 MirrorMenu *menu;
490 menu = g_object_new (mirror_menu_get_type (), NULL);
491 menu->items = g_sequence_new (test_item_free);
492 menu->clone_of = g_object_ref (clone_of);
494 if (g_menu_model_is_mutable (clone_of))
495 menu->handler_id = g_signal_connect (clone_of, "items-changed", G_CALLBACK (mirror_menu_changed), menu);
496 mirror_menu_changed (clone_of, 0, 0, g_menu_model_get_n_items (clone_of), menu);
498 return menu;
501 /* check_menus_equal(), assert_menus_equal() {{{1 */
502 static gboolean
503 check_menus_equal (GMenuModel *a,
504 GMenuModel *b)
506 gboolean equal = TRUE;
507 gint a_n, b_n;
508 gint i;
510 a_n = g_menu_model_get_n_items (a);
511 b_n = g_menu_model_get_n_items (b);
513 if (a_n != b_n)
514 return FALSE;
516 for (i = 0; i < a_n; i++)
518 GMenuAttributeIter *attr_iter;
519 GVariant *a_value, *b_value;
520 GMenuLinkIter *link_iter;
521 GMenuModel *a_menu, *b_menu;
522 const gchar *name;
524 attr_iter = g_menu_model_iterate_item_attributes (a, i);
525 while (g_menu_attribute_iter_get_next (attr_iter, &name, &a_value))
527 b_value = g_menu_model_get_item_attribute_value (b, i, name, NULL);
528 equal &= b_value && g_variant_equal (a_value, b_value);
529 if (b_value)
530 g_variant_unref (b_value);
531 g_variant_unref (a_value);
533 g_object_unref (attr_iter);
535 attr_iter = g_menu_model_iterate_item_attributes (b, i);
536 while (g_menu_attribute_iter_get_next (attr_iter, &name, &b_value))
538 a_value = g_menu_model_get_item_attribute_value (a, i, name, NULL);
539 equal &= a_value && g_variant_equal (a_value, b_value);
540 if (a_value)
541 g_variant_unref (a_value);
542 g_variant_unref (b_value);
544 g_object_unref (attr_iter);
546 link_iter = g_menu_model_iterate_item_links (a, i);
547 while (g_menu_link_iter_get_next (link_iter, &name, &a_menu))
549 b_menu = g_menu_model_get_item_link (b, i, name);
550 equal &= b_menu && check_menus_equal (a_menu, b_menu);
551 if (b_menu)
552 g_object_unref (b_menu);
553 g_object_unref (a_menu);
555 g_object_unref (link_iter);
557 link_iter = g_menu_model_iterate_item_links (b, i);
558 while (g_menu_link_iter_get_next (link_iter, &name, &b_menu))
560 a_menu = g_menu_model_get_item_link (a, i, name);
561 equal &= a_menu && check_menus_equal (a_menu, b_menu);
562 if (a_menu)
563 g_object_unref (a_menu);
564 g_object_unref (b_menu);
566 g_object_unref (link_iter);
569 return equal;
572 static void
573 assert_menus_equal (GMenuModel *a,
574 GMenuModel *b)
576 if (!check_menus_equal (a, b))
578 GString *string;
580 string = g_string_new ("\n <a>\n");
581 g_menu_markup_print_string (string, G_MENU_MODEL (a), 4, 2);
582 g_string_append (string, " </a>\n\n-------------\n <b>\n");
583 g_menu_markup_print_string (string, G_MENU_MODEL (b), 4, 2);
584 g_string_append (string, " </b>\n");
585 g_error ("%s", string->str);
589 static void
590 assert_menuitem_equal (GMenuItem *item,
591 GMenuModel *model,
592 gint index)
594 GMenuAttributeIter *attr_iter;
595 GMenuLinkIter *link_iter;
596 const gchar *name;
597 GVariant *value;
598 GMenuModel *linked_model;
600 /* NOTE we can't yet test whether item has attributes or links that
601 * are not in the model, because there's no iterator API for menu
602 * items */
604 attr_iter = g_menu_model_iterate_item_attributes (model, index);
605 while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
607 GVariant *item_value;
609 item_value = g_menu_item_get_attribute_value (item, name, g_variant_get_type (value));
610 g_assert (item_value && g_variant_equal (item_value, value));
612 g_variant_unref (item_value);
613 g_variant_unref (value);
616 link_iter = g_menu_model_iterate_item_links (model, index);
617 while (g_menu_link_iter_get_next (link_iter, &name, &linked_model))
619 GMenuModel *item_linked_model;
621 item_linked_model = g_menu_item_get_link (item, name);
622 g_assert (linked_model == item_linked_model);
624 g_object_unref (item_linked_model);
625 g_object_unref (linked_model);
628 g_object_unref (attr_iter);
629 g_object_unref (link_iter);
632 /* Test cases {{{1 */
633 static void
634 test_equality (void)
636 GRand *randa, *randb;
637 guint32 seed;
638 gint i;
640 seed = g_test_rand_int ();
642 randa = g_rand_new_with_seed (seed);
643 randb = g_rand_new_with_seed (seed);
645 for (i = 0; i < 500; i++)
647 RandomMenu *a, *b;
649 a = random_menu_new (randa, TOP_ORDER);
650 b = random_menu_new (randb, TOP_ORDER);
651 assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
652 g_object_unref (b);
653 g_object_unref (a);
656 g_rand_int (randa);
658 for (i = 0; i < 500;)
660 RandomMenu *a, *b;
662 a = random_menu_new (randa, TOP_ORDER);
663 b = random_menu_new (randb, TOP_ORDER);
664 if (check_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b)))
666 /* by chance, they may really be equal. double check. */
667 GString *as, *bs;
669 as = g_menu_markup_print_string (NULL, G_MENU_MODEL (a), 4, 2);
670 bs = g_menu_markup_print_string (NULL, G_MENU_MODEL (b), 4, 2);
671 g_assert_cmpstr (as->str, ==, bs->str);
672 g_string_free (bs, TRUE);
673 g_string_free (as, TRUE);
675 /* we're here because randa and randb just generated equal
676 * menus. they may do it again, so throw away randb and make
677 * a fresh one.
679 g_rand_free (randb);
680 randb = g_rand_new_with_seed (g_rand_int (randa));
682 else
683 /* make sure we get enough unequals (ie: no GRand failure) */
684 i++;
686 g_object_unref (b);
687 g_object_unref (a);
690 g_rand_free (randb);
691 g_rand_free (randa);
694 static void
695 test_random (void)
697 RandomMenu *random;
698 MirrorMenu *mirror;
699 GRand *rand;
700 gint i;
702 rand = g_rand_new_with_seed (g_test_rand_int ());
703 random = random_menu_new (rand, TOP_ORDER);
704 mirror = mirror_menu_new (G_MENU_MODEL (random));
706 for (i = 0; i < 500; i++)
708 assert_menus_equal (G_MENU_MODEL (random), G_MENU_MODEL (mirror));
709 random_menu_change (random, rand);
712 g_object_unref (mirror);
713 g_object_unref (random);
715 g_rand_free (rand);
718 struct roundtrip_state
720 RandomMenu *random;
721 MirrorMenu *proxy_mirror;
722 GDBusMenuModel *proxy;
723 GMainLoop *loop;
724 GRand *rand;
725 gint success;
726 gint count;
729 static gboolean
730 roundtrip_step (gpointer data)
732 struct roundtrip_state *state = data;
734 if (check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy)) &&
735 check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy_mirror)))
737 state->success++;
738 state->count = 0;
740 if (state->success < 100)
741 random_menu_change (state->random, state->rand);
742 else
743 g_main_loop_quit (state->loop);
745 else if (state->count == 100)
747 assert_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy));
748 g_assert_not_reached ();
750 else
751 state->count++;
753 return G_SOURCE_CONTINUE;
756 static void
757 test_dbus_roundtrip (void)
759 struct roundtrip_state state;
760 GDBusConnection *bus;
761 guint export_id;
762 guint id;
764 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
766 state.rand = g_rand_new_with_seed (g_test_rand_int ());
768 state.random = random_menu_new (state.rand, 2);
769 export_id = g_dbus_connection_export_menu_model (bus, "/", G_MENU_MODEL (state.random), NULL);
770 state.proxy = g_dbus_menu_model_get (bus, g_dbus_connection_get_unique_name (bus), "/");
771 state.proxy_mirror = mirror_menu_new (G_MENU_MODEL (state.proxy));
772 state.count = 0;
773 state.success = 0;
775 id = g_timeout_add (10, roundtrip_step, &state);
777 state.loop = g_main_loop_new (NULL, FALSE);
778 g_main_loop_run (state.loop);
780 g_main_loop_unref (state.loop);
781 g_source_remove (id);
782 g_object_unref (state.proxy);
783 g_dbus_connection_unexport_menu_model (bus, export_id);
784 g_object_unref (state.random);
785 g_object_unref (state.proxy_mirror);
786 g_rand_free (state.rand);
787 g_object_unref (bus);
790 static gint items_changed_count;
792 static void
793 items_changed (GMenuModel *model,
794 gint position,
795 gint removed,
796 gint added,
797 gpointer data)
799 items_changed_count++;
802 static gboolean
803 stop_loop (gpointer data)
805 GMainLoop *loop = data;
807 g_main_loop_quit (loop);
809 return G_SOURCE_REMOVE;
812 static void
813 test_dbus_subscriptions (void)
815 GDBusConnection *bus;
816 GMenu *menu;
817 GDBusMenuModel *proxy;
818 GMainLoop *loop;
819 GError *error = NULL;
820 guint export_id;
822 loop = g_main_loop_new (NULL, FALSE);
824 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
826 menu = g_menu_new ();
828 export_id = g_dbus_connection_export_menu_model (bus, "/", G_MENU_MODEL (menu), &error);
829 g_assert_no_error (error);
831 proxy = g_dbus_menu_model_get (bus, g_dbus_connection_get_unique_name (bus), "/");
832 items_changed_count = 0;
833 g_signal_connect (proxy, "items-changed",
834 G_CALLBACK (items_changed), NULL);
836 g_menu_append (menu, "item1", NULL);
837 g_menu_append (menu, "item2", NULL);
838 g_menu_append (menu, "item3", NULL);
840 g_assert_cmpint (items_changed_count, ==, 0);
842 g_timeout_add (100, stop_loop, loop);
843 g_main_loop_run (loop);
845 g_menu_model_get_n_items (G_MENU_MODEL (proxy));
847 g_timeout_add (100, stop_loop, loop);
848 g_main_loop_run (loop);
850 g_assert_cmpint (items_changed_count, ==, 1);
851 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
853 g_timeout_add (100, stop_loop, loop);
854 g_main_loop_run (loop);
856 g_menu_append (menu, "item4", NULL);
857 g_menu_append (menu, "item5", NULL);
858 g_menu_append (menu, "item6", NULL);
859 g_menu_remove (menu, 0);
860 g_menu_remove (menu, 0);
862 g_timeout_add (200, stop_loop, loop);
863 g_main_loop_run (loop);
865 g_assert_cmpint (items_changed_count, ==, 6);
867 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 4);
868 g_object_unref (proxy);
870 g_timeout_add (100, stop_loop, loop);
871 g_main_loop_run (loop);
873 g_menu_remove (menu, 0);
874 g_menu_remove (menu, 0);
876 g_timeout_add (100, stop_loop, loop);
877 g_main_loop_run (loop);
879 g_assert_cmpint (items_changed_count, ==, 6);
881 g_dbus_connection_unexport_menu_model (bus, export_id);
882 g_object_unref (menu);
884 g_main_loop_unref (loop);
885 g_object_unref (bus);
888 static gpointer
889 do_modify (gpointer data)
891 RandomMenu *menu = data;
892 GRand *rand;
893 gint i;
895 rand = g_rand_new_with_seed (g_test_rand_int ());
897 for (i = 0; i < 10000; i++)
899 random_menu_change (menu, rand);
902 return NULL;
905 static gpointer
906 do_export (gpointer data)
908 GMenuModel *menu = data;
909 gint i;
910 GDBusConnection *bus;
911 gchar *path;
912 GError *error = NULL;
913 guint id;
915 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
916 path = g_strdup_printf ("/%p", data);
918 for (i = 0; i < 10000; i++)
920 id = g_dbus_connection_export_menu_model (bus, path, menu, &error);
921 g_assert_no_error (error);
922 g_dbus_connection_unexport_menu_model (bus, id);
923 while (g_main_context_iteration (NULL, FALSE));
926 g_free (path);
928 g_object_unref (bus);
930 return NULL;
933 static void
934 test_dbus_threaded (void)
936 RandomMenu *menu[10];
937 GThread *call[10];
938 GThread *export[10];
939 gint i;
941 for (i = 0; i < 10; i++)
943 menu[i] = random_menu_new (g_rand_new_with_seed (g_test_rand_int ()), 2);
944 call[i] = g_thread_new ("call", do_modify, menu[i]);
945 export[i] = g_thread_new ("export", do_export, menu[i]);
948 for (i = 0; i < 10; i++)
950 g_thread_join (call[i]);
951 g_thread_join (export[i]);
954 for (i = 0; i < 10; i++)
955 g_object_unref (menu[i]);
958 static void
959 test_attributes (void)
961 GMenu *menu;
962 GMenuItem *item;
963 GVariant *v;
965 menu = g_menu_new ();
967 item = g_menu_item_new ("test", NULL);
968 g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
969 g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
971 g_menu_item_set_attribute (item, "double", "d", 1.5);
972 v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
973 g_menu_item_set_attribute_value (item, "complex", v);
974 g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
976 g_menu_append_item (menu, item);
978 g_menu_item_set_attribute (item, "double", "d", G_PI);
980 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
982 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "boolean", NULL);
983 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
984 g_variant_unref (v);
986 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "string", NULL);
987 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
988 g_variant_unref (v);
990 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "double", NULL);
991 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
992 g_variant_unref (v);
994 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "complex", NULL);
995 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
996 g_variant_unref (v);
998 g_menu_remove_all (menu);
1000 g_object_unref (menu);
1001 g_object_unref (item);
1004 static void
1005 test_attribute_iter (void)
1007 GMenu *menu;
1008 GMenuItem *item;
1009 const gchar *name;
1010 GVariant *v;
1011 GMenuAttributeIter *iter;
1012 GHashTable *found;
1014 menu = g_menu_new ();
1016 item = g_menu_item_new ("test", NULL);
1017 g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
1018 g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
1020 g_menu_item_set_attribute (item, "double", "d", 1.5);
1021 v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
1022 g_menu_item_set_attribute_value (item, "complex", v);
1023 g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
1025 g_menu_append_item (menu, item);
1027 g_menu_item_set_attribute (item, "double", "d", G_PI);
1029 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
1031 found = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
1033 iter = g_menu_model_iterate_item_attributes (G_MENU_MODEL (menu), 0);
1034 while (g_menu_attribute_iter_get_next (iter, &name, &v))
1035 g_hash_table_insert (found, g_strdup (name), v);
1037 g_assert_cmpint (g_hash_table_size (found), ==, 6);
1039 v = g_hash_table_lookup (found, "label");
1040 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1042 v = g_hash_table_lookup (found, "boolean");
1043 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
1045 v = g_hash_table_lookup (found, "string");
1046 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1048 v = g_hash_table_lookup (found, "double");
1049 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1051 v = g_hash_table_lookup (found, "complex");
1052 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1054 v = g_hash_table_lookup (found, "test-123");
1055 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1057 g_hash_table_unref (found);
1059 g_menu_remove_all (menu);
1061 g_object_unref (menu);
1062 g_object_unref (item);
1065 static void
1066 test_links (void)
1068 GMenu *menu;
1069 GMenuModel *m;
1070 GMenuModel *x;
1071 GMenuItem *item;
1073 m = G_MENU_MODEL (g_menu_new ());
1074 g_menu_append (G_MENU (m), "test", NULL);
1076 menu = g_menu_new ();
1078 item = g_menu_item_new ("test2", NULL);
1079 g_menu_item_set_link (item, "submenu", m);
1080 g_menu_prepend_item (menu, item);
1082 item = g_menu_item_new ("test1", NULL);
1083 g_menu_item_set_link (item, "section", m);
1084 g_menu_insert_item (menu, 0, item);
1086 item = g_menu_item_new ("test3", NULL);
1087 g_menu_item_set_link (item, "wallet", m);
1088 g_menu_insert_item (menu, 1000, item);
1090 item = g_menu_item_new ("test4", NULL);
1091 g_menu_item_set_link (item, "purse", m);
1092 g_menu_item_set_link (item, "purse", NULL);
1093 g_menu_append_item (menu, item);
1095 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 4);
1097 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, "section");
1098 g_assert (x == m);
1099 g_object_unref (x);
1101 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 1, "submenu");
1102 g_assert (x == m);
1103 g_object_unref (x);
1105 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 2, "wallet");
1106 g_assert (x == m);
1107 g_object_unref (x);
1109 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 3, "purse");
1110 g_assert (x == NULL);
1112 g_object_unref (m);
1113 g_object_unref (menu);
1116 static void
1117 test_mutable (void)
1119 GMenu *menu;
1121 menu = g_menu_new ();
1122 g_menu_append (menu, "test", "test");
1124 g_assert (g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1125 g_menu_freeze (menu);
1126 g_assert (!g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1128 g_object_unref (menu);
1131 static void
1132 test_convenience (void)
1134 GMenu *m1, *m2;
1135 GMenu *sub;
1136 GMenuItem *item;
1138 m1 = g_menu_new ();
1139 m2 = g_menu_new ();
1140 sub = g_menu_new ();
1142 g_menu_prepend (m1, "label1", "do::something");
1143 g_menu_insert (m2, 0, "label1", "do::something");
1145 g_menu_append (m1, "label2", "do::somethingelse");
1146 g_menu_insert (m2, -1, "label2", "do::somethingelse");
1148 g_menu_insert_section (m1, 10, "label3", G_MENU_MODEL (sub));
1149 item = g_menu_item_new_section ("label3", G_MENU_MODEL (sub));
1150 g_menu_insert_item (m2, 10, item);
1151 g_object_unref (item);
1153 g_menu_prepend_section (m1, "label4", G_MENU_MODEL (sub));
1154 g_menu_insert_section (m2, 0, "label4", G_MENU_MODEL (sub));
1156 g_menu_append_section (m1, "label5", G_MENU_MODEL (sub));
1157 g_menu_insert_section (m2, -1, "label5", G_MENU_MODEL (sub));
1159 g_menu_insert_submenu (m1, 5, "label6", G_MENU_MODEL (sub));
1160 item = g_menu_item_new_submenu ("label6", G_MENU_MODEL (sub));
1161 g_menu_insert_item (m2, 5, item);
1162 g_object_unref (item);
1164 g_menu_prepend_submenu (m1, "label7", G_MENU_MODEL (sub));
1165 g_menu_insert_submenu (m2, 0, "label7", G_MENU_MODEL (sub));
1167 g_menu_append_submenu (m1, "label8", G_MENU_MODEL (sub));
1168 g_menu_insert_submenu (m2, -1, "label8", G_MENU_MODEL (sub));
1170 assert_menus_equal (G_MENU_MODEL (m1), G_MENU_MODEL (m2));
1172 g_object_unref (m1);
1173 g_object_unref (m2);
1176 static void
1177 test_menuitem (void)
1179 GMenu *menu;
1180 GMenu *submenu;
1181 GMenuItem *item;
1182 GIcon *icon;
1183 gboolean b;
1184 gchar *s;
1186 menu = g_menu_new ();
1187 submenu = g_menu_new ();
1189 item = g_menu_item_new ("label", "action");
1190 g_menu_item_set_attribute (item, "attribute", "b", TRUE);
1191 g_menu_item_set_link (item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu));
1192 g_menu_append_item (menu, item);
1194 icon = g_themed_icon_new ("bla");
1195 g_menu_item_set_icon (item, icon);
1196 g_object_unref (icon);
1198 g_assert (g_menu_item_get_attribute (item, "attribute", "b", &b));
1199 g_assert (b);
1201 g_menu_item_set_action_and_target (item, "action", "(bs)", TRUE, "string");
1202 g_assert (g_menu_item_get_attribute (item, "target", "(bs)", &b, &s));
1203 g_assert (b);
1204 g_assert_cmpstr (s, ==, "string");
1205 g_free (s);
1207 g_object_unref (item);
1209 item = g_menu_item_new_from_model (G_MENU_MODEL (menu), 0);
1210 assert_menuitem_equal (item, G_MENU_MODEL (menu), 0);
1211 g_object_unref (item);
1213 g_object_unref (menu);
1214 g_object_unref (submenu);
1217 /* Epilogue {{{1 */
1219 main (int argc, char **argv)
1221 gboolean ret;
1223 g_test_init (&argc, &argv, NULL);
1225 session_bus_up ();
1227 g_test_add_func ("/gmenu/equality", test_equality);
1228 g_test_add_func ("/gmenu/random", test_random);
1229 g_test_add_func ("/gmenu/dbus/roundtrip", test_dbus_roundtrip);
1230 g_test_add_func ("/gmenu/dbus/subscriptions", test_dbus_subscriptions);
1231 g_test_add_func ("/gmenu/dbus/threaded", test_dbus_threaded);
1232 g_test_add_func ("/gmenu/attributes", test_attributes);
1233 g_test_add_func ("/gmenu/attributes/iterate", test_attribute_iter);
1234 g_test_add_func ("/gmenu/links", test_links);
1235 g_test_add_func ("/gmenu/mutable", test_mutable);
1236 g_test_add_func ("/gmenu/convenience", test_convenience);
1237 g_test_add_func ("/gmenu/menuitem", test_menuitem);
1239 ret = g_test_run ();
1241 session_bus_down ();
1243 return ret;
1245 /* vim:set foldmethod=marker: */