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 : 12 : indent_string (GString *string,
42 : : gint indent)
43 : : {
44 [ + + ]: 60 : while (indent--)
45 : : g_string_append_c (string, ' ');
46 : 12 : }
47 : :
48 : : static GString *
49 : 12 : g_menu_markup_print_string (GString *string,
50 : : GMenuModel *model,
51 : : gint indent,
52 : : gint tabstop)
53 : : {
54 : 12 : gboolean need_nl = FALSE;
55 : : gint i, n;
56 : :
57 [ + - ]: 12 : if G_UNLIKELY (string == NULL)
58 : 12 : string = g_string_new (NULL);
59 : :
60 : 12 : n = g_menu_model_get_n_items (model);
61 : :
62 [ + + ]: 24 : for (i = 0; i < n; i++)
63 : : {
64 : : GMenuAttributeIter *attr_iter;
65 : : GMenuLinkIter *link_iter;
66 : : GString *contents;
67 : : GString *attrs;
68 : :
69 : 12 : attr_iter = g_menu_model_iterate_item_attributes (model, i);
70 : 12 : link_iter = g_menu_model_iterate_item_links (model, i);
71 : 12 : contents = g_string_new (NULL);
72 : 12 : attrs = g_string_new (NULL);
73 : :
74 [ + + ]: 24 : while (g_menu_attribute_iter_next (attr_iter))
75 : : {
76 : 12 : const char *name = g_menu_attribute_iter_get_name (attr_iter);
77 : 12 : GVariant *value = g_menu_attribute_iter_get_value (attr_iter);
78 : :
79 [ + - ]: 12 : if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
80 : : {
81 : : gchar *str;
82 : 12 : str = g_markup_printf_escaped (" %s='%s'", name, g_variant_get_string (value, NULL));
83 : : g_string_append (attrs, str);
84 : 12 : 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 : 12 : g_variant_unref (value);
103 : : }
104 : 12 : g_object_unref (attr_iter);
105 : :
106 [ - + ]: 12 : 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 : 12 : g_object_unref (link_iter);
127 : :
128 [ - + ]: 12 : 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 [ - + ]: 12 : if (need_nl)
141 : : g_string_append_c (string, '\n');
142 : :
143 : 12 : indent_string (string, indent);
144 : 12 : g_string_append_printf (string, "<item%s/>\n", attrs->str);
145 : 12 : need_nl = FALSE;
146 : : }
147 : :
148 : 12 : g_string_free (contents, TRUE);
149 : 12 : g_string_free (attrs, TRUE);
150 : : }
151 : :
152 : 12 : 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 : 767962 : test_item_new (GHashTable *attributes,
167 : : GHashTable *links)
168 : : {
169 : : TestItem *item;
170 : :
171 : 767962 : item = g_slice_new (TestItem);
172 : 772572 : item->attributes = g_hash_table_ref (attributes);
173 : 773363 : item->links = g_hash_table_ref (links);
174 : :
175 : 773187 : return item;
176 : : }
177 : :
178 : : static void
179 : 773859 : test_item_free (gpointer data)
180 : : {
181 : 773859 : TestItem *item = data;
182 : :
183 : 773859 : g_hash_table_unref (item->attributes);
184 : 773665 : g_hash_table_unref (item->links);
185 : :
186 : 773841 : g_slice_free (TestItem, item);
187 : 774329 : }
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 [ + + + - : 207319 : G_DEFINE_TYPE (RandomMenu, random_menu, G_TYPE_MENU_MODEL)
+ + ]
204 : :
205 : : static gboolean
206 : 3361 : random_menu_is_mutable (GMenuModel *model)
207 : : {
208 : 3361 : return TRUE;
209 : : }
210 : :
211 : : static gint
212 : 216343 : random_menu_get_n_items (GMenuModel *model)
213 : : {
214 : 216343 : RandomMenu *menu = (RandomMenu *) model;
215 : :
216 : 216343 : return g_sequence_get_length (menu->items);
217 : : }
218 : :
219 : : static void
220 : 1258657 : random_menu_get_item_attributes (GMenuModel *model,
221 : : gint position,
222 : : GHashTable **table)
223 : : {
224 : 1258657 : RandomMenu *menu = (RandomMenu *) model;
225 : : TestItem *item;
226 : :
227 : 1258657 : item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
228 : 1258657 : *table = g_hash_table_ref (item->attributes);
229 : 1258657 : }
230 : :
231 : : static void
232 : 753233 : random_menu_get_item_links (GMenuModel *model,
233 : : gint position,
234 : : GHashTable **table)
235 : : {
236 : 753233 : RandomMenu *menu = (RandomMenu *) model;
237 : : TestItem *item;
238 : :
239 : 753233 : item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
240 : 753233 : *table = g_hash_table_ref (item->links);
241 : 753233 : }
242 : :
243 : : static void
244 : 207563 : random_menu_finalize (GObject *object)
245 : : {
246 : 207563 : RandomMenu *menu = (RandomMenu *) object;
247 : :
248 : 207563 : g_sequence_free (menu->items);
249 : :
250 : 207565 : G_OBJECT_CLASS (random_menu_parent_class)
251 : 207200 : ->finalize (object);
252 : 207547 : }
253 : :
254 : : static void
255 : 206857 : random_menu_init (RandomMenu *menu)
256 : : {
257 : 206857 : }
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 : 307537 : 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 : 307537 : n_items = g_sequence_get_length (menu->items);
284 : :
285 : : do
286 : : {
287 : 391160 : position = g_rand_int_range (rand, 0, n_items + 1);
288 : 391104 : removes = g_rand_int_range (rand, 0, n_items - position + 1);
289 : 391091 : adds = g_rand_int_range (rand, 0, MAX_ITEMS - (n_items - removes) + 1);
290 : : }
291 [ + + + + ]: 391036 : while (removes == 0 && adds == 0);
292 : :
293 : 307917 : point = g_sequence_get_iter_at_pos (menu->items, position + removes);
294 : :
295 [ + + ]: 307914 : if (removes)
296 : : {
297 : : GSequenceIter *start;
298 : :
299 : 75204 : start = g_sequence_get_iter_at_pos (menu->items, position);
300 : 75209 : g_sequence_remove_range (start, point);
301 : : }
302 : :
303 [ + + ]: 1068582 : for (i = 0; i < adds; i++)
304 : : {
305 : : const gchar *label;
306 : : GHashTable *links;
307 : : GHashTable *attributes;
308 : :
309 : 760936 : attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
310 : 761934 : links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
311 : :
312 [ + + + + ]: 762369 : if (menu->order > 0 && g_rand_boolean (rand))
313 : 205422 : {
314 : : RandomMenu *child;
315 : : const gchar *subtype;
316 : :
317 : 205104 : child = random_menu_new (rand, menu->order - 1);
318 : :
319 [ + + ]: 205602 : if (g_rand_boolean (rand))
320 : : {
321 : 103319 : subtype = G_MENU_LINK_SECTION;
322 : : /* label some section headers */
323 [ + + ]: 103319 : if (g_rand_boolean (rand))
324 : 51653 : label = "Section";
325 : : else
326 : 51677 : label = NULL;
327 : : }
328 : : else
329 : : {
330 : : /* label all submenus */
331 : 102309 : subtype = G_MENU_LINK_SUBMENU;
332 : 102309 : label = "Submenu";
333 : : }
334 : :
335 : 205570 : g_hash_table_insert (links, g_strdup (subtype), child);
336 : : }
337 : : else
338 : : /* label all terminals */
339 : 557406 : label = "Menu Item";
340 : :
341 [ + + ]: 762828 : if (label)
342 : 1418642 : g_hash_table_insert (attributes, g_strdup ("label"), g_variant_ref_sink (g_variant_new_string (label)));
343 : :
344 : 760452 : g_sequence_insert_before (point, test_item_new (attributes, links));
345 : 760072 : g_hash_table_unref (links);
346 : 760353 : g_hash_table_unref (attributes);
347 : : }
348 : :
349 : 307646 : g_menu_model_items_changed (G_MENU_MODEL (menu), position, removes, adds);
350 : 308305 : }
351 : :
352 : : static RandomMenu *
353 : 207409 : random_menu_new (GRand *rand,
354 : : gint order)
355 : : {
356 : : RandomMenu *menu;
357 : :
358 : 207409 : menu = g_object_new (random_menu_get_type (), NULL);
359 : 207059 : menu->items = g_sequence_new (test_item_free);
360 : 207270 : menu->order = order;
361 : :
362 : 207270 : random_menu_change (menu, rand);
363 : :
364 : 207657 : 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 [ + + + - : 3363 : 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 : 87446 : mirror_menu_get_n_items (GMenuModel *model)
391 : : {
392 : 87446 : MirrorMenu *menu = (MirrorMenu *) model;
393 : :
394 : 87446 : return g_sequence_get_length (menu->items);
395 : : }
396 : :
397 : : static void
398 : 520259 : mirror_menu_get_item_attributes (GMenuModel *model,
399 : : gint position,
400 : : GHashTable **table)
401 : : {
402 : 520259 : MirrorMenu *menu = (MirrorMenu *) model;
403 : : TestItem *item;
404 : :
405 : 520259 : item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
406 : 520259 : *table = g_hash_table_ref (item->attributes);
407 : 520259 : }
408 : :
409 : : static void
410 : 308764 : mirror_menu_get_item_links (GMenuModel *model,
411 : : gint position,
412 : : GHashTable **table)
413 : : {
414 : 308764 : MirrorMenu *menu = (MirrorMenu *) model;
415 : : TestItem *item;
416 : :
417 : 308764 : item = g_sequence_get (g_sequence_get_iter_at_pos (menu->items, position));
418 : 308764 : *table = g_hash_table_ref (item->links);
419 : 308764 : }
420 : :
421 : : static void
422 : 3361 : mirror_menu_finalize (GObject *object)
423 : : {
424 : 3361 : MirrorMenu *menu = (MirrorMenu *) object;
425 : :
426 [ + - ]: 3361 : if (menu->handler_id)
427 : 3361 : g_signal_handler_disconnect (menu->clone_of, menu->handler_id);
428 : :
429 : 3361 : g_sequence_free (menu->items);
430 : 3361 : g_object_unref (menu->clone_of);
431 : :
432 : 3361 : G_OBJECT_CLASS (mirror_menu_parent_class)
433 : 3361 : ->finalize (object);
434 : 3361 : }
435 : :
436 : : static void
437 : 3361 : mirror_menu_init (MirrorMenu *menu)
438 : : {
439 : 3361 : }
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 : 4309 : mirror_menu_changed (GMenuModel *model,
458 : : gint position,
459 : : gint removed,
460 : : gint added,
461 : : gpointer user_data)
462 : : {
463 : 4309 : MirrorMenu *menu = user_data;
464 : : GSequenceIter *point;
465 : : gint i;
466 : :
467 : 4309 : g_assert (model == menu->clone_of);
468 : :
469 : 4309 : point = g_sequence_get_iter_at_pos (menu->items, position + removed);
470 : :
471 [ + + ]: 4309 : if (removed)
472 : : {
473 : : GSequenceIter *start;
474 : :
475 : 525 : start = g_sequence_get_iter_at_pos (menu->items, position);
476 : 525 : g_sequence_remove_range (start, point);
477 : : }
478 : :
479 [ + + ]: 15485 : 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 : 11176 : attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
490 : 11176 : links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
491 : :
492 : 11176 : attr_iter = g_menu_model_iterate_item_attributes (model, i);
493 [ + + ]: 21508 : while (g_menu_attribute_iter_get_next (attr_iter, &name, &value))
494 : : {
495 : 20664 : g_hash_table_insert (attributes, g_strdup (name), value);
496 : : }
497 : 11176 : g_object_unref (attr_iter);
498 : :
499 : 11176 : link_iter = g_menu_model_iterate_item_links (model, i);
500 [ + + ]: 14534 : while (g_menu_link_iter_get_next (link_iter, &name, &child))
501 : : {
502 : 6716 : g_hash_table_insert (links, g_strdup (name), mirror_menu_new (child));
503 : 3358 : g_object_unref (child);
504 : : }
505 : 11176 : g_object_unref (link_iter);
506 : :
507 : 11176 : g_sequence_insert_before (point, test_item_new (attributes, links));
508 : 11176 : g_hash_table_unref (attributes);
509 : 11176 : g_hash_table_unref (links);
510 : : }
511 : :
512 : 4309 : g_menu_model_items_changed (G_MENU_MODEL (menu), position, removed, added);
513 : 4309 : }
514 : :
515 : : static MirrorMenu *
516 : 3361 : mirror_menu_new (GMenuModel *clone_of)
517 : : {
518 : : MirrorMenu *menu;
519 : :
520 : 3361 : menu = g_object_new (mirror_menu_get_type (), NULL);
521 : 3361 : menu->items = g_sequence_new (test_item_free);
522 : 3361 : menu->clone_of = g_object_ref (clone_of);
523 : :
524 [ + - ]: 3361 : if (g_menu_model_is_mutable (clone_of))
525 : 3361 : menu->handler_id = g_signal_connect (clone_of, "items-changed", G_CALLBACK (mirror_menu_changed), menu);
526 : 3361 : mirror_menu_changed (clone_of, 0, 0, g_menu_model_get_n_items (clone_of), menu);
527 : :
528 : 3361 : return menu;
529 : : }
530 : :
531 : : /* check_menus_equal(), assert_menus_equal() {{{1 */
532 : : static gboolean
533 : 152113 : check_menus_equal (GMenuModel *a,
534 : : GMenuModel *b)
535 : : {
536 : 152113 : gboolean equal = TRUE;
537 : : gint a_n, b_n;
538 : : gint i;
539 : :
540 : 152113 : a_n = g_menu_model_get_n_items (a);
541 : 152113 : b_n = g_menu_model_get_n_items (b);
542 : :
543 [ + + ]: 152113 : if (a_n != b_n)
544 : 474 : return FALSE;
545 : :
546 [ + + ]: 608497 : 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 : 456858 : attr_iter = g_menu_model_iterate_item_attributes (a, i);
555 [ + + ]: 895205 : while (g_menu_attribute_iter_get_next (attr_iter, &name, &a_value))
556 : : {
557 : 438347 : b_value = g_menu_model_get_item_attribute_value (b, i, name, NULL);
558 [ + + + + ]: 438347 : equal &= b_value && g_variant_equal (a_value, b_value);
559 [ + + ]: 438347 : if (b_value)
560 : 438314 : g_variant_unref (b_value);
561 : 438347 : g_variant_unref (a_value);
562 : : }
563 : 456858 : g_object_unref (attr_iter);
564 : :
565 : 456858 : attr_iter = g_menu_model_iterate_item_attributes (b, i);
566 [ + + ]: 895215 : while (g_menu_attribute_iter_get_next (attr_iter, &name, &b_value))
567 : : {
568 : 438357 : a_value = g_menu_model_get_item_attribute_value (a, i, name, NULL);
569 [ + + + + ]: 438357 : equal &= a_value && g_variant_equal (a_value, b_value);
570 [ + + ]: 438357 : if (a_value)
571 : 438314 : g_variant_unref (a_value);
572 : 438357 : g_variant_unref (b_value);
573 : : }
574 : 456858 : g_object_unref (attr_iter);
575 : :
576 : 456858 : link_iter = g_menu_model_iterate_item_links (a, i);
577 [ + + ]: 532100 : while (g_menu_link_iter_get_next (link_iter, &name, &a_menu))
578 : : {
579 : 75242 : b_menu = g_menu_model_get_item_link (b, i, name);
580 [ + + + + ]: 75242 : equal &= b_menu && check_menus_equal (a_menu, b_menu);
581 [ + + ]: 75242 : if (b_menu)
582 : 75103 : g_object_unref (b_menu);
583 : 75242 : g_object_unref (a_menu);
584 : : }
585 : 456858 : g_object_unref (link_iter);
586 : :
587 : 456858 : link_iter = g_menu_model_iterate_item_links (b, i);
588 [ + + ]: 532091 : while (g_menu_link_iter_get_next (link_iter, &name, &b_menu))
589 : : {
590 : 75233 : a_menu = g_menu_model_get_item_link (a, i, name);
591 [ + + + + ]: 75233 : equal &= a_menu && check_menus_equal (a_menu, b_menu);
592 [ + + ]: 75233 : if (a_menu)
593 : 75103 : g_object_unref (a_menu);
594 : 75233 : g_object_unref (b_menu);
595 : : }
596 : 456858 : g_object_unref (link_iter);
597 : : }
598 : :
599 : 151639 : 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 [ + + ]: 507 : for (i = 0; i < 500;)
689 : : {
690 : : RandomMenu *a, *b;
691 : :
692 : 506 : a = random_menu_new (randa, TOP_ORDER);
693 : 506 : b = random_menu_new (randb, TOP_ORDER);
694 [ + + ]: 506 : 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 : 6 : as = g_menu_markup_print_string (NULL, G_MENU_MODEL (a), 4, 2);
700 : 6 : bs = g_menu_markup_print_string (NULL, G_MENU_MODEL (b), 4, 2);
701 : 6 : g_assert_cmpstr (as->str, ==, bs->str);
702 : 6 : g_string_free (bs, TRUE);
703 : 6 : 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 : 6 : g_rand_free (randb);
710 : 6 : 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 : 506 : g_object_unref (b);
717 : 506 : 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 : : #ifdef _GLIB_ADDRESS_SANITIZER
1011 : : g_test_incomplete ("FIXME: Leaks a GCancellableSource, see glib#2313");
1012 : : (void) peer_connection_up;
1013 : : (void) peer_connection_down;
1014 : : #else
1015 : : PeerConnection peer;
1016 : :
1017 : 1 : peer_connection_up (&peer);
1018 : 1 : do_roundtrip (peer.server_connection, peer.client_connection);
1019 : 1 : peer_connection_down (&peer);
1020 : : #endif
1021 : 1 : }
1022 : :
1023 : : static gint items_changed_count;
1024 : :
1025 : : static void
1026 : 12 : items_changed (GMenuModel *model,
1027 : : gint position,
1028 : : gint removed,
1029 : : gint added,
1030 : : gpointer data)
1031 : : {
1032 : 12 : items_changed_count++;
1033 : 12 : }
1034 : :
1035 : : static gboolean
1036 : 9 : stop_loop (gpointer data)
1037 : : {
1038 : 9 : GMainLoop *loop = data;
1039 : :
1040 : 9 : g_main_loop_quit (loop);
1041 : :
1042 : 9 : return G_SOURCE_REMOVE;
1043 : : }
1044 : :
1045 : : static void
1046 : 2 : do_subscriptions (GDBusConnection *exporter_connection,
1047 : : GDBusConnection *proxy_connection)
1048 : : {
1049 : : GMenu *menu;
1050 : : GDBusMenuModel *proxy;
1051 : : GMainLoop *loop;
1052 : 2 : GError *error = NULL;
1053 : : guint export_id;
1054 : : guint timeout_id;
1055 : :
1056 : 2 : timeout_id = add_timeout (60);
1057 : 2 : loop = g_main_loop_new (NULL, FALSE);
1058 : :
1059 : 2 : menu = g_menu_new ();
1060 : :
1061 : 2 : export_id = g_dbus_connection_export_menu_model (exporter_connection,
1062 : : "/",
1063 : 2 : G_MENU_MODEL (menu),
1064 : : &error);
1065 : 2 : g_assert_no_error (error);
1066 : :
1067 : 2 : proxy = g_dbus_menu_model_get (proxy_connection,
1068 : : g_dbus_connection_get_unique_name (proxy_connection),
1069 : : "/");
1070 : 2 : items_changed_count = 0;
1071 : 2 : g_signal_connect (proxy, "items-changed",
1072 : : G_CALLBACK (items_changed), NULL);
1073 : :
1074 : 2 : g_menu_append (menu, "item1", NULL);
1075 : 2 : g_menu_append (menu, "item2", NULL);
1076 : 2 : g_menu_append (menu, "item3", NULL);
1077 : :
1078 : 2 : g_assert_cmpint (items_changed_count, ==, 0);
1079 : :
1080 : : /* We don't subscribe to change-notification until we look at the items */
1081 : 2 : g_timeout_add (100, stop_loop, loop);
1082 : 2 : g_main_loop_run (loop);
1083 : :
1084 : : /* Looking at the items triggers subscription */
1085 : 2 : g_menu_model_get_n_items (G_MENU_MODEL (proxy));
1086 : :
1087 [ + + ]: 9 : while (items_changed_count < 1)
1088 : 7 : g_main_context_iteration (NULL, TRUE);
1089 : :
1090 : : /* We get all three items in one batch */
1091 : 2 : g_assert_cmpint (items_changed_count, ==, 1);
1092 : 2 : g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
1093 : :
1094 : : /* If we wait, we don't get any more */
1095 : 2 : g_timeout_add (100, stop_loop, loop);
1096 : 2 : g_main_loop_run (loop);
1097 : 2 : g_assert_cmpint (items_changed_count, ==, 1);
1098 : 2 : g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 3);
1099 : :
1100 : : /* Now we're subscribed, we get changes individually */
1101 : 2 : g_menu_append (menu, "item4", NULL);
1102 : 2 : g_menu_append (menu, "item5", NULL);
1103 : 2 : g_menu_append (menu, "item6", NULL);
1104 : 2 : g_menu_remove (menu, 0);
1105 : 2 : g_menu_remove (menu, 0);
1106 : :
1107 [ + + ]: 12 : while (items_changed_count < 6)
1108 : 10 : g_main_context_iteration (NULL, TRUE);
1109 : :
1110 : 2 : g_assert_cmpint (items_changed_count, ==, 6);
1111 : :
1112 : 2 : g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (proxy)), ==, 4);
1113 : :
1114 : : /* After destroying the proxy and waiting a bit, we don't get any more
1115 : : * items-changed signals */
1116 : 2 : g_object_unref (proxy);
1117 : :
1118 : 2 : g_timeout_add (100, stop_loop, loop);
1119 : 2 : g_main_loop_run (loop);
1120 : :
1121 : 2 : g_menu_remove (menu, 0);
1122 : 2 : g_menu_remove (menu, 0);
1123 : :
1124 : 2 : g_timeout_add (100, stop_loop, loop);
1125 : 2 : g_main_loop_run (loop);
1126 : :
1127 : 2 : g_assert_cmpint (items_changed_count, ==, 6);
1128 : :
1129 : 2 : g_dbus_connection_unexport_menu_model (exporter_connection, export_id);
1130 : 2 : g_object_unref (menu);
1131 : :
1132 : 2 : g_main_loop_unref (loop);
1133 : 2 : cancel_timeout (timeout_id);
1134 : 2 : }
1135 : :
1136 : : static void
1137 : 1 : test_dbus_subscriptions (void)
1138 : : {
1139 : : GDBusConnection *bus;
1140 : :
1141 : 1 : bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1142 : 1 : do_subscriptions (bus, bus);
1143 : 1 : g_object_unref (bus);
1144 : 1 : }
1145 : :
1146 : : static void
1147 : 1 : test_dbus_peer_subscriptions (void)
1148 : : {
1149 : : #ifdef _GLIB_ADDRESS_SANITIZER
1150 : : g_test_incomplete ("FIXME: Leaks a GCancellableSource, see glib#2313");
1151 : : (void) peer_connection_up;
1152 : : (void) peer_connection_down;
1153 : : #else
1154 : : PeerConnection peer;
1155 : :
1156 : 1 : peer_connection_up (&peer);
1157 : 1 : do_subscriptions (peer.server_connection, peer.client_connection);
1158 : 1 : peer_connection_down (&peer);
1159 : : #endif
1160 : 1 : }
1161 : :
1162 : : static gpointer
1163 : 10 : do_modify (gpointer data)
1164 : : {
1165 : 10 : RandomMenu *menu = data;
1166 : : GRand *rand;
1167 : : gint i;
1168 : :
1169 : 10 : rand = g_rand_new_with_seed (g_test_rand_int ());
1170 : :
1171 [ + + ]: 99981 : for (i = 0; i < 10000; i++)
1172 : : {
1173 : 99971 : random_menu_change (menu, rand);
1174 : : }
1175 : :
1176 : 10 : g_rand_free (rand);
1177 : :
1178 : 10 : return NULL;
1179 : : }
1180 : :
1181 : : static gpointer
1182 : 10 : do_export (gpointer data)
1183 : : {
1184 : 10 : GMenuModel *menu = data;
1185 : : gint i;
1186 : : GDBusConnection *bus;
1187 : : gchar *path;
1188 : 10 : GError *error = NULL;
1189 : : guint id;
1190 : :
1191 : 10 : bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1192 : 10 : path = g_strdup_printf ("/%p", data);
1193 : :
1194 [ + + ]: 100010 : for (i = 0; i < 10000; i++)
1195 : : {
1196 : 100000 : id = g_dbus_connection_export_menu_model (bus, path, menu, &error);
1197 : 100000 : g_assert_no_error (error);
1198 : 100000 : g_dbus_connection_unexport_menu_model (bus, id);
1199 [ + + ]: 184375 : while (g_main_context_iteration (NULL, FALSE));
1200 : : }
1201 : :
1202 : 10 : g_free (path);
1203 : :
1204 : 10 : g_object_unref (bus);
1205 : :
1206 : 10 : return NULL;
1207 : : }
1208 : :
1209 : : static void
1210 : 1 : test_dbus_threaded (void)
1211 : : {
1212 : : RandomMenu *menu[10];
1213 : : GThread *call[10];
1214 : : GThread *export[10];
1215 : : gint i;
1216 : :
1217 [ + + ]: 11 : for (i = 0; i < 10; i++)
1218 : : {
1219 : 10 : GRand *rand = g_rand_new_with_seed (g_test_rand_int ());
1220 : 10 : menu[i] = random_menu_new (rand, 2);
1221 : 10 : call[i] = g_thread_new ("call", do_modify, menu[i]);
1222 : 10 : export[i] = g_thread_new ("export", do_export, menu[i]);
1223 : 10 : g_rand_free (rand);
1224 : : }
1225 : :
1226 [ + + ]: 11 : for (i = 0; i < 10; i++)
1227 : : {
1228 : 10 : g_thread_join (call[i]);
1229 : 10 : g_thread_join (export[i]);
1230 : : }
1231 : :
1232 [ + + ]: 11 : for (i = 0; i < 10; i++)
1233 : 10 : g_object_unref (menu[i]);
1234 : 1 : }
1235 : :
1236 : : static void
1237 : 1 : test_attributes (void)
1238 : : {
1239 : : GMenu *menu;
1240 : : GMenuItem *item;
1241 : : GVariant *v;
1242 : :
1243 : 1 : menu = g_menu_new ();
1244 : :
1245 : 1 : item = g_menu_item_new ("test", NULL);
1246 : 1 : g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
1247 : 1 : g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
1248 : :
1249 : 1 : g_menu_item_set_attribute (item, "double", "d", 1.5);
1250 : 1 : v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
1251 : 1 : g_menu_item_set_attribute_value (item, "complex", v);
1252 : 1 : g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
1253 : :
1254 : 1 : g_menu_append_item (menu, item);
1255 : :
1256 : 1 : g_menu_item_set_attribute (item, "double", "d", G_PI);
1257 : :
1258 : 1 : g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
1259 : :
1260 : 1 : v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "boolean", NULL);
1261 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
1262 : 1 : g_variant_unref (v);
1263 : :
1264 : 1 : v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "string", NULL);
1265 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1266 : 1 : g_variant_unref (v);
1267 : :
1268 : 1 : v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "double", NULL);
1269 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1270 : 1 : g_variant_unref (v);
1271 : :
1272 : 1 : v = g_menu_model_get_item_attribute_value (G_MENU_MODEL (menu), 0, "complex", NULL);
1273 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1274 : 1 : g_variant_unref (v);
1275 : :
1276 : 1 : g_menu_remove_all (menu);
1277 : :
1278 : 1 : g_object_unref (menu);
1279 : 1 : g_object_unref (item);
1280 : 1 : }
1281 : :
1282 : : static void
1283 : 1 : test_attribute_iter (void)
1284 : : {
1285 : : GMenu *menu;
1286 : : GMenuItem *item;
1287 : : const gchar *name;
1288 : : GVariant *v;
1289 : : GMenuAttributeIter *iter;
1290 : : GHashTable *found;
1291 : :
1292 : 1 : menu = g_menu_new ();
1293 : :
1294 : 1 : item = g_menu_item_new ("test", NULL);
1295 : 1 : g_menu_item_set_attribute_value (item, "boolean", g_variant_new_boolean (FALSE));
1296 : 1 : g_menu_item_set_attribute_value (item, "string", g_variant_new_string ("bla"));
1297 : :
1298 : 1 : g_menu_item_set_attribute (item, "double", "d", 1.5);
1299 : 1 : v = g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
1300 : 1 : g_menu_item_set_attribute_value (item, "complex", v);
1301 : 1 : g_menu_item_set_attribute_value (item, "test-123", g_variant_new_string ("test-123"));
1302 : :
1303 : 1 : g_menu_append_item (menu, item);
1304 : :
1305 : 1 : g_menu_item_set_attribute (item, "double", "d", G_PI);
1306 : :
1307 : 1 : g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 1);
1308 : :
1309 : 1 : found = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
1310 : :
1311 : 1 : iter = g_menu_model_iterate_item_attributes (G_MENU_MODEL (menu), 0);
1312 [ + + ]: 7 : while (g_menu_attribute_iter_get_next (iter, &name, &v))
1313 : 12 : g_hash_table_insert (found, g_strdup (name), v);
1314 : 1 : g_object_unref (iter);
1315 : :
1316 : 1 : g_assert_cmpint (g_hash_table_size (found), ==, 6);
1317 : :
1318 : 1 : v = g_hash_table_lookup (found, "label");
1319 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1320 : :
1321 : 1 : v = g_hash_table_lookup (found, "boolean");
1322 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN));
1323 : :
1324 : 1 : v = g_hash_table_lookup (found, "string");
1325 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1326 : :
1327 : 1 : v = g_hash_table_lookup (found, "double");
1328 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_DOUBLE));
1329 : :
1330 : 1 : v = g_hash_table_lookup (found, "complex");
1331 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE("a(si)")));
1332 : :
1333 : 1 : v = g_hash_table_lookup (found, "test-123");
1334 : 1 : g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING));
1335 : :
1336 : 1 : g_hash_table_unref (found);
1337 : :
1338 : 1 : g_menu_remove_all (menu);
1339 : :
1340 : 1 : g_object_unref (menu);
1341 : 1 : g_object_unref (item);
1342 : 1 : }
1343 : :
1344 : : static void
1345 : 1 : test_links (void)
1346 : : {
1347 : : GMenu *menu;
1348 : : GMenuModel *m;
1349 : : GMenuModel *x;
1350 : : GMenuItem *item;
1351 : :
1352 : 1 : m = G_MENU_MODEL (g_menu_new ());
1353 : 1 : g_menu_append (G_MENU (m), "test", NULL);
1354 : :
1355 : 1 : menu = g_menu_new ();
1356 : :
1357 : 1 : item = g_menu_item_new ("test2", NULL);
1358 : 1 : g_menu_item_set_link (item, "submenu", m);
1359 : 1 : g_menu_prepend_item (menu, item);
1360 : 1 : g_object_unref (item);
1361 : :
1362 : 1 : item = g_menu_item_new ("test1", NULL);
1363 : 1 : g_menu_item_set_link (item, "section", m);
1364 : 1 : g_menu_insert_item (menu, 0, item);
1365 : 1 : g_object_unref (item);
1366 : :
1367 : 1 : item = g_menu_item_new ("test3", NULL);
1368 : 1 : g_menu_item_set_link (item, "wallet", m);
1369 : 1 : g_menu_insert_item (menu, 1000, item);
1370 : 1 : g_object_unref (item);
1371 : :
1372 : 1 : item = g_menu_item_new ("test4", NULL);
1373 : 1 : g_menu_item_set_link (item, "purse", m);
1374 : 1 : g_menu_item_set_link (item, "purse", NULL);
1375 : 1 : g_menu_append_item (menu, item);
1376 : 1 : g_object_unref (item);
1377 : :
1378 : 1 : g_assert_cmpint (g_menu_model_get_n_items (G_MENU_MODEL (menu)), ==, 4);
1379 : :
1380 : 1 : x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, "section");
1381 : 1 : g_assert (x == m);
1382 : 1 : g_object_unref (x);
1383 : :
1384 : 1 : x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 1, "submenu");
1385 : 1 : g_assert (x == m);
1386 : 1 : g_object_unref (x);
1387 : :
1388 : 1 : x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 2, "wallet");
1389 : 1 : g_assert (x == m);
1390 : 1 : g_object_unref (x);
1391 : :
1392 : 1 : x = g_menu_model_get_item_link (G_MENU_MODEL (menu), 3, "purse");
1393 : 1 : g_assert (x == NULL);
1394 : :
1395 : 1 : g_object_unref (m);
1396 : 1 : g_object_unref (menu);
1397 : 1 : }
1398 : :
1399 : : static void
1400 : 1 : test_mutable (void)
1401 : : {
1402 : : GMenu *menu;
1403 : :
1404 : 1 : menu = g_menu_new ();
1405 : 1 : g_menu_append (menu, "test", "test");
1406 : :
1407 : 1 : g_assert (g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1408 : 1 : g_menu_freeze (menu);
1409 : 1 : g_assert (!g_menu_model_is_mutable (G_MENU_MODEL (menu)));
1410 : :
1411 : 1 : g_object_unref (menu);
1412 : 1 : }
1413 : :
1414 : : static void
1415 : 1 : test_convenience (void)
1416 : : {
1417 : : GMenu *m1, *m2;
1418 : : GMenu *sub;
1419 : : GMenuItem *item;
1420 : :
1421 : 1 : m1 = g_menu_new ();
1422 : 1 : m2 = g_menu_new ();
1423 : 1 : sub = g_menu_new ();
1424 : :
1425 : 1 : g_menu_prepend (m1, "label1", "do::something");
1426 : 1 : g_menu_insert (m2, 0, "label1", "do::something");
1427 : :
1428 : 1 : g_menu_append (m1, "label2", "do::somethingelse");
1429 : 1 : g_menu_insert (m2, -1, "label2", "do::somethingelse");
1430 : :
1431 : 1 : g_menu_insert_section (m1, 10, "label3", G_MENU_MODEL (sub));
1432 : 1 : item = g_menu_item_new_section ("label3", G_MENU_MODEL (sub));
1433 : 1 : g_menu_insert_item (m2, 10, item);
1434 : 1 : g_object_unref (item);
1435 : :
1436 : 1 : g_menu_prepend_section (m1, "label4", G_MENU_MODEL (sub));
1437 : 1 : g_menu_insert_section (m2, 0, "label4", G_MENU_MODEL (sub));
1438 : :
1439 : 1 : g_menu_append_section (m1, "label5", G_MENU_MODEL (sub));
1440 : 1 : g_menu_insert_section (m2, -1, "label5", G_MENU_MODEL (sub));
1441 : :
1442 : 1 : g_menu_insert_submenu (m1, 5, "label6", G_MENU_MODEL (sub));
1443 : 1 : item = g_menu_item_new_submenu ("label6", G_MENU_MODEL (sub));
1444 : 1 : g_menu_insert_item (m2, 5, item);
1445 : 1 : g_object_unref (item);
1446 : :
1447 : 1 : g_menu_prepend_submenu (m1, "label7", G_MENU_MODEL (sub));
1448 : 1 : g_menu_insert_submenu (m2, 0, "label7", G_MENU_MODEL (sub));
1449 : :
1450 : 1 : g_menu_append_submenu (m1, "label8", G_MENU_MODEL (sub));
1451 : 1 : g_menu_insert_submenu (m2, -1, "label8", G_MENU_MODEL (sub));
1452 : :
1453 : 1 : assert_menus_equal (G_MENU_MODEL (m1), G_MENU_MODEL (m2));
1454 : :
1455 : 1 : g_object_unref (m1);
1456 : 1 : g_object_unref (m2);
1457 : 1 : g_object_unref (sub);
1458 : 1 : }
1459 : :
1460 : : static void
1461 : 1 : test_menuitem (void)
1462 : : {
1463 : : GMenu *menu;
1464 : : GMenu *submenu;
1465 : : GMenuItem *item;
1466 : : GIcon *icon;
1467 : : gboolean b;
1468 : : gchar *s;
1469 : :
1470 : 1 : menu = g_menu_new ();
1471 : 1 : submenu = g_menu_new ();
1472 : :
1473 : 1 : item = g_menu_item_new ("label", "action");
1474 : 1 : g_menu_item_set_attribute (item, "attribute", "b", TRUE);
1475 : 1 : g_menu_item_set_link (item, G_MENU_LINK_SUBMENU, G_MENU_MODEL (submenu));
1476 : 1 : g_menu_append_item (menu, item);
1477 : :
1478 : 1 : icon = g_themed_icon_new ("bla");
1479 : 1 : g_menu_item_set_icon (item, icon);
1480 : 1 : g_object_unref (icon);
1481 : :
1482 : 1 : g_assert (g_menu_item_get_attribute (item, "attribute", "b", &b));
1483 : 1 : g_assert (b);
1484 : :
1485 : 1 : g_menu_item_set_action_and_target (item, "action", "(bs)", TRUE, "string");
1486 : 1 : g_assert (g_menu_item_get_attribute (item, "target", "(bs)", &b, &s));
1487 : 1 : g_assert (b);
1488 : 1 : g_assert_cmpstr (s, ==, "string");
1489 : 1 : g_free (s);
1490 : :
1491 : 1 : g_object_unref (item);
1492 : :
1493 : 1 : item = g_menu_item_new_from_model (G_MENU_MODEL (menu), 0);
1494 : 1 : assert_menuitem_equal (item, G_MENU_MODEL (menu), 0);
1495 : 1 : g_object_unref (item);
1496 : :
1497 : 1 : g_object_unref (menu);
1498 : 1 : g_object_unref (submenu);
1499 : 1 : }
1500 : :
1501 : : static GDBusInterfaceInfo *
1502 : 1 : org_gtk_Menus_get_interface (void)
1503 : : {
1504 : : static GDBusInterfaceInfo *interface_info;
1505 : :
1506 [ + - ]: 1 : if (interface_info == NULL)
1507 : : {
1508 : 1 : GError *error = NULL;
1509 : : GDBusNodeInfo *info;
1510 : :
1511 : 1 : info = g_dbus_node_info_new_for_xml ("<node>"
1512 : : " <interface name='org.gtk.Menus'>"
1513 : : " <method name='Start'>"
1514 : : " <arg type='au' name='groups' direction='in'/>"
1515 : : " <arg type='a(uuaa{sv})' name='content' direction='out'/>"
1516 : : " </method>"
1517 : : " <method name='End'>"
1518 : : " <arg type='au' name='groups' direction='in'/>"
1519 : : " </method>"
1520 : : " <signal name='Changed'>"
1521 : : " arg type='a(uuuuaa{sv})' name='changes'/>"
1522 : : " </signal>"
1523 : : " </interface>"
1524 : : "</node>", &error);
1525 [ - + ]: 1 : if (info == NULL)
1526 : 0 : g_error ("%s\n", error->message);
1527 : 1 : interface_info = g_dbus_node_info_lookup_interface (info, "org.gtk.Menus");
1528 : 1 : g_assert (interface_info != NULL);
1529 : 1 : g_dbus_interface_info_ref (interface_info);
1530 : 1 : g_dbus_node_info_unref (info);
1531 : : }
1532 : :
1533 : 1 : return interface_info;
1534 : : }
1535 : :
1536 : : static void
1537 : 1 : g_menu_exporter_method_call (GDBusConnection *connection,
1538 : : const gchar *sender,
1539 : : const gchar *object_path,
1540 : : const gchar *interface_name,
1541 : : const gchar *method_name,
1542 : : GVariant *parameters,
1543 : : GDBusMethodInvocation *invocation,
1544 : : gpointer user_data)
1545 : : {
1546 : : const struct {
1547 : : guint position;
1548 : : guint removed;
1549 : 1 : } data[] = {
1550 : : { -2, 4 },
1551 : : { 0, 3 },
1552 : : { 4, 1 }
1553 : : };
1554 : : gsize i;
1555 : 1 : GError *error = NULL;
1556 : :
1557 : 1 : g_dbus_method_invocation_return_value (invocation, g_variant_new_parsed ("@(a(uuaa{sv})) ([(0, 0, [{ 'label': <'test'> }])],)"));
1558 : :
1559 : : /* invalid signatures */
1560 : 1 : g_dbus_connection_emit_signal (connection, sender, "/", "org.gtk.Menus", "Changed",
1561 : : g_variant_new_parsed ("([(1, 2, 3)],)"), &error);
1562 : 1 : g_assert_no_error (error);
1563 : :
1564 : : /* add an item at an invalid position */
1565 : 1 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*invalid*");
1566 : 1 : g_dbus_connection_emit_signal (connection, sender, "/", "org.gtk.Menus", "Changed",
1567 : : g_variant_new_parsed ("@(a(uuuuaa{sv})) ([(%u, %u, %u, %u, [{ 'label': <'test'> }])],)", 0, 0, 2, 0),
1568 : : &error);
1569 : 1 : g_assert_no_error (error);
1570 : :
1571 [ + + ]: 4 : for (i = 0; i < G_N_ELEMENTS (data); i++)
1572 : : {
1573 : : GVariant *params;
1574 : :
1575 : 3 : g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "*invalid*");
1576 : 3 : params = g_variant_new_parsed ("@(a(uuuuaa{sv})) ([(%u, %u, %u, %u, [])],)", 0, 0, data[i].position, data[i].removed);
1577 : 3 : g_dbus_connection_emit_signal (connection, sender, "/", "org.gtk.Menus", "Changed", params, &error);
1578 : 3 : g_assert_no_error (error);
1579 : : }
1580 : 1 : }
1581 : :
1582 : : static void
1583 : 1 : menu_changed (GMenuModel *menu,
1584 : : gint position,
1585 : : gint removed,
1586 : : gint added,
1587 : : gpointer user_data)
1588 : : {
1589 : 1 : unsigned int *counter = user_data;
1590 : :
1591 : 1 : *counter += 1;
1592 : 1 : }
1593 : :
1594 : : static void
1595 : 1 : test_input_validation (void)
1596 : : {
1597 : 1 : const GDBusInterfaceVTable vtable = {
1598 : : g_menu_exporter_method_call, NULL, NULL, { NULL, }
1599 : : };
1600 : 1 : GError *error = NULL;
1601 : : GDBusConnection *bus;
1602 : : GDBusMenuModel *proxy;
1603 : : guint id;
1604 : : const gchar *bus_name;
1605 : : GMainLoop *loop;
1606 : 1 : unsigned int n_signal_emissions = 0;
1607 : : gulong signal_id;
1608 : :
1609 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/861");
1610 : :
1611 : 1 : bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
1612 : 1 : g_assert_no_error (error);
1613 : :
1614 : 1 : id = g_dbus_connection_register_object (bus, "/", org_gtk_Menus_get_interface (),
1615 : : &vtable, NULL, NULL, &error);
1616 : 1 : g_assert_no_error (error);
1617 : :
1618 : 1 : bus_name = g_dbus_connection_get_unique_name (bus);
1619 : 1 : proxy = g_dbus_menu_model_get (bus, bus_name, "/");
1620 : :
1621 : 1 : signal_id = g_signal_connect (proxy, "items-changed", G_CALLBACK (menu_changed), &n_signal_emissions);
1622 : :
1623 : : /* get over laziness */
1624 : 1 : g_menu_model_get_n_items (G_MENU_MODEL (proxy));
1625 : :
1626 : 1 : loop = g_main_loop_new (NULL, FALSE);
1627 : 1 : g_timeout_add (100, stop_loop, loop);
1628 : 1 : g_main_loop_run (loop);
1629 : :
1630 : : /* "items-changed" should only be emitted for the initial contents of
1631 : : * the menu. Subsequent calls are all invalid.
1632 : : */
1633 : 1 : g_assert_cmpuint (n_signal_emissions, ==, 1);
1634 : :
1635 : 1 : g_test_assert_expected_messages ();
1636 : :
1637 : 1 : g_main_loop_unref (loop);
1638 : 1 : g_dbus_connection_unregister_object (bus, id);
1639 : 1 : g_signal_handler_disconnect (proxy, signal_id);
1640 : 1 : g_object_unref (proxy);
1641 : 1 : g_object_unref (bus);
1642 : 1 : }
1643 : :
1644 : : /* Epilogue {{{1 */
1645 : : int
1646 : 1 : main (int argc, char **argv)
1647 : : {
1648 : : gboolean ret;
1649 : :
1650 : 1 : g_test_init (&argc, &argv, NULL);
1651 : :
1652 : 1 : session_bus_up ();
1653 : :
1654 : 1 : g_test_add_func ("/gmenu/equality", test_equality);
1655 : 1 : g_test_add_func ("/gmenu/random", test_random);
1656 : 1 : g_test_add_func ("/gmenu/dbus/roundtrip", test_dbus_roundtrip);
1657 : 1 : g_test_add_func ("/gmenu/dbus/subscriptions", test_dbus_subscriptions);
1658 : 1 : g_test_add_func ("/gmenu/dbus/threaded", test_dbus_threaded);
1659 : 1 : g_test_add_func ("/gmenu/dbus/peer/roundtrip", test_dbus_peer_roundtrip);
1660 : 1 : g_test_add_func ("/gmenu/dbus/peer/subscriptions", test_dbus_peer_subscriptions);
1661 : 1 : g_test_add_func ("/gmenu/attributes", test_attributes);
1662 : 1 : g_test_add_func ("/gmenu/attributes/iterate", test_attribute_iter);
1663 : 1 : g_test_add_func ("/gmenu/links", test_links);
1664 : 1 : g_test_add_func ("/gmenu/mutable", test_mutable);
1665 : 1 : g_test_add_func ("/gmenu/convenience", test_convenience);
1666 : 1 : g_test_add_func ("/gmenu/menuitem", test_menuitem);
1667 : 1 : g_test_add_func ("/gmenu/input-validation", test_input_validation);
1668 : :
1669 : 1 : ret = g_test_run ();
1670 : :
1671 : 1 : session_bus_down ();
1672 : :
1673 : 1 : return ret;
1674 : : }
1675 : : /* vim:set foldmethod=marker: */
|