]> git.proxmox.com Git - mirror_qemu.git/blame - tests/test-util-filemonitor.c
Revert "vl: Fix to create migration object before block backends again"
[mirror_qemu.git] / tests / test-util-filemonitor.c
CommitLineData
90e33dfe
DB
1/*
2 * Tests for util/filemonitor-*.c
3 *
4 * Copyright 2018 Red Hat, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include "qemu/osdep.h"
22#include "qemu/main-loop.h"
23#include "qapi/error.h"
24#include "qemu/filemonitor.h"
25
26#include <utime.h>
27
28enum {
29 QFILE_MONITOR_TEST_OP_CREATE,
30 QFILE_MONITOR_TEST_OP_APPEND,
31 QFILE_MONITOR_TEST_OP_TRUNC,
32 QFILE_MONITOR_TEST_OP_RENAME,
33 QFILE_MONITOR_TEST_OP_TOUCH,
34 QFILE_MONITOR_TEST_OP_UNLINK,
35};
36
37typedef struct {
38 int type;
39 const char *filesrc;
40 const char *filedst;
41} QFileMonitorTestOp;
42
43typedef struct {
44 const char *file;
45} QFileMonitorTestWatch;
46
47typedef struct {
48 gsize nwatches;
49 const QFileMonitorTestWatch *watches;
50
51 gsize nops;
52 const QFileMonitorTestOp *ops;
53} QFileMonitorTestPlan;
54
55typedef struct {
56 int id;
57 QFileMonitorEvent event;
58 char *filename;
59} QFileMonitorTestRecord;
60
61
62typedef struct {
63 QemuMutex lock;
64 GList *records;
65} QFileMonitorTestData;
66
67static QemuMutex evlock;
68static bool evstopping;
69static bool evrunning;
70
71/*
72 * Main function for a background thread that is
73 * running the event loop during the test
74 */
75static void *
76qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
77{
78 qemu_mutex_lock(&evlock);
79
80 while (!evstopping) {
81 qemu_mutex_unlock(&evlock);
82 main_loop_wait(true);
83 qemu_mutex_lock(&evlock);
84 }
85
86 evrunning = false;
87 qemu_mutex_unlock(&evlock);
88 return NULL;
89}
90
91
92/*
93 * File monitor event handler which simply maintains
94 * an ordered list of all events that it receives
95 */
96static void
97qemu_file_monitor_test_handler(int id,
98 QFileMonitorEvent event,
99 const char *filename,
100 void *opaque)
101{
102 QFileMonitorTestData *data = opaque;
103 QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
104
105 rec->id = id;
106 rec->event = event;
107 rec->filename = g_strdup(filename);
108
109 qemu_mutex_lock(&data->lock);
110 data->records = g_list_append(data->records, rec);
111 qemu_mutex_unlock(&data->lock);
112}
113
114
115static void
116qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
117{
118 g_free(rec->filename);
119 g_free(rec);
120}
121
122
123/*
124 * Get the next event record that has been received by
125 * the file monitor event handler. Since events are
126 * emitted in the background thread running the event
127 * loop, we can't assume there is a record available
128 * immediately. Thus we will sleep for upto 5 seconds
129 * to wait for the event to be queued for us.
130 */
131static QFileMonitorTestRecord *
132qemu_file_monitor_test_next_record(QFileMonitorTestData *data)
133{
134 GTimer *timer = g_timer_new();
135 QFileMonitorTestRecord *record = NULL;
136 GList *tmp;
137
138 qemu_mutex_lock(&data->lock);
139 while (!data->records && g_timer_elapsed(timer, NULL) < 5) {
140 qemu_mutex_unlock(&data->lock);
141 usleep(10 * 1000);
142 qemu_mutex_lock(&data->lock);
143 }
144 if (data->records) {
145 record = data->records->data;
146 tmp = data->records;
147 data->records = g_list_remove_link(data->records, tmp);
148 g_list_free(tmp);
149 }
150 qemu_mutex_unlock(&data->lock);
151
152 g_timer_destroy(timer);
153 return record;
154}
155
156
157/*
158 * Check whether the event record we retrieved matches
159 * data we were expecting to see for the event
160 */
161static bool
162qemu_file_monitor_test_expect(QFileMonitorTestData *data,
163 int id,
164 QFileMonitorEvent event,
165 const char *filename)
166{
167 QFileMonitorTestRecord *rec;
168 bool ret = false;
169
170 rec = qemu_file_monitor_test_next_record(data);
171
172 if (!rec) {
173 g_printerr("Missing event watch id %d event %d file %s\n",
174 id, event, filename);
175 return false;
176 }
177
178 if (id != rec->id) {
179 g_printerr("Expected watch id %d but got %d\n", id, rec->id);
180 goto cleanup;
181 }
182
183 if (event != rec->event) {
184 g_printerr("Expected event %d but got %d\n", event, rec->event);
185 goto cleanup;
186 }
187
188 if (!g_str_equal(filename, rec->filename)) {
189 g_printerr("Expected filename %s but got %s\n",
190 filename, rec->filename);
191 goto cleanup;
192 }
193
194 ret = true;
195
196 cleanup:
197 qemu_file_monitor_test_record_free(rec);
198 return ret;
199}
200
201
202static void
203test_file_monitor_events(const void *opaque)
204{
205 const QFileMonitorTestPlan *plan = opaque;
206 Error *local_err = NULL;
207 GError *gerr = NULL;
208 QFileMonitor *mon = qemu_file_monitor_new(&local_err);
209 QemuThread th;
210 GTimer *timer;
211 gchar *dir = NULL;
212 int err = -1;
213 gsize i, j;
214 char *pathsrc = NULL;
215 char *pathdst = NULL;
216 QFileMonitorTestData data;
217
218 qemu_mutex_init(&data.lock);
219 data.records = NULL;
220
221 /*
222 * The file monitor needs the main loop running in
223 * order to receive events from inotify. We must
224 * thus spawn a background thread to run an event
225 * loop impl, while this thread triggers the
226 * actual file operations we're testing
227 */
228 evrunning = 1;
229 evstopping = 0;
230 qemu_thread_create(&th, "event-loop",
231 qemu_file_monitor_test_event_loop, NULL,
232 QEMU_THREAD_JOINABLE);
233
234 if (local_err) {
235 g_printerr("File monitoring not available: %s",
236 error_get_pretty(local_err));
237 error_free(local_err);
238 return;
239 }
240
241 dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
242 &gerr);
243 if (!dir) {
244 g_printerr("Unable to create tmp dir %s",
245 gerr->message);
246 g_error_free(gerr);
247 abort();
248 }
249
250 /*
251 * First register all the directory / file watches
252 * we're interested in seeing events against
253 */
254 for (i = 0; i < plan->nwatches; i++) {
255 int watchid;
256 watchid = qemu_file_monitor_add_watch(mon,
257 dir,
258 plan->watches[i].file,
259 qemu_file_monitor_test_handler,
260 &data,
261 &local_err);
262 if (watchid < 0) {
263 g_printerr("Unable to add watch %s",
264 error_get_pretty(local_err));
265 goto cleanup;
266 }
267 }
268
269
270 /*
271 * Now invoke all the file operations (create,
272 * delete, rename, chmod, etc). These operations
273 * will trigger the various file monitor events
274 */
275 for (i = 0; i < plan->nops; i++) {
276 const QFileMonitorTestOp *op = &(plan->ops[i]);
277 int fd;
278 struct utimbuf ubuf;
279
280 pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
281 if (op->filedst) {
282 pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
283 }
284
285 switch (op->type) {
286 case QFILE_MONITOR_TEST_OP_CREATE:
287 fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
288 if (fd < 0) {
289 g_printerr("Unable to create %s: %s",
290 pathsrc, strerror(errno));
291 goto cleanup;
292 }
293 close(fd);
294 break;
295
296 case QFILE_MONITOR_TEST_OP_APPEND:
297 fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
298 if (fd < 0) {
299 g_printerr("Unable to open %s: %s",
300 pathsrc, strerror(errno));
301 goto cleanup;
302 }
303
304 if (write(fd, "Hello World", 10) != 10) {
305 g_printerr("Unable to write %s: %s",
306 pathsrc, strerror(errno));
307 close(fd);
308 goto cleanup;
309 }
310 close(fd);
311 break;
312
313 case QFILE_MONITOR_TEST_OP_TRUNC:
314 if (truncate(pathsrc, 4) < 0) {
315 g_printerr("Unable to truncate %s: %s",
316 pathsrc, strerror(errno));
317 goto cleanup;
318 }
319 break;
320
321 case QFILE_MONITOR_TEST_OP_RENAME:
322 if (rename(pathsrc, pathdst) < 0) {
323 g_printerr("Unable to rename %s to %s: %s",
324 pathsrc, pathdst, strerror(errno));
325 goto cleanup;
326 }
327 break;
328
329 case QFILE_MONITOR_TEST_OP_UNLINK:
330 if (unlink(pathsrc) < 0) {
331 g_printerr("Unable to unlink %s: %s",
332 pathsrc, strerror(errno));
333 goto cleanup;
334 }
335 break;
336
337 case QFILE_MONITOR_TEST_OP_TOUCH:
338 ubuf.actime = 1024;
339 ubuf.modtime = 1025;
340 if (utime(pathsrc, &ubuf) < 0) {
341 g_printerr("Unable to touch %s: %s",
342 pathsrc, strerror(errno));
343 goto cleanup;
344 }
345 break;
346
347 default:
348 g_assert_not_reached();
349 }
350
351 g_free(pathsrc);
352 g_free(pathdst);
353 pathsrc = pathdst = NULL;
354 }
355
356
357 /*
358 * Finally validate that we have received all the events
359 * we expect to see for the combination of watches and
360 * file operations
361 */
362 for (i = 0; i < plan->nops; i++) {
363 const QFileMonitorTestOp *op = &(plan->ops[i]);
364
365 switch (op->type) {
366 case QFILE_MONITOR_TEST_OP_CREATE:
367 for (j = 0; j < plan->nwatches; j++) {
368 if (plan->watches[j].file &&
369 !g_str_equal(plan->watches[j].file, op->filesrc))
370 continue;
371
372 if (!qemu_file_monitor_test_expect(
373 &data, j, QFILE_MONITOR_EVENT_CREATED, op->filesrc))
374 goto cleanup;
375 }
376 break;
377
378 case QFILE_MONITOR_TEST_OP_APPEND:
379 case QFILE_MONITOR_TEST_OP_TRUNC:
380 for (j = 0; j < plan->nwatches; j++) {
381 if (plan->watches[j].file &&
382 !g_str_equal(plan->watches[j].file, op->filesrc))
383 continue;
384
385 if (!qemu_file_monitor_test_expect(
386 &data, j, QFILE_MONITOR_EVENT_MODIFIED, op->filesrc))
387 goto cleanup;
388 }
389 break;
390
391 case QFILE_MONITOR_TEST_OP_RENAME:
392 for (j = 0; j < plan->nwatches; j++) {
393 if (plan->watches[j].file &&
394 !g_str_equal(plan->watches[j].file, op->filesrc))
395 continue;
396
397 if (!qemu_file_monitor_test_expect(
398 &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
399 goto cleanup;
400 }
401
402 for (j = 0; j < plan->nwatches; j++) {
403 if (plan->watches[j].file &&
404 !g_str_equal(plan->watches[j].file, op->filedst))
405 continue;
406
407 if (!qemu_file_monitor_test_expect(
408 &data, j, QFILE_MONITOR_EVENT_CREATED, op->filedst))
409 goto cleanup;
410 }
411 break;
412
413 case QFILE_MONITOR_TEST_OP_TOUCH:
414 for (j = 0; j < plan->nwatches; j++) {
415 if (plan->watches[j].file &&
416 !g_str_equal(plan->watches[j].file, op->filesrc))
417 continue;
418
419 if (!qemu_file_monitor_test_expect(
420 &data, j, QFILE_MONITOR_EVENT_ATTRIBUTES, op->filesrc))
421 goto cleanup;
422 }
423 break;
424
425 case QFILE_MONITOR_TEST_OP_UNLINK:
426 for (j = 0; j < plan->nwatches; j++) {
427 if (plan->watches[j].file &&
428 !g_str_equal(plan->watches[j].file, op->filesrc))
429 continue;
430
431 if (!qemu_file_monitor_test_expect(
432 &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
433 goto cleanup;
434 }
435 break;
436
437 default:
438 g_assert_not_reached();
439 }
440 }
441
442 err = 0;
443
444 cleanup:
445 g_free(pathsrc);
446 g_free(pathdst);
447
448 qemu_mutex_lock(&evlock);
449 evstopping = 1;
450 timer = g_timer_new();
451 while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
452 qemu_mutex_unlock(&evlock);
453 usleep(10 * 1000);
454 qemu_mutex_lock(&evlock);
455 }
456 qemu_mutex_unlock(&evlock);
457
458 if (g_timer_elapsed(timer, NULL) >= 5) {
459 g_printerr("Event loop failed to quit after 5 seconds\n");
460 }
461 g_timer_destroy(timer);
462
463 for (i = 0; i < plan->nops; i++) {
464 const QFileMonitorTestOp *op = &(plan->ops[i]);
465 pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
466 unlink(pathsrc);
467 g_free(pathsrc);
468 if (op->filedst) {
469 pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
470 unlink(pathdst);
471 g_free(pathdst);
472 }
473 }
474
475 qemu_file_monitor_free(mon);
476 g_list_foreach(data.records,
477 (GFunc)qemu_file_monitor_test_record_free, NULL);
478 g_list_free(data.records);
479 qemu_mutex_destroy(&data.lock);
480 if (dir) {
481 rmdir(dir);
482 }
483 g_free(dir);
484 g_assert(err == 0);
485}
486
487
488/*
489 * Set of structs which define which file name patterns
490 * we're trying to watch against. NULL, means all files
491 * in the directory
492 */
493static const QFileMonitorTestWatch watches_any[] = {
494 { NULL },
495};
496
497static const QFileMonitorTestWatch watches_one[] = {
498 { "one.txt" },
499};
500
501static const QFileMonitorTestWatch watches_two[] = {
502 { "two.txt" },
503};
504
505static const QFileMonitorTestWatch watches_many[] = {
506 { NULL },
507 { "one.txt" },
508 { "two.txt" },
509};
510
511
512/*
513 * Various sets of file operations we're going to
514 * trigger and validate events for
515 */
516static const QFileMonitorTestOp ops_create_one[] = {
517 { .type = QFILE_MONITOR_TEST_OP_CREATE,
518 .filesrc = "one.txt", }
519};
520
521static const QFileMonitorTestOp ops_delete_one[] = {
522 { .type = QFILE_MONITOR_TEST_OP_CREATE,
523 .filesrc = "one.txt", },
524 { .type = QFILE_MONITOR_TEST_OP_UNLINK,
525 .filesrc = "one.txt", }
526};
527
528static const QFileMonitorTestOp ops_create_many[] = {
529 { .type = QFILE_MONITOR_TEST_OP_CREATE,
530 .filesrc = "one.txt", },
531 { .type = QFILE_MONITOR_TEST_OP_CREATE,
532 .filesrc = "two.txt", },
533 { .type = QFILE_MONITOR_TEST_OP_CREATE,
534 .filesrc = "three.txt", }
535};
536
537static const QFileMonitorTestOp ops_rename_one[] = {
538 { .type = QFILE_MONITOR_TEST_OP_CREATE,
539 .filesrc = "one.txt", },
540 { .type = QFILE_MONITOR_TEST_OP_RENAME,
541 .filesrc = "one.txt", .filedst = "two.txt" }
542};
543
544static const QFileMonitorTestOp ops_rename_many[] = {
545 { .type = QFILE_MONITOR_TEST_OP_CREATE,
546 .filesrc = "one.txt", },
547 { .type = QFILE_MONITOR_TEST_OP_CREATE,
548 .filesrc = "two.txt", },
549 { .type = QFILE_MONITOR_TEST_OP_RENAME,
550 .filesrc = "one.txt", .filedst = "two.txt" }
551};
552
553static const QFileMonitorTestOp ops_append_one[] = {
554 { .type = QFILE_MONITOR_TEST_OP_CREATE,
555 .filesrc = "one.txt", },
556 { .type = QFILE_MONITOR_TEST_OP_APPEND,
557 .filesrc = "one.txt", },
558};
559
560static const QFileMonitorTestOp ops_trunc_one[] = {
561 { .type = QFILE_MONITOR_TEST_OP_CREATE,
562 .filesrc = "one.txt", },
563 { .type = QFILE_MONITOR_TEST_OP_TRUNC,
564 .filesrc = "one.txt", },
565};
566
567static const QFileMonitorTestOp ops_touch_one[] = {
568 { .type = QFILE_MONITOR_TEST_OP_CREATE,
569 .filesrc = "one.txt", },
570 { .type = QFILE_MONITOR_TEST_OP_TOUCH,
571 .filesrc = "one.txt", },
572};
573
574
575/*
576 * No we define data sets for the combinatorial
577 * expansion of file watches and operation sets
578 */
579#define PLAN_DATA(o, w) \
580 static const QFileMonitorTestPlan plan_ ## o ## _ ## w = { \
581 .nops = G_N_ELEMENTS(ops_ ##o), \
582 .ops = ops_ ##o, \
583 .nwatches = G_N_ELEMENTS(watches_ ##w), \
584 .watches = watches_ ## w, \
585 }
586
587PLAN_DATA(create_one, any);
588PLAN_DATA(create_one, one);
589PLAN_DATA(create_one, two);
590PLAN_DATA(create_one, many);
591
592PLAN_DATA(delete_one, any);
593PLAN_DATA(delete_one, one);
594PLAN_DATA(delete_one, two);
595PLAN_DATA(delete_one, many);
596
597PLAN_DATA(create_many, any);
598PLAN_DATA(create_many, one);
599PLAN_DATA(create_many, two);
600PLAN_DATA(create_many, many);
601
602PLAN_DATA(rename_one, any);
603PLAN_DATA(rename_one, one);
604PLAN_DATA(rename_one, two);
605PLAN_DATA(rename_one, many);
606
607PLAN_DATA(rename_many, any);
608PLAN_DATA(rename_many, one);
609PLAN_DATA(rename_many, two);
610PLAN_DATA(rename_many, many);
611
612PLAN_DATA(append_one, any);
613PLAN_DATA(append_one, one);
614PLAN_DATA(append_one, two);
615PLAN_DATA(append_one, many);
616
617PLAN_DATA(trunc_one, any);
618PLAN_DATA(trunc_one, one);
619PLAN_DATA(trunc_one, two);
620PLAN_DATA(trunc_one, many);
621
622PLAN_DATA(touch_one, any);
623PLAN_DATA(touch_one, one);
624PLAN_DATA(touch_one, two);
625PLAN_DATA(touch_one, many);
626
627
628int main(int argc, char **argv)
629{
630 g_test_init(&argc, &argv, NULL);
631
632 qemu_init_main_loop(&error_abort);
633
634 qemu_mutex_init(&evlock);
635
636 /*
637 * Register test cases for the combinatorial
638 * expansion of file watches and operation sets
639 */
640 #define PLAN_REGISTER(o, w) \
641 g_test_add_data_func("/util/filemonitor/" # o "/" # w, \
642 &plan_ ## o ## _ ## w, test_file_monitor_events)
643
644 PLAN_REGISTER(create_one, any);
645 PLAN_REGISTER(create_one, one);
646 PLAN_REGISTER(create_one, two);
647 PLAN_REGISTER(create_one, many);
648
649 PLAN_REGISTER(delete_one, any);
650 PLAN_REGISTER(delete_one, one);
651 PLAN_REGISTER(delete_one, two);
652 PLAN_REGISTER(delete_one, many);
653
654 PLAN_REGISTER(create_many, any);
655 PLAN_REGISTER(create_many, one);
656 PLAN_REGISTER(create_many, two);
657 PLAN_REGISTER(create_many, many);
658
659 PLAN_REGISTER(rename_one, any);
660 PLAN_REGISTER(rename_one, one);
661 PLAN_REGISTER(rename_one, two);
662 PLAN_REGISTER(rename_one, many);
663
664 PLAN_REGISTER(rename_many, any);
665 PLAN_REGISTER(rename_many, one);
666 PLAN_REGISTER(rename_many, two);
667 PLAN_REGISTER(rename_many, many);
668
669 PLAN_REGISTER(append_one, any);
670 PLAN_REGISTER(append_one, one);
671 PLAN_REGISTER(append_one, two);
672 PLAN_REGISTER(append_one, many);
673
674 PLAN_REGISTER(trunc_one, any);
675 PLAN_REGISTER(trunc_one, one);
676 PLAN_REGISTER(trunc_one, two);
677 PLAN_REGISTER(trunc_one, many);
678
679 PLAN_REGISTER(touch_one, any);
680 PLAN_REGISTER(touch_one, one);
681 PLAN_REGISTER(touch_one, two);
682 PLAN_REGISTER(touch_one, many);
683
684 return g_test_run();
685}