Add a regression test that can reproduce fd.o #34393
[platform/upstream/dbus.git] / test / dbus-daemon.c
1 /* Integration tests for the dbus-daemon
2  *
3  * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
4  * Copyright © 2010-2011 Nokia Corporation
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction,
9  * including without limitation the rights to use, copy, modify, merge,
10  * publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26
27 #include <config.h>
28
29 #include <glib.h>
30
31 #include <dbus/dbus.h>
32 #include <dbus/dbus-glib-lowlevel.h>
33
34 #include <string.h>
35
36 #ifdef DBUS_WIN
37 # include <windows.h>
38 #else
39 # include <signal.h>
40 # include <unistd.h>
41 #endif
42
43 typedef struct {
44     gboolean skip;
45
46     DBusError e;
47     GError *ge;
48
49     gint daemon_pid;
50
51     DBusConnection *left_conn;
52
53     DBusConnection *right_conn;
54     gboolean right_conn_echo;
55 } Fixture;
56
57 #define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
58 static void
59 _assert_no_error (const DBusError *e,
60     const char *file,
61     int line)
62 {
63   if (G_UNLIKELY (dbus_error_is_set (e)))
64     g_error ("%s:%d: expected success but got error: %s: %s",
65         file, line, e->name, e->message);
66 }
67
68 static gchar *
69 spawn_dbus_daemon (gchar *binary,
70     gchar *configuration,
71     gint *daemon_pid)
72 {
73   GError *error = NULL;
74   GString *address;
75   gint address_fd;
76   gchar *argv[] = {
77       binary,
78       configuration,
79       "--nofork",
80       "--print-address=1", /* stdout */
81       NULL
82   };
83
84   g_spawn_async_with_pipes (NULL, /* working directory */
85       argv,
86       NULL, /* envp */
87       G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
88       NULL, /* child_setup */
89       NULL, /* user data */
90       daemon_pid,
91       NULL, /* child's stdin = /dev/null */
92       &address_fd,
93       NULL, /* child's stderr = our stderr */
94       &error);
95   g_assert_no_error (error);
96
97   address = g_string_new (NULL);
98
99   /* polling until the dbus-daemon writes out its address is a bit stupid,
100    * but at least it's simple, unlike dbus-launch... in principle we could
101    * use select() here, but life's too short */
102   while (1)
103     {
104       gssize bytes;
105       gchar buf[4096];
106       gchar *newline;
107
108       bytes = read (address_fd, buf, sizeof (buf));
109
110       if (bytes > 0)
111         g_string_append_len (address, buf, bytes);
112
113       newline = strchr (address->str, '\n');
114
115       if (newline != NULL)
116         {
117           g_string_truncate (address, newline - address->str);
118           break;
119         }
120
121       g_usleep (G_USEC_PER_SEC / 10);
122     }
123
124   return g_string_free (address, FALSE);
125 }
126
127 static DBusConnection *
128 connect_to_bus (const gchar *address)
129 {
130   DBusConnection *conn;
131   DBusError error = DBUS_ERROR_INIT;
132   dbus_bool_t ok;
133
134   conn = dbus_connection_open_private (address, &error);
135   assert_no_error (&error);
136   g_assert (conn != NULL);
137
138   ok = dbus_bus_register (conn, &error);
139   assert_no_error (&error);
140   g_assert (ok);
141   g_assert (dbus_bus_get_unique_name (conn) != NULL);
142
143   dbus_connection_setup_with_g_main (conn, NULL);
144   return conn;
145 }
146
147 static DBusHandlerResult
148 echo_filter (DBusConnection *connection,
149     DBusMessage *message,
150     void *user_data)
151 {
152   DBusMessage *reply;
153   DBusError error = DBUS_ERROR_INIT;
154   int *sleep_ms = user_data;
155
156   if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
157     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
158
159   reply = dbus_message_new_method_return (message);
160
161   if (reply == NULL)
162     g_error ("OOM");
163
164   if (!dbus_connection_send (connection, reply, NULL))
165     g_error ("OOM");
166
167   dbus_message_unref (reply);
168
169   return DBUS_HANDLER_RESULT_HANDLED;
170 }
171
172 typedef struct {
173     const char *bug_ref;
174     guint min_messages;
175     const char *config_file;
176 } Config;
177
178 static void
179 setup (Fixture *f,
180     gconstpointer context)
181 {
182   const Config *config = context;
183   gchar *dbus_daemon;
184   gchar *arg;
185   gchar *address;
186
187   f->ge = NULL;
188   dbus_error_init (&f->e);
189
190   if (config != NULL && config->config_file != NULL)
191     {
192       if (g_getenv ("DBUS_TEST_DATA") == NULL)
193         {
194           g_message ("SKIP: set DBUS_TEST_DATA to a directory containing %s",
195               config->config_file);
196           f->skip = TRUE;
197           return;
198         }
199
200       arg = g_strdup_printf (
201           "--config-file=%s/%s",
202           g_getenv ("DBUS_TEST_DATA"), config->config_file);
203     }
204   else if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
205     {
206       arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
207           g_getenv ("DBUS_TEST_SYSCONFDIR"));
208     }
209   else if (g_getenv ("DBUS_TEST_DATA") != NULL)
210     {
211       arg = g_strdup_printf (
212           "--config-file=%s/valid-config-files/session.conf",
213           g_getenv ("DBUS_TEST_DATA"));
214     }
215   else
216     {
217       arg = g_strdup ("--session");
218     }
219
220   dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
221
222   if (dbus_daemon == NULL)
223     dbus_daemon = g_strdup ("dbus-daemon");
224
225   address = spawn_dbus_daemon (dbus_daemon, arg, &f->daemon_pid);
226
227   g_free (dbus_daemon);
228   g_free (arg);
229
230   f->left_conn = connect_to_bus (address);
231   f->right_conn = connect_to_bus (address);
232   g_free (address);
233 }
234
235 static void
236 add_echo_filter (Fixture *f)
237 {
238   if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL))
239     g_error ("OOM");
240
241   f->right_conn_echo = TRUE;
242 }
243
244 static void
245 pc_count (DBusPendingCall *pc,
246     void *data)
247 {
248   guint *received_p = data;
249
250   (*received_p)++;
251 }
252
253 static void
254 test_echo (Fixture *f,
255     gconstpointer context)
256 {
257   const Config *config = context;
258   guint count = 2000;
259   guint sent;
260   guint received = 0;
261   double elapsed;
262
263   if (f->skip)
264     return;
265
266   if (config != NULL && config->bug_ref != NULL)
267     g_test_bug (config->bug_ref);
268
269   if (g_test_perf ())
270     count = 100000;
271
272   if (config != NULL)
273     count = MAX (config->min_messages, count);
274
275   add_echo_filter (f);
276
277   g_test_timer_start ();
278
279   for (sent = 0; sent < count; sent++)
280     {
281       DBusMessage *m = dbus_message_new_method_call (
282           dbus_bus_get_unique_name (f->right_conn), "/",
283           "com.example", "Spam");
284       DBusPendingCall *pc;
285
286       if (m == NULL)
287         g_error ("OOM");
288
289       if (!dbus_connection_send_with_reply (f->left_conn, m, &pc,
290                                             DBUS_TIMEOUT_INFINITE) ||
291           pc == NULL)
292         g_error ("OOM");
293
294       if (dbus_pending_call_get_completed (pc))
295         pc_count (pc, &received);
296       else if (!dbus_pending_call_set_notify (pc, pc_count, &received,
297             NULL))
298         g_error ("OOM");
299
300       dbus_pending_call_unref (pc);
301       dbus_message_unref (m);
302     }
303
304   while (received < count)
305     g_main_context_iteration (NULL, TRUE);
306
307   elapsed = g_test_timer_elapsed ();
308
309   g_test_maximized_result (count / elapsed, "%u messages / %f seconds",
310       count, elapsed);
311 }
312
313 static void
314 teardown (Fixture *f,
315     gconstpointer context G_GNUC_UNUSED)
316 {
317   dbus_error_free (&f->e);
318   g_clear_error (&f->ge);
319
320   if (f->left_conn != NULL)
321     {
322       dbus_connection_close (f->left_conn);
323       dbus_connection_unref (f->left_conn);
324       f->left_conn = NULL;
325     }
326
327   if (f->right_conn != NULL)
328     {
329       if (f->right_conn_echo)
330         {
331           dbus_connection_remove_filter (f->right_conn, echo_filter, NULL);
332           f->right_conn_echo = FALSE;
333         }
334
335       dbus_connection_close (f->right_conn);
336       dbus_connection_unref (f->right_conn);
337       f->right_conn = NULL;
338     }
339
340   if (f->daemon_pid != 0)
341     {
342 #ifdef DBUS_WIN
343       TerminateProcess (f->daemon_pid, 1);
344 #else
345       kill (f->daemon_pid, SIGTERM);
346 #endif
347
348       g_spawn_close_pid (f->daemon_pid);
349       f->daemon_pid = 0;
350     }
351 }
352
353 static Config limited_config = {
354     "34393", 10000, "valid-config-files/incoming-limit.conf"
355 };
356
357 int
358 main (int argc,
359     char **argv)
360 {
361   g_test_init (&argc, &argv, NULL);
362   g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
363
364   g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown);
365   g_test_add ("/echo/limited", Fixture, &limited_config,
366       setup, test_echo, teardown);
367
368   return g_test_run ();
369 }