meson: generate qemu-version.h
[qemu/kevin.git] / tests / test-util-filemonitor.c
blob8f0eff3d03ea3f1e137ec443c2a00baa26904236
1 /*
2 * Tests for util/filemonitor-*.c
4 * Copyright 2018 Red Hat, Inc.
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.
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.
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/>.
21 #include "qemu/osdep.h"
22 #include "qemu/main-loop.h"
23 #include "qapi/error.h"
24 #include "qemu/filemonitor.h"
26 #include <utime.h>
28 enum {
29 QFILE_MONITOR_TEST_OP_ADD_WATCH,
30 QFILE_MONITOR_TEST_OP_DEL_WATCH,
31 QFILE_MONITOR_TEST_OP_EVENT,
32 QFILE_MONITOR_TEST_OP_CREATE,
33 QFILE_MONITOR_TEST_OP_APPEND,
34 QFILE_MONITOR_TEST_OP_TRUNC,
35 QFILE_MONITOR_TEST_OP_RENAME,
36 QFILE_MONITOR_TEST_OP_TOUCH,
37 QFILE_MONITOR_TEST_OP_UNLINK,
38 QFILE_MONITOR_TEST_OP_MKDIR,
39 QFILE_MONITOR_TEST_OP_RMDIR,
42 typedef struct {
43 int type;
44 const char *filesrc;
45 const char *filedst;
46 int64_t *watchid;
47 int eventid;
49 * Only valid with OP_EVENT - this event might be
50 * swapped with the next OP_EVENT
52 bool swapnext;
53 } QFileMonitorTestOp;
55 typedef struct {
56 int64_t id;
57 QFileMonitorEvent event;
58 char *filename;
59 } QFileMonitorTestRecord;
62 typedef struct {
63 QemuMutex lock;
64 GList *records;
65 } QFileMonitorTestData;
67 static QemuMutex evlock;
68 static bool evstopping;
69 static bool evrunning;
70 static bool debug;
73 * Main function for a background thread that is
74 * running the event loop during the test
76 static void *
77 qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
79 qemu_mutex_lock(&evlock);
81 while (!evstopping) {
82 qemu_mutex_unlock(&evlock);
83 main_loop_wait(true);
84 qemu_mutex_lock(&evlock);
87 evrunning = false;
88 qemu_mutex_unlock(&evlock);
89 return NULL;
94 * File monitor event handler which simply maintains
95 * an ordered list of all events that it receives
97 static void
98 qemu_file_monitor_test_handler(int64_t id,
99 QFileMonitorEvent event,
100 const char *filename,
101 void *opaque)
103 QFileMonitorTestData *data = opaque;
104 QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
106 if (debug) {
107 g_printerr("Queue event id %" PRIx64 " event %d file %s\n",
108 id, event, filename);
110 rec->id = id;
111 rec->event = event;
112 rec->filename = g_strdup(filename);
114 qemu_mutex_lock(&data->lock);
115 data->records = g_list_append(data->records, rec);
116 qemu_mutex_unlock(&data->lock);
120 static void
121 qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
123 g_free(rec->filename);
124 g_free(rec);
129 * Get the next event record that has been received by
130 * the file monitor event handler. Since events are
131 * emitted in the background thread running the event
132 * loop, we can't assume there is a record available
133 * immediately. Thus we will sleep for upto 5 seconds
134 * to wait for the event to be queued for us.
136 static QFileMonitorTestRecord *
137 qemu_file_monitor_test_next_record(QFileMonitorTestData *data,
138 QFileMonitorTestRecord *pushback)
140 GTimer *timer = g_timer_new();
141 QFileMonitorTestRecord *record = NULL;
142 GList *tmp;
144 qemu_mutex_lock(&data->lock);
145 while (!data->records && g_timer_elapsed(timer, NULL) < 5) {
146 qemu_mutex_unlock(&data->lock);
147 usleep(10 * 1000);
148 qemu_mutex_lock(&data->lock);
150 if (data->records) {
151 record = data->records->data;
152 if (pushback) {
153 data->records->data = pushback;
154 } else {
155 tmp = data->records;
156 data->records = g_list_remove_link(data->records, tmp);
157 g_list_free(tmp);
159 } else if (pushback) {
160 qemu_file_monitor_test_record_free(pushback);
162 qemu_mutex_unlock(&data->lock);
164 g_timer_destroy(timer);
165 return record;
170 * Check whether the event record we retrieved matches
171 * data we were expecting to see for the event
173 static bool
174 qemu_file_monitor_test_expect(QFileMonitorTestData *data,
175 int64_t id,
176 QFileMonitorEvent event,
177 const char *filename,
178 bool swapnext)
180 QFileMonitorTestRecord *rec;
181 bool ret = false;
183 rec = qemu_file_monitor_test_next_record(data, NULL);
185 retry:
186 if (!rec) {
187 g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n",
188 id, event, filename);
189 return false;
192 if (id != rec->id) {
193 if (swapnext) {
194 rec = qemu_file_monitor_test_next_record(data, rec);
195 swapnext = false;
196 goto retry;
198 g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n",
199 id, rec->id);
200 goto cleanup;
203 if (event != rec->event) {
204 g_printerr("Expected event %d but got %d\n", event, rec->event);
205 goto cleanup;
208 if (!g_str_equal(filename, rec->filename)) {
209 g_printerr("Expected filename %s but got %s\n",
210 filename, rec->filename);
211 goto cleanup;
214 ret = true;
216 cleanup:
217 qemu_file_monitor_test_record_free(rec);
218 return ret;
222 static void
223 test_file_monitor_events(void)
225 int64_t watch0 = 0;
226 int64_t watch1 = 0;
227 int64_t watch2 = 0;
228 int64_t watch3 = 0;
229 int64_t watch4 = 0;
230 int64_t watch5 = 0;
231 QFileMonitorTestOp ops[] = {
232 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
233 .filesrc = NULL, .watchid = &watch0 },
234 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
235 .filesrc = "one.txt", .watchid = &watch1 },
236 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
237 .filesrc = "two.txt", .watchid = &watch2 },
240 { .type = QFILE_MONITOR_TEST_OP_CREATE,
241 .filesrc = "one.txt", },
242 { .type = QFILE_MONITOR_TEST_OP_EVENT,
243 .filesrc = "one.txt", .watchid = &watch0,
244 .eventid = QFILE_MONITOR_EVENT_CREATED },
245 { .type = QFILE_MONITOR_TEST_OP_EVENT,
246 .filesrc = "one.txt", .watchid = &watch1,
247 .eventid = QFILE_MONITOR_EVENT_CREATED },
250 { .type = QFILE_MONITOR_TEST_OP_CREATE,
251 .filesrc = "two.txt", },
252 { .type = QFILE_MONITOR_TEST_OP_EVENT,
253 .filesrc = "two.txt", .watchid = &watch0,
254 .eventid = QFILE_MONITOR_EVENT_CREATED },
255 { .type = QFILE_MONITOR_TEST_OP_EVENT,
256 .filesrc = "two.txt", .watchid = &watch2,
257 .eventid = QFILE_MONITOR_EVENT_CREATED },
260 { .type = QFILE_MONITOR_TEST_OP_CREATE,
261 .filesrc = "three.txt", },
262 { .type = QFILE_MONITOR_TEST_OP_EVENT,
263 .filesrc = "three.txt", .watchid = &watch0,
264 .eventid = QFILE_MONITOR_EVENT_CREATED },
267 { .type = QFILE_MONITOR_TEST_OP_UNLINK,
268 .filesrc = "three.txt", },
269 { .type = QFILE_MONITOR_TEST_OP_EVENT,
270 .filesrc = "three.txt", .watchid = &watch0,
271 .eventid = QFILE_MONITOR_EVENT_DELETED },
274 { .type = QFILE_MONITOR_TEST_OP_RENAME,
275 .filesrc = "one.txt", .filedst = "two.txt" },
276 { .type = QFILE_MONITOR_TEST_OP_EVENT,
277 .filesrc = "one.txt", .watchid = &watch0,
278 .eventid = QFILE_MONITOR_EVENT_DELETED },
279 { .type = QFILE_MONITOR_TEST_OP_EVENT,
280 .filesrc = "one.txt", .watchid = &watch1,
281 .eventid = QFILE_MONITOR_EVENT_DELETED },
282 { .type = QFILE_MONITOR_TEST_OP_EVENT,
283 .filesrc = "two.txt", .watchid = &watch0,
284 .eventid = QFILE_MONITOR_EVENT_CREATED },
285 { .type = QFILE_MONITOR_TEST_OP_EVENT,
286 .filesrc = "two.txt", .watchid = &watch2,
287 .eventid = QFILE_MONITOR_EVENT_CREATED },
290 { .type = QFILE_MONITOR_TEST_OP_APPEND,
291 .filesrc = "two.txt", },
292 { .type = QFILE_MONITOR_TEST_OP_EVENT,
293 .filesrc = "two.txt", .watchid = &watch0,
294 .eventid = QFILE_MONITOR_EVENT_MODIFIED },
295 { .type = QFILE_MONITOR_TEST_OP_EVENT,
296 .filesrc = "two.txt", .watchid = &watch2,
297 .eventid = QFILE_MONITOR_EVENT_MODIFIED },
300 { .type = QFILE_MONITOR_TEST_OP_TOUCH,
301 .filesrc = "two.txt", },
302 { .type = QFILE_MONITOR_TEST_OP_EVENT,
303 .filesrc = "two.txt", .watchid = &watch0,
304 .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
305 { .type = QFILE_MONITOR_TEST_OP_EVENT,
306 .filesrc = "two.txt", .watchid = &watch2,
307 .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
310 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
311 .filesrc = "one.txt", .watchid = &watch1 },
312 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
313 .filesrc = "one.txt", .watchid = &watch3 },
314 { .type = QFILE_MONITOR_TEST_OP_CREATE,
315 .filesrc = "one.txt", },
316 { .type = QFILE_MONITOR_TEST_OP_EVENT,
317 .filesrc = "one.txt", .watchid = &watch0,
318 .eventid = QFILE_MONITOR_EVENT_CREATED },
319 { .type = QFILE_MONITOR_TEST_OP_EVENT,
320 .filesrc = "one.txt", .watchid = &watch3,
321 .eventid = QFILE_MONITOR_EVENT_CREATED },
324 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
325 .filesrc = "one.txt", .watchid = &watch3 },
326 { .type = QFILE_MONITOR_TEST_OP_UNLINK,
327 .filesrc = "one.txt", },
328 { .type = QFILE_MONITOR_TEST_OP_EVENT,
329 .filesrc = "one.txt", .watchid = &watch0,
330 .eventid = QFILE_MONITOR_EVENT_DELETED },
333 { .type = QFILE_MONITOR_TEST_OP_MKDIR,
334 .filesrc = "fish", },
335 { .type = QFILE_MONITOR_TEST_OP_EVENT,
336 .filesrc = "fish", .watchid = &watch0,
337 .eventid = QFILE_MONITOR_EVENT_CREATED },
340 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
341 .filesrc = "fish/", .watchid = &watch4 },
342 { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
343 .filesrc = "fish/one.txt", .watchid = &watch5 },
344 { .type = QFILE_MONITOR_TEST_OP_CREATE,
345 .filesrc = "fish/one.txt", },
346 { .type = QFILE_MONITOR_TEST_OP_EVENT,
347 .filesrc = "one.txt", .watchid = &watch4,
348 .eventid = QFILE_MONITOR_EVENT_CREATED },
349 { .type = QFILE_MONITOR_TEST_OP_EVENT,
350 .filesrc = "one.txt", .watchid = &watch5,
351 .eventid = QFILE_MONITOR_EVENT_CREATED },
354 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
355 .filesrc = "fish/one.txt", .watchid = &watch5 },
356 { .type = QFILE_MONITOR_TEST_OP_RENAME,
357 .filesrc = "fish/one.txt", .filedst = "two.txt", },
358 { .type = QFILE_MONITOR_TEST_OP_EVENT,
359 .filesrc = "one.txt", .watchid = &watch4,
360 .eventid = QFILE_MONITOR_EVENT_DELETED },
361 { .type = QFILE_MONITOR_TEST_OP_EVENT,
362 .filesrc = "two.txt", .watchid = &watch0,
363 .eventid = QFILE_MONITOR_EVENT_CREATED },
364 { .type = QFILE_MONITOR_TEST_OP_EVENT,
365 .filesrc = "two.txt", .watchid = &watch2,
366 .eventid = QFILE_MONITOR_EVENT_CREATED },
369 { .type = QFILE_MONITOR_TEST_OP_RMDIR,
370 .filesrc = "fish", },
371 { .type = QFILE_MONITOR_TEST_OP_EVENT,
372 .filesrc = "", .watchid = &watch4,
373 .eventid = QFILE_MONITOR_EVENT_IGNORED,
374 .swapnext = true },
375 { .type = QFILE_MONITOR_TEST_OP_EVENT,
376 .filesrc = "fish", .watchid = &watch0,
377 .eventid = QFILE_MONITOR_EVENT_DELETED },
378 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
379 .filesrc = "fish", .watchid = &watch4 },
382 { .type = QFILE_MONITOR_TEST_OP_UNLINK,
383 .filesrc = "two.txt", },
384 { .type = QFILE_MONITOR_TEST_OP_EVENT,
385 .filesrc = "two.txt", .watchid = &watch0,
386 .eventid = QFILE_MONITOR_EVENT_DELETED },
387 { .type = QFILE_MONITOR_TEST_OP_EVENT,
388 .filesrc = "two.txt", .watchid = &watch2,
389 .eventid = QFILE_MONITOR_EVENT_DELETED },
392 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
393 .filesrc = "two.txt", .watchid = &watch2 },
394 { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
395 .filesrc = NULL, .watchid = &watch0 },
397 Error *local_err = NULL;
398 GError *gerr = NULL;
399 QFileMonitor *mon = qemu_file_monitor_new(&local_err);
400 QemuThread th;
401 GTimer *timer;
402 gchar *dir = NULL;
403 int err = -1;
404 gsize i;
405 char *pathsrc = NULL;
406 char *pathdst = NULL;
407 QFileMonitorTestData data;
408 GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal);
409 char *travis_arch;
411 qemu_mutex_init(&data.lock);
412 data.records = NULL;
415 * This test does not work on Travis LXD containers since some
416 * syscalls are blocked in that environment.
418 travis_arch = getenv("TRAVIS_ARCH");
419 if (travis_arch && !g_str_equal(travis_arch, "x86_64")) {
420 g_test_skip("Test does not work on non-x86 Travis containers.");
421 return;
425 * The file monitor needs the main loop running in
426 * order to receive events from inotify. We must
427 * thus spawn a background thread to run an event
428 * loop impl, while this thread triggers the
429 * actual file operations we're testing
431 evrunning = 1;
432 evstopping = 0;
433 qemu_thread_create(&th, "event-loop",
434 qemu_file_monitor_test_event_loop, NULL,
435 QEMU_THREAD_JOINABLE);
437 if (local_err) {
438 g_printerr("File monitoring not available: %s",
439 error_get_pretty(local_err));
440 error_free(local_err);
441 return;
444 dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
445 &gerr);
446 if (!dir) {
447 g_printerr("Unable to create tmp dir %s",
448 gerr->message);
449 g_error_free(gerr);
450 abort();
454 * Run through the operation sequence validating events
455 * as we go
457 for (i = 0; i < G_N_ELEMENTS(ops); i++) {
458 const QFileMonitorTestOp *op = &(ops[i]);
459 int fd;
460 struct utimbuf ubuf;
461 char *watchdir;
462 const char *watchfile;
464 pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
465 if (op->filedst) {
466 pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
469 switch (op->type) {
470 case QFILE_MONITOR_TEST_OP_ADD_WATCH:
471 if (debug) {
472 g_printerr("Add watch %s %s\n",
473 dir, op->filesrc);
475 if (op->filesrc && strchr(op->filesrc, '/')) {
476 watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
477 watchfile = strrchr(watchdir, '/');
478 *(char *)watchfile = '\0';
479 watchfile++;
480 if (*watchfile == '\0') {
481 watchfile = NULL;
483 } else {
484 watchdir = g_strdup(dir);
485 watchfile = op->filesrc;
487 *op->watchid =
488 qemu_file_monitor_add_watch(mon,
489 watchdir,
490 watchfile,
491 qemu_file_monitor_test_handler,
492 &data,
493 &local_err);
494 g_free(watchdir);
495 if (*op->watchid < 0) {
496 g_printerr("Unable to add watch %s",
497 error_get_pretty(local_err));
498 error_free(local_err);
499 goto cleanup;
501 if (debug) {
502 g_printerr("Watch ID %" PRIx64 "\n", *op->watchid);
504 if (g_hash_table_contains(ids, op->watchid)) {
505 g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid);
506 goto cleanup;
508 g_hash_table_add(ids, op->watchid);
509 break;
510 case QFILE_MONITOR_TEST_OP_DEL_WATCH:
511 if (debug) {
512 g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid);
514 if (op->filesrc && strchr(op->filesrc, '/')) {
515 watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
516 watchfile = strrchr(watchdir, '/');
517 *(char *)watchfile = '\0';
518 } else {
519 watchdir = g_strdup(dir);
521 g_hash_table_remove(ids, op->watchid);
522 qemu_file_monitor_remove_watch(mon,
523 watchdir,
524 *op->watchid);
525 g_free(watchdir);
526 break;
527 case QFILE_MONITOR_TEST_OP_EVENT:
528 if (debug) {
529 g_printerr("Event id=%" PRIx64 " event=%d file=%s\n",
530 *op->watchid, op->eventid, op->filesrc);
532 if (!qemu_file_monitor_test_expect(&data, *op->watchid,
533 op->eventid, op->filesrc,
534 op->swapnext))
535 goto cleanup;
536 break;
537 case QFILE_MONITOR_TEST_OP_CREATE:
538 if (debug) {
539 g_printerr("Create %s\n", pathsrc);
541 fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
542 if (fd < 0) {
543 g_printerr("Unable to create %s: %s",
544 pathsrc, strerror(errno));
545 goto cleanup;
547 close(fd);
548 break;
550 case QFILE_MONITOR_TEST_OP_APPEND:
551 if (debug) {
552 g_printerr("Append %s\n", pathsrc);
554 fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
555 if (fd < 0) {
556 g_printerr("Unable to open %s: %s",
557 pathsrc, strerror(errno));
558 goto cleanup;
561 if (write(fd, "Hello World", 10) != 10) {
562 g_printerr("Unable to write %s: %s",
563 pathsrc, strerror(errno));
564 close(fd);
565 goto cleanup;
567 close(fd);
568 break;
570 case QFILE_MONITOR_TEST_OP_TRUNC:
571 if (debug) {
572 g_printerr("Truncate %s\n", pathsrc);
574 if (truncate(pathsrc, 4) < 0) {
575 g_printerr("Unable to truncate %s: %s",
576 pathsrc, strerror(errno));
577 goto cleanup;
579 break;
581 case QFILE_MONITOR_TEST_OP_RENAME:
582 if (debug) {
583 g_printerr("Rename %s -> %s\n", pathsrc, pathdst);
585 if (rename(pathsrc, pathdst) < 0) {
586 g_printerr("Unable to rename %s to %s: %s",
587 pathsrc, pathdst, strerror(errno));
588 goto cleanup;
590 break;
592 case QFILE_MONITOR_TEST_OP_UNLINK:
593 if (debug) {
594 g_printerr("Unlink %s\n", pathsrc);
596 if (unlink(pathsrc) < 0) {
597 g_printerr("Unable to unlink %s: %s",
598 pathsrc, strerror(errno));
599 goto cleanup;
601 break;
603 case QFILE_MONITOR_TEST_OP_TOUCH:
604 if (debug) {
605 g_printerr("Touch %s\n", pathsrc);
607 ubuf.actime = 1024;
608 ubuf.modtime = 1025;
609 if (utime(pathsrc, &ubuf) < 0) {
610 g_printerr("Unable to touch %s: %s",
611 pathsrc, strerror(errno));
612 goto cleanup;
614 break;
616 case QFILE_MONITOR_TEST_OP_MKDIR:
617 if (debug) {
618 g_printerr("Mkdir %s\n", pathsrc);
620 if (mkdir(pathsrc, 0700) < 0) {
621 g_printerr("Unable to mkdir %s: %s",
622 pathsrc, strerror(errno));
623 goto cleanup;
625 break;
627 case QFILE_MONITOR_TEST_OP_RMDIR:
628 if (debug) {
629 g_printerr("Rmdir %s\n", pathsrc);
631 if (rmdir(pathsrc) < 0) {
632 g_printerr("Unable to rmdir %s: %s",
633 pathsrc, strerror(errno));
634 goto cleanup;
636 break;
638 default:
639 g_assert_not_reached();
642 g_free(pathsrc);
643 g_free(pathdst);
644 pathsrc = pathdst = NULL;
647 g_assert_cmpint(g_hash_table_size(ids), ==, 0);
649 err = 0;
651 cleanup:
652 g_free(pathsrc);
653 g_free(pathdst);
655 qemu_mutex_lock(&evlock);
656 evstopping = 1;
657 timer = g_timer_new();
658 while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
659 qemu_mutex_unlock(&evlock);
660 usleep(10 * 1000);
661 qemu_mutex_lock(&evlock);
663 qemu_mutex_unlock(&evlock);
665 if (g_timer_elapsed(timer, NULL) >= 5) {
666 g_printerr("Event loop failed to quit after 5 seconds\n");
668 g_timer_destroy(timer);
670 qemu_file_monitor_free(mon);
671 g_list_foreach(data.records,
672 (GFunc)qemu_file_monitor_test_record_free, NULL);
673 g_list_free(data.records);
674 qemu_mutex_destroy(&data.lock);
675 if (dir) {
676 for (i = 0; i < G_N_ELEMENTS(ops); i++) {
677 const QFileMonitorTestOp *op = &(ops[i]);
678 char *path = g_strdup_printf("%s/%s",
679 dir, op->filesrc);
680 if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) {
681 rmdir(path);
682 g_free(path);
683 } else {
684 unlink(path);
685 g_free(path);
686 if (op->filedst) {
687 path = g_strdup_printf("%s/%s",
688 dir, op->filedst);
689 unlink(path);
690 g_free(path);
694 if (rmdir(dir) < 0) {
695 g_printerr("Failed to remove %s: %s\n",
696 dir, strerror(errno));
697 abort();
700 g_hash_table_unref(ids);
701 g_free(dir);
702 g_assert(err == 0);
706 int main(int argc, char **argv)
708 g_test_init(&argc, &argv, NULL);
710 qemu_init_main_loop(&error_abort);
712 qemu_mutex_init(&evlock);
714 debug = getenv("FILEMONITOR_DEBUG") != NULL;
715 g_test_add_func("/util/filemonitor", test_file_monitor_events);
717 return g_test_run();