]> Git Repo - qemu.git/blob - tests/test-util-filemonitor.c
Merge remote-tracking branch 'remotes/ehabkost/tags/python-next-pull-request' into...
[qemu.git] / tests / test-util-filemonitor.c
1 /*
2  * Tests for util/filemonitor-*.c
3  *
4  * Copyright 2018 Red Hat, Inc.
5  *
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.
10  *
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.
15  *
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/>.
18  *
19  */
20
21 #include "qemu/osdep.h"
22 #include "qemu/main-loop.h"
23 #include "qapi/error.h"
24 #include "qemu/filemonitor.h"
25
26 #include <utime.h>
27
28 enum {
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,
35 };
36
37 typedef struct {
38     int type;
39     const char *filesrc;
40     const char *filedst;
41 } QFileMonitorTestOp;
42
43 typedef struct {
44     const char *file;
45 } QFileMonitorTestWatch;
46
47 typedef struct {
48     gsize nwatches;
49     const QFileMonitorTestWatch *watches;
50
51     gsize nops;
52     const QFileMonitorTestOp *ops;
53 } QFileMonitorTestPlan;
54
55 typedef struct {
56     int id;
57     QFileMonitorEvent event;
58     char *filename;
59 } QFileMonitorTestRecord;
60
61
62 typedef struct {
63     QemuMutex lock;
64     GList *records;
65 } QFileMonitorTestData;
66
67 static QemuMutex evlock;
68 static bool evstopping;
69 static bool evrunning;
70
71 /*
72  * Main function for a background thread that is
73  * running the event loop during the test
74  */
75 static void *
76 qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
77 {
78     qemu_mutex_lock(&evlock);
79
80     while (!evstopping) {
81         qemu_mutex_unlock(&evlock);
82         main_loop_wait(true);
83         qemu_mutex_lock(&evlock);
84     }
85
86     evrunning = false;
87     qemu_mutex_unlock(&evlock);
88     return NULL;
89 }
90
91
92 /*
93  * File monitor event handler which simply maintains
94  * an ordered list of all events that it receives
95  */
96 static void
97 qemu_file_monitor_test_handler(int id,
98                                QFileMonitorEvent event,
99                                const char *filename,
100                                void *opaque)
101 {
102     QFileMonitorTestData *data = opaque;
103     QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
104
105     rec->id = id;
106     rec->event = event;
107     rec->filename = g_strdup(filename);
108
109     qemu_mutex_lock(&data->lock);
110     data->records = g_list_append(data->records, rec);
111     qemu_mutex_unlock(&data->lock);
112 }
113
114
115 static void
116 qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
117 {
118     g_free(rec->filename);
119     g_free(rec);
120 }
121
122
123 /*
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.
130  */
131 static QFileMonitorTestRecord *
132 qemu_file_monitor_test_next_record(QFileMonitorTestData *data)
133 {
134     GTimer *timer = g_timer_new();
135     QFileMonitorTestRecord *record = NULL;
136     GList *tmp;
137
138     qemu_mutex_lock(&data->lock);
139     while (!data->records && g_timer_elapsed(timer, NULL) < 5) {
140         qemu_mutex_unlock(&data->lock);
141         usleep(10 * 1000);
142         qemu_mutex_lock(&data->lock);
143     }
144     if (data->records) {
145         record = data->records->data;
146         tmp = data->records;
147         data->records = g_list_remove_link(data->records, tmp);
148         g_list_free(tmp);
149     }
150     qemu_mutex_unlock(&data->lock);
151
152     g_timer_destroy(timer);
153     return record;
154 }
155
156
157 /*
158  * Check whether the event record we retrieved matches
159  * data we were expecting to see for the event
160  */
161 static bool
162 qemu_file_monitor_test_expect(QFileMonitorTestData *data,
163                               int id,
164                               QFileMonitorEvent event,
165                               const char *filename)
166 {
167     QFileMonitorTestRecord *rec;
168     bool ret = false;
169
170     rec = qemu_file_monitor_test_next_record(data);
171
172     if (!rec) {
173         g_printerr("Missing event watch id %d event %d file %s\n",
174                    id, event, filename);
175         return false;
176     }
177
178     if (id != rec->id) {
179         g_printerr("Expected watch id %d but got %d\n", id, rec->id);
180         goto cleanup;
181     }
182
183     if (event != rec->event) {
184         g_printerr("Expected event %d but got %d\n", event, rec->event);
185         goto cleanup;
186     }
187
188     if (!g_str_equal(filename, rec->filename)) {
189         g_printerr("Expected filename %s but got %s\n",
190                    filename, rec->filename);
191         goto cleanup;
192     }
193
194     ret = true;
195
196  cleanup:
197     qemu_file_monitor_test_record_free(rec);
198     return ret;
199 }
200
201
202 static void
203 test_file_monitor_events(const void *opaque)
204 {
205     const QFileMonitorTestPlan *plan = opaque;
206     Error *local_err = NULL;
207     GError *gerr = NULL;
208     QFileMonitor *mon = qemu_file_monitor_new(&local_err);
209     QemuThread th;
210     GTimer *timer;
211     gchar *dir = NULL;
212     int err = -1;
213     gsize i, j;
214     char *pathsrc = NULL;
215     char *pathdst = NULL;
216     QFileMonitorTestData data;
217
218     qemu_mutex_init(&data.lock);
219     data.records = NULL;
220
221     /*
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
227      */
228     evrunning = 1;
229     evstopping = 0;
230     qemu_thread_create(&th, "event-loop",
231                        qemu_file_monitor_test_event_loop, NULL,
232                        QEMU_THREAD_JOINABLE);
233
234     if (local_err) {
235         g_printerr("File monitoring not available: %s",
236                    error_get_pretty(local_err));
237         error_free(local_err);
238         return;
239     }
240
241     dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
242                          &gerr);
243     if (!dir) {
244         g_printerr("Unable to create tmp dir %s",
245                    gerr->message);
246         g_error_free(gerr);
247         abort();
248     }
249
250     /*
251      * First register all the directory / file watches
252      * we're interested in seeing events against
253      */
254     for (i = 0; i < plan->nwatches; i++) {
255         int watchid;
256         watchid = qemu_file_monitor_add_watch(mon,
257                                               dir,
258                                               plan->watches[i].file,
259                                               qemu_file_monitor_test_handler,
260                                               &data,
261                                               &local_err);
262         if (watchid < 0) {
263             g_printerr("Unable to add watch %s",
264                        error_get_pretty(local_err));
265             goto cleanup;
266         }
267     }
268
269
270     /*
271      * Now invoke all the file operations (create,
272      * delete, rename, chmod, etc). These operations
273      * will trigger the various file monitor events
274      */
275     for (i = 0; i < plan->nops; i++) {
276         const QFileMonitorTestOp *op = &(plan->ops[i]);
277         int fd;
278         struct utimbuf ubuf;
279
280         pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
281         if (op->filedst) {
282             pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
283         }
284
285         switch (op->type) {
286         case QFILE_MONITOR_TEST_OP_CREATE:
287             fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
288             if (fd < 0) {
289                 g_printerr("Unable to create %s: %s",
290                            pathsrc, strerror(errno));
291                 goto cleanup;
292             }
293             close(fd);
294             break;
295
296         case QFILE_MONITOR_TEST_OP_APPEND:
297             fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
298             if (fd < 0) {
299                 g_printerr("Unable to open %s: %s",
300                            pathsrc, strerror(errno));
301                 goto cleanup;
302             }
303
304             if (write(fd, "Hello World", 10) != 10) {
305                 g_printerr("Unable to write %s: %s",
306                            pathsrc, strerror(errno));
307                 close(fd);
308                 goto cleanup;
309             }
310             close(fd);
311             break;
312
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));
317                 goto cleanup;
318             }
319             break;
320
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));
325                 goto cleanup;
326             }
327             break;
328
329         case QFILE_MONITOR_TEST_OP_UNLINK:
330             if (unlink(pathsrc) < 0) {
331                 g_printerr("Unable to unlink %s: %s",
332                            pathsrc, strerror(errno));
333                 goto cleanup;
334             }
335             break;
336
337         case QFILE_MONITOR_TEST_OP_TOUCH:
338             ubuf.actime = 1024;
339             ubuf.modtime = 1025;
340             if (utime(pathsrc, &ubuf) < 0) {
341                 g_printerr("Unable to touch %s: %s",
342                            pathsrc, strerror(errno));
343                 goto cleanup;
344             }
345             break;
346
347         default:
348             g_assert_not_reached();
349         }
350
351         g_free(pathsrc);
352         g_free(pathdst);
353         pathsrc = pathdst = NULL;
354     }
355
356
357     /*
358      * Finally validate that we have received all the events
359      * we expect to see for the combination of watches and
360      * file operations
361      */
362     for (i = 0; i < plan->nops; i++) {
363         const QFileMonitorTestOp *op = &(plan->ops[i]);
364
365         switch (op->type) {
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))
370                     continue;
371
372                 if (!qemu_file_monitor_test_expect(
373                         &data, j, QFILE_MONITOR_EVENT_CREATED, op->filesrc))
374                     goto cleanup;
375             }
376             break;
377
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))
383                     continue;
384
385                 if (!qemu_file_monitor_test_expect(
386                         &data, j, QFILE_MONITOR_EVENT_MODIFIED, op->filesrc))
387                     goto cleanup;
388             }
389             break;
390
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))
395                     continue;
396
397                 if (!qemu_file_monitor_test_expect(
398                         &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
399                     goto cleanup;
400             }
401
402             for (j = 0; j < plan->nwatches; j++) {
403                 if (plan->watches[j].file &&
404                     !g_str_equal(plan->watches[j].file, op->filedst))
405                     continue;
406
407                 if (!qemu_file_monitor_test_expect(
408                         &data, j, QFILE_MONITOR_EVENT_CREATED, op->filedst))
409                     goto cleanup;
410             }
411             break;
412
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))
417                     continue;
418
419                 if (!qemu_file_monitor_test_expect(
420                         &data, j, QFILE_MONITOR_EVENT_ATTRIBUTES, op->filesrc))
421                     goto cleanup;
422             }
423             break;
424
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))
429                     continue;
430
431                 if (!qemu_file_monitor_test_expect(
432                         &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
433                     goto cleanup;
434             }
435             break;
436
437         default:
438             g_assert_not_reached();
439         }
440     }
441
442     err = 0;
443
444  cleanup:
445     g_free(pathsrc);
446     g_free(pathdst);
447
448     qemu_mutex_lock(&evlock);
449     evstopping = 1;
450     timer = g_timer_new();
451     while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
452         qemu_mutex_unlock(&evlock);
453         usleep(10 * 1000);
454         qemu_mutex_lock(&evlock);
455     }
456     qemu_mutex_unlock(&evlock);
457
458     if (g_timer_elapsed(timer, NULL) >= 5) {
459         g_printerr("Event loop failed to quit after 5 seconds\n");
460     }
461     g_timer_destroy(timer);
462
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);
466         unlink(pathsrc);
467         g_free(pathsrc);
468         if (op->filedst) {
469             pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
470             unlink(pathdst);
471             g_free(pathdst);
472         }
473     }
474
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);
480     if (dir) {
481         rmdir(dir);
482     }
483     g_free(dir);
484     g_assert(err == 0);
485 }
486
487
488 /*
489  * Set of structs which define which file name patterns
490  * we're trying to watch against. NULL, means all files
491  * in the directory
492  */
493 static const QFileMonitorTestWatch watches_any[] = {
494     { NULL },
495 };
496
497 static const QFileMonitorTestWatch watches_one[] = {
498     { "one.txt" },
499 };
500
501 static const QFileMonitorTestWatch watches_two[] = {
502     { "two.txt" },
503 };
504
505 static const QFileMonitorTestWatch watches_many[] = {
506     { NULL },
507     { "one.txt" },
508     { "two.txt" },
509 };
510
511
512 /*
513  * Various sets of file operations we're going to
514  * trigger and validate events for
515  */
516 static const QFileMonitorTestOp ops_create_one[] = {
517     { .type = QFILE_MONITOR_TEST_OP_CREATE,
518       .filesrc = "one.txt", }
519 };
520
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", }
526 };
527
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", }
535 };
536
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" }
542 };
543
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" }
551 };
552
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", },
558 };
559
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", },
565 };
566
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", },
572 };
573
574
575 /*
576  * No we define data sets for the combinatorial
577  * expansion of file watches and operation sets
578  */
579 #define PLAN_DATA(o, w) \
580     static const QFileMonitorTestPlan plan_ ## o ## _ ## w = { \
581         .nops = G_N_ELEMENTS(ops_ ##o), \
582         .ops = ops_ ##o, \
583         .nwatches = G_N_ELEMENTS(watches_ ##w), \
584         .watches = watches_ ## w, \
585     }
586
587 PLAN_DATA(create_one, any);
588 PLAN_DATA(create_one, one);
589 PLAN_DATA(create_one, two);
590 PLAN_DATA(create_one, many);
591
592 PLAN_DATA(delete_one, any);
593 PLAN_DATA(delete_one, one);
594 PLAN_DATA(delete_one, two);
595 PLAN_DATA(delete_one, many);
596
597 PLAN_DATA(create_many, any);
598 PLAN_DATA(create_many, one);
599 PLAN_DATA(create_many, two);
600 PLAN_DATA(create_many, many);
601
602 PLAN_DATA(rename_one, any);
603 PLAN_DATA(rename_one, one);
604 PLAN_DATA(rename_one, two);
605 PLAN_DATA(rename_one, many);
606
607 PLAN_DATA(rename_many, any);
608 PLAN_DATA(rename_many, one);
609 PLAN_DATA(rename_many, two);
610 PLAN_DATA(rename_many, many);
611
612 PLAN_DATA(append_one, any);
613 PLAN_DATA(append_one, one);
614 PLAN_DATA(append_one, two);
615 PLAN_DATA(append_one, many);
616
617 PLAN_DATA(trunc_one, any);
618 PLAN_DATA(trunc_one, one);
619 PLAN_DATA(trunc_one, two);
620 PLAN_DATA(trunc_one, many);
621
622 PLAN_DATA(touch_one, any);
623 PLAN_DATA(touch_one, one);
624 PLAN_DATA(touch_one, two);
625 PLAN_DATA(touch_one, many);
626
627
628 int main(int argc, char **argv)
629 {
630     g_test_init(&argc, &argv, NULL);
631
632     qemu_init_main_loop(&error_abort);
633
634     qemu_mutex_init(&evlock);
635
636     /*
637      * Register test cases for the combinatorial
638      * expansion of file watches and operation sets
639      */
640     #define PLAN_REGISTER(o, w)                                         \
641         g_test_add_data_func("/util/filemonitor/" # o "/" # w,          \
642                              &plan_ ## o ## _ ## w, test_file_monitor_events)
643
644     PLAN_REGISTER(create_one, any);
645     PLAN_REGISTER(create_one, one);
646     PLAN_REGISTER(create_one, two);
647     PLAN_REGISTER(create_one, many);
648
649     PLAN_REGISTER(delete_one, any);
650     PLAN_REGISTER(delete_one, one);
651     PLAN_REGISTER(delete_one, two);
652     PLAN_REGISTER(delete_one, many);
653
654     PLAN_REGISTER(create_many, any);
655     PLAN_REGISTER(create_many, one);
656     PLAN_REGISTER(create_many, two);
657     PLAN_REGISTER(create_many, many);
658
659     PLAN_REGISTER(rename_one, any);
660     PLAN_REGISTER(rename_one, one);
661     PLAN_REGISTER(rename_one, two);
662     PLAN_REGISTER(rename_one, many);
663
664     PLAN_REGISTER(rename_many, any);
665     PLAN_REGISTER(rename_many, one);
666     PLAN_REGISTER(rename_many, two);
667     PLAN_REGISTER(rename_many, many);
668
669     PLAN_REGISTER(append_one, any);
670     PLAN_REGISTER(append_one, one);
671     PLAN_REGISTER(append_one, two);
672     PLAN_REGISTER(append_one, many);
673
674     PLAN_REGISTER(trunc_one, any);
675     PLAN_REGISTER(trunc_one, one);
676     PLAN_REGISTER(trunc_one, two);
677     PLAN_REGISTER(trunc_one, many);
678
679     PLAN_REGISTER(touch_one, any);
680     PLAN_REGISTER(touch_one, one);
681     PLAN_REGISTER(touch_one, two);
682     PLAN_REGISTER(touch_one, many);
683
684     return g_test_run();
685 }
This page took 0.06277 seconds and 4 git commands to generate.