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, &copy_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, &copy_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, &copy_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, &copy_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