2003-04-06 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 "loop.h"
29 #include <dbus/dbus-internals.h>
30 #include <dbus/dbus-list.h>
31
32 /* The "debug client" watch/timeout handlers don't dispatch messages,
33  * as we manually pull them in order to verify them. This is why they
34  * are different from the real handlers in connection.c
35  */
36 static DBusList *clients = NULL;
37 static BusLoop *client_loop = NULL;
38
39 static dbus_bool_t
40 client_watch_callback (DBusWatch     *watch,
41                        unsigned int   condition,
42                        void          *data)
43 {
44   DBusConnection *connection = data;
45   dbus_bool_t retval;
46   
47   dbus_connection_ref (connection);
48   
49   retval = dbus_connection_handle_watch (connection, watch, condition);
50
51   dbus_connection_unref (connection);
52
53   return retval;
54 }
55
56 static dbus_bool_t
57 add_client_watch (DBusWatch      *watch,
58                   void           *data)
59 {
60   DBusConnection *connection = data;
61   
62   return bus_loop_add_watch (client_loop,
63                              watch, client_watch_callback, connection,
64                              NULL);
65 }
66
67 static void
68 remove_client_watch (DBusWatch      *watch,
69                      void           *data)
70 {
71   DBusConnection *connection = data;
72   
73   bus_loop_remove_watch (client_loop,
74                          watch, client_watch_callback, connection);
75 }
76
77 static void
78 client_timeout_callback (DBusTimeout   *timeout,
79                          void          *data)
80 {
81   DBusConnection *connection = data;
82
83   dbus_connection_ref (connection);
84
85   /* can return FALSE on OOM but we just let it fire again later */
86   dbus_timeout_handle (timeout);
87
88   dbus_connection_unref (connection);
89 }
90
91 static dbus_bool_t
92 add_client_timeout (DBusTimeout    *timeout,
93                     void           *data)
94 {
95   DBusConnection *connection = data;
96   
97   return bus_loop_add_timeout (client_loop, timeout, client_timeout_callback, connection, NULL);
98 }
99
100 static void
101 remove_client_timeout (DBusTimeout    *timeout,
102                        void           *data)
103 {
104   DBusConnection *connection = data;
105   
106   bus_loop_remove_timeout (client_loop, timeout, client_timeout_callback, connection);
107 }
108
109 static DBusHandlerResult
110 client_disconnect_handler (DBusMessageHandler *handler,
111                            DBusConnection     *connection,
112                            DBusMessage        *message,
113                            void               *user_data)
114 {
115   _dbus_verbose ("Removing client %p in disconnect handler\n",
116                  connection);
117   
118   _dbus_list_remove (&clients, connection);
119
120   dbus_connection_unref (connection);
121   
122   if (clients == NULL)
123     {
124       bus_loop_unref (client_loop);
125       client_loop = NULL;
126     }
127   
128   return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
129 }
130
131 static int handler_slot = -1;
132 static int handler_slot_refcount = 0;
133
134 static dbus_bool_t
135 handler_slot_ref (void)
136 {
137   if (handler_slot < 0)
138     {
139       handler_slot = dbus_connection_allocate_data_slot ();
140       
141       if (handler_slot < 0)
142         return FALSE;
143
144       _dbus_assert (handler_slot_refcount == 0);
145     }  
146
147   handler_slot_refcount += 1;
148
149   return TRUE;
150
151 }
152
153 static void
154 handler_slot_unref (void)
155 {
156   _dbus_assert (handler_slot_refcount > 0);
157
158   handler_slot_refcount -= 1;
159   
160   if (handler_slot_refcount == 0)
161     {
162       dbus_connection_free_data_slot (handler_slot);
163       handler_slot = -1;
164     }
165 }
166
167 static void
168 free_handler (void *data)
169 {
170   DBusMessageHandler *handler = data;
171
172   dbus_message_handler_unref (handler);
173   handler_slot_unref ();
174 }
175
176 dbus_bool_t
177 bus_setup_debug_client (DBusConnection *connection)
178 {
179   DBusMessageHandler *disconnect_handler;
180   const char *to_handle[] = { DBUS_MESSAGE_LOCAL_DISCONNECT };
181   dbus_bool_t retval;
182   
183   disconnect_handler = dbus_message_handler_new (client_disconnect_handler,
184                                                  NULL, NULL);
185
186   if (disconnect_handler == NULL)
187     return FALSE;
188
189   if (!dbus_connection_register_handler (connection,
190                                          disconnect_handler,
191                                          to_handle,
192                                          _DBUS_N_ELEMENTS (to_handle)))
193     {
194       dbus_message_handler_unref (disconnect_handler);
195       return FALSE;
196     }
197
198   retval = FALSE;
199
200   if (client_loop == NULL)
201     {
202       client_loop = bus_loop_new ();
203       if (client_loop == NULL)
204         goto out;
205     }
206   
207   if (!dbus_connection_set_watch_functions (connection,
208                                             add_client_watch,
209                                             remove_client_watch,
210                                             NULL,
211                                             connection,
212                                             NULL))
213     goto out;
214       
215   if (!dbus_connection_set_timeout_functions (connection,
216                                               add_client_timeout,
217                                               remove_client_timeout,
218                                               NULL,
219                                               connection, NULL))
220     goto out;
221
222   if (!_dbus_list_append (&clients, connection))
223     goto out;
224
225   if (!handler_slot_ref ())
226     goto out;
227
228   /* Set up handler to be destroyed */  
229   if (!dbus_connection_set_data (connection, handler_slot,
230                                  disconnect_handler,
231                                  free_handler))
232     {
233       handler_slot_unref ();
234       goto out;
235     }
236   
237   retval = TRUE;
238   
239  out:
240   if (!retval)
241     {
242       dbus_message_handler_unref (disconnect_handler); /* unregisters it */
243       
244       dbus_connection_set_watch_functions (connection,
245                                            NULL, NULL, NULL, NULL, NULL);
246       dbus_connection_set_timeout_functions (connection,
247                                              NULL, NULL, NULL, NULL, NULL);
248
249       _dbus_list_remove_last (&clients, connection);
250
251       if (clients == NULL)
252         {
253           bus_loop_unref (client_loop);
254           client_loop = NULL;
255         }
256     }
257       
258   return retval;
259 }
260
261 void
262 bus_test_clients_foreach (BusConnectionForeachFunction  function,
263                           void                         *data)
264 {
265   DBusList *link;
266   
267   link = _dbus_list_get_first_link (&clients);
268   while (link != NULL)
269     {
270       DBusConnection *connection = link->data;
271       DBusList *next = _dbus_list_get_next_link (&clients, link);
272
273       if (!(* function) (connection, data))
274         break;
275       
276       link = next;
277     }
278 }
279
280 dbus_bool_t
281 bus_test_client_listed (DBusConnection *connection)
282 {
283   DBusList *link;
284   
285   link = _dbus_list_get_first_link (&clients);
286   while (link != NULL)
287     {
288       DBusConnection *c = link->data;
289       DBusList *next = _dbus_list_get_next_link (&clients, link);
290
291       if (c == connection)
292         return TRUE;
293       
294       link = next;
295     }
296
297   return FALSE;
298 }
299
300 void
301 bus_test_run_clients_loop (void)
302 {
303   if (client_loop == NULL)
304     return;
305   
306   /* Do one blocking wait, since we're expecting data */
307   bus_loop_iterate (client_loop, TRUE);
308
309   /* Then mop everything up */
310   while (bus_loop_iterate (client_loop, FALSE))
311     ;
312 }
313
314 void
315 bus_test_run_bus_loop (BusContext *context)
316 {
317   /* Do one blocking wait, since we're expecting data */
318   bus_loop_iterate (bus_context_get_loop (context), TRUE);
319
320   /* Then mop everything up */
321   while (bus_loop_iterate (bus_context_get_loop (context), FALSE))
322     ;
323 }
324
325 void
326 bus_test_run_everything (BusContext *context)
327 {
328   int i;
329
330   i = 0;
331   while (i < 2)
332     {
333       while (bus_loop_iterate (bus_context_get_loop (context), FALSE) ||
334              (client_loop == NULL || bus_loop_iterate (client_loop, FALSE)))
335         ;
336       ++i;
337     }
338 }
339
340 BusContext*
341 bus_context_new_test (const DBusString *test_data_dir,
342                       const char       *filename)
343 {
344   DBusError error;
345   DBusString config_file;
346   DBusString relative;
347   BusContext *context;
348   
349   if (!_dbus_string_init (&config_file))
350     {
351       _dbus_warn ("No memory\n");
352       return NULL;
353     }
354
355   if (!_dbus_string_copy (test_data_dir, 0,
356                           &config_file, 0))
357     {
358       _dbus_warn ("No memory\n");
359       _dbus_string_free (&config_file);
360       return NULL;
361     }
362
363   _dbus_string_init_const (&relative, filename);
364
365   if (!_dbus_concat_dir_and_file (&config_file, &relative))
366     {
367       _dbus_warn ("No memory\n");
368       _dbus_string_free (&config_file);
369       return NULL;
370     }
371   
372   dbus_error_init (&error);
373   context = bus_context_new (&config_file, -1, &error);
374   if (context == NULL)
375     {
376       _DBUS_ASSERT_ERROR_IS_SET (&error);
377       
378       _dbus_warn ("Failed to create debug bus context from configuration file %s: %s\n",
379                   filename, error.message);
380
381       dbus_error_free (&error);
382       
383       _dbus_string_free (&config_file);
384       
385       return NULL;
386     }
387
388   _dbus_string_free (&config_file);
389   
390   return context;
391 }
392
393 #endif