X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Famflock-test.c;h=cffde0aa6e5c64a99115e6668dae21e537e2a1f4;hb=fd48f3e498442f0cbff5f3606c7c403d0566150e;hp=2c5b151fd74135f7d983bb503f7d78b032de1c1f;hpb=96f35b20267e8b1a1c846d476f27fcd330e0b018;p=debian%2Famanda diff --git a/common-src/amflock-test.c b/common-src/amflock-test.c index 2c5b151..cffde0a 100644 --- a/common-src/amflock-test.c +++ b/common-src/amflock-test.c @@ -26,18 +26,20 @@ #include "amanda.h" #include "testutils.h" +#include "glib-util.h" /* from amflock.c */ extern amflock_impl_t *amflock_impls[]; +#define TEST_FILENAME "./amflocktest.file" + /* Test all amflock implementations available for basic * functionality */ static int -test_impls(void) +test_old_impls(void) { amflock_impl_t **imp = amflock_impls; - char *filename = "./amflocktest.file"; char *resource = "rez"; int fd; int lock_ro; @@ -50,12 +52,12 @@ test_impls(void) tu_dbg("Testing amflock-%s\n", (*imp)->impl_name); for (lock_ro = 0; lock_ro < 2; lock_ro++) { /* false (0) or true (1) */ - if (unlink(filename) == -1 && errno != ENOENT) { + if (unlink(TEST_FILENAME) == -1 && errno != ENOENT) { perror("unlink"); return 0; } - if ((fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) { + if ((fd = open(TEST_FILENAME, O_RDWR | O_CREAT | O_EXCL, 0600)) == -1) { perror("open"); return 0; } @@ -78,7 +80,7 @@ test_impls(void) } close(fd); /* ignore error */ - unlink(filename); /* ignore error */ + unlink(TEST_FILENAME); /* ignore error */ } fprintf(stderr, " PASS amflock-%s\n", (*imp)->impl_name); @@ -89,15 +91,280 @@ test_impls(void) return 1; } -/* TODO: a more serious test of exclusion using multiple processes */ +/* + * Test lock and write_and_unlock + */ +static gboolean +inc_counter(file_lock *lock) +{ + char old_val = 'a'; + char new_val; + + if (lock->len) { + old_val = lock->data[0]; + } + + g_assert(old_val < 'z'); + + new_val = old_val + 1; + if (file_lock_write(lock, &new_val, 1) == -1) { + g_fprintf(stderr, "file_lock_write: %s\n", + strerror(errno)); + return FALSE; + } + + return TRUE; +} + +#define pipeget(fd, cp) \ + (read((fd), (cp), 1) == 1) + +#define pipeput(fd, s) \ + g_assert(write((fd), s, 1) == 1); + +static void +locking_slave(int in_fd, int out_fd) +{ + char cmd; + int rv; + file_lock *lock = file_lock_new(TEST_FILENAME); + gboolean locked = 0; + + while (1) { + if (!pipeget(in_fd, &cmd)) + cmd = 'q'; + + switch (cmd) { + case 'q': /* q = quit */ + tu_dbg("slave: quitting\n"); + file_lock_free(lock); + lock = NULL; + return; + + case 'l': /* l = try locking; reply with 'y' or 'n' */ + g_assert(!locked); + rv = file_lock_lock(lock); + if (rv == -1) { + g_fprintf(stderr, "file_lock_lock: %s\n", + strerror(errno)); + return; + } + tu_dbg("slave: lock attempt => %s\n", (rv == 1)? "n" : "y"); + pipeput(out_fd, (rv == 1)? "n" : "y"); + if (rv != 1) + locked = 1; + break; + + case 'i': /* i = increment counter, reply with new value */ + g_assert(locked); + if (!inc_counter(lock)) + return; + tu_dbg("slave: inc'd to %c\n", lock->data[0]); + pipeput(out_fd, lock->data); + break; + + case 'u': /* u = try unlocking; reply with 'k' */ + g_assert(locked); + rv = file_lock_unlock(lock); + if (rv != 0) { + g_fprintf(stderr, "file_lock_unlock: %s\n", + strerror(errno)); + return; + } + tu_dbg("slave: unlocked\n"); + pipeput(out_fd, "k"); + locked = 0; + break; + + default: + return; + } + } +} + +static int +locking_master(int in_fd, int out_fd) +{ + file_lock *lock = file_lock_new(TEST_FILENAME); + int rv; + char slaveres; + + /* start by locking here and incrementing the value */ + rv = file_lock_lock(lock); + if (rv == -1) { + g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno)); + return 0; + } + g_assert(rv != 1); /* not already locked */ + tu_dbg("master: locked\n"); + + if (!inc_counter(lock)) + return 0; + + g_assert(lock->data[0] == 'b'); + tu_dbg("master: inc'd to b\n"); + + /* unlock and re-lock */ + rv = file_lock_unlock(lock); + if (rv != 0) { + g_fprintf(stderr, "file_lock_unlock: %s\n", strerror(errno)); + return 0; + } + tu_dbg("master: unlocked\n"); + + rv = file_lock_lock(lock); + if (rv == -1) { + g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno)); + return 0; + } + g_assert(rv != 1); /* not already locked */ + tu_dbg("master: locked\n"); + + /* inc it again */ + g_assert(lock->data[0] == 'b'); + inc_counter(lock); + g_assert(lock->data[0] == 'c'); + tu_dbg("master: inc'd to c\n"); + + /* the slave should fail to get a lock now */ + pipeput(out_fd, "l"); + g_assert(pipeget(in_fd, &slaveres)); + g_assert(slaveres == 'n'); + + /* and, finally unlock */ + rv = file_lock_unlock(lock); + if (rv != 0) { + g_fprintf(stderr, "file_lock_unlock: %s\n", strerror(errno)); + return 0; + } + tu_dbg("master: unlocked\n"); + + /* the slave should succeed now */ + pipeput(out_fd, "l"); + g_assert(pipeget(in_fd, &slaveres)); + g_assert(slaveres == 'y'); + + pipeput(out_fd, "i"); + g_assert(pipeget(in_fd, &slaveres)); + g_assert(slaveres == 'd'); + + /* master shouldn't be able to lock now */ + rv = file_lock_lock(lock); + if (rv == -1) { + g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno)); + return 0; + } + g_assert(rv == 1); /* already locked */ + tu_dbg("master: lock attempt failed (as expected)\n"); + + pipeput(out_fd, "i"); + g_assert(pipeget(in_fd, &slaveres)); + g_assert(slaveres == 'e'); + + /* get the slave to unlock */ + pipeput(out_fd, "u"); + g_assert(pipeget(in_fd, &slaveres)); + g_assert(slaveres == 'k'); + + /* we should get a lock now */ + rv = file_lock_lock(lock); + if (rv == -1) { + g_fprintf(stderr, "file_lock_lock: %s\n", strerror(errno)); + return 0; + } + g_assert(rv != 1); /* not already locked */ + tu_dbg("master: lock attempt succeeded\n"); + + g_assert(lock->data[0] == 'e'); + + /* leave it unlocked, just to see what happens */ + + return 1; +} + +static gpointer +test_intra_proc_locking_thd(gpointer *fdptr) +{ + int *fds = (int *)fdptr; + locking_slave(fds[0], fds[1]); + return NULL; +} + +static int +test_intra_proc_locking(void) +{ + GThread *thd; + int outpipe[2], inpipe[2]; + int thd_fds[2]; + int rv; + + unlink(TEST_FILENAME); + + g_assert(pipe(outpipe) == 0); + g_assert(pipe(inpipe) == 0); + + thd_fds[0] = outpipe[0]; + thd_fds[1] = inpipe[1]; + thd = g_thread_create((GThreadFunc)test_intra_proc_locking_thd, (gpointer)thd_fds, TRUE, NULL); + + rv = locking_master(inpipe[0], outpipe[1]); + + /* close the write end of the outgoing pipe, which should trigger an EOF on + * the slave if it's still running */ + close(outpipe[1]); + g_thread_join(thd); + unlink(TEST_FILENAME); + + /* caller will kill the remaining files */ + + return rv; +} + +static int +test_inter_proc_locking(void) +{ + int outpipe[2], inpipe[2]; + int pid; + int rv; + + unlink(TEST_FILENAME); + + g_assert(pipe(outpipe) == 0); + g_assert(pipe(inpipe) == 0); + + if ((pid = fork()) == 0) { + close(outpipe[1]); + close(inpipe[0]); + locking_slave(outpipe[0], inpipe[1]); + exit(0); + } + + close(outpipe[0]); + close(inpipe[1]); + + rv = locking_master(inpipe[0], outpipe[1]); + + /* close the write end of the outgoing pipe, which should trigger an EOF on + * the slave if it's still running */ + close(outpipe[1]); + waitpid(pid, NULL, 0); + unlink(TEST_FILENAME); + + /* caller will kill the remaining files */ + + return rv; +} int main(int argc, char **argv) { static TestUtilsTest tests[] = { - TU_TEST(test_impls, 10), + TU_TEST(test_old_impls, 90), + TU_TEST(test_inter_proc_locking, 60), + TU_TEST(test_intra_proc_locking, 60), TU_END() }; + glib_init(); return testutils_run_tests(argc, argv, tests); }