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
,
52 QFileMonitorEvent event
;
54 } QFileMonitorTestRecord
;
60 } QFileMonitorTestData
;
62 static QemuMutex evlock
;
63 static bool evstopping
;
64 static bool evrunning
;
68 * Main function for a background thread that is
69 * running the event loop during the test
72 qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED
)
74 qemu_mutex_lock(&evlock
);
77 qemu_mutex_unlock(&evlock
);
79 qemu_mutex_lock(&evlock
);
83 qemu_mutex_unlock(&evlock
);
89 * File monitor event handler which simply maintains
90 * an ordered list of all events that it receives
93 qemu_file_monitor_test_handler(int id
,
94 QFileMonitorEvent event
,
98 QFileMonitorTestData
*data
= opaque
;
99 QFileMonitorTestRecord
*rec
= g_new0(QFileMonitorTestRecord
, 1);
103 rec
->filename
= g_strdup(filename
);
105 qemu_mutex_lock(&data
->lock
);
106 data
->records
= g_list_append(data
->records
, rec
);
107 qemu_mutex_unlock(&data
->lock
);
112 qemu_file_monitor_test_record_free(QFileMonitorTestRecord
*rec
)
114 g_free(rec
->filename
);
120 * Get the next event record that has been received by
121 * the file monitor event handler. Since events are
122 * emitted in the background thread running the event
123 * loop, we can't assume there is a record available
124 * immediately. Thus we will sleep for upto 5 seconds
125 * to wait for the event to be queued for us.
127 static QFileMonitorTestRecord
*
128 qemu_file_monitor_test_next_record(QFileMonitorTestData
*data
)
130 GTimer
*timer
= g_timer_new();
131 QFileMonitorTestRecord
*record
= NULL
;
134 qemu_mutex_lock(&data
->lock
);
135 while (!data
->records
&& g_timer_elapsed(timer
, NULL
) < 5) {
136 qemu_mutex_unlock(&data
->lock
);
138 qemu_mutex_lock(&data
->lock
);
141 record
= data
->records
->data
;
143 data
->records
= g_list_remove_link(data
->records
, tmp
);
146 qemu_mutex_unlock(&data
->lock
);
148 g_timer_destroy(timer
);
154 * Check whether the event record we retrieved matches
155 * data we were expecting to see for the event
158 qemu_file_monitor_test_expect(QFileMonitorTestData
*data
,
160 QFileMonitorEvent event
,
161 const char *filename
)
163 QFileMonitorTestRecord
*rec
;
166 rec
= qemu_file_monitor_test_next_record(data
);
169 g_printerr("Missing event watch id %d event %d file %s\n",
170 id
, event
, filename
);
175 g_printerr("Expected watch id %d but got %d\n", id
, rec
->id
);
179 if (event
!= rec
->event
) {
180 g_printerr("Expected event %d but got %d\n", event
, rec
->event
);
184 if (!g_str_equal(filename
, rec
->filename
)) {
185 g_printerr("Expected filename %s but got %s\n",
186 filename
, rec
->filename
);
193 qemu_file_monitor_test_record_free(rec
);
199 test_file_monitor_events(void)
201 QFileMonitorTestOp ops
[] = {
202 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
203 .filesrc
= NULL
, .watchid
= 0 },
204 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
205 .filesrc
= "one.txt", .watchid
= 1 },
206 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
207 .filesrc
= "two.txt", .watchid
= 2 },
210 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
211 .filesrc
= "one.txt", },
212 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
213 .filesrc
= "one.txt", .watchid
= 0,
214 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
215 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
216 .filesrc
= "one.txt", .watchid
= 1,
217 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
220 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
221 .filesrc
= "two.txt", },
222 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
223 .filesrc
= "two.txt", .watchid
= 0,
224 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
225 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
226 .filesrc
= "two.txt", .watchid
= 2,
227 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
230 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
231 .filesrc
= "three.txt", },
232 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
233 .filesrc
= "three.txt", .watchid
= 0,
234 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
237 { .type
= QFILE_MONITOR_TEST_OP_UNLINK
,
238 .filesrc
= "three.txt", },
239 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
240 .filesrc
= "three.txt", .watchid
= 0,
241 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
244 { .type
= QFILE_MONITOR_TEST_OP_RENAME
,
245 .filesrc
= "one.txt", .filedst
= "two.txt" },
246 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
247 .filesrc
= "one.txt", .watchid
= 0,
248 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
249 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
250 .filesrc
= "one.txt", .watchid
= 1,
251 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
252 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
253 .filesrc
= "two.txt", .watchid
= 0,
254 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
255 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
256 .filesrc
= "two.txt", .watchid
= 2,
257 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
260 { .type
= QFILE_MONITOR_TEST_OP_APPEND
,
261 .filesrc
= "two.txt", },
262 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
263 .filesrc
= "two.txt", .watchid
= 0,
264 .eventid
= QFILE_MONITOR_EVENT_MODIFIED
},
265 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
266 .filesrc
= "two.txt", .watchid
= 2,
267 .eventid
= QFILE_MONITOR_EVENT_MODIFIED
},
270 { .type
= QFILE_MONITOR_TEST_OP_TOUCH
,
271 .filesrc
= "two.txt", },
272 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
273 .filesrc
= "two.txt", .watchid
= 0,
274 .eventid
= QFILE_MONITOR_EVENT_ATTRIBUTES
},
275 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
276 .filesrc
= "two.txt", .watchid
= 2,
277 .eventid
= QFILE_MONITOR_EVENT_ATTRIBUTES
},
280 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
281 .filesrc
= "one.txt", .watchid
= 1 },
282 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
283 .filesrc
= "one.txt", .watchid
= 3 },
284 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
285 .filesrc
= "one.txt", },
286 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
287 .filesrc
= "one.txt", .watchid
= 0,
288 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
289 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
290 .filesrc
= "one.txt", .watchid
= 3,
291 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
294 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
295 .filesrc
= "one.txt", .watchid
= 3 },
296 { .type
= QFILE_MONITOR_TEST_OP_UNLINK
,
297 .filesrc
= "one.txt", },
298 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
299 .filesrc
= "one.txt", .watchid
= 0,
300 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
303 { .type
= QFILE_MONITOR_TEST_OP_MKDIR
,
304 .filesrc
= "fish", },
305 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
306 .filesrc
= "fish", .watchid
= 0,
307 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
310 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
311 .filesrc
= "fish/", .watchid
= 4 },
312 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
313 .filesrc
= "fish/one.txt", .watchid
= 5 },
314 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
315 .filesrc
= "fish/one.txt", },
316 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
317 .filesrc
= "one.txt", .watchid
= 4,
318 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
319 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
320 .filesrc
= "one.txt", .watchid
= 5,
321 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
324 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
325 .filesrc
= "fish/one.txt", .watchid
= 5 },
326 { .type
= QFILE_MONITOR_TEST_OP_RENAME
,
327 .filesrc
= "fish/one.txt", .filedst
= "two.txt", },
328 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
329 .filesrc
= "one.txt", .watchid
= 4,
330 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
331 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
332 .filesrc
= "two.txt", .watchid
= 0,
333 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
334 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
335 .filesrc
= "two.txt", .watchid
= 2,
336 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
339 { .type
= QFILE_MONITOR_TEST_OP_RMDIR
,
340 .filesrc
= "fish", },
341 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
342 .filesrc
= "", .watchid
= 4,
343 .eventid
= QFILE_MONITOR_EVENT_IGNORED
},
344 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
345 .filesrc
= "fish", .watchid
= 0,
346 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
347 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
348 .filesrc
= "fish", .watchid
= 4 },
351 { .type
= QFILE_MONITOR_TEST_OP_UNLINK
,
352 .filesrc
= "two.txt", },
353 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
354 .filesrc
= "two.txt", .watchid
= 0,
355 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
356 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
357 .filesrc
= "two.txt", .watchid
= 2,
358 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
361 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
362 .filesrc
= "two.txt", .watchid
= 2 },
363 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
364 .filesrc
= NULL
, .watchid
= 0 },
366 Error
*local_err
= NULL
;
368 QFileMonitor
*mon
= qemu_file_monitor_new(&local_err
);
374 char *pathsrc
= NULL
;
375 char *pathdst
= NULL
;
376 QFileMonitorTestData data
;
378 qemu_mutex_init(&data
.lock
);
382 * The file monitor needs the main loop running in
383 * order to receive events from inotify. We must
384 * thus spawn a background thread to run an event
385 * loop impl, while this thread triggers the
386 * actual file operations we're testing
390 qemu_thread_create(&th
, "event-loop",
391 qemu_file_monitor_test_event_loop
, NULL
,
392 QEMU_THREAD_JOINABLE
);
395 g_printerr("File monitoring not available: %s",
396 error_get_pretty(local_err
));
397 error_free(local_err
);
401 dir
= g_dir_make_tmp("test-util-filemonitor-XXXXXX",
404 g_printerr("Unable to create tmp dir %s",
411 * Run through the operation sequence validating events
414 for (i
= 0; i
< G_N_ELEMENTS(ops
); i
++) {
415 const QFileMonitorTestOp
*op
= &(ops
[i
]);
420 const char *watchfile
;
422 pathsrc
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
424 pathdst
= g_strdup_printf("%s/%s", dir
, op
->filedst
);
428 case QFILE_MONITOR_TEST_OP_ADD_WATCH
:
430 g_printerr("Add watch %s %s %d\n",
431 dir
, op
->filesrc
, op
->watchid
);
433 if (op
->filesrc
&& strchr(op
->filesrc
, '/')) {
434 watchdir
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
435 watchfile
= strrchr(watchdir
, '/');
436 *(char *)watchfile
= '\0';
438 if (*watchfile
== '\0') {
442 watchdir
= g_strdup(dir
);
443 watchfile
= op
->filesrc
;
446 qemu_file_monitor_add_watch(mon
,
449 qemu_file_monitor_test_handler
,
454 g_printerr("Unable to add watch %s",
455 error_get_pretty(local_err
));
458 if (watchid
!= op
->watchid
) {
459 g_printerr("Unexpected watch ID %d, wanted %d\n",
460 watchid
, op
->watchid
);
464 case QFILE_MONITOR_TEST_OP_DEL_WATCH
:
466 g_printerr("Del watch %s %d\n", dir
, op
->watchid
);
468 if (op
->filesrc
&& strchr(op
->filesrc
, '/')) {
469 watchdir
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
470 watchfile
= strrchr(watchdir
, '/');
471 *(char *)watchfile
= '\0';
473 watchdir
= g_strdup(dir
);
475 qemu_file_monitor_remove_watch(mon
,
480 case QFILE_MONITOR_TEST_OP_EVENT
:
482 g_printerr("Event id=%d event=%d file=%s\n",
483 op
->watchid
, op
->eventid
, op
->filesrc
);
485 if (!qemu_file_monitor_test_expect(
486 &data
, op
->watchid
, op
->eventid
, op
->filesrc
))
489 case QFILE_MONITOR_TEST_OP_CREATE
:
491 g_printerr("Create %s\n", pathsrc
);
493 fd
= open(pathsrc
, O_WRONLY
| O_CREAT
, 0700);
495 g_printerr("Unable to create %s: %s",
496 pathsrc
, strerror(errno
));
502 case QFILE_MONITOR_TEST_OP_APPEND
:
504 g_printerr("Append %s\n", pathsrc
);
506 fd
= open(pathsrc
, O_WRONLY
| O_APPEND
, 0700);
508 g_printerr("Unable to open %s: %s",
509 pathsrc
, strerror(errno
));
513 if (write(fd
, "Hello World", 10) != 10) {
514 g_printerr("Unable to write %s: %s",
515 pathsrc
, strerror(errno
));
522 case QFILE_MONITOR_TEST_OP_TRUNC
:
524 g_printerr("Truncate %s\n", pathsrc
);
526 if (truncate(pathsrc
, 4) < 0) {
527 g_printerr("Unable to truncate %s: %s",
528 pathsrc
, strerror(errno
));
533 case QFILE_MONITOR_TEST_OP_RENAME
:
535 g_printerr("Rename %s -> %s\n", pathsrc
, pathdst
);
537 if (rename(pathsrc
, pathdst
) < 0) {
538 g_printerr("Unable to rename %s to %s: %s",
539 pathsrc
, pathdst
, strerror(errno
));
544 case QFILE_MONITOR_TEST_OP_UNLINK
:
546 g_printerr("Unlink %s\n", pathsrc
);
548 if (unlink(pathsrc
) < 0) {
549 g_printerr("Unable to unlink %s: %s",
550 pathsrc
, strerror(errno
));
555 case QFILE_MONITOR_TEST_OP_TOUCH
:
557 g_printerr("Touch %s\n", pathsrc
);
561 if (utime(pathsrc
, &ubuf
) < 0) {
562 g_printerr("Unable to touch %s: %s",
563 pathsrc
, strerror(errno
));
568 case QFILE_MONITOR_TEST_OP_MKDIR
:
570 g_printerr("Mkdir %s\n", pathsrc
);
572 if (mkdir(pathsrc
, 0700) < 0) {
573 g_printerr("Unable to mkdir %s: %s",
574 pathsrc
, strerror(errno
));
579 case QFILE_MONITOR_TEST_OP_RMDIR
:
581 g_printerr("Rmdir %s\n", pathsrc
);
583 if (rmdir(pathsrc
) < 0) {
584 g_printerr("Unable to rmdir %s: %s",
585 pathsrc
, strerror(errno
));
591 g_assert_not_reached();
596 pathsrc
= pathdst
= NULL
;
605 qemu_mutex_lock(&evlock
);
607 timer
= g_timer_new();
608 while (evrunning
&& g_timer_elapsed(timer
, NULL
) < 5) {
609 qemu_mutex_unlock(&evlock
);
611 qemu_mutex_lock(&evlock
);
613 qemu_mutex_unlock(&evlock
);
615 if (g_timer_elapsed(timer
, NULL
) >= 5) {
616 g_printerr("Event loop failed to quit after 5 seconds\n");
618 g_timer_destroy(timer
);
620 qemu_file_monitor_free(mon
);
621 g_list_foreach(data
.records
,
622 (GFunc
)qemu_file_monitor_test_record_free
, NULL
);
623 g_list_free(data
.records
);
624 qemu_mutex_destroy(&data
.lock
);
626 for (i
= 0; i
< G_N_ELEMENTS(ops
); i
++) {
627 const QFileMonitorTestOp
*op
= &(ops
[i
]);
628 char *path
= g_strdup_printf("%s/%s",
630 if (op
->type
== QFILE_MONITOR_TEST_OP_MKDIR
) {
637 path
= g_strdup_printf("%s/%s",
644 if (rmdir(dir
) < 0) {
645 g_printerr("Failed to remove %s: %s\n",
646 dir
, strerror(errno
));
655 int main(int argc
, char **argv
)
657 g_test_init(&argc
, &argv
, NULL
);
659 qemu_init_main_loop(&error_abort
);
661 qemu_mutex_init(&evlock
);
663 debug
= getenv("FILEMONITOR_DEBUG") != NULL
;
664 g_test_add_func("/util/filemonitor", test_file_monitor_events
);