From 7608314cf735f540ed025a5649c2510b98dbff12 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 11 Mar 2011 13:13:17 +0000 Subject: [PATCH 08/19] Add a simple integration test for dbus-daemon This just pushes 2000 messages (or 100000 in performance-testing mode) through the dbus-daemon, to an echo service and back. --- test/.gitignore | 1 + test/Makefile.am | 8 ++ test/dbus-daemon.c | 320 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+), 0 deletions(-) create mode 100644 test/dbus-daemon.c diff --git a/test/.gitignore b/test/.gitignore index b329d58..d63568d 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -25,3 +25,4 @@ test-shell-service test-names test-loopback test-relay +test-dbus-daemon diff --git a/test/Makefile.am b/test/Makefile.am index ce05ec4..97b080c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -73,6 +73,7 @@ spawn_test_LDFLAGS=@R_DYNAMIC_LDFLAG@ EXTRA_DIST= modular_tests = \ + test-dbus-daemon \ test-loopback \ test-relay @@ -100,6 +101,13 @@ test_relay_LDADD = $(top_builddir)/dbus/libdbus-1.la \ $(GLIB_LIBS) \ $(DBUS_GLIB_LIBS) +test_dbus_daemon_SOURCES = dbus-daemon.c +test_dbus_daemon_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS) +test_dbus_daemon_LDFLAGS = @R_DYNAMIC_LDFLAG@ +test_dbus_daemon_LDADD = $(top_builddir)/dbus/libdbus-1.la \ + $(GLIB_LIBS) \ + $(DBUS_GLIB_LIBS) + if DBUS_ENABLE_MODULAR_TESTS noinst_PROGRAMS += $(modular_tests) TESTS += $(modular_tests) diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c new file mode 100644 index 0000000..04955e0 --- /dev/null +++ b/test/dbus-daemon.c @@ -0,0 +1,320 @@ +/* Integration tests for the dbus-daemon + * + * Author: Simon McVittie + * Copyright © 2010 Nokia Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include + +#include +#include + +#include + +#ifdef DBUS_WIN +# include +#else +# include +# include +#endif + +typedef struct { + DBusError e; + GError *ge; + + gint daemon_pid; + + DBusConnection *left_conn; + + DBusConnection *right_conn; + gboolean right_conn_echo; +} Fixture; + +#define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__) +static void +_assert_no_error (const DBusError *e, + const char *file, + int line) +{ + if (G_UNLIKELY (dbus_error_is_set (e))) + g_error ("%s:%d: expected success but got error: %s: %s", + file, line, e->name, e->message); +} + +static gchar * +spawn_dbus_daemon (gchar *binary, + gchar *configuration, + gint *daemon_pid) +{ + GError *error = NULL; + GString *address; + gint address_fd; + gchar *argv[] = { + binary, + configuration, + "--nofork", + "--print-address=1", /* stdout */ + NULL + }; + + g_spawn_async_with_pipes (NULL, /* working directory */ + argv, + NULL, /* envp */ + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, /* child_setup */ + NULL, /* user data */ + daemon_pid, + NULL, /* child's stdin = /dev/null */ + &address_fd, + NULL, /* child's stderr = our stderr */ + &error); + g_assert_no_error (error); + + address = g_string_new (NULL); + + /* polling until the dbus-daemon writes out its address is a bit stupid, + * but at least it's simple, unlike dbus-launch... in principle we could + * use select() here, but life's too short */ + while (1) + { + gssize bytes; + gchar buf[4096]; + gchar *newline; + + bytes = read (address_fd, buf, sizeof (buf)); + + if (bytes > 0) + g_string_append_len (address, buf, bytes); + + newline = strchr (address->str, '\n'); + + if (newline != NULL) + { + g_string_truncate (address, newline - address->str); + break; + } + + g_usleep (G_USEC_PER_SEC / 10); + } + + return g_string_free (address, FALSE); +} + +static DBusConnection * +connect_to_bus (const gchar *address) +{ + DBusConnection *conn; + DBusError error = DBUS_ERROR_INIT; + dbus_bool_t ok; + + conn = dbus_connection_open_private (address, &error); + assert_no_error (&error); + g_assert (conn != NULL); + + ok = dbus_bus_register (conn, &error); + assert_no_error (&error); + g_assert (ok); + g_assert (dbus_bus_get_unique_name (conn) != NULL); + + dbus_connection_setup_with_g_main (conn, NULL); + return conn; +} + +static DBusHandlerResult +echo_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + DBusMessage *reply; + DBusError error = DBUS_ERROR_INIT; + int *sleep_ms = user_data; + + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + reply = dbus_message_new_method_return (message); + + if (reply == NULL) + g_error ("OOM"); + + if (!dbus_connection_send (connection, reply, NULL)) + g_error ("OOM"); + + dbus_message_unref (reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void +setup (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + gchar *dbus_daemon; + gchar *config; + gchar *address; + + f->ge = NULL; + dbus_error_init (&f->e); + + g_assert (g_getenv ("DBUS_TEST_DAEMON") != NULL); + + dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON")); + + if (g_getenv ("DBUS_TEST_USE_INSTALLED") != NULL) + { + config = g_strdup ("--session"); + } + else + { + g_assert (g_getenv ("DBUS_TEST_DATA") != NULL); + config = g_strdup_printf ( + "--config-file=%s/valid-config-files/session.conf", + g_getenv ("DBUS_TEST_DATA")); + } + + address = spawn_dbus_daemon (dbus_daemon, config, &f->daemon_pid); + + g_free (dbus_daemon); + g_free (config); + + f->left_conn = connect_to_bus (address); + f->right_conn = connect_to_bus (address); + g_free (address); +} + +static void +add_echo_filter (Fixture *f) +{ + if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL)) + g_error ("OOM"); + + f->right_conn_echo = TRUE; +} + +static void +pc_count (DBusPendingCall *pc, + void *data) +{ + guint *received_p = data; + + (*received_p)++; +} + +static void +test_echo (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + guint count = 2000; + guint sent; + guint received = 0; + double elapsed; + + if (g_test_perf ()) + count = 100000; + + add_echo_filter (f); + + g_test_timer_start (); + + for (sent = 0; sent < count; sent++) + { + DBusMessage *m = dbus_message_new_method_call ( + dbus_bus_get_unique_name (f->right_conn), "/", + "com.example", "Spam"); + DBusPendingCall *pc; + + if (m == NULL) + g_error ("OOM"); + + if (!dbus_connection_send_with_reply (f->left_conn, m, &pc, 0x7FFFFFFF) + || pc == NULL) + g_error ("OOM"); + + if (dbus_pending_call_get_completed (pc)) + pc_count (pc, &received); + else if (!dbus_pending_call_set_notify (pc, pc_count, &received, + NULL)) + g_error ("OOM"); + + dbus_pending_call_unref (pc); + dbus_message_unref (m); + } + + while (received < count) + g_main_context_iteration (NULL, TRUE); + + elapsed = g_test_timer_elapsed (); + + g_test_maximized_result (count / elapsed, "%u messages / %f seconds", + count, elapsed); +} + +static void +teardown (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + dbus_error_free (&f->e); + g_clear_error (&f->ge); + + if (f->left_conn != NULL) + { + dbus_connection_close (f->left_conn); + dbus_connection_unref (f->left_conn); + f->left_conn = NULL; + } + + if (f->right_conn != NULL) + { + if (f->right_conn_echo) + { + dbus_connection_remove_filter (f->right_conn, echo_filter, NULL); + f->right_conn_echo = FALSE; + } + + dbus_connection_close (f->right_conn); + dbus_connection_unref (f->right_conn); + f->right_conn = NULL; + } + +#ifdef DBUS_WIN + TerminateProcess (f->daemon_pid, 1); +#else + kill (f->daemon_pid, SIGTERM); +#endif + + g_spawn_close_pid (f->daemon_pid); +} + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown); + + return g_test_run (); +} -- 1.7.4.1