ci: collect test coverage and deploy a html report through gitlab pages
[glib.git] / gio / tests / gmenumodel.c
blob5d6f485c4ca055c75926dcaa05565717d582789f
1 #include <gio/gio.h>
2 #include <gio/gunixsocketaddress.h>
3 #include <glib/gstdio.h>
4 #include <string.h>
6 #include "gdbus-sessionbus.h"
8 static gboolean
9 time_out (gpointer unused G_GNUC_UNUSED)
11 g_error ("Timed out");
12 /* not reached */
13 return FALSE;
16 static guint
17 add_timeout (guint seconds)
19 #ifdef G_OS_UNIX
20 /* Safety-catch against the main loop having blocked */
21 alarm (seconds + 5);
22 #endif
23 return g_timeout_add_seconds (seconds, time_out, NULL);
26 static void
27 cancel_timeout (guint timeout_id)
29 #ifdef G_OS_UNIX
30 alarm (0);
31 #endif
32 g_source_remove (timeout_id);
35 /* Markup printing {{{1 */
37 /* This used to be part of GLib, but it was removed before the stable
38 * release because it wasn't generally useful. We want it here, though.
40 static void
41 indent_string (GString *string,
42 gint indent)
44 while (indent--)
45 g_string_append_c (string, ' ');
48 static GString *
49 g_menu_markup_print_string (GString *string,
50 GMenuModel *model,
51 gint indent,
52 gint tabstop)
54 gboolean need_nl = FALSE;
55 gint i, n;
57 if G_UNLIKELY (string == NULL)
58 string = g_string_new (NULL);
60 n = g_menu_model_get_n_items (model);
62 for (i = 0; i < n; i++)
64 GMenuAttributeIter *attr_iter;
65 GMenuLinkIter *link_iter;
66 GString *contents;
67 GString *attrs;
69 attr_iter = g_menu_model_iterate_item_attributes (model, i);
70 link_iter = g_menu_model_iterate_item_links (model, i);
71 contents = g_string_new (NULL);
72 attrs = g_string_new (NULL);
74 while (g_menu_attribute_iter_next (attr_iter))
76 const char *name = g_menu_attribute_iter_get_name (attr_iter);
77 GVariant *value = g_menu_attribute_iter_get_value (attr_iter);
79 if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
81 gchar *str;
82 str = g_markup_printf_escaped (" %s='%s'", name, g_variant_get_string (value, NULL));
83 g_string_append (attrs, str);
84 g_free (str);
87 else
89 gchar *printed;
90 gchar *str;
91 const gchar *type;
93 printed = g_variant_print (value, TRUE);
94 type = g_variant_type_peek_string (g_variant_get_type (value));
95 str = g_markup_printf_escaped ("<attribute name='%s' type='%s'>%s</attribute>\n", name, type, printed);
96 indent_string (contents, indent + tabstop);
97 g_string_append (contents, str);
98 g_free (printed);
99 g_free (str);
102 g_variant_unref (value);
104 g_object_unref (attr_iter);
106 while (g_menu_link_iter_next (link_iter))
108 const gchar *name = g_menu_link_iter_get_name (link_iter);
109 GMenuModel *menu = g_menu_link_iter_get_value (link_iter);
110 gchar *str;
112 if (contents->str[0])
113 g_string_append_c (contents, '\n');
115 str = g_markup_printf_escaped ("<link name='%s'>\n", name);
116 indent_string (contents, indent + tabstop);
117 g_string_append (contents, str);
118 g_free (str);
120 g_menu_markup_print_string (contents, menu, indent + 2 * tabstop, tabstop);
122 indent_string (contents, indent + tabstop);
123 g_string_append (contents, "</link>\n");
124 g_object_unref (menu);
126 g_object_unref (link_iter);
128 if (contents->str[0])
130 indent_string (string, indent);
131 g_string_append_printf (string, "<item%s>\n", attrs->str);
132 g_string_append (string, contents->str);
133 indent_string (string, indent);
134 g_string_append (string, "</item>\n");
135 need_nl = TRUE;
138 else
140 if (need_nl)
141 g_string_append_c (string, '\n');
143 indent_string (string, indent);
144 g_string_append_printf (string, "<item%s/>\n", attrs->str);
145 need_nl = FALSE;
148 g_string_free (contents, TRUE);
149 g_string_free (attrs, TRUE);
152 return string;
155 /* TestItem {{{1 */
157 /* This utility struct is used by both the RandomMenu and MirrorMenu
158 * class implementations below.
160 typedef struct {
161 GHashTable *attributes;
162 GHashTable *links;
163 } TestItem;
165 static TestItem *
166 test_item_new (GHashTable *attributes,
167 GHashTable *links)
169 TestItem *item;
171 item = g_slice_new (TestItem);
172 item->attributes = g_hash_table_ref (attributes);
173 item->links = g_hash_table_ref (links);
175 return item;
178 static void
179 test_item_free (gpointer data)
181 TestItem *item = data;
183 g_hash_table_unref (item->attributes);
184 g_hash_table_unref (item->links);
186 g_slice_free (TestItem, item);
189 /* RandomMenu {{{1 */
190 #define MAX_ITEMS 5
191 #define TOP_ORDER 4
193 typedef struct {
194 GMenuModel parent_instance;
196 GSequence *items;
197 gint order;
198 } RandomMenu;
200 typedef GMenuModelClass RandomMenuClass;
202 static GType random_menu_get_type (void);
203 G_DEFINE_TYPE (RandomMenu, random_menu, G_TYPE_MENU_MODEL)
205 static gboolean
206 random_menu_is_mutable (GMenuModel *model)
208 return TRUE;
211 static gint
212 random_menu_get_n_items (GMenuModel *model)
214 RandomMenu *menu = (RandomMenu *) model;
216 return g_sequence_get_length (menu->items);
219 static void
220 random_menu_get_item_attributes (GMenuModel *model,
221 gint position,
222 GHashTable **table)
224 RandomMenu *menu = (RandomMenu *) model;
225 TestItem *item;
227 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
228 *table = g_hash_table_ref (item->attributes);
231 static void
232 random_menu_get_item_links (GMenuModel *model,
233 gint position,
234 GHashTable **table)
236 RandomMenu *menu = (RandomMenu *) model;
237 TestItem *item;
239 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
240 *table = g_hash_table_ref (item->links);
243 static void
244 random_menu_finalize (GObject *object)
246 RandomMenu *menu = (RandomMenu *) object;
248 g_sequence_free (menu->items);
250 G_OBJECT_CLASS (random_menu_parent_class)
251 ->finalize (object);
254 static void
255 random_menu_init (RandomMenu *menu)
259 static void
260 random_menu_class_init (GMenuModelClass *class)
262 GObjectClass *object_class = G_OBJECT_CLASS (class);
264 class->is_mutable = random_menu_is_mutable;
265 class->get_n_items = random_menu_get_n_items;
266 class->get_item_attributes = random_menu_get_item_attributes;
267 class->get_item_links = random_menu_get_item_links;
269 object_class->finalize = random_menu_finalize;
272 static RandomMenu * random_menu_new (GRand *rand, gint order);
274 static void
275 random_menu_change (RandomMenu *menu,
276 GRand *rand)
278 gint position, removes, adds;
279 GSequenceIter *point;
280 gint n_items;
281 gint i;
283 n_items = g_sequence_get_length (menu->items);
287 position = g_rand_int_range (rand, 0, n_items + 1);
288 removes = g_rand_int_range (rand, 0, n_items - position + 1);
289 adds = g_rand_int_range (rand, 0, MAX_ITEMS - (n_items - removes) + 1);
291 while (removes == 0 && adds == 0);
293 point = g_sequence_get_iter_at_pos (menu->items, position + removes);
295 if (removes)
297 GSequenceIter *start;
299 start = g_sequence_get_iter_at_pos (menu->items, position);
300 g_sequence_remove_range (start, point);
303 for (i = 0; i < adds; i++)
305 const gchar *label;
306 GHashTable *links;
307 GHashTable *attributes;
309 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
310 links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
312 if (menu->order > 0 && g_rand_boolean (rand))
314 RandomMenu *child;
315 const gchar *subtype;
317 child = random_menu_new (rand, menu->order - 1);
319 if (g_rand_boolean (rand))
321 subtype = G_MENU_LINK_SECTION;
322 /* label some section headers */
323 if (g_rand_boolean (rand))
324 label = "Section";
325 else
326 label = NULL;
328 else
330 /* label all submenus */
331 subtype = G_MENU_LINK_SUBMENU;
332 label = "Submenu";
335 g_hash_table_insert (links, g_strdup (subtype), child);
337 else
338 /* label all terminals */
339 label = "Menu Item";
341 if (label)
342 g_hash_table_insert (attributes, g_strdup ("label"), g_variant_ref_sink (g_variant_new_string (label)));
344 g_sequence_insert_before (point, test_item_new (attributes, links));
345 g_hash_table_unref (links);
346 g_hash_table_unref (attributes);
349 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removes, adds);
352 static RandomMenu *
353 random_menu_new (GRand *rand,
354 gint order)
356 RandomMenu *menu;
358 menu = g_object_new (random_menu_get_type (), NULL);
359 menu->items = g_sequence_new (test_item_free);
360 menu->order = order;
362 random_menu_change (menu, rand);
364 return menu;
367 /* MirrorMenu {{{1 */
368 typedef struct {
369 GMenuModel parent_instance;
371 GMenuModel *clone_of;
372 GSequence *items;
373 gulong handler_id;
374 } MirrorMenu;
376 typedef GMenuModelClass MirrorMenuClass;
378 static GType mirror_menu_get_type (void);
379 G_DEFINE_TYPE (MirrorMenu, mirror_menu, G_TYPE_MENU_MODEL)
381 static gboolean
382 mirror_menu_is_mutable (GMenuModel *model)
384 MirrorMenu *menu = (MirrorMenu *) model;
386 return menu->handler_id != 0;
389 static gint
390 mirror_menu_get_n_items (GMenuModel *model)
392 MirrorMenu *menu = (MirrorMenu *) model;
394 return g_sequence_get_length (menu->items);
397 static void
398 mirror_menu_get_item_attributes (GMenuModel *model,
399 gint position,
400 GHashTable **table)
402 MirrorMenu *menu = (MirrorMenu *) model;
403 TestItem *item;
405 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
406 *table = g_hash_table_ref (item->attributes);
409 static void
410 mirror_menu_get_item_links (GMenuModel *model,
411 gint position,
412 GHashTable **table)
414 MirrorMenu *menu = (MirrorMenu *) model;
415 TestItem *item;
417 item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
418 *table = g_hash_table_ref (item->links);
421 static void
422 mirror_menu_finalize (GObject *object)
424 MirrorMenu *menu = (MirrorMenu *) object;
426 if (menu->handler_id)
427 g_signal_handler_disconnect (menu->clone_of, menu->handler_id);
429 g_sequence_free (menu->items);
430 g_object_unref (menu->clone_of);
432 G_OBJECT_CLASS (mirror_menu_parent_class)
433 ->finalize (object);
436 static void
437 mirror_menu_init (MirrorMenu *menu)
441 static void
442 mirror_menu_class_init (GMenuModelClass *class)
444 GObjectClass *object_class = G_OBJECT_CLASS (class);
446 class->is_mutable = mirror_menu_is_mutable;
447 class->get_n_items = mirror_menu_get_n_items;
448 class->get_item_attributes = mirror_menu_get_item_attributes;
449 class->get_item_links = mirror_menu_get_item_links;
451 object_class->finalize = mirror_menu_finalize;
454 static MirrorMenu * mirror_menu_new (GMenuModel *clone_of);
456 static void
457 mirror_menu_changed (GMenuModel *model,
458 gint position,
459 gint removed,
460 gint added,
461 gpointer user_data)
463 MirrorMenu *menu = user_data;
464 GSequenceIter *point;
465 gint i;
467 g_assert (model == menu->clone_of);
469 point = g_sequence_get_iter_at_pos (menu->items, position + removed);
471 if (removed)
473 GSequenceIter *start;
475 start = g_sequence_get_iter_at_pos (menu->items, position);
476 g_sequence_remove_range (start, point);
479 for (i = position; i < position + added; i++)
481 GMenuAttributeIter *attr_iter;
482 GMenuLinkIter *link_iter;
483 GHashTable *links;
484 GHashTable *attributes;
485 const gchar *name;
486 GMenuModel *child;
487 GVariant *value;
489 attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
490 links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
492 attr_iter = g_menu_model_iterate_item_attributes (model, i);
493 while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
495 g_hash_table_insert (attributes, g_strdup (name), value);
497 g_object_unref (attr_iter);
499 link_iter = g_menu_model_iterate_item_links (model, i);
500 while (g_menu_link_iter_get_next (link_iter, &name, &child))
502 g_hash_table_insert (links, g_strdup (name), mirror_menu_new (child));
503 g_object_unref (child);
505 g_object_unref (link_iter);
507 g_sequence_insert_before (point, test_item_new (attributes, links));
508 g_hash_table_unref (attributes);
509 g_hash_table_unref (links);
512 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removed, added);
515 static MirrorMenu *
516 mirror_menu_new (GMenuModel *clone_of)
518 MirrorMenu *menu;
520 menu = g_object_new (mirror_menu_get_type (), NULL);
521 menu->items = g_sequence_new (test_item_free);
522 menu->clone_of = g_object_ref (clone_of);
524 if (g_menu_model_is_mutable (clone_of))
525 menu->handler_id = g_signal_connect (clone_of, "items-changed", G_CALLBACK (mirror_menu_changed), menu);
526 mirror_menu_changed (clone_of, 0, 0, g_menu_model_get_n_items (clone_of), menu);
528 return menu;
531 /* check_menus_equal(), assert_menus_equal() {{{1 */
532 static gboolean
533 check_menus_equal (GMenuModel *a,
534 GMenuModel *b)
536 gboolean equal = TRUE;
537 gint a_n, b_n;
538 gint i;
540 a_n = g_menu_model_get_n_items (a);
541 b_n = g_menu_model_get_n_items (b);
543 if (a_n != b_n)
544 return FALSE;
546 for (i = 0; i < a_n; i++)
548 GMenuAttributeIter *attr_iter;
549 GVariant *a_value, *b_value;
550 GMenuLinkIter *link_iter;
551 GMenuModel *a_menu, *b_menu;
552 const gchar *name;
554 attr_iter = g_menu_model_iterate_item_attributes (a, i);
555 while (g_menu_attribute_iter_get_next (attr_iter, &name, &a_value))
557 b_value = g_menu_model_get_item_attribute_value (b, i, name, NULL);
558 equal &= b_value && g_variant_equal (a_value, b_value);
559 if (b_value)
560 g_variant_unref (b_value);
561 g_variant_unref (a_value);
563 g_object_unref (attr_iter);
565 attr_iter = g_menu_model_iterate_item_attributes (b, i);
566 while (g_menu_attribute_iter_get_next (attr_iter, &name, &b_value))
568 a_value = g_menu_model_get_item_attribute_value (a, i, name, NULL);
569 equal &= a_value && g_variant_equal (a_value, b_value);
570 if (a_value)
571 g_variant_unref (a_value);
572 g_variant_unref (b_value);
574 g_object_unref (attr_iter);
576 link_iter = g_menu_model_iterate_item_links (a, i);
577 while (g_menu_link_iter_get_next (link_iter, &name, &a_menu))
579 b_menu = g_menu_model_get_item_link (b, i, name);
580 equal &= b_menu && check_menus_equal (a_menu, b_menu);
581 if (b_menu)
582 g_object_unref (b_menu);
583 g_object_unref (a_menu);
585 g_object_unref (link_iter);
587 link_iter = g_menu_model_iterate_item_links (b, i);
588 while (g_menu_link_iter_get_next (link_iter, &name, &b_menu))
590 a_menu = g_menu_model_get_item_link (a, i, name);
591 equal &= a_menu && check_menus_equal (a_menu, b_menu);
592 if (a_menu)
593 g_object_unref (a_menu);
594 g_object_unref (b_menu);
596 g_object_unref (link_iter);
599 return equal;
602 static void
603 assert_menus_equal (GMenuModel *a,
604 GMenuModel *b)
606 if (!check_menus_equal (a, b))
608 GString *string;
610 string = g_string_new ("\n <a>\n");
611 g_menu_markup_print_string (string, G_MENU_MODEL (a), 4, 2);
612 g_string_append (string, " </a>\n\n-------------\n <b>\n");
613 g_menu_markup_print_string (string, G_MENU_MODEL (b), 4, 2);
614 g_string_append (string, " </b>\n");
615 g_error ("%s", string->str);
619 static void
620 assert_menuitem_equal (GMenuItem *item,
621 GMenuModel *model,
622 gint index)
624 GMenuAttributeIter *attr_iter;
625 GMenuLinkIter *link_iter;
626 const gchar *name;
627 GVariant *value;
628 GMenuModel *linked_model;
630 /* NOTE we can't yet test whether item has attributes or links that
631 * are not in the model, because there's no iterator API for menu
632 * items */
634 attr_iter = g_menu_model_iterate_item_attributes (model, index);
635 while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
637 GVariant *item_value;
639 item_value = g_menu_item_get_attribute_value (item, name, g_variant_get_type (value));
640 g_assert (item_value && g_variant_equal (item_value, value));
642 g_variant_unref (item_value);
643 g_variant_unref (value);
646 link_iter = g_menu_model_iterate_item_links (model, index);
647 while (g_menu_link_iter_get_next (link_iter, &name, &linked_model))
649 GMenuModel *item_linked_model;
651 item_linked_model = g_menu_item_get_link (item, name);
652 g_assert (linked_model == item_linked_model);
654 g_object_unref (item_linked_model);
655 g_object_unref (linked_model);
658 g_object_unref (attr_iter);
659 g_object_unref (link_iter);
662 /* Test cases {{{1 */
663 static void
664 test_equality (void)
666 GRand *randa, *randb;
667 guint32 seed;
668 gint i;
670 seed = g_test_rand_int ();
672 randa = g_rand_new_with_seed (seed);
673 randb = g_rand_new_with_seed (seed);
675 for (i = 0; i < 500; i++)
677 RandomMenu *a, *b;
679 a = random_menu_new (randa, TOP_ORDER);
680 b = random_menu_new (randb, TOP_ORDER);
681 assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
682 g_object_unref (b);
683 g_object_unref (a);
686 g_rand_int (randa);
688 for (i = 0; i < 500;)
690 RandomMenu *a, *b;
692 a = random_menu_new (randa, TOP_ORDER);
693 b = random_menu_new (randb, TOP_ORDER);
694 if (check_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b)))
696 /* by chance, they may really be equal. double check. */
697 GString *as, *bs;
699 as = g_menu_markup_print_string (NULL, G_MENU_MODEL (a), 4, 2);
700 bs = g_menu_markup_print_string (NULL, G_MENU_MODEL (b), 4, 2);
701 g_assert_cmpstr (as->str, ==, bs->str);
702 g_string_free (bs, TRUE);
703 g_string_free (as, TRUE);
705 /* we're here because randa and randb just generated equal
706 * menus. they may do it again, so throw away randb and make
707 * a fresh one.
709 g_rand_free (randb);
710 randb = g_rand_new_with_seed (g_rand_int (randa));
712 else
713 /* make sure we get enough unequals (ie: no GRand failure) */
714 i++;
716 g_object_unref (b);
717 g_object_unref (a);
720 g_rand_free (randb);
721 g_rand_free (randa);
724 static void
725 test_random (void)
727 RandomMenu *random;
728 MirrorMenu *mirror;
729 GRand *rand;
730 gint i;
732 rand = g_rand_new_with_seed (g_test_rand_int ());
733 random = random_menu_new (rand, TOP_ORDER);
734 mirror = mirror_menu_new (G_MENU_MODEL (random));
736 for (i = 0; i < 500; i++)
738 assert_menus_equal (G_MENU_MODEL (random), G_MENU_MODEL (mirror));
739 random_menu_change (random, rand);
742 g_object_unref (mirror);
743 g_object_unref (random);
745 g_rand_free (rand);
748 typedef struct
750 GDBusConnection *client_connection;
751 GDBusConnection *server_connection;
752 GDBusServer *server;
754 GThread *service_thread;
755 GMutex service_loop_lock;
756 GCond service_loop_cond;
758 GMainLoop *service_loop;
759 GMainLoop *loop;
760 } PeerConnection;
762 static gboolean
763 on_new_connection (GDBusServer *server,
764 GDBusConnection *connection,
765 gpointer user_data)
767 PeerConnection *data = user_data;
769 data->server_connection = g_object_ref (connection);
771 g_main_loop_quit (data->loop);
773 return TRUE;
776 static void
777 create_service_loop (GMainContext *service_context,
778 PeerConnection *data)
780 g_assert (data->service_loop == NULL);
781 g_mutex_lock (&data->service_loop_lock);
782 data->service_loop = g_main_loop_new (service_context, FALSE);
783 g_cond_broadcast (&data->service_loop_cond);
784 g_mutex_unlock (&data->service_loop_lock);
787 static void
788 teardown_service_loop (PeerConnection *data)
790 g_mutex_lock (&data->service_loop_lock);
791 g_clear_pointer (&data->service_loop, g_main_loop_unref);
792 g_mutex_unlock (&data->service_loop_lock);
795 static void
796 await_service_loop (PeerConnection *data)
798 g_mutex_lock (&data->service_loop_lock);
799 while (data->service_loop == NULL)
800 g_cond_wait (&data->service_loop_cond, &data->service_loop_lock);
801 g_mutex_unlock (&data->service_loop_lock);
804 static gpointer
805 service_thread_func (gpointer user_data)
807 PeerConnection *data = user_data;
808 GMainContext *service_context;
809 GError *error;
810 gchar *address;
811 gchar *tmpdir;
812 GDBusServerFlags flags;
813 gchar *guid;
815 service_context = g_main_context_new ();
816 g_main_context_push_thread_default (service_context);
818 tmpdir = NULL;
819 flags = G_DBUS_SERVER_FLAGS_NONE;
821 #ifdef G_OS_UNIX
822 if (g_unix_socket_address_abstract_names_supported ())
823 address = g_strdup ("unix:tmpdir=/tmp/test-dbus-peer");
824 else
826 tmpdir = g_dir_make_tmp ("test-dbus-peer-XXXXXX", NULL);
827 address = g_strdup_printf ("unix:tmpdir=%s", tmpdir);
829 #else
830 address = g_strdup ("nonce-tcp:");
831 flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
832 #endif
834 guid = g_dbus_generate_guid ();
836 error = NULL;
837 data->server = g_dbus_server_new_sync (address,
838 flags,
839 guid,
840 NULL,
841 NULL,
842 &error);
843 g_assert_no_error (error);
844 g_free (address);
845 g_free (guid);
847 g_signal_connect (data->server,
848 "new-connection",
849 G_CALLBACK (on_new_connection),
850 data);
852 g_dbus_server_start (data->server);
854 create_service_loop (service_context, data);
855 g_main_loop_run (data->service_loop);
857 g_main_context_pop_thread_default (service_context);
859 teardown_service_loop (data);
860 g_main_context_unref (service_context);
862 if (tmpdir)
864 g_rmdir (tmpdir);
865 g_free (tmpdir);
868 return NULL;
871 static void
872 peer_connection_up (PeerConnection *data)
874 GError *error;
876 memset (data, '\0', sizeof (PeerConnection));
877 data->loop = g_main_loop_new (NULL, FALSE);
879 g_mutex_init (&data->service_loop_lock);
880 g_cond_init (&data->service_loop_cond);
882 /* bring up a server - we run the server in a different thread to
883 avoid deadlocks */
884 data->service_thread = g_thread_new ("test_dbus_peer",
885 service_thread_func,
886 data);
887 await_service_loop (data);
888 g_assert (data->server != NULL);
890 /* bring up a connection and accept it */
891 error = NULL;
892 data->client_connection =
893 g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (data->server),
894 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
895 NULL, /* GDBusAuthObserver */
896 NULL, /* cancellable */
897 &error);
898 g_assert_no_error (error);
899 g_assert (data->client_connection != NULL);
900 while (data->server_connection == NULL)
901 g_main_loop_run (data->loop);
904 static void
905 peer_connection_down (PeerConnection *data)
907 g_object_unref (data->client_connection);
908 g_object_unref (data->server_connection);
910 g_dbus_server_stop (data->server);
911 g_object_unref (data->server);
913 g_main_loop_quit (data->service_loop);
914 g_thread_join (data->service_thread);
916 g_mutex_clear (&data->service_loop_lock);
917 g_cond_clear (&data->service_loop_cond);
919 g_main_loop_unref (data->loop);
922 struct roundtrip_state
924 RandomMenu *random;
925 MirrorMenu *proxy_mirror;
926 GDBusMenuModel *proxy;
927 GMainLoop *loop;
928 GRand *rand;
929 gint success;
930 gint count;
933 static gboolean
934 roundtrip_step (gpointer data)
936 struct roundtrip_state *state = data;
938 if (check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy)) &&
939 check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy_mirror)))
941 state->success++;
942 state->count = 0;
944 if (state->success < 100)
945 random_menu_change (state->random, state->rand);
946 else
947 g_main_loop_quit (state->loop);
949 else if (state->count == 100)
951 assert_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy));
952 g_assert_not_reached ();
954 else
955 state->count++;
957 return G_SOURCE_CONTINUE;
960 static void
961 do_roundtrip (GDBusConnection *exporter_connection,
962 GDBusConnection *proxy_connection)
964 struct roundtrip_state state;
965 guint export_id;
966 guint id;
968 state.rand = g_rand_new_with_seed (g_test_rand_int ());
970 state.random = random_menu_new (state.rand, 2);
971 export_id = g_dbus_connection_export_menu_model (exporter_connection,
972 "/",
973 G_MENU_MODEL (state.random),
974 NULL);
975 state.proxy = g_dbus_menu_model_get (proxy_connection,
976 g_dbus_connection_get_unique_name (proxy_connection),
977 "/");
978 state.proxy_mirror = mirror_menu_new (G_MENU_MODEL (state.proxy));
979 state.count = 0;
980 state.success = 0;
982 id = g_timeout_add (10, roundtrip_step, &state);
984 state.loop = g_main_loop_new (NULL, FALSE);
985 g_main_loop_run (state.loop);
987 g_main_loop_unref (state.loop);
988 g_source_remove (id);
989 g_object_unref (state.proxy);
990 g_dbus_connection_unexport_menu_model (exporter_connection, export_id);
991 g_object_unref (state.random);
992 g_object_unref (state.proxy_mirror);
993 g_rand_free (state.rand);
996 static void
997 test_dbus_roundtrip (void)
999 GDBusConnection *bus;
1001 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1002 do_roundtrip (bus, bus);
1003 g_object_unref (bus);
1006 static void
1007 test_dbus_peer_roundtrip (void)
1009 PeerConnection peer;
1011 peer_connection_up (&peer);
1012 do_roundtrip (peer.server_connection, peer.client_connection);
1013 peer_connection_down (&peer);
1016 static gint items_changed_count;
1018 static void
1019 items_changed (GMenuModel *model,
1020 gint position,
1021 gint removed,
1022 gint added,
1023 gpointer data)
1025 items_changed_count++;
1028 static gboolean
1029 stop_loop (gpointer data)
1031 GMainLoop *loop = data;
1033 g_main_loop_quit (loop);
1035 return G_SOURCE_REMOVE;
1038 static void
1039 do_subscriptions (GDBusConnection *exporter_connection,
1040 GDBusConnection *proxy_connection)
1042 GMenu *menu;
1043 GDBusMenuModel *proxy;
1044 GMainLoop *loop;
1045 GError *error = NULL;
1046 guint export_id;
1047 guint timeout_id;
1049 timeout_id = add_timeout (60);
1050 loop = g_main_loop_new (NULL, FALSE);
1052 menu = g_menu_new ();
1054 export_id = g_dbus_connection_export_menu_model (exporter_connection,
1055 "/",
1056 G_MENU_MODEL (menu),
1057 &error);
1058 g_assert_no_error (error);
1060 proxy = g_dbus_menu_model_get (proxy_connection,
1061 g_dbus_connection_get_unique_name (proxy_connection),
1062 "/");
1063 items_changed_count = 0;
1064 g_signal_connect (proxy, "items-changed",
1065 G_CALLBACK (items_changed), NULL);
1067 g_menu_append (menu, "item1", NULL);
1068 g_menu_append (menu, "item2", NULL);
1069 g_menu_append (menu, "item3", NULL);
1071 g_assert_cmpint (items_changed_count, ==, 0);
1073 /* We don't subscribe to change-notification until we look at the items */
1074 g_timeout_add (100, stop_loop, loop);
1075 g_main_loop_run (loop);
1077 /* Looking at the items triggers subscription */
1078 g_menu_model_get_n_items (G_MENU_MODEL (proxy));
1080 while (items_changed_count < 1)
1081 g_main_context_iteration (NULL, TRUE);
1083 /* We get all three items in one batch */
1084 g_assert_cmpint (items_changed_count, ==, 1);
1085 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
1087 /* If we wait, we don't get any more */
1088 g_timeout_add (100, stop_loop, loop);
1089 g_main_loop_run (loop);
1090 g_assert_cmpint (items_changed_count, ==, 1);
1091 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
1093 /* Now we're subscribed, we get changes individually */
1094 g_menu_append (menu, "item4", NULL);
1095 g_menu_append (menu, "item5", NULL);
1096 g_menu_append (menu, "item6", NULL);
1097 g_menu_remove (menu, 0);
1098 g_menu_remove (menu, 0);
1100 while (items_changed_count < 6)
1101 g_main_context_iteration (NULL, TRUE);
1103 g_assert_cmpint (items_changed_count, ==, 6);
1105 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 4);
1107 /* After destroying the proxy and waiting a bit, we don't get any more
1108 * items-changed signals */
1109 g_object_unref (proxy);
1111 g_timeout_add (100, stop_loop, loop);
1112 g_main_loop_run (loop);
1114 g_menu_remove (menu, 0);
1115 g_menu_remove (menu, 0);
1117 g_timeout_add (100, stop_loop, loop);
1118 g_main_loop_run (loop);
1120 g_assert_cmpint (items_changed_count, ==, 6);
1122 g_dbus_connection_unexport_menu_model (exporter_connection, export_id);
1123 g_object_unref (menu);
1125 g_main_loop_unref (loop);
1126 cancel_timeout (timeout_id);
1129 static void
1130 test_dbus_subscriptions (void)
1132 GDBusConnection *bus;
1134 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1135 do_subscriptions (bus, bus);
1136 g_object_unref (bus);
1139 static void
1140 test_dbus_peer_subscriptions (void)
1142 PeerConnection peer;
1144 peer_connection_up (&peer);
1145 do_subscriptions (peer.server_connection, peer.client_connection);
1146 peer_connection_down (&peer);
1149 static gpointer
1150 do_modify (gpointer data)
1152 RandomMenu *menu = data;
1153 GRand *rand;
1154 gint i;
1156 rand = g_rand_new_with_seed (g_test_rand_int ());
1158 for (i = 0; i < 10000; i++)
1160 random_menu_change (menu, rand);
1163 return NULL;
1166 static gpointer
1167 do_export (gpointer data)
1169 GMenuModel *menu = data;
1170 gint i;
1171 GDBusConnection *bus;
1172 gchar *path;
1173 GError *error = NULL;
1174 guint id;
1176 bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1177 path = g_strdup_printf ("/%p", data);
1179 for (i = 0; i < 10000; i++)
1181 id = g_dbus_connection_export_menu_model (bus, path, menu, &error);
1182 g_assert_no_error (error);
1183 g_dbus_connection_unexport_menu_model (bus, id);
1184 while (g_main_context_iteration (NULL, FALSE));
1187 g_free (path);
1189 g_object_unref (bus);
1191 return NULL;
1194 static void
1195 test_dbus_threaded (void)
1197 RandomMenu *menu[10];
1198 GThread *call[10];
1199 GThread *export[10];
1200 gint i;
1202 for (i = 0; i < 10; i++)
1204 menu[i] = random_menu_new (g_rand_new_with_seed (g_test_rand_int ()), 2);
1205 call[i] = g_thread_new ("call", do_modify, menu[i]);
1206 export[i] = g_thread_new ("export", do_export, menu[i]);
1209 for (i = 0; i < 10; i++)
1211 g_thread_join (call[i]);
1212 g_thread_join (export[i]);
1215 for (i = 0; i < 10; i++)
1216 g_object_unref (menu[i]);
1219 static void
1220 test_attributes (void)
1222 GMenu *menu;
1223 GMenuItem *item;
1224 GVariant *v;
1226 menu = g_menu_new ();
1228 item = g_menu_item_new ("test", NULL);
1229 g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
1230 g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
1232 g_menu_item_set_attribute (item, "double", "d", 1.5);
1233 v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
1234 g_menu_item_set_attribute_value (item, "complex", v);
1235 g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
1237 g_menu_append_item (menu, item);
1239 g_menu_item_set_attribute (item, "double", "d", G_PI);
1241 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
1243 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "boolean", NULL);
1244 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
1245 g_variant_unref (v);
1247 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "string", NULL);
1248 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1249 g_variant_unref (v);
1251 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "double", NULL);
1252 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1253 g_variant_unref (v);
1255 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "complex", NULL);
1256 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1257 g_variant_unref (v);
1259 g_menu_remove_all (menu);
1261 g_object_unref (menu);
1262 g_object_unref (item);
1265 static void
1266 test_attribute_iter (void)
1268 GMenu *menu;
1269 GMenuItem *item;
1270 const gchar *name;
1271 GVariant *v;
1272 GMenuAttributeIter *iter;
1273 GHashTable *found;
1275 menu = g_menu_new ();
1277 item = g_menu_item_new ("test", NULL);
1278 g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
1279 g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
1281 g_menu_item_set_attribute (item, "double", "d", 1.5);
1282 v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
1283 g_menu_item_set_attribute_value (item, "complex", v);
1284 g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
1286 g_menu_append_item (menu, item);
1288 g_menu_item_set_attribute (item, "double", "d", G_PI);
1290 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
1292 found = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
1294 iter = g_menu_model_iterate_item_attributes (G_MENU_MODEL (menu), 0);
1295 while (g_menu_attribute_iter_get_next (iter, &name, &v))
1296 g_hash_table_insert (found, g_strdup (name), v);
1298 g_assert_cmpint (g_hash_table_size (found), ==, 6);
1300 v = g_hash_table_lookup (found, "label");
1301 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1303 v = g_hash_table_lookup (found, "boolean");
1304 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
1306 v = g_hash_table_lookup (found, "string");
1307 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1309 v = g_hash_table_lookup (found, "double");
1310 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1312 v = g_hash_table_lookup (found, "complex");
1313 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1315 v = g_hash_table_lookup (found, "test-123");
1316 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1318 g_hash_table_unref (found);
1320 g_menu_remove_all (menu);
1322 g_object_unref (menu);
1323 g_object_unref (item);
1326 static void
1327 test_links (void)
1329 GMenu *menu;
1330 GMenuModel *m;
1331 GMenuModel *x;
1332 GMenuItem *item;
1334 m = G_MENU_MODEL (g_menu_new ());
1335 g_menu_append (G_MENU (m), "test", NULL);
1337 menu = g_menu_new ();
1339 item = g_menu_item_new ("test2", NULL);
1340 g_menu_item_set_link (item, "submenu", m);
1341 g_menu_prepend_item (menu, item);
1343 item = g_menu_item_new ("test1", NULL);
1344 g_menu_item_set_link (item, "section", m);
1345 g_menu_insert_item (menu, 0, item);
1347 item = g_menu_item_new ("test3", NULL);
1348 g_menu_item_set_link (item, "wallet", m);
1349 g_menu_insert_item (menu, 1000, item);
1351 item = g_menu_item_new ("test4", NULL);
1352 g_menu_item_set_link (item, "purse", m);
1353 g_menu_item_set_link (item, "purse", NULL);
1354 g_menu_append_item (menu, item);
1356 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 4);
1358 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, "section");
1359 g_assert (x == m);
1360 g_object_unref (x);
1362 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 1, "submenu");
1363 g_assert (x == m);
1364 g_object_unref (x);
1366 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 2, "wallet");
1367 g_assert (x == m);
1368 g_object_unref (x);
1370 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 3, "purse");
1371 g_assert (x == NULL);
1373 g_object_unref (m);
1374 g_object_unref (menu);
1377 static void
1378 test_mutable (void)
1380 GMenu *menu;
1382 menu = g_menu_new ();
1383 g_menu_append (menu, "test", "test");
1385 g_assert (g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1386 g_menu_freeze (menu);
1387 g_assert (!g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1389 g_object_unref (menu);
1392 static void
1393 test_convenience (void)
1395 GMenu *m1, *m2;
1396 GMenu *sub;
1397 GMenuItem *item;
1399 m1 = g_menu_new ();
1400 m2 = g_menu_new ();
1401 sub = g_menu_new ();
1403 g_menu_prepend (m1, "label1", "do::something");
1404 g_menu_insert (m2, 0, "label1", "do::something");
1406 g_menu_append (m1, "label2", "do::somethingelse");
1407 g_menu_insert (m2, -1, "label2", "do::somethingelse");
1409 g_menu_insert_section (m1, 10, "label3", G_MENU_MODEL (sub));
1410 item = g_menu_item_new_section ("label3", G_MENU_MODEL (sub));
1411 g_menu_insert_item (m2, 10, item);
1412 g_object_unref (item);
1414 g_menu_prepend_section (m1, "label4", G_MENU_MODEL (sub));
1415 g_menu_insert_section (m2, 0, "label4", G_MENU_MODEL (sub));
1417 g_menu_append_section (m1, "label5", G_MENU_MODEL (sub));
1418 g_menu_insert_section (m2, -1, "label5", G_MENU_MODEL (sub));
1420 g_menu_insert_submenu (m1, 5, "label6", G_MENU_MODEL (sub));
1421 item = g_menu_item_new_submenu ("label6", G_MENU_MODEL (sub));
1422 g_menu_insert_item (m2, 5, item);
1423 g_object_unref (item);
1425 g_menu_prepend_submenu (m1, "label7", G_MENU_MODEL (sub));
1426 g_menu_insert_submenu (m2, 0, "label7", G_MENU_MODEL (sub));
1428 g_menu_append_submenu (m1, "label8", G_MENU_MODEL (sub));
1429 g_menu_insert_submenu (m2, -1, "label8", G_MENU_MODEL (sub));
1431 assert_menus_equal (G_MENU_MODEL (m1), G_MENU_MODEL (m2));
1433 g_object_unref (m1);
1434 g_object_unref (m2);
1437 static void
1438 test_menuitem (void)
1440 GMenu *menu;
1441 GMenu *submenu;
1442 GMenuItem *item;
1443 GIcon *icon;
1444 gboolean b;
1445 gchar *s;
1447 menu = g_menu_new ();
1448 submenu = g_menu_new ();
1450 item = g_menu_item_new ("label", "action");
1451 g_menu_item_set_attribute (item, "attribute", "b", TRUE);
1452 g_menu_item_set_link (item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu));
1453 g_menu_append_item (menu, item);
1455 icon = g_themed_icon_new ("bla");
1456 g_menu_item_set_icon (item, icon);
1457 g_object_unref (icon);
1459 g_assert (g_menu_item_get_attribute (item, "attribute", "b", &b));
1460 g_assert (b);
1462 g_menu_item_set_action_and_target (item, "action", "(bs)", TRUE, "string");
1463 g_assert (g_menu_item_get_attribute (item, "target", "(bs)", &b, &s));
1464 g_assert (b);
1465 g_assert_cmpstr (s, ==, "string");
1466 g_free (s);
1468 g_object_unref (item);
1470 item = g_menu_item_new_from_model (G_MENU_MODEL (menu), 0);
1471 assert_menuitem_equal (item, G_MENU_MODEL (menu), 0);
1472 g_object_unref (item);
1474 g_object_unref (menu);
1475 g_object_unref (submenu);
1478 /* Epilogue {{{1 */
1480 main (int argc, char **argv)
1482 gboolean ret;
1484 g_test_init (&argc, &argv, NULL);
1486 session_bus_up ();
1488 g_test_add_func ("/gmenu/equality", test_equality);
1489 g_test_add_func ("/gmenu/random", test_random);
1490 g_test_add_func ("/gmenu/dbus/roundtrip", test_dbus_roundtrip);
1491 g_test_add_func ("/gmenu/dbus/subscriptions", test_dbus_subscriptions);
1492 g_test_add_func ("/gmenu/dbus/threaded", test_dbus_threaded);
1493 g_test_add_func ("/gmenu/dbus/peer/roundtrip", test_dbus_peer_roundtrip);
1494 g_test_add_func ("/gmenu/dbus/peer/subscriptions", test_dbus_peer_subscriptions);
1495 g_test_add_func ("/gmenu/attributes", test_attributes);
1496 g_test_add_func ("/gmenu/attributes/iterate", test_attribute_iter);
1497 g_test_add_func ("/gmenu/links", test_links);
1498 g_test_add_func ("/gmenu/mutable", test_mutable);
1499 g_test_add_func ("/gmenu/convenience", test_convenience);
1500 g_test_add_func ("/gmenu/menuitem", test_menuitem);
1502 ret = g_test_run ();
1504 session_bus_down ();
1506 return ret;
1508 /* vim:set foldmethod=marker: */