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