]> git.proxmox.com Git - mirror_qemu.git/blobdiff - tests/test-util-filemonitor.c
qdev: add qdev_add_vm_change_state_handler()
[mirror_qemu.git] / tests / test-util-filemonitor.c
index 5d95cea5ee3571171d015f5558d1c3bf895fc6e8..46e781c022310c496b81ff9ecf8856cc3ac3cc8e 100644 (file)
 #include <utime.h>
 
 enum {
+    QFILE_MONITOR_TEST_OP_ADD_WATCH,
+    QFILE_MONITOR_TEST_OP_DEL_WATCH,
+    QFILE_MONITOR_TEST_OP_EVENT,
     QFILE_MONITOR_TEST_OP_CREATE,
     QFILE_MONITOR_TEST_OP_APPEND,
     QFILE_MONITOR_TEST_OP_TRUNC,
     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;
+    int64_t *watchid;
+    int eventid;
 } QFileMonitorTestOp;
 
 typedef struct {
-    const char *file;
-} QFileMonitorTestWatch;
-
-typedef struct {
-    gsize nwatches;
-    const QFileMonitorTestWatch *watches;
-
-    gsize nops;
-    const QFileMonitorTestOp *ops;
-} QFileMonitorTestPlan;
-
-typedef struct {
-    int id;
+    int64_t id;
     QFileMonitorEvent event;
     char *filename;
 } QFileMonitorTestRecord;
@@ -67,6 +62,7 @@ typedef struct {
 static QemuMutex evlock;
 static bool evstopping;
 static bool evrunning;
+static bool debug;
 
 /*
  * Main function for a background thread that is
@@ -94,7 +90,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)
@@ -160,7 +156,7 @@ 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)
 {
@@ -170,13 +166,14 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data,
     rec = qemu_file_monitor_test_next_record(data);
 
     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);
+        g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n",
+                   id, rec->id);
         goto cleanup;
     }
 
@@ -200,9 +197,179 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data,
 
 
 static void
-test_file_monitor_events(const void *opaque)
+test_file_monitor_events(void)
 {
-    const QFileMonitorTestPlan *plan = opaque;
+    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 = &watch0 },
+        { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
+          .filesrc = "one.txt", .watchid = &watch1 },
+        { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
+          .filesrc = "two.txt", .watchid = &watch2 },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_CREATE,
+          .filesrc = "one.txt", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "one.txt", .watchid = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .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 = &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_CREATE,
+          .filesrc = "three.txt", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .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 = &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 = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_DELETED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "one.txt", .watchid = &watch1,
+          .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_APPEND,
+          .filesrc = "two.txt", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "two.txt", .watchid = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_MODIFIED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .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 = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "two.txt", .watchid = &watch2,
+          .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
+          .filesrc = "one.txt", .watchid = &watch1 },
+        { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
+          .filesrc = "one.txt", .watchid = &watch3 },
+        { .type = QFILE_MONITOR_TEST_OP_CREATE,
+          .filesrc = "one.txt", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "one.txt", .watchid = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "one.txt", .watchid = &watch3,
+          .eventid = QFILE_MONITOR_EVENT_CREATED },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
+          .filesrc = "one.txt", .watchid = &watch3 },
+        { .type = QFILE_MONITOR_TEST_OP_UNLINK,
+          .filesrc = "one.txt", },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .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 },
+        { .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 = &watch0,
+          .eventid = QFILE_MONITOR_EVENT_DELETED },
+        { .type = QFILE_MONITOR_TEST_OP_EVENT,
+          .filesrc = "two.txt", .watchid = &watch2,
+          .eventid = QFILE_MONITOR_EVENT_DELETED },
+
+
+        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
+          .filesrc = "two.txt", .watchid = &watch2 },
+        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
+          .filesrc = NULL, .watchid = &watch0 },
+    };
     Error *local_err = NULL;
     GError *gerr = NULL;
     QFileMonitor *mon = qemu_file_monitor_new(&local_err);
@@ -210,10 +377,11 @@ test_file_monitor_events(const void *opaque)
     GTimer *timer;
     gchar *dir = NULL;
     int err = -1;
-    gsize i, j;
+    gsize i;
     char *pathsrc = NULL;
     char *pathdst = NULL;
     QFileMonitorTestData data;
+    GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal);
 
     qemu_mutex_init(&data.lock);
     data.records = NULL;
@@ -248,34 +416,15 @@ test_file_monitor_events(const void *opaque)
     }
 
     /*
-     * First register all the directory / file watches
-     * we're interested in seeing events against
-     */
-    for (i = 0; i < plan->nwatches; i++) {
-        int watchid;
-        watchid = qemu_file_monitor_add_watch(mon,
-                                              dir,
-                                              plan->watches[i].file,
-                                              qemu_file_monitor_test_handler,
-                                              &data,
-                                              &local_err);
-        if (watchid < 0) {
-            g_printerr("Unable to add watch %s",
-                       error_get_pretty(local_err));
-            goto cleanup;
-        }
-    }
-
-
-    /*
-     * Now invoke all the file operations (create,
-     * delete, rename, chmod, etc). These operations
-     * will trigger the various file monitor events
+     * Run through the operation sequence validating events
+     * as we go
      */
-    for (i = 0; i < plan->nops; i++) {
-        const QFileMonitorTestOp *op = &(plan->ops[i]);
+    for (i = 0; i < G_N_ELEMENTS(ops); i++) {
+        const QFileMonitorTestOp *op = &(ops[i]);
         int fd;
         struct utimbuf ubuf;
+        char *watchdir;
+        const char *watchfile;
 
         pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
         if (op->filedst) {
@@ -283,7 +432,75 @@ test_file_monitor_events(const void *opaque)
         }
 
         switch (op->type) {
+        case QFILE_MONITOR_TEST_OP_ADD_WATCH:
+            if (debug) {
+                g_printerr("Add watch %s %s\n",
+                           dir, op->filesrc);
+            }
+            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,
+                                            watchdir,
+                                            watchfile,
+                                            qemu_file_monitor_test_handler,
+                                            &data,
+                                            &local_err);
+            g_free(watchdir);
+            if (*op->watchid < 0) {
+                g_printerr("Unable to add watch %s",
+                           error_get_pretty(local_err));
+                goto cleanup;
+            }
+            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 %" 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,
+                                           watchdir,
+                                           *op->watchid);
+            g_free(watchdir);
+            break;
+        case QFILE_MONITOR_TEST_OP_EVENT:
+            if (debug) {
+                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))
+                goto cleanup;
+            break;
         case QFILE_MONITOR_TEST_OP_CREATE:
+            if (debug) {
+                g_printerr("Create %s\n", pathsrc);
+            }
             fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
             if (fd < 0) {
                 g_printerr("Unable to create %s: %s",
@@ -294,6 +511,9 @@ test_file_monitor_events(const void *opaque)
             break;
 
         case QFILE_MONITOR_TEST_OP_APPEND:
+            if (debug) {
+                g_printerr("Append %s\n", pathsrc);
+            }
             fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
             if (fd < 0) {
                 g_printerr("Unable to open %s: %s",
@@ -311,6 +531,9 @@ test_file_monitor_events(const void *opaque)
             break;
 
         case QFILE_MONITOR_TEST_OP_TRUNC:
+            if (debug) {
+                g_printerr("Truncate %s\n", pathsrc);
+            }
             if (truncate(pathsrc, 4) < 0) {
                 g_printerr("Unable to truncate %s: %s",
                            pathsrc, strerror(errno));
@@ -319,6 +542,9 @@ test_file_monitor_events(const void *opaque)
             break;
 
         case QFILE_MONITOR_TEST_OP_RENAME:
+            if (debug) {
+                g_printerr("Rename %s -> %s\n", pathsrc, pathdst);
+            }
             if (rename(pathsrc, pathdst) < 0) {
                 g_printerr("Unable to rename %s to %s: %s",
                            pathsrc, pathdst, strerror(errno));
@@ -327,6 +553,9 @@ test_file_monitor_events(const void *opaque)
             break;
 
         case QFILE_MONITOR_TEST_OP_UNLINK:
+            if (debug) {
+                g_printerr("Unlink %s\n", pathsrc);
+            }
             if (unlink(pathsrc) < 0) {
                 g_printerr("Unable to unlink %s: %s",
                            pathsrc, strerror(errno));
@@ -335,6 +564,9 @@ test_file_monitor_events(const void *opaque)
             break;
 
         case QFILE_MONITOR_TEST_OP_TOUCH:
+            if (debug) {
+                g_printerr("Touch %s\n", pathsrc);
+            }
             ubuf.actime = 1024;
             ubuf.modtime = 1025;
             if (utime(pathsrc, &ubuf) < 0) {
@@ -344,101 +576,39 @@ test_file_monitor_events(const void *opaque)
             }
             break;
 
-        default:
-            g_assert_not_reached();
-        }
-
-        g_free(pathsrc);
-        g_free(pathdst);
-        pathsrc = pathdst = NULL;
-    }
-
-
-    /*
-     * Finally validate that we have received all the events
-     * we expect to see for the combination of watches and
-     * file operations
-     */
-    for (i = 0; i < plan->nops; i++) {
-        const QFileMonitorTestOp *op = &(plan->ops[i]);
-
-        switch (op->type) {
-        case QFILE_MONITOR_TEST_OP_CREATE:
-            for (j = 0; j < plan->nwatches; j++) {
-                if (plan->watches[j].file &&
-                    !g_str_equal(plan->watches[j].file, op->filesrc))
-                    continue;
-
-                if (!qemu_file_monitor_test_expect(
-                        &data, j, QFILE_MONITOR_EVENT_CREATED, op->filesrc))
-                    goto cleanup;
+        case QFILE_MONITOR_TEST_OP_MKDIR:
+            if (debug) {
+                g_printerr("Mkdir %s\n", pathsrc);
             }
-            break;
-
-        case QFILE_MONITOR_TEST_OP_APPEND:
-        case QFILE_MONITOR_TEST_OP_TRUNC:
-            for (j = 0; j < plan->nwatches; j++) {
-                if (plan->watches[j].file &&
-                    !g_str_equal(plan->watches[j].file, op->filesrc))
-                    continue;
-
-                if (!qemu_file_monitor_test_expect(
-                        &data, j, QFILE_MONITOR_EVENT_MODIFIED, op->filesrc))
-                    goto cleanup;
-            }
-            break;
-
-        case QFILE_MONITOR_TEST_OP_RENAME:
-            for (j = 0; j < plan->nwatches; j++) {
-                if (plan->watches[j].file &&
-                    !g_str_equal(plan->watches[j].file, op->filesrc))
-                    continue;
-
-                if (!qemu_file_monitor_test_expect(
-                        &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
-                    goto cleanup;
-            }
-
-            for (j = 0; j < plan->nwatches; j++) {
-                if (plan->watches[j].file &&
-                    !g_str_equal(plan->watches[j].file, op->filedst))
-                    continue;
-
-                if (!qemu_file_monitor_test_expect(
-                        &data, j, QFILE_MONITOR_EVENT_CREATED, op->filedst))
-                    goto cleanup;
+            if (mkdir(pathsrc, 0700) < 0) {
+                g_printerr("Unable to mkdir %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
             }
             break;
 
-        case QFILE_MONITOR_TEST_OP_TOUCH:
-            for (j = 0; j < plan->nwatches; j++) {
-                if (plan->watches[j].file &&
-                    !g_str_equal(plan->watches[j].file, op->filesrc))
-                    continue;
-
-                if (!qemu_file_monitor_test_expect(
-                        &data, j, QFILE_MONITOR_EVENT_ATTRIBUTES, op->filesrc))
-                    goto cleanup;
+        case QFILE_MONITOR_TEST_OP_RMDIR:
+            if (debug) {
+                g_printerr("Rmdir %s\n", pathsrc);
             }
-            break;
-
-        case QFILE_MONITOR_TEST_OP_UNLINK:
-            for (j = 0; j < plan->nwatches; j++) {
-                if (plan->watches[j].file &&
-                    !g_str_equal(plan->watches[j].file, op->filesrc))
-                    continue;
-
-                if (!qemu_file_monitor_test_expect(
-                        &data, j, QFILE_MONITOR_EVENT_DELETED, op->filesrc))
-                    goto cleanup;
+            if (rmdir(pathsrc) < 0) {
+                g_printerr("Unable to rmdir %s: %s",
+                           pathsrc, strerror(errno));
+                goto cleanup;
             }
             break;
 
         default:
             g_assert_not_reached();
         }
+
+        g_free(pathsrc);
+        g_free(pathdst);
+        pathsrc = pathdst = NULL;
     }
 
+    g_assert_cmpint(g_hash_table_size(ids), ==, 0);
+
     err = 0;
 
  cleanup:
@@ -460,171 +630,42 @@ test_file_monitor_events(const void *opaque)
     }
     g_timer_destroy(timer);
 
-    for (i = 0; i < plan->nops; i++) {
-        const QFileMonitorTestOp *op = &(plan->ops[i]);
-        pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
-        unlink(pathsrc);
-        g_free(pathsrc);
-        if (op->filedst) {
-            pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
-            unlink(pathdst);
-            g_free(pathdst);
-        }
-    }
-
     qemu_file_monitor_free(mon);
     g_list_foreach(data.records,
                    (GFunc)qemu_file_monitor_test_record_free, NULL);
     g_list_free(data.records);
     qemu_mutex_destroy(&data.lock);
     if (dir) {
-        rmdir(dir);
+        for (i = 0; i < G_N_ELEMENTS(ops); i++) {
+            const QFileMonitorTestOp *op = &(ops[i]);
+            char *path = g_strdup_printf("%s/%s",
+                                         dir, op->filesrc);
+            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) {
+            g_printerr("Failed to remove %s: %s\n",
+                       dir, strerror(errno));
+            abort();
+        }
     }
+    g_hash_table_unref(ids);
     g_free(dir);
     g_assert(err == 0);
 }
 
 
-/*
- * Set of structs which define which file name patterns
- * we're trying to watch against. NULL, means all files
- * in the directory
- */
-static const QFileMonitorTestWatch watches_any[] = {
-    { NULL },
-};
-
-static const QFileMonitorTestWatch watches_one[] = {
-    { "one.txt" },
-};
-
-static const QFileMonitorTestWatch watches_two[] = {
-    { "two.txt" },
-};
-
-static const QFileMonitorTestWatch watches_many[] = {
-    { NULL },
-    { "one.txt" },
-    { "two.txt" },
-};
-
-
-/*
- * Various sets of file operations we're going to
- * trigger and validate events for
- */
-static const QFileMonitorTestOp ops_create_one[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", }
-};
-
-static const QFileMonitorTestOp ops_delete_one[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_UNLINK,
-      .filesrc = "one.txt", }
-};
-
-static const QFileMonitorTestOp ops_create_many[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "two.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "three.txt", }
-};
-
-static const QFileMonitorTestOp ops_rename_one[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_RENAME,
-      .filesrc = "one.txt", .filedst = "two.txt" }
-};
-
-static const QFileMonitorTestOp ops_rename_many[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "two.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_RENAME,
-      .filesrc = "one.txt", .filedst = "two.txt" }
-};
-
-static const QFileMonitorTestOp ops_append_one[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_APPEND,
-      .filesrc = "one.txt", },
-};
-
-static const QFileMonitorTestOp ops_trunc_one[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_TRUNC,
-      .filesrc = "one.txt", },
-};
-
-static const QFileMonitorTestOp ops_touch_one[] = {
-    { .type = QFILE_MONITOR_TEST_OP_CREATE,
-      .filesrc = "one.txt", },
-    { .type = QFILE_MONITOR_TEST_OP_TOUCH,
-      .filesrc = "one.txt", },
-};
-
-
-/*
- * No we define data sets for the combinatorial
- * expansion of file watches and operation sets
- */
-#define PLAN_DATA(o, w) \
-    static const QFileMonitorTestPlan plan_ ## o ## _ ## w = { \
-        .nops = G_N_ELEMENTS(ops_ ##o), \
-        .ops = ops_ ##o, \
-        .nwatches = G_N_ELEMENTS(watches_ ##w), \
-        .watches = watches_ ## w, \
-    }
-
-PLAN_DATA(create_one, any);
-PLAN_DATA(create_one, one);
-PLAN_DATA(create_one, two);
-PLAN_DATA(create_one, many);
-
-PLAN_DATA(delete_one, any);
-PLAN_DATA(delete_one, one);
-PLAN_DATA(delete_one, two);
-PLAN_DATA(delete_one, many);
-
-PLAN_DATA(create_many, any);
-PLAN_DATA(create_many, one);
-PLAN_DATA(create_many, two);
-PLAN_DATA(create_many, many);
-
-PLAN_DATA(rename_one, any);
-PLAN_DATA(rename_one, one);
-PLAN_DATA(rename_one, two);
-PLAN_DATA(rename_one, many);
-
-PLAN_DATA(rename_many, any);
-PLAN_DATA(rename_many, one);
-PLAN_DATA(rename_many, two);
-PLAN_DATA(rename_many, many);
-
-PLAN_DATA(append_one, any);
-PLAN_DATA(append_one, one);
-PLAN_DATA(append_one, two);
-PLAN_DATA(append_one, many);
-
-PLAN_DATA(trunc_one, any);
-PLAN_DATA(trunc_one, one);
-PLAN_DATA(trunc_one, two);
-PLAN_DATA(trunc_one, many);
-
-PLAN_DATA(touch_one, any);
-PLAN_DATA(touch_one, one);
-PLAN_DATA(touch_one, two);
-PLAN_DATA(touch_one, many);
-
-
 int main(int argc, char **argv)
 {
     g_test_init(&argc, &argv, NULL);
@@ -633,53 +674,8 @@ int main(int argc, char **argv)
 
     qemu_mutex_init(&evlock);
 
-    /*
-     * Register test cases for the combinatorial
-     * expansion of file watches and operation sets
-     */
-    #define PLAN_REGISTER(o, w)                                         \
-        g_test_add_data_func("/util/filemonitor/" # o "/" # w,          \
-                             &plan_ ## o ## _ ## w, test_file_monitor_events)
-
-    PLAN_REGISTER(create_one, any);
-    PLAN_REGISTER(create_one, one);
-    PLAN_REGISTER(create_one, two);
-    PLAN_REGISTER(create_one, many);
-
-    PLAN_REGISTER(delete_one, any);
-    PLAN_REGISTER(delete_one, one);
-    PLAN_REGISTER(delete_one, two);
-    PLAN_REGISTER(delete_one, many);
-
-    PLAN_REGISTER(create_many, any);
-    PLAN_REGISTER(create_many, one);
-    PLAN_REGISTER(create_many, two);
-    PLAN_REGISTER(create_many, many);
-
-    PLAN_REGISTER(rename_one, any);
-    PLAN_REGISTER(rename_one, one);
-    PLAN_REGISTER(rename_one, two);
-    PLAN_REGISTER(rename_one, many);
-
-    PLAN_REGISTER(rename_many, any);
-    PLAN_REGISTER(rename_many, one);
-    PLAN_REGISTER(rename_many, two);
-    PLAN_REGISTER(rename_many, many);
-
-    PLAN_REGISTER(append_one, any);
-    PLAN_REGISTER(append_one, one);
-    PLAN_REGISTER(append_one, two);
-    PLAN_REGISTER(append_one, many);
-
-    PLAN_REGISTER(trunc_one, any);
-    PLAN_REGISTER(trunc_one, one);
-    PLAN_REGISTER(trunc_one, two);
-    PLAN_REGISTER(trunc_one, many);
-
-    PLAN_REGISTER(touch_one, any);
-    PLAN_REGISTER(touch_one, one);
-    PLAN_REGISTER(touch_one, two);
-    PLAN_REGISTER(touch_one, many);
+    debug = getenv("FILEMONITOR_DEBUG") != NULL;
+    g_test_add_func("/util/filemonitor", test_file_monitor_events);
 
     return g_test_run();
 }