1 #include "config.h"
2
3 #include <telepathy-glib/dbus.h>
4 #include <telepathy-glib/debug.h>
5 #include <telepathy-glib/errors.h>
6 #include <telepathy-glib/interfaces.h>
7 #include <telepathy-glib/intset.h>
8 #include <telepathy-glib/proxy-subclass.h> /* for _invalidated etc. */
9 #include <telepathy-glib/util.h>
10
11 #include "tests/lib/myassert.h"
12 #include "tests/lib/stub-object.h"
13 #include "tests/lib/util.h"
14
15 /* just for convenience, since it's used a lot */
16 #define PTR(ui) GUINT_TO_POINTER(ui)
17
18 /* state tracking */
19 static GMainLoop *mainloop;
20 static TpDBusDaemon *a;
21 static TpDBusDaemon *b;
22 static TpDBusDaemon *c;
23 static TpDBusDaemon *d;
24 static TpDBusDaemon *e;
25 static TpDBusDaemon *f;
26 static TpDBusDaemon *g;
27 static TpDBusDaemon *h;
28 static TpDBusDaemon *i;
29 static TpDBusDaemon *j;
30 static TpDBusDaemon *k;
31 static TpDBusDaemon *z;
32 static TpIntset *method_ok;
33 static TpIntset *method_error;
34 static TpIntset *freed_user_data;
35 static gpointer copy_of_d;
36 static gpointer copy_of_g;
37 static gpointer copy_of_h;
38 static gpointer copy_of_i;
39
40 enum {
41 TEST_A,
42 TEST_B,
43 TEST_C,
44 TEST_D,
45 TEST_E,
46 TEST_F,
47 TEST_G,
48 TEST_H,
49 TEST_I,
50 TEST_J,
51 TEST_K,
52 TEST_Z = 25,
53 N_DAEMONS
54 };
55
56 static void
destroy_user_data(gpointer user_data)57 destroy_user_data (gpointer user_data)
58 {
59 guint which = GPOINTER_TO_UINT (user_data);
60 g_message ("User data %c destroyed", 'A' + which);
61 tp_intset_add (freed_user_data, which);
62 }
63
64 static void
j_stub_destroyed(gpointer data,GObject * stub)65 j_stub_destroyed (gpointer data,
66 GObject *stub)
67 {
68 destroy_user_data (data);
69 }
70
71 static void
k_stub_destroyed(gpointer data,GObject * stub)72 k_stub_destroyed (gpointer data,
73 GObject *stub)
74 {
75 TpProxyPendingCall **p = data;
76
77 tp_proxy_pending_call_cancel (*p);
78 }
79
80 static void
listed_names(TpDBusDaemon * proxy,const gchar ** names,const GError * error,gpointer user_data,GObject * weak_object)81 listed_names (TpDBusDaemon *proxy,
82 const gchar **names,
83 const GError *error,
84 gpointer user_data,
85 GObject *weak_object)
86 {
87 guint which = GPOINTER_TO_UINT (user_data);
88 TpDBusDaemon *want_proxy = NULL;
89 GObject *want_object = NULL;
90
91 if (error == NULL)
92 {
93 g_message ("ListNames() succeeded (first name: %s), according to "
94 "user_data this was on proxy #%d '%c'", *names, which, 'a' + which);
95 tp_intset_add (method_ok, which);
96
97 switch (which)
98 {
99 case TEST_A:
100 want_proxy = a;
101 want_object = (GObject *) z;
102 break;
103 case TEST_C:
104 want_proxy = c;
105 want_object = NULL;
106 break;
107 case TEST_D:
108 want_proxy = copy_of_d;
109 want_object = NULL;
110 break;
111 case TEST_G:
112 want_proxy = copy_of_g;
113 want_object = (GObject *) copy_of_g;
114 break;
115 case TEST_Z:
116 want_proxy = z;
117 want_object = (GObject *) a;
118 break;
119 default:
120 MYASSERT (FALSE, ": %c (%p) method call succeeded, which shouldn't "
121 "happen", 'a' + which, proxy);
122 return;
123 }
124 }
125 else
126 {
127 g_message ("ListNames() failed (%s), according to "
128 "user_data this was on proxy #%d '%c'", error->message,
129 which, 'a' + which);
130 tp_intset_add (method_error, which);
131
132 switch (which)
133 {
134 case TEST_C:
135 want_proxy = c;
136 want_object = NULL;
137 break;
138 case TEST_F:
139 want_proxy = f;
140 want_object = NULL;
141 break;
142 default:
143 MYASSERT (FALSE, ": %c (%p) method call failed, which shouldn't "
144 "happen", 'a' + which, proxy);
145 }
146 }
147
148 MYASSERT (proxy == want_proxy, ": Proxy is %p, expected %p", proxy,
149 want_proxy);
150 MYASSERT (weak_object == want_object, ": Weak object is %p, expected %p",
151 weak_object, want_object);
152
153 if (which == TEST_Z)
154 g_main_loop_quit (mainloop);
155 }
156
157 static void
noc(TpDBusDaemon * proxy,const gchar * name,const gchar * old,const gchar * new,gpointer user_data,GObject * weak_object)158 noc (TpDBusDaemon *proxy,
159 const gchar *name,
160 const gchar *old,
161 const gchar *new,
162 gpointer user_data,
163 GObject *weak_object)
164 {
165 /* do nothing */
166 }
167
168 int
main(int argc,char ** argv)169 main (int argc,
170 char **argv)
171 {
172 GObject *b_stub, *i_stub, *j_stub, *k_stub;
173 GError err = { TP_ERROR, TP_ERROR_INVALID_ARGUMENT, "Because I said so" };
174 TpProxyPendingCall *pc;
175 gpointer tmp_obj;
176
177 tp_tests_abort_after (10);
178 tp_debug_set_flags ("all");
179
180 freed_user_data = tp_intset_sized_new (N_DAEMONS);
181 method_ok = tp_intset_sized_new (N_DAEMONS);
182 method_error = tp_intset_sized_new (N_DAEMONS);
183
184 mainloop = g_main_loop_new (NULL, FALSE);
185
186 /* We use TpDBusDaemon because it's a convenient concrete subclass of
187 * TpProxy. */
188 g_message ("Creating proxies");
189 a = tp_tests_dbus_daemon_dup_or_die ();
190 g_message ("a=%p", a);
191 b = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
192 g_message ("b=%p", b);
193 c = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
194 g_message ("c=%p", c);
195 d = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
196 g_message ("d=%p", d);
197 e = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
198 g_message ("e=%p", e);
199 f = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
200 g_message ("f=%p", f);
201 g = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
202 g_message ("g=%p", g);
203 h = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
204 g_message ("h=%p", h);
205 i = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
206 g_message ("i=%p", i);
207 j = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
208 g_message ("j=%p", j);
209 k = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
210 g_message ("k=%p", k);
211 z = tp_dbus_daemon_new (tp_proxy_get_dbus_connection (a));
212 g_message ("z=%p", z);
213
214 /* a survives */
215 g_message ("Starting call on a");
216 tp_cli_dbus_daemon_call_list_names (a, -1, listed_names, PTR (TEST_A),
217 destroy_user_data, (GObject *) z);
218 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_A), "");
219 MYASSERT (!tp_intset_is_member (method_ok, TEST_A), "");
220 MYASSERT (!tp_intset_is_member (method_error, TEST_A), "");
221
222 /* b gets its pending call cancelled because the weak object is
223 * destroyed */
224 b_stub = tp_tests_object_new_static_class (tp_tests_stub_object_get_type (),
225 NULL);
226 g_message ("Starting call on b");
227 tp_cli_dbus_daemon_call_list_names (b, -1, listed_names, PTR (TEST_B),
228 destroy_user_data, b_stub);
229 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_B), "");
230 g_object_unref (b_stub);
231 MYASSERT (!tp_intset_is_member (method_ok, TEST_B), "");
232 MYASSERT (!tp_intset_is_member (method_error, TEST_B), "");
233
234 /* c is explicitly invalidated for an application-specific reason,
235 * but its call still proceeds */
236 g_message ("Starting call on c");
237 tp_cli_dbus_daemon_call_list_names (c, -1, listed_names, PTR (TEST_C),
238 destroy_user_data, NULL);
239 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_C), "");
240 g_message ("Forcibly invalidating c");
241 tp_proxy_invalidate ((TpProxy *) c, &err);
242 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_C), "");
243 MYASSERT (!tp_intset_is_member (method_ok, TEST_C), "");
244 MYASSERT (!tp_intset_is_member (method_error, TEST_C), "");
245
246 /* d gets unreferenced, but survives long enough for the call to complete
247 * successfully later, because the pending call holds a reference */
248 g_message ("Starting call on d");
249 tp_cli_dbus_daemon_call_list_names (d, -1, listed_names, PTR (TEST_D),
250 destroy_user_data, NULL);
251 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_D), "");
252 g_message ("Unreferencing d");
253 copy_of_d = d;
254 g_object_add_weak_pointer (copy_of_d, ©_of_d);
255 g_object_unref (d);
256 d = NULL;
257 MYASSERT (copy_of_d != NULL, "");
258 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_D), "");
259 MYASSERT (!tp_intset_is_member (method_ok, TEST_D), "");
260 MYASSERT (!tp_intset_is_member (method_error, TEST_D), "");
261
262 /* e gets its method call cancelled explicitly */
263 g_message ("Starting call on e");
264 pc = tp_cli_dbus_daemon_call_list_names (e, -1, listed_names, PTR (TEST_E),
265 destroy_user_data, NULL);
266 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_E), "");
267 g_message ("Cancelling call on e");
268 tp_proxy_pending_call_cancel (pc);
269 MYASSERT (!tp_intset_is_member (method_ok, TEST_E), "");
270 MYASSERT (!tp_intset_is_member (method_error, TEST_E), "");
271
272 /* f's method call fails with an error, because it's implicitly
273 * invalidated by its DBusGProxy being destroyed.
274 *
275 * Note that this test case exploits implementation details of dbus-glib.
276 * If it stops working after a dbus-glib upgrade, that's probably why. */
277 g_message ("Starting call on f");
278 tp_cli_dbus_daemon_call_list_names (f, -1, listed_names, PTR (TEST_F),
279 destroy_user_data, NULL);
280 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_F), "");
281 g_message ("Forcibly disposing f's DBusGProxy to simulate name owner loss");
282 tmp_obj = tp_proxy_borrow_interface_by_id ((TpProxy *) f,
283 TP_IFACE_QUARK_DBUS_DAEMON, NULL);
284 MYASSERT (tmp_obj != NULL, "");
285 g_object_run_dispose (tmp_obj);
286 /* the callback will be queued (to avoid reentrancy), so we don't get it
287 * until the main loop runs */
288 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_F), "");
289 MYASSERT (!tp_intset_is_member (method_ok, TEST_F), "");
290 MYASSERT (!tp_intset_is_member (method_error, TEST_F), "");
291
292 /* g gets unreferenced, but survives long enough for the call to complete
293 * successfully later, because the pending call holds a reference;
294 * however, unlike case D, here the pending call weakly references the
295 * proxy. This is never necessary, but is an interesting corner case that
296 * should be tested. */
297 g_message ("Starting call on g");
298 tp_cli_dbus_daemon_call_list_names (g, -1, listed_names, PTR (TEST_G),
299 destroy_user_data, (GObject *) g);
300 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_G), "");
301 g_message ("Unreferencing g");
302 copy_of_g = g;
303 g_object_add_weak_pointer (copy_of_g, ©_of_g);
304 g_object_unref (g);
305 g = NULL;
306 MYASSERT (copy_of_g != NULL, "");
307 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_G), "");
308 MYASSERT (!tp_intset_is_member (method_ok, TEST_G), "");
309 MYASSERT (!tp_intset_is_member (method_error, TEST_G), "");
310
311 /* h gets unreferenced, *and* the call is cancelled (regression test for
312 * fd.o #14576) */
313 g_message ("Starting call on h");
314 pc = tp_cli_dbus_daemon_call_list_names (h, -1, listed_names, PTR (TEST_H),
315 destroy_user_data, NULL);
316 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_H), "");
317 g_message ("Unreferencing h");
318 copy_of_h = h;
319 g_object_add_weak_pointer (copy_of_h, ©_of_h);
320 g_object_unref (h);
321 h = NULL;
322 MYASSERT (copy_of_h != NULL, "");
323 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_H), "");
324 MYASSERT (!tp_intset_is_member (method_ok, TEST_H), "");
325 MYASSERT (!tp_intset_is_member (method_error, TEST_H), "");
326 g_message ("Cancelling call on h");
327 tp_proxy_pending_call_cancel (pc);
328 MYASSERT (!tp_intset_is_member (method_ok, TEST_H), "");
329 MYASSERT (!tp_intset_is_member (method_error, TEST_H), "");
330
331 /* i gets its pending call cancelled because i_stub is
332 * destroyed, *and* the pending call holds the last reference to it,
333 * *and* there is a signal connection
334 * (used to reproduce fd.o #14750 - see case h in test-disconnection.c
335 * for the minimal regression test) */
336 i_stub = tp_tests_object_new_static_class (tp_tests_stub_object_get_type (),
337 NULL);
338 tp_cli_dbus_daemon_connect_to_name_owner_changed (i, noc, PTR (TEST_I),
339 NULL, i_stub, NULL);
340 g_message ("Starting call on i");
341 tp_cli_dbus_daemon_call_list_names (i, -1, listed_names, PTR (TEST_I),
342 destroy_user_data, i_stub);
343 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_I), "");
344 tp_cli_dbus_daemon_connect_to_name_owner_changed (i, noc, PTR (TEST_I),
345 NULL, i_stub, NULL);
346 g_message ("Unreferencing i");
347 copy_of_i = i;
348 g_object_add_weak_pointer (copy_of_i, ©_of_i);
349 g_object_unref (i);
350 i = NULL;
351 MYASSERT (copy_of_i != NULL, "");
352 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_I), "");
353 MYASSERT (!tp_intset_is_member (method_ok, TEST_I), "");
354 MYASSERT (!tp_intset_is_member (method_error, TEST_I), "");
355 g_object_unref (i_stub);
356 MYASSERT (!tp_intset_is_member (method_ok, TEST_I), "");
357 MYASSERT (!tp_intset_is_member (method_error, TEST_I), "");
358
359 /* j gets its pending call cancelled explicitly, and j_stub is
360 * destroyed in response (related to fd.o #14750) */
361 j_stub = tp_tests_object_new_static_class (tp_tests_stub_object_get_type (),
362 NULL);
363 g_object_weak_ref (j_stub, j_stub_destroyed, PTR (TEST_J));
364 g_message ("Starting call on j");
365 pc = tp_cli_dbus_daemon_call_list_names (j, -1, listed_names, j_stub,
366 g_object_unref, j_stub);
367 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_J), "");
368 g_message ("Cancelling call on j");
369 tp_proxy_pending_call_cancel (pc);
370 MYASSERT (!tp_intset_is_member (method_ok, TEST_J), "");
371 MYASSERT (!tp_intset_is_member (method_error, TEST_J), "");
372
373 /* k gets its pending call cancelled explicitly because its weak object
374 * is destroyed, meaning there are simultaneously two reasons for it
375 * to become cancelled (equivalent to fd.o#14750, but for pending calls
376 * rather than signal connections) */
377 k_stub = tp_tests_object_new_static_class (tp_tests_stub_object_get_type (),
378 NULL);
379 g_message ("Starting call on k");
380 g_object_weak_ref (k_stub, k_stub_destroyed, &pc);
381 tp_cli_dbus_daemon_call_list_names (k, -1, listed_names, PTR (TEST_K),
382 destroy_user_data, k_stub);
383 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_K), "");
384 MYASSERT (!tp_intset_is_member (method_ok, TEST_K), "");
385 MYASSERT (!tp_intset_is_member (method_error, TEST_K), "");
386 g_object_unref (k_stub);
387 MYASSERT (!tp_intset_is_member (method_ok, TEST_K), "");
388 MYASSERT (!tp_intset_is_member (method_error, TEST_K), "");
389
390 /* z survives too; we assume that method calls succeed in order,
391 * so when z has had its reply, we can stop the main loop */
392 g_message ("Starting call on z");
393 tp_cli_dbus_daemon_call_list_names (z, -1, listed_names, PTR (TEST_Z),
394 destroy_user_data, (GObject *) a);
395 MYASSERT (!tp_intset_is_member (freed_user_data, TEST_Z), "");
396 MYASSERT (!tp_intset_is_member (method_ok, TEST_Z), "");
397 MYASSERT (!tp_intset_is_member (method_error, TEST_Z), "");
398
399 g_message ("Running main loop");
400 g_main_loop_run (mainloop);
401 g_main_loop_unref (mainloop);
402
403 /* now that the calls have been delivered, d will finally have gone away */
404 MYASSERT (tp_intset_is_member (freed_user_data, TEST_D), "");
405 MYASSERT (tp_intset_is_member (method_ok, TEST_D), "");
406 MYASSERT (!tp_intset_is_member (method_error, TEST_D), "");
407 MYASSERT (copy_of_d == NULL, "");
408
409 /* ... and g too */
410 MYASSERT (tp_intset_is_member (freed_user_data, TEST_G), "");
411 MYASSERT (tp_intset_is_member (method_ok, TEST_G), "");
412 MYASSERT (!tp_intset_is_member (method_error, TEST_G), "");
413 MYASSERT (copy_of_g == NULL, "");
414
415 /* also, F will have been invalidated */
416 MYASSERT (tp_intset_is_member (freed_user_data, TEST_F), "");
417 MYASSERT (!tp_intset_is_member (method_ok, TEST_F), "");
418 MYASSERT (tp_intset_is_member (method_error, TEST_F), "");
419
420 /* Now that its call has been cancelled, h will have gone away. Likewise
421 * for i */
422 MYASSERT (copy_of_h == NULL, "");
423 MYASSERT (copy_of_i == NULL, "");
424
425 /* User data for all the cancelled calls has also gone away */
426 MYASSERT (tp_intset_is_member (freed_user_data, TEST_B), "");
427 MYASSERT (tp_intset_is_member (freed_user_data, TEST_E), "");
428 MYASSERT (tp_intset_is_member (freed_user_data, TEST_H), "");
429 MYASSERT (tp_intset_is_member (freed_user_data, TEST_I), "");
430 MYASSERT (tp_intset_is_member (freed_user_data, TEST_J), "");
431 MYASSERT (tp_intset_is_member (freed_user_data, TEST_K), "");
432
433 /* the calls have been delivered to A, C and Z by now */
434 MYASSERT (tp_intset_is_member (freed_user_data, TEST_A), "");
435 MYASSERT (tp_intset_is_member (method_ok, TEST_A), "");
436 MYASSERT (!tp_intset_is_member (method_error, TEST_A), "");
437
438 MYASSERT (tp_intset_is_member (freed_user_data, TEST_C), "");
439 MYASSERT (tp_intset_is_member (method_ok, TEST_C), "");
440 MYASSERT (!tp_intset_is_member (method_error, TEST_C), "");
441
442 MYASSERT (tp_intset_is_member (freed_user_data, TEST_Z), "");
443 MYASSERT (tp_intset_is_member (method_ok, TEST_Z), "");
444 MYASSERT (!tp_intset_is_member (method_error, TEST_Z), "");
445
446 g_message ("Dereferencing remaining proxies");
447 g_object_unref (a);
448 a = NULL;
449 g_object_unref (b);
450 b = NULL;
451 g_object_unref (c);
452 c = NULL;
453 MYASSERT (d == NULL, "");
454 g_object_unref (e);
455 e = NULL;
456 g_object_unref (f);
457 f = NULL;
458 MYASSERT (g == NULL, "");
459 MYASSERT (h == NULL, "");
460 MYASSERT (i == NULL, "");
461 g_object_unref (j);
462 j = NULL;
463 g_object_unref (k);
464 k = NULL;
465 g_object_unref (z);
466 z = NULL;
467
468 /* we should already have checked each of these at least once, but just to
469 * make sure we have a systematic test that all user data is freed... */
470 MYASSERT (tp_intset_is_member (freed_user_data, TEST_A), "");
471 MYASSERT (tp_intset_is_member (freed_user_data, TEST_B), "");
472 MYASSERT (tp_intset_is_member (freed_user_data, TEST_C), "");
473 MYASSERT (tp_intset_is_member (freed_user_data, TEST_D), "");
474 MYASSERT (tp_intset_is_member (freed_user_data, TEST_E), "");
475 MYASSERT (tp_intset_is_member (freed_user_data, TEST_F), "");
476 MYASSERT (tp_intset_is_member (freed_user_data, TEST_G), "");
477 MYASSERT (tp_intset_is_member (freed_user_data, TEST_H), "");
478 MYASSERT (tp_intset_is_member (freed_user_data, TEST_I), "");
479 MYASSERT (tp_intset_is_member (freed_user_data, TEST_J), "");
480 MYASSERT (tp_intset_is_member (freed_user_data, TEST_K), "");
481 MYASSERT (tp_intset_is_member (freed_user_data, TEST_Z), "");
482
483 tp_intset_destroy (freed_user_data);
484 tp_intset_destroy (method_ok);
485 tp_intset_destroy (method_error);
486
487 return 0;
488 }
489