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 2.1
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 */
35static DBusList *clients = NULL;
36static DBusLoop *client_loop = NULL;
37
38static dbus_bool_t
39client_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
51static dbus_bool_t
52add_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
62static void
63remove_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
72static void
73client_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
86static dbus_bool_t
87add_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
95static void
96remove_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
104static DBusHandlerResult
105client_disconnect_filter (DBusConnection     *connection,
106                          DBusMessage        *message,
107                          void               *user_data)
108{
109  if (!dbus_message_is_signal (message,
110                               DBUS_INTERFACE_LOCAL,
111                               "Disconnected"))
112    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
113
114  _dbus_verbose ("Removing client %p in disconnect handler\n",
115                 connection);
116
117  _dbus_list_remove (&clients, connection);
118
119  dbus_connection_unref (connection);
120
121  if (clients == NULL)
122    {
123      _dbus_loop_unref (client_loop);
124      client_loop = NULL;
125    }
126
127  return DBUS_HANDLER_RESULT_HANDLED;
128}
129
130dbus_bool_t
131bus_setup_debug_client (DBusConnection *connection)
132{
133  dbus_bool_t retval;
134
135  if (!dbus_connection_add_filter (connection,
136                                   client_disconnect_filter,
137                                   NULL, NULL))
138    return FALSE;
139
140  retval = FALSE;
141
142  if (client_loop == NULL)
143    {
144      client_loop = _dbus_loop_new ();
145      if (client_loop == NULL)
146        goto out;
147    }
148
149  if (!dbus_connection_set_watch_functions (connection,
150                                            add_client_watch,
151                                            remove_client_watch,
152                                            NULL,
153                                            connection,
154                                            NULL))
155    goto out;
156
157  if (!dbus_connection_set_timeout_functions (connection,
158                                              add_client_timeout,
159                                              remove_client_timeout,
160                                              NULL,
161                                              connection, NULL))
162    goto out;
163
164  if (!_dbus_list_append (&clients, connection))
165    goto out;
166
167  retval = TRUE;
168
169 out:
170  if (!retval)
171    {
172      dbus_connection_remove_filter (connection,
173                                     client_disconnect_filter,
174                                     NULL);
175
176      dbus_connection_set_watch_functions (connection,
177                                           NULL, NULL, NULL, NULL, NULL);
178      dbus_connection_set_timeout_functions (connection,
179                                             NULL, NULL, NULL, NULL, NULL);
180
181      _dbus_list_remove_last (&clients, connection);
182
183      if (clients == NULL)
184        {
185          _dbus_loop_unref (client_loop);
186          client_loop = NULL;
187        }
188    }
189
190  return retval;
191}
192
193void
194bus_test_clients_foreach (BusConnectionForeachFunction  function,
195                          void                         *data)
196{
197  DBusList *link;
198
199  link = _dbus_list_get_first_link (&clients);
200  while (link != NULL)
201    {
202      DBusConnection *connection = link->data;
203      DBusList *next = _dbus_list_get_next_link (&clients, link);
204
205      if (!(* function) (connection, data))
206        break;
207
208      link = next;
209    }
210}
211
212dbus_bool_t
213bus_test_client_listed (DBusConnection *connection)
214{
215  DBusList *link;
216
217  link = _dbus_list_get_first_link (&clients);
218  while (link != NULL)
219    {
220      DBusConnection *c = link->data;
221      DBusList *next = _dbus_list_get_next_link (&clients, link);
222
223      if (c == connection)
224        return TRUE;
225
226      link = next;
227    }
228
229  return FALSE;
230}
231
232void
233bus_test_run_clients_loop (dbus_bool_t block_once)
234{
235  if (client_loop == NULL)
236    return;
237
238  _dbus_verbose ("---> Dispatching on \"client side\"\n");
239
240  /* dispatch before we block so pending dispatches
241   * won't make our block return early
242   */
243  _dbus_loop_dispatch (client_loop);
244
245  /* Do one blocking wait, since we're expecting data */
246  if (block_once)
247    {
248      _dbus_verbose ("---> blocking on \"client side\"\n");
249      _dbus_loop_iterate (client_loop, TRUE);
250    }
251
252  /* Then mop everything up */
253  while (_dbus_loop_iterate (client_loop, FALSE))
254    ;
255
256  _dbus_verbose ("---> Done dispatching on \"client side\"\n");
257}
258
259void
260bus_test_run_bus_loop (BusContext *context,
261                       dbus_bool_t block_once)
262{
263  _dbus_verbose ("---> Dispatching on \"server side\"\n");
264
265  /* dispatch before we block so pending dispatches
266   * won't make our block return early
267   */
268  _dbus_loop_dispatch (bus_context_get_loop (context));
269
270  /* Do one blocking wait, since we're expecting data */
271  if (block_once)
272    {
273      _dbus_verbose ("---> blocking on \"server side\"\n");
274      _dbus_loop_iterate (bus_context_get_loop (context), TRUE);
275    }
276
277  /* Then mop everything up */
278  while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE))
279    ;
280
281  _dbus_verbose ("---> Done dispatching on \"server side\"\n");
282}
283
284void
285bus_test_run_everything (BusContext *context)
286{
287  while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE) ||
288         (client_loop == NULL || _dbus_loop_iterate (client_loop, FALSE)))
289    ;
290}
291
292BusContext*
293bus_context_new_test (const DBusString *test_data_dir,
294                      const char       *filename)
295{
296  DBusError error;
297  DBusString config_file;
298  DBusString relative;
299  BusContext *context;
300
301  if (!_dbus_string_init (&config_file))
302    {
303      _dbus_warn ("No memory\n");
304      return NULL;
305    }
306
307  if (!_dbus_string_copy (test_data_dir, 0,
308                          &config_file, 0))
309    {
310      _dbus_warn ("No memory\n");
311      _dbus_string_free (&config_file);
312      return NULL;
313    }
314
315  _dbus_string_init_const (&relative, filename);
316
317  if (!_dbus_concat_dir_and_file (&config_file, &relative))
318    {
319      _dbus_warn ("No memory\n");
320      _dbus_string_free (&config_file);
321      return NULL;
322    }
323
324  dbus_error_init (&error);
325  context = bus_context_new (&config_file, FALSE, -1, -1, &error);
326  if (context == NULL)
327    {
328      _DBUS_ASSERT_ERROR_IS_SET (&error);
329
330      _dbus_warn ("Failed to create debug bus context from configuration file %s: %s\n",
331                  filename, error.message);
332
333      dbus_error_free (&error);
334
335      _dbus_string_free (&config_file);
336
337      return NULL;
338    }
339
340  _dbus_string_free (&config_file);
341
342  return context;
343}
344
345#endif
346