1 /* Test case for GNOME #662395
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 * Copyright (C) 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>
29 #include "test-io-stream.h"
30 #include "test-pipe-unix.h"
32 #define MY_TYPE_OUTPUT_STREAM \
33 (my_output_stream_get_type ())
34 #define MY_OUTPUT_STREAM(o) \
35 (G_TYPE_CHECK_INSTANCE_CAST ((o), \
36 MY_TYPE_OUTPUT_STREAM, \
38 #define MY_IS_OUTPUT_STREAM(o) \
39 (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_OUTPUT_STREAM))
41 G_LOCK_DEFINE_STATIC (write
);
44 GFilterOutputStream parent
;
46 volatile gint started
;
47 volatile gint finished
;
48 volatile gint flushed
;
50 GOutputStream
*real_output
;
54 GFilterOutputStreamClass parent
;
55 } MyOutputStreamClass
;
57 static GType
my_output_stream_get_type (void) G_GNUC_CONST
;
59 G_DEFINE_TYPE (MyOutputStream
, my_output_stream
, G_TYPE_FILTER_OUTPUT_STREAM
)
61 /* Called from GDBusWorker thread */
63 my_output_stream_write (GOutputStream
*os
,
66 GCancellable
*cancellable
,
69 MyOutputStream
*self
= MY_OUTPUT_STREAM (os
);
70 GFilterOutputStream
*filter
= G_FILTER_OUTPUT_STREAM (os
);
71 GOutputStream
*real
= g_filter_output_stream_get_base_stream (filter
);
74 g_atomic_int_add (&self
->started
, count
);
75 /* Other threads can make writing block forever by taking this lock */
77 ret
= g_output_stream_write (real
, buffer
, count
, cancellable
, error
);
79 g_atomic_int_add (&self
->finished
, count
);
83 /* Called from GDBusWorker thread */
85 my_output_stream_flush (GOutputStream
*os
,
86 GCancellable
*cancellable
,
89 MyOutputStream
*self
= MY_OUTPUT_STREAM (os
);
90 GFilterOutputStream
*filter
= G_FILTER_OUTPUT_STREAM (os
);
91 GOutputStream
*real
= g_filter_output_stream_get_base_stream (filter
);
92 gint started
, finished
;
95 /* These should be equal because you're not allowed to flush with a
96 * write pending, and GOutputStream enforces that for its subclasses
98 started
= g_atomic_int_get (&self
->started
);
99 finished
= g_atomic_int_get (&self
->finished
);
100 g_assert_cmpint (started
, ==, finished
);
102 ret
= g_output_stream_flush (real
, cancellable
, error
);
104 /* As above, this shouldn't have changed during the flush */
105 finished
= g_atomic_int_get (&self
->finished
);
106 g_assert_cmpint (started
, ==, finished
);
108 /* Checkpoint reached */
109 g_atomic_int_set (&self
->flushed
, finished
);
113 /* Called from any thread; thread-safe */
115 my_output_stream_get_bytes_started (GOutputStream
*os
)
117 MyOutputStream
*self
= MY_OUTPUT_STREAM (os
);
119 return g_atomic_int_get (&self
->started
);
122 /* Called from any thread; thread-safe */
124 my_output_stream_get_bytes_finished (GOutputStream
*os
)
126 MyOutputStream
*self
= MY_OUTPUT_STREAM (os
);
128 return g_atomic_int_get (&self
->finished
);
131 /* Called from any thread; thread-safe */
133 my_output_stream_get_bytes_flushed (GOutputStream
*os
)
135 MyOutputStream
*self
= MY_OUTPUT_STREAM (os
);
137 return g_atomic_int_get (&self
->flushed
);
141 my_output_stream_init (MyOutputStream
*self
)
146 my_output_stream_class_init (MyOutputStreamClass
*cls
)
148 GOutputStreamClass
*ostream_class
= (GOutputStreamClass
*) cls
;
150 ostream_class
->write_fn
= my_output_stream_write
;
151 ostream_class
->flush
= my_output_stream_flush
;
154 /* ---------------------------------------------------------------------------------------------------- */
161 GIOStream
*client_stream
;
162 GInputStream
*client_istream
;
163 GOutputStream
*client_ostream
;
164 GOutputStream
*client_real_ostream
;
165 GDBusConnection
*client_conn
;
167 GIOStream
*server_stream
;
168 GInputStream
*server_istream
;
169 GOutputStream
*server_ostream
;
170 GDBusConnection
*server_conn
;
174 setup_client_cb (GObject
*source
,
178 Fixture
*f
= user_data
;
180 f
->client_conn
= g_dbus_connection_new_finish (res
, &f
->error
);
181 g_assert_no_error (f
->error
);
182 g_assert (G_IS_DBUS_CONNECTION (f
->client_conn
));
183 g_assert (f
->client_conn
== G_DBUS_CONNECTION (source
));
187 setup_server_cb (GObject
*source
,
191 Fixture
*f
= user_data
;
193 f
->server_conn
= g_dbus_connection_new_finish (res
, &f
->error
);
194 g_assert_no_error (f
->error
);
195 g_assert (G_IS_DBUS_CONNECTION (f
->server_conn
));
196 g_assert (f
->server_conn
== G_DBUS_CONNECTION (source
));
201 gconstpointer test_data G_GNUC_UNUSED
)
205 f
->guid
= g_dbus_generate_guid ();
207 ok
= test_pipe (&f
->server_istream
, &f
->client_real_ostream
, &f
->error
);
208 g_assert_no_error (f
->error
);
209 g_assert (G_IS_OUTPUT_STREAM (f
->client_real_ostream
));
210 g_assert (G_IS_INPUT_STREAM (f
->server_istream
));
213 f
->client_ostream
= g_object_new (MY_TYPE_OUTPUT_STREAM
,
214 "base-stream", f
->client_real_ostream
,
215 "close-base-stream", TRUE
,
217 g_assert (G_IS_OUTPUT_STREAM (f
->client_ostream
));
219 ok
= test_pipe (&f
->client_istream
, &f
->server_ostream
, &f
->error
);
220 g_assert_no_error (f
->error
);
221 g_assert (G_IS_OUTPUT_STREAM (f
->server_ostream
));
222 g_assert (G_IS_INPUT_STREAM (f
->client_istream
));
225 f
->client_stream
= test_io_stream_new (f
->client_istream
, f
->client_ostream
);
226 f
->server_stream
= test_io_stream_new (f
->server_istream
, f
->server_ostream
);
228 g_dbus_connection_new (f
->client_stream
, NULL
,
229 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT
,
230 NULL
, NULL
, setup_client_cb
, f
);
231 g_dbus_connection_new (f
->server_stream
, f
->guid
,
232 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER
,
233 NULL
, NULL
, setup_server_cb
, f
);
235 while (f
->client_conn
== NULL
|| f
->server_conn
== NULL
)
236 g_main_context_iteration (NULL
, TRUE
);
240 flush_cb (GObject
*source
,
244 Fixture
*f
= user_data
;
247 g_assert (G_IS_DBUS_CONNECTION (source
));
248 g_assert (G_IS_DBUS_CONNECTION (f
->client_conn
));
249 g_assert_cmpuint ((guintptr
) f
->client_conn
, ==, (guintptr
) G_DBUS_CONNECTION (source
));
251 ok
= g_dbus_connection_flush_finish (f
->client_conn
, res
, &f
->error
);
252 g_assert_no_error (f
->error
);
259 test_flush_busy (Fixture
*f
,
260 gconstpointer test_data G_GNUC_UNUSED
)
262 gint initial
, started
;
265 initial
= my_output_stream_get_bytes_started (f
->client_ostream
);
266 /* make sure the actual write will block */
269 ok
= g_dbus_connection_emit_signal (f
->client_conn
, NULL
, "/",
270 "com.example.Foo", "SomeSignal", NULL
,
272 g_assert_no_error (f
->error
);
275 /* wait for at least part of the message to have started writing -
276 * the write will block indefinitely in the worker thread
279 started
= my_output_stream_get_bytes_started (f
->client_ostream
);
281 } while (initial
>= started
);
283 /* we haven't flushed anything */
284 g_assert_cmpint (my_output_stream_get_bytes_flushed (f
->client_ostream
),
287 /* start to flush: it can't happen til the write finishes */
288 g_dbus_connection_flush (f
->client_conn
, NULL
, flush_cb
, f
);
290 /* we still haven't actually flushed anything */
291 g_assert_cmpint (my_output_stream_get_bytes_flushed (f
->client_ostream
),
294 /* let the write finish */
297 /* wait for the flush to happen */
299 g_main_context_iteration (NULL
, TRUE
);
301 /* now we have flushed at least what we'd written - but before fixing
302 * GNOME#662395 this assertion would fail
304 g_assert_cmpint (my_output_stream_get_bytes_flushed (f
->client_ostream
),
309 test_flush_idle (Fixture
*f
,
310 gconstpointer test_data G_GNUC_UNUSED
)
312 gint initial
, finished
;
315 initial
= my_output_stream_get_bytes_finished (f
->client_ostream
);
317 ok
= g_dbus_connection_emit_signal (f
->client_conn
, NULL
, "/",
318 "com.example.Foo", "SomeSignal", NULL
,
320 g_assert_no_error (f
->error
);
323 /* wait for at least part of the message to have been written */
325 finished
= my_output_stream_get_bytes_finished (f
->client_ostream
);
327 } while (initial
>= finished
);
329 /* we haven't flushed anything */
330 g_assert_cmpint (my_output_stream_get_bytes_flushed (f
->client_ostream
),
333 /* flush with fully-written, but unflushed, messages */
334 ok
= g_dbus_connection_flush_sync (f
->client_conn
, NULL
, &f
->error
);
336 /* now we have flushed at least what we'd written - but before fixing
337 * GNOME#662395 this assertion would fail
339 g_assert_cmpint (my_output_stream_get_bytes_flushed (f
->client_ostream
),
344 teardown (Fixture
*f
,
345 gconstpointer test_data G_GNUC_UNUSED
)
347 g_clear_error (&f
->error
);
349 g_clear_object (&f
->client_stream
);
350 g_clear_object (&f
->client_istream
);
351 g_clear_object (&f
->client_ostream
);
352 g_clear_object (&f
->client_real_ostream
);
353 g_clear_object (&f
->client_conn
);
355 g_clear_object (&f
->server_stream
);
356 g_clear_object (&f
->server_istream
);
357 g_clear_object (&f
->server_ostream
);
358 g_clear_object (&f
->server_conn
);
363 /* ---------------------------------------------------------------------------------------------------- */
371 g_test_init (&argc
, &argv
, NULL
);
373 g_test_add ("/gdbus/connection/flush/busy", Fixture
, NULL
,
374 setup
, test_flush_busy
, teardown
);
375 g_test_add ("/gdbus/connection/flush/idle", Fixture
, NULL
,
376 setup
, test_flush_idle
, teardown
);