1/*
2 * Copyright © 2014 Canonical Limited
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Ryan Lortie <desrt@desrt.ca>
18 */
19
20#include <gio/gio.h>
21#include <string.h>
22
23static gboolean expected_read_success;
24static guint expected_read;
25static gboolean got_read_done;
26
27static void
28read_done (GObject *source,
29 GAsyncResult *result,
30 gpointer user_data)
31{
32 gboolean success;
33 gsize read;
34
35 success = g_input_stream_read_all_finish (G_INPUT_STREAM (source), result, bytes_read: &read, NULL);
36 g_assert_cmpint (expected_read_success, ==, success);
37 g_assert_cmpint (expected_read, ==, read);
38 got_read_done = TRUE;
39}
40
41static void
42wait_for_read (gboolean success,
43 gsize read)
44{
45 g_assert (!got_read_done);
46 expected_read_success = success;
47 expected_read = read;
48
49 while (!got_read_done)
50 g_main_context_iteration (NULL, TRUE);
51
52 got_read_done = FALSE;
53}
54
55static gboolean expected_write_success;
56static guint expected_written;
57static gboolean got_write_done;
58
59static void
60write_done (GObject *source,
61 GAsyncResult *result,
62 gpointer user_data)
63{
64 gboolean success;
65 gsize written;
66
67 success = g_output_stream_write_all_finish (G_OUTPUT_STREAM (source), result, bytes_written: &written, NULL);
68 g_assert_cmpint (expected_write_success, ==, success);
69 g_assert_cmpint (expected_written, ==, written);
70 got_write_done = TRUE;
71}
72
73static void
74wait_for_write (gboolean success,
75 gsize written)
76{
77 g_assert (!got_write_done);
78 expected_write_success = success;
79 expected_written = written;
80
81 while (!got_write_done)
82 g_main_context_iteration (NULL, TRUE);
83
84 got_write_done = FALSE;
85}
86
87static void
88test_write_all_async_memory (void)
89{
90 GOutputStream *ms;
91 gchar b[24];
92
93 ms = g_memory_output_stream_new (data: b, size: sizeof b, NULL, NULL);
94
95 g_output_stream_write_all_async (stream: ms, buffer: "0123456789", count: 10, io_priority: 0, NULL, callback: write_done, NULL);
96 wait_for_write (TRUE, written: 10);
97
98 g_output_stream_write_all_async (stream: ms, buffer: "0123456789", count: 10, io_priority: 0, NULL, callback: write_done, NULL);
99 wait_for_write (TRUE, written: 10);
100
101 /* this will trigger an out-of-space error, but we will see the
102 * partial write...
103 */
104 g_output_stream_write_all_async (stream: ms, buffer: "0123456789", count: 10, io_priority: 0, NULL, callback: write_done, NULL);
105 wait_for_write (FALSE, written: 4);
106
107 /* and still an error, but no further bytes written */
108 g_output_stream_write_all_async (stream: ms, buffer: "0123456789", count: 10, io_priority: 0, NULL, callback: write_done, NULL);
109 wait_for_write (FALSE, written: 0);
110
111 g_assert (!memcmp (b, "012345678901234567890123", 24));
112
113 g_object_unref (object: ms);
114}
115
116static void
117test_read_all_async_memory (void)
118{
119 GInputStream *ms;
120 gchar b[24] = "0123456789ABCDEFGHIJ!@#$";
121 gchar buf[10];
122
123 ms = g_memory_input_stream_new_from_data (data: b, len: sizeof b, NULL);
124
125 g_input_stream_read_all_async (stream: ms, buffer: buf, count: 10, io_priority: 0, NULL, callback: read_done, NULL);
126 wait_for_read (TRUE, read: 10);
127 g_assert (!memcmp (buf, "0123456789", 10));
128
129 g_input_stream_read_all_async (stream: ms, buffer: buf, count: 10, io_priority: 0, NULL, callback: read_done, NULL);
130 wait_for_read (TRUE, read: 10);
131 g_assert (!memcmp (buf, "ABCDEFGHIJ", 10));
132
133 /* partial read... */
134 g_input_stream_read_all_async (stream: ms, buffer: buf, count: 10, io_priority: 0, NULL, callback: read_done, NULL);
135 wait_for_read (TRUE, read: 4);
136 g_assert (!memcmp (buf, "!@#$", 4));
137
138 /* EOF */
139 g_input_stream_read_all_async (stream: ms, buffer: buf, count: 10, io_priority: 0, NULL, callback: read_done, NULL);
140 wait_for_read (TRUE, read: 0);
141
142 g_object_unref (object: ms);
143}
144
145#ifdef G_OS_UNIX
146#include <errno.h>
147#include <sys/types.h>
148#include <sys/socket.h>
149#include <gio/gunixinputstream.h>
150#include <gio/gunixoutputstream.h>
151
152static void
153test_read_write_all_async_pipe (void)
154{
155 GCancellable *cancellable;
156 GError *error = NULL;
157 GOutputStream *out;
158 GInputStream *in;
159 gsize in_flight;
160 gsize s;
161 gchar wbuf[100] = { 0, };
162 gchar rbuf[100];
163
164 {
165 gint sv[2];
166 gint s;
167
168 s = socketpair (AF_UNIX, SOCK_STREAM, protocol: 0, fds: sv);
169 g_assert (s == 0);
170
171 out = g_unix_output_stream_new (fd: sv[0], TRUE);
172 in = g_unix_input_stream_new (fd: sv[1], TRUE);
173 }
174
175 /* Try to fill up the buffer */
176 in_flight = 0;
177 while (g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (out)))
178 {
179 s = g_output_stream_write (stream: out, buffer: wbuf, count: sizeof wbuf, NULL, error: &error);
180 g_assert_no_error (error);
181 g_assert (s > 0);
182 in_flight += s;
183 }
184
185 /* Now start a blocking write_all; nothing should happen. */
186 cancellable = g_cancellable_new ();
187 g_output_stream_write_all_async (stream: out, buffer: "0123456789", count: 10, io_priority: 0, cancellable, callback: write_done, NULL);
188 while (g_main_context_iteration (NULL, FALSE))
189 ;
190 g_assert (!got_write_done);
191
192 /* Cancel that to make sure it works */
193 g_cancellable_cancel (cancellable);
194 g_object_unref (object: cancellable);
195 wait_for_write (FALSE, written: 0);
196
197 /* Start it again */
198 g_output_stream_write_all_async (stream: out, buffer: "0123456789", count: 10, io_priority: 0, NULL, callback: write_done, NULL);
199 while (g_main_context_iteration (NULL, FALSE))
200 ;
201 g_assert (!got_write_done);
202
203 /* Now drain as much as we originally put in the buffer to make it
204 * block -- this will unblock the writer.
205 */
206 while (in_flight)
207 {
208 s = g_input_stream_read (stream: in, buffer: rbuf, MIN (sizeof wbuf, in_flight), NULL, error: &error);
209 g_assert_no_error (error);
210 g_assert (s > 0);
211 in_flight -= s;
212 }
213
214 /* That will have caused some writing to start happening. Do a
215 * read_all as well, for more bytes than was written.
216 */
217 g_input_stream_read_all_async (stream: in, buffer: rbuf, count: sizeof rbuf, io_priority: 0, NULL, callback: read_done, NULL);
218
219 /* The write is surely finished by now */
220 wait_for_write (TRUE, written: 10);
221 /* ...but the read will not yet be satisfied */
222 g_assert (!got_read_done);
223
224 /* Feed the read more than it asked for; this really should not block
225 * since the buffer is so small...
226 */
227 g_output_stream_write_all (stream: out, buffer: wbuf, count: sizeof wbuf, bytes_written: 0, NULL, error: &error);
228 g_assert_no_error (error);
229
230 /* Read will have finished now */
231 wait_for_read (TRUE, read: sizeof rbuf);
232
233 /* Close the writer end to make an EOF condition */
234 g_output_stream_close (stream: out, NULL, NULL);
235
236 /* ... and we should have exactly 10 extra bytes left in the buffer */
237 g_input_stream_read_all_async (stream: in, buffer: rbuf, count: sizeof rbuf, io_priority: 0, NULL, callback: read_done, NULL);
238 wait_for_read (TRUE, read: 10);
239
240 g_object_unref (object: out);
241 g_object_unref (object: in);
242}
243#endif
244
245int
246main (int argc,
247 char **argv)
248{
249 g_test_init (argc: &argc, argv: &argv, NULL);
250
251 g_test_add_func (testpath: "/stream/read_all_async/memory", test_func: test_read_all_async_memory);
252 g_test_add_func (testpath: "/stream/write_all_async/memory", test_func: test_write_all_async_memory);
253#ifdef G_OS_UNIX
254 g_test_add_func (testpath: "/stream/read_write_all_async/pipe", test_func: test_read_write_all_async_pipe);
255#endif
256
257 return g_test_run();
258}
259

source code of gtk/subprojects/glib/gio/tests/stream-rw_all.c