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_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
,
45 } QFileMonitorTestWatch
;
49 const QFileMonitorTestWatch
*watches
;
52 const QFileMonitorTestOp
*ops
;
53 } QFileMonitorTestPlan
;
57 QFileMonitorEvent event
;
59 } QFileMonitorTestRecord
;
65 } QFileMonitorTestData
;
67 static QemuMutex evlock
;
68 static bool evstopping
;
69 static bool evrunning
;
72 * Main function for a background thread that is
73 * running the event loop during the test
76 qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED
)
78 qemu_mutex_lock(&evlock
);
81 qemu_mutex_unlock(&evlock
);
83 qemu_mutex_lock(&evlock
);
87 qemu_mutex_unlock(&evlock
);
93 * File monitor event handler which simply maintains
94 * an ordered list of all events that it receives
97 qemu_file_monitor_test_handler(int id
,
98 QFileMonitorEvent event
,
102 QFileMonitorTestData
*data
= opaque
;
103 QFileMonitorTestRecord
*rec
= g_new0(QFileMonitorTestRecord
, 1);
107 rec
->filename
= g_strdup(filename
);
109 qemu_mutex_lock(&data
->lock
);
110 data
->records
= g_list_append(data
->records
, rec
);
111 qemu_mutex_unlock(&data
->lock
);
116 qemu_file_monitor_test_record_free(QFileMonitorTestRecord
*rec
)
118 g_free(rec
->filename
);
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.
131 static QFileMonitorTestRecord
*
132 qemu_file_monitor_test_next_record(QFileMonitorTestData
*data
)
134 GTimer
*timer
= g_timer_new();
135 QFileMonitorTestRecord
*record
= NULL
;
138 qemu_mutex_lock(&data
->lock
);
139 while (!data
->records
&& g_timer_elapsed(timer
, NULL
) < 5) {
140 qemu_mutex_unlock(&data
->lock
);
142 qemu_mutex_lock(&data
->lock
);
145 record
= data
->records
->data
;
147 data
->records
= g_list_remove_link(data
->records
, tmp
);
150 qemu_mutex_unlock(&data
->lock
);
152 g_timer_destroy(timer
);
158 * Check whether the event record we retrieved matches
159 * data we were expecting to see for the event
162 qemu_file_monitor_test_expect(QFileMonitorTestData
*data
,
164 QFileMonitorEvent event
,
165 const char *filename
)
167 QFileMonitorTestRecord
*rec
;
170 rec
= qemu_file_monitor_test_next_record(data
);
173 g_printerr("Missing event watch id %d event %d file %s\n",
174 id
, event
, filename
);
179 g_printerr("Expected watch id %d but got %d\n", id
, rec
->id
);
183 if (event
!= rec
->event
) {
184 g_printerr("Expected event %d but got %d\n", event
, rec
->event
);
188 if (!g_str_equal(filename
, rec
->filename
)) {
189 g_printerr("Expected filename %s but got %s\n",
190 filename
, rec
->filename
);
197 qemu_file_monitor_test_record_free(rec
);
203 test_file_monitor_events(const void *opaque
)
205 const QFileMonitorTestPlan
*plan
= opaque
;
206 Error
*local_err
= NULL
;
208 QFileMonitor
*mon
= qemu_file_monitor_new(&local_err
);
214 char *pathsrc
= NULL
;
215 char *pathdst
= NULL
;
216 QFileMonitorTestData data
;
218 qemu_mutex_init(&data
.lock
);
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
230 qemu_thread_create(&th
, "event-loop",
231 qemu_file_monitor_test_event_loop
, NULL
,
232 QEMU_THREAD_JOINABLE
);
235 g_printerr("File monitoring not available: %s",
236 error_get_pretty(local_err
));
237 error_free(local_err
);
241 dir
= g_dir_make_tmp("test-util-filemonitor-XXXXXX",
244 g_printerr("Unable to create tmp dir %s",
251 * First register all the directory / file watches
252 * we're interested in seeing events against
254 for (i
= 0; i
< plan
->nwatches
; i
++) {
256 watchid
= qemu_file_monitor_add_watch(mon
,
258 plan
->watches
[i
].file
,
259 qemu_file_monitor_test_handler
,
263 g_printerr("Unable to add watch %s",
264 error_get_pretty(local_err
));
271 * Now invoke all the file operations (create,
272 * delete, rename, chmod, etc). These operations
273 * will trigger the various file monitor events
275 for (i
= 0; i
< plan
->nops
; i
++) {
276 const QFileMonitorTestOp
*op
= &(plan
->ops
[i
]);
280 pathsrc
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
282 pathdst
= g_strdup_printf("%s/%s", dir
, op
->filedst
);
286 case QFILE_MONITOR_TEST_OP_CREATE
:
287 fd
= open(pathsrc
, O_WRONLY
| O_CREAT
, 0700);
289 g_printerr("Unable to create %s: %s",
290 pathsrc
, strerror(errno
));
296 case QFILE_MONITOR_TEST_OP_APPEND
:
297 fd
= open(pathsrc
, O_WRONLY
| O_APPEND
, 0700);
299 g_printerr("Unable to open %s: %s",
300 pathsrc
, strerror(errno
));
304 if (write(fd
, "Hello World", 10) != 10) {
305 g_printerr("Unable to write %s: %s",
306 pathsrc
, strerror(errno
));
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
));
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
));
329 case QFILE_MONITOR_TEST_OP_UNLINK
:
330 if (unlink(pathsrc
) < 0) {
331 g_printerr("Unable to unlink %s: %s",
332 pathsrc
, strerror(errno
));
337 case QFILE_MONITOR_TEST_OP_TOUCH
:
340 if (utime(pathsrc
, &ubuf
) < 0) {
341 g_printerr("Unable to touch %s: %s",
342 pathsrc
, strerror(errno
));
348 g_assert_not_reached();
353 pathsrc
= pathdst
= NULL
;
358 * Finally validate that we have received all the events
359 * we expect to see for the combination of watches and
362 for (i
= 0; i
< plan
->nops
; i
++) {
363 const QFileMonitorTestOp
*op
= &(plan
->ops
[i
]);
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
))
372 if (!qemu_file_monitor_test_expect(
373 &data
, j
, QFILE_MONITOR_EVENT_CREATED
, op
->filesrc
))
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
))
385 if (!qemu_file_monitor_test_expect(
386 &data
, j
, QFILE_MONITOR_EVENT_MODIFIED
, op
->filesrc
))
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
))
397 if (!qemu_file_monitor_test_expect(
398 &data
, j
, QFILE_MONITOR_EVENT_DELETED
, op
->filesrc
))
402 for (j
= 0; j
< plan
->nwatches
; j
++) {
403 if (plan
->watches
[j
].file
&&
404 !g_str_equal(plan
->watches
[j
].file
, op
->filedst
))
407 if (!qemu_file_monitor_test_expect(
408 &data
, j
, QFILE_MONITOR_EVENT_CREATED
, op
->filedst
))
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
))
419 if (!qemu_file_monitor_test_expect(
420 &data
, j
, QFILE_MONITOR_EVENT_ATTRIBUTES
, op
->filesrc
))
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
))
431 if (!qemu_file_monitor_test_expect(
432 &data
, j
, QFILE_MONITOR_EVENT_DELETED
, op
->filesrc
))
438 g_assert_not_reached();
448 qemu_mutex_lock(&evlock
);
450 timer
= g_timer_new();
451 while (evrunning
&& g_timer_elapsed(timer
, NULL
) < 5) {
452 qemu_mutex_unlock(&evlock
);
454 qemu_mutex_lock(&evlock
);
456 qemu_mutex_unlock(&evlock
);
458 if (g_timer_elapsed(timer
, NULL
) >= 5) {
459 g_printerr("Event loop failed to quit after 5 seconds\n");
461 g_timer_destroy(timer
);
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
);
469 pathdst
= g_strdup_printf("%s/%s", dir
, op
->filedst
);
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
);
489 * Set of structs which define which file name patterns
490 * we're trying to watch against. NULL, means all files
493 static const QFileMonitorTestWatch watches_any
[] = {
497 static const QFileMonitorTestWatch watches_one
[] = {
501 static const QFileMonitorTestWatch watches_two
[] = {
505 static const QFileMonitorTestWatch watches_many
[] = {
513 * Various sets of file operations we're going to
514 * trigger and validate events for
516 static const QFileMonitorTestOp ops_create_one
[] = {
517 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
518 .filesrc
= "one.txt", }
521 static 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", }
528 static 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", }
537 static 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" }
544 static 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" }
553 static 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", },
560 static 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", },
567 static 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", },
576 * No we define data sets for the combinatorial
577 * expansion of file watches and operation sets
579 #define PLAN_DATA(o, w) \
580 static const QFileMonitorTestPlan plan_ ## o ## _ ## w = { \
581 .nops = G_N_ELEMENTS(ops_ ##o), \
583 .nwatches = G_N_ELEMENTS(watches_ ##w), \
584 .watches = watches_ ## w, \
587 PLAN_DATA(create_one
, any
);
588 PLAN_DATA(create_one
, one
);
589 PLAN_DATA(create_one
, two
);
590 PLAN_DATA(create_one
, many
);
592 PLAN_DATA(delete_one
, any
);
593 PLAN_DATA(delete_one
, one
);
594 PLAN_DATA(delete_one
, two
);
595 PLAN_DATA(delete_one
, many
);
597 PLAN_DATA(create_many
, any
);
598 PLAN_DATA(create_many
, one
);
599 PLAN_DATA(create_many
, two
);
600 PLAN_DATA(create_many
, many
);
602 PLAN_DATA(rename_one
, any
);
603 PLAN_DATA(rename_one
, one
);
604 PLAN_DATA(rename_one
, two
);
605 PLAN_DATA(rename_one
, many
);
607 PLAN_DATA(rename_many
, any
);
608 PLAN_DATA(rename_many
, one
);
609 PLAN_DATA(rename_many
, two
);
610 PLAN_DATA(rename_many
, many
);
612 PLAN_DATA(append_one
, any
);
613 PLAN_DATA(append_one
, one
);
614 PLAN_DATA(append_one
, two
);
615 PLAN_DATA(append_one
, many
);
617 PLAN_DATA(trunc_one
, any
);
618 PLAN_DATA(trunc_one
, one
);
619 PLAN_DATA(trunc_one
, two
);
620 PLAN_DATA(trunc_one
, many
);
622 PLAN_DATA(touch_one
, any
);
623 PLAN_DATA(touch_one
, one
);
624 PLAN_DATA(touch_one
, two
);
625 PLAN_DATA(touch_one
, many
);
628 int main(int argc
, char **argv
)
630 g_test_init(&argc
, &argv
, NULL
);
632 qemu_init_main_loop(&error_abort
);
634 qemu_mutex_init(&evlock
);
637 * Register test cases for the combinatorial
638 * expansion of file watches and operation sets
640 #define PLAN_REGISTER(o, w) \
641 g_test_add_data_func("/util/filemonitor/" # o "/" # w, \
642 &plan_ ## o ## _ ## w, test_file_monitor_events)
644 PLAN_REGISTER(create_one
, any
);
645 PLAN_REGISTER(create_one
, one
);
646 PLAN_REGISTER(create_one
, two
);
647 PLAN_REGISTER(create_one
, many
);
649 PLAN_REGISTER(delete_one
, any
);
650 PLAN_REGISTER(delete_one
, one
);
651 PLAN_REGISTER(delete_one
, two
);
652 PLAN_REGISTER(delete_one
, many
);
654 PLAN_REGISTER(create_many
, any
);
655 PLAN_REGISTER(create_many
, one
);
656 PLAN_REGISTER(create_many
, two
);
657 PLAN_REGISTER(create_many
, many
);
659 PLAN_REGISTER(rename_one
, any
);
660 PLAN_REGISTER(rename_one
, one
);
661 PLAN_REGISTER(rename_one
, two
);
662 PLAN_REGISTER(rename_one
, many
);
664 PLAN_REGISTER(rename_many
, any
);
665 PLAN_REGISTER(rename_many
, one
);
666 PLAN_REGISTER(rename_many
, two
);
667 PLAN_REGISTER(rename_many
, many
);
669 PLAN_REGISTER(append_one
, any
);
670 PLAN_REGISTER(append_one
, one
);
671 PLAN_REGISTER(append_one
, two
);
672 PLAN_REGISTER(append_one
, many
);
674 PLAN_REGISTER(trunc_one
, any
);
675 PLAN_REGISTER(trunc_one
, one
);
676 PLAN_REGISTER(trunc_one
, two
);
677 PLAN_REGISTER(trunc_one
, many
);
679 PLAN_REGISTER(touch_one
, any
);
680 PLAN_REGISTER(touch_one
, one
);
681 PLAN_REGISTER(touch_one
, two
);
682 PLAN_REGISTER(touch_one
, many
);