v2 / vlib / os / filelock / filelock_test.v
174 lines · 161 sloc · 4.05 KB · 3914749f5694a7f1f938d07e303a8be61e227e94
Raw
1import os
2import os.filelock
3import time
4
5const test_dir = os.join_path(os.vtmp_dir(), 'filelock_tests')
6
7const helper_mode_env = 'V_FILELOCK_HELPER_MODE'
8const helper_path_env = 'V_FILELOCK_HELPER_PATH'
9const helper_lock_mode_env = 'V_FILELOCK_LOCK_MODE'
10const helper_start_env = 'V_FILELOCK_START'
11const helper_len_env = 'V_FILELOCK_LEN'
12
13fn testsuite_begin() {
14 if os.getenv(helper_mode_env) != '' {
15 run_helper_process()
16 }
17 os.mkdir_all(test_dir) or {}
18}
19
20fn testsuite_end() {
21 os.rmdir_all(test_dir) or {}
22}
23
24fn run_helper_process() {
25 path := os.getenv(helper_path_env)
26 lock_mode := if os.getenv(helper_lock_mode_env) == 'shared' {
27 filelock.LockMode.shared
28 } else {
29 filelock.LockMode.exclusive
30 }
31 start := os.getenv(helper_start_env).u64()
32 len := os.getenv(helper_len_env).u64()
33 mut file_lock := filelock.new_file(path, filelock.LockOptions{
34 mode: lock_mode
35 start: start
36 len: len
37 })
38 match os.getenv(helper_mode_env) {
39 'hold' {
40 file_lock.acquire() or {
41 eprintln(err.msg())
42 exit(2)
43 }
44 println('locked')
45 os.flush()
46 time.sleep(300 * time.millisecond)
47 file_lock.release()
48 exit(0)
49 }
50 'try' {
51 exit(if file_lock.try_acquire() {
52 file_lock.release()
53 0
54 } else {
55 1
56 })
57 }
58 else {
59 exit(3)
60 }
61 }
62}
63
64fn helper_path(name string) string {
65 return os.join_path(test_dir, name)
66}
67
68fn new_helper_process(path string, lock_mode filelock.LockMode, start u64, len u64, mode string) &os.Process {
69 mut p := os.new_process(os.executable())
70 mut env := os.environ()
71 env[helper_mode_env] = mode
72 env[helper_path_env] = path
73 env[helper_lock_mode_env] = lock_mode.str()
74 env[helper_start_env] = start.str()
75 env[helper_len_env] = len.str()
76 p.set_environment(env)
77 p.set_redirect_stdio()
78 return p
79}
80
81fn wait_for_lock(mut p os.Process) {
82 mut output := ''
83 for _ in 0 .. 50 {
84 if p.is_pending(.stdout) {
85 output += p.stdout_read()
86 if output.contains('locked') {
87 return
88 }
89 }
90 if !p.is_alive() {
91 break
92 }
93 time.sleep(10 * time.millisecond)
94 }
95 p.wait()
96 stderr := p.stderr_slurp().trim_space()
97 assert false, 'helper failed to acquire lock; exit=${p.code} stdout="${output.trim_space()}" stderr="${stderr}"'
98}
99
100fn try_lock_from_helper(path string, lock_mode filelock.LockMode, start u64, len u64) int {
101 mut p := new_helper_process(path, lock_mode, start, len, 'try')
102 p.run()
103 p.wait()
104 stderr := p.stderr_slurp().trim_space()
105 code := p.code
106 p.close()
107 assert stderr == ''
108 return code
109}
110
111fn test_flock() {
112 lockfile := helper_path('test.lock')
113 os.rm(lockfile) or {}
114 mut l := filelock.new(lockfile)
115 assert !os.exists(lockfile)
116 l.acquire() or { panic(err) }
117 assert os.exists(lockfile)
118 l.release()
119 assert !os.exists(lockfile)
120}
121
122fn test_flock_try() {
123 lockfile := helper_path('test-try.lock')
124 os.rm(lockfile) or {}
125 mut l := filelock.new(lockfile)
126 assert l.try_acquire()
127 l.release()
128 assert !os.exists(lockfile)
129 assert l.try_acquire()
130 assert os.exists(lockfile)
131 l.release()
132 assert l.try_acquire()
133 l.release()
134 assert !os.exists(lockfile)
135}
136
137fn test_existing_file_lock_range() {
138 target := helper_path('existing-range.txt')
139 os.write_file(target, '0123456789abcdef')!
140 mut holder := new_helper_process(target, .exclusive, 0, 5, 'hold')
141 holder.run()
142 defer {
143 if holder.status == .running || holder.is_alive() {
144 holder.wait()
145 }
146 holder.close()
147 }
148 wait_for_lock(mut holder)
149 assert try_lock_from_helper(target, .exclusive, 0, 5) == 1
150 assert try_lock_from_helper(target, .exclusive, 6, 5) == 0
151 holder.wait()
152 assert holder.code == 0
153 assert os.exists(target)
154 assert os.read_file(target)! == '0123456789abcdef'
155}
156
157fn test_existing_file_lock_shared_mode() {
158 target := helper_path('existing-shared.txt')
159 os.write_file(target, '0123456789abcdef')!
160 mut holder := new_helper_process(target, .shared, 0, 5, 'hold')
161 holder.run()
162 defer {
163 if holder.status == .running || holder.is_alive() {
164 holder.wait()
165 }
166 holder.close()
167 }
168 wait_for_lock(mut holder)
169 assert try_lock_from_helper(target, .shared, 0, 5) == 0
170 assert try_lock_from_helper(target, .exclusive, 0, 5) == 1
171 holder.wait()
172 assert holder.code == 0
173 assert os.exists(target)
174}
175