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