2003-03-31 Havoc Pennington <hp@redhat.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
38 static dbus_bool_t
39 client_watch_callback (DBusWatch     *watch,
40                            unsigned int   condition,
41                            void          *data)
42 {
43   DBusConnection *connection = data;
44   dbus_bool_t retval;
45   
46   dbus_connection_ref (connection);
47   
48   retval = dbus_connection_handle_watch (connection, watch, condition);
49
50   dbus_connection_unref (connection);
51
52   return retval;
53 }
54
55 static dbus_bool_t
56 add_client_watch (DBusWatch      *watch,
57                       DBusConnection *connection)
58 {
59   return bus_loop_add_watch (watch, client_watch_callback, connection,
60                              NULL);
61 }
62
63 static void
64 remove_client_watch (DBusWatch      *watch,
65                          DBusConnection *connection)
66 {
67   bus_loop_remove_watch (watch, client_watch_callback, connection);
68 }
69
70 static void
71 client_timeout_callback (DBusTimeout   *timeout,
72                              void          *data)
73 {
74   DBusConnection *connection = data;
75
76   dbus_connection_ref (connection);
77
78   /* can return FALSE on OOM but we just let it fire again later */
79   dbus_timeout_handle (timeout);
80
81   dbus_connection_unref (connection);
82 }
83
84 static dbus_bool_t
85 add_client_timeout (DBusTimeout    *timeout,
86                         DBusConnection *connection)
87 {
88   return bus_loop_add_timeout (timeout, client_timeout_callback, connection, NULL);
89 }
90
91 static void
92 remove_client_timeout (DBusTimeout    *timeout,
93                            DBusConnection *connection)
94 {
95   bus_loop_remove_timeout (timeout, client_timeout_callback, connection);
96 }
97
98 static DBusHandlerResult
99 client_disconnect_handler (DBusMessageHandler *handler,
100                            DBusConnection     *connection,
101                            DBusMessage        *message,
102                            void               *user_data)
103 {
104   _dbus_verbose ("Removing client %p in disconnect handler\n",
105                  connection);
106   
107   _dbus_list_remove (&clients, connection);
108   
109   dbus_connection_unref (connection);
110   
111   return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
112 }
113
114 static int handler_slot = -1;
115 static int handler_slot_refcount = 0;
116
117 static dbus_bool_t
118 handler_slot_ref (void)
119 {
120   if (handler_slot < 0)
121     {
122       handler_slot = dbus_connection_allocate_data_slot ();
123       
124       if (handler_slot < 0)
125         return FALSE;
126
127       _dbus_assert (handler_slot_refcount == 0);
128     }  
129
130   handler_slot_refcount += 1;
131
132   return TRUE;
133
134 }
135
136 static void
137 handler_slot_unref (void)
138 {
139   _dbus_assert (handler_slot_refcount > 0);
140
141   handler_slot_refcount -= 1;
142   
143   if (handler_slot_refcount == 0)
144     {
145       dbus_connection_free_data_slot (handler_slot);
146       handler_slot = -1;
147     }
148 }
149
150 static void
151 free_handler (void *data)
152 {
153   DBusMessageHandler *handler = data;
154
155   dbus_message_handler_unref (handler);
156   handler_slot_unref ();
157 }
158
159 dbus_bool_t
160 bus_setup_debug_client (DBusConnection *connection)
161 {
162   DBusMessageHandler *disconnect_handler;
163   const char *to_handle[] = { DBUS_MESSAGE_LOCAL_DISCONNECT };
164   dbus_bool_t retval;
165   
166   disconnect_handler = dbus_message_handler_new (client_disconnect_handler,
167                                                  NULL, NULL);
168
169   if (disconnect_handler == NULL)
170     return FALSE;
171
172   if (!dbus_connection_register_handler (connection,
173                                          disconnect_handler,
174                                          to_handle,
175                                          _DBUS_N_ELEMENTS (to_handle)))
176     {
177       dbus_message_handler_unref (disconnect_handler);
178       return FALSE;
179     }
180
181   retval = FALSE;
182   
183   if (!dbus_connection_set_watch_functions (connection,
184                                             (DBusAddWatchFunction) add_client_watch,
185                                             (DBusRemoveWatchFunction) remove_client_watch,
186                                             NULL,
187                                             connection,
188                                             NULL))
189     goto out;
190       
191   if (!dbus_connection_set_timeout_functions (connection,
192                                               (DBusAddTimeoutFunction) add_client_timeout,
193                                               (DBusRemoveTimeoutFunction) remove_client_timeout,
194                                               NULL,
195                                               connection, NULL))
196     goto out;
197
198   if (!_dbus_list_append (&clients, connection))
199     goto out;
200
201   if (!handler_slot_ref ())
202     goto out;
203
204   /* Set up handler to be destroyed */  
205   if (!dbus_connection_set_data (connection, handler_slot,
206                                  disconnect_handler,
207                                  free_handler))
208     {
209       handler_slot_unref ();
210       goto out;
211     }
212   
213   retval = TRUE;
214   
215  out:
216   if (!retval)
217     {
218       dbus_message_handler_unref (disconnect_handler); /* unregisters it */
219       
220       dbus_connection_set_watch_functions (connection,
221                                            NULL, NULL, NULL, NULL, NULL);
222       dbus_connection_set_timeout_functions (connection,
223                                              NULL, NULL, NULL, NULL, NULL);
224
225       _dbus_list_remove_last (&clients, connection);
226     }
227       
228   return retval;
229 }
230
231 void
232 bus_test_clients_foreach (BusConnectionForeachFunction  function,
233                           void                         *data)
234 {
235   DBusList *link;
236   
237   link = _dbus_list_get_first_link (&clients);
238   while (link != NULL)
239     {
240       DBusConnection *connection = link->data;
241       DBusList *next = _dbus_list_get_next_link (&clients, link);
242
243       if (!(* function) (connection, data))
244         break;
245       
246       link = next;
247     }
248 }
249
250 dbus_bool_t
251 bus_test_client_listed (DBusConnection *connection)
252 {
253   DBusList *link;
254   
255   link = _dbus_list_get_first_link (&clients);
256   while (link != NULL)
257     {
258       DBusConnection *c = link->data;
259       DBusList *next = _dbus_list_get_next_link (&clients, link);
260
261       if (c == connection)
262         return TRUE;
263       
264       link = next;
265     }
266
267   return FALSE;
268 }
269
270 void
271 bus_test_flush_bus (BusContext *context)
272 {
273   /* This is race condition city, obviously. since we're all in one
274    * process we can't block, we just have to wait for data we put in
275    * one end of the debug pipe to come out the other end...
276    * a more robust setup would be good. Blocking on the other
277    * end of pipes we've pushed data into or something.
278    * A simple hack might be to just make the debug server always
279    * poll for read on the other end of the pipe after writing.
280    */
281   while (bus_loop_iterate (FALSE))
282     ;
283 #if 0
284   _dbus_sleep_milliseconds (15);
285 #endif
286   while (bus_loop_iterate (FALSE))
287     ;
288 }
289
290 BusContext*
291 bus_context_new_test (const DBusString *test_data_dir,
292                       const char       *filename)
293 {
294   DBusError error;
295   DBusString config_file;
296   DBusString relative;
297   BusContext *context;
298   
299   if (!_dbus_string_init (&config_file))
300     {
301       _dbus_warn ("No memory\n");
302       return NULL;
303     }
304
305   if (!_dbus_string_copy (test_data_dir, 0,
306                           &config_file, 0))
307     {
308       _dbus_warn ("No memory\n");
309       _dbus_string_free (&config_file);
310       return NULL;
311     }
312
313   _dbus_string_init_const (&relative, filename);
314
315   if (!_dbus_concat_dir_and_file (&config_file, &relative))
316     {
317       _dbus_warn ("No memory\n");
318       _dbus_string_free (&config_file);
319       return NULL;
320     }
321   
322   dbus_error_init (&error);
323   context = bus_context_new (&config_file, &error);
324   if (context == NULL)
325     {
326       _DBUS_ASSERT_ERROR_IS_SET (&error);
327       
328       _dbus_warn ("Failed to create debug bus context from configuration file %s: %s\n",
329                   filename, error.message);
330
331       dbus_error_free (&error);
332       
333       _dbus_string_free (&config_file);
334       
335       return NULL;
336     }
337
338   _dbus_string_free (&config_file);
339   
340   return context;
341 }
342
343 #endif