From b26c3f9cbd784a5f7db68049fd9c49a0a2077734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 14 Mar 2019 14:45:08 +0000 Subject: tests: refactor file monitor test to make it more understandable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current file monitor unit tests are too clever for their own good making it hard to understand the desired output. Instead of trying to infer the expected events, explicitly list the events we expect in the operation sequence. Instead of dynamically building a matrix of tests, just have one giant operation sequence that validates all scenarios in a single test. Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- tests/test-util-filemonitor.c | 534 ++++++++++++++++-------------------------- 1 file changed, 208 insertions(+), 326 deletions(-) (limited to 'tests/test-util-filemonitor.c') diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c index 5d95cea5ee..ea3715a8f4 100644 --- a/tests/test-util-filemonitor.c +++ b/tests/test-util-filemonitor.c @@ -26,6 +26,9 @@ #include 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, @@ -38,20 +41,10 @@ typedef struct { int type; const char *filesrc; const char *filedst; + int 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; QFileMonitorEvent event; @@ -67,6 +60,7 @@ typedef struct { static QemuMutex evlock; static bool evstopping; static bool evrunning; +static bool debug; /* * Main function for a background thread that is @@ -200,9 +194,125 @@ 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; + QFileMonitorTestOp ops[] = { + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = NULL, .watchid = 0 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = 1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "two.txt", .watchid = 2 }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 1, + .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, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .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, + .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, + .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, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 1, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .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, + .eventid = QFILE_MONITOR_EVENT_MODIFIED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .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, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = 1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = 3 }, + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 3, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = 3 }, + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = 2, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "two.txt", .watchid = 2 }, + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = NULL, .watchid = 0 }, + }; Error *local_err = NULL; GError *gerr = NULL; QFileMonitor *mon = qemu_file_monitor_new(&local_err); @@ -210,7 +320,7 @@ 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; @@ -248,33 +358,13 @@ test_file_monitor_events(const void *opaque) } /* - * First register all the directory / file watches - * we're interested in seeing events against + * Run through the operation sequence validating events + * as we go */ - 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 - */ - 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; + int watchid; struct utimbuf ubuf; pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); @@ -283,7 +373,50 @@ 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 %d\n", + dir, op->filesrc, op->watchid); + } + watchid = + qemu_file_monitor_add_watch(mon, + dir, + op->filesrc, + 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; + } + if (watchid != op->watchid) { + g_printerr("Unexpected watch ID %d, wanted %d\n", + watchid, op->watchid); + goto cleanup; + } + break; + case QFILE_MONITOR_TEST_OP_DEL_WATCH: + if (debug) { + g_printerr("Del watch %s %d\n", dir, op->watchid); + } + qemu_file_monitor_remove_watch(mon, + dir, + op->watchid); + 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); + } + 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 +427,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 +447,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 +458,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 +469,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 +480,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) { @@ -353,92 +501,6 @@ test_file_monitor_events(const void *opaque) 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; - } - 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; - } - 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; - } - 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; - } - break; - - default: - g_assert_not_reached(); - } - } - err = 0; cleanup: @@ -460,171 +522,36 @@ 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); + 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_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 +560,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(); } -- cgit v1.2.3