/* 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 . * * Author: David Zeuthen */ #include "config.h" #include #include #include /* for open(2) */ #include #include #include #include /* for g_unlink() */ #include #include #include #include /* used in test_overflow */ #ifdef G_OS_UNIX #include #include #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 = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; 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) { 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; }