1 | /* GLib testing framework examples and tests |
2 | * |
3 | * Copyright (C) 2008-2010 Red Hat, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Author: David Zeuthen <davidz@redhat.com> |
19 | */ |
20 | |
21 | #include "config.h" |
22 | |
23 | #include <gio/gio.h> |
24 | #include <unistd.h> |
25 | #include <string.h> |
26 | |
27 | /* for open(2) */ |
28 | #include <sys/types.h> |
29 | #include <sys/stat.h> |
30 | #include <fcntl.h> |
31 | #include <string.h> |
32 | |
33 | /* for g_unlink() */ |
34 | #include <glib/gstdio.h> |
35 | |
36 | #include <gio/gnetworking.h> |
37 | #include <gio/gunixsocketaddress.h> |
38 | #include <gio/gunixfdlist.h> |
39 | |
40 | /* used in test_overflow */ |
41 | #ifdef G_OS_UNIX |
42 | #include <gio/gunixconnection.h> |
43 | #include <errno.h> |
44 | #endif |
45 | |
46 | #ifdef G_OS_UNIX |
47 | static gboolean is_unix = TRUE; |
48 | #else |
49 | static gboolean is_unix = FALSE; |
50 | #endif |
51 | |
52 | static gchar *tmp_address = NULL; |
53 | static gchar *test_guid = NULL; |
54 | static GMainLoop *loop = NULL; |
55 | |
56 | static const gchar *test_interface_introspection_xml = |
57 | "<node>" |
58 | " <interface name='org.gtk.GDBus.PeerTestInterface'>" |
59 | " <method name='HelloPeer'>" |
60 | " <arg type='s' name='greeting' direction='in'/>" |
61 | " <arg type='s' name='response' direction='out'/>" |
62 | " </method>" |
63 | " <method name='EmitSignal'/>" |
64 | " <method name='EmitSignalWithNameSet'/>" |
65 | " <method name='OpenFile'>" |
66 | " <arg type='s' name='path' direction='in'/>" |
67 | " </method>" |
68 | " <signal name='PeerSignal'>" |
69 | " <arg type='s' name='a_string'/>" |
70 | " </signal>" |
71 | " <property type='s' name='PeerProperty' access='read'/>" |
72 | " </interface>" |
73 | "</node>" ; |
74 | static GDBusInterfaceInfo *test_interface_introspection_data = NULL; |
75 | |
76 | |
77 | #ifdef G_OS_UNIX |
78 | |
79 | /* Chosen to be big enough to overflow the socket buffer */ |
80 | #define OVERFLOW_NUM_SIGNALS 5000 |
81 | #define OVERFLOW_TIMEOUT_SEC 10 |
82 | |
83 | static GDBusMessage * |
84 | overflow_filter_func (GDBusConnection *connection, |
85 | GDBusMessage *message, |
86 | gboolean incoming, |
87 | gpointer user_data) |
88 | { |
89 | gint *counter = user_data; /* (atomic) */ |
90 | g_atomic_int_inc (counter); |
91 | return message; |
92 | } |
93 | |
94 | static gboolean |
95 | overflow_on_500ms_later_func (gpointer user_data) |
96 | { |
97 | g_main_loop_quit (loop); |
98 | return G_SOURCE_REMOVE; |
99 | } |
100 | |
101 | static void |
102 | test_overflow (void) |
103 | { |
104 | gint sv[2]; |
105 | gint n; |
106 | GSocket *socket; |
107 | GSocketConnection *socket_connection; |
108 | GDBusConnection *producer, *consumer; |
109 | GError *error; |
110 | GTimer *timer; |
111 | gint n_messages_received; /* (atomic) */ |
112 | gint n_messages_sent; /* (atomic) */ |
113 | |
114 | g_assert_cmpint (socketpair (AF_UNIX, SOCK_STREAM, 0, sv), ==, 0); |
115 | |
116 | error = NULL; |
117 | socket = g_socket_new_from_fd (fd: sv[0], error: &error); |
118 | g_assert_no_error (error); |
119 | socket_connection = g_socket_connection_factory_create_connection (socket); |
120 | g_assert (socket_connection != NULL); |
121 | g_object_unref (object: socket); |
122 | producer = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), |
123 | NULL, /* guid */ |
124 | flags: G_DBUS_CONNECTION_FLAGS_NONE, |
125 | NULL, /* GDBusAuthObserver */ |
126 | NULL, /* GCancellable */ |
127 | |
128 | error: &error); |
129 | g_dbus_connection_set_exit_on_close (connection: producer, TRUE); |
130 | g_assert_no_error (error); |
131 | g_object_unref (object: socket_connection); |
132 | g_atomic_int_set (&n_messages_sent, 0); |
133 | g_dbus_connection_add_filter (connection: producer, filter_function: overflow_filter_func, user_data: (gpointer) &n_messages_sent, NULL); |
134 | |
135 | /* send enough data that we get an EAGAIN */ |
136 | for (n = 0; n < OVERFLOW_NUM_SIGNALS; n++) |
137 | { |
138 | error = NULL; |
139 | g_dbus_connection_emit_signal (connection: producer, |
140 | NULL, /* destination */ |
141 | object_path: "/org/foo/Object" , |
142 | interface_name: "org.foo.Interface" , |
143 | signal_name: "Member" , |
144 | parameters: g_variant_new (format_string: "(s)" , "a string" ), |
145 | error: &error); |
146 | g_assert_no_error (error); |
147 | } |
148 | |
149 | /* sleep for 0.5 sec (to allow the GDBus IO thread to fill up the |
150 | * kernel buffers) and verify that n_messages_sent < |
151 | * OVERFLOW_NUM_SIGNALS |
152 | * |
153 | * This is to verify that not all the submitted messages have been |
154 | * sent to the underlying transport. |
155 | */ |
156 | g_timeout_add (interval: 500, function: overflow_on_500ms_later_func, NULL); |
157 | g_main_loop_run (loop); |
158 | g_assert_cmpint (g_atomic_int_get (&n_messages_sent), <, OVERFLOW_NUM_SIGNALS); |
159 | |
160 | /* now suck it all out as a client, and add it up */ |
161 | socket = g_socket_new_from_fd (fd: sv[1], error: &error); |
162 | g_assert_no_error (error); |
163 | socket_connection = g_socket_connection_factory_create_connection (socket); |
164 | g_assert (socket_connection != NULL); |
165 | g_object_unref (object: socket); |
166 | consumer = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), |
167 | NULL, /* guid */ |
168 | flags: G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, |
169 | NULL, /* GDBusAuthObserver */ |
170 | NULL, /* GCancellable */ |
171 | error: &error); |
172 | g_assert_no_error (error); |
173 | g_object_unref (object: socket_connection); |
174 | g_atomic_int_set (&n_messages_received, 0); |
175 | g_dbus_connection_add_filter (connection: consumer, filter_function: overflow_filter_func, user_data: (gpointer) &n_messages_received, NULL); |
176 | g_dbus_connection_start_message_processing (connection: consumer); |
177 | |
178 | timer = g_timer_new (); |
179 | g_timer_start (timer); |
180 | |
181 | while (g_atomic_int_get (&n_messages_received) < OVERFLOW_NUM_SIGNALS && g_timer_elapsed (timer, NULL) < OVERFLOW_TIMEOUT_SEC) |
182 | g_main_context_iteration (NULL, FALSE); |
183 | |
184 | g_assert_cmpint (g_atomic_int_get (&n_messages_sent), ==, OVERFLOW_NUM_SIGNALS); |
185 | g_assert_cmpint (g_atomic_int_get (&n_messages_received), ==, OVERFLOW_NUM_SIGNALS); |
186 | |
187 | g_timer_destroy (timer); |
188 | g_object_unref (object: consumer); |
189 | g_object_unref (object: producer); |
190 | } |
191 | #else |
192 | static void |
193 | test_overflow (void) |
194 | { |
195 | /* TODO: test this with e.g. GWin32InputStream/GWin32OutputStream */ |
196 | } |
197 | #endif |
198 | |
199 | /* ---------------------------------------------------------------------------------------------------- */ |
200 | |
201 | |
202 | int |
203 | main (int argc, |
204 | char *argv[]) |
205 | { |
206 | gint ret; |
207 | GDBusNodeInfo *introspection_data = NULL; |
208 | gchar *tmpdir = NULL; |
209 | |
210 | g_test_init (argc: &argc, argv: &argv, NULL); |
211 | |
212 | introspection_data = g_dbus_node_info_new_for_xml (xml_data: test_interface_introspection_xml, NULL); |
213 | g_assert (introspection_data != NULL); |
214 | test_interface_introspection_data = introspection_data->interfaces[0]; |
215 | |
216 | test_guid = g_dbus_generate_guid (); |
217 | |
218 | if (is_unix) |
219 | { |
220 | if (g_unix_socket_address_abstract_names_supported ()) |
221 | tmp_address = g_strdup (str: "unix:tmpdir=/tmp/gdbus-test-" ); |
222 | else |
223 | { |
224 | tmpdir = g_dir_make_tmp (tmpl: "gdbus-test-XXXXXX" , NULL); |
225 | tmp_address = g_strdup_printf (format: "unix:tmpdir=%s" , tmpdir); |
226 | } |
227 | } |
228 | else |
229 | tmp_address = g_strdup (str: "nonce-tcp:" ); |
230 | |
231 | /* all the tests rely on a shared main loop */ |
232 | loop = g_main_loop_new (NULL, FALSE); |
233 | |
234 | g_test_add_func (testpath: "/gdbus/overflow" , test_func: test_overflow); |
235 | |
236 | ret = g_test_run(); |
237 | |
238 | g_main_loop_unref (loop); |
239 | g_free (mem: test_guid); |
240 | g_dbus_node_info_unref (info: introspection_data); |
241 | if (is_unix) |
242 | g_free (mem: tmp_address); |
243 | if (tmpdir) |
244 | { |
245 | g_rmdir (filename: tmpdir); |
246 | g_free (mem: tmpdir); |
247 | } |
248 | |
249 | return ret; |
250 | } |
251 | |