]> git.proxmox.com Git - mirror_qemu.git/blobdiff - tests/test-util-filemonitor.c
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
[mirror_qemu.git] / tests / test-util-filemonitor.c
index ea3715a8f4437d14d3cddc1059147abe65b7315a..45009c69f41d6e9164b50420640cc0d946fbb50d 100644 (file)
@@ -35,18 +35,25 @@ enum {
     QFILE_MONITOR_TEST_OP_RENAME,
     QFILE_MONITOR_TEST_OP_TOUCH,
     QFILE_MONITOR_TEST_OP_UNLINK,
+    QFILE_MONITOR_TEST_OP_MKDIR,
+    QFILE_MONITOR_TEST_OP_RMDIR,
 };
 
 typedef struct {
     int type;
     const char *filesrc;
     const char *filedst;
-    int watchid;
+    int64_t *watchid;
     int eventid;
+    /*
+     * Only valid with OP_EVENT - this event might be
+     * swapped with the next OP_EVENT
+     */
+    bool swapnext;
 } QFileMonitorTestOp;
 
 typedef struct {
-    int id;
+    int64_t id;
     QFileMonitorEvent event;
     char *filename;
 } QFileMonitorTestRecord;
@@ -88,7 +95,7 @@ qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
  * an ordered list of all events that it receives
  */
 static void
-qemu_file_monitor_test_handler(int id,
+qemu_file_monitor_test_handler(int64_t id,
                                QFileMonitorEvent event,
                                const char *filename,
                                void *opaque)
@@ -96,6 +103,10 @@ qemu_file_monitor_test_handler(int id,
     QFileMonitorTestData *data = opaque;
     QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
 
+    if (debug) {
+        g_printerr("Queue event id %" PRIx64 " event %d file %s\n",
+                   id, event, filename);
+    }
     rec->id = id;
     rec->event = event;
     rec->filename = g_strdup(filename);
@@ -123,7 +134,8 @@ qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
  * to wait for the event to be queued for us.
  */
 static QFileMonitorTestRecord *
-qemu_file_monitor_test_next_record(QFileMonitorTestData *data)
+qemu_file_monitor_test_next_record(QFileMonitorTestData *data,
+                                   QFileMonitorTestRecord *pushback)
 {
     GTimer *timer = g_timer_new();
     QFileMonitorTestRecord *record = NULL;
@@ -137,9 +149,15 @@ qemu_file_monitor_test_next_record(QFileMonitorTestData *data)
     }
     if (data->records) {
         record = data->records->data;
-        tmp = data->records;
-        data->records = g_list_remove_link(data->records, tmp);
-        g_list_free(tmp);
+        if (pushback) {
+            data->records->data = pushback;
+        } else {
+            tmp = data->records;
+            data->records = g_list_remove_link(data->records, tmp);
+            g_list_free(tmp);
+        }
+    } else if (pushback) {
+        qemu_file_monitor_test_record_free(pushback);
     }
     qemu_mutex_unlock(&data->lock);
 
@@ -154,23 +172,31 @@ qemu_file_monitor_test_next_record(QFileMonitorTestData *data)
  */
 static bool
 qemu_file_monitor_test_expect(QFileMonitorTestData *data,
-                              int id,
+                              int64_t id,
                               QFileMonitorEvent event,
-                              const char *filename)
+                              const char *filename,
+                              bool swapnext)
 {
     QFileMonitorTestRecord *rec;
     bool ret = false;
 
-    rec = qemu_file_monitor_test_next_record(data);
+    rec = qemu_file_monitor_test_next_record(data, NULL);
 
+ retry:
     if (!rec) {
-        g_printerr("Missing event watch id %d event %d file %s\n",
+        g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n",
                    id, event, filename);
         return false;
     }
 
     if (id != rec->id) {
-        g_printerr("Expected watch id %d but got %d\n", id, rec->id);
+        if (swapnext) {
+            rec = qemu_file_monitor_test_next_record(data, rec);
+            swapnext = false;
+            goto retry;
+        }
+        g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n",
+                   id, rec->id);
         goto cleanup;
     }
 
@@ -196,122 +222,177 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data,
 static void
 test_file_monitor_events(void)
 {
+    int64_t watch0 = 0;
+    int64_t watch1 = 0;
+    int64_t watch2 = 0;
+    int64_t watch3 = 0;
+    int64_t watch4 = 0;
+    int64_t watch5 = 0;
     QFileMonitorTestOp ops[] = {
         { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
-          .filesrc = NULL, .watchid = 0 },
+          .filesrc = NULL, .watchid = &watch0 },
         { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
-          .filesrc = "one.txt", .watchid = 1 },
+          .filesrc = "one.txt", .watchid = &watch1 },
         { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
-          .filesrc = "two.txt", .watchid = 2 },
+          .filesrc = "two.txt", .watchid = &watch2 },
 
 
         { .type = QFILE_MONITOR_TEST_OP_CREATE,
           .filesrc = "one.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "one.txt", .watchid = 0,
+          .filesrc = "one.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "one.txt", .watchid = 1,
+          .filesrc = "one.txt", .watchid = &watch1,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_CREATE,
           .filesrc = "two.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 0,
+          .filesrc = "two.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 2,
+          .filesrc = "two.txt", .watchid = &watch2,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_CREATE,
           .filesrc = "three.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "three.txt", .watchid = 0,
+          .filesrc = "three.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_UNLINK,
           .filesrc = "three.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "three.txt", .watchid = 0,
+          .filesrc = "three.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_DELETED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_RENAME,
           .filesrc = "one.txt", .filedst = "two.txt" },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "one.txt", .watchid = 0,
+          .filesrc = "one.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_DELETED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "one.txt", .watchid = 1,
+          .filesrc = "one.txt", .watchid = &watch1,
           .eventid = QFILE_MONITOR_EVENT_DELETED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 0,
+          .filesrc = "two.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 2,
+          .filesrc = "two.txt", .watchid = &watch2,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_APPEND,
           .filesrc = "two.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 0,
+          .filesrc = "two.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_MODIFIED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 2,
+          .filesrc = "two.txt", .watchid = &watch2,
           .eventid = QFILE_MONITOR_EVENT_MODIFIED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_TOUCH,
           .filesrc = "two.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 0,
+          .filesrc = "two.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 2,
+          .filesrc = "two.txt", .watchid = &watch2,
           .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
 
 
         { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
-          .filesrc = "one.txt", .watchid = 1 },
+          .filesrc = "one.txt", .watchid = &watch1 },
         { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
-          .filesrc = "one.txt", .watchid = 3 },
+          .filesrc = "one.txt", .watchid = &watch3 },
         { .type = QFILE_MONITOR_TEST_OP_CREATE,
           .filesrc = "one.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "one.txt", .watchid = 0,
+          .filesrc = "one.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "one.txt", .watchid = 3,
+          .filesrc = "one.txt", .watchid = &watch3,
           .eventid = QFILE_MONITOR_EVENT_CREATED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
-          .filesrc = "one.txt", .watchid = 3 },
+          .filesrc = "one.txt", .watchid = &watch3 },
         { .type = QFILE_MONITOR_TEST_OP_UNLINK,
           .filesrc = "one.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "one.txt", .watchid = 0,
+          .filesrc = "one.txt", .watchid = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_DELETED },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_MKDIR,
+          .filesrc = "fish", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "fish", .watchid = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
+          .filesrc = "fish/", .watchid = &watch4 },
+        { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
+          .filesrc = "fish/one.txt", .watchid = &watch5 },
+        { .type = QFILE_MONITOR_TEST_OP_CREATE,
+          .filesrc = "fish/one.txt", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "one.txt", .watchid = &watch4,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "one.txt", .watchid = &watch5,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
+          .filesrc = "fish/one.txt", .watchid = &watch5 },
+        { .type = QFILE_MONITOR_TEST_OP_RENAME,
+          .filesrc = "fish/one.txt", .filedst = "two.txt", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "one.txt", .watchid = &watch4,
+          .eventid = QFILE_MONITOR_EVENT_DELETED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "two.txt", .watchid = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "two.txt", .watchid = &watch2,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_RMDIR,
+          .filesrc = "fish", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "", .watchid = &watch4,
+          .eventid = QFILE_MONITOR_EVENT_IGNORED,
+          .swapnext = true },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "fish", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_DELETED },
+        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
+          .filesrc = "fish", .watchid = &watch4 },
 
 
         { .type = QFILE_MONITOR_TEST_OP_UNLINK,
           .filesrc = "two.txt", },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 0,
+          .filesrc = "two.txt", .watchid = &watch0,
           .eventid = QFILE_MONITOR_EVENT_DELETED },
         { .type = QFILE_MONITOR_TEST_OP_EVENT,
-          .filesrc = "two.txt", .watchid = 2,
+          .filesrc = "two.txt", .watchid = &watch2,
           .eventid = QFILE_MONITOR_EVENT_DELETED },
 
 
         { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
-          .filesrc = "two.txt", .watchid = 2 },
+          .filesrc = "two.txt", .watchid = &watch2 },
         { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
-          .filesrc = NULL, .watchid = 0 },
+          .filesrc = NULL, .watchid = &watch0 },
     };
     Error *local_err = NULL;
     GError *gerr = NULL;
@@ -324,10 +405,22 @@ test_file_monitor_events(void)
     char *pathsrc = NULL;
     char *pathdst = NULL;
     QFileMonitorTestData data;
+    GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal);
+    char *travis_arch;
 
     qemu_mutex_init(&data.lock);
     data.records = NULL;
 
+    /*
+     * This test does not work on Travis LXD containers since some
+     * syscalls are blocked in that environment.
+     */
+    travis_arch = getenv("TRAVIS_ARCH");
+    if (travis_arch && !g_str_equal(travis_arch, "x86_64")) {
+        g_test_skip("Test does not work on non-x86 Travis containers.");
+        return;
+    }
+
     /*
      * The file monitor needs the main loop running in
      * order to receive events from inotify. We must
@@ -364,8 +457,9 @@ test_file_monitor_events(void)
     for (i = 0; i < G_N_ELEMENTS(ops); i++) {
         const QFileMonitorTestOp *op = &(ops[i]);
         int fd;
-        int watchid;
         struct utimbuf ubuf;
+        char *watchdir;
+        const char *watchfile;
 
         pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
         if (op->filedst) {
@@ -375,42 +469,68 @@ test_file_monitor_events(void)
         switch (op->type) {
         case QFILE_MONITOR_TEST_OP_ADD_WATCH:
             if (debug) {
-                g_printerr("Add watch %s %s %d\n",
-                           dir, op->filesrc, op->watchid);
+                g_printerr("Add watch %s %s\n",
+                           dir, op->filesrc);
             }
-            watchid =
+            if (op->filesrc && strchr(op->filesrc, '/')) {
+                watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
+                watchfile = strrchr(watchdir, '/');
+                *(char *)watchfile = '\0';
+                watchfile++;
+                if (*watchfile == '\0') {
+                    watchfile = NULL;
+                }
+            } else {
+                watchdir = g_strdup(dir);
+                watchfile = op->filesrc;
+            }
+            *op->watchid =
                 qemu_file_monitor_add_watch(mon,
-                                            dir,
-                                            op->filesrc,
+                                            watchdir,
+                                            watchfile,
                                             qemu_file_monitor_test_handler,
                                             &data,
                                             &local_err);
-            if (watchid < 0) {
+            g_free(watchdir);
+            if (*op->watchid < 0) {
                 g_printerr("Unable to add watch %s",
                            error_get_pretty(local_err));
                 goto cleanup;
             }
-            if (watchid != op->watchid) {
-                g_printerr("Unexpected watch ID %d, wanted %d\n",
-                           watchid, op->watchid);
+            if (debug) {
+                g_printerr("Watch ID %" PRIx64 "\n", *op->watchid);
+            }
+            if (g_hash_table_contains(ids, op->watchid)) {
+                g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid);
                 goto cleanup;
             }
+            g_hash_table_add(ids, op->watchid);
             break;
         case QFILE_MONITOR_TEST_OP_DEL_WATCH:
             if (debug) {
-                g_printerr("Del watch %s %d\n", dir, op->watchid);
+                g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid);
+            }
+            if (op->filesrc && strchr(op->filesrc, '/')) {
+                watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
+                watchfile = strrchr(watchdir, '/');
+                *(char *)watchfile = '\0';
+            } else {
+                watchdir = g_strdup(dir);
             }
+            g_hash_table_remove(ids, op->watchid);
             qemu_file_monitor_remove_watch(mon,
-                                           dir,
-                                           op->watchid);
+                                           watchdir,
+                                           *op->watchid);
+            g_free(watchdir);
             break;
         case QFILE_MONITOR_TEST_OP_EVENT:
             if (debug) {
-                g_printerr("Event id=%d event=%d file=%s\n",
-                           op->watchid, op->eventid, op->filesrc);
+                g_printerr("Event id=%" PRIx64 " event=%d file=%s\n",
+                           *op->watchid, op->eventid, op->filesrc);
             }
-            if (!qemu_file_monitor_test_expect(
-                    &data, op->watchid, op->eventid, op->filesrc))
+            if (!qemu_file_monitor_test_expect(&data, *op->watchid,
+                                               op->eventid, op->filesrc,
+                                               op->swapnext))
                 goto cleanup;
             break;
         case QFILE_MONITOR_TEST_OP_CREATE:
@@ -492,6 +612,28 @@ test_file_monitor_events(void)
             }
             break;
 
+        case QFILE_MONITOR_TEST_OP_MKDIR:
+            if (debug) {
+                g_printerr("Mkdir %s\n", pathsrc);
+            }
+            if (mkdir(pathsrc, 0700) < 0) {
+                g_printerr("Unable to mkdir %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
+            }
+            break;
+
+        case QFILE_MONITOR_TEST_OP_RMDIR:
+            if (debug) {
+                g_printerr("Rmdir %s\n", pathsrc);
+            }
+            if (rmdir(pathsrc) < 0) {
+                g_printerr("Unable to rmdir %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
+            }
+            break;
+
         default:
             g_assert_not_reached();
         }
@@ -501,6 +643,8 @@ test_file_monitor_events(void)
         pathsrc = pathdst = NULL;
     }
 
+    g_assert_cmpint(g_hash_table_size(ids), ==, 0);
+
     err = 0;
 
  cleanup:
@@ -532,13 +676,18 @@ test_file_monitor_events(void)
             const QFileMonitorTestOp *op = &(ops[i]);
             char *path = g_strdup_printf("%s/%s",
                                          dir, op->filesrc);
-            unlink(path);
-            g_free(path);
-            if (op->filedst) {
-                path = g_strdup_printf("%s/%s",
-                                       dir, op->filedst);
+            if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) {
+                rmdir(path);
+                g_free(path);
+            } else {
                 unlink(path);
                 g_free(path);
+                if (op->filedst) {
+                    path = g_strdup_printf("%s/%s",
+                                           dir, op->filedst);
+                    unlink(path);
+                    g_free(path);
+                }
             }
         }
         if (rmdir(dir) < 0) {
@@ -547,6 +696,7 @@ test_file_monitor_events(void)
             abort();
         }
     }
+    g_hash_table_unref(ids);
     g_free(dir);
     g_assert(err == 0);
 }