summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2011-06-10 18:52:07 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2011-06-10 18:52:07 +0100
commitc83287898cc99d9dee75cf4211341be46b1ea39f (patch)
tree9fa61b30a919c67997b22dbbe698948b138dd70f /test
parent963a38d0b1eb17abadc5e83028f861b1c2be8d8a (diff)
parent5b68cf3ed9c7b655a6d4e971d17ffa8924c3fadc (diff)
downloaddbus-c83287898cc99d9dee75cf4211341be46b1ea39f.tar.gz
Merge branch 'dbus-1.4'
Diffstat (limited to 'test')
-rw-r--r--test/.gitignore5
-rw-r--r--test/Makefile.am87
-rw-r--r--test/corrupt.c362
-rw-r--r--test/dbus-daemon.c326
-rwxr-xr-xtest/dbus-test-runner43
-rw-r--r--test/loopback.c211
-rw-r--r--test/marshal.c258
-rw-r--r--test/relay.c318
8 files changed, 1608 insertions, 2 deletions
diff --git a/test/.gitignore b/test/.gitignore
index f7a65876..1337de64 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -15,6 +15,7 @@ unbase64
*.gcov
break-loader
spawn-test
+test-corrupt
test-exit
test-segfault
test-service
@@ -23,3 +24,7 @@ decode-gcov
shell-test
test-shell-service
test-names
+test-loopback
+test-relay
+test-dbus-daemon
+test-marshal
diff --git a/test/Makefile.am b/test/Makefile.am
index 9d0681b7..afa99dc1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -52,7 +52,8 @@ test_segfault_SOURCES = \
test_sleep_forever_SOURCES = \
test-sleep-forever.c
-# When any programs are not linked to libdbus-internal, fix this.
+# This assumes that most tests will be linked to libdbus-internal;
+# tests linked to only the public libdbus have their own CPPFLAGS.
AM_CPPFLAGS=-DDBUS_STATIC_BUILD
TEST_LIBS=$(top_builddir)/dbus/libdbus-internal.la $(DBUS_TEST_LIBS)
@@ -69,7 +70,89 @@ shell_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
spawn_test_LDADD=$(TEST_LIBS)
spawn_test_LDFLAGS=@R_DYNAMIC_LDFLAG@
-EXTRA_DIST=
+EXTRA_DIST = dbus-test-runner
+
+testexecdir = $(libdir)/dbus-1.0/test
+
+testexec_PROGRAMS =
+
+installable_tests = \
+ test-corrupt \
+ test-dbus-daemon \
+ test-loopback \
+ test-marshal \
+ test-relay \
+ $(NULL)
+
+installcheck_tests =
+installcheck_environment = \
+ DBUS_TEST_DAEMON=$(DESTDIR)$(DBUS_DAEMONDIR)/dbus-daemon$(EXEEXT) \
+ DBUS_TEST_SYSCONFDIR=$(DESTDIR)$(sysconfdir)
+
+TESTS_ENVIRONMENT = \
+ DBUS_TEST_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT) \
+ DBUS_TEST_DATA=@abs_top_builddir@/test/data
+
+test_corrupt_SOURCES = corrupt.c
+test_corrupt_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
+test_corrupt_LDFLAGS = @R_DYNAMIC_LDFLAG@
+test_corrupt_LDADD = $(top_builddir)/dbus/libdbus-1.la \
+ $(GLIB_LIBS) \
+ $(DBUS_GLIB_LIBS)
+
+test_loopback_SOURCES = loopback.c
+test_loopback_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
+test_loopback_LDFLAGS = @R_DYNAMIC_LDFLAG@
+test_loopback_LDADD = $(top_builddir)/dbus/libdbus-1.la \
+ $(GLIB_LIBS) \
+ $(DBUS_GLIB_LIBS)
+
+test_relay_SOURCES = relay.c
+test_relay_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
+test_relay_LDFLAGS = @R_DYNAMIC_LDFLAG@
+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)
+
+test_marshal_SOURCES = marshal.c
+test_marshal_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS)
+test_marshal_LDFLAGS = @R_DYNAMIC_LDFLAG@
+test_marshal_LDADD = $(top_builddir)/dbus/libdbus-1.la \
+ $(GLIB_LIBS) \
+ $(DBUS_GLIB_LIBS)
+
+if DBUS_ENABLE_MODULAR_TESTS
+TESTS += $(installable_tests)
+installcheck_tests += $(installable_tests)
+
+if DBUS_ENABLE_INSTALLED_TESTS
+ testexec_PROGRAMS += $(installable_tests)
+else !DBUS_ENABLE_INSTALLED_TESTS
+ testexec_PROGRAMS += $(installable_tests)
+endif !DBUS_ENABLE_INSTALLED_TESTS
+
+endif DBUS_ENABLE_MODULAR_TESTS
+
+# If we're installing the tests into a DESTDIR we can't run them
+# again using the installed copy, because we don't know how to
+# do a portable equivalent of setting LD_LIBRARY_PATH.
+installcheck-local:
+ $(MAKE) check-TESTS TESTS='$$(installcheck_tests)' \
+ TESTS_ENVIRONMENT='$$(installcheck_environment)'
+if DBUS_ENABLE_INSTALLED_TESTS
+ test -n "$(DESTDIR)" || \
+ $(installcheck_environment) \
+ $(srcdir)/dbus-test-runner \
+ $(testexecdir) \
+ $(testexec_PROGRAMS)
+endif DBUS_ENABLE_INSTALLED_TESTS
## keep these in creation order, i.e. uppermost dirs first
TESTDIRS= \
diff --git a/test/corrupt.c b/test/corrupt.c
new file mode 100644
index 00000000..ef9951af
--- /dev/null
+++ b/test/corrupt.c
@@ -0,0 +1,362 @@
+/* Regression test for being disconnected by a corrupt message (fd.o #15578)
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2010-2011 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 <config.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+typedef struct {
+ DBusError e;
+
+ DBusServer *server;
+ DBusConnection *server_conn;
+ /* queue of DBusMessage */
+ GQueue client_messages;
+
+ DBusConnection *client_conn;
+} Fixture;
+
+static void
+assert_no_error (const DBusError *e)
+{
+ if (G_UNLIKELY (dbus_error_is_set (e)))
+ g_error ("expected success but got error: %s: %s", e->name, e->message);
+}
+
+static DBusHandlerResult
+client_message_cb (DBusConnection *client_conn,
+ DBusMessage *message,
+ void *data)
+{
+ Fixture *f = data;
+
+ g_assert (client_conn == f->client_conn);
+ g_queue_push_tail (&f->client_messages, dbus_message_ref (message));
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+new_conn_cb (DBusServer *server,
+ DBusConnection *server_conn,
+ void *data)
+{
+ Fixture *f = data;
+
+ g_assert (f->server_conn == NULL);
+ f->server_conn = dbus_connection_ref (server_conn);
+ dbus_connection_setup_with_g_main (server_conn, NULL);
+}
+
+static void
+setup (Fixture *f,
+ gconstpointer addr)
+{
+ dbus_error_init (&f->e);
+ g_queue_init (&f->client_messages);
+
+ f->server = dbus_server_listen (addr, &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->server != NULL);
+
+ dbus_server_set_new_connection_function (f->server,
+ new_conn_cb, f, NULL);
+ dbus_server_setup_with_g_main (f->server, NULL);
+}
+
+static void
+test_connect (Fixture *f,
+ gconstpointer addr G_GNUC_UNUSED)
+{
+ dbus_bool_t have_mem;
+
+ g_assert (f->server_conn == NULL);
+
+ f->client_conn = dbus_connection_open_private (
+ dbus_server_get_address (f->server), &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->client_conn != NULL);
+ dbus_connection_setup_with_g_main (f->client_conn, NULL);
+
+ while (f->server_conn == NULL)
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ have_mem = dbus_connection_add_filter (f->client_conn,
+ client_message_cb, f, NULL);
+ g_assert (have_mem);
+}
+
+static void
+test_message (Fixture *f,
+ gconstpointer addr)
+{
+ dbus_bool_t have_mem;
+ dbus_uint32_t serial;
+ DBusMessage *outgoing, *incoming;
+
+ test_connect (f, addr);
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing != NULL);
+
+ have_mem = dbus_connection_send (f->server_conn, outgoing, &serial);
+ g_assert (have_mem);
+ g_assert (serial != 0);
+
+ while (g_queue_is_empty (&f->client_messages))
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ g_assert_cmpuint (g_queue_get_length (&f->client_messages), ==, 1);
+
+ incoming = g_queue_pop_head (&f->client_messages);
+
+ g_assert (!dbus_message_contains_unix_fds (incoming));
+ g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
+ "com.example.Hello");
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
+ g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
+ g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
+ g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
+
+ dbus_message_unref (incoming);
+
+ dbus_message_unref (outgoing);
+}
+
+/* Enough bytes for it to be obvious that this connection is broken */
+#define CORRUPT_LEN 1024
+
+/* All-zero is not a valid D-Bus message header - for a start, this is
+ * protocol version 1, not 0 */
+static const gchar not_a_dbus_message[CORRUPT_LEN] = { 0 };
+
+static void
+test_corrupt (Fixture *f,
+ gconstpointer addr)
+{
+ GSocket *socket;
+ GError *gerror = NULL;
+ int fd;
+ gssize len, total_sent;
+ DBusMessage *incoming;
+
+ test_message (f, addr);
+
+ dbus_connection_flush (f->server_conn);
+
+ /* OK, now the connection is working, let's break it! Don't try this
+ * at home; splicing arbitrary bytes into the middle of the stream is
+ * specifically documented as not a valid thing to do. Who'd have thought? */
+ if (!dbus_connection_get_socket (f->server_conn, &fd))
+ g_error ("failed to steal fd from server connection");
+
+ socket = g_socket_new_from_fd (fd, &gerror);
+ g_assert_no_error (gerror);
+ g_assert (socket != NULL);
+
+ total_sent = 0;
+
+ while (total_sent < CORRUPT_LEN)
+ {
+ len = g_socket_send_with_blocking (socket,
+ not_a_dbus_message + total_sent, CORRUPT_LEN - total_sent,
+ TRUE, NULL, &gerror);
+ g_assert_no_error (gerror);
+ g_assert (len >= 0);
+ total_sent += len;
+ }
+
+ /* Now spin on the client connection: the server just sent it complete
+ * rubbish, so it should disconnect */
+ while (g_queue_is_empty (&f->client_messages))
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ incoming = g_queue_pop_head (&f->client_messages);
+
+ g_assert (!dbus_message_contains_unix_fds (incoming));
+ g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
+ "org.freedesktop.DBus.Local");
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Disconnected");
+ g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
+ g_assert_cmpstr (dbus_message_get_path (incoming), ==,
+ "/org/freedesktop/DBus/Local");
+
+ dbus_message_unref (incoming);
+}
+
+static void
+test_byte_order (Fixture *f,
+ gconstpointer addr)
+{
+ GSocket *socket;
+ GError *gerror = NULL;
+ int fd;
+ char *blob;
+ const gchar *arg = not_a_dbus_message;
+ const gchar * const *args = &arg;
+ int blob_len, len, total_sent;
+ DBusMessage *message;
+ dbus_bool_t mem;
+
+ test_message (f, addr);
+
+ message = dbus_message_new_signal ("/", "a.b", "c");
+ g_assert (message != NULL);
+ /* Append 0xFF bytes, so that the length of the body when byte-swapped
+ * is 0xFF000000, which is invalid */
+ mem = dbus_message_append_args (message,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &args, 0xFF,
+ DBUS_TYPE_INVALID);
+ g_assert (mem);
+ mem = dbus_message_marshal (message, &blob, &blob_len);
+ g_assert (mem);
+ g_assert_cmpuint (blob_len, >, 0xFF);
+ g_assert (blob != NULL);
+
+ dbus_message_unref (message);
+
+ /* Break the message by changing its claimed byte order, without actually
+ * byteswapping anything. We happen to know that byte order is the first
+ * byte. */
+ if (blob[0] == 'B')
+ blob[0] = 'l';
+ else
+ blob[0] = 'B';
+
+ /* OK, now the connection is working, let's break it */
+
+ dbus_connection_flush (f->server_conn);
+
+ if (!dbus_connection_get_socket (f->server_conn, &fd))
+ g_error ("failed to steal fd from server connection");
+
+ socket = g_socket_new_from_fd (fd, &gerror);
+ g_assert_no_error (gerror);
+ g_assert (socket != NULL);
+
+ total_sent = 0;
+
+ while (total_sent < blob_len)
+ {
+ len = g_socket_send_with_blocking (socket, blob + total_sent,
+ blob_len - total_sent, TRUE, NULL, &gerror);
+ g_assert_no_error (gerror);
+ g_assert (len >= 0);
+ total_sent += len;
+ }
+
+ dbus_free (blob);
+
+ /* Now spin on the client connection: the server just sent it a faulty
+ * message, so it should disconnect */
+ while (g_queue_is_empty (&f->client_messages))
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ message = g_queue_pop_head (&f->client_messages);
+
+ g_assert (!dbus_message_contains_unix_fds (message));
+ g_assert_cmpstr (dbus_message_get_destination (message), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (message), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (message), ==,
+ "org.freedesktop.DBus.Local");
+ g_assert_cmpstr (dbus_message_get_member (message), ==, "Disconnected");
+ g_assert_cmpstr (dbus_message_get_sender (message), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_signature (message), ==, "");
+ g_assert_cmpstr (dbus_message_get_path (message), ==,
+ "/org/freedesktop/DBus/Local");
+
+ dbus_message_unref (message);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer addr G_GNUC_UNUSED)
+{
+ if (f->client_conn != NULL)
+ {
+ dbus_connection_close (f->client_conn);
+ dbus_connection_unref (f->client_conn);
+ f->client_conn = NULL;
+ }
+
+ if (f->server_conn != NULL)
+ {
+ dbus_connection_close (f->server_conn);
+ dbus_connection_unref (f->server_conn);
+ f->server_conn = NULL;
+ }
+
+ if (f->server != NULL)
+ {
+ dbus_server_disconnect (f->server);
+ dbus_server_unref (f->server);
+ f->server = NULL;
+ }
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+ g_type_init ();
+
+ g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
+ test_corrupt, teardown);
+
+#ifdef DBUS_UNIX
+ g_test_add ("/corrupt/unix", Fixture, "unix:tmpdir=/tmp", setup,
+ test_corrupt, teardown);
+#endif
+
+ g_test_add ("/corrupt/byte-order/tcp", Fixture, "tcp:host=127.0.0.1", setup,
+ test_byte_order, teardown);
+
+ return g_test_run ();
+}
diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c
new file mode 100644
index 00000000..ed10d098
--- /dev/null
+++ b/test/dbus-daemon.c
@@ -0,0 +1,326 @@
+/* Integration tests for the dbus-daemon
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2010-2011 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 <config.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <string.h>
+
+#ifdef DBUS_WIN
+# include <windows.h>
+#else
+# include <signal.h>
+# include <unistd.h>
+#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 | G_SPAWN_SEARCH_PATH,
+ 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);
+
+ dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
+
+ if (dbus_daemon == NULL)
+ dbus_daemon = g_strdup ("dbus-daemon");
+
+ if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
+ {
+ config = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
+ g_getenv ("DBUS_TEST_SYSCONFDIR"));
+ }
+ else if (g_getenv ("DBUS_TEST_DATA") != NULL)
+ {
+ config = g_strdup_printf (
+ "--config-file=%s/valid-config-files/session.conf",
+ g_getenv ("DBUS_TEST_DATA"));
+ }
+ else
+ {
+ config = g_strdup ("--session");
+ }
+
+ 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,
+ DBUS_TIMEOUT_INFINITE) ||
+ 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 ();
+}
diff --git a/test/dbus-test-runner b/test/dbus-test-runner
new file mode 100755
index 00000000..a3dc3960
--- /dev/null
+++ b/test/dbus-test-runner
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+set -e
+
+dir="$1"
+shift
+
+if ! test -d "$dir"; then
+ echo "Usage: dbus-test-runner directory [executable...]"
+ exit 0
+fi
+
+passed=0
+failed=0
+skipped=0
+
+for prog in "$@"; do
+ e=0
+ "$dir/$prog" || e=$?
+ case $e in
+ (0)
+ echo "PASS: $prog"
+ passed=`expr $passed + 1`
+ ;;
+ (77)
+ echo "SKIP: $prog"
+ skipped=`expr $skipped + 1`
+ ;;
+ (*)
+ echo "FAIL: $prog"
+ failed=`expr $failed + 1`
+ ;;
+ esac
+done
+
+if test $failed = 0; then
+ # avoid saying "FAIL", to make it easy to grep results!
+ echo "PASSED $passed / SKIPPED $skipped"
+ exit 0
+else
+ echo "PASSED $passed / FAILED $failed / SKIPPED $skipped"
+ exit 1
+fi
diff --git a/test/loopback.c b/test/loopback.c
new file mode 100644
index 00000000..d0d69c82
--- /dev/null
+++ b/test/loopback.c
@@ -0,0 +1,211 @@
+/* Simple sanity-check for loopback through TCP and Unix sockets.
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2010-2011 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 <config.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+typedef struct {
+ DBusError e;
+
+ DBusServer *server;
+ DBusConnection *server_conn;
+ /* queue of DBusMessage */
+ GQueue server_messages;
+
+ DBusConnection *client_conn;
+} Fixture;
+
+static void
+assert_no_error (const DBusError *e)
+{
+ if (G_UNLIKELY (dbus_error_is_set (e)))
+ g_error ("expected success but got error: %s: %s", e->name, e->message);
+}
+
+static DBusHandlerResult
+server_message_cb (DBusConnection *server_conn,
+ DBusMessage *message,
+ void *data)
+{
+ Fixture *f = data;
+
+ g_assert (server_conn == f->server_conn);
+ g_queue_push_tail (&f->server_messages, dbus_message_ref (message));
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+new_conn_cb (DBusServer *server,
+ DBusConnection *server_conn,
+ void *data)
+{
+ Fixture *f = data;
+ dbus_bool_t have_mem;
+
+ g_assert (f->server_conn == NULL);
+ f->server_conn = dbus_connection_ref (server_conn);
+ dbus_connection_setup_with_g_main (server_conn, NULL);
+
+ have_mem = dbus_connection_add_filter (server_conn,
+ server_message_cb, f, NULL);
+ g_assert (have_mem);
+}
+
+static void
+setup (Fixture *f,
+ gconstpointer addr)
+{
+ dbus_error_init (&f->e);
+ g_queue_init (&f->server_messages);
+
+ f->server = dbus_server_listen (addr, &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->server != NULL);
+
+ dbus_server_set_new_connection_function (f->server,
+ new_conn_cb, f, NULL);
+ dbus_server_setup_with_g_main (f->server, NULL);
+}
+
+static void
+test_connect (Fixture *f,
+ gconstpointer addr G_GNUC_UNUSED)
+{
+ g_assert (f->server_conn == NULL);
+
+ f->client_conn = dbus_connection_open_private (
+ dbus_server_get_address (f->server), &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->client_conn != NULL);
+ dbus_connection_setup_with_g_main (f->client_conn, NULL);
+
+ while (f->server_conn == NULL)
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+}
+
+static void
+test_message (Fixture *f,
+ gconstpointer addr)
+{
+ dbus_bool_t have_mem;
+ dbus_uint32_t serial;
+ DBusMessage *outgoing, *incoming;
+
+ test_connect (f, addr);
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing != NULL);
+
+ have_mem = dbus_connection_send (f->client_conn, outgoing, &serial);
+ g_assert (have_mem);
+ g_assert (serial != 0);
+
+ while (g_queue_is_empty (&f->server_messages))
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ g_assert_cmpuint (g_queue_get_length (&f->server_messages), ==, 1);
+
+ incoming = g_queue_pop_head (&f->server_messages);
+
+ g_assert (!dbus_message_contains_unix_fds (incoming));
+ g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
+ "com.example.Hello");
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
+ g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_signature (incoming), ==, "");
+ g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
+ g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
+
+ dbus_message_unref (incoming);
+
+ dbus_message_unref (outgoing);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer addr G_GNUC_UNUSED)
+{
+ if (f->client_conn != NULL)
+ {
+ dbus_connection_close (f->client_conn);
+ dbus_connection_unref (f->client_conn);
+ f->client_conn = NULL;
+ }
+
+ if (f->server_conn != NULL)
+ {
+ dbus_connection_close (f->server_conn);
+ dbus_connection_unref (f->server_conn);
+ f->server_conn = NULL;
+ }
+
+ if (f->server != NULL)
+ {
+ dbus_server_disconnect (f->server);
+ dbus_server_unref (f->server);
+ f->server = NULL;
+ }
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/connect/tcp", Fixture, "tcp:host=127.0.0.1", setup,
+ test_connect, teardown);
+ g_test_add ("/message/tcp", Fixture, "tcp:host=127.0.0.1", setup,
+ test_message, teardown);
+
+ g_test_add ("/connect/nonce-tcp", Fixture, "nonce-tcp:host=127.0.0.1", setup,
+ test_connect, teardown);
+ g_test_add ("/message/nonce-tcp", Fixture, "nonce-tcp:host=127.0.0.1", setup,
+ test_message, teardown);
+
+#ifdef DBUS_UNIX
+ g_test_add ("/connect/unix", Fixture, "unix:tmpdir=/tmp", setup,
+ test_connect, teardown);
+ g_test_add ("/message/unix", Fixture, "unix:tmpdir=/tmp", setup,
+ test_message, teardown);
+#endif
+
+ return g_test_run ();
+}
diff --git a/test/marshal.c b/test/marshal.c
new file mode 100644
index 00000000..4cee9412
--- /dev/null
+++ b/test/marshal.c
@@ -0,0 +1,258 @@
+/* Simple sanity-check for D-Bus message serialization.
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2010-2011 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 <config.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+typedef struct {
+ DBusError e;
+} Fixture;
+
+static void
+assert_no_error (const DBusError *e)
+{
+ if (G_UNLIKELY (dbus_error_is_set (e)))
+ g_error ("expected success but got error: %s: %s", e->name, e->message);
+}
+
+static void
+setup (Fixture *f,
+ gconstpointer arg G_GNUC_UNUSED)
+{
+ dbus_error_init (&f->e);
+}
+
+/* this is meant to be obviously correct, not efficient! */
+static guint32
+get_uint32 (const gchar *blob,
+ gsize offset,
+ char endian)
+{
+ if (endian == 'l')
+ {
+ return
+ blob[offset] |
+ (blob[offset + 1] << 8) |
+ (blob[offset + 2] << 16) |
+ (blob[offset + 3] << 24);
+ }
+ else if (endian == 'B')
+ {
+ return
+ (blob[offset] << 24) |
+ (blob[offset + 1] << 16) |
+ (blob[offset + 2] << 8) |
+ blob[offset + 3];
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+}
+
+#define BLOB_LENGTH (sizeof (le_blob) - 1)
+#define OFFSET_BODY_LENGTH (4)
+#define OFFSET_SERIAL (8)
+
+const gchar le_blob[] =
+ /* byte 0 */
+ /* yyyyuu fixed headers */
+ "l" /* little-endian */
+ "\2" /* reply (which is the simplest message) */
+ "\2" /* no auto-starting */
+ "\1" /* D-Bus version = 1 */
+ /* byte 4 */
+ "\4\0\0\0" /* bytes in body = 4 */
+ /* byte 8 */
+ "\x78\x56\x34\x12" /* serial number = 0x12345678 */
+ /* byte 12 */
+ /* a(uv) variable headers start here */
+ "\x0f\0\0\0" /* bytes in array of variable headers = 15 */
+ /* pad to 8-byte boundary = nothing */
+ /* byte 16 */
+ "\5" /* in reply to: */
+ "\1u\0" /* variant signature = u */
+ /* pad to 4-byte boundary = nothing */
+ "\x12\xef\xcd\xab" /* 0xabcdef12 */
+ /* pad to 8-byte boundary = nothing */
+ /* byte 24 */
+ "\x08" /* signature: */
+ "\1g\0" /* variant signature = g */
+ "\1u\0" /* 1 byte, u, NUL (no alignment needed) */
+ "\0" /* pad to 8-byte boundary for body */
+ /* body; byte 32 */
+ "\xef\xbe\xad\xde" /* 0xdeadbeef */
+ ;
+
+const gchar be_blob[] =
+ /* byte 0 */
+ /* yyyyuu fixed headers */
+ "B" /* big-endian */
+ "\2" /* reply (which is the simplest message) */
+ "\2" /* no auto-starting */
+ "\1" /* D-Bus version = 1 */
+ /* byte 4 */
+ "\0\0\0\4" /* bytes in body = 4 */
+ /* byte 8 */
+ "\x12\x34\x56\x78" /* serial number = 0x12345678 */
+ /* byte 12 */
+ /* a(uv) variable headers start here */
+ "\0\0\0\x0f" /* bytes in array of variable headers = 15 */
+ /* pad to 8-byte boundary = nothing */
+ /* byte 16 */
+ "\5" /* in reply to: */
+ "\1u\0" /* variant signature = u */
+ /* pad to 4-byte boundary = nothing */
+ "\xab\xcd\xef\x12" /* 0xabcdef12 */
+ /* pad to 8-byte boundary = nothing */
+ /* byte 24 */
+ "\x08" /* signature: */
+ "\1g\0" /* variant signature = g */
+ "\1u\0" /* 1 byte, u, NUL (no alignment needed) */
+ "\0" /* pad to 8-byte boundary for body */
+ /* body; byte 32 */
+ "\xde\xad\xbe\xef" /* 0xdeadbeef */
+ ;
+
+static void
+test_endian (Fixture *f,
+ gconstpointer arg)
+{
+ const gchar *blob = arg;
+ const gchar *native_blob;
+ char *output;
+ DBusMessage *m;
+ int len;
+ dbus_uint32_t u;
+ dbus_bool_t ok;
+
+ g_assert_cmpuint ((guint) sizeof (le_blob), ==, (guint) sizeof (be_blob));
+
+ g_assert_cmpuint (get_uint32 (blob, OFFSET_BODY_LENGTH, blob[0]), ==, 4);
+ g_assert_cmpuint (get_uint32 (blob, OFFSET_SERIAL, blob[0]), ==,
+ 0x12345678u);
+
+ len = dbus_message_demarshal_bytes_needed (blob, sizeof (le_blob));
+ /* everything in the string except the implicit "\0" at the end is part of
+ * the message */
+ g_assert_cmpint (len, ==, BLOB_LENGTH);
+
+ m = dbus_message_demarshal (blob, sizeof (le_blob), &f->e);
+ assert_no_error (&f->e);
+ g_assert (m != NULL);
+
+ g_assert_cmpuint (dbus_message_get_serial (m), ==, 0x12345678u);
+ g_assert_cmpuint (dbus_message_get_reply_serial (m), ==, 0xabcdef12u);
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, "u");
+
+ /* Implementation detail: appending to the message results in it being
+ * byteswapped into compiler byte order, which exposed a bug in libdbus,
+ * fd.o #38120. (If that changes, this test might not exercise that
+ * particular bug but will still be valid.) */
+ u = 0xdecafbadu;
+ ok = dbus_message_append_args (m,
+ DBUS_TYPE_UINT32, &u,
+ DBUS_TYPE_INVALID);
+ g_assert (ok);
+
+ dbus_message_marshal (m, &output, &len);
+
+ g_assert (output[0] == 'l' || output[0] == 'B');
+ /* the single-byte fields are unaffected, even if the endianness was
+ * swapped */
+ g_assert_cmpint (output[1], ==, blob[1]);
+ g_assert_cmpint (output[2], ==, blob[2]);
+ g_assert_cmpint (output[3], ==, blob[3]);
+ /* the length and serial are in the new endianness, the length has expanded
+ * to 8, and the serial is correct */
+ g_assert_cmpuint (get_uint32 (output, OFFSET_BODY_LENGTH, output[0]), ==, 8);
+ g_assert_cmpuint (get_uint32 (output, OFFSET_SERIAL, output[0]), ==,
+ 0x12345678u);
+ /* the second "u" in the signature replaced a padding byte, so only
+ * the length of the body changed */
+ g_assert_cmpint (len, ==, BLOB_LENGTH + 4);
+}
+
+static void
+test_needed (Fixture *f,
+ gconstpointer arg)
+{
+ const gchar *blob = arg;
+
+ /* We need at least 16 bytes to know how long the message is - that's just
+ * a fact of the D-Bus protocol. */
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, 0), ==, 0);
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, 15), ==, 0);
+ /* This is enough that we should be able to tell how much we need. */
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, 16), ==, BLOB_LENGTH);
+ /* The header is 32 bytes long (here), so that's another interesting
+ * boundary. */
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, 31), ==, BLOB_LENGTH);
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, 32), ==, BLOB_LENGTH);
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, 33), ==, BLOB_LENGTH);
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, BLOB_LENGTH - 1), ==,
+ BLOB_LENGTH);
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, BLOB_LENGTH), ==,
+ BLOB_LENGTH);
+ g_assert_cmpint (
+ dbus_message_demarshal_bytes_needed (blob, sizeof (be_blob)), ==,
+ BLOB_LENGTH);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer arg G_GNUC_UNUSED)
+{
+ dbus_error_free (&f->e);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/demarshal/le", Fixture, le_blob, setup, test_endian, teardown);
+ g_test_add ("/demarshal/be", Fixture, be_blob, setup, test_endian, teardown);
+ g_test_add ("/demarshal/needed/le", Fixture, le_blob, setup, test_needed,
+ teardown);
+ g_test_add ("/demarshal/needed/be", Fixture, be_blob, setup, test_needed,
+ teardown);
+
+ return g_test_run ();
+}
diff --git a/test/relay.c b/test/relay.c
new file mode 100644
index 00000000..bad39f47
--- /dev/null
+++ b/test/relay.c
@@ -0,0 +1,318 @@
+/* Regression test for passing unmodified messages between connections
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2010-2011 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 <config.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+/* This is basically a miniature dbus-daemon. We relay messages from the client
+ * on the left to the client on the right.
+ *
+ * left socket left dispatch right socket right
+ * client ===========> server --------------> server ===========> client
+ * conn conn conn conn
+ *
+ * In the real dbus-daemon, the client connections would be out-of-process,
+ * but here we're cheating and doing everything in-process.
+ */
+
+typedef struct {
+ DBusError e;
+
+ DBusServer *server;
+
+ DBusConnection *left_client_conn;
+ DBusConnection *left_server_conn;
+
+ DBusConnection *right_server_conn;
+ DBusConnection *right_client_conn;
+ /* queue of DBusMessage received by right_client_conn */
+ GQueue messages;
+} Fixture;
+
+static void
+assert_no_error (const DBusError *e)
+{
+ if (G_UNLIKELY (dbus_error_is_set (e)))
+ g_error ("expected success but got error: %s: %s", e->name, e->message);
+}
+
+static DBusHandlerResult
+server_message_cb (DBusConnection *server_conn,
+ DBusMessage *message,
+ void *data)
+{
+ Fixture *f = data;
+
+ g_assert (server_conn == f->left_server_conn);
+ g_assert (f->right_server_conn != NULL);
+
+ dbus_connection_send (f->right_server_conn, message, NULL);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+right_client_message_cb (DBusConnection *client_conn,
+ DBusMessage *message,
+ void *data)
+{
+ Fixture *f = data;
+
+ g_assert (client_conn == f->right_client_conn);
+ g_queue_push_tail (&f->messages, dbus_message_ref (message));
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+new_conn_cb (DBusServer *server,
+ DBusConnection *server_conn,
+ void *data)
+{
+ Fixture *f = data;
+ dbus_bool_t have_mem;
+
+ if (f->left_server_conn == NULL)
+ {
+ f->left_server_conn = dbus_connection_ref (server_conn);
+
+ have_mem = dbus_connection_add_filter (server_conn,
+ server_message_cb, f, NULL);
+ g_assert (have_mem);
+ }
+ else
+ {
+ g_assert (f->right_server_conn == NULL);
+ f->right_server_conn = dbus_connection_ref (server_conn);
+ }
+
+ dbus_connection_setup_with_g_main (server_conn, NULL);
+}
+
+static void
+setup (Fixture *f,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ dbus_error_init (&f->e);
+ g_queue_init (&f->messages);
+
+ f->server = dbus_server_listen ("tcp:host=127.0.0.1", &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->server != NULL);
+
+ dbus_server_set_new_connection_function (f->server,
+ new_conn_cb, f, NULL);
+ dbus_server_setup_with_g_main (f->server, NULL);
+}
+
+static void
+test_connect (Fixture *f,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ dbus_bool_t have_mem;
+
+ g_assert (f->left_server_conn == NULL);
+ g_assert (f->right_server_conn == NULL);
+
+ f->left_client_conn = dbus_connection_open_private (
+ dbus_server_get_address (f->server), &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->left_client_conn != NULL);
+ dbus_connection_setup_with_g_main (f->left_client_conn, NULL);
+
+ while (f->left_server_conn == NULL)
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ f->right_client_conn = dbus_connection_open_private (
+ dbus_server_get_address (f->server), &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->right_client_conn != NULL);
+ dbus_connection_setup_with_g_main (f->right_client_conn, NULL);
+
+ while (f->right_server_conn == NULL)
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ have_mem = dbus_connection_add_filter (f->right_client_conn,
+ right_client_message_cb, f, NULL);
+ g_assert (have_mem);
+}
+
+static dbus_uint32_t
+send_one (Fixture *f,
+ const char *member)
+{
+ dbus_bool_t have_mem;
+ dbus_uint32_t serial;
+ DBusMessage *outgoing;
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", member);
+ g_assert (outgoing != NULL);
+
+ have_mem = dbus_connection_send (f->left_client_conn, outgoing, &serial);
+ g_assert (have_mem);
+ g_assert (serial != 0);
+
+ dbus_message_unref (outgoing);
+ return serial;
+}
+
+static void
+test_relay (Fixture *f,
+ gconstpointer data)
+{
+ DBusMessage *incoming;
+
+ test_connect (f, data);
+
+ send_one (f, "First");
+ send_one (f, "Second");
+
+ while (g_queue_get_length (&f->messages) < 2)
+ {
+ g_print (".");
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 2);
+
+ incoming = g_queue_pop_head (&f->messages);
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "First");
+ dbus_message_unref (incoming);
+
+ incoming = g_queue_pop_head (&f->messages);
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Second");
+ dbus_message_unref (incoming);
+}
+
+/* An arbitrary number of messages */
+#define MANY 8192
+
+static void
+test_limit (Fixture *f,
+ gconstpointer data)
+{
+ DBusMessage *incoming;
+ guint i;
+
+ test_connect (f, data);
+
+ /* This was an attempt to reproduce fd.o #34393. It didn't work. */
+ g_test_bug ("34393");
+ dbus_connection_set_max_received_size (f->left_server_conn, 1);
+ g_main_context_iteration (NULL, TRUE);
+
+ for (i = 0; i < MANY; i++)
+ {
+ gchar *buf = g_strdup_printf ("Message%u", i);
+
+ send_one (f, buf);
+ g_free (buf);
+ }
+
+ i = 0;
+
+ while (i < MANY)
+ {
+ while (g_queue_is_empty (&f->messages))
+ {
+ g_main_context_iteration (NULL, TRUE);
+ }
+
+ while ((incoming = g_queue_pop_head (&f->messages)) != NULL)
+ {
+ i++;
+ dbus_message_unref (incoming);
+ }
+ }
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ if (f->left_client_conn != NULL)
+ {
+ dbus_connection_close (f->left_client_conn);
+ dbus_connection_unref (f->left_client_conn);
+ f->left_client_conn = NULL;
+ }
+
+ if (f->right_client_conn != NULL)
+ {
+ dbus_connection_close (f->right_client_conn);
+ dbus_connection_unref (f->right_client_conn);
+ f->right_client_conn = NULL;
+ }
+
+ if (f->left_server_conn != NULL)
+ {
+ dbus_connection_close (f->left_server_conn);
+ dbus_connection_unref (f->left_server_conn);
+ f->left_server_conn = NULL;
+ }
+
+ if (f->right_server_conn != NULL)
+ {
+ dbus_connection_close (f->right_server_conn);
+ dbus_connection_unref (f->right_server_conn);
+ f->right_server_conn = NULL;
+ }
+
+ if (f->server != NULL)
+ {
+ dbus_server_disconnect (f->server);
+ dbus_server_unref (f->server);
+ f->server = NULL;
+ }
+}
+
+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 ("/connect", Fixture, NULL, setup,
+ test_connect, teardown);
+ g_test_add ("/relay", Fixture, NULL, setup,
+ test_relay, teardown);
+ g_test_add ("/limit", Fixture, NULL, setup,
+ test_limit, teardown);
+
+ return g_test_run ();
+}