1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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#include <dbus/dbus-sysdeps.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 */
36static DBusList *clients = NULL;
37static DBusLoop *client_loop = NULL;
38
39static dbus_bool_t
40add_client_watch (DBusWatch      *watch,
41                  void           *data)
42{
43  return _dbus_loop_add_watch (client_loop, watch);
44}
45
46static void
47remove_client_watch (DBusWatch      *watch,
48                     void           *data)
49{
50  _dbus_loop_remove_watch (client_loop, watch);
51}
52
53static void
54toggle_client_watch (DBusWatch      *watch,
55                     void           *data)
56{
57  _dbus_loop_toggle_watch (client_loop, watch);
58}
59
60static dbus_bool_t
61add_client_timeout (DBusTimeout    *timeout,
62                    void           *data)
63{
64  return _dbus_loop_add_timeout (client_loop, timeout);
65}
66
67static void
68remove_client_timeout (DBusTimeout    *timeout,
69                       void           *data)
70{
71  _dbus_loop_remove_timeout (client_loop, timeout);
72}
73
74static DBusHandlerResult
75client_disconnect_filter (DBusConnection     *connection,
76                          DBusMessage        *message,
77                          void               *user_data)
78{
79  if (!dbus_message_is_signal (message,
80                               DBUS_INTERFACE_LOCAL,
81                               "Disconnected"))
82    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
83
84  _dbus_verbose ("Removing client %p in disconnect handler\n",
85                 connection);
86
87  _dbus_list_remove (&clients, connection);
88
89  dbus_connection_unref (connection);
90
91  if (clients == NULL)
92    {
93      _dbus_loop_unref (client_loop);
94      client_loop = NULL;
95    }
96
97  return DBUS_HANDLER_RESULT_HANDLED;
98}
99
100dbus_bool_t
101bus_setup_debug_client (DBusConnection *connection)
102{
103  dbus_bool_t retval;
104
105  if (!dbus_connection_add_filter (connection,
106                                   client_disconnect_filter,
107                                   NULL, NULL))
108    return FALSE;
109
110  retval = FALSE;
111
112  if (client_loop == NULL)
113    {
114      client_loop = _dbus_loop_new ();
115      if (client_loop == NULL)
116        goto out;
117    }
118
119  if (!dbus_connection_set_watch_functions (connection,
120                                            add_client_watch,
121                                            remove_client_watch,
122                                            toggle_client_watch,
123                                            connection,
124                                            NULL))
125    goto out;
126
127  if (!dbus_connection_set_timeout_functions (connection,
128                                              add_client_timeout,
129                                              remove_client_timeout,
130                                              NULL,
131                                              connection, NULL))
132    goto out;
133
134  if (!_dbus_list_append (&clients, connection))
135    goto out;
136
137  retval = TRUE;
138
139 out:
140  if (!retval)
141    {
142      dbus_connection_remove_filter (connection,
143                                     client_disconnect_filter,
144                                     NULL);
145
146      dbus_connection_set_watch_functions (connection,
147                                           NULL, NULL, NULL, NULL, NULL);
148      dbus_connection_set_timeout_functions (connection,
149                                             NULL, NULL, NULL, NULL, NULL);
150
151      _dbus_list_remove_last (&clients, connection);
152
153      if (clients == NULL)
154        {
155          _dbus_loop_unref (client_loop);
156          client_loop = NULL;
157        }
158    }
159
160  return retval;
161}
162
163void
164bus_test_clients_foreach (BusConnectionForeachFunction  function,
165                          void                         *data)
166{
167  DBusList *link;
168
169  link = _dbus_list_get_first_link (&clients);
170  while (link != NULL)
171    {
172      DBusConnection *connection = link->data;
173      DBusList *next = _dbus_list_get_next_link (&clients, link);
174
175      if (!(* function) (connection, data))
176        break;
177
178      link = next;
179    }
180}
181
182dbus_bool_t
183bus_test_client_listed (DBusConnection *connection)
184{
185  DBusList *link;
186
187  link = _dbus_list_get_first_link (&clients);
188  while (link != NULL)
189    {
190      DBusConnection *c = link->data;
191      DBusList *next = _dbus_list_get_next_link (&clients, link);
192
193      if (c == connection)
194        return TRUE;
195
196      link = next;
197    }
198
199  return FALSE;
200}
201
202void
203bus_test_run_clients_loop (dbus_bool_t block_once)
204{
205  if (client_loop == NULL)
206    return;
207
208  _dbus_verbose ("---> Dispatching on \"client side\"\n");
209
210  /* dispatch before we block so pending dispatches
211   * won't make our block return early
212   */
213  _dbus_loop_dispatch (client_loop);
214
215  /* Do one blocking wait, since we're expecting data */
216  if (block_once)
217    {
218      _dbus_verbose ("---> blocking on \"client side\"\n");
219      _dbus_loop_iterate (client_loop, TRUE);
220    }
221
222  /* Then mop everything up */
223  while (_dbus_loop_iterate (client_loop, FALSE))
224    ;
225
226  _dbus_verbose ("---> Done dispatching on \"client side\"\n");
227}
228
229void
230bus_test_run_bus_loop (BusContext *context,
231                       dbus_bool_t block_once)
232{
233  _dbus_verbose ("---> Dispatching on \"server side\"\n");
234
235  /* dispatch before we block so pending dispatches
236   * won't make our block return early
237   */
238  _dbus_loop_dispatch (bus_context_get_loop (context));
239
240  /* Do one blocking wait, since we're expecting data */
241  if (block_once)
242    {
243      _dbus_verbose ("---> blocking on \"server side\"\n");
244      _dbus_loop_iterate (bus_context_get_loop (context), TRUE);
245    }
246
247  /* Then mop everything up */
248  while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE))
249    ;
250
251  _dbus_verbose ("---> Done dispatching on \"server side\"\n");
252}
253
254void
255bus_test_run_everything (BusContext *context)
256{
257  while (_dbus_loop_iterate (bus_context_get_loop (context), FALSE) ||
258         (client_loop == NULL || _dbus_loop_iterate (client_loop, FALSE)))
259    ;
260}
261
262BusContext*
263bus_context_new_test (const DBusString *test_data_dir,
264                      const char       *filename)
265{
266  DBusError error;
267  DBusString config_file;
268  DBusString relative;
269  BusContext *context;
270
271  if (!_dbus_string_init (&config_file))
272    {
273      _dbus_warn ("No memory\n");
274      return NULL;
275    }
276
277  if (!_dbus_string_copy (test_data_dir, 0,
278                          &config_file, 0))
279    {
280      _dbus_warn ("No memory\n");
281      _dbus_string_free (&config_file);
282      return NULL;
283    }
284
285  _dbus_string_init_const (&relative, filename);
286
287  if (!_dbus_concat_dir_and_file (&config_file, &relative))
288    {
289      _dbus_warn ("No memory\n");
290      _dbus_string_free (&config_file);
291      return NULL;
292    }
293
294  dbus_error_init (&error);
295  context = bus_context_new (&config_file, BUS_CONTEXT_FLAG_NONE, NULL, NULL, NULL, &error);
296  if (context == NULL)
297    {
298      _DBUS_ASSERT_ERROR_IS_SET (&error);
299
300      _dbus_warn ("Failed to create debug bus context from configuration file %s: %s\n",
301                  filename, error.message);
302
303      dbus_error_free (&error);
304
305      _dbus_string_free (&config_file);
306
307      return NULL;
308    }
309
310  _dbus_string_free (&config_file);
311
312  return context;
313}
314
315#endif
316