2003-04-27 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / bus / test.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* test.c  unit test routines
3  *
4  * Copyright (C) 2003 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <config.h>
25
26 #ifdef DBUS_BUILD_TESTS
27 #include "test.h"
28 #include <dbus/dbus-internals.h>
29 #include <dbus/dbus-list.h>
30
31 /* The "debug client" watch/timeout handlers don't dispatch messages,
32  * as we manually pull them in order to verify them. This is why they
33  * are different from the real handlers in connection.c
34  */
35 static DBusList *clients = NULL;
36 static DBusLoop *client_loop = NULL;
37
38 static dbus_bool_t
39 client_watch_callback (DBusWatch     *watch,
40                        unsigned int   condition,
41                        void          *data)
42 {
43   /* FIXME this can be done in dbus-mainloop.c
44    * if the code in activation.c for the babysitter
45    * watch handler is fixed.
46    */
47  
48   return dbus_watch_handle (watch, condition);
49 }
50
51 static dbus_bool_t
52 add_client_watch (DBusWatch      *watch,
53                   void           *data)
54 {
55   DBusConnection *connection = data;
56   
57   return _dbus_loop_add_watch (client_loop,
58                                watch, client_watch_callback, connection,
59                                NULL);
60 }
61
62 static void
63 remove_client_watch (DBusWatch      *watch,
64                      void           *data)
65 {
66   DBusConnection *connection = data;
67   
68   _dbus_loop_remove_watch (client_loop,
69                            watch, client_watch_callback, connection);
70 }
71
72 static void
73 client_timeout_callback (DBusTimeout   *timeout,
74                          void          *data)
75 {
76   DBusConnection *connection = data;
77
78   dbus_connection_ref (connection);
79
80   /* can return FALSE on OOM but we just let it fire again later */
81   dbus_timeout_handle (timeout);
82
83   dbus_connection_unref (connection);
84 }
85
86 static dbus_bool_t
87 add_client_timeout (DBusTimeout    *timeout,
88                     void           *data)
89 {
90   DBusConnection *connection = data;
91   
92   return _dbus_loop_add_timeout (client_loop, timeout, client_timeout_callback, connection, NULL);
93 }
94
95 static void
96 remove_client_timeout (DBusTimeout    *timeout,
97                        void           *data)
98 {
99   DBusConnection *connection = data;
100   
101   _dbus_loop_remove_timeout (client_loop, timeout, client_timeout_callback, connection);
102 }
103
104 static DBusHandlerResult
105 client_disconnect_handler (DBusMessageHandler *handler,
106                            DBusConnection     *connection,
107                            DBusMessage        *message,
108                            void               *user_data)
109 {
110   _dbus_verbose ("Removing client %p in disconnect handler\n",
111                  connection);
112   
113   _dbus_list_remove (&clients, connection);
114
115   dbus_connection_unref (connection);
116   
117   if (clients == NULL)
118     {
119       _dbus_loop_unref (client_loop);
120       client_loop = NULL;
121     }
122   
123   return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
124 }
125
126 static int handler_slot = -1;
127 static int handler_slot_refcount = 0;
128
129 static dbus_bool_t
130 handler_slot_ref (void)
131 {
132   if (handler_slot < 0)
133     {
134       handler_slot = dbus_connection_allocate_data_slot ();
135       
136       if (handler_slot < 0)
137         return FALSE;
138
139       _dbus_assert (handler_slot_refcount == 0);
140     }  
141
142   handler_slot_refcount += 1;
143
144   return TRUE;
145
146 }
147
148 static void
149 handler_slot_unref (void)
150 {
151   _dbus_assert (handler_slot_refcount > 0);
152
153   handler_slot_refcount -= 1;
154   
155   if (handler_slot_refcount == 0)
156     {
157       dbus_connection_free_data_slot (handler_slot);
158       handler_slot = -1;
159     }
160 }
161
162 static void
163 free_handler (void *data)
164 {
165   DBusMessageHandler *handler = data;
166
167   dbus_message_handler_unref (handler);
168   handler_slot_unref ();
169 }
170
171 dbus_bool_t
172 bus_setup_debug_client (DBusConnection *connection)
173 {
174   DBusMessageHandler *disconnect_handler;
175   const char *to_handle[] = { DBUS_MESSAGE_LOCAL_DISCONNECT };
176   dbus_bool_t retval;
177   
178   disconnect_handler = dbus_message_handler_new (client_disconnect_handler,
179                                                  NULL, NULL);
180
181   if (disconnect_handler == NULL)
182     return FALSE;
183
184   if (!dbus_connection_register_handler (connection,
185                                          disconnect_handler,
186                                          to_handle,
187                                          _DBUS_N_ELEMENTS (to_handle)))
188     {
189       dbus_message_handler_unref (disconnect_handler);
190       return FALSE;
191     }
192
193   retval = FALSE;
194
195   if (client_loop == NULL)
196     {
197       client_loop = _dbus_loop_new ();
198       if (client_loop == NULL)
199         goto out;
200     }
201   
202   if (!dbus_connection_set_watch_functions (connection,
203                                             add_client_watch,
204                                             remove_client_watch,
205                                             NULL,
206                                             connection,
207                                             NULL))
208     goto out;
209       
210   if (!dbus_connection_set_timeout_functions (connection,
211                                               add_client_timeout,
212                                               remove_client_timeout,
213                                               NULL,
214                                               connection, NULL))
215     goto out;
216
217   if (!_dbus_list_append (&clients, connection))
218     goto out;
219
220   if (!handler_slot_ref ())
221     goto out;
222
223   /* Set up handler to be destroyed */  
224   if (!dbus_connection_set_data (connection, handler_slot,
225                                  disconnect_handler,
226                                  free_handler))
227     {
228       handler_slot_unref ();
229       goto out;
230     }
231   
232   retval = TRUE;
233   
234  out:
235   if (!retval)
236     {
237       dbus_message_handler_unref (disconnect_handler); /* unregisters it */
238       
239       dbus_connection_set_watch_functions (connection,
240                                            NULL, NULL, NULL, NULL, NULL);
241       dbus_connection_set_timeout_functions (connection,
242                                              NULL, NULL, NULL, NULL, NULL);
243
244       _dbus_list_remove_last (&clients, connection);
245
246       if (clients == NULL)
247         {
248           _dbus_loop_unref (client_loop);
249           client_loop = NULL;
250         }
251     }
252       
253   return retval;
254 }
255
256 void
257 bus_test_clients_foreach (BusConnectionForeachFunction  function,
258                           void                         *data)
259 {
260   DBusList *link;
261   
262   link = _dbus_list_get_first_link (&clients);
263   while (link != NULL)
264     {
265       DBusConnection *connection = link->data;
266       DBusList *next = _dbus_list_get_next_link (&clients, link);
267
268       if (!(* function) (connection, data))
269         break;
270       
271       link = next;
272     }
273 }
274
275 dbus_bool_t
276 bus_test_client_listed (DBusConnection *connection)
277 {
278   DBusList *link;
279   
280   link = _dbus_list_get_first_link (&clients);
281   while (link != NULL)
282     {
283       DBusConnection *c = link->data;
284       DBusList *next = _dbus_list_get_next_link (&clients, link);
285
286       if (c == connection)
287         return TRUE;
288       
289       link = next;
290     }
291
292   return FALSE;
293 }
294
295 void
296 bus_test_run_clients_loop (dbus_bool_t block_once)
297 {
298   if (client_loop == NULL)
299     return;
300
301   /* dispatch before we block so pending dispatches
302    * won't make our block return early
303    */
304   _dbus_loop_dispatch (client_loop);
305   
306   /* Do one blocking wait, since we're expecting data */
307   if (block_once)
308     {
309       _dbus_verbose ("---> blocking on \"client side\"\n");
310       _dbus_loop_iterate (client_loop, TRUE);
311     }
312
313   /* Then mop everything up */
314   while (_dbus_loop_iterate (client_loop, FALSE))
315     ;
316 }
317
318 void
319 bus_test_run_bus_loop (BusContext *context,
320                        dbus_bool_t block_once)
321 {
322   /* dispatch before we block so pending dispatches
323    * won't make our block return early
324    */
325   _dbus_loop_dispatch (bus_context_get_loop (context));
326   
327   /* Do one blocking wait, since we're expecting data */
328   if (block_once)
329     {
330       _dbus_verbose ("---> blocking on \"server side\"\n");
331       _dbus_loop_iterate (bus_context_get_loop (context), TRUE);
332     }
333
334   /* Then mop everything up */
335   while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE))
336     ;
337 }
338
339 void
340 bus_test_run_everything (BusContext *context)
341 {
342   while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE) ||
343          (client_loop == NULL || _dbus_loop_iterate (client_loop, FALSE)))
344     ;
345 }
346
347 BusContext*
348 bus_context_new_test (const DBusString *test_data_dir,
349                       const char       *filename)
350 {
351   DBusError error;
352   DBusString config_file;
353   DBusString relative;
354   BusContext *context;
355   
356   if (!_dbus_string_init (&config_file))
357     {
358       _dbus_warn ("No memory\n");
359       return NULL;
360     }
361
362   if (!_dbus_string_copy (test_data_dir, 0,
363                           &config_file, 0))
364     {
365       _dbus_warn ("No memory\n");
366       _dbus_string_free (&config_file);
367       return NULL;
368     }
369
370   _dbus_string_init_const (&relative, filename);
371
372   if (!_dbus_concat_dir_and_file (&config_file, &relative))
373     {
374       _dbus_warn ("No memory\n");
375       _dbus_string_free (&config_file);
376       return NULL;
377     }
378   
379   dbus_error_init (&error);
380   context = bus_context_new (&config_file, -1, &error);
381   if (context == NULL)
382     {
383       _DBUS_ASSERT_ERROR_IS_SET (&error);
384       
385       _dbus_warn ("Failed to create debug bus context from configuration file %s: %s\n",
386                   filename, error.message);
387
388       dbus_error_free (&error);
389       
390       _dbus_string_free (&config_file);
391       
392       return NULL;
393     }
394
395   _dbus_string_free (&config_file);
396   
397   return context;
398 }
399
400 #endif