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 : : }
|