| /* GLib testing framework examples and tests | |
| * | |
| * Copyright (C) 2008-2010 Red Hat, Inc. | |
| * | |
| * SPDX-License-Identifier: LGPL-2.1-or-later | |
| * | |
| * 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, G_TEST_OPTION_ISOLATE_DIRS, 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) | |
| { | |
| 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; | |
| } |