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