LCOV - code coverage report
Current view: top level - gio/tests - gdbus-subscribe.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 96.8 % 309 299
Test Date: 2024-11-26 05:23:01 Functions: 100.0 % 17 17
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright 2024 Collabora Ltd.
       3                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4                 :             :  */
       5                 :             : 
       6                 :             : #include <gio/gio.h>
       7                 :             : 
       8                 :             : #include "gdbusprivate.h"
       9                 :             : #include "gdbus-tests.h"
      10                 :             : 
      11                 :             : #define NAME_OWNER_CHANGED "NameOwnerChanged"
      12                 :             : 
      13                 :             : /* A signal that each connection emits to indicate that it has finished
      14                 :             :  * emitting other signals */
      15                 :             : #define FINISHED_PATH "/org/gtk/Test/Finished"
      16                 :             : #define FINISHED_INTERFACE "org.gtk.Test.Finished"
      17                 :             : #define FINISHED_SIGNAL "Finished"
      18                 :             : 
      19                 :             : /* A signal emitted during testing */
      20                 :             : #define EXAMPLE_PATH "/org/gtk/GDBus/ExampleInterface"
      21                 :             : #define EXAMPLE_INTERFACE "org.gtk.GDBus.ExampleInterface"
      22                 :             : #define FOO_SIGNAL "Foo"
      23                 :             : 
      24                 :             : #define ALREADY_OWNED_NAME "org.gtk.Test.AlreadyOwned"
      25                 :             : #define OWNED_LATER_NAME "org.gtk.Test.OwnedLater"
      26                 :             : 
      27                 :             : /* Log @s in a debug message. */
      28                 :             : static inline const char *
      29                 :         144 : nonnull (const char *s,
      30                 :             :          const char *if_null)
      31                 :             : {
      32                 :         144 :   return (s == NULL) ? if_null : s;
      33                 :             : }
      34                 :             : 
      35                 :             : typedef enum
      36                 :             : {
      37                 :             :   TEST_CONN_NONE,
      38                 :             :   TEST_CONN_FIRST,
      39                 :             :   /* A connection that subscribes to signals */
      40                 :             :   TEST_CONN_SUBSCRIBER = TEST_CONN_FIRST,
      41                 :             :   /* A mockup of a legitimate service */
      42                 :             :   TEST_CONN_SERVICE,
      43                 :             :   /* A mockup of a second legitimate service */
      44                 :             :   TEST_CONN_SERVICE2,
      45                 :             :   /* A connection that tries to trick @subscriber into processing its signals
      46                 :             :    * as if they came from @service */
      47                 :             :   TEST_CONN_ATTACKER,
      48                 :             :   NUM_TEST_CONNS
      49                 :             : } TestConn;
      50                 :             : 
      51                 :             : static const char * const test_conn_descriptions[NUM_TEST_CONNS] =
      52                 :             : {
      53                 :             :   "(unused)",
      54                 :             :   "subscriber",
      55                 :             :   "service",
      56                 :             :   "service 2",
      57                 :             :   "attacker"
      58                 :             : };
      59                 :             : 
      60                 :             : typedef enum
      61                 :             : {
      62                 :             :   SUBSCRIPTION_MODE_CONN,
      63                 :             :   SUBSCRIPTION_MODE_PROXY,
      64                 :             :   SUBSCRIPTION_MODE_PARALLEL
      65                 :             : } SubscriptionMode;
      66                 :             : 
      67                 :             : typedef struct
      68                 :             : {
      69                 :             :   GDBusProxy *received_by_proxy;
      70                 :             :   TestConn sender;
      71                 :             :   char *path;
      72                 :             :   char *iface;
      73                 :             :   char *member;
      74                 :             :   GVariant *parameters;
      75                 :             :   char *arg0;
      76                 :             :   guint32 step;
      77                 :             : } ReceivedMessage;
      78                 :             : 
      79                 :             : static void
      80                 :          44 : received_message_free (ReceivedMessage *self)
      81                 :             : {
      82                 :             : 
      83                 :          44 :   g_clear_object (&self->received_by_proxy);
      84                 :          44 :   g_free (self->path);
      85                 :          44 :   g_free (self->iface);
      86                 :          44 :   g_free (self->member);
      87                 :          44 :   g_clear_pointer (&self->parameters, g_variant_unref);
      88                 :          44 :   g_free (self->arg0);
      89                 :          44 :   g_free (self);
      90                 :          44 : }
      91                 :             : 
      92                 :             : typedef struct
      93                 :             : {
      94                 :             :   TestConn sender;
      95                 :             :   TestConn unicast_to;
      96                 :             :   const char *path;
      97                 :             :   const char *iface;
      98                 :             :   const char *member;
      99                 :             :   const char *arg0;
     100                 :             :   const char *args;
     101                 :             :   guint received_by_conn;
     102                 :             :   guint received_by_proxy;
     103                 :             : } TestEmitSignal;
     104                 :             : 
     105                 :             : typedef struct
     106                 :             : {
     107                 :             :   const char *string_sender;
     108                 :             :   TestConn unique_sender;
     109                 :             :   const char *path;
     110                 :             :   const char *iface;
     111                 :             :   const char *member;
     112                 :             :   const char *arg0;
     113                 :             :   GDBusSignalFlags flags;
     114                 :             :   gboolean unsubscribe_immediately;
     115                 :             : } TestSubscribe;
     116                 :             : 
     117                 :             : typedef struct
     118                 :             : {
     119                 :             :   const char *name;
     120                 :             :   TestConn owner;
     121                 :             :   guint received_by_conn;
     122                 :             :   guint received_by_proxy;
     123                 :             : } TestOwnName;
     124                 :             : 
     125                 :             : typedef enum
     126                 :             : {
     127                 :             :   TEST_ACTION_NONE = 0,
     128                 :             :   TEST_ACTION_SUBSCRIBE,
     129                 :             :   TEST_ACTION_EMIT_SIGNAL,
     130                 :             :   TEST_ACTION_OWN_NAME,
     131                 :             : } TestAction;
     132                 :             : 
     133                 :             : typedef struct
     134                 :             : {
     135                 :             :   TestAction action;
     136                 :             :   union {
     137                 :             :     TestEmitSignal signal;
     138                 :             :     TestSubscribe subscribe;
     139                 :             :     TestOwnName own_name;
     140                 :             :     guint unsubscribe_undo_step;
     141                 :             :   } u;
     142                 :             : } TestStep;
     143                 :             : 
     144                 :             : /* Arbitrary, extend as necessary to accommodate the longest test */
     145                 :             : #define MAX_TEST_STEPS 10
     146                 :             : 
     147                 :             : typedef struct
     148                 :             : {
     149                 :             :   const char *description;
     150                 :             :   TestStep steps[MAX_TEST_STEPS];
     151                 :             : } TestPlan;
     152                 :             : 
     153                 :             : static const TestPlan plan_simple =
     154                 :             : {
     155                 :             :   .description = "A broadcast is only received after subscribing to it",
     156                 :             :   .steps = {
     157                 :             :     {
     158                 :             :       /* We don't receive a signal if we haven't subscribed yet */
     159                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     160                 :             :       .u.signal = {
     161                 :             :         .sender = TEST_CONN_SERVICE,
     162                 :             :         .path = EXAMPLE_PATH,
     163                 :             :         .iface = EXAMPLE_INTERFACE,
     164                 :             :         .member = FOO_SIGNAL,
     165                 :             :         .received_by_conn = 0,
     166                 :             :         .received_by_proxy = 0
     167                 :             :       },
     168                 :             :     },
     169                 :             :     {
     170                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     171                 :             :       .u.subscribe = {
     172                 :             :         .path = EXAMPLE_PATH,
     173                 :             :         .iface = EXAMPLE_INTERFACE,
     174                 :             :       },
     175                 :             :     },
     176                 :             :     {
     177                 :             :       /* Now it works */
     178                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     179                 :             :       .u.signal = {
     180                 :             :         .sender = TEST_CONN_SERVICE,
     181                 :             :         .path = EXAMPLE_PATH,
     182                 :             :         .iface = EXAMPLE_INTERFACE,
     183                 :             :         .member = FOO_SIGNAL,
     184                 :             :         .received_by_conn = 1,
     185                 :             :         /* The proxy can't be used in this case, because it needs
     186                 :             :          * a bus name to subscribe to */
     187                 :             :         .received_by_proxy = 0
     188                 :             :       },
     189                 :             :     },
     190                 :             :   },
     191                 :             : };
     192                 :             : 
     193                 :             : static const TestPlan plan_broadcast_from_anyone =
     194                 :             : {
     195                 :             :   .description = "A subscription with NULL sender accepts broadcast and unicast",
     196                 :             :   .steps = {
     197                 :             :     {
     198                 :             :       /* Subscriber wants to receive signals from anyone */
     199                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     200                 :             :       .u.subscribe = {
     201                 :             :         .path = EXAMPLE_PATH,
     202                 :             :         .iface = EXAMPLE_INTERFACE,
     203                 :             :       },
     204                 :             :     },
     205                 :             :     {
     206                 :             :       /* First service sends a broadcast */
     207                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     208                 :             :       .u.signal = {
     209                 :             :         .sender = TEST_CONN_SERVICE,
     210                 :             :         .path = EXAMPLE_PATH,
     211                 :             :         .iface = EXAMPLE_INTERFACE,
     212                 :             :         .member = FOO_SIGNAL,
     213                 :             :         .received_by_conn = 1,
     214                 :             :         .received_by_proxy = 0
     215                 :             :       },
     216                 :             :     },
     217                 :             :     {
     218                 :             :       /* Second service also sends a broadcast */
     219                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     220                 :             :       .u.signal = {
     221                 :             :         .sender = TEST_CONN_SERVICE2,
     222                 :             :         .path = EXAMPLE_PATH,
     223                 :             :         .iface = EXAMPLE_INTERFACE,
     224                 :             :         .member = FOO_SIGNAL,
     225                 :             :         .received_by_conn = 1,
     226                 :             :         .received_by_proxy = 0
     227                 :             :       },
     228                 :             :     },
     229                 :             :     {
     230                 :             :       /* First service sends a unicast signal */
     231                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     232                 :             :       .u.signal = {
     233                 :             :         .sender = TEST_CONN_SERVICE,
     234                 :             :         .unicast_to = TEST_CONN_SUBSCRIBER,
     235                 :             :         .path = EXAMPLE_PATH,
     236                 :             :         .iface = EXAMPLE_INTERFACE,
     237                 :             :         .member = FOO_SIGNAL,
     238                 :             :         .received_by_conn = 1,
     239                 :             :         .received_by_proxy = 0
     240                 :             :       },
     241                 :             :     },
     242                 :             :     {
     243                 :             :       /* Second service also sends a unicast signal */
     244                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     245                 :             :       .u.signal = {
     246                 :             :         .sender = TEST_CONN_SERVICE2,
     247                 :             :         .unicast_to = TEST_CONN_SUBSCRIBER,
     248                 :             :         .path = EXAMPLE_PATH,
     249                 :             :         .iface = EXAMPLE_INTERFACE,
     250                 :             :         .member = FOO_SIGNAL,
     251                 :             :         .received_by_conn = 1,
     252                 :             :         .received_by_proxy = 0
     253                 :             :       },
     254                 :             :     },
     255                 :             :   },
     256                 :             : };
     257                 :             : 
     258                 :             : static const TestPlan plan_match_twice =
     259                 :             : {
     260                 :             :   .description = "A message matching more than one subscription is received "
     261                 :             :                  "once per subscription",
     262                 :             :   .steps = {
     263                 :             :     {
     264                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     265                 :             :       .u.subscribe = {
     266                 :             :         .unique_sender = TEST_CONN_SERVICE,
     267                 :             :         .path = EXAMPLE_PATH,
     268                 :             :         .iface = EXAMPLE_INTERFACE,
     269                 :             :       },
     270                 :             :     },
     271                 :             :     {
     272                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     273                 :             :       .u.subscribe = {
     274                 :             :         .path = EXAMPLE_PATH,
     275                 :             :       },
     276                 :             :     },
     277                 :             :     {
     278                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     279                 :             :       .u.subscribe = {
     280                 :             :         .iface = EXAMPLE_INTERFACE,
     281                 :             :       },
     282                 :             :     },
     283                 :             :     {
     284                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     285                 :             :       .u.subscribe = {
     286                 :             :         .unique_sender = TEST_CONN_SERVICE,
     287                 :             :         .path = EXAMPLE_PATH,
     288                 :             :         .iface = EXAMPLE_INTERFACE,
     289                 :             :       },
     290                 :             :     },
     291                 :             :     {
     292                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     293                 :             :       .u.signal = {
     294                 :             :         .sender = TEST_CONN_SERVICE,
     295                 :             :         .path = EXAMPLE_PATH,
     296                 :             :         .iface = EXAMPLE_INTERFACE,
     297                 :             :         .member = FOO_SIGNAL,
     298                 :             :         .received_by_conn = 4,
     299                 :             :         /* Only the first and last work with GDBusProxy */
     300                 :             :         .received_by_proxy = 2
     301                 :             :       },
     302                 :             :     },
     303                 :             :   },
     304                 :             : };
     305                 :             : 
     306                 :             : static const TestPlan plan_limit_by_unique_name =
     307                 :             : {
     308                 :             :   .description = "A subscription via a unique name only accepts messages "
     309                 :             :                  "sent by that same unique name",
     310                 :             :   .steps = {
     311                 :             :     {
     312                 :             :       /* Subscriber wants to receive signals from service */
     313                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     314                 :             :       .u.subscribe = {
     315                 :             :         .unique_sender = TEST_CONN_SERVICE,
     316                 :             :         .path = EXAMPLE_PATH,
     317                 :             :         .iface = EXAMPLE_INTERFACE,
     318                 :             :       },
     319                 :             :     },
     320                 :             :     {
     321                 :             :       /* Attacker wants to trick subscriber into thinking that service
     322                 :             :        * sent a signal */
     323                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     324                 :             :       .u.signal = {
     325                 :             :         .sender = TEST_CONN_ATTACKER,
     326                 :             :         .path = EXAMPLE_PATH,
     327                 :             :         .iface = EXAMPLE_INTERFACE,
     328                 :             :         .member = FOO_SIGNAL,
     329                 :             :         .received_by_conn = 0,
     330                 :             :         .received_by_proxy = 0
     331                 :             :       },
     332                 :             :     },
     333                 :             :     {
     334                 :             :       /* Attacker tries harder, by sending a signal unicast directly to
     335                 :             :        * the subscriber */
     336                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     337                 :             :       .u.signal = {
     338                 :             :         .sender = TEST_CONN_ATTACKER,
     339                 :             :         .unicast_to = TEST_CONN_SUBSCRIBER,
     340                 :             :         .path = EXAMPLE_PATH,
     341                 :             :         .iface = EXAMPLE_INTERFACE,
     342                 :             :         .member = FOO_SIGNAL,
     343                 :             :         .received_by_conn = 0,
     344                 :             :         .received_by_proxy = 0
     345                 :             :       },
     346                 :             :     },
     347                 :             :     {
     348                 :             :       /* When the real service sends a signal, it should still get through */
     349                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     350                 :             :       .u.signal = {
     351                 :             :         .sender = TEST_CONN_SERVICE,
     352                 :             :         .path = EXAMPLE_PATH,
     353                 :             :         .iface = EXAMPLE_INTERFACE,
     354                 :             :         .member = FOO_SIGNAL,
     355                 :             :         .received_by_conn = 1,
     356                 :             :         .received_by_proxy = 1
     357                 :             :       },
     358                 :             :     },
     359                 :             :   },
     360                 :             : };
     361                 :             : 
     362                 :             : static const TestPlan plan_nonexistent_unique_name =
     363                 :             : {
     364                 :             :   .description = "A subscription via a unique name that doesn't exist "
     365                 :             :                  "accepts no messages",
     366                 :             :   .steps = {
     367                 :             :     {
     368                 :             :       /* Subscriber wants to receive signals from service */
     369                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     370                 :             :       .u.subscribe = {
     371                 :             :         /* This relies on the implementation detail that the dbus-daemon
     372                 :             :          * (and presumably other bus implementations) never actually generates
     373                 :             :          * a unique name in this format */
     374                 :             :         .string_sender = ":0.this.had.better.not.exist",
     375                 :             :         .path = EXAMPLE_PATH,
     376                 :             :         .iface = EXAMPLE_INTERFACE,
     377                 :             :       },
     378                 :             :     },
     379                 :             :     {
     380                 :             :       /* Attacker wants to trick subscriber into thinking that service
     381                 :             :        * sent a signal */
     382                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     383                 :             :       .u.signal = {
     384                 :             :         .sender = TEST_CONN_ATTACKER,
     385                 :             :         .path = EXAMPLE_PATH,
     386                 :             :         .iface = EXAMPLE_INTERFACE,
     387                 :             :         .member = FOO_SIGNAL,
     388                 :             :         .received_by_conn = 0,
     389                 :             :         .received_by_proxy = 0
     390                 :             :       },
     391                 :             :     },
     392                 :             :     {
     393                 :             :       /* Attacker tries harder, by sending a signal unicast directly to
     394                 :             :        * the subscriber */
     395                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     396                 :             :       .u.signal = {
     397                 :             :         .sender = TEST_CONN_ATTACKER,
     398                 :             :         .unicast_to = TEST_CONN_SUBSCRIBER,
     399                 :             :         .path = EXAMPLE_PATH,
     400                 :             :         .iface = EXAMPLE_INTERFACE,
     401                 :             :         .member = FOO_SIGNAL,
     402                 :             :         .received_by_conn = 0,
     403                 :             :         .received_by_proxy = 0
     404                 :             :       },
     405                 :             :     },
     406                 :             :   },
     407                 :             : };
     408                 :             : 
     409                 :             : static const TestPlan plan_limit_by_well_known_name =
     410                 :             : {
     411                 :             :   .description = "A subscription via a well-known name only accepts messages "
     412                 :             :                  "sent by the owner of that well-known name",
     413                 :             :   .steps = {
     414                 :             :     {
     415                 :             :       /* Service already owns one name */
     416                 :             :       .action = TEST_ACTION_OWN_NAME,
     417                 :             :       .u.own_name = {
     418                 :             :         .name = ALREADY_OWNED_NAME,
     419                 :             :         .owner = TEST_CONN_SERVICE
     420                 :             :       },
     421                 :             :     },
     422                 :             :     {
     423                 :             :       /* Subscriber wants to receive signals from service */
     424                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     425                 :             :       .u.subscribe = {
     426                 :             :         .string_sender = ALREADY_OWNED_NAME,
     427                 :             :         .path = EXAMPLE_PATH,
     428                 :             :         .iface = EXAMPLE_INTERFACE,
     429                 :             :       },
     430                 :             :     },
     431                 :             :     {
     432                 :             :       /* Subscriber wants to receive signals from service by another name */
     433                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     434                 :             :       .u.subscribe = {
     435                 :             :         .string_sender = OWNED_LATER_NAME,
     436                 :             :         .path = EXAMPLE_PATH,
     437                 :             :         .iface = EXAMPLE_INTERFACE,
     438                 :             :       },
     439                 :             :     },
     440                 :             :     {
     441                 :             :       /* Attacker wants to trick subscriber into thinking that service
     442                 :             :        * sent a signal */
     443                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     444                 :             :       .u.signal = {
     445                 :             :         .sender = TEST_CONN_ATTACKER,
     446                 :             :         .path = EXAMPLE_PATH,
     447                 :             :         .iface = EXAMPLE_INTERFACE,
     448                 :             :         .member = FOO_SIGNAL,
     449                 :             :         .received_by_conn = 0,
     450                 :             :         .received_by_proxy = 0
     451                 :             :       },
     452                 :             :     },
     453                 :             :     {
     454                 :             :       /* Attacker tries harder, by sending a signal unicast directly to
     455                 :             :        * the subscriber */
     456                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     457                 :             :       .u.signal = {
     458                 :             :         .sender = TEST_CONN_ATTACKER,
     459                 :             :         .unicast_to = TEST_CONN_SUBSCRIBER,
     460                 :             :         .path = EXAMPLE_PATH,
     461                 :             :         .iface = EXAMPLE_INTERFACE,
     462                 :             :         .member = FOO_SIGNAL,
     463                 :             :         .received_by_conn = 0,
     464                 :             :         .received_by_proxy = 0
     465                 :             :       },
     466                 :             :     },
     467                 :             :     {
     468                 :             :       /* When the service sends a signal with the name it already owns,
     469                 :             :        * it should get through */
     470                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     471                 :             :       .u.signal = {
     472                 :             :         .sender = TEST_CONN_SERVICE,
     473                 :             :         .path = EXAMPLE_PATH,
     474                 :             :         .iface = EXAMPLE_INTERFACE,
     475                 :             :         .member = FOO_SIGNAL,
     476                 :             :         .received_by_conn = 1,
     477                 :             :         .received_by_proxy = 1
     478                 :             :       },
     479                 :             :     },
     480                 :             :     {
     481                 :             :       /* Service claims another name */
     482                 :             :       .action = TEST_ACTION_OWN_NAME,
     483                 :             :       .u.own_name = {
     484                 :             :         .name = OWNED_LATER_NAME,
     485                 :             :         .owner = TEST_CONN_SERVICE
     486                 :             :       },
     487                 :             :     },
     488                 :             :     {
     489                 :             :       /* Now the subscriber gets this signal twice, once for each
     490                 :             :        * subscription; and similarly each of the two proxies gets this
     491                 :             :        * signal twice */
     492                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     493                 :             :       .u.signal = {
     494                 :             :         .sender = TEST_CONN_SERVICE,
     495                 :             :         .path = EXAMPLE_PATH,
     496                 :             :         .iface = EXAMPLE_INTERFACE,
     497                 :             :         .member = FOO_SIGNAL,
     498                 :             :         .received_by_conn = 2,
     499                 :             :         .received_by_proxy = 2
     500                 :             :       },
     501                 :             :     },
     502                 :             :   },
     503                 :             : };
     504                 :             : 
     505                 :             : static const TestPlan plan_unsubscribe_immediately =
     506                 :             : {
     507                 :             :   .description = "Unsubscribing before GetNameOwner can return doesn't result in a crash",
     508                 :             :   .steps = {
     509                 :             :     {
     510                 :             :       /* Service already owns one name */
     511                 :             :       .action = TEST_ACTION_OWN_NAME,
     512                 :             :       .u.own_name = {
     513                 :             :         .name = ALREADY_OWNED_NAME,
     514                 :             :         .owner = TEST_CONN_SERVICE
     515                 :             :       },
     516                 :             :     },
     517                 :             :     {
     518                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     519                 :             :       .u.subscribe = {
     520                 :             :         .string_sender = ALREADY_OWNED_NAME,
     521                 :             :         .path = EXAMPLE_PATH,
     522                 :             :         .iface = EXAMPLE_INTERFACE,
     523                 :             :         .unsubscribe_immediately = TRUE
     524                 :             :       },
     525                 :             :     },
     526                 :             :     {
     527                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     528                 :             :       .u.signal = {
     529                 :             :         .sender = TEST_CONN_SERVICE,
     530                 :             :         .path = EXAMPLE_PATH,
     531                 :             :         .iface = EXAMPLE_INTERFACE,
     532                 :             :         .member = FOO_SIGNAL,
     533                 :             :         .received_by_conn = 0,
     534                 :             :         /* The proxy can't unsubscribe, except by destroying the proxy
     535                 :             :          * completely, which we don't currently implement in this test */
     536                 :             :         .received_by_proxy = 1
     537                 :             :       },
     538                 :             :     },
     539                 :             :   },
     540                 :             : };
     541                 :             : 
     542                 :             : static const TestPlan plan_limit_to_message_bus =
     543                 :             : {
     544                 :             :   .description = "A subscription to the message bus only accepts messages "
     545                 :             :                  "from the message bus",
     546                 :             :   .steps = {
     547                 :             :     {
     548                 :             :       /* Subscriber wants to receive signals from the message bus itself */
     549                 :             :       .action = TEST_ACTION_SUBSCRIBE,
     550                 :             :       .u.subscribe = {
     551                 :             :         .string_sender = DBUS_SERVICE_DBUS,
     552                 :             :         .path = DBUS_PATH_DBUS,
     553                 :             :         .iface = DBUS_INTERFACE_DBUS,
     554                 :             :       },
     555                 :             :     },
     556                 :             :     {
     557                 :             :       /* Attacker wants to trick subscriber into thinking that the message
     558                 :             :        * bus sent a signal */
     559                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     560                 :             :       .u.signal = {
     561                 :             :         .sender = TEST_CONN_ATTACKER,
     562                 :             :         .path = DBUS_PATH_DBUS,
     563                 :             :         .iface = DBUS_INTERFACE_DBUS,
     564                 :             :         .member = NAME_OWNER_CHANGED,
     565                 :             :         .arg0 = "would I lie to you?",
     566                 :             :         .received_by_conn = 0,
     567                 :             :         .received_by_proxy = 0
     568                 :             :       },
     569                 :             :     },
     570                 :             :     {
     571                 :             :       /* Attacker tries harder, by sending a signal unicast directly to
     572                 :             :        * the subscriber, and using more realistic arguments */
     573                 :             :       .action = TEST_ACTION_EMIT_SIGNAL,
     574                 :             :       .u.signal = {
     575                 :             :         .unicast_to = TEST_CONN_SUBSCRIBER,
     576                 :             :         .sender = TEST_CONN_ATTACKER,
     577                 :             :         .path = DBUS_PATH_DBUS,
     578                 :             :         .iface = DBUS_INTERFACE_DBUS,
     579                 :             :         .member = NAME_OWNER_CHANGED,
     580                 :             :         .args = "('com.example.Name', '', ':1.12')",
     581                 :             :         .received_by_conn = 0,
     582                 :             :         .received_by_proxy = 0
     583                 :             :       },
     584                 :             :     },
     585                 :             :     {
     586                 :             :       /* When the message bus sends a signal (in this case triggered by
     587                 :             :        * owning a name), it should still get through */
     588                 :             :       .action = TEST_ACTION_OWN_NAME,
     589                 :             :       .u.own_name = {
     590                 :             :         .name = OWNED_LATER_NAME,
     591                 :             :         .owner = TEST_CONN_SERVICE,
     592                 :             :         .received_by_conn = 1,
     593                 :             :         .received_by_proxy = 1
     594                 :             :       },
     595                 :             :     },
     596                 :             :   },
     597                 :             : };
     598                 :             : 
     599                 :             : typedef struct
     600                 :             : {
     601                 :             :   const TestPlan *plan;
     602                 :             :   SubscriptionMode mode;
     603                 :             :   GError *error;
     604                 :             :   /* (element-type ReceivedMessage) */
     605                 :             :   GPtrArray *received;
     606                 :             :   /* conns[TEST_CONN_NONE] is unused and remains NULL */
     607                 :             :   GDBusConnection *conns[NUM_TEST_CONNS];
     608                 :             :   /* Proxies on conns[TEST_CONN_SUBSCRIBER] */
     609                 :             :   GPtrArray *proxies;
     610                 :             :   /* unique_names[TEST_CONN_NONE] is unused and remains NULL */
     611                 :             :   const char *unique_names[NUM_TEST_CONNS];
     612                 :             :   /* finished[TEST_CONN_NONE] is unused and remains FALSE */
     613                 :             :   gboolean finished[NUM_TEST_CONNS];
     614                 :             :   /* Remains 0 for any step that is not a subscription */
     615                 :             :   guint subscriptions[MAX_TEST_STEPS];
     616                 :             :   /* Number of times the signal from step n was received */
     617                 :             :   guint received_by_conn[MAX_TEST_STEPS];
     618                 :             :   /* Number of times the signal from step n was received */
     619                 :             :   guint received_by_proxy[MAX_TEST_STEPS];
     620                 :             :   guint finished_subscription;
     621                 :             : } Fixture;
     622                 :             : 
     623                 :             : /* Wait for asynchronous messages from @conn to have been processed
     624                 :             :  * by the message bus, as a sequence point so that we can make
     625                 :             :  * "happens before" and "happens after" assertions relative to this.
     626                 :             :  * The easiest way to achieve this is to call a message bus method that has
     627                 :             :  * no arguments and wait for it to return: because the message bus processes
     628                 :             :  * messages in-order, anything we sent before this must have been processed
     629                 :             :  * by the time this call arrives. */
     630                 :             : static void
     631                 :         117 : connection_wait_for_bus (GDBusConnection *conn)
     632                 :             : {
     633                 :         117 :   GError *error = NULL;
     634                 :             :   GVariant *call_result;
     635                 :             : 
     636                 :         117 :   call_result = g_dbus_connection_call_sync (conn,
     637                 :             :                                              DBUS_SERVICE_DBUS,
     638                 :             :                                              DBUS_PATH_DBUS,
     639                 :             :                                              DBUS_INTERFACE_DBUS,
     640                 :             :                                              "GetId",
     641                 :             :                                              NULL,   /* arguments */
     642                 :             :                                              NULL,   /* result type */
     643                 :             :                                              G_DBUS_CALL_FLAGS_NONE,
     644                 :             :                                              -1,
     645                 :             :                                              NULL,
     646                 :             :                                              &error);
     647                 :         117 :   g_assert_no_error (error);
     648                 :         117 :   g_assert_nonnull (call_result);
     649                 :         117 :   g_variant_unref (call_result);
     650                 :         117 : }
     651                 :             : 
     652                 :             : /*
     653                 :             :  * Called when the subscriber receives a message from any connection
     654                 :             :  * announcing that it has emitted all the signals that it plans to emit.
     655                 :             :  */
     656                 :             : static void
     657                 :          96 : subscriber_finished_cb (GDBusConnection *conn,
     658                 :             :                         const char      *sender_name,
     659                 :             :                         const char      *path,
     660                 :             :                         const char      *iface,
     661                 :             :                         const char      *member,
     662                 :             :                         GVariant        *parameters,
     663                 :             :                         void            *user_data)
     664                 :             : {
     665                 :          96 :   Fixture *f = user_data;
     666                 :          96 :   GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
     667                 :             :   guint i;
     668                 :             : 
     669                 :          96 :   g_assert_true (conn == subscriber);
     670                 :             : 
     671                 :         240 :   for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
     672                 :             :     {
     673                 :         240 :       if (g_str_equal (sender_name, f->unique_names[i]))
     674                 :             :         {
     675                 :          96 :           g_assert_false (f->finished[i]);
     676                 :          96 :           f->finished[i] = TRUE;
     677                 :             : 
     678                 :          96 :           g_test_message ("Received Finished signal from %s %s",
     679                 :          96 :                           test_conn_descriptions[i], sender_name);
     680                 :          96 :           return;
     681                 :             :         }
     682                 :             :     }
     683                 :             : 
     684                 :           0 :   g_error ("Received Finished signal from unknown sender %s", sender_name);
     685                 :             : }
     686                 :             : 
     687                 :             : /*
     688                 :             :  * Called when we receive a signal, either via the GDBusProxy (proxy != NULL)
     689                 :             :  * or via the GDBusConnection (proxy == NULL).
     690                 :             :  */
     691                 :             : static void
     692                 :          44 : fixture_received_signal (Fixture    *f,
     693                 :             :                          GDBusProxy *proxy,
     694                 :             :                          const char *sender_name,
     695                 :             :                          const char *path,
     696                 :             :                          const char *iface,
     697                 :             :                          const char *member,
     698                 :             :                          GVariant   *parameters)
     699                 :             : {
     700                 :             :   guint i;
     701                 :             :   ReceivedMessage *received;
     702                 :             : 
     703                 :             :   /* Ignore the Finished signal if it matches a wildcard subscription */
     704                 :          44 :   if (g_str_equal (member, FINISHED_SIGNAL))
     705                 :           0 :     return;
     706                 :             : 
     707                 :          44 :   received = g_new0 (ReceivedMessage, 1);
     708                 :             : 
     709                 :          44 :   if (proxy != NULL)
     710                 :          16 :     received->received_by_proxy = g_object_ref (proxy);
     711                 :             :   else
     712                 :          28 :     received->received_by_proxy = NULL;
     713                 :             : 
     714                 :          44 :   received->path = g_strdup (path);
     715                 :          44 :   received->iface = g_strdup (iface);
     716                 :          44 :   received->member = g_strdup (member);
     717                 :          44 :   received->parameters = g_variant_ref (parameters);
     718                 :             : 
     719                 :         104 :   for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
     720                 :             :     {
     721                 :         100 :       if (g_str_equal (sender_name, f->unique_names[i]))
     722                 :             :         {
     723                 :          40 :           received->sender = i;
     724                 :          40 :           g_assert_false (f->finished[i]);
     725                 :          40 :           break;
     726                 :             :         }
     727                 :             :     }
     728                 :             : 
     729                 :          44 :   if (g_str_equal (sender_name, DBUS_SERVICE_DBUS))
     730                 :             :     {
     731                 :           4 :       g_test_message ("Signal received from message bus %s",
     732                 :             :                       sender_name);
     733                 :             :     }
     734                 :             :   else
     735                 :             :     {
     736                 :          40 :       g_test_message ("Signal received from %s %s",
     737                 :          40 :                       test_conn_descriptions[received->sender],
     738                 :             :                       sender_name);
     739                 :          40 :       g_assert_cmpint (received->sender, !=, TEST_CONN_NONE);
     740                 :             :     }
     741                 :             : 
     742                 :          44 :   g_test_message ("Signal received from %s %s via %s",
     743                 :          44 :                   test_conn_descriptions[received->sender],
     744                 :             :                   sender_name,
     745                 :             :                   proxy != NULL ? "proxy" : "connection");
     746                 :          44 :   g_test_message ("\tPath: %s", path);
     747                 :          44 :   g_test_message ("\tInterface: %s", iface);
     748                 :          44 :   g_test_message ("\tMember: %s", member);
     749                 :             : 
     750                 :          44 :   if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(su)")))
     751                 :             :     {
     752                 :           0 :       g_variant_get (parameters, "(su)", &received->arg0, &received->step);
     753                 :           0 :       g_test_message ("\tString argument 0: %s", received->arg0);
     754                 :           0 :       g_test_message ("\tSent in step: %u", received->step);
     755                 :             :     }
     756                 :          44 :   else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)")))
     757                 :             :     {
     758                 :          40 :       g_variant_get (parameters, "(uu)", NULL, &received->step);
     759                 :          40 :       g_test_message ("\tArgument 0: (not a string)");
     760                 :          40 :       g_test_message ("\tSent in step: %u", received->step);
     761                 :             :     }
     762                 :           4 :   else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
     763                 :             :     {
     764                 :             :       const char *name;
     765                 :             :       const char *old_owner;
     766                 :             :       const char *new_owner;
     767                 :             : 
     768                 :             :       /* The only signal of this signature that we legitimately receive
     769                 :             :        * during this test is NameOwnerChanged, so just assert that it
     770                 :             :        * is from the message bus and can be matched to a plausible step.
     771                 :             :        * (This is less thorough than the above, and will not work if we
     772                 :             :        * add a test scenario where a name's ownership is repeatedly
     773                 :             :        * changed while watching NameOwnerChanged - so don't do that.) */
     774                 :           4 :       g_assert_cmpstr (sender_name, ==, DBUS_SERVICE_DBUS);
     775                 :           4 :       g_assert_cmpstr (path, ==, DBUS_PATH_DBUS);
     776                 :           4 :       g_assert_cmpstr (iface, ==, DBUS_INTERFACE_DBUS);
     777                 :           4 :       g_assert_cmpstr (member, ==, NAME_OWNER_CHANGED);
     778                 :             : 
     779                 :           4 :       g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner);
     780                 :             : 
     781                 :          16 :       for (i = 0; i < G_N_ELEMENTS (f->plan->steps); i++)
     782                 :             :         {
     783                 :          16 :           const TestStep *step = &f->plan->steps[i];
     784                 :             : 
     785                 :          16 :           if (step->action == TEST_ACTION_OWN_NAME)
     786                 :             :             {
     787                 :           4 :               const TestOwnName *own_name = &step->u.own_name;
     788                 :             : 
     789                 :           4 :               if (g_str_equal (name, own_name->name)
     790                 :           4 :                   && g_str_equal (new_owner, f->unique_names[own_name->owner])
     791                 :           4 :                   && own_name->received_by_conn > 0)
     792                 :             :                 {
     793                 :           4 :                   received->step = i;
     794                 :           4 :                   break;
     795                 :             :                 }
     796                 :             :             }
     797                 :             : 
     798                 :          12 :           if (i >= G_N_ELEMENTS (f->plan->steps))
     799                 :           0 :             g_error ("Could not match message to a test step");
     800                 :             :         }
     801                 :             :     }
     802                 :             :   else
     803                 :             :     {
     804                 :           0 :       g_error ("Unexpected message received");
     805                 :             :     }
     806                 :             : 
     807                 :          44 :   g_ptr_array_add (f->received, g_steal_pointer (&received));
     808                 :             : }
     809                 :             : 
     810                 :             : static void
     811                 :          16 : proxy_signal_cb (GDBusProxy *proxy,
     812                 :             :                  const char *sender_name,
     813                 :             :                  const char *member,
     814                 :             :                  GVariant   *parameters,
     815                 :             :                  void       *user_data)
     816                 :             : {
     817                 :          16 :   Fixture *f = user_data;
     818                 :             : 
     819                 :          16 :   fixture_received_signal (f, proxy, sender_name,
     820                 :          16 :                            g_dbus_proxy_get_object_path (proxy),
     821                 :          16 :                            g_dbus_proxy_get_interface_name (proxy),
     822                 :             :                            member, parameters);
     823                 :          16 : }
     824                 :             : 
     825                 :             : static void
     826                 :          28 : subscribed_signal_cb (GDBusConnection *conn,
     827                 :             :                       const char      *sender_name,
     828                 :             :                       const char      *path,
     829                 :             :                       const char      *iface,
     830                 :             :                       const char      *member,
     831                 :             :                       GVariant        *parameters,
     832                 :             :                       void            *user_data)
     833                 :             : {
     834                 :          28 :   Fixture *f = user_data;
     835                 :          28 :   GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
     836                 :             : 
     837                 :          28 :   g_assert_true (conn == subscriber);
     838                 :             : 
     839                 :          28 :   fixture_received_signal (f, NULL, sender_name, path, iface, member, parameters);
     840                 :          28 : }
     841                 :             : 
     842                 :             : static void
     843                 :          36 : fixture_subscribe (Fixture             *f,
     844                 :             :                    const TestSubscribe *subscribe,
     845                 :             :                    guint                step_number)
     846                 :             : {
     847                 :          36 :   GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
     848                 :             :   const char *sender;
     849                 :             : 
     850                 :          36 :   if (subscribe->string_sender != NULL)
     851                 :             :     {
     852                 :          15 :       sender = subscribe->string_sender;
     853                 :          15 :       g_test_message ("\tSender: %s", sender);
     854                 :             :     }
     855                 :          21 :   else if (subscribe->unique_sender != TEST_CONN_NONE)
     856                 :             :     {
     857                 :           9 :       sender = f->unique_names[subscribe->unique_sender];
     858                 :           9 :       g_test_message ("\tSender: %s %s",
     859                 :           9 :                       test_conn_descriptions[subscribe->unique_sender],
     860                 :             :                       sender);
     861                 :             :     }
     862                 :             :   else
     863                 :             :     {
     864                 :          12 :       sender = NULL;
     865                 :          12 :       g_test_message ("\tSender: (any)");
     866                 :             :     }
     867                 :             : 
     868                 :          36 :   g_test_message ("\tPath: %s", nonnull (subscribe->path, "(any)"));
     869                 :          36 :   g_test_message ("\tInterface: %s",
     870                 :          36 :                   nonnull (subscribe->iface, "(any)"));
     871                 :          36 :   g_test_message ("\tMember: %s",
     872                 :          36 :                   nonnull (subscribe->member, "(any)"));
     873                 :          36 :   g_test_message ("\tString argument 0: %s",
     874                 :          36 :                   nonnull (subscribe->arg0, "(any)"));
     875                 :          36 :   g_test_message ("\tFlags: %x", subscribe->flags);
     876                 :             : 
     877                 :          36 :   if (f->mode != SUBSCRIPTION_MODE_PROXY)
     878                 :             :     {
     879                 :             :       /* CONN or PARALLEL */
     880                 :             :       guint id;
     881                 :             : 
     882                 :          24 :       g_test_message ("\tSubscribing via connection");
     883                 :          48 :       id = g_dbus_connection_signal_subscribe (subscriber,
     884                 :             :                                                sender,
     885                 :          24 :                                                subscribe->iface,
     886                 :          24 :                                                subscribe->member,
     887                 :          24 :                                                subscribe->path,
     888                 :          24 :                                                subscribe->arg0,
     889                 :          24 :                                                subscribe->flags,
     890                 :             :                                                subscribed_signal_cb,
     891                 :             :                                                f, NULL);
     892                 :             : 
     893                 :          24 :       g_assert_cmpuint (id, !=, 0);
     894                 :             : 
     895                 :          24 :       if (subscribe->unsubscribe_immediately)
     896                 :             :         {
     897                 :           2 :           g_test_message ("\tImmediately unsubscribing");
     898                 :           2 :           g_dbus_connection_signal_unsubscribe (subscriber, g_steal_handle_id (&id));
     899                 :             :         }
     900                 :             :       else
     901                 :             :         {
     902                 :          22 :           f->subscriptions[step_number] = id;
     903                 :             :         }
     904                 :             :     }
     905                 :             : 
     906                 :          36 :   if (f->mode != SUBSCRIPTION_MODE_CONN)
     907                 :             :     {
     908                 :             :       /* PROXY or PARALLEL */
     909                 :             : 
     910                 :          24 :       if (sender == NULL)
     911                 :             :         {
     912                 :           8 :           g_test_message ("\tCannot subscribe via proxy: no bus name");
     913                 :             :         }
     914                 :          16 :       else if (subscribe->path == NULL)
     915                 :             :         {
     916                 :           0 :           g_test_message ("\tCannot subscribe via proxy: no path");
     917                 :             :         }
     918                 :          16 :       else if (subscribe->iface == NULL)
     919                 :             :         {
     920                 :           0 :           g_test_message ("\tCannot subscribe via proxy: no interface");
     921                 :             :         }
     922                 :             :       else
     923                 :             :         {
     924                 :             :           GDBusProxy *proxy;
     925                 :             : 
     926                 :          16 :           g_test_message ("\tSubscribing via proxy");
     927                 :          32 :           proxy = g_dbus_proxy_new_sync (subscriber,
     928                 :             :                                          (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
     929                 :             :                                           | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
     930                 :             :                                          NULL,    /* GDBusInterfaceInfo */
     931                 :             :                                          sender,
     932                 :          16 :                                          subscribe->path,
     933                 :          16 :                                          subscribe->iface,
     934                 :             :                                          NULL,    /* GCancellable */
     935                 :             :                                          &f->error);
     936                 :          16 :           g_assert_no_error (f->error);
     937                 :          16 :           g_assert_nonnull (proxy);
     938                 :          16 :           g_signal_connect (proxy, "g-signal", G_CALLBACK (proxy_signal_cb), f);
     939                 :          16 :           g_ptr_array_add (f->proxies, g_steal_pointer (&proxy));
     940                 :             :         }
     941                 :             :     }
     942                 :             : 
     943                 :             :   /* As in setup(), we need to wait for AddMatch to happen. */
     944                 :          36 :   g_test_message ("Waiting for AddMatch to be processed");
     945                 :          36 :   connection_wait_for_bus (subscriber);
     946                 :          36 : }
     947                 :             : 
     948                 :             : static void
     949                 :          57 : fixture_emit_signal (Fixture              *f,
     950                 :             :                      const TestEmitSignal *signal,
     951                 :             :                      guint                 step_number)
     952                 :             : {
     953                 :             :   GVariant *body;
     954                 :             :   const char *destination;
     955                 :             :   gboolean ok;
     956                 :             : 
     957                 :          57 :   g_test_message ("\tSender: %s",
     958                 :          57 :                   test_conn_descriptions[signal->sender]);
     959                 :             : 
     960                 :          57 :   if (signal->unicast_to != TEST_CONN_NONE)
     961                 :             :     {
     962                 :          18 :       destination = f->unique_names[signal->unicast_to];
     963                 :          18 :       g_test_message ("\tDestination: %s %s",
     964                 :          18 :                       test_conn_descriptions[signal->unicast_to],
     965                 :             :                       destination);
     966                 :             :     }
     967                 :             :   else
     968                 :             :     {
     969                 :          39 :       destination = NULL;
     970                 :          39 :       g_test_message ("\tDestination: (broadcast)");
     971                 :             :     }
     972                 :             : 
     973                 :          57 :   g_assert_nonnull (signal->path);
     974                 :          57 :   g_test_message ("\tPath: %s", signal->path);
     975                 :          57 :   g_assert_nonnull (signal->iface);
     976                 :          57 :   g_test_message ("\tInterface: %s", signal->iface);
     977                 :          57 :   g_assert_nonnull (signal->member);
     978                 :          57 :   g_test_message ("\tMember: %s", signal->member);
     979                 :             : 
     980                 :             :   /* If arg0 is non-NULL, put it in the message's argument 0.
     981                 :             :    * Otherwise put something that will not match any arg0.
     982                 :             :    * Either way, put the sequence number in argument 1 so we can
     983                 :             :    * correlate sent messages with received messages later. */
     984                 :          57 :   if (signal->args != NULL)
     985                 :             :     {
     986                 :             :       /* floating */
     987                 :           3 :       body = g_variant_new_parsed (signal->args);
     988                 :           3 :       g_assert_nonnull (body);
     989                 :             :     }
     990                 :          54 :   else if (signal->arg0 != NULL)
     991                 :             :     {
     992                 :           3 :       g_test_message ("\tString argument 0: %s", signal->arg0);
     993                 :           3 :       body = g_variant_new ("(su)", signal->arg0, (guint32) step_number);
     994                 :             :     }
     995                 :             :   else
     996                 :             :     {
     997                 :          51 :       g_test_message ("\tArgument 0: (not a string)");
     998                 :          51 :       body = g_variant_new ("(uu)", (guint32) 0, (guint32) step_number);
     999                 :             :     }
    1000                 :             : 
    1001                 :         114 :   ok = g_dbus_connection_emit_signal (f->conns[signal->sender],
    1002                 :             :                                       destination,
    1003                 :          57 :                                       signal->path,
    1004                 :          57 :                                       signal->iface,
    1005                 :          57 :                                       signal->member,
    1006                 :             :                                       /* steals floating reference */
    1007                 :          57 :                                       g_steal_pointer (&body),
    1008                 :             :                                       &f->error);
    1009                 :          57 :   g_assert_no_error (f->error);
    1010                 :          57 :   g_assert_true (ok);
    1011                 :             : 
    1012                 :             :   /* Emitting the signal is asynchronous, so if we want subsequent steps
    1013                 :             :    * to be guaranteed to happen after the signal from the message bus's
    1014                 :             :    * perspective, we have to do a round-trip to the message bus to sync up. */
    1015                 :          57 :   g_test_message ("Waiting for signal to reach message bus");
    1016                 :          57 :   connection_wait_for_bus (f->conns[signal->sender]);
    1017                 :          57 : }
    1018                 :             : 
    1019                 :             : static void
    1020                 :          12 : fixture_own_name (Fixture *f,
    1021                 :             :                   const TestOwnName *own_name)
    1022                 :             : {
    1023                 :             :   GVariant *call_result;
    1024                 :             :   guint32 flags;
    1025                 :             :   guint32 result_code;
    1026                 :             : 
    1027                 :          12 :   g_test_message ("\tName: %s", own_name->name);
    1028                 :          12 :   g_test_message ("\tOwner: %s",
    1029                 :          12 :                   test_conn_descriptions[own_name->owner]);
    1030                 :             : 
    1031                 :             :   /* For simplicity, we do this via a direct bus call rather than
    1032                 :             :    * using g_bus_own_name_on_connection(). The flags in
    1033                 :             :    * GBusNameOwnerFlags are numerically equal to those in the
    1034                 :             :    * D-Bus wire protocol. */
    1035                 :          12 :   flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE;
    1036                 :          24 :   call_result = g_dbus_connection_call_sync (f->conns[own_name->owner],
    1037                 :             :                                              DBUS_SERVICE_DBUS,
    1038                 :             :                                              DBUS_PATH_DBUS,
    1039                 :             :                                              DBUS_INTERFACE_DBUS,
    1040                 :             :                                              "RequestName",
    1041                 :             :                                              g_variant_new ("(su)",
    1042                 :          12 :                                                            own_name->name,
    1043                 :             :                                                            flags),
    1044                 :             :                                              G_VARIANT_TYPE ("(u)"),
    1045                 :             :                                              G_DBUS_CALL_FLAGS_NONE,
    1046                 :             :                                              -1,
    1047                 :             :                                              NULL,
    1048                 :             :                                              &f->error);
    1049                 :          12 :   g_assert_no_error (f->error);
    1050                 :          12 :   g_assert_nonnull (call_result);
    1051                 :          12 :   g_variant_get (call_result, "(u)", &result_code);
    1052                 :          12 :   g_assert_cmpuint (result_code, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
    1053                 :          12 :   g_variant_unref (call_result);
    1054                 :          12 : }
    1055                 :             : 
    1056                 :             : static void
    1057                 :          24 : fixture_run_plan (Fixture          *f,
    1058                 :             :                   const TestPlan   *plan,
    1059                 :             :                   SubscriptionMode  mode)
    1060                 :             : {
    1061                 :             :   guint i;
    1062                 :             : 
    1063                 :             :   G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->subscriptions));
    1064                 :             :   G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_conn));
    1065                 :             :   G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_proxy));
    1066                 :             : 
    1067                 :          24 :   f->mode = mode;
    1068                 :          24 :   f->plan = plan;
    1069                 :             : 
    1070                 :          24 :   g_test_summary (plan->description);
    1071                 :             : 
    1072                 :         264 :   for (i = 0; i < G_N_ELEMENTS (plan->steps); i++)
    1073                 :             :     {
    1074                 :         240 :       const TestStep *step = &plan->steps[i];
    1075                 :             : 
    1076                 :         240 :       switch (step->action)
    1077                 :             :         {
    1078                 :          36 :           case TEST_ACTION_SUBSCRIBE:
    1079                 :          36 :             g_test_message ("Step %u: adding subscription", i);
    1080                 :          36 :             fixture_subscribe (f, &step->u.subscribe, i);
    1081                 :          36 :             break;
    1082                 :             : 
    1083                 :          57 :           case TEST_ACTION_EMIT_SIGNAL:
    1084                 :          57 :             g_test_message ("Step %u: emitting signal", i);
    1085                 :          57 :             fixture_emit_signal (f, &step->u.signal, i);
    1086                 :          57 :             break;
    1087                 :             : 
    1088                 :          12 :           case TEST_ACTION_OWN_NAME:
    1089                 :          12 :             g_test_message ("Step %u: claiming bus name", i);
    1090                 :          12 :             fixture_own_name (f, &step->u.own_name);
    1091                 :          12 :             break;
    1092                 :             : 
    1093                 :         135 :           case TEST_ACTION_NONE:
    1094                 :             :             /* Padding to fill the rest of the array, do nothing */
    1095                 :         135 :             break;
    1096                 :             : 
    1097                 :           0 :           default:
    1098                 :             :             g_return_if_reached ();
    1099                 :             :         }
    1100                 :             :     }
    1101                 :             : 
    1102                 :             :   /* Now that we have done everything we wanted to do, emit Finished
    1103                 :             :    * from each connection. */
    1104                 :         120 :   for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
    1105                 :             :     {
    1106                 :             :       gboolean ok;
    1107                 :             : 
    1108                 :          96 :       ok = g_dbus_connection_emit_signal (f->conns[i],
    1109                 :             :                                           NULL,
    1110                 :             :                                           FINISHED_PATH,
    1111                 :             :                                           FINISHED_INTERFACE,
    1112                 :             :                                           FINISHED_SIGNAL,
    1113                 :             :                                           NULL,
    1114                 :             :                                           &f->error);
    1115                 :          96 :       g_assert_no_error (f->error);
    1116                 :          96 :       g_assert_true (ok);
    1117                 :             :     }
    1118                 :             : 
    1119                 :             :   /* Wait until we have seen the Finished signal from each sender */
    1120                 :             :   while (TRUE)
    1121                 :          91 :     {
    1122                 :         115 :       gboolean all_finished = TRUE;
    1123                 :             : 
    1124                 :         575 :       for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
    1125                 :         460 :         all_finished = all_finished && f->finished[i];
    1126                 :             : 
    1127                 :         115 :       if (all_finished)
    1128                 :          24 :         break;
    1129                 :             : 
    1130                 :          91 :       g_main_context_iteration (NULL, TRUE);
    1131                 :             :     }
    1132                 :             : 
    1133                 :             :   /* Assert that the correct things happened before each Finished signal */
    1134                 :          68 :   for (i = 0; i < f->received->len; i++)
    1135                 :             :     {
    1136                 :          44 :       const ReceivedMessage *received = g_ptr_array_index (f->received, i);
    1137                 :             : 
    1138                 :          44 :       g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_conn));
    1139                 :          44 :       g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_proxy));
    1140                 :             : 
    1141                 :          44 :       if (received->received_by_proxy != NULL)
    1142                 :          16 :         f->received_by_proxy[received->step] += 1;
    1143                 :             :       else
    1144                 :          28 :         f->received_by_conn[received->step] += 1;
    1145                 :             :     }
    1146                 :             : 
    1147                 :         264 :   for (i = 0; i < G_N_ELEMENTS (plan->steps); i++)
    1148                 :             :     {
    1149                 :         240 :       const TestStep *step = &plan->steps[i];
    1150                 :             : 
    1151                 :         240 :       if (step->action == TEST_ACTION_EMIT_SIGNAL)
    1152                 :             :         {
    1153                 :          57 :           const TestEmitSignal *signal = &plan->steps[i].u.signal;
    1154                 :             : 
    1155                 :          57 :           if (mode != SUBSCRIPTION_MODE_PROXY)
    1156                 :             :             {
    1157                 :          38 :               g_test_message ("Signal from step %u was received %u times by "
    1158                 :             :                               "GDBusConnection, expected %u",
    1159                 :          38 :                               i, f->received_by_conn[i], signal->received_by_conn);
    1160                 :          38 :               g_assert_cmpuint (f->received_by_conn[i], ==, signal->received_by_conn);
    1161                 :             :             }
    1162                 :             :           else
    1163                 :             :             {
    1164                 :          19 :               g_assert_cmpuint (f->received_by_conn[i], ==, 0);
    1165                 :             :             }
    1166                 :             : 
    1167                 :          57 :           if (mode != SUBSCRIPTION_MODE_CONN)
    1168                 :             :             {
    1169                 :          38 :               g_test_message ("Signal from step %u was received %u times by "
    1170                 :             :                               "GDBusProxy, expected %u",
    1171                 :          38 :                               i, f->received_by_proxy[i], signal->received_by_proxy);
    1172                 :          38 :               g_assert_cmpuint (f->received_by_proxy[i], ==, signal->received_by_proxy);
    1173                 :             :             }
    1174                 :             :           else
    1175                 :             :             {
    1176                 :          19 :               g_assert_cmpuint (f->received_by_proxy[i], ==, 0);
    1177                 :             :             }
    1178                 :             :         }
    1179                 :         183 :       else if (step->action == TEST_ACTION_OWN_NAME)
    1180                 :             :         {
    1181                 :          12 :           const TestOwnName *own_name = &plan->steps[i].u.own_name;
    1182                 :             : 
    1183                 :          12 :           if (mode != SUBSCRIPTION_MODE_PROXY)
    1184                 :             :             {
    1185                 :           8 :               g_test_message ("NameOwnerChanged from step %u was received %u "
    1186                 :             :                               "times by GDBusConnection, expected %u",
    1187                 :           8 :                               i, f->received_by_conn[i], own_name->received_by_conn);
    1188                 :           8 :               g_assert_cmpuint (f->received_by_conn[i], ==, own_name->received_by_conn);
    1189                 :             :             }
    1190                 :             :           else
    1191                 :             :             {
    1192                 :           4 :               g_assert_cmpuint (f->received_by_conn[i], ==, 0);
    1193                 :             :             }
    1194                 :             : 
    1195                 :          12 :           if (mode != SUBSCRIPTION_MODE_CONN)
    1196                 :             :             {
    1197                 :           8 :               g_test_message ("NameOwnerChanged from step %u was received %u "
    1198                 :             :                               "times by GDBusProxy, expected %u",
    1199                 :           8 :                               i, f->received_by_proxy[i], own_name->received_by_proxy);
    1200                 :           8 :               g_assert_cmpuint (f->received_by_proxy[i], ==, own_name->received_by_proxy);
    1201                 :             :             }
    1202                 :             :           else
    1203                 :             :             {
    1204                 :           4 :               g_assert_cmpuint (f->received_by_proxy[i], ==, 0);
    1205                 :             :             }
    1206                 :             :         }
    1207                 :             :     }
    1208                 :             : }
    1209                 :             : 
    1210                 :             : static void
    1211                 :          24 : setup (Fixture *f,
    1212                 :             :        G_GNUC_UNUSED const void *context)
    1213                 :             : {
    1214                 :             :   GDBusConnection *subscriber;
    1215                 :             :   guint i;
    1216                 :             : 
    1217                 :          24 :   session_bus_up ();
    1218                 :             : 
    1219                 :          24 :   f->proxies = g_ptr_array_new_full (MAX_TEST_STEPS, g_object_unref);
    1220                 :          24 :   f->received = g_ptr_array_new_full (MAX_TEST_STEPS,
    1221                 :             :                                       (GDestroyNotify) received_message_free);
    1222                 :             : 
    1223                 :         120 :   for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
    1224                 :             :     {
    1225                 :          96 :       f->conns[i] = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &f->error);
    1226                 :          96 :       g_assert_no_error (f->error);
    1227                 :          96 :       g_assert_nonnull (f->conns[i]);
    1228                 :             : 
    1229                 :          96 :       f->unique_names[i] = g_dbus_connection_get_unique_name (f->conns[i]);
    1230                 :          96 :       g_assert_nonnull (f->unique_names[i]);
    1231                 :          96 :       g_test_message ("%s is %s",
    1232                 :          96 :                       test_conn_descriptions[i],
    1233                 :             :                       f->unique_names[i]);
    1234                 :             :     }
    1235                 :             : 
    1236                 :          24 :   subscriber = f->conns[TEST_CONN_SUBSCRIBER];
    1237                 :             : 
    1238                 :             :   /* Used to wait for all connections to finish sending whatever they
    1239                 :             :    * wanted to send */
    1240                 :          24 :   f->finished_subscription = g_dbus_connection_signal_subscribe (subscriber,
    1241                 :             :                                                                  NULL,
    1242                 :             :                                                                  FINISHED_INTERFACE,
    1243                 :             :                                                                  FINISHED_SIGNAL,
    1244                 :             :                                                                  FINISHED_PATH,
    1245                 :             :                                                                  NULL,
    1246                 :             :                                                                  G_DBUS_SIGNAL_FLAGS_NONE,
    1247                 :             :                                                                  subscriber_finished_cb,
    1248                 :             :                                                                  f, NULL);
    1249                 :             :   /* AddMatch is sent asynchronously, so we don't know how
    1250                 :             :    * soon it will be processed. Before emitting signals, we
    1251                 :             :    * need to wait for the message bus to get as far as processing
    1252                 :             :    * AddMatch. */
    1253                 :          24 :   g_test_message ("Waiting for AddMatch to be processed");
    1254                 :          24 :   connection_wait_for_bus (subscriber);
    1255                 :          24 : }
    1256                 :             : 
    1257                 :             : static void
    1258                 :           8 : test_conn_subscribe (Fixture *f,
    1259                 :             :                      const void *context)
    1260                 :             : {
    1261                 :           8 :   fixture_run_plan (f, context, SUBSCRIPTION_MODE_CONN);
    1262                 :           8 : }
    1263                 :             : 
    1264                 :             : static void
    1265                 :           8 : test_proxy_subscribe (Fixture *f,
    1266                 :             :                       const void *context)
    1267                 :             : {
    1268                 :           8 :   fixture_run_plan (f, context, SUBSCRIPTION_MODE_PROXY);
    1269                 :           8 : }
    1270                 :             : 
    1271                 :             : static void
    1272                 :           8 : test_parallel_subscribe (Fixture *f,
    1273                 :             :                          const void *context)
    1274                 :             : {
    1275                 :           8 :   fixture_run_plan (f, context, SUBSCRIPTION_MODE_PARALLEL);
    1276                 :           8 : }
    1277                 :             : 
    1278                 :             : static void
    1279                 :          24 : teardown (Fixture *f,
    1280                 :             :           G_GNUC_UNUSED const void *context)
    1281                 :             : {
    1282                 :          24 :   GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
    1283                 :             :   guint i;
    1284                 :             : 
    1285                 :          24 :   g_ptr_array_unref (f->proxies);
    1286                 :             : 
    1287                 :          24 :   if (f->finished_subscription != 0)
    1288                 :          24 :     g_dbus_connection_signal_unsubscribe (subscriber, g_steal_handle_id (&f->finished_subscription));
    1289                 :             : 
    1290                 :         264 :   for (i = 0; i < G_N_ELEMENTS (f->subscriptions); i++)
    1291                 :             :     {
    1292                 :         240 :       if (f->subscriptions[i] != 0)
    1293                 :          22 :         g_dbus_connection_signal_unsubscribe (subscriber, g_steal_handle_id (&f->subscriptions[i]));
    1294                 :             :     }
    1295                 :             : 
    1296                 :          24 :   g_ptr_array_unref (f->received);
    1297                 :             : 
    1298                 :         120 :   for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
    1299                 :          96 :     g_clear_object (&f->conns[i]);
    1300                 :             : 
    1301                 :          24 :   g_clear_error (&f->error);
    1302                 :             : 
    1303                 :          24 :   session_bus_down ();
    1304                 :          24 : }
    1305                 :             : 
    1306                 :             : int
    1307                 :           1 : main (int   argc,
    1308                 :             :       char *argv[])
    1309                 :             : {
    1310                 :           1 :   g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
    1311                 :             : 
    1312                 :           1 :   g_test_dbus_unset ();
    1313                 :             : 
    1314                 :             : #define ADD_SUBSCRIBE_TEST(name) \
    1315                 :             :   do { \
    1316                 :             :     g_test_add ("/gdbus/subscribe/conn/" #name, \
    1317                 :             :                 Fixture, &plan_ ## name, \
    1318                 :             :                 setup, test_conn_subscribe, teardown); \
    1319                 :             :     g_test_add ("/gdbus/subscribe/proxy/" #name, \
    1320                 :             :                 Fixture, &plan_ ## name, \
    1321                 :             :                 setup, test_proxy_subscribe, teardown); \
    1322                 :             :     g_test_add ("/gdbus/subscribe/parallel/" #name, \
    1323                 :             :                 Fixture, &plan_ ## name, \
    1324                 :             :                 setup, test_parallel_subscribe, teardown); \
    1325                 :             :   } while (0)
    1326                 :             : 
    1327                 :           1 :   ADD_SUBSCRIBE_TEST (simple);
    1328                 :           1 :   ADD_SUBSCRIBE_TEST (broadcast_from_anyone);
    1329                 :           1 :   ADD_SUBSCRIBE_TEST (match_twice);
    1330                 :           1 :   ADD_SUBSCRIBE_TEST (limit_by_unique_name);
    1331                 :           1 :   ADD_SUBSCRIBE_TEST (nonexistent_unique_name);
    1332                 :           1 :   ADD_SUBSCRIBE_TEST (limit_by_well_known_name);
    1333                 :           1 :   ADD_SUBSCRIBE_TEST (limit_to_message_bus);
    1334                 :           1 :   ADD_SUBSCRIBE_TEST (unsubscribe_immediately);
    1335                 :             : 
    1336                 :           1 :   return g_test_run();
    1337                 :             : }
        

Generated by: LCOV version 2.0-1