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