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