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
,
50 QFileMonitorEvent event
;
52 } QFileMonitorTestRecord
;
58 } QFileMonitorTestData
;
60 static QemuMutex evlock
;
61 static bool evstopping
;
62 static bool evrunning
;
66 * Main function for a background thread that is
67 * running the event loop during the test
70 qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED
)
72 qemu_mutex_lock(&evlock
);
75 qemu_mutex_unlock(&evlock
);
77 qemu_mutex_lock(&evlock
);
81 qemu_mutex_unlock(&evlock
);
87 * File monitor event handler which simply maintains
88 * an ordered list of all events that it receives
91 qemu_file_monitor_test_handler(int id
,
92 QFileMonitorEvent event
,
96 QFileMonitorTestData
*data
= opaque
;
97 QFileMonitorTestRecord
*rec
= g_new0(QFileMonitorTestRecord
, 1);
101 rec
->filename
= g_strdup(filename
);
103 qemu_mutex_lock(&data
->lock
);
104 data
->records
= g_list_append(data
->records
, rec
);
105 qemu_mutex_unlock(&data
->lock
);
110 qemu_file_monitor_test_record_free(QFileMonitorTestRecord
*rec
)
112 g_free(rec
->filename
);
118 * Get the next event record that has been received by
119 * the file monitor event handler. Since events are
120 * emitted in the background thread running the event
121 * loop, we can't assume there is a record available
122 * immediately. Thus we will sleep for upto 5 seconds
123 * to wait for the event to be queued for us.
125 static QFileMonitorTestRecord
*
126 qemu_file_monitor_test_next_record(QFileMonitorTestData
*data
)
128 GTimer
*timer
= g_timer_new();
129 QFileMonitorTestRecord
*record
= NULL
;
132 qemu_mutex_lock(&data
->lock
);
133 while (!data
->records
&& g_timer_elapsed(timer
, NULL
) < 5) {
134 qemu_mutex_unlock(&data
->lock
);
136 qemu_mutex_lock(&data
->lock
);
139 record
= data
->records
->data
;
141 data
->records
= g_list_remove_link(data
->records
, tmp
);
144 qemu_mutex_unlock(&data
->lock
);
146 g_timer_destroy(timer
);
152 * Check whether the event record we retrieved matches
153 * data we were expecting to see for the event
156 qemu_file_monitor_test_expect(QFileMonitorTestData
*data
,
158 QFileMonitorEvent event
,
159 const char *filename
)
161 QFileMonitorTestRecord
*rec
;
164 rec
= qemu_file_monitor_test_next_record(data
);
167 g_printerr("Missing event watch id %d event %d file %s\n",
168 id
, event
, filename
);
173 g_printerr("Expected watch id %d but got %d\n", id
, rec
->id
);
177 if (event
!= rec
->event
) {
178 g_printerr("Expected event %d but got %d\n", event
, rec
->event
);
182 if (!g_str_equal(filename
, rec
->filename
)) {
183 g_printerr("Expected filename %s but got %s\n",
184 filename
, rec
->filename
);
191 qemu_file_monitor_test_record_free(rec
);
197 test_file_monitor_events(void)
199 QFileMonitorTestOp ops
[] = {
200 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
201 .filesrc
= NULL
, .watchid
= 0 },
202 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
203 .filesrc
= "one.txt", .watchid
= 1 },
204 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
205 .filesrc
= "two.txt", .watchid
= 2 },
208 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
209 .filesrc
= "one.txt", },
210 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
211 .filesrc
= "one.txt", .watchid
= 0,
212 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
213 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
214 .filesrc
= "one.txt", .watchid
= 1,
215 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
218 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
219 .filesrc
= "two.txt", },
220 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
221 .filesrc
= "two.txt", .watchid
= 0,
222 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
223 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
224 .filesrc
= "two.txt", .watchid
= 2,
225 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
228 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
229 .filesrc
= "three.txt", },
230 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
231 .filesrc
= "three.txt", .watchid
= 0,
232 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
235 { .type
= QFILE_MONITOR_TEST_OP_UNLINK
,
236 .filesrc
= "three.txt", },
237 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
238 .filesrc
= "three.txt", .watchid
= 0,
239 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
242 { .type
= QFILE_MONITOR_TEST_OP_RENAME
,
243 .filesrc
= "one.txt", .filedst
= "two.txt" },
244 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
245 .filesrc
= "one.txt", .watchid
= 0,
246 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
247 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
248 .filesrc
= "one.txt", .watchid
= 1,
249 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
250 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
251 .filesrc
= "two.txt", .watchid
= 0,
252 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
253 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
254 .filesrc
= "two.txt", .watchid
= 2,
255 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
258 { .type
= QFILE_MONITOR_TEST_OP_APPEND
,
259 .filesrc
= "two.txt", },
260 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
261 .filesrc
= "two.txt", .watchid
= 0,
262 .eventid
= QFILE_MONITOR_EVENT_MODIFIED
},
263 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
264 .filesrc
= "two.txt", .watchid
= 2,
265 .eventid
= QFILE_MONITOR_EVENT_MODIFIED
},
268 { .type
= QFILE_MONITOR_TEST_OP_TOUCH
,
269 .filesrc
= "two.txt", },
270 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
271 .filesrc
= "two.txt", .watchid
= 0,
272 .eventid
= QFILE_MONITOR_EVENT_ATTRIBUTES
},
273 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
274 .filesrc
= "two.txt", .watchid
= 2,
275 .eventid
= QFILE_MONITOR_EVENT_ATTRIBUTES
},
278 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
279 .filesrc
= "one.txt", .watchid
= 1 },
280 { .type
= QFILE_MONITOR_TEST_OP_ADD_WATCH
,
281 .filesrc
= "one.txt", .watchid
= 3 },
282 { .type
= QFILE_MONITOR_TEST_OP_CREATE
,
283 .filesrc
= "one.txt", },
284 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
285 .filesrc
= "one.txt", .watchid
= 0,
286 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
287 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
288 .filesrc
= "one.txt", .watchid
= 3,
289 .eventid
= QFILE_MONITOR_EVENT_CREATED
},
292 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
293 .filesrc
= "one.txt", .watchid
= 3 },
294 { .type
= QFILE_MONITOR_TEST_OP_UNLINK
,
295 .filesrc
= "one.txt", },
296 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
297 .filesrc
= "one.txt", .watchid
= 0,
298 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
301 { .type
= QFILE_MONITOR_TEST_OP_UNLINK
,
302 .filesrc
= "two.txt", },
303 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
304 .filesrc
= "two.txt", .watchid
= 0,
305 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
306 { .type
= QFILE_MONITOR_TEST_OP_EVENT
,
307 .filesrc
= "two.txt", .watchid
= 2,
308 .eventid
= QFILE_MONITOR_EVENT_DELETED
},
311 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
312 .filesrc
= "two.txt", .watchid
= 2 },
313 { .type
= QFILE_MONITOR_TEST_OP_DEL_WATCH
,
314 .filesrc
= NULL
, .watchid
= 0 },
316 Error
*local_err
= NULL
;
318 QFileMonitor
*mon
= qemu_file_monitor_new(&local_err
);
324 char *pathsrc
= NULL
;
325 char *pathdst
= NULL
;
326 QFileMonitorTestData data
;
328 qemu_mutex_init(&data
.lock
);
332 * The file monitor needs the main loop running in
333 * order to receive events from inotify. We must
334 * thus spawn a background thread to run an event
335 * loop impl, while this thread triggers the
336 * actual file operations we're testing
340 qemu_thread_create(&th
, "event-loop",
341 qemu_file_monitor_test_event_loop
, NULL
,
342 QEMU_THREAD_JOINABLE
);
345 g_printerr("File monitoring not available: %s",
346 error_get_pretty(local_err
));
347 error_free(local_err
);
351 dir
= g_dir_make_tmp("test-util-filemonitor-XXXXXX",
354 g_printerr("Unable to create tmp dir %s",
361 * Run through the operation sequence validating events
364 for (i
= 0; i
< G_N_ELEMENTS(ops
); i
++) {
365 const QFileMonitorTestOp
*op
= &(ops
[i
]);
370 pathsrc
= g_strdup_printf("%s/%s", dir
, op
->filesrc
);
372 pathdst
= g_strdup_printf("%s/%s", dir
, op
->filedst
);
376 case QFILE_MONITOR_TEST_OP_ADD_WATCH
:
378 g_printerr("Add watch %s %s %d\n",
379 dir
, op
->filesrc
, op
->watchid
);
382 qemu_file_monitor_add_watch(mon
,
385 qemu_file_monitor_test_handler
,
389 g_printerr("Unable to add watch %s",
390 error_get_pretty(local_err
));
393 if (watchid
!= op
->watchid
) {
394 g_printerr("Unexpected watch ID %d, wanted %d\n",
395 watchid
, op
->watchid
);
399 case QFILE_MONITOR_TEST_OP_DEL_WATCH
:
401 g_printerr("Del watch %s %d\n", dir
, op
->watchid
);
403 qemu_file_monitor_remove_watch(mon
,
407 case QFILE_MONITOR_TEST_OP_EVENT
:
409 g_printerr("Event id=%d event=%d file=%s\n",
410 op
->watchid
, op
->eventid
, op
->filesrc
);
412 if (!qemu_file_monitor_test_expect(
413 &data
, op
->watchid
, op
->eventid
, op
->filesrc
))
416 case QFILE_MONITOR_TEST_OP_CREATE
:
418 g_printerr("Create %s\n", pathsrc
);
420 fd
= open(pathsrc
, O_WRONLY
| O_CREAT
, 0700);
422 g_printerr("Unable to create %s: %s",
423 pathsrc
, strerror(errno
));
429 case QFILE_MONITOR_TEST_OP_APPEND
:
431 g_printerr("Append %s\n", pathsrc
);
433 fd
= open(pathsrc
, O_WRONLY
| O_APPEND
, 0700);
435 g_printerr("Unable to open %s: %s",
436 pathsrc
, strerror(errno
));
440 if (write(fd
, "Hello World", 10) != 10) {
441 g_printerr("Unable to write %s: %s",
442 pathsrc
, strerror(errno
));
449 case QFILE_MONITOR_TEST_OP_TRUNC
:
451 g_printerr("Truncate %s\n", pathsrc
);
453 if (truncate(pathsrc
, 4) < 0) {
454 g_printerr("Unable to truncate %s: %s",
455 pathsrc
, strerror(errno
));
460 case QFILE_MONITOR_TEST_OP_RENAME
:
462 g_printerr("Rename %s -> %s\n", pathsrc
, pathdst
);
464 if (rename(pathsrc
, pathdst
) < 0) {
465 g_printerr("Unable to rename %s to %s: %s",
466 pathsrc
, pathdst
, strerror(errno
));
471 case QFILE_MONITOR_TEST_OP_UNLINK
:
473 g_printerr("Unlink %s\n", pathsrc
);
475 if (unlink(pathsrc
) < 0) {
476 g_printerr("Unable to unlink %s: %s",
477 pathsrc
, strerror(errno
));
482 case QFILE_MONITOR_TEST_OP_TOUCH
:
484 g_printerr("Touch %s\n", pathsrc
);
488 if (utime(pathsrc
, &ubuf
) < 0) {
489 g_printerr("Unable to touch %s: %s",
490 pathsrc
, strerror(errno
));
496 g_assert_not_reached();
501 pathsrc
= pathdst
= NULL
;
510 qemu_mutex_lock(&evlock
);
512 timer
= g_timer_new();
513 while (evrunning
&& g_timer_elapsed(timer
, NULL
) < 5) {
514 qemu_mutex_unlock(&evlock
);
516 qemu_mutex_lock(&evlock
);
518 qemu_mutex_unlock(&evlock
);
520 if (g_timer_elapsed(timer
, NULL
) >= 5) {
521 g_printerr("Event loop failed to quit after 5 seconds\n");
523 g_timer_destroy(timer
);
525 qemu_file_monitor_free(mon
);
526 g_list_foreach(data
.records
,
527 (GFunc
)qemu_file_monitor_test_record_free
, NULL
);
528 g_list_free(data
.records
);
529 qemu_mutex_destroy(&data
.lock
);
531 for (i
= 0; i
< G_N_ELEMENTS(ops
); i
++) {
532 const QFileMonitorTestOp
*op
= &(ops
[i
]);
533 char *path
= g_strdup_printf("%s/%s",
538 path
= g_strdup_printf("%s/%s",
544 if (rmdir(dir
) < 0) {
545 g_printerr("Failed to remove %s: %s\n",
546 dir
, strerror(errno
));
555 int main(int argc
, char **argv
)
557 g_test_init(&argc
, &argv
, NULL
);
559 qemu_init_main_loop(&error_abort
);
561 qemu_mutex_init(&evlock
);
563 debug
= getenv("FILEMONITOR_DEBUG") != NULL
;
564 g_test_add_func("/util/filemonitor", test_file_monitor_events
);