1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
#include "test.h"
#include "../src/alloc.h"
#include "../src/lock.h"
static const char *lockfile="utest_lockfile";
static struct lock *setup(void)
{
struct lock *lock;
fail_unless((lock=lock_alloc())!=NULL);
fail_unless(!lock_init(lock, lockfile));
ck_assert_str_eq(lock->path, lockfile);
fail_unless(lock->status==GET_LOCK_NOT_GOT);
return lock;
}
static void tear_down(struct lock **lock, struct lock **locklist)
{
lock_free(lock);
locks_release_and_free(locklist);
alloc_check();
}
static void assert_can_get_lock(struct lock *lock)
{
fail_unless(!lock_test(lockfile));
lock_get_quick(lock);
fail_unless(lock->status==GET_LOCK_GOT);
fail_unless(!lock_release(lock));
fail_unless(lock->status==GET_LOCK_NOT_GOT);
}
static void do_fork(int child_exit_early)
{
switch(fork())
{
case -1: fail_unless(0==1);
break;
case 0: // Child.
{
struct lock *lock;
lock=lock_alloc_and_init(lockfile);
lock_get_quick(lock);
if(!child_exit_early)
{
sleep(2);
lock_release(lock);
lock_free(&lock);
}
exit(0);
}
default: break;
}
// Parent.
}
static void run_with_fork(int child_exit_early)
{
int stat;
struct lock *lock=setup();
do_fork(child_exit_early);
if(!child_exit_early)
{
sleep(1);
fail_unless(lock_test(lockfile)==-1);
lock_get_quick(lock);
fail_unless(lock->status==GET_LOCK_NOT_GOT);
}
wait(&stat);
// The child has exited, should now be able to get it.
assert_can_get_lock(lock);
tear_down(&lock, NULL);
}
START_TEST(test_lock_simple_success)
{
struct lock *lock;
lock=setup();
assert_can_get_lock(lock);
tear_down(&lock, NULL);
}
END_TEST
START_TEST(test_lock_simple_failure)
{
// Child will get the lock, and wait.
// The parent will wait a shorter time, to give the child time to
// get the lock. The parent will then attempt to get the lock, and
// it should not succeed.
run_with_fork(0 /* child will not exit early */);
}
END_TEST
START_TEST(test_lock_left_behind)
{
// Child will get the lock, then exit, leaving an old lockfile behind.
// The parent will wait and then attempt to get the lock, and it
// should succeed.
run_with_fork(1 /* child will exit early */);
}
END_TEST
static void init_and_add_to_list(struct lock **locklist, const char *path)
{
struct lock *lock;
fail_unless((lock=lock_alloc_and_init(path))!=NULL);
lock_add_to_list(locklist, lock);
}
START_TEST(test_lock_list)
{
struct lock *lock=NULL;
struct lock *locklist=NULL;
init_and_add_to_list(&locklist, "path1");
init_and_add_to_list(&locklist, "path2");
init_and_add_to_list(&locklist, "path3");
lock=locklist;
ck_assert_str_eq(lock->path, "path3"); lock=lock->next;
ck_assert_str_eq(lock->path, "path2"); lock=lock->next;
ck_assert_str_eq(lock->path, "path1"); fail_unless(lock->next==NULL);
tear_down(NULL, &locklist);
}
END_TEST
Suite *suite_lock(void)
{
Suite *s;
TCase *tc_core;
s=suite_create("lock");
tc_core=tcase_create("Core");
tcase_add_test(tc_core, test_lock_simple_success);
tcase_add_test(tc_core, test_lock_simple_failure);
tcase_add_test(tc_core, test_lock_left_behind);
tcase_add_test(tc_core, test_lock_list);
suite_add_tcase(s, tc_core);
return s;
}
|