1#include <gio/gio.h>
2#include <gio/gunixsocketaddress.h>
3#include <glib/gstdio.h>
4#include <string.h>
5
6#include "gdbus-sessionbus.h"
7
8#include "glib/glib-private.h"
9
10static gboolean
11time_out (gpointer unused G_GNUC_UNUSED)
12{
13 g_error ("Timed out");
14 /* not reached */
15 return FALSE;
16}
17
18static guint
19add_timeout (guint seconds)
20{
21#ifdef G_OS_UNIX
22 /* Safety-catch against the main loop having blocked */
23 alarm (seconds: seconds + 5);
24#endif
25 return g_timeout_add_seconds (interval: seconds, function: time_out, NULL);
26}
27
28static void
29cancel_timeout (guint timeout_id)
30{
31#ifdef G_OS_UNIX
32 alarm (seconds: 0);
33#endif
34 g_source_remove (tag: timeout_id);
35}
36
37/* Markup printing {{{1 */
38
39/* This used to be part of GLib, but it was removed before the stable
40 * release because it wasn't generally useful. We want it here, though.
41 */
42static void
43indent_string (GString *string,
44 gint indent)
45{
46 while (indent--)
47 g_string_append_c (string, ' ');
48}
49
50static GString *
51g_menu_markup_print_string (GString *string,
52 GMenuModel *model,
53 gint indent,
54 gint tabstop)
55{
56 gboolean need_nl = FALSE;
57 gint i, n;
58
59 if G_UNLIKELY (string == NULL)
60 string = g_string_new (NULL);
61
62 n = g_menu_model_get_n_items (model);
63
64 for (i = 0; i < n; i++)
65 {
66 GMenuAttributeIter *attr_iter;
67 GMenuLinkIter *link_iter;
68 GString *contents;
69 GString *attrs;
70
71 attr_iter = g_menu_model_iterate_item_attributes (model, item_index: i);
72 link_iter = g_menu_model_iterate_item_links (model, item_index: i);
73 contents = g_string_new (NULL);
74 attrs = g_string_new (NULL);
75
76 while (g_menu_attribute_iter_next (iter: attr_iter))
77 {
78 const char *name = g_menu_attribute_iter_get_name (iter: attr_iter);
79 GVariant *value = g_menu_attribute_iter_get_value (iter: attr_iter);
80
81 if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
82 {
83 gchar *str;
84 str = g_markup_printf_escaped (format: " %s='%s'", name, g_variant_get_string (value, NULL));
85 g_string_append (string: attrs, val: str);
86 g_free (mem: str);
87 }
88
89 else
90 {
91 gchar *printed;
92 gchar *str;
93 const gchar *type;
94
95 printed = g_variant_print (value, TRUE);
96 type = g_variant_type_peek_string (type: g_variant_get_type (value));
97 str = g_markup_printf_escaped (format: "<attribute name='%s' type='%s'>%s</attribute>\n", name, type, printed);
98 indent_string (string: contents, indent: indent + tabstop);
99 g_string_append (string: contents, val: str);
100 g_free (mem: printed);
101 g_free (mem: str);
102 }
103
104 g_variant_unref (value);
105 }
106 g_object_unref (object: attr_iter);
107
108 while (g_menu_link_iter_next (iter: link_iter))
109 {
110 const gchar *name = g_menu_link_iter_get_name (iter: link_iter);
111 GMenuModel *menu = g_menu_link_iter_get_value (iter: link_iter);
112 gchar *str;
113
114 if (contents->str[0])
115 g_string_append_c (contents, '\n');
116
117 str = g_markup_printf_escaped (format: "<link name='%s'>\n", name);
118 indent_string (string: contents, indent: indent + tabstop);
119 g_string_append (string: contents, val: str);
120 g_free (mem: str);
121
122 g_menu_markup_print_string (string: contents, model: menu, indent: indent + 2 * tabstop, tabstop);
123
124 indent_string (string: contents, indent: indent + tabstop);
125 g_string_append (string: contents, val: "</link>\n");
126 g_object_unref (object: menu);
127 }
128 g_object_unref (object: link_iter);
129
130 if (contents->str[0])
131 {
132 indent_string (string, indent);
133 g_string_append_printf (string, format: "<item%s>\n", attrs->str);
134 g_string_append (string, val: contents->str);
135 indent_string (string, indent);
136 g_string_append (string, val: "</item>\n");
137 need_nl = TRUE;
138 }
139
140 else
141 {
142 if (need_nl)
143 g_string_append_c (string, '\n');
144
145 indent_string (string, indent);
146 g_string_append_printf (string, format: "<item%s/>\n", attrs->str);
147 need_nl = FALSE;
148 }
149
150 g_string_free (string: contents, TRUE);
151 g_string_free (string: attrs, TRUE);
152 }
153
154 return string;
155}
156
157/* TestItem {{{1 */
158
159/* This utility struct is used by both the RandomMenu and MirrorMenu
160 * class implementations below.
161 */
162typedef struct {
163 GHashTable *attributes;
164 GHashTable *links;
165} TestItem;
166
167static TestItem *
168test_item_new (GHashTable *attributes,
169 GHashTable *links)
170{
171 TestItem *item;
172
173 item = g_slice_new (TestItem);
174 item->attributes = g_hash_table_ref (hash_table: attributes);
175 item->links = g_hash_table_ref (hash_table: links);
176
177 return item;
178}
179
180static void
181test_item_free (gpointer data)
182{
183 TestItem *item = data;
184
185 g_hash_table_unref (hash_table: item->attributes);
186 g_hash_table_unref (hash_table: item->links);
187
188 g_slice_free (TestItem, item);
189}
190
191/* RandomMenu {{{1 */
192#define MAX_ITEMS 5
193#define TOP_ORDER 4
194
195typedef struct {
196 GMenuModel parent_instance;
197
198 GSequence *items;
199 gint order;
200} RandomMenu;
201
202typedef GMenuModelClass RandomMenuClass;
203
204static GType random_menu_get_type (void);
205G_DEFINE_TYPE (RandomMenu, random_menu, G_TYPE_MENU_MODEL)
206
207static gboolean
208random_menu_is_mutable (GMenuModel *model)
209{
210 return TRUE;
211}
212
213static gint
214random_menu_get_n_items (GMenuModel *model)
215{
216 RandomMenu *menu = (RandomMenu *) model;
217
218 return g_sequence_get_length (seq: menu->items);
219}
220
221static void
222random_menu_get_item_attributes (GMenuModel *model,
223 gint position,
224 GHashTable **table)
225{
226 RandomMenu *menu = (RandomMenu *) model;
227 TestItem *item;
228
229 item = g_sequence_get (iter: g_sequence_get_iter_at_pos (seq: menu->items, pos: position));
230 *table = g_hash_table_ref (hash_table: item->attributes);
231}
232
233static void
234random_menu_get_item_links (GMenuModel *model,
235 gint position,
236 GHashTable **table)
237{
238 RandomMenu *menu = (RandomMenu *) model;
239 TestItem *item;
240
241 item = g_sequence_get (iter: g_sequence_get_iter_at_pos (seq: menu->items, pos: position));
242 *table = g_hash_table_ref (hash_table: item->links);
243}
244
245static void
246random_menu_finalize (GObject *object)
247{
248 RandomMenu *menu = (RandomMenu *) object;
249
250 g_sequence_free (seq: menu->items);
251
252 G_OBJECT_CLASS (random_menu_parent_class)
253 ->finalize (object);
254}
255
256static void
257random_menu_init (RandomMenu *menu)
258{
259}
260
261static void
262random_menu_class_init (GMenuModelClass *class)
263{
264 GObjectClass *object_class = G_OBJECT_CLASS (class);
265
266 class->is_mutable = random_menu_is_mutable;
267 class->get_n_items = random_menu_get_n_items;
268 class->get_item_attributes = random_menu_get_item_attributes;
269 class->get_item_links = random_menu_get_item_links;
270
271 object_class->finalize = random_menu_finalize;
272}
273
274static RandomMenu * random_menu_new (GRand *rand, gint order);
275
276static void
277random_menu_change (RandomMenu *menu,
278 GRand *rand)
279{
280 gint position, removes, adds;
281 GSequenceIter *point;
282 gint n_items;
283 gint i;
284
285 n_items = g_sequence_get_length (seq: menu->items);
286
287 do
288 {
289 position = g_rand_int_range (rand_: rand, begin: 0, end: n_items + 1);
290 removes = g_rand_int_range (rand_: rand, begin: 0, end: n_items - position + 1);
291 adds = g_rand_int_range (rand_: rand, begin: 0, MAX_ITEMS - (n_items - removes) + 1);
292 }
293 while (removes == 0 && adds == 0);
294
295 point = g_sequence_get_iter_at_pos (seq: menu->items, pos: position + removes);
296
297 if (removes)
298 {
299 GSequenceIter *start;
300
301 start = g_sequence_get_iter_at_pos (seq: menu->items, pos: position);
302 g_sequence_remove_range (begin: start, end: point);
303 }
304
305 for (i = 0; i < adds; i++)
306 {
307 const gchar *label;
308 GHashTable *links;
309 GHashTable *attributes;
310
311 attributes = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, value_destroy_func: (GDestroyNotify) g_variant_unref);
312 links = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, value_destroy_func: (GDestroyNotify) g_object_unref);
313
314 if (menu->order > 0 && g_rand_boolean (rand))
315 {
316 RandomMenu *child;
317 const gchar *subtype;
318
319 child = random_menu_new (rand, order: menu->order - 1);
320
321 if (g_rand_boolean (rand))
322 {
323 subtype = G_MENU_LINK_SECTION;
324 /* label some section headers */
325 if (g_rand_boolean (rand))
326 label = "Section";
327 else
328 label = NULL;
329 }
330 else
331 {
332 /* label all submenus */
333 subtype = G_MENU_LINK_SUBMENU;
334 label = "Submenu";
335 }
336
337 g_hash_table_insert (hash_table: links, key: g_strdup (str: subtype), value: child);
338 }
339 else
340 /* label all terminals */
341 label = "Menu Item";
342
343 if (label)
344 g_hash_table_insert (hash_table: attributes, key: g_strdup (str: "label"), value: g_variant_ref_sink (value: g_variant_new_string (string: label)));
345
346 g_sequence_insert_before (iter: point, data: test_item_new (attributes, links));
347 g_hash_table_unref (hash_table: links);
348 g_hash_table_unref (hash_table: attributes);
349 }
350
351 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removed: removes, added: adds);
352}
353
354static RandomMenu *
355random_menu_new (GRand *rand,
356 gint order)
357{
358 RandomMenu *menu;
359
360 menu = g_object_new (object_type: random_menu_get_type (), NULL);
361 menu->items = g_sequence_new (data_destroy: test_item_free);
362 menu->order = order;
363
364 random_menu_change (menu, rand);
365
366 return menu;
367}
368
369/* MirrorMenu {{{1 */
370typedef struct {
371 GMenuModel parent_instance;
372
373 GMenuModel *clone_of;
374 GSequence *items;
375 gulong handler_id;
376} MirrorMenu;
377
378typedef GMenuModelClass MirrorMenuClass;
379
380static GType mirror_menu_get_type (void);
381G_DEFINE_TYPE (MirrorMenu, mirror_menu, G_TYPE_MENU_MODEL)
382
383static gboolean
384mirror_menu_is_mutable (GMenuModel *model)
385{
386 MirrorMenu *menu = (MirrorMenu *) model;
387
388 return menu->handler_id != 0;
389}
390
391static gint
392mirror_menu_get_n_items (GMenuModel *model)
393{
394 MirrorMenu *menu = (MirrorMenu *) model;
395
396 return g_sequence_get_length (seq: menu->items);
397}
398
399static void
400mirror_menu_get_item_attributes (GMenuModel *model,
401 gint position,
402 GHashTable **table)
403{
404 MirrorMenu *menu = (MirrorMenu *) model;
405 TestItem *item;
406
407 item = g_sequence_get (iter: g_sequence_get_iter_at_pos (seq: menu->items, pos: position));
408 *table = g_hash_table_ref (hash_table: item->attributes);
409}
410
411static void
412mirror_menu_get_item_links (GMenuModel *model,
413 gint position,
414 GHashTable **table)
415{
416 MirrorMenu *menu = (MirrorMenu *) model;
417 TestItem *item;
418
419 item = g_sequence_get (iter: g_sequence_get_iter_at_pos (seq: menu->items, pos: position));
420 *table = g_hash_table_ref (hash_table: item->links);
421}
422
423static void
424mirror_menu_finalize (GObject *object)
425{
426 MirrorMenu *menu = (MirrorMenu *) object;
427
428 if (menu->handler_id)
429 g_signal_handler_disconnect (instance: menu->clone_of, handler_id: menu->handler_id);
430
431 g_sequence_free (seq: menu->items);
432 g_object_unref (object: menu->clone_of);
433
434 G_OBJECT_CLASS (mirror_menu_parent_class)
435 ->finalize (object);
436}
437
438static void
439mirror_menu_init (MirrorMenu *menu)
440{
441}
442
443static void
444mirror_menu_class_init (GMenuModelClass *class)
445{
446 GObjectClass *object_class = G_OBJECT_CLASS (class);
447
448 class->is_mutable = mirror_menu_is_mutable;
449 class->get_n_items = mirror_menu_get_n_items;
450 class->get_item_attributes = mirror_menu_get_item_attributes;
451 class->get_item_links = mirror_menu_get_item_links;
452
453 object_class->finalize = mirror_menu_finalize;
454}
455
456static MirrorMenu * mirror_menu_new (GMenuModel *clone_of);
457
458static void
459mirror_menu_changed (GMenuModel *model,
460 gint position,
461 gint removed,
462 gint added,
463 gpointer user_data)
464{
465 MirrorMenu *menu = user_data;
466 GSequenceIter *point;
467 gint i;
468
469 g_assert (model == menu->clone_of);
470
471 point = g_sequence_get_iter_at_pos (seq: menu->items, pos: position + removed);
472
473 if (removed)
474 {
475 GSequenceIter *start;
476
477 start = g_sequence_get_iter_at_pos (seq: menu->items, pos: position);
478 g_sequence_remove_range (begin: start, end: point);
479 }
480
481 for (i = position; i < position + added; i++)
482 {
483 GMenuAttributeIter *attr_iter;
484 GMenuLinkIter *link_iter;
485 GHashTable *links;
486 GHashTable *attributes;
487 const gchar *name;
488 GMenuModel *child;
489 GVariant *value;
490
491 attributes = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, value_destroy_func: (GDestroyNotify) g_variant_unref);
492 links = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, value_destroy_func: (GDestroyNotify) g_object_unref);
493
494 attr_iter = g_menu_model_iterate_item_attributes (model, item_index: i);
495 while (g_menu_attribute_iter_get_next (iter: attr_iter, out_name: &name, value: &value))
496 {
497 g_hash_table_insert (hash_table: attributes, key: g_strdup (str: name), value);
498 }
499 g_object_unref (object: attr_iter);
500
501 link_iter = g_menu_model_iterate_item_links (model, item_index: i);
502 while (g_menu_link_iter_get_next (iter: link_iter, out_link: &name, value: &child))
503 {
504 g_hash_table_insert (hash_table: links, key: g_strdup (str: name), value: mirror_menu_new (clone_of: child));
505 g_object_unref (object: child);
506 }
507 g_object_unref (object: link_iter);
508
509 g_sequence_insert_before (iter: point, data: test_item_new (attributes, links));
510 g_hash_table_unref (hash_table: attributes);
511 g_hash_table_unref (hash_table: links);
512 }
513
514 g_menu_model_items_changed (G_MENU_MODEL (menu), position, removed, added);
515}
516
517static MirrorMenu *
518mirror_menu_new (GMenuModel *clone_of)
519{
520 MirrorMenu *menu;
521
522 menu = g_object_new (object_type: mirror_menu_get_type (), NULL);
523 menu->items = g_sequence_new (data_destroy: test_item_free);
524 menu->clone_of = g_object_ref (clone_of);
525
526 if (g_menu_model_is_mutable (model: clone_of))
527 menu->handler_id = g_signal_connect (clone_of, "items-changed", G_CALLBACK (mirror_menu_changed), menu);
528 mirror_menu_changed (model: clone_of, position: 0, removed: 0, added: g_menu_model_get_n_items (model: clone_of), user_data: menu);
529
530 return menu;
531}
532
533/* check_menus_equal(), assert_menus_equal() {{{1 */
534static gboolean
535check_menus_equal (GMenuModel *a,
536 GMenuModel *b)
537{
538 gboolean equal = TRUE;
539 gint a_n, b_n;
540 gint i;
541
542 a_n = g_menu_model_get_n_items (model: a);
543 b_n = g_menu_model_get_n_items (model: b);
544
545 if (a_n != b_n)
546 return FALSE;
547
548 for (i = 0; i < a_n; i++)
549 {
550 GMenuAttributeIter *attr_iter;
551 GVariant *a_value, *b_value;
552 GMenuLinkIter *link_iter;
553 GMenuModel *a_menu, *b_menu;
554 const gchar *name;
555
556 attr_iter = g_menu_model_iterate_item_attributes (model: a, item_index: i);
557 while (g_menu_attribute_iter_get_next (iter: attr_iter, out_name: &name, value: &a_value))
558 {
559 b_value = g_menu_model_get_item_attribute_value (model: b, item_index: i, attribute: name, NULL);
560 equal &= b_value && g_variant_equal (one: a_value, two: b_value);
561 if (b_value)
562 g_variant_unref (value: b_value);
563 g_variant_unref (value: a_value);
564 }
565 g_object_unref (object: attr_iter);
566
567 attr_iter = g_menu_model_iterate_item_attributes (model: b, item_index: i);
568 while (g_menu_attribute_iter_get_next (iter: attr_iter, out_name: &name, value: &b_value))
569 {
570 a_value = g_menu_model_get_item_attribute_value (model: a, item_index: i, attribute: name, NULL);
571 equal &= a_value && g_variant_equal (one: a_value, two: b_value);
572 if (a_value)
573 g_variant_unref (value: a_value);
574 g_variant_unref (value: b_value);
575 }
576 g_object_unref (object: attr_iter);
577
578 link_iter = g_menu_model_iterate_item_links (model: a, item_index: i);
579 while (g_menu_link_iter_get_next (iter: link_iter, out_link: &name, value: &a_menu))
580 {
581 b_menu = g_menu_model_get_item_link (model: b, item_index: i, link: name);
582 equal &= b_menu && check_menus_equal (a: a_menu, b: b_menu);
583 if (b_menu)
584 g_object_unref (object: b_menu);
585 g_object_unref (object: a_menu);
586 }
587 g_object_unref (object: link_iter);
588
589 link_iter = g_menu_model_iterate_item_links (model: b, item_index: i);
590 while (g_menu_link_iter_get_next (iter: link_iter, out_link: &name, value: &b_menu))
591 {
592 a_menu = g_menu_model_get_item_link (model: a, item_index: i, link: name);
593 equal &= a_menu && check_menus_equal (a: a_menu, b: b_menu);
594 if (a_menu)
595 g_object_unref (object: a_menu);
596 g_object_unref (object: b_menu);
597 }
598 g_object_unref (object: link_iter);
599 }
600
601 return equal;
602}
603
604static void
605assert_menus_equal (GMenuModel *a,
606 GMenuModel *b)
607{
608 if (!check_menus_equal (a, b))
609 {
610 GString *string;
611
612 string = g_string_new (init: "\n <a>\n");
613 g_menu_markup_print_string (string, G_MENU_MODEL (a), indent: 4, tabstop: 2);
614 g_string_append (string, val: " </a>\n\n-------------\n <b>\n");
615 g_menu_markup_print_string (string, G_MENU_MODEL (b), indent: 4, tabstop: 2);
616 g_string_append (string, val: " </b>\n");
617 g_error ("%s", string->str);
618 }
619}
620
621static void
622assert_menuitem_equal (GMenuItem *item,
623 GMenuModel *model,
624 gint index)
625{
626 GMenuAttributeIter *attr_iter;
627 GMenuLinkIter *link_iter;
628 const gchar *name;
629 GVariant *value;
630 GMenuModel *linked_model;
631
632 /* NOTE we can't yet test whether item has attributes or links that
633 * are not in the model, because there's no iterator API for menu
634 * items */
635
636 attr_iter = g_menu_model_iterate_item_attributes (model, item_index: index);
637 while (g_menu_attribute_iter_get_next (iter: attr_iter, out_name: &name, value: &value))
638 {
639 GVariant *item_value;
640
641 item_value = g_menu_item_get_attribute_value (menu_item: item, attribute: name, expected_type: g_variant_get_type (value));
642 g_assert (item_value && g_variant_equal (item_value, value));
643
644 g_variant_unref (value: item_value);
645 g_variant_unref (value);
646 }
647
648 link_iter = g_menu_model_iterate_item_links (model, item_index: index);
649 while (g_menu_link_iter_get_next (iter: link_iter, out_link: &name, value: &linked_model))
650 {
651 GMenuModel *item_linked_model;
652
653 item_linked_model = g_menu_item_get_link (menu_item: item, link: name);
654 g_assert (linked_model == item_linked_model);
655
656 g_object_unref (object: item_linked_model);
657 g_object_unref (object: linked_model);
658 }
659
660 g_object_unref (object: attr_iter);
661 g_object_unref (object: link_iter);
662}
663
664/* Test cases {{{1 */
665static void
666test_equality (void)
667{
668 GRand *randa, *randb;
669 guint32 seed;
670 gint i;
671
672 seed = g_test_rand_int ();
673
674 randa = g_rand_new_with_seed (seed);
675 randb = g_rand_new_with_seed (seed);
676
677 for (i = 0; i < 500; i++)
678 {
679 RandomMenu *a, *b;
680
681 a = random_menu_new (rand: randa, TOP_ORDER);
682 b = random_menu_new (rand: randb, TOP_ORDER);
683 assert_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b));
684 g_object_unref (object: b);
685 g_object_unref (object: a);
686 }
687
688 g_rand_int (rand_: randa);
689
690 for (i = 0; i < 500;)
691 {
692 RandomMenu *a, *b;
693
694 a = random_menu_new (rand: randa, TOP_ORDER);
695 b = random_menu_new (rand: randb, TOP_ORDER);
696 if (check_menus_equal (G_MENU_MODEL (a), G_MENU_MODEL (b)))
697 {
698 /* by chance, they may really be equal. double check. */
699 GString *as, *bs;
700
701 as = g_menu_markup_print_string (NULL, G_MENU_MODEL (a), indent: 4, tabstop: 2);
702 bs = g_menu_markup_print_string (NULL, G_MENU_MODEL (b), indent: 4, tabstop: 2);
703 g_assert_cmpstr (as->str, ==, bs->str);
704 g_string_free (string: bs, TRUE);
705 g_string_free (string: as, TRUE);
706
707 /* we're here because randa and randb just generated equal
708 * menus. they may do it again, so throw away randb and make
709 * a fresh one.
710 */
711 g_rand_free (rand_: randb);
712 randb = g_rand_new_with_seed (seed: g_rand_int (rand_: randa));
713 }
714 else
715 /* make sure we get enough unequals (ie: no GRand failure) */
716 i++;
717
718 g_object_unref (object: b);
719 g_object_unref (object: a);
720 }
721
722 g_rand_free (rand_: randb);
723 g_rand_free (rand_: randa);
724}
725
726static void
727test_random (void)
728{
729 RandomMenu *random;
730 MirrorMenu *mirror;
731 GRand *rand;
732 gint i;
733
734 rand = g_rand_new_with_seed (seed: g_test_rand_int ());
735 random = random_menu_new (rand, TOP_ORDER);
736 mirror = mirror_menu_new (G_MENU_MODEL (random));
737
738 for (i = 0; i < 500; i++)
739 {
740 assert_menus_equal (G_MENU_MODEL (random), G_MENU_MODEL (mirror));
741 random_menu_change (menu: random, rand);
742 }
743
744 g_object_unref (object: mirror);
745 g_object_unref (object: random);
746
747 g_rand_free (rand_: rand);
748}
749
750typedef struct
751{
752 GDBusConnection *client_connection;
753 GDBusConnection *server_connection;
754 GDBusServer *server;
755
756 GThread *service_thread;
757 /* Protects server_connection and service_loop. */
758 GMutex service_loop_lock;
759 GCond service_loop_cond;
760
761 GMainLoop *service_loop;
762} PeerConnection;
763
764static gboolean
765on_new_connection (GDBusServer *server,
766 GDBusConnection *connection,
767 gpointer user_data)
768{
769 PeerConnection *data = user_data;
770
771 g_mutex_lock (mutex: &data->service_loop_lock);
772 data->server_connection = g_object_ref (connection);
773 g_cond_broadcast (cond: &data->service_loop_cond);
774 g_mutex_unlock (mutex: &data->service_loop_lock);
775
776 return TRUE;
777}
778
779static void
780create_service_loop (GMainContext *service_context,
781 PeerConnection *data)
782{
783 g_assert (data->service_loop == NULL);
784 g_mutex_lock (mutex: &data->service_loop_lock);
785 data->service_loop = g_main_loop_new (context: service_context, FALSE);
786 g_cond_broadcast (cond: &data->service_loop_cond);
787 g_mutex_unlock (mutex: &data->service_loop_lock);
788}
789
790static void
791teardown_service_loop (PeerConnection *data)
792{
793 g_mutex_lock (mutex: &data->service_loop_lock);
794 g_clear_pointer (&data->service_loop, g_main_loop_unref);
795 g_mutex_unlock (mutex: &data->service_loop_lock);
796}
797
798static void
799await_service_loop (PeerConnection *data)
800{
801 g_mutex_lock (mutex: &data->service_loop_lock);
802 while (data->service_loop == NULL)
803 g_cond_wait (cond: &data->service_loop_cond, mutex: &data->service_loop_lock);
804 g_mutex_unlock (mutex: &data->service_loop_lock);
805}
806
807static void
808await_server_connection (PeerConnection *data)
809{
810 g_mutex_lock (mutex: &data->service_loop_lock);
811 while (data->server_connection == NULL)
812 g_cond_wait (cond: &data->service_loop_cond, mutex: &data->service_loop_lock);
813 g_mutex_unlock (mutex: &data->service_loop_lock);
814}
815
816static gpointer
817service_thread_func (gpointer user_data)
818{
819 PeerConnection *data = user_data;
820 GMainContext *service_context;
821 GError *error;
822 gchar *address;
823 gchar *tmpdir;
824 GDBusServerFlags flags;
825 gchar *guid;
826
827 service_context = g_main_context_new ();
828 g_main_context_push_thread_default (context: service_context);
829
830 tmpdir = NULL;
831 flags = G_DBUS_SERVER_FLAGS_NONE;
832
833#ifdef G_OS_UNIX
834 if (g_unix_socket_address_abstract_names_supported ())
835 address = g_strdup (str: "unix:tmpdir=/tmp/test-dbus-peer");
836 else
837 {
838 tmpdir = g_dir_make_tmp (tmpl: "test-dbus-peer-XXXXXX", NULL);
839 address = g_strdup_printf (format: "unix:tmpdir=%s", tmpdir);
840 }
841#else
842 address = g_strdup ("nonce-tcp:");
843 flags |= G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS;
844#endif
845
846 guid = g_dbus_generate_guid ();
847
848 error = NULL;
849 data->server = g_dbus_server_new_sync (address,
850 flags,
851 guid,
852 NULL,
853 NULL,
854 error: &error);
855 g_assert_no_error (error);
856 g_free (mem: address);
857 g_free (mem: guid);
858
859 g_signal_connect (data->server,
860 "new-connection",
861 G_CALLBACK (on_new_connection),
862 data);
863
864 g_dbus_server_start (server: data->server);
865
866 create_service_loop (service_context, data);
867 g_main_loop_run (loop: data->service_loop);
868
869 g_main_context_pop_thread_default (context: service_context);
870
871 teardown_service_loop (data);
872 g_main_context_unref (context: service_context);
873
874 if (tmpdir)
875 {
876 g_rmdir (filename: tmpdir);
877 g_free (mem: tmpdir);
878 }
879
880 return NULL;
881}
882
883static void
884peer_connection_up (PeerConnection *data)
885{
886 GError *error;
887
888 memset (s: data, c: '\0', n: sizeof (PeerConnection));
889
890 g_mutex_init (mutex: &data->service_loop_lock);
891 g_cond_init (cond: &data->service_loop_cond);
892
893 /* bring up a server - we run the server in a different thread to
894 avoid deadlocks */
895 data->service_thread = g_thread_new (name: "test_dbus_peer",
896 func: service_thread_func,
897 data);
898 await_service_loop (data);
899 g_assert (data->server != NULL);
900
901 /* bring up a connection and accept it */
902 error = NULL;
903 data->client_connection =
904 g_dbus_connection_new_for_address_sync (address: g_dbus_server_get_client_address (server: data->server),
905 flags: G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
906 NULL, /* GDBusAuthObserver */
907 NULL, /* cancellable */
908 error: &error);
909 g_assert_no_error (error);
910 g_assert (data->client_connection != NULL);
911 await_server_connection (data);
912}
913
914static void
915peer_connection_down (PeerConnection *data)
916{
917 g_object_unref (object: data->client_connection);
918 g_object_unref (object: data->server_connection);
919
920 g_dbus_server_stop (server: data->server);
921 g_object_unref (object: data->server);
922
923 g_main_loop_quit (loop: data->service_loop);
924 g_thread_join (thread: data->service_thread);
925
926 g_mutex_clear (mutex: &data->service_loop_lock);
927 g_cond_clear (cond: &data->service_loop_cond);
928}
929
930struct roundtrip_state
931{
932 RandomMenu *random;
933 MirrorMenu *proxy_mirror;
934 GDBusMenuModel *proxy;
935 GMainLoop *loop;
936 GRand *rand;
937 gint success;
938 gint count;
939};
940
941static gboolean
942roundtrip_step (gpointer data)
943{
944 struct roundtrip_state *state = data;
945
946 if (check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy)) &&
947 check_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy_mirror)))
948 {
949 state->success++;
950 state->count = 0;
951
952 if (state->success < 100)
953 random_menu_change (menu: state->random, rand: state->rand);
954 else
955 g_main_loop_quit (loop: state->loop);
956 }
957 else if (state->count == 100)
958 {
959 assert_menus_equal (G_MENU_MODEL (state->random), G_MENU_MODEL (state->proxy));
960 g_assert_not_reached ();
961 }
962 else
963 state->count++;
964
965 return G_SOURCE_CONTINUE;
966}
967
968static void
969do_roundtrip (GDBusConnection *exporter_connection,
970 GDBusConnection *proxy_connection)
971{
972 struct roundtrip_state state;
973 guint export_id;
974 guint id;
975
976 state.rand = g_rand_new_with_seed (seed: g_test_rand_int ());
977
978 state.random = random_menu_new (rand: state.rand, order: 2);
979 export_id = g_dbus_connection_export_menu_model (connection: exporter_connection,
980 object_path: "/",
981 G_MENU_MODEL (state.random),
982 NULL);
983 state.proxy = g_dbus_menu_model_get (connection: proxy_connection,
984 bus_name: g_dbus_connection_get_unique_name (connection: proxy_connection),
985 object_path: "/");
986 state.proxy_mirror = mirror_menu_new (G_MENU_MODEL (state.proxy));
987 state.count = 0;
988 state.success = 0;
989
990 id = g_timeout_add (interval: 10, function: roundtrip_step, data: &state);
991
992 state.loop = g_main_loop_new (NULL, FALSE);
993 g_main_loop_run (loop: state.loop);
994
995 g_main_loop_unref (loop: state.loop);
996 g_source_remove (tag: id);
997 g_object_unref (object: state.proxy);
998 g_dbus_connection_unexport_menu_model (connection: exporter_connection, export_id);
999 g_object_unref (object: state.random);
1000 g_object_unref (object: state.proxy_mirror);
1001 g_rand_free (rand_: state.rand);
1002}
1003
1004static void
1005test_dbus_roundtrip (void)
1006{
1007 GDBusConnection *bus;
1008
1009 bus = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL);
1010 do_roundtrip (exporter_connection: bus, proxy_connection: bus);
1011 g_object_unref (object: bus);
1012}
1013
1014static void
1015test_dbus_peer_roundtrip (void)
1016{
1017#ifdef _GLIB_ADDRESS_SANITIZER
1018 g_test_incomplete ("FIXME: Leaks a GCancellableSource, see glib#2313");
1019 (void) peer_connection_up;
1020 (void) peer_connection_down;
1021#else
1022 PeerConnection peer;
1023
1024 peer_connection_up (data: &peer);
1025 do_roundtrip (exporter_connection: peer.server_connection, proxy_connection: peer.client_connection);
1026 peer_connection_down (data: &peer);
1027#endif
1028}
1029
1030static gint items_changed_count;
1031
1032static void
1033items_changed (GMenuModel *model,
1034 gint position,
1035 gint removed,
1036 gint added,
1037 gpointer data)
1038{
1039 items_changed_count++;
1040}
1041
1042static gboolean
1043stop_loop (gpointer data)
1044{
1045 GMainLoop *loop = data;
1046
1047 g_main_loop_quit (loop);
1048
1049 return G_SOURCE_REMOVE;
1050}
1051
1052static void
1053do_subscriptions (GDBusConnection *exporter_connection,
1054 GDBusConnection *proxy_connection)
1055{
1056 GMenu *menu;
1057 GDBusMenuModel *proxy;
1058 GMainLoop *loop;
1059 GError *error = NULL;
1060 guint export_id;
1061 guint timeout_id;
1062
1063 timeout_id = add_timeout (seconds: 60);
1064 loop = g_main_loop_new (NULL, FALSE);
1065
1066 menu = g_menu_new ();
1067
1068 export_id = g_dbus_connection_export_menu_model (connection: exporter_connection,
1069 object_path: "/",
1070 G_MENU_MODEL (menu),
1071 error: &error);
1072 g_assert_no_error (error);
1073
1074 proxy = g_dbus_menu_model_get (connection: proxy_connection,
1075 bus_name: g_dbus_connection_get_unique_name (connection: proxy_connection),
1076 object_path: "/");
1077 items_changed_count = 0;
1078 g_signal_connect (proxy, "items-changed",
1079 G_CALLBACK (items_changed), NULL);
1080
1081 g_menu_append (menu, label: "item1", NULL);
1082 g_menu_append (menu, label: "item2", NULL);
1083 g_menu_append (menu, label: "item3", NULL);
1084
1085 g_assert_cmpint (items_changed_count, ==, 0);
1086
1087 /* We don't subscribe to change-notification until we look at the items */
1088 g_timeout_add (interval: 100, function: stop_loop, data: loop);
1089 g_main_loop_run (loop);
1090
1091 /* Looking at the items triggers subscription */
1092 g_menu_model_get_n_items (G_MENU_MODEL (proxy));
1093
1094 while (items_changed_count < 1)
1095 g_main_context_iteration (NULL, TRUE);
1096
1097 /* We get all three items in one batch */
1098 g_assert_cmpint (items_changed_count, ==, 1);
1099 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
1100
1101 /* If we wait, we don't get any more */
1102 g_timeout_add (interval: 100, function: stop_loop, data: loop);
1103 g_main_loop_run (loop);
1104 g_assert_cmpint (items_changed_count, ==, 1);
1105 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
1106
1107 /* Now we're subscribed, we get changes individually */
1108 g_menu_append (menu, label: "item4", NULL);
1109 g_menu_append (menu, label: "item5", NULL);
1110 g_menu_append (menu, label: "item6", NULL);
1111 g_menu_remove (menu, position: 0);
1112 g_menu_remove (menu, position: 0);
1113
1114 while (items_changed_count < 6)
1115 g_main_context_iteration (NULL, TRUE);
1116
1117 g_assert_cmpint (items_changed_count, ==, 6);
1118
1119 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 4);
1120
1121 /* After destroying the proxy and waiting a bit, we don't get any more
1122 * items-changed signals */
1123 g_object_unref (object: proxy);
1124
1125 g_timeout_add (interval: 100, function: stop_loop, data: loop);
1126 g_main_loop_run (loop);
1127
1128 g_menu_remove (menu, position: 0);
1129 g_menu_remove (menu, position: 0);
1130
1131 g_timeout_add (interval: 100, function: stop_loop, data: loop);
1132 g_main_loop_run (loop);
1133
1134 g_assert_cmpint (items_changed_count, ==, 6);
1135
1136 g_dbus_connection_unexport_menu_model (connection: exporter_connection, export_id);
1137 g_object_unref (object: menu);
1138
1139 g_main_loop_unref (loop);
1140 cancel_timeout (timeout_id);
1141}
1142
1143static void
1144test_dbus_subscriptions (void)
1145{
1146 GDBusConnection *bus;
1147
1148 bus = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL);
1149 do_subscriptions (exporter_connection: bus, proxy_connection: bus);
1150 g_object_unref (object: bus);
1151}
1152
1153static void
1154test_dbus_peer_subscriptions (void)
1155{
1156#ifdef _GLIB_ADDRESS_SANITIZER
1157 g_test_incomplete ("FIXME: Leaks a GCancellableSource, see glib#2313");
1158 (void) peer_connection_up;
1159 (void) peer_connection_down;
1160#else
1161 PeerConnection peer;
1162
1163 peer_connection_up (data: &peer);
1164 do_subscriptions (exporter_connection: peer.server_connection, proxy_connection: peer.client_connection);
1165 peer_connection_down (data: &peer);
1166#endif
1167}
1168
1169static gpointer
1170do_modify (gpointer data)
1171{
1172 RandomMenu *menu = data;
1173 GRand *rand;
1174 gint i;
1175
1176 rand = g_rand_new_with_seed (seed: g_test_rand_int ());
1177
1178 for (i = 0; i < 10000; i++)
1179 {
1180 random_menu_change (menu, rand);
1181 }
1182
1183 g_rand_free (rand_: rand);
1184
1185 return NULL;
1186}
1187
1188static gpointer
1189do_export (gpointer data)
1190{
1191 GMenuModel *menu = data;
1192 gint i;
1193 GDBusConnection *bus;
1194 gchar *path;
1195 GError *error = NULL;
1196 guint id;
1197
1198 bus = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL);
1199 path = g_strdup_printf (format: "/%p", data);
1200
1201 for (i = 0; i < 10000; i++)
1202 {
1203 id = g_dbus_connection_export_menu_model (connection: bus, object_path: path, menu, error: &error);
1204 g_assert_no_error (error);
1205 g_dbus_connection_unexport_menu_model (connection: bus, export_id: id);
1206 while (g_main_context_iteration (NULL, FALSE));
1207 }
1208
1209 g_free (mem: path);
1210
1211 g_object_unref (object: bus);
1212
1213 return NULL;
1214}
1215
1216static void
1217test_dbus_threaded (void)
1218{
1219 RandomMenu *menu[10];
1220 GThread *call[10];
1221 GThread *export[10];
1222 gint i;
1223
1224 for (i = 0; i < 10; i++)
1225 {
1226 GRand *rand = g_rand_new_with_seed (seed: g_test_rand_int ());
1227 menu[i] = random_menu_new (rand, order: 2);
1228 call[i] = g_thread_new (name: "call", func: do_modify, data: menu[i]);
1229 export[i] = g_thread_new (name: "export", func: do_export, data: menu[i]);
1230 g_rand_free (rand_: rand);
1231 }
1232
1233 for (i = 0; i < 10; i++)
1234 {
1235 g_thread_join (thread: call[i]);
1236 g_thread_join (thread: export[i]);
1237 }
1238
1239 for (i = 0; i < 10; i++)
1240 g_object_unref (object: menu[i]);
1241}
1242
1243static void
1244test_attributes (void)
1245{
1246 GMenu *menu;
1247 GMenuItem *item;
1248 GVariant *v;
1249
1250 menu = g_menu_new ();
1251
1252 item = g_menu_item_new (label: "test", NULL);
1253 g_menu_item_set_attribute_value (menu_item: item, attribute: "boolean", value: g_variant_new_boolean (FALSE));
1254 g_menu_item_set_attribute_value (menu_item: item, attribute: "string", value: g_variant_new_string (string: "bla"));
1255
1256 g_menu_item_set_attribute (menu_item: item, attribute: "double", format_string: "d", 1.5);
1257 v = g_variant_new_parsed (format: "[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
1258 g_menu_item_set_attribute_value (menu_item: item, attribute: "complex", value: v);
1259 g_menu_item_set_attribute_value (menu_item: item, attribute: "test-123", value: g_variant_new_string (string: "test-123"));
1260
1261 g_menu_append_item (menu, item);
1262
1263 g_menu_item_set_attribute (menu_item: item, attribute: "double", format_string: "d", G_PI);
1264
1265 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
1266
1267 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), item_index: 0, attribute: "boolean", NULL);
1268 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
1269 g_variant_unref (value: v);
1270
1271 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), item_index: 0, attribute: "string", NULL);
1272 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1273 g_variant_unref (value: v);
1274
1275 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), item_index: 0, attribute: "double", NULL);
1276 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1277 g_variant_unref (value: v);
1278
1279 v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), item_index: 0, attribute: "complex", NULL);
1280 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1281 g_variant_unref (value: v);
1282
1283 g_menu_remove_all (menu);
1284
1285 g_object_unref (object: menu);
1286 g_object_unref (object: item);
1287}
1288
1289static void
1290test_attribute_iter (void)
1291{
1292 GMenu *menu;
1293 GMenuItem *item;
1294 const gchar *name;
1295 GVariant *v;
1296 GMenuAttributeIter *iter;
1297 GHashTable *found;
1298
1299 menu = g_menu_new ();
1300
1301 item = g_menu_item_new (label: "test", NULL);
1302 g_menu_item_set_attribute_value (menu_item: item, attribute: "boolean", value: g_variant_new_boolean (FALSE));
1303 g_menu_item_set_attribute_value (menu_item: item, attribute: "string", value: g_variant_new_string (string: "bla"));
1304
1305 g_menu_item_set_attribute (menu_item: item, attribute: "double", format_string: "d", 1.5);
1306 v = g_variant_new_parsed (format: "[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
1307 g_menu_item_set_attribute_value (menu_item: item, attribute: "complex", value: v);
1308 g_menu_item_set_attribute_value (menu_item: item, attribute: "test-123", value: g_variant_new_string (string: "test-123"));
1309
1310 g_menu_append_item (menu, item);
1311
1312 g_menu_item_set_attribute (menu_item: item, attribute: "double", format_string: "d", G_PI);
1313
1314 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
1315
1316 found = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, key_destroy_func: g_free, value_destroy_func: (GDestroyNotify)g_variant_unref);
1317
1318 iter = g_menu_model_iterate_item_attributes (G_MENU_MODEL (menu), item_index: 0);
1319 while (g_menu_attribute_iter_get_next (iter, out_name: &name, value: &v))
1320 g_hash_table_insert (hash_table: found, key: g_strdup (str: name), value: v);
1321
1322 g_assert_cmpint (g_hash_table_size (found), ==, 6);
1323
1324 v = g_hash_table_lookup (hash_table: found, key: "label");
1325 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1326
1327 v = g_hash_table_lookup (hash_table: found, key: "boolean");
1328 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
1329
1330 v = g_hash_table_lookup (hash_table: found, key: "string");
1331 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1332
1333 v = g_hash_table_lookup (hash_table: found, key: "double");
1334 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1335
1336 v = g_hash_table_lookup (hash_table: found, key: "complex");
1337 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1338
1339 v = g_hash_table_lookup (hash_table: found, key: "test-123");
1340 g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1341
1342 g_hash_table_unref (hash_table: found);
1343
1344 g_menu_remove_all (menu);
1345
1346 g_object_unref (object: menu);
1347 g_object_unref (object: item);
1348}
1349
1350static void
1351test_links (void)
1352{
1353 GMenu *menu;
1354 GMenuModel *m;
1355 GMenuModel *x;
1356 GMenuItem *item;
1357
1358 m = G_MENU_MODEL (g_menu_new ());
1359 g_menu_append (G_MENU (m), label: "test", NULL);
1360
1361 menu = g_menu_new ();
1362
1363 item = g_menu_item_new (label: "test2", NULL);
1364 g_menu_item_set_link (menu_item: item, link: "submenu", model: m);
1365 g_menu_prepend_item (menu, item);
1366
1367 item = g_menu_item_new (label: "test1", NULL);
1368 g_menu_item_set_link (menu_item: item, link: "section", model: m);
1369 g_menu_insert_item (menu, position: 0, item);
1370
1371 item = g_menu_item_new (label: "test3", NULL);
1372 g_menu_item_set_link (menu_item: item, link: "wallet", model: m);
1373 g_menu_insert_item (menu, position: 1000, item);
1374
1375 item = g_menu_item_new (label: "test4", NULL);
1376 g_menu_item_set_link (menu_item: item, link: "purse", model: m);
1377 g_menu_item_set_link (menu_item: item, link: "purse", NULL);
1378 g_menu_append_item (menu, item);
1379
1380 g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 4);
1381
1382 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), item_index: 0, link: "section");
1383 g_assert (x == m);
1384 g_object_unref (object: x);
1385
1386 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), item_index: 1, link: "submenu");
1387 g_assert (x == m);
1388 g_object_unref (object: x);
1389
1390 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), item_index: 2, link: "wallet");
1391 g_assert (x == m);
1392 g_object_unref (object: x);
1393
1394 x = g_menu_model_get_item_link (G_MENU_MODEL (menu), item_index: 3, link: "purse");
1395 g_assert (x == NULL);
1396
1397 g_object_unref (object: m);
1398 g_object_unref (object: menu);
1399}
1400
1401static void
1402test_mutable (void)
1403{
1404 GMenu *menu;
1405
1406 menu = g_menu_new ();
1407 g_menu_append (menu, label: "test", detailed_action: "test");
1408
1409 g_assert (g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1410 g_menu_freeze (menu);
1411 g_assert (!g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1412
1413 g_object_unref (object: menu);
1414}
1415
1416static void
1417test_convenience (void)
1418{
1419 GMenu *m1, *m2;
1420 GMenu *sub;
1421 GMenuItem *item;
1422
1423 m1 = g_menu_new ();
1424 m2 = g_menu_new ();
1425 sub = g_menu_new ();
1426
1427 g_menu_prepend (menu: m1, label: "label1", detailed_action: "do::something");
1428 g_menu_insert (menu: m2, position: 0, label: "label1", detailed_action: "do::something");
1429
1430 g_menu_append (menu: m1, label: "label2", detailed_action: "do::somethingelse");
1431 g_menu_insert (menu: m2, position: -1, label: "label2", detailed_action: "do::somethingelse");
1432
1433 g_menu_insert_section (menu: m1, position: 10, label: "label3", G_MENU_MODEL (sub));
1434 item = g_menu_item_new_section (label: "label3", G_MENU_MODEL (sub));
1435 g_menu_insert_item (menu: m2, position: 10, item);
1436 g_object_unref (object: item);
1437
1438 g_menu_prepend_section (menu: m1, label: "label4", G_MENU_MODEL (sub));
1439 g_menu_insert_section (menu: m2, position: 0, label: "label4", G_MENU_MODEL (sub));
1440
1441 g_menu_append_section (menu: m1, label: "label5", G_MENU_MODEL (sub));
1442 g_menu_insert_section (menu: m2, position: -1, label: "label5", G_MENU_MODEL (sub));
1443
1444 g_menu_insert_submenu (menu: m1, position: 5, label: "label6", G_MENU_MODEL (sub));
1445 item = g_menu_item_new_submenu (label: "label6", G_MENU_MODEL (sub));
1446 g_menu_insert_item (menu: m2, position: 5, item);
1447 g_object_unref (object: item);
1448
1449 g_menu_prepend_submenu (menu: m1, label: "label7", G_MENU_MODEL (sub));
1450 g_menu_insert_submenu (menu: m2, position: 0, label: "label7", G_MENU_MODEL (sub));
1451
1452 g_menu_append_submenu (menu: m1, label: "label8", G_MENU_MODEL (sub));
1453 g_menu_insert_submenu (menu: m2, position: -1, label: "label8", G_MENU_MODEL (sub));
1454
1455 assert_menus_equal (G_MENU_MODEL (m1), G_MENU_MODEL (m2));
1456
1457 g_object_unref (object: m1);
1458 g_object_unref (object: m2);
1459}
1460
1461static void
1462test_menuitem (void)
1463{
1464 GMenu *menu;
1465 GMenu *submenu;
1466 GMenuItem *item;
1467 GIcon *icon;
1468 gboolean b;
1469 gchar *s;
1470
1471 menu = g_menu_new ();
1472 submenu = g_menu_new ();
1473
1474 item = g_menu_item_new (label: "label", detailed_action: "action");
1475 g_menu_item_set_attribute (menu_item: item, attribute: "attribute", format_string: "b", TRUE);
1476 g_menu_item_set_link (menu_item: item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu));
1477 g_menu_append_item (menu, item);
1478
1479 icon = g_themed_icon_new (iconname: "bla");
1480 g_menu_item_set_icon (menu_item: item, icon);
1481 g_object_unref (object: icon);
1482
1483 g_assert (g_menu_item_get_attribute (item, "attribute", "b", &b));
1484 g_assert (b);
1485
1486 g_menu_item_set_action_and_target (menu_item: item, action: "action", format_string: "(bs)", TRUE, "string");
1487 g_assert (g_menu_item_get_attribute (item, "target", "(bs)", &b, &s));
1488 g_assert (b);
1489 g_assert_cmpstr (s, ==, "string");
1490 g_free (mem: s);
1491
1492 g_object_unref (object: item);
1493
1494 item = g_menu_item_new_from_model (G_MENU_MODEL (menu), item_index: 0);
1495 assert_menuitem_equal (item, G_MENU_MODEL (menu), index: 0);
1496 g_object_unref (object: item);
1497
1498 g_object_unref (object: menu);
1499 g_object_unref (object: submenu);
1500}
1501
1502/* Epilogue {{{1 */
1503int
1504main (int argc, char **argv)
1505{
1506 gboolean ret;
1507
1508 g_test_init (argc: &argc, argv: &argv, NULL);
1509
1510 session_bus_up ();
1511
1512 g_test_add_func (testpath: "/gmenu/equality", test_func: test_equality);
1513 g_test_add_func (testpath: "/gmenu/random", test_func: test_random);
1514 g_test_add_func (testpath: "/gmenu/dbus/roundtrip", test_func: test_dbus_roundtrip);
1515 g_test_add_func (testpath: "/gmenu/dbus/subscriptions", test_func: test_dbus_subscriptions);
1516 g_test_add_func (testpath: "/gmenu/dbus/threaded", test_func: test_dbus_threaded);
1517 g_test_add_func (testpath: "/gmenu/dbus/peer/roundtrip", test_func: test_dbus_peer_roundtrip);
1518 g_test_add_func (testpath: "/gmenu/dbus/peer/subscriptions", test_func: test_dbus_peer_subscriptions);
1519 g_test_add_func (testpath: "/gmenu/attributes", test_func: test_attributes);
1520 g_test_add_func (testpath: "/gmenu/attributes/iterate", test_func: test_attribute_iter);
1521 g_test_add_func (testpath: "/gmenu/links", test_func: test_links);
1522 g_test_add_func (testpath: "/gmenu/mutable", test_func: test_mutable);
1523 g_test_add_func (testpath: "/gmenu/convenience", test_func: test_convenience);
1524 g_test_add_func (testpath: "/gmenu/menuitem", test_func: test_menuitem);
1525
1526 ret = g_test_run ();
1527
1528 session_bus_down ();
1529
1530 return ret;
1531}
1532/* vim:set foldmethod=marker: */
1533

source code of gtk/subprojects/glib/gio/tests/gmenumodel.c