1 /* GLib testing framework examples and tests
2 *
3 * Copyright © 2001 Hidetoshi Tajima
4 * Copyright © 2001 Ron Steinke
5 * Copyright © 2001 Owen Taylor
6 * Copyright © 2002 Manish Singh
7 * Copyright © 2011 Sjoerd Simons
8 * Copyright © 2012 Simon McVittie
9 * Copyright © 2013 Stef Walter
10 * Copyright © 2005, 2006, 2008, 2012, 2013 Matthias Clasen
11 * Copyright © 2020 Endless Mobile, Inc.
12 *
13 * SPDX-License-Identifier: LGPL-2.1-or-later
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General
26 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 *
28 * Author: Philip Withnall <withnall@endlessm.com>
29 */
30
31 #include <glib.h>
32 #include <glib/gstdio.h>
33
34 static void
35 test_small_writes (void)
36 {
37 GIOChannel *io;
38 GIOStatus status = G_IO_STATUS_ERROR;
39 guint bytes_remaining;
40 gchar tmp;
41 GError *local_error = NULL;
42
43 io = g_io_channel_new_file ("iochannel-test-outfile", "w", &local_error);
44 g_assert_no_error (local_error);
45
46 g_io_channel_set_encoding (io, NULL, NULL);
47 g_io_channel_set_buffer_size (io, 1022);
48
49 bytes_remaining = 2 * g_io_channel_get_buffer_size (io);
50 tmp = 0;
51
52 while (bytes_remaining)
53 {
54 status = g_io_channel_write_chars (io, &tmp, 1, NULL, NULL);
55 if (status == G_IO_STATUS_ERROR)
56 break;
57 if (status == G_IO_STATUS_NORMAL)
58 bytes_remaining--;
59 }
60
61 g_assert_cmpint (status, ==, G_IO_STATUS_NORMAL);
62
63 g_io_channel_unref (io);
64 g_remove ("iochannel-test-outfile");
65 }
66
67 static void
68 test_read_write (void)
69 {
70 GIOChannel *gio_r, *gio_w ;
71 GError *local_error = NULL;
72 GString *buffer;
73 char *filename;
74 gint rlength = 0;
75 glong wlength = 0;
76 gsize length_out;
77 const gchar *encoding = "EUC-JP";
78 GIOStatus status;
79 const gsize buffer_size_bytes = 1024;
80
81 filename = g_test_build_filename (G_TEST_DIST, "iochannel-test-infile", NULL);
82
83 setbuf (stdout, NULL); /* For debugging */
84
85 gio_r = g_io_channel_new_file (filename, "r", &local_error);
86 g_assert_no_error (local_error);
87
88 gio_w = g_io_channel_new_file ("iochannel-test-outfile", "w", &local_error);
89 g_assert_no_error (local_error);
90
91 g_io_channel_set_encoding (gio_r, encoding, &local_error);
92 g_assert_no_error (local_error);
93
94 g_io_channel_set_buffer_size (gio_r, buffer_size_bytes);
95
96 status = g_io_channel_set_flags (gio_r, G_IO_FLAG_NONBLOCK, &local_error);
97 if (status == G_IO_STATUS_ERROR)
98 {
99 #ifdef G_OS_WIN32
100 g_test_message ("FIXME: not implemented on win32");
101 #else
102 /* Errors should not happen */
103 g_assert_no_error (local_error);
104 #endif
105 g_clear_error (&local_error);
106 }
107 buffer = g_string_sized_new (buffer_size_bytes);
108
109 while (TRUE)
110 {
111 do
112 status = g_io_channel_read_line_string (gio_r, buffer, NULL, &local_error);
113 while (status == G_IO_STATUS_AGAIN);
114 if (status != G_IO_STATUS_NORMAL)
115 break;
116
117 rlength += buffer->len;
118
119 do
120 status = g_io_channel_write_chars (gio_w, buffer->str, buffer->len,
121 &length_out, &local_error);
122 while (status == G_IO_STATUS_AGAIN);
123 if (status != G_IO_STATUS_NORMAL)
124 break;
125
126 wlength += length_out;
127
128 /* Ensure the whole line was written */
129 g_assert_cmpuint (length_out, ==, buffer->len);
130
131 g_test_message ("%s", buffer->str);
132 g_string_truncate (buffer, 0);
133 }
134
135 switch (status)
136 {
137 case G_IO_STATUS_EOF:
138 break;
139 case G_IO_STATUS_ERROR:
140 /* Errors should not happen */
141 g_assert_no_error (local_error);
142 g_clear_error (&local_error);
143 break;
144 default:
145 g_assert_not_reached ();
146 break;
147 }
148
149 do
150 status = g_io_channel_flush (gio_w, &local_error);
151 while (status == G_IO_STATUS_AGAIN);
152
153 if (status == G_IO_STATUS_ERROR)
154 {
155 /* Errors should not happen */
156 g_assert_no_error (local_error);
157 g_clear_error (&local_error);
158 }
159
160 g_test_message ("read %d bytes, wrote %ld bytes", rlength, wlength);
161
162 g_io_channel_unref (gio_r);
163 g_io_channel_unref (gio_w);
164
165 test_small_writes ();
166
167 g_free (filename);
168 g_string_free (buffer, TRUE);
169 }
170
171 static void
172 test_read_line_embedded_nuls (void)
173 {
174 const guint8 test_data[] = { 'H', 'i', '!', '\0', 'y', 'o', 'u', '\n', ':', ')', '\n' };
175 gint fd;
176 gchar *filename = NULL;
177 GIOChannel *channel = NULL;
178 GError *local_error = NULL;
179 gchar *line = NULL;
180 gsize line_length, terminator_pos;
181 GIOStatus status;
182
183 g_test_summary ("Test that reading a line containing embedded nuls works "
184 "when using non-standard line terminators.");
185
186 /* Write out a temporary file. */
187 fd = g_file_open_tmp ("glib-test-io-channel-XXXXXX", &filename, &local_error);
188 g_assert_no_error (local_error);
189 g_close (fd, NULL);
190 fd = -1;
191
192 g_file_set_contents (filename, (const gchar *) test_data, sizeof (test_data), &local_error);
193 g_assert_no_error (local_error);
194
195 /* Create the channel. */
196 channel = g_io_channel_new_file (filename, "r", &local_error);
197 g_assert_no_error (local_error);
198
199 /* Only break on newline characters, not nuls.
200 * Use length -1 here to exercise glib#2323; the case where length > 0
201 * is covered in glib/tests/protocol.c. */
202 g_io_channel_set_line_term (channel, "\n", -1);
203 g_io_channel_set_encoding (channel, NULL, &local_error);
204 g_assert_no_error (local_error);
205
206 status = g_io_channel_read_line (channel, &line, &line_length,
207 &terminator_pos, &local_error);
208 g_assert_no_error (local_error);
209 g_assert_cmpint (status, ==, G_IO_STATUS_NORMAL);
210 g_assert_cmpuint (line_length, ==, 8);
211 g_assert_cmpuint (terminator_pos, ==, 7);
212 g_assert_cmpmem (line, line_length, test_data, 8);
213
214 g_free (line);
215 g_io_channel_unref (channel);
216 g_free (filename);
217 }
218
219 int
220 main (int argc,
221 char *argv[])
222 {
223 g_test_init (&argc, &argv, NULL);
224
225 g_test_add_func ("/io-channel/read-write", test_read_write);
226 g_test_add_func ("/io-channel/read-line/embedded-nuls", test_read_line_embedded_nuls);
227
228 return g_test_run ();
229 }