| /* GLib testing framework examples and tests |
| * |
| * Copyright (C) 2008-2010 Red Hat, Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General |
| * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| * |
| * Author: David Zeuthen <davidz@redhat.com> |
| */ |
| |
| #include "config.h" |
| |
| #include <gio/gio.h> |
| #include <unistd.h> |
| #include <string.h> |
| |
| /* for open(2) */ |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| |
| /* for g_unlink() */ |
| #include <glib/gstdio.h> |
| |
| #include <gio/gnetworking.h> |
| #include <gio/gunixsocketaddress.h> |
| #include <gio/gunixfdlist.h> |
| |
| /* used in test_overflow */ |
| #ifdef G_OS_UNIX |
| #include <gio/gunixconnection.h> |
| #include <errno.h> |
| #endif |
| |
| #ifdef G_OS_UNIX |
| static gboolean is_unix = TRUE; |
| #else |
| static gboolean is_unix = FALSE; |
| #endif |
| |
| static gchar *tmp_address = NULL; |
| static gchar *test_guid = NULL; |
| static GMainLoop *loop = NULL; |
| |
| static const gchar *test_interface_introspection_xml = |
| "<node>" |
| " <interface name='org.gtk.GDBus.PeerTestInterface'>" |
| " <method name='HelloPeer'>" |
| " <arg type='s' name='greeting' direction='in'/>" |
| " <arg type='s' name='response' direction='out'/>" |
| " </method>" |
| " <method name='EmitSignal'/>" |
| " <method name='EmitSignalWithNameSet'/>" |
| " <method name='OpenFile'>" |
| " <arg type='s' name='path' direction='in'/>" |
| " </method>" |
| " <signal name='PeerSignal'>" |
| " <arg type='s' name='a_string'/>" |
| " </signal>" |
| " <property type='s' name='PeerProperty' access='read'/>" |
| " </interface>" |
| "</node>"; |
| static GDBusInterfaceInfo *test_interface_introspection_data = NULL; |
| |
| |
| #ifdef G_OS_UNIX |
| |
| /* Chosen to be big enough to overflow the socket buffer */ |
| #define OVERFLOW_NUM_SIGNALS 5000 |
| #define OVERFLOW_TIMEOUT_SEC 10 |
| |
| static GDBusMessage * |
| overflow_filter_func (GDBusConnection *connection, |
| GDBusMessage *message, |
| gboolean incoming, |
| gpointer user_data) |
| { |
| gint *counter = user_data; /* (atomic) */ |
| g_atomic_int_inc (counter); |
| return message; |
| } |
| |
| static gboolean |
| overflow_on_500ms_later_func (gpointer user_data) |
| { |
| g_main_loop_quit (loop); |
| return G_SOURCE_REMOVE; |
| } |
| |
| static void |
| test_overflow (void) |
| { |
| gint sv[2]; |
| gint n; |
| GSocket *socket; |
| GSocketConnection *socket_connection; |
| GDBusConnection *producer, *consumer; |
| GError *error; |
| GTimer *timer; |
| gint n_messages_received; /* (atomic) */ |
| gint n_messages_sent; /* (atomic) */ |
| |
| g_assert_cmpint (socketpair (AF_UNIX, SOCK_STREAM, 0, sv), ==, 0); |
| |
| error = NULL; |
| socket = g_socket_new_from_fd (sv[0], &error); |
| g_assert_no_error (error); |
| socket_connection = g_socket_connection_factory_create_connection (socket); |
| g_assert (socket_connection != NULL); |
| g_object_unref (socket); |
| producer = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), |
| NULL, /* guid */ |
| G_DBUS_CONNECTION_FLAGS_NONE, |
| NULL, /* GDBusAuthObserver */ |
| NULL, /* GCancellable */ |
| |
| &error); |
| g_dbus_connection_set_exit_on_close (producer, TRUE); |
| g_assert_no_error (error); |
| g_object_unref (socket_connection); |
| g_atomic_int_set (&n_messages_sent, 0); |
| g_dbus_connection_add_filter (producer, overflow_filter_func, (gpointer) &n_messages_sent, NULL); |
| |
| /* send enough data that we get an EAGAIN */ |
| for (n = 0; n < OVERFLOW_NUM_SIGNALS; n++) |
| { |
| error = NULL; |
| g_dbus_connection_emit_signal (producer, |
| NULL, /* destination */ |
| "/org/foo/Object", |
| "org.foo.Interface", |
| "Member", |
| g_variant_new ("(s)", "a string"), |
| &error); |
| g_assert_no_error (error); |
| } |
| |
| /* sleep for 0.5 sec (to allow the GDBus IO thread to fill up the |
| * kernel buffers) and verify that n_messages_sent < |
| * OVERFLOW_NUM_SIGNALS |
| * |
| * This is to verify that not all the submitted messages have been |
| * sent to the underlying transport. |
| */ |
| g_timeout_add (500, overflow_on_500ms_later_func, NULL); |
| g_main_loop_run (loop); |
| g_assert_cmpint (g_atomic_int_get (&n_messages_sent), <, OVERFLOW_NUM_SIGNALS); |
| |
| /* now suck it all out as a client, and add it up */ |
| socket = g_socket_new_from_fd (sv[1], &error); |
| g_assert_no_error (error); |
| socket_connection = g_socket_connection_factory_create_connection (socket); |
| g_assert (socket_connection != NULL); |
| g_object_unref (socket); |
| consumer = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), |
| NULL, /* guid */ |
| G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, |
| NULL, /* GDBusAuthObserver */ |
| NULL, /* GCancellable */ |
| &error); |
| g_assert_no_error (error); |
| g_object_unref (socket_connection); |
| g_atomic_int_set (&n_messages_received, 0); |
| g_dbus_connection_add_filter (consumer, overflow_filter_func, (gpointer) &n_messages_received, NULL); |
| g_dbus_connection_start_message_processing (consumer); |
| |
| timer = g_timer_new (); |
| g_timer_start (timer); |
| |
| while (g_atomic_int_get (&n_messages_received) < OVERFLOW_NUM_SIGNALS && g_timer_elapsed (timer, NULL) < OVERFLOW_TIMEOUT_SEC) |
| g_main_context_iteration (NULL, FALSE); |
| |
| g_assert_cmpint (g_atomic_int_get (&n_messages_sent), ==, OVERFLOW_NUM_SIGNALS); |
| g_assert_cmpint (g_atomic_int_get (&n_messages_received), ==, OVERFLOW_NUM_SIGNALS); |
| |
| g_timer_destroy (timer); |
| g_object_unref (consumer); |
| g_object_unref (producer); |
| } |
| #else |
| static void |
| test_overflow (void) |
| { |
| /* TODO: test this with e.g. GWin32InputStream/GWin32OutputStream */ |
| } |
| #endif |
| |
| /* ---------------------------------------------------------------------------------------------------- */ |
| |
| |
| int |
| main (int argc, |
| char *argv[]) |
| { |
| gint ret; |
| GDBusNodeInfo *introspection_data = NULL; |
| gchar *tmpdir = NULL; |
| |
| g_test_init (&argc, &argv, NULL); |
| |
| introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL); |
| g_assert (introspection_data != NULL); |
| test_interface_introspection_data = introspection_data->interfaces[0]; |
| |
| test_guid = g_dbus_generate_guid (); |
| |
| if (is_unix) |
| { |
| if (g_unix_socket_address_abstract_names_supported ()) |
| tmp_address = g_strdup ("unix:tmpdir=/tmp/gdbus-test-"); |
| else |
| { |
| tmpdir = g_dir_make_tmp ("gdbus-test-XXXXXX", NULL); |
| tmp_address = g_strdup_printf ("unix:tmpdir=%s", tmpdir); |
| } |
| } |
| else |
| tmp_address = g_strdup ("nonce-tcp:"); |
| |
| /* all the tests rely on a shared main loop */ |
| loop = g_main_loop_new (NULL, FALSE); |
| |
| g_test_add_func ("/gdbus/overflow", test_overflow); |
| |
| ret = g_test_run(); |
| |
| g_main_loop_unref (loop); |
| g_free (test_guid); |
| g_dbus_node_info_unref (introspection_data); |
| if (is_unix) |
| g_free (tmp_address); |
| if (tmpdir) |
| { |
| g_rmdir (tmpdir); |
| g_free (tmpdir); |
| } |
| |
| return ret; |
| } |