@samitouri / QOSamiQemu / commits / 6a9a6f5aa8

tests/unit: add pread/pwrite _all tests for io channel file

Add unit tests for the new qio_channel_pread{v,}_all{,_eof}() and qio_channel_pwrite{v,}_all() APIs. The basic tests write data to a file channel, then read it back at various offsets using both the single-buffer and iovec variants to make sure the round-trip produces identical content. The _eof tests verify all three return cases -- full read (1), clean EOF (0), and partial-then-EOF (-1 with error set) -- and check that the strict wrappers (preadv_all / pread_all) treat a clean EOF as an error. All tests are guarded by CONFIG_PREADV since the underlying channel methods require preadv(2). Signed-off-by: Junjie Cao <junjie.cao@intel.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Acked-by: Daniel P. Berrangé <berrange@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260413214549.926435-5-junjie.cao@intel.com Signed-off-by: Fabiano Rosas <farosas@suse.de>

Junjie Cao committed Apr 14, 2026 at 05:45 UTC 6a9a6f5aa8e541bfcc41b7e67437d38e2f33308a
1 file changed +207
tests/unit/test-io-channel-file.c
+207
@@ -102,6 +102,203 @@ static void test_io_channel_fd(void)
102 }
103
104
105 +#ifdef CONFIG_PREADV
106 +static void test_io_channel_pread_all(void)
107 +{
108 + QIOChannel *ioc;
109 + char write_buf[] = "Hello World, pread_all";
110 + char read_buf[sizeof(write_buf)] = {0};
111 + int ret;
112 +
113 + unlink(TEST_FILE);
114 + ioc = QIO_CHANNEL(qio_channel_file_new_path(
115 + TEST_FILE,
116 + O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
117 + TEST_MASK,
118 + &error_abort));
119 +
120 + ret = qio_channel_pwrite_all(ioc, write_buf, sizeof(write_buf),
121 + 0, &error_abort);
122 + g_assert_cmpint(ret, ==, 0);
123 +
124 + /* Read back at offset 0 */
125 + ret = qio_channel_pread_all(ioc, read_buf, sizeof(read_buf),
126 + 0, &error_abort);
127 + g_assert_cmpint(ret, ==, 0);
128 + g_assert_cmpmem(write_buf, sizeof(write_buf),
129 + read_buf, sizeof(read_buf));
130 +
131 + /* Read at a non-zero offset */
132 + memset(read_buf, 0, sizeof(read_buf));
133 + ret = qio_channel_pread_all(ioc, read_buf, sizeof(write_buf) - 7,
134 + 7, &error_abort);
135 + g_assert_cmpint(ret, ==, 0);
136 + g_assert_cmpmem(write_buf + 7, sizeof(write_buf) - 7,
137 + read_buf, sizeof(write_buf) - 7);
138 +
139 + unlink(TEST_FILE);
140 + object_unref(OBJECT(ioc));
141 +}
142 +
143 +static void test_io_channel_preadv_all(void)
144 +{
145 + QIOChannel *ioc;
146 + char write_buf[256];
147 + char read_buf[256] = {0};
148 + struct iovec write_iov[2];
149 + struct iovec read_iov[2];
150 + int ret;
151 + size_t i;
152 +
153 + for (i = 0; i < sizeof(write_buf); i++) {
154 + write_buf[i] = i & 0xff;
155 + }
156 +
157 + unlink(TEST_FILE);
158 + ioc = QIO_CHANNEL(qio_channel_file_new_path(
159 + TEST_FILE,
160 + O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
161 + TEST_MASK,
162 + &error_abort));
163 +
164 + /* Write using pwritev_all with 2 iovecs */
165 + write_iov[0].iov_base = write_buf;
166 + write_iov[0].iov_len = 128;
167 + write_iov[1].iov_base = write_buf + 128;
168 + write_iov[1].iov_len = 128;
169 + ret = qio_channel_pwritev_all(ioc, write_iov, 2, 0, &error_abort);
170 + g_assert_cmpint(ret, ==, 0);
171 +
172 + /* Read back using preadv_all with 2 iovecs */
173 + read_iov[0].iov_base = read_buf;
174 + read_iov[0].iov_len = 128;
175 + read_iov[1].iov_base = read_buf + 128;
176 + read_iov[1].iov_len = 128;
177 + ret = qio_channel_preadv_all(ioc, read_iov, 2, 0, &error_abort);
178 + g_assert_cmpint(ret, ==, 0);
179 +
180 + g_assert_cmpmem(write_buf, sizeof(write_buf),
181 + read_buf, sizeof(read_buf));
182 +
183 + /* Read at non-zero offset with preadv_all */
184 + memset(read_buf, 0, sizeof(read_buf));
185 + read_iov[0].iov_base = read_buf;
186 + read_iov[0].iov_len = 64;
187 + read_iov[1].iov_base = read_buf + 64;
188 + read_iov[1].iov_len = 64;
189 + ret = qio_channel_preadv_all(ioc, read_iov, 2, 128, &error_abort);
190 + g_assert_cmpint(ret, ==, 0);
191 +
192 + g_assert_cmpmem(write_buf + 128, 128,
193 + read_buf, 128);
194 +
195 + unlink(TEST_FILE);
196 + object_unref(OBJECT(ioc));
197 +}
198 +
199 +static void test_io_channel_preadv_all_eof(void)
200 +{
201 + QIOChannel *ioc;
202 + char write_buf[] = "Hello World, preadv_all_eof";
203 + char read_buf[sizeof(write_buf)] = {0};
204 + struct iovec iov;
205 + int ret;
206 + Error *err = NULL;
207 +
208 + unlink(TEST_FILE);
209 + ioc = QIO_CHANNEL(qio_channel_file_new_path(
210 + TEST_FILE,
211 + O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
212 + TEST_MASK,
213 + &error_abort));
214 +
215 + ret = qio_channel_pwrite_all(ioc, write_buf, sizeof(write_buf),
216 + 0, &error_abort);
217 + g_assert_cmpint(ret, ==, 0);
218 +
219 + /* Full read succeeds: should return 1 */
220 + iov.iov_base = read_buf;
221 + iov.iov_len = sizeof(read_buf);
222 + ret = qio_channel_preadv_all_eof(ioc, &iov, 1, 0, &error_abort);
223 + g_assert_cmpint(ret, ==, 1);
224 + g_assert_cmpmem(write_buf, sizeof(write_buf),
225 + read_buf, sizeof(read_buf));
226 +
227 + /* Clean EOF: offset at file end, should return 0 */
228 + iov.iov_base = read_buf;
229 + iov.iov_len = 1;
230 + ret = qio_channel_preadv_all_eof(ioc, &iov, 1,
231 + sizeof(write_buf), &err);
232 + g_assert_cmpint(ret, ==, 0);
233 + g_assert_null(err);
234 +
235 + /* Partial EOF: start before end, request extends past */
236 + iov.iov_base = read_buf;
237 + iov.iov_len = 8;
238 + ret = qio_channel_preadv_all_eof(ioc, &iov, 1,
239 + sizeof(write_buf) - 4, &err);
240 + g_assert_cmpint(ret, ==, -1);
241 + g_assert_nonnull(err);
242 + error_free(err);
243 + err = NULL;
244 +
245 + /* Strict wrapper (preadv_all) treats clean EOF as error */
246 + iov.iov_base = read_buf;
247 + iov.iov_len = 1;
248 + ret = qio_channel_preadv_all(ioc, &iov, 1,
249 + sizeof(write_buf), &err);
250 + g_assert_cmpint(ret, ==, -1);
251 + g_assert_nonnull(err);
252 + error_free(err);
253 +
254 + unlink(TEST_FILE);
255 + object_unref(OBJECT(ioc));
256 +}
257 +
258 +static void test_io_channel_pread_all_eof(void)
259 +{
260 + QIOChannel *ioc;
261 + char write_buf[] = "Hello World, pread_all_eof";
262 + char read_buf[sizeof(write_buf)] = {0};
263 + int ret;
264 + Error *err = NULL;
265 +
266 + unlink(TEST_FILE);
267 + ioc = QIO_CHANNEL(qio_channel_file_new_path(
268 + TEST_FILE,
269 + O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
270 + TEST_MASK,
271 + &error_abort));
272 +
273 + ret = qio_channel_pwrite_all(ioc, write_buf, sizeof(write_buf),
274 + 0, &error_abort);
275 + g_assert_cmpint(ret, ==, 0);
276 +
277 + /* Full read succeeds: should return 1 */
278 + ret = qio_channel_pread_all_eof(ioc, read_buf, sizeof(read_buf),
279 + 0, &error_abort);
280 + g_assert_cmpint(ret, ==, 1);
281 + g_assert_cmpmem(write_buf, sizeof(write_buf),
282 + read_buf, sizeof(read_buf));
283 +
284 + /* Clean EOF: should return 0 */
285 + ret = qio_channel_pread_all_eof(ioc, read_buf, 1,
286 + sizeof(write_buf), &err);
287 + g_assert_cmpint(ret, ==, 0);
288 + g_assert_null(err);
289 +
290 + /* Partial EOF: should return -1 */
291 + ret = qio_channel_pread_all_eof(ioc, read_buf, 8,
292 + sizeof(write_buf) - 4, &err);
293 + g_assert_cmpint(ret, ==, -1);
294 + g_assert_nonnull(err);
295 + error_free(err);
296 +
297 + unlink(TEST_FILE);
298 + object_unref(OBJECT(ioc));
299 +}
300 +#endif /* CONFIG_PREADV */
301 +
302 #ifndef _WIN32
303 static void test_io_channel_pipe(bool async)
304 {
@@ -147,6 +344,16 @@ int main(int argc, char **argv)
344 g_test_add_func("/io/channel/file", test_io_channel_file);
345 g_test_add_func("/io/channel/file/rdwr", test_io_channel_file_rdwr);
346 g_test_add_func("/io/channel/file/fd", test_io_channel_fd);
347 +#ifdef CONFIG_PREADV
348 + g_test_add_func("/io/channel/file/pread-all",
349 + test_io_channel_pread_all);
350 + g_test_add_func("/io/channel/file/preadv-all",
351 + test_io_channel_preadv_all);
352 + g_test_add_func("/io/channel/file/preadv-all-eof",
353 + test_io_channel_preadv_all_eof);
354 + g_test_add_func("/io/channel/file/pread-all-eof",
355 + test_io_channel_pread_all_eof);
356 +#endif
357 #ifndef _WIN32
358 g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync);
359 g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async);