LCOV - code coverage report
Current view: top level - gio/tests - gmenumodel.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 95.3 % 793 756
Test Date: 2024-11-26 05:23:01 Functions: 97.1 % 68 66
Branches: - 0 0

             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: */
        

Generated by: LCOV version 2.0-1