v / vlib / os
Raw file | 455 loc (395 sloc) | 10.58 KB | Latest commit hash 3625a74ec
1import os
2
3const tfolder = os.join_path(os.vtmp_dir(), 'v', 'tests', 'os_file_test')
4
5const tfile = os.join_path_single(tfolder, 'test_file')
6
7fn testsuite_begin() {
8 os.rmdir_all(tfolder) or {}
9 assert !os.is_dir(tfolder)
10 os.mkdir_all(tfolder)!
11 os.chdir(tfolder)!
12 assert os.is_dir(tfolder)
13}
14
15fn testsuite_end() {
16 os.rmdir_all(tfolder) or {}
17}
18
19struct Point {
20 x f64
21 y f64
22 z f64
23}
24
25struct Extended_Point {
26 a f64
27 b f64
28 c f64
29 d f64
30 e f64
31 f f64
32 g f64
33 h f64
34 i f64
35}
36
37enum Color {
38 red
39 green
40 blue
41}
42
43[flag]
44enum Permissions {
45 read
46 write
47 execute
48}
49
50const (
51 unit_point = Point{1.0, 1.0, 1.0}
52 another_point = Point{0.25, 2.25, 6.25}
53 extended_point = Extended_Point{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}
54 another_byte = u8(123)
55 another_color = Color.red
56 another_permission = Permissions.read | .write
57)
58
59// test_read_bytes_into_newline_text tests reading text from a file with newlines.
60// This test simulates reading a larger text file step by step into a buffer and
61// returning on each newline, even before the buffer is full, and reaching EOF before
62// the buffer is completely filled.
63fn test_read_bytes_into_newline_text() {
64 mut f := os.open_file(tfile, 'w')!
65 f.write_string('Hello World!\nGood\r morning.')!
66 f.close()
67
68 f = os.open_file(tfile, 'r')!
69 mut buf := []u8{len: 8}
70
71 n0 := f.read_bytes_into_newline(mut buf)!
72 assert n0 == 8
73
74 n1 := f.read_bytes_into_newline(mut buf)!
75 assert n1 == 5
76
77 n2 := f.read_bytes_into_newline(mut buf)!
78 assert n2 == 8
79
80 n3 := f.read_bytes_into_newline(mut buf)!
81 assert n3 == 6
82
83 f.close()
84}
85
86// test_read_bytes_into_newline_binary tests reading a binary file with NUL bytes.
87// This test simulates the scenario when a byte stream is read and a newline byte
88// appears in that stream and an EOF occurs before the buffer is full.
89fn test_read_bytes_into_newline_binary() {
90 os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w'
91 mut bw := []u8{len: 15}
92 bw[9] = 0xff
93 bw[12] = 10 // newline
94
95 n0_bytes := bw[0..10]
96 n1_bytes := bw[10..13]
97 n2_bytes := bw[13..]
98
99 mut f := os.open_file(tfile, 'w')!
100 f.write(bw)!
101 f.close()
102
103 f = os.open_file(tfile, 'r')!
104 mut buf := []u8{len: 10}
105
106 n0 := f.read_bytes_into_newline(mut buf)!
107 assert n0 == 10
108 assert buf[..n0] == n0_bytes
109
110 n1 := f.read_bytes_into_newline(mut buf)!
111 assert n1 == 3
112 assert buf[..n1] == n1_bytes
113
114 n2 := f.read_bytes_into_newline(mut buf)!
115 assert n2 == 2
116 assert buf[..n2] == n2_bytes
117 f.close()
118}
119
120// test_read_eof_last_read_partial_buffer_fill tests that when reading a file
121// the end-of-file is detected and results in a none error being returned. This
122// test simulates file reading where the end-of-file is reached inside an fread
123// containing data.
124fn test_read_eof_last_read_partial_buffer_fill() {
125 mut f := os.open_file(tfile, 'w')!
126 bw := []u8{len: 199, init: 5}
127 f.write(bw)!
128 f.close()
129
130 f = os.open_file(tfile, 'r')!
131 mut br := []u8{len: 100}
132 // Read first 100 bytes of 199 byte file, should fill buffer with no error.
133 n0 := f.read(mut br)!
134 assert n0 == 100
135 // Read remaining 99 bytes of 199 byte file, should fill buffer with no
136 // error, even though end-of-file was reached.
137 n1 := f.read(mut br)!
138 assert n1 == 99
139 // Read again, end-of-file was previously reached so should return none
140 // error.
141 if _ := f.read(mut br) {
142 // This is not intended behavior because the read function should
143 // not return a number of bytes read when end-of-file is reached.
144 assert false
145 } else {
146 // Expected an error when received end-of-file.
147 assert err is os.Eof
148 }
149 f.close()
150}
151
152// test_read_eof_last_read_full_buffer_fill tests that when reading a file the
153// end-of-file is detected and results in a none error being returned. This test
154// simulates file reading where the end-of-file is reached at the beinning of an
155// fread that returns no data.
156fn test_read_eof_last_read_full_buffer_fill() {
157 mut f := os.open_file(tfile, 'w')!
158 bw := []u8{len: 200, init: 5}
159 f.write(bw)!
160 f.close()
161
162 f = os.open_file(tfile, 'r')!
163 mut br := []u8{len: 100}
164 // Read first 100 bytes of 200 byte file, should fill buffer with no error.
165 n0 := f.read(mut br)!
166 assert n0 == 100
167 // Read remaining 100 bytes of 200 byte file, should fill buffer with no
168 // error. The end-of-file isn't reached yet, but there is no more data.
169 n1 := f.read(mut br)!
170 assert n1 == 100
171 // Read again, end-of-file was previously reached so should return none
172 // error.
173 if _ := f.read(mut br) {
174 // This is not intended behavior because the read function should
175 // not return a number of bytes read when end-of-file is reached.
176 assert false
177 } else {
178 // Expect an error at EOF.
179 assert err is os.Eof
180 }
181 f.close()
182}
183
184fn test_write_struct() {
185 os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w'
186 size_of_point := int(sizeof(Point))
187 mut f := os.open_file(tfile, 'w')!
188 f.write_struct(another_point)!
189 f.close()
190 x := os.read_file(tfile)!
191 pcopy := unsafe { &u8(memdup(&another_point, size_of_point)) }
192 y := unsafe { pcopy.vstring_with_len(size_of_point) }
193 assert x == y
194 $if debug {
195 eprintln(x.bytes())
196 eprintln(y.bytes())
197 }
198}
199
200fn test_write_struct_at() {
201 mut f := os.open_file(tfile, 'w')!
202 f.write_struct(extended_point)!
203 f.write_struct_at(another_point, 3)!
204 f.close()
205 f = os.open_file(tfile, 'r')!
206 mut p := Point{}
207 f.read_struct_at(mut p, 3)!
208 f.close()
209
210 assert p == another_point
211}
212
213fn test_read_struct() {
214 mut f := os.open_file(tfile, 'w')!
215 f.write_struct(another_point)!
216 f.close()
217
218 f = os.open_file(tfile, 'r')!
219 mut p := Point{}
220 f.read_struct(mut p)!
221 f.close()
222
223 assert p == another_point
224}
225
226fn test_read_struct_at() {
227 mut f := os.open_file(tfile, 'w')!
228 f.write([u8(1), 2, 3])!
229 f.write_struct(another_point)!
230 f.close()
231 f = os.open_file(tfile, 'r')!
232 mut p := Point{}
233 f.read_struct_at(mut p, 3)!
234 f.close()
235
236 assert p == another_point
237}
238
239fn test_write_raw() {
240 os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w'
241 size_of_point := int(sizeof(Point))
242 mut f := os.open_file(tfile, 'w')!
243 f.write_raw(another_point)!
244 f.close()
245 x := os.read_file(tfile)!
246 pcopy := unsafe { &u8(memdup(&another_point, size_of_point)) }
247 y := unsafe { pcopy.vstring_with_len(size_of_point) }
248 assert x == y
249 $if debug {
250 eprintln(x.bytes())
251 eprintln(y.bytes())
252 }
253}
254
255fn test_write_raw_at() {
256 mut f := os.open_file(tfile, 'w')!
257 f.write_raw(extended_point)!
258 f.write_raw_at(another_point, 3)!
259 f.close()
260 f = os.open_file(tfile, 'r')!
261 mut p := Point{}
262 f.read_struct_at(mut p, 3)!
263 f.close()
264
265 assert p == another_point
266}
267
268fn test_write_raw_at_negative_pos() {
269 mut f := os.open_file(tfile, 'w')!
270 if _ := f.write_raw_at(another_point, u64(-1)) {
271 assert false
272 }
273 f.write_raw_at(another_point, u64(-1)) or { assert err.msg() == 'Invalid argument' }
274 f.close()
275}
276
277fn test_read_raw() {
278 mut f := os.open_file(tfile, 'w')!
279 f.write_raw(another_point)!
280 f.write_raw(another_byte)!
281 f.write_raw(another_color)!
282 f.write_raw(another_permission)!
283 f.close()
284 f = os.open_file(tfile, 'r')!
285 p := f.read_raw[Point]()!
286 b := f.read_raw[u8]()!
287 c := f.read_raw[Color]()!
288 x := f.read_raw[Permissions]()!
289 f.close()
290
291 assert p == another_point
292 assert b == another_byte
293 assert c == another_color
294 assert x == another_permission
295}
296
297fn test_read_raw_at() {
298 mut f := os.open_file(tfile, 'w')!
299 f.write([u8(1), 2, 3])!
300 f.write_raw(another_point)!
301 f.write_raw(another_byte)!
302 f.write_raw(another_color)!
303 f.write_raw(another_permission)!
304 f.close()
305 f = os.open_file(tfile, 'r')!
306 mut at := u64(3)
307 p := f.read_raw_at[Point](at)!
308 at += sizeof(Point)
309 b := f.read_raw_at[u8](at)!
310 at += sizeof(u8)
311 c := f.read_raw_at[Color](at)!
312 at += sizeof(Color)
313 x := f.read_raw_at[Permissions](at)!
314 at += sizeof(Permissions)
315 f.close()
316
317 assert p == another_point
318 assert b == another_byte
319 assert c == another_color
320 assert x == another_permission
321}
322
323fn test_read_raw_at_negative_pos() {
324 mut f := os.open_file(tfile, 'r')!
325 if _ := f.read_raw_at[Point](u64(-1)) {
326 assert false
327 }
328 f.read_raw_at[Point](u64(-1)) or { assert err.msg() == 'Invalid argument' }
329 f.close()
330}
331
332fn test_seek() {
333 mut f := os.open_file(tfile, 'w')!
334 f.write_raw(another_point)!
335 f.write_raw(another_byte)!
336 f.write_raw(another_color)!
337 f.write_raw(another_permission)!
338 f.close()
339
340 // println('> ${sizeof(Point)} ${sizeof(byte)} ${sizeof(Color)} ${sizeof(Permissions)}')
341 f = os.open_file(tfile, 'r')!
342 //
343 f.seek(i64(sizeof(Point)), .start)!
344 assert f.tell()! == sizeof(Point)
345 b := f.read_raw[u8]()!
346 assert b == another_byte
347
348 f.seek(i64(sizeof(Color)), .current)!
349 x := f.read_raw[Permissions]()!
350 assert x == another_permission
351 //
352 f.close()
353}
354
355fn test_tell() {
356 for size in 10 .. 30 {
357 s := 'x'.repeat(size)
358 os.write_file(tfile, s)!
359 fs := os.file_size(tfile)
360 assert int(fs) == size
361 //
362 mut f := os.open_file(tfile, 'r')!
363 f.seek(-5, .end)!
364 pos := f.tell()!
365 f.close()
366 // dump(pos)
367 assert pos == size - 5
368 }
369}
370
371fn test_reopen() {
372 tfile1 := os.join_path_single(tfolder, 'tfile1')
373 tfile2 := os.join_path_single(tfolder, 'tfile2')
374 os.write_file(tfile1, 'Hello World!\nGood\r morning.\nBye 1.')!
375 os.write_file(tfile2, 'Another file\nAnother line.\nBye 2.')!
376 assert os.file_size(tfile1) > 0
377 assert os.file_size(tfile2) > 0
378
379 mut line_buffer := []u8{len: 1024}
380
381 mut f2 := os.open(tfile2)!
382 x := f2.read_bytes_into_newline(mut line_buffer)!
383 assert !f2.eof()
384 assert x > 0
385 assert line_buffer#[..x].bytestr() == 'Another file\n'
386
387 // Note: after this call, f2 should be using the file `tfile1`:
388 f2.reopen(tfile1, 'r')!
389 assert !f2.eof()
390
391 z := f2.read(mut line_buffer) or { panic(err) }
392 assert f2.eof()
393 assert z > 0
394 content := line_buffer#[..z].bytestr()
395 // dump(content)
396 assert content.starts_with('Hello World')
397 assert content.ends_with('Bye 1.')
398}
399
400fn test_eof() {
401 os.write_file(tfile, 'Hello World!\n')!
402
403 mut f := os.open(tfile)!
404 f.read_bytes(10)
405 assert !f.eof()
406 f.read_bytes(100)
407 assert f.eof()
408 f.close()
409}
410
411fn test_open_file_wb_ab() {
412 os.rm(tfile) or {}
413 mut wfile := os.open_file('text.txt', 'wb', 0o666)!
414 wfile.write_string('hello')!
415 wfile.close()
416 assert os.read_file('text.txt')! == 'hello'
417 //
418 mut afile := os.open_file('text.txt', 'ab', 0o666)!
419 afile.write_string('hello')!
420 afile.close()
421 assert os.read_file('text.txt')! == 'hellohello'
422}
423
424fn test_open_append() {
425 os.rm(tfile) or {}
426 mut f1 := os.open_append(tfile)!
427 f1.write_string('abc\n')!
428 f1.close()
429 assert os.read_lines(tfile)! == ['abc']
430 //
431 mut f2 := os.open_append(tfile)!
432 f2.write_string('abc\n')!
433 f2.close()
434 assert os.read_lines(tfile)! == ['abc', 'abc']
435 //
436 mut f3 := os.open_append(tfile)!
437 f3.write_string('def\n')!
438 f3.close()
439 assert os.read_lines(tfile)! == ['abc', 'abc', 'def']
440}
441
442fn test_open_file_on_chinese_windows() {
443 $if windows {
444 os.rm('中文.txt') or {}
445 mut f1 := os.open_file('中文.txt', 'w+', 0x666) or { panic(err) }
446 f1.write_string('test')!
447 f1.close()
448
449 assert os.read_file('中文.txt')! == 'test'
450 assert os.file_size('中文.txt') == 4
451
452 os.truncate('中文.txt', 2)!
453 assert os.file_size('中文.txt') == 2
454 }
455}