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"
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
,
49 * Only valid with OP_EVENT - this event might be
50 * swapped with the next OP_EVENT
57 QFileMonitorEvent event
;
59 } QFileMonitorTestRecord
;
65 } QFileMonitorTestData
;
67 static QemuMutex evlock
;
68 static bool evstopping
;
69 static bool evrunning
;
73 * Main function for a background thread that is
74 * running the event loop during the test
77 qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED
)
79 qemu_mutex_lock(&evlock
);
82 qemu_mutex_unlock(&evlock
);
84 qemu_mutex_lock(&evlock
);
88 qemu_mutex_unlock(&evlock
);
94 * File monitor event handler which simply maintains
95 * an ordered list of all events that it receives
98 qemu_file_monitor_test_handler(int64_t id
,
99 QFileMonitorEvent event
,
100 const char *filename
,
103 QFileMonitorTestData
*data
= opaque
;
104 QFileMonitorTestRecord
*rec
= g_new0(QFileMonitorTestRecord
, 1);
107 g_printerr("Queue event id %" PRIx64
" event %d file %s\n",
108 id
, event
, filename
);
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
);
121 qemu_file_monitor_test_record_free(QFileMonitorTestRecord
*rec
)
123 g_free(rec
->filename
);
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
;
144 qemu_mutex_lock(&data
->lock
);
145 while (!data
->records
&& g_timer_elapsed(timer
, NULL
) < 5) {
146 qemu_mutex_unlock(&data
->lock
);
148 qemu_mutex_lock(&data
->lock
);
151 record
= data
->records
->data
;
153 data
->records
->data
= pushback
;
156 data
->records
= g_list_remove_link(data
->records
, tmp
);
159 } else if (pushback
) {
160 qemu_file_monitor_test_record_free(pushback
);
162 qemu_mutex_unlock(&data
->lock
);
164 g_timer_destroy(timer
);
170 * Check whether the event record we retrieved matches
171 * data we were expecting to see for the event
174 qemu_file_monitor_test_expect(QFileMonitorTestData
*data
,
176 QFileMonitorEvent event
,
177 const char *filename
,
180 QFileMonitorTestRecord
*rec
;
183 rec
= qemu_file_monitor_test_next_record(data
, NULL
);
187 g_printerr("Missing event watch id %" PRIx64
" event %d file %s\n",
188 id
, event
, filename
);
194 rec
= qemu_file_monitor_test_next_record(data
, rec
);
198 g_printerr("Expected watch id %" PRIx64
" but got %" PRIx64
"\n",
203 if (event
!= rec
->event
) {
204 g_printerr("Expected event %d but got %d\n", event
, rec
->event
);
208 if (!g_str_equal(filename
, rec
->filename
)) {
209 g_printerr("Expected filename %s but got %s\n",
210 filename
, rec
->filename
);
217 qemu_file_monitor_test_record_free(rec
);
223 test_file_monitor_events(void)
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
,
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
;
399 QFileMonitor
*mon
= qemu_file_monitor_new(&local_err
);
405 char *pathsrc
= NULL
;
406 char *pathdst
= NULL
;
407 QFileMonitorTestData data
;
408 GHashTable
*ids
= g_hash_table_new(g_int64_hash
, g_int64_equal
);
410 qemu_mutex_init(&data
.lock
);
414 * The file monitor needs the main loop running in
415 * order to receive events from inotify. We must
416 * thus spawn a background thread to run an event
417 * loop impl, while this thread triggers the
418 * actual file operations we're testing
422 qemu_thread_create(&th
, "event-loop",
423 qemu_file_monitor_test_event_loop
, NULL
,
424 QEMU_THREAD_JOINABLE
);
427 g_printerr("File monitoring not available: %s",
428 error_get_pretty(local_err
));
429 error_free(local_err
);
433 dir
= g_dir_make_tmp("test-util-filemonitor-XXXXXX",
436 g_printerr("Unable to create tmp dir %s",
443 * Run through the operation sequence validating events
446 for (i
= 0; i
< G_N_ELEMENTS(ops
); i
++) {
447 const QFileMonitorTestOp
*op
= &(ops
[i
]);
451 const char *watchfile
;
453 pathsrc
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
455 pathdst
= g_strdup_printf("%s/%s", dir
, op
->filedst
);
459 case QFILE_MONITOR_TEST_OP_ADD_WATCH
:
461 g_printerr("Add watch %s %s\n",
464 if (op
->filesrc
&& strchr(op
->filesrc
, '/')) {
465 watchdir
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
466 watchfile
= strrchr(watchdir
, '/');
467 *(char *)watchfile
= '\0';
469 if (*watchfile
== '\0') {
473 watchdir
= g_strdup(dir
);
474 watchfile
= op
->filesrc
;
477 qemu_file_monitor_add_watch(mon
,
480 qemu_file_monitor_test_handler
,
484 if (*op
->watchid
< 0) {
485 g_printerr("Unable to add watch %s",
486 error_get_pretty(local_err
));
490 g_printerr("Watch ID %" PRIx64
"\n", *op
->watchid
);
492 if (g_hash_table_contains(ids
, op
->watchid
)) {
493 g_printerr("Watch ID %" PRIx64
"already exists", *op
->watchid
);
496 g_hash_table_add(ids
, op
->watchid
);
498 case QFILE_MONITOR_TEST_OP_DEL_WATCH
:
500 g_printerr("Del watch %s %" PRIx64
"\n", dir
, *op
->watchid
);
502 if (op
->filesrc
&& strchr(op
->filesrc
, '/')) {
503 watchdir
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
504 watchfile
= strrchr(watchdir
, '/');
505 *(char *)watchfile
= '\0';
507 watchdir
= g_strdup(dir
);
509 g_hash_table_remove(ids
, op
->watchid
);
510 qemu_file_monitor_remove_watch(mon
,
515 case QFILE_MONITOR_TEST_OP_EVENT
:
517 g_printerr("Event id=%" PRIx64
" event=%d file=%s\n",
518 *op
->watchid
, op
->eventid
, op
->filesrc
);
520 if (!qemu_file_monitor_test_expect(&data
, *op
->watchid
,
521 op
->eventid
, op
->filesrc
,
525 case QFILE_MONITOR_TEST_OP_CREATE
:
527 g_printerr("Create %s\n", pathsrc
);
529 fd
= open(pathsrc
, O_WRONLY
| O_CREAT
, 0700);
531 g_printerr("Unable to create %s: %s",
532 pathsrc
, strerror(errno
));
538 case QFILE_MONITOR_TEST_OP_APPEND
:
540 g_printerr("Append %s\n", pathsrc
);
542 fd
= open(pathsrc
, O_WRONLY
| O_APPEND
, 0700);
544 g_printerr("Unable to open %s: %s",
545 pathsrc
, strerror(errno
));
549 if (write(fd
, "Hello World", 10) != 10) {
550 g_printerr("Unable to write %s: %s",
551 pathsrc
, strerror(errno
));
558 case QFILE_MONITOR_TEST_OP_TRUNC
:
560 g_printerr("Truncate %s\n", pathsrc
);
562 if (truncate(pathsrc
, 4) < 0) {
563 g_printerr("Unable to truncate %s: %s",
564 pathsrc
, strerror(errno
));
569 case QFILE_MONITOR_TEST_OP_RENAME
:
571 g_printerr("Rename %s -> %s\n", pathsrc
, pathdst
);
573 if (rename(pathsrc
, pathdst
) < 0) {
574 g_printerr("Unable to rename %s to %s: %s",
575 pathsrc
, pathdst
, strerror(errno
));
580 case QFILE_MONITOR_TEST_OP_UNLINK
:
582 g_printerr("Unlink %s\n", pathsrc
);
584 if (unlink(pathsrc
) < 0) {
585 g_printerr("Unable to unlink %s: %s",
586 pathsrc
, strerror(errno
));
591 case QFILE_MONITOR_TEST_OP_TOUCH
:
593 g_printerr("Touch %s\n", pathsrc
);
597 if (utime(pathsrc
, &ubuf
) < 0) {
598 g_printerr("Unable to touch %s: %s",
599 pathsrc
, strerror(errno
));
604 case QFILE_MONITOR_TEST_OP_MKDIR
:
606 g_printerr("Mkdir %s\n", pathsrc
);
608 if (mkdir(pathsrc
, 0700) < 0) {
609 g_printerr("Unable to mkdir %s: %s",
610 pathsrc
, strerror(errno
));
615 case QFILE_MONITOR_TEST_OP_RMDIR
:
617 g_printerr("Rmdir %s\n", pathsrc
);
619 if (rmdir(pathsrc
) < 0) {
620 g_printerr("Unable to rmdir %s: %s",
621 pathsrc
, strerror(errno
));
627 g_assert_not_reached();
632 pathsrc
= pathdst
= NULL
;
635 g_assert_cmpint(g_hash_table_size(ids
), ==, 0);
643 qemu_mutex_lock(&evlock
);
645 timer
= g_timer_new();
646 while (evrunning
&& g_timer_elapsed(timer
, NULL
) < 5) {
647 qemu_mutex_unlock(&evlock
);
649 qemu_mutex_lock(&evlock
);
651 qemu_mutex_unlock(&evlock
);
653 if (g_timer_elapsed(timer
, NULL
) >= 5) {
654 g_printerr("Event loop failed to quit after 5 seconds\n");
656 g_timer_destroy(timer
);
658 qemu_file_monitor_free(mon
);
659 g_list_foreach(data
.records
,
660 (GFunc
)qemu_file_monitor_test_record_free
, NULL
);
661 g_list_free(data
.records
);
662 qemu_mutex_destroy(&data
.lock
);
664 for (i
= 0; i
< G_N_ELEMENTS(ops
); i
++) {
665 const QFileMonitorTestOp
*op
= &(ops
[i
]);
666 char *path
= g_strdup_printf("%s/%s",
668 if (op
->type
== QFILE_MONITOR_TEST_OP_MKDIR
) {
675 path
= g_strdup_printf("%s/%s",
682 if (rmdir(dir
) < 0) {
683 g_printerr("Failed to remove %s: %s\n",
684 dir
, strerror(errno
));
688 g_hash_table_unref(ids
);
694 int main(int argc
, char **argv
)
696 g_test_init(&argc
, &argv
, NULL
);
698 qemu_init_main_loop(&error_abort
);
700 qemu_mutex_init(&evlock
);
702 debug
= getenv("FILEMONITOR_DEBUG") != NULL
;
703 g_test_add_func("/util/filemonitor", test_file_monitor_events
);