1 /* GDBus regression test - close a stream when a message remains to be written
3 * Copyright © 2006-2010 Red Hat, Inc.
4 * Copyright © 2011 Nokia Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
32 # include <glib/glib-unix.h>
33 # include <gio/gunixinputstream.h>
34 # include <gio/gunixoutputstream.h>
35 # include <gio/gunixconnection.h>
37 # error This test is currently Unix-specific due to use of g_unix_open_pipe()
40 #include "gdbus-tests.h"
42 #define CLOSE_TIME_MS 1
43 #define N_REPEATS_SLOW 5000
46 /* ---------- MyIOStream ------------------------------------------------- */
48 #define MY_TYPE_IO_STREAM (my_io_stream_get_type ())
49 #define MY_IO_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_IO_STREAM, MyIOStream))
50 #define MY_IS_IO_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_IO_STREAM))
54 GIOStream parent_instance
;
55 GInputStream
*input_stream
;
56 GOutputStream
*output_stream
;
61 GIOStreamClass parent_class
;
64 static GType
my_io_stream_get_type (void) G_GNUC_CONST
;
66 G_DEFINE_TYPE (MyIOStream
, my_io_stream
, G_TYPE_IO_STREAM
)
69 my_io_stream_finalize (GObject
*object
)
71 MyIOStream
*stream
= MY_IO_STREAM (object
);
72 g_object_unref (stream
->input_stream
);
73 g_object_unref (stream
->output_stream
);
74 G_OBJECT_CLASS (my_io_stream_parent_class
)->finalize (object
);
78 my_io_stream_init (MyIOStream
*stream
)
83 my_io_stream_get_input_stream (GIOStream
*_stream
)
85 MyIOStream
*stream
= MY_IO_STREAM (_stream
);
86 return stream
->input_stream
;
89 static GOutputStream
*
90 my_io_stream_get_output_stream (GIOStream
*_stream
)
92 MyIOStream
*stream
= MY_IO_STREAM (_stream
);
93 return stream
->output_stream
;
97 my_io_stream_class_init (MyIOStreamClass
*klass
)
99 GObjectClass
*gobject_class
;
100 GIOStreamClass
*giostream_class
;
102 gobject_class
= G_OBJECT_CLASS (klass
);
103 gobject_class
->finalize
= my_io_stream_finalize
;
105 giostream_class
= G_IO_STREAM_CLASS (klass
);
106 giostream_class
->get_input_stream
= my_io_stream_get_input_stream
;
107 giostream_class
->get_output_stream
= my_io_stream_get_output_stream
;
111 my_io_stream_new (GInputStream
*input_stream
,
112 GOutputStream
*output_stream
)
115 g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream
), NULL
);
116 g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream
), NULL
);
117 stream
= MY_IO_STREAM (g_object_new (MY_TYPE_IO_STREAM
, NULL
));
118 stream
->input_stream
= g_object_ref (input_stream
);
119 stream
->output_stream
= g_object_ref (output_stream
);
120 return G_IO_STREAM (stream
);
123 /* ---------- MySlowCloseOutputStream ------------------------------------ */
127 GFilterOutputStream parent_instance
;
128 } MySlowCloseOutputStream
;
132 GFilterOutputStreamClass parent_class
;
133 } MySlowCloseOutputStreamClass
;
135 #define MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM \
136 (my_slow_close_output_stream_get_type ())
137 #define MY_OUTPUT_STREAM(o) \
138 (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM, \
139 MySlowCloseOutputStream))
140 #define MY_IS_SLOW_CLOSE_OUTPUT_STREAM(o) \
141 (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM))
143 static GType
my_slow_close_output_stream_get_type (void) G_GNUC_CONST
;
145 G_DEFINE_TYPE (MySlowCloseOutputStream
, my_slow_close_output_stream
,
146 G_TYPE_FILTER_OUTPUT_STREAM
)
149 my_slow_close_output_stream_init (MySlowCloseOutputStream
*stream
)
154 my_slow_close_output_stream_close (GOutputStream
*stream
,
155 GCancellable
*cancellable
,
158 g_usleep (CLOSE_TIME_MS
* 1000);
159 return G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class
)->
160 close_fn (stream
, cancellable
, error
);
164 GOutputStream
*stream
;
166 GCancellable
*cancellable
;
167 GAsyncReadyCallback callback
;
172 delayed_close_free (gpointer data
)
174 DelayedClose
*df
= data
;
176 g_object_unref (df
->stream
);
178 g_object_unref (df
->cancellable
);
183 delayed_close_cb (gpointer data
)
185 DelayedClose
*df
= data
;
187 G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class
)->
188 close_async (df
->stream
, df
->io_priority
, df
->cancellable
, df
->callback
,
195 my_slow_close_output_stream_close_async (GOutputStream
*stream
,
197 GCancellable
*cancellable
,
198 GAsyncReadyCallback callback
,
204 df
= g_new0 (DelayedClose
, 1);
205 df
->stream
= g_object_ref (stream
);
206 df
->io_priority
= io_priority
;
207 df
->cancellable
= (cancellable
!= NULL
? g_object_ref (cancellable
) : NULL
);
208 df
->callback
= callback
;
209 df
->user_data
= user_data
;
211 later
= g_timeout_source_new (CLOSE_TIME_MS
);
212 g_source_set_callback (later
, delayed_close_cb
, df
, delayed_close_free
);
213 g_source_attach (later
, g_main_context_get_thread_default ());
217 my_slow_close_output_stream_close_finish (GOutputStream
*stream
,
218 GAsyncResult
*result
,
221 return G_OUTPUT_STREAM_CLASS (my_slow_close_output_stream_parent_class
)->
222 close_finish (stream
, result
, error
);
226 my_slow_close_output_stream_class_init (MySlowCloseOutputStreamClass
*klass
)
228 GOutputStreamClass
*ostream_class
;
230 ostream_class
= G_OUTPUT_STREAM_CLASS (klass
);
231 ostream_class
->close_fn
= my_slow_close_output_stream_close
;
232 ostream_class
->close_async
= my_slow_close_output_stream_close_async
;
233 ostream_class
->close_finish
= my_slow_close_output_stream_close_finish
;
237 my_io_stream_new_for_fds (gint fd_in
, gint fd_out
)
240 GInputStream
*input_stream
;
241 GOutputStream
*real_output_stream
;
242 GOutputStream
*output_stream
;
244 input_stream
= g_unix_input_stream_new (fd_in
, TRUE
);
245 real_output_stream
= g_unix_output_stream_new (fd_out
, TRUE
);
246 output_stream
= g_object_new (MY_TYPE_SLOW_CLOSE_OUTPUT_STREAM
,
247 "base-stream", real_output_stream
,
249 stream
= my_io_stream_new (input_stream
, output_stream
);
250 g_object_unref (input_stream
);
251 g_object_unref (output_stream
);
252 g_object_unref (real_output_stream
);
256 /* ---------- Tests ------------------------------------------------------ */
259 gint server_to_client
[2];
260 gint client_to_server
[2];
261 GIOStream
*server_iostream
;
262 GDBusConnection
*server_conn
;
264 GDBusConnection
*connection
;
271 gconstpointer context
)
273 f
->guid
= g_dbus_generate_guid ();
277 teardown (Fixture
*f
,
278 gconstpointer context
)
280 g_clear_object (&f
->server_iostream
);
281 g_clear_object (&f
->server_conn
);
282 g_clear_object (&f
->iostream
);
283 g_clear_object (&f
->connection
);
284 g_clear_error (&f
->error
);
289 on_new_conn (GObject
*source
,
293 GDBusConnection
**connection
= user_data
;
294 GError
*error
= NULL
;
296 *connection
= g_dbus_connection_new_for_address_finish (res
, &error
);
297 g_assert_no_error (error
);
301 test_once (Fixture
*f
,
302 gconstpointer context
)
304 GDBusMessage
*message
;
307 pipe_res
= g_unix_open_pipe (f
->server_to_client
, FD_CLOEXEC
, &f
->error
);
309 pipe_res
= g_unix_open_pipe (f
->client_to_server
, FD_CLOEXEC
, &f
->error
);
312 f
->server_iostream
= my_io_stream_new_for_fds (f
->client_to_server
[0],
313 f
->server_to_client
[1]);
314 f
->iostream
= my_io_stream_new_for_fds (f
->server_to_client
[0],
315 f
->client_to_server
[1]);
317 g_dbus_connection_new (f
->server_iostream
,
319 (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER
|
320 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS
),
321 NULL
/* auth observer */,
322 NULL
/* cancellable */,
323 on_new_conn
, &f
->server_conn
);
325 g_dbus_connection_new (f
->iostream
,
327 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT
,
328 NULL
/* auth observer */,
329 NULL
/* cancellable */,
330 on_new_conn
, &f
->connection
);
332 while (f
->server_conn
== NULL
|| f
->connection
== NULL
)
333 g_main_context_iteration (NULL
, TRUE
);
336 * queue a message - it'll sometimes be sent while the close is pending,
339 message
= g_dbus_message_new_signal ("/", "com.example.Foo", "Bar");
340 g_dbus_connection_send_message (f
->connection
, message
, 0, NULL
, &f
->error
);
341 g_assert_no_error (f
->error
);
342 g_object_unref (message
);
344 /* close the connection (deliberately or via last-unref) */
345 if (g_strcmp0 (context
, "unref") == 0)
347 g_clear_object (&f
->connection
);
351 g_dbus_connection_close_sync (f
->connection
, NULL
, &f
->error
);
352 g_assert_no_error (f
->error
);
355 /* either way, wait for the connection to close */
356 while (!g_dbus_connection_is_closed (f
->server_conn
))
357 g_main_context_iteration (NULL
, TRUE
);
359 /* clean up before the next run */
360 g_clear_object (&f
->iostream
);
361 g_clear_object (&f
->server_iostream
);
362 g_clear_object (&f
->connection
);
363 g_clear_object (&f
->server_conn
);
364 g_clear_error (&f
->error
);
368 test_many_times (Fixture
*f
,
369 gconstpointer context
)
374 n_repeats
= N_REPEATS_SLOW
;
376 n_repeats
= N_REPEATS
;
378 for (i
= 0; i
< n_repeats
; i
++)
379 test_once (f
, context
);
386 g_test_init (&argc
, &argv
, NULL
);
388 g_test_add ("/gdbus/close-pending", Fixture
, "close",
389 setup
, test_many_times
, teardown
);
390 g_test_add ("/gdbus/unref-pending", Fixture
, "unref",
391 setup
, test_many_times
, teardown
);