]> git.proxmox.com Git - mirror_qemu.git/blob - tests/test-char.c
tests: add /char/file test
[mirror_qemu.git] / tests / test-char.c
1 #include "qemu/osdep.h"
2 #include <glib/gstdio.h>
3
4 #include "qemu-common.h"
5 #include "qemu/config-file.h"
6 #include "sysemu/char.h"
7 #include "sysemu/sysemu.h"
8 #include "qapi/error.h"
9 #include "qmp-commands.h"
10
11 static bool quit;
12
13 typedef struct FeHandler {
14 int read_count;
15 int last_event;
16 char read_buf[128];
17 } FeHandler;
18
19 #ifndef _WIN32
20 static void main_loop(void)
21 {
22 bool nonblocking;
23 int last_io = 0;
24
25 quit = false;
26 do {
27 nonblocking = last_io > 0;
28 last_io = main_loop_wait(nonblocking);
29 } while (!quit);
30 }
31 #endif
32
33 static int fe_can_read(void *opaque)
34 {
35 FeHandler *h = opaque;
36
37 return sizeof(h->read_buf) - h->read_count;
38 }
39
40 static void fe_read(void *opaque, const uint8_t *buf, int size)
41 {
42 FeHandler *h = opaque;
43
44 g_assert_cmpint(size, <=, fe_can_read(opaque));
45
46 memcpy(h->read_buf + h->read_count, buf, size);
47 h->read_count += size;
48 quit = true;
49 }
50
51 static void fe_event(void *opaque, int event)
52 {
53 FeHandler *h = opaque;
54
55 h->last_event = event;
56 quit = true;
57 }
58
59 #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
60 static void char_stdio_test_subprocess(void)
61 {
62 Chardev *chr;
63 CharBackend be;
64 int ret;
65
66 chr = qemu_chr_new("label", "stdio");
67 g_assert_nonnull(chr);
68
69 qemu_chr_fe_init(&be, chr, &error_abort);
70 qemu_chr_fe_set_open(&be, true);
71 ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
72 g_assert_cmpint(ret, ==, 4);
73
74 qemu_chr_fe_deinit(&be);
75 object_unparent(OBJECT(chr));
76 }
77
78 static void char_stdio_test(void)
79 {
80 g_test_trap_subprocess("/char/stdio/subprocess", 0, 0);
81 g_test_trap_assert_passed();
82 g_test_trap_assert_stdout("buf");
83 }
84 #endif
85
86
87 static void char_ringbuf_test(void)
88 {
89 QemuOpts *opts;
90 Chardev *chr;
91 CharBackend be;
92 char *data;
93 int ret;
94
95 opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
96 1, &error_abort);
97 qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
98
99 qemu_opt_set(opts, "size", "5", &error_abort);
100 chr = qemu_chr_new_from_opts(opts, NULL);
101 g_assert_null(chr);
102 qemu_opts_del(opts);
103
104 opts = qemu_opts_create(qemu_find_opts("chardev"), "ringbuf-label",
105 1, &error_abort);
106 qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
107 qemu_opt_set(opts, "size", "2", &error_abort);
108 chr = qemu_chr_new_from_opts(opts, &error_abort);
109 g_assert_nonnull(chr);
110 qemu_opts_del(opts);
111
112 qemu_chr_fe_init(&be, chr, &error_abort);
113 ret = qemu_chr_fe_write(&be, (void *)"buff", 4);
114 g_assert_cmpint(ret, ==, 4);
115
116 data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
117 g_assert_cmpstr(data, ==, "ff");
118 g_free(data);
119
120 data = qmp_ringbuf_read("ringbuf-label", 4, false, 0, &error_abort);
121 g_assert_cmpstr(data, ==, "");
122 g_free(data);
123
124 qemu_chr_fe_deinit(&be);
125 object_unparent(OBJECT(chr));
126
127 /* check alias */
128 opts = qemu_opts_create(qemu_find_opts("chardev"), "memory-label",
129 1, &error_abort);
130 qemu_opt_set(opts, "backend", "memory", &error_abort);
131 qemu_opt_set(opts, "size", "2", &error_abort);
132 chr = qemu_chr_new_from_opts(opts, NULL);
133 g_assert_nonnull(chr);
134 object_unparent(OBJECT(chr));
135 qemu_opts_del(opts);
136 }
137
138 static void char_mux_test(void)
139 {
140 QemuOpts *opts;
141 Chardev *chr, *base;
142 char *data;
143 FeHandler h1 = { 0, }, h2 = { 0, };
144 CharBackend chr_be1, chr_be2;
145
146 opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
147 1, &error_abort);
148 qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
149 qemu_opt_set(opts, "size", "128", &error_abort);
150 qemu_opt_set(opts, "mux", "on", &error_abort);
151 chr = qemu_chr_new_from_opts(opts, &error_abort);
152 g_assert_nonnull(chr);
153 qemu_opts_del(opts);
154
155 qemu_chr_fe_init(&chr_be1, chr, &error_abort);
156 qemu_chr_fe_set_handlers(&chr_be1,
157 fe_can_read,
158 fe_read,
159 fe_event,
160 &h1,
161 NULL, true);
162
163 qemu_chr_fe_init(&chr_be2, chr, &error_abort);
164 qemu_chr_fe_set_handlers(&chr_be2,
165 fe_can_read,
166 fe_read,
167 fe_event,
168 &h2,
169 NULL, true);
170 qemu_chr_fe_take_focus(&chr_be2);
171
172 base = qemu_chr_find("mux-label-base");
173 g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0);
174
175 qemu_chr_be_write(base, (void *)"hello", 6);
176 g_assert_cmpint(h1.read_count, ==, 0);
177 g_assert_cmpint(h2.read_count, ==, 6);
178 g_assert_cmpstr(h2.read_buf, ==, "hello");
179 h2.read_count = 0;
180
181 /* switch focus */
182 qemu_chr_be_write(base, (void *)"\1c", 2);
183
184 qemu_chr_be_write(base, (void *)"hello", 6);
185 g_assert_cmpint(h2.read_count, ==, 0);
186 g_assert_cmpint(h1.read_count, ==, 6);
187 g_assert_cmpstr(h1.read_buf, ==, "hello");
188 h1.read_count = 0;
189
190 /* remove first handler */
191 qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, true);
192 qemu_chr_be_write(base, (void *)"hello", 6);
193 g_assert_cmpint(h1.read_count, ==, 0);
194 g_assert_cmpint(h2.read_count, ==, 0);
195
196 qemu_chr_be_write(base, (void *)"\1c", 2);
197 qemu_chr_be_write(base, (void *)"hello", 6);
198 g_assert_cmpint(h1.read_count, ==, 0);
199 g_assert_cmpint(h2.read_count, ==, 6);
200 g_assert_cmpstr(h2.read_buf, ==, "hello");
201 h2.read_count = 0;
202
203 /* print help */
204 qemu_chr_be_write(base, (void *)"\1?", 2);
205 data = qmp_ringbuf_read("mux-label-base", 128, false, 0, &error_abort);
206 g_assert_cmpint(strlen(data), !=, 0);
207 g_free(data);
208
209 qemu_chr_fe_deinit(&chr_be1);
210 qemu_chr_fe_deinit(&chr_be2);
211 object_unparent(OBJECT(chr));
212 }
213
214 #ifndef _WIN32
215 static void char_pipe_test(void)
216 {
217 gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL);
218 gchar *tmp, *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL);
219 Chardev *chr;
220 CharBackend be;
221 int ret, fd;
222 char buf[10];
223 FeHandler fe = { 0, };
224
225 in = g_strdup_printf("%s.in", pipe);
226 if (mkfifo(in, 0600) < 0) {
227 abort();
228 }
229 out = g_strdup_printf("%s.out", pipe);
230 if (mkfifo(out, 0600) < 0) {
231 abort();
232 }
233
234 tmp = g_strdup_printf("pipe:%s", pipe);
235 chr = qemu_chr_new("pipe", tmp);
236 g_assert_nonnull(chr);
237 g_free(tmp);
238
239 qemu_chr_fe_init(&be, chr, &error_abort);
240
241 ret = qemu_chr_fe_write(&be, (void *)"pipe-out", 9);
242 g_assert_cmpint(ret, ==, 9);
243
244 fd = open(out, O_RDWR);
245 ret = read(fd, buf, sizeof(buf));
246 g_assert_cmpint(ret, ==, 9);
247 g_assert_cmpstr(buf, ==, "pipe-out");
248 close(fd);
249
250 fd = open(in, O_WRONLY);
251 ret = write(fd, "pipe-in", 8);
252 g_assert_cmpint(ret, ==, 8);
253 close(fd);
254
255 qemu_chr_fe_set_handlers(&be,
256 fe_can_read,
257 fe_read,
258 fe_event,
259 &fe,
260 NULL, true);
261
262 main_loop();
263
264 g_assert_cmpint(fe.read_count, ==, 8);
265 g_assert_cmpstr(fe.read_buf, ==, "pipe-in");
266
267 qemu_chr_fe_deinit(&be);
268 object_unparent(OBJECT(chr));
269
270 g_assert(g_unlink(in) == 0);
271 g_assert(g_unlink(out) == 0);
272 g_assert(g_rmdir(tmp_path) == 0);
273 g_free(in);
274 g_free(out);
275 g_free(tmp_path);
276 g_free(pipe);
277 }
278 #endif
279
280 static void char_file_test(void)
281 {
282 char *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL);
283 char *out = g_build_filename(tmp_path, "out", NULL);
284 char *contents = NULL;
285 ChardevFile file = { .out = out };
286 ChardevBackend backend = { .type = CHARDEV_BACKEND_KIND_FILE,
287 .u.file.data = &file };
288 Chardev *chr;
289 gsize length;
290 int ret;
291
292 chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend,
293 &error_abort);
294 ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6);
295 g_assert_cmpint(ret, ==, 6);
296 object_unref(OBJECT(chr));
297
298 ret = g_file_get_contents(out, &contents, &length, NULL);
299 g_assert(ret == TRUE);
300 g_assert_cmpint(length, ==, 6);
301 g_assert(strncmp(contents, "hello!", 6) == 0);
302 g_free(contents);
303
304 #ifndef _WIN32
305 {
306 CharBackend be;
307 FeHandler fe = { 0, };
308 char *fifo = g_build_filename(tmp_path, "fifo", NULL);
309 int fd;
310
311 if (mkfifo(fifo, 0600) < 0) {
312 abort();
313 }
314
315 fd = open(fifo, O_RDWR);
316 ret = write(fd, "fifo-in", 8);
317 g_assert_cmpint(ret, ==, 8);
318
319 file.in = fifo;
320 file.has_in = true;
321 chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend,
322 &error_abort);
323
324 qemu_chr_fe_init(&be, chr, &error_abort);
325 qemu_chr_fe_set_handlers(&be,
326 fe_can_read,
327 fe_read,
328 fe_event,
329 &fe, NULL, true);
330
331 main_loop();
332
333 close(fd);
334
335 g_assert_cmpint(fe.read_count, ==, 8);
336 g_assert_cmpstr(fe.read_buf, ==, "fifo-in");
337 qemu_chr_fe_deinit(&be);
338 object_unref(OBJECT(chr));
339 g_unlink(fifo);
340 g_free(fifo);
341 }
342 #endif
343
344 g_unlink(out);
345 g_rmdir(tmp_path);
346 g_free(tmp_path);
347 g_free(out);
348 }
349
350 static void char_null_test(void)
351 {
352 Error *err = NULL;
353 Chardev *chr;
354 CharBackend be;
355 int ret;
356
357 chr = qemu_chr_find("label-null");
358 g_assert_null(chr);
359
360 chr = qemu_chr_new("label-null", "null");
361 chr = qemu_chr_find("label-null");
362 g_assert_nonnull(chr);
363
364 g_assert(qemu_chr_has_feature(chr,
365 QEMU_CHAR_FEATURE_FD_PASS) == false);
366 g_assert(qemu_chr_has_feature(chr,
367 QEMU_CHAR_FEATURE_RECONNECTABLE) == false);
368
369 /* check max avail */
370 qemu_chr_fe_init(&be, chr, &error_abort);
371 qemu_chr_fe_init(&be, chr, &err);
372 error_free_or_abort(&err);
373
374 /* deinit & reinit */
375 qemu_chr_fe_deinit(&be);
376 qemu_chr_fe_init(&be, chr, &error_abort);
377
378 qemu_chr_fe_set_open(&be, true);
379
380 qemu_chr_fe_set_handlers(&be,
381 fe_can_read,
382 fe_read,
383 fe_event,
384 NULL, NULL, true);
385
386 ret = qemu_chr_fe_write(&be, (void *)"buf", 4);
387 g_assert_cmpint(ret, ==, 4);
388
389 qemu_chr_fe_deinit(&be);
390 object_unparent(OBJECT(chr));
391 }
392
393 static void char_invalid_test(void)
394 {
395 Chardev *chr;
396
397 chr = qemu_chr_new("label-invalid", "invalid");
398 g_assert_null(chr);
399 }
400
401 int main(int argc, char **argv)
402 {
403 qemu_init_main_loop(&error_abort);
404
405 g_test_init(&argc, &argv, NULL);
406
407 module_call_init(MODULE_INIT_QOM);
408 qemu_add_opts(&qemu_chardev_opts);
409
410 g_test_add_func("/char/null", char_null_test);
411 g_test_add_func("/char/invalid", char_invalid_test);
412 g_test_add_func("/char/ringbuf", char_ringbuf_test);
413 g_test_add_func("/char/mux", char_mux_test);
414 #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
415 g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess);
416 g_test_add_func("/char/stdio", char_stdio_test);
417 #endif
418 #ifndef _WIN32
419 g_test_add_func("/char/pipe", char_pipe_test);
420 #endif
421 g_test_add_func("/char/file", char_file_test);
422
423 return g_test_run();
424 }