1 /* Integration tests for the dbus-daemon 2 * 3 * Author: Simon McVittie <simon.mcvittie (at) 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 <io.h> 38 # include <windows.h> 39 #else 40 # include <signal.h> 41 # include <unistd.h> 42 #endif 43 44 typedef struct { 45 gboolean skip; 46 47 DBusError e; 48 GError *ge; 49 50 GPid daemon_pid; 51 52 DBusConnection *left_conn; 53 54 DBusConnection *right_conn; 55 gboolean right_conn_echo; 56 } Fixture; 57 58 #define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__) 59 static void 60 _assert_no_error (const DBusError *e, 61 const char *file, 62 int line) 63 { 64 if (G_UNLIKELY (dbus_error_is_set (e))) 65 g_error ("%s:%d: expected success but got error: %s: %s", 66 file, line, e->name, e->message); 67 } 68 69 static gchar * 70 spawn_dbus_daemon (gchar *binary, 71 gchar *configuration, 72 GPid *daemon_pid) 73 { 74 GError *error = NULL; 75 GString *address; 76 gint address_fd; 77 gchar *argv[] = { 78 binary, 79 configuration, 80 "--nofork", 81 "--print-address=1", /* stdout */ 82 NULL 83 }; 84 85 g_spawn_async_with_pipes (NULL, /* working directory */ 86 argv, 87 NULL, /* envp */ 88 G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, 89 NULL, /* child_setup */ 90 NULL, /* user data */ 91 daemon_pid, 92 NULL, /* child's stdin = /dev/null */ 93 &address_fd, 94 NULL, /* child's stderr = our stderr */ 95 &error); 96 g_assert_no_error (error); 97 98 address = g_string_new (NULL); 99 100 /* polling until the dbus-daemon writes out its address is a bit stupid, 101 * but at least it's simple, unlike dbus-launch... in principle we could 102 * use select() here, but life's too short */ 103 while (1) 104 { 105 gssize bytes; 106 gchar buf[4096]; 107 gchar *newline; 108 109 bytes = read (address_fd, buf, sizeof (buf)); 110 111 if (bytes > 0) 112 g_string_append_len (address, buf, bytes); 113 114 newline = strchr (address->str, '\n'); 115 116 if (newline != NULL) 117 { 118 g_string_truncate (address, newline - address->str); 119 break; 120 } 121 122 g_usleep (G_USEC_PER_SEC / 10); 123 } 124 125 return g_string_free (address, FALSE); 126 } 127 128 static DBusConnection * 129 connect_to_bus (const gchar *address) 130 { 131 DBusConnection *conn; 132 DBusError error = DBUS_ERROR_INIT; 133 dbus_bool_t ok; 134 135 conn = dbus_connection_open_private (address, &error); 136 assert_no_error (&error); 137 g_assert (conn != NULL); 138 139 ok = dbus_bus_register (conn, &error); 140 assert_no_error (&error); 141 g_assert (ok); 142 g_assert (dbus_bus_get_unique_name (conn) != NULL); 143 144 dbus_connection_setup_with_g_main (conn, NULL); 145 return conn; 146 } 147 148 static DBusHandlerResult 149 echo_filter (DBusConnection *connection, 150 DBusMessage *message, 151 void *user_data) 152 { 153 DBusMessage *reply; 154 155 if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL) 156 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 157 158 reply = dbus_message_new_method_return (message); 159 160 if (reply == NULL) 161 g_error ("OOM"); 162 163 if (!dbus_connection_send (connection, reply, NULL)) 164 g_error ("OOM"); 165 166 dbus_message_unref (reply); 167 168 return DBUS_HANDLER_RESULT_HANDLED; 169 } 170 171 typedef struct { 172 const char *bug_ref; 173 guint min_messages; 174 const char *config_file; 175 } Config; 176 177 static void 178 setup (Fixture *f, 179 gconstpointer context) 180 { 181 const Config *config = context; 182 gchar *dbus_daemon; 183 gchar *arg; 184 gchar *address; 185 186 f->ge = NULL; 187 dbus_error_init (&f->e); 188 189 if (config != NULL && config->config_file != NULL) 190 { 191 if (g_getenv ("DBUS_TEST_DATA") == NULL) 192 { 193 g_message ("SKIP: set DBUS_TEST_DATA to a directory containing %s", 194 config->config_file); 195 f->skip = TRUE; 196 return; 197 } 198 199 arg = g_strdup_printf ( 200 "--config-file=%s/%s", 201 g_getenv ("DBUS_TEST_DATA"), config->config_file); 202 } 203 else if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL) 204 { 205 arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf", 206 g_getenv ("DBUS_TEST_SYSCONFDIR")); 207 } 208 else if (g_getenv ("DBUS_TEST_DATA") != NULL) 209 { 210 arg = g_strdup_printf ( 211 "--config-file=%s/valid-config-files/session.conf", 212 g_getenv ("DBUS_TEST_DATA")); 213 } 214 else 215 { 216 arg = g_strdup ("--session"); 217 } 218 219 dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON")); 220 221 if (dbus_daemon == NULL) 222 dbus_daemon = g_strdup ("dbus-daemon"); 223 224 address = spawn_dbus_daemon (dbus_daemon, arg, &f->daemon_pid); 225 226 g_free (dbus_daemon); 227 g_free (arg); 228 229 f->left_conn = connect_to_bus (address); 230 f->right_conn = connect_to_bus (address); 231 g_free (address); 232 } 233 234 static void 235 add_echo_filter (Fixture *f) 236 { 237 if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL)) 238 g_error ("OOM"); 239 240 f->right_conn_echo = TRUE; 241 } 242 243 static void 244 pc_count (DBusPendingCall *pc, 245 void *data) 246 { 247 guint *received_p = data; 248 249 (*received_p)++; 250 } 251 252 static void 253 test_echo (Fixture *f, 254 gconstpointer context) 255 { 256 const Config *config = context; 257 guint count = 2000; 258 guint sent; 259 guint received = 0; 260 double elapsed; 261 262 if (f->skip) 263 return; 264 265 if (config != NULL && config->bug_ref != NULL) 266 g_test_bug (config->bug_ref); 267 268 if (g_test_perf ()) 269 count = 100000; 270 271 if (config != NULL) 272 count = MAX (config->min_messages, count); 273 274 add_echo_filter (f); 275 276 g_test_timer_start (); 277 278 for (sent = 0; sent < count; sent++) 279 { 280 DBusMessage *m = dbus_message_new_method_call ( 281 dbus_bus_get_unique_name (f->right_conn), "/", 282 "com.example", "Spam"); 283 DBusPendingCall *pc; 284 285 if (m == NULL) 286 g_error ("OOM"); 287 288 if (!dbus_connection_send_with_reply (f->left_conn, m, &pc, 289 DBUS_TIMEOUT_INFINITE) || 290 pc == NULL) 291 g_error ("OOM"); 292 293 if (dbus_pending_call_get_completed (pc)) 294 pc_count (pc, &received); 295 else if (!dbus_pending_call_set_notify (pc, pc_count, &received, 296 NULL)) 297 g_error ("OOM"); 298 299 dbus_pending_call_unref (pc); 300 dbus_message_unref (m); 301 } 302 303 while (received < count) 304 g_main_context_iteration (NULL, TRUE); 305 306 elapsed = g_test_timer_elapsed (); 307 308 g_test_maximized_result (count / elapsed, "%u messages / %f seconds", 309 count, elapsed); 310 } 311 312 static void 313 teardown (Fixture *f, 314 gconstpointer context G_GNUC_UNUSED) 315 { 316 dbus_error_free (&f->e); 317 g_clear_error (&f->ge); 318 319 if (f->left_conn != NULL) 320 { 321 dbus_connection_close (f->left_conn); 322 dbus_connection_unref (f->left_conn); 323 f->left_conn = NULL; 324 } 325 326 if (f->right_conn != NULL) 327 { 328 if (f->right_conn_echo) 329 { 330 dbus_connection_remove_filter (f->right_conn, echo_filter, NULL); 331 f->right_conn_echo = FALSE; 332 } 333 334 dbus_connection_close (f->right_conn); 335 dbus_connection_unref (f->right_conn); 336 f->right_conn = NULL; 337 } 338 339 if (f->daemon_pid != 0) 340 { 341 #ifdef DBUS_WIN 342 TerminateProcess (f->daemon_pid, 1); 343 #else 344 kill (f->daemon_pid, SIGTERM); 345 #endif 346 347 g_spawn_close_pid (f->daemon_pid); 348 f->daemon_pid = 0; 349 } 350 } 351 352 static Config limited_config = { 353 "34393", 10000, "valid-config-files/incoming-limit.conf" 354 }; 355 356 int 357 main (int argc, 358 char **argv) 359 { 360 g_test_init (&argc, &argv, NULL); 361 g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id="); 362 363 g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown); 364 g_test_add ("/echo/limited", Fixture, &limited_config, 365 setup, test_echo, teardown); 366 367 return g_test_run (); 368 } 369