v2 / vlib / os / os_test.c.v
1264 lines · 1145 sloc · 35.66 KB · 6d79f6b97355681a47958d8a99df67aef772b67d
Raw
1import os
2
3// tfolder will contain all the temporary files/subfolders made by
4// the different tests. It would be removed in testsuite_end(), so
5// individual os tests do not need to clean up after themselves.
6const tfolder = os.join_path(os.vtmp_dir(), 'os_tests')
7const utf16le_stdout_source_code = '
8module main
9
10import os
11
12fn main() {
13 payload := [u8(`O`), 0, `K`, 0, u8(10), 0]
14 mut out := os.stdout()
15 out.write(payload) or { panic(err) }
16}
17'
18
19// os.args has to be *already initialized* with the program's argc/argv at this point
20// thus it can be used for other consts too:
21const args_at_start = os.args.clone()
22
23fn testsuite_begin() {
24 eprintln('testsuite_begin, tfolder = ${tfolder}')
25 os.rmdir_all(tfolder) or {}
26 assert !os.is_dir(tfolder)
27 os.mkdir_all(tfolder) or { panic(err) }
28 os.chdir(tfolder) or {}
29 assert os.is_dir(tfolder)
30 // println('args_at_start: ${args_at_start}')
31 assert args_at_start.len > 0
32 assert args_at_start == os.args
33}
34
35fn testsuite_end() {
36 os.chdir(os.wd_at_startup) or {}
37 os.rmdir_all(tfolder) or {}
38 // assert !os.is_dir(tfolder)
39 // eprintln('testsuite_end , tfolder = ${tfolder} removed.')
40}
41
42fn test_open_file() {
43 filename := './test1.txt'
44 hello := 'hello world!'
45 os.open_file(filename, 'r+', 0o666) or {
46 assert err.msg() == 'No such file or directory'
47 os.File{}
48 }
49 mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) }
50 file.write_string(hello) or { panic(err) }
51 file.close()
52 assert u64(hello.len) == os.file_size(filename)
53 read_hello := os.read_file(filename) or { panic('error reading file ' + filename) }
54 assert hello == read_hello
55 os.rm(filename) or { panic(err) }
56}
57
58fn test_read_file_from_virtual_filesystem() {
59 $if linux {
60 mounts := os.read_file('/proc/mounts')!
61
62 // it is not empty, contains some mounting such as root filesystem: /dev/x / ext4 rw 0 0
63 assert mounts.len > 20
64 assert mounts.contains('/')
65 assert mounts.contains(' ')
66 }
67}
68
69fn test_read_binary_from_virtual_filesystem() {
70 $if linux {
71 mounts_raw := os.read_bytes('/proc/mounts')!
72 mounts := mounts_raw.bytestr()
73
74 // it is not empty, contains some mounting such as root filesystem: /dev/x / ext4 rw 0 0
75 assert mounts.len > 20
76 assert mounts.contains('/')
77 assert mounts.contains(' ')
78 }
79}
80
81fn test_open_file_binary() {
82 filename := './test1.dat'
83 hello := 'hello \n world!'
84 os.open_file(filename, 'r+', 0o666) or {
85 assert err.msg() == 'No such file or directory'
86 os.File{}
87 }
88 mut file := os.open_file(filename, 'wb+', 0o666) or { panic(err) }
89 bytes := hello.bytes()
90 unsafe { file.write_ptr(bytes.data, bytes.len) }
91 file.close()
92 assert u64(hello.len) == os.file_size(filename)
93 read_hello := os.read_bytes(filename) or { panic('error reading file ' + filename) }
94 assert bytes == read_hello
95 os.rm(filename) or { panic(err) }
96}
97
98// fn test_file_get_line() {
99// filename := './fgetline.txt'
100// os.write_file(filename, 'line 1\nline 2')
101// mut f := os.open_file(filename, 'r', 0) or {
102// assert false
103// return
104// }
105// line1 := f.get_line() or {
106// ''
107// }
108// line2 := f.get_line() or {
109// ''
110// }
111// f.close()
112// //
113// eprintln('line1: ${line1} ${line1.bytes()}')
114// eprintln('line2: ${line2} ${line2.bytes()}')
115// assert line1 == 'line 1\n'
116// assert line2 == 'line 2'
117// }
118
119fn create_file(fpath string) ! {
120 mut f := os.create(fpath)!
121 f.close()
122}
123
124fn create_and_write_to_file(fpath string, content string) ! {
125 mut f := os.create(fpath)!
126 f.write_string(content)!
127 f.close()
128}
129
130fn test_create_file() {
131 filename := './test1.txt'
132 hello := 'hello world!'
133 create_and_write_to_file(filename, hello)!
134 assert u64(hello.len) == os.file_size(filename)
135 os.rm(filename) or { panic(err) }
136}
137
138fn test_is_file() {
139 // Setup
140 work_dir := os.join_path_single(tfolder, 'is_file_test')
141 os.mkdir_all(work_dir) or { panic(err) }
142 tfile := os.join_path_single(work_dir, 'tmp_file')
143 // Test things that shouldn't be a file
144 assert os.is_file(work_dir) == false
145 assert os.is_file('non-existent_file.tmp') == false
146 // Test file
147 tfile_content := 'temporary file'
148 os.write_file(tfile, tfile_content) or { panic(err) }
149 assert os.is_file(tfile)
150 // Test dir symlinks
151 $if windows {
152 assert true
153 } $else {
154 dsymlink := os.join_path_single(work_dir, 'dir_symlink')
155 os.symlink(work_dir, dsymlink) or { panic(err) }
156 assert os.is_file(dsymlink) == false
157 }
158 // Test file symlinks
159 $if windows {
160 assert true
161 } $else {
162 fsymlink := os.join_path_single(work_dir, 'file_symlink')
163 os.symlink(tfile, fsymlink) or { panic(err) }
164 assert os.is_file(fsymlink)
165 }
166}
167
168fn test_write_and_read_string_to_file() {
169 filename := './test1.txt'
170 hello := 'hello world!'
171 os.write_file(filename, hello) or { panic(err) }
172 assert u64(hello.len) == os.file_size(filename)
173 read_hello := os.read_file(filename) or { panic('error reading file ' + filename) }
174 assert hello == read_hello
175 os.rm(filename) or { panic(err) }
176}
177
178// test_write_and_read_bytes checks for regressions made in the functions
179// read_bytes, read_bytes_at and write_bytes.
180
181fn test_write_and_read_bytes() {
182 file_name := './byte_reader_writer.tst'
183 payload := [u8(`I`), `D`, `D`, `Q`, `D`]
184 mut file_write := os.create(os.real_path(file_name)) or {
185 eprintln('failed to create file ${file_name}')
186 return
187 }
188 // We use the standard write_bytes function to write the payload and
189 // compare the length of the array with the file size (have to match).
190 unsafe { file_write.write_ptr(payload.data, 5) }
191 file_write.close()
192 assert u64(payload.len) == os.file_size(file_name)
193 mut file_read := os.open(os.real_path(file_name)) or {
194 eprintln('failed to open file ${file_name}')
195 return
196 }
197 // We only need to test read_bytes because this function calls
198 // read_bytes_at with second parameter zeroed (size, 0).
199 rbytes := file_read.read_bytes(5)
200 // eprintln('rbytes: ${rbytes}')
201 // eprintln('payload: ${payload}')
202 assert rbytes == payload
203 // check that trying to read data from EOF doesn't error and returns 0
204 mut a := []u8{len: 5}
205 nread := file_read.read_bytes_into(5, mut a) or {
206 n := if err is os.Eof {
207 int(0)
208 } else {
209 eprintln(err)
210 int(-1)
211 }
212 n
213 }
214 assert nread == 0
215 file_read.close()
216 // We finally delete the test file.
217 os.rm(file_name) or { panic(err) }
218}
219
220fn test_ls() {
221 if x := os.ls('') {
222 assert false
223 } else {
224 assert true
225 }
226 if x := os.ls('.') {
227 assert x.len > 0
228 } else {
229 assert false
230 }
231}
232
233fn create_tree() ! {
234 os.mkdir_all('myfolder/f1/f2/f3')!
235 os.mkdir_all('myfolder/a1/a2/a3', mode: 0o700)!
236 f3 := os.real_path('myfolder/f1/f2/f3')
237 assert os.is_dir(f3)
238 create_file('myfolder/f1/f2/f3/.hfile1')!
239 create_file('myfolder/f1/f2/f3/a.txt')!
240 create_file('myfolder/f1/f2/f3/b.txt')!
241 create_file('myfolder/f1/f2/f3/c.txt')!
242 create_file('myfolder/f1/f2/f3/d.md')!
243 create_file('myfolder/f1/0.txt')!
244 create_file('myfolder/another.md')!
245 create_file('myfolder/a1/a2/a3/x.txt')!
246 create_file('myfolder/a1/a2/a3/y.txt')!
247 create_file('myfolder/a1/a2/a3/z.txt')!
248 create_file('myfolder/a1/1.txt')!
249 create_file('myfolder/a1/.hfile2')!
250 create_file('myfolder/xyz.ini')!
251}
252
253fn remove_tree() {
254 os.rmdir_all('myfolder') or {}
255}
256
257fn normalise_paths(paths []string) []string {
258 mut res := paths.map(it.replace(os.path_separator, '/'))
259 res.sort()
260 return res
261}
262
263fn test_walk_ext() {
264 create_tree()!
265 defer {
266 remove_tree()
267 }
268 all := os.walk_ext('.', '')
269 assert all.len > 10
270 top := normalise_paths(os.walk_ext('myfolder', '.txt'))
271 assert top == [
272 'myfolder/a1/1.txt',
273 'myfolder/a1/a2/a3/x.txt',
274 'myfolder/a1/a2/a3/y.txt',
275 'myfolder/a1/a2/a3/z.txt',
276 'myfolder/f1/0.txt',
277 'myfolder/f1/f2/f3/a.txt',
278 'myfolder/f1/f2/f3/b.txt',
279 'myfolder/f1/f2/f3/c.txt',
280 ]
281 subfolder_txts := normalise_paths(os.walk_ext('myfolder/a1/a2', '.txt'))
282 assert subfolder_txts == [
283 'myfolder/a1/a2/a3/x.txt',
284 'myfolder/a1/a2/a3/y.txt',
285 'myfolder/a1/a2/a3/z.txt',
286 ]
287 mut mds := normalise_paths(os.walk_ext('myfolder', '.md'))
288 assert mds == ['myfolder/another.md', 'myfolder/f1/f2/f3/d.md']
289
290 all_with_hidden := os.walk_ext('.', '', hidden: true)
291 assert all_with_hidden.len > all.len
292 hidden := normalise_paths(all_with_hidden.filter(it !in all))
293 assert hidden == ['./myfolder/a1/.hfile2', './myfolder/f1/f2/f3/.hfile1']
294}
295
296fn test_walk_with_context() {
297 create_tree()!
298 defer {
299 remove_tree()
300 }
301 mut res := []string{}
302 os.walk_with_context('myfolder', &res, fn (ctx voidptr, fpath string) {
303 mut res := unsafe { &[]string(ctx) }
304 res << fpath
305 })
306 res = normalise_paths(res)
307 assert 'myfolder/f1/f2/f3/b.txt' in res
308 assert 'myfolder/another.md' in res
309}
310
311fn test_create_and_delete_folder() {
312 folder := './test1'
313 os.mkdir(folder) or { panic(err) }
314 assert os.is_dir(folder)
315 folder_contents := os.ls(folder) or { panic(err) }
316 assert folder_contents.len == 0
317 os.rmdir(folder) or { panic(err) }
318 folder_exists := os.is_dir(folder)
319 assert folder_exists == false
320}
321
322fn walk_callback(file string) {
323 if file == '.' || file == '..' {
324 return
325 }
326 assert file == 'test_walk' + os.path_separator + 'test1'
327}
328
329fn test_walk() {
330 folder := 'test_walk'
331 os.mkdir(folder) or { panic(err) }
332 file1 := folder + os.path_separator + 'test1'
333 os.write_file(file1, 'test-1') or { panic(err) }
334 os.walk(folder, walk_callback)
335 os.rm(file1) or { panic(err) }
336 os.rmdir(folder) or { panic(err) }
337}
338
339fn test_cp() {
340 old_file_name := 'cp_example.txt'
341 new_file_name := 'cp_new_example.txt'
342 os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐')!
343 os.cp(old_file_name, new_file_name)!
344 old_file := os.read_file(old_file_name)!
345 new_file := os.read_file(new_file_name)!
346 assert old_file == new_file
347 os.rm(old_file_name)!
348 os.rm(new_file_name)!
349}
350
351fn test_cp_to_folder() {
352 file_name := 'cp_to_folder_example.txt'
353 folder := 'test_cp_to_folder'
354 os.mkdir(folder)!
355 os.write_file(file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐')!
356 os.cp(file_name, folder)!
357 new_file_path := os.join_path_single(folder, file_name)
358 old_file := os.read_file(file_name)!
359 new_file := os.read_file(new_file_path)!
360 assert old_file == new_file
361 os.rm(file_name)!
362 os.rm(new_file_path)!
363 os.rmdir(folder)!
364}
365
366fn test_cp_fail_if_exists() {
367 file_name := 'cp_fail_if_exists_example.txt'
368 folder := 'test_cp_fail_if_exists'
369 os.mkdir(folder)!
370 os.write_file(file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐')!
371 os.cp(file_name, folder)!
372 new_file_path := os.join_path_single(folder, file_name)
373 if _ := os.cp(file_name, folder, fail_if_exists: true) {
374 assert false
375 } else {
376 assert err.str().starts_with('cp: failed to '), 'cp err: ${err}'
377 }
378 os.rm(file_name)!
379 os.rm(new_file_path)!
380 os.rmdir(folder)!
381}
382
383fn test_mv() {
384 work_dir := os.join_path_single(tfolder, 'mv_test')
385 os.mkdir_all(work_dir) or { panic(err) }
386 // Setup test files
387 tfile1 := os.join_path_single(work_dir, 'file')
388 tfile2 := os.join_path_single(work_dir, 'file.test')
389 tfile3 := os.join_path_single(work_dir, 'file.3')
390 tfile_content := 'temporary file'
391 os.write_file(tfile1, tfile_content) or { panic(err) }
392 os.write_file(tfile2, tfile_content) or { panic(err) }
393 // Setup test dirs
394 tdir1 := os.join_path_single(work_dir, 'dir')
395 tdir2 := os.join_path_single(work_dir, 'dir2')
396 tdir3 := os.join_path_single(work_dir, 'dir3')
397 os.mkdir(tdir1) or { panic(err) }
398 os.mkdir(tdir2) or { panic(err) }
399 // Move file with no extension to dir
400 os.mv(tfile1, tdir1) or { panic(err) }
401 mut expected := os.join_path_single(tdir1, 'file')
402 assert os.exists(expected)
403 assert !os.is_dir(expected)
404 // Move dir with contents to other dir
405 os.mv(tdir1, tdir2) or { panic(err) }
406 expected = os.join_path_single(tdir2, 'dir')
407 assert os.exists(expected)
408 assert os.is_dir(expected)
409 expected = os.join_path(tdir2, 'dir', 'file')
410 assert os.exists(expected)
411 assert !os.is_dir(expected)
412 // Move dir with contents to other dir (by renaming)
413 os.mv(os.join_path_single(tdir2, 'dir'), tdir3) or { panic(err) }
414 expected = tdir3
415 assert os.exists(expected)
416 assert os.is_dir(expected)
417 assert os.is_dir_empty(tdir2)
418 // Move file with extension to dir
419 os.mv(tfile2, tdir2) or { panic(err) }
420 expected = os.join_path_single(tdir2, 'file.test')
421 assert os.exists(expected)
422 assert !os.is_dir(expected)
423 // Move file to dir (by renaming)
424 os.mv(os.join_path_single(tdir2, 'file.test'), tfile3) or { panic(err) }
425 expected = tfile3
426 assert os.exists(expected)
427 assert !os.is_dir(expected)
428}
429
430fn test_is_dir_empty() {
431 // Test that is_dir_empty returns true on
432 // non-existent directories ***as stated in it's doc string***
433 assert os.is_dir_empty('dir that does not exist at all')
434}
435
436fn test_cp_all() {
437 // fileX -> dir/fileX
438 // Note: clean up of the files happens inside the cleanup_leftovers function
439 os.write_file('ex1.txt', 'wow!') or { panic(err) }
440 os.mkdir('ex') or { panic(err) }
441 os.cp_all('ex1.txt', 'ex', false) or { panic(err) }
442 old := os.read_file('ex1.txt') or { panic(err) }
443 new := os.read_file('ex/ex1.txt') or { panic(err) }
444 assert old == new
445 os.mkdir('ex/ex2') or { panic(err) }
446 os.write_file('ex2.txt', 'great!') or { panic(err) }
447 os.cp_all('ex2.txt', 'ex/ex2', false) or { panic(err) }
448 old2 := os.read_file('ex2.txt') or { panic(err) }
449 new2 := os.read_file('ex/ex2/ex2.txt') or { panic(err) }
450 assert old2 == new2
451 // recurring on dir -> local dir
452 os.cp_all('ex', './', true) or { panic(err) }
453 // regression test for executive runs with overwrite := true
454 os.cp_all('ex', './', true) or { panic(err) }
455 os.cp_all('ex', 'nonexisting', true) or { panic(err) }
456 assert os.exists(os.join_path_single('nonexisting', 'ex1.txt'))
457}
458
459fn test_realpath_of_empty_string_works() {
460 assert os.real_path('') == ''
461}
462
463fn test_realpath_non_existing() {
464 non_existing_path := 'sdyfuisd_non_existing_file'
465 rpath := os.real_path(non_existing_path)
466 $if windows {
467 // on windows, the workdir is prepended, so the result is absolute:
468 assert rpath.len > non_existing_path.len
469 }
470 $if !windows {
471 // on unix, the workdir is NOT prepended for now, so the result remains the same.
472 // TODO: the windows behaviour seems saner, think about normalising the unix case to do the same.
473 assert os.real_path(non_existing_path) == non_existing_path
474 }
475}
476
477fn test_realpath_existing() {
478 existing_file_name := 'existing_file.txt'
479 existing_file := os.join_path_single(tfolder, existing_file_name)
480 os.rm(existing_file) or {}
481 os.write_file(existing_file, 'abc') or {}
482 assert os.exists(existing_file)
483 rpath := os.real_path(existing_file)
484 assert os.is_abs_path(rpath)
485 assert rpath.ends_with(existing_file_name)
486 $if windows {
487 assert !rpath.starts_with('UNC\\')
488 assert !rpath.starts_with('\\\\?\\')
489 }
490 os.rm(existing_file) or {}
491}
492
493fn test_realpath_removes_dots() {
494 examples_folder := os.join_path(@VEXEROOT, 'vlib', 'v', '..', '..', 'cmd', '.', '..',
495 'examples')
496 real_path_of_examples_folder := os.real_path(examples_folder)
497 assert real_path_of_examples_folder.len < examples_folder.len
498 assert !real_path_of_examples_folder.contains('..')
499}
500
501fn test_realpath_absolutizes_existing_relative_paths() {
502 old_wd := os.getwd()
503 defer {
504 os.chdir(old_wd) or { panic(err) }
505 }
506 os.chdir(@VEXEROOT) or { panic(err) }
507 examples_folder := os.join_path('vlib', 'v', '..', '..', 'cmd', '.', '..', 'examples')
508 real_path_of_examples_folder := os.real_path(examples_folder)
509 assert os.is_abs_path(real_path_of_examples_folder)
510}
511
512// TODO: think much more about whether this is desirable:
513
514fn test_realpath_does_not_absolutize_non_existing_relative_paths() {
515 relative_path := os.join_path('one', 'nonexisting_folder', '..', 'something')
516 $if !windows {
517 assert os.real_path(relative_path).contains('..')
518 assert os.real_path(relative_path) == relative_path
519 }
520}
521
522fn handle_privilege_error(err IError) ! {
523 if err.msg().contains('required privilege is not held by the client') {
524 eprintln('skipping ${@METHOD} on windows, since the user is not administrator, err:\n ${err}')
525 return err
526 }
527 panic(err)
528}
529
530fn test_realpath_absolutepath_symlink() ! {
531 file_name := 'tolink_file.txt'
532 symlink_name := 'symlink.txt'
533 create_file(file_name)!
534 os.symlink(file_name, symlink_name) or { handle_privilege_error(err) or { return } }
535 rpath := os.real_path(symlink_name)
536 println(rpath)
537 assert os.is_abs_path(rpath)
538 assert rpath.ends_with(file_name)
539 $if windows {
540 assert !rpath.starts_with('UNC\\')
541 assert !rpath.starts_with('\\\\?\\')
542 }
543 os.rm(symlink_name) or {}
544 os.rm(file_name) or {}
545}
546
547fn test_make_symlink_check_is_link_and_remove_symlink() {
548 folder := 'tfolder'
549 symlink := 'tsymlink'
550 // windows creates a directory symlink, so delete it with rmdir()
551 $if windows {
552 os.rmdir(symlink) or {}
553 } $else {
554 os.rm(symlink) or {}
555 }
556 os.rmdir(folder) or {}
557 os.mkdir(folder) or { panic(err) }
558 folder_contents := os.ls(folder) or { panic(err) }
559 assert folder_contents.len == 0
560 os.symlink(folder, symlink) or { handle_privilege_error(err) or { return } }
561 assert os.is_link(symlink)
562 $if windows {
563 os.rmdir(symlink) or { panic(err) }
564 } $else {
565 os.rm(symlink) or { panic(err) }
566 }
567 os.rmdir(folder) or { panic(err) }
568 folder_exists := os.is_dir(folder)
569 assert folder_exists == false
570 symlink_exists := os.is_link(symlink)
571 assert symlink_exists == false
572}
573
574fn test_make_symlink_check_is_link_and_remove_symlink_with_file() {
575 file := 'tfile'
576 symlink := 'tsymlink'
577 os.rm(symlink) or {}
578 os.rm(file) or {}
579 create_file(file)!
580 os.symlink(file, symlink) or { handle_privilege_error(err) or { return } }
581 assert os.is_link(symlink)
582 os.rm(symlink) or { panic(err) }
583 os.rm(file) or { panic(err) }
584 symlink_exists := os.is_link(symlink)
585 assert symlink_exists == false
586}
587
588fn test_make_hardlink_check_is_link_and_remove_hardlink_with_file() {
589 file := 'tfile'
590 symlink := 'tsymlink'
591 os.rm(symlink) or {}
592 os.rm(file) or {}
593 create_file(file)!
594 os.link(file, symlink) or { panic(err) }
595 assert os.exists(symlink)
596 os.rm(symlink) or { panic(err) }
597 os.rm(file) or { panic(err) }
598 symlink_exists := os.is_link(symlink)
599 assert symlink_exists == false
600}
601
602// fn test_fork() {
603// pid := os.fork()
604// if pid == 0 {
605// println('Child')
606// }
607// else {
608// println('Parent')
609// }
610// }
611// fn test_wait() {
612// pid := os.fork()
613// if pid == 0 {
614// println('Child')
615// exit(0)
616// }
617// else {
618// cpid := os.wait()
619// println('Parent')
620// println(cpid)
621// }
622// }
623
624fn test_symlink() {
625 os.mkdir('symlink') or { panic(err) }
626 os.symlink('symlink', 'symlink2') or { handle_privilege_error(err) or { return } }
627 assert os.exists('symlink2')
628 // cleanup
629 os.rmdir('symlink') or { panic(err) }
630 $if windows {
631 os.rmdir('symlink2') or { panic(err) }
632 } $else {
633 os.rm('symlink2') or { panic(err) }
634 }
635}
636
637fn test_readlink() {
638 $if windows {
639 eprintln('skipping ${@METHOD} on windows, api not supported')
640 return
641 }
642 os.symlink('some_target_string', 'some_symlink')!
643 defer { os.rm('some_symlink') or { panic(err) } }
644 assert os.readlink('some_symlink')! == 'some_target_string'
645}
646
647fn test_exists_symlink_dangling() {
648 $if msvc {
649 eprintln('skipping ${@METHOD} on windows + msvc; TODO: investigate why os.lstat/1 behaves differently than for gcc/clang')
650 return
651 }
652 os.symlink('nonexistent', 'dangling_symlink') or { handle_privilege_error(err) or { return } }
653 // sanity check that the symlink truly does exist. the lack of error alone is the check.
654 // (on linux, `.get_filetype() == os.FileType.symbolic_link` is true, but on windows, a dangling symlink is reported as a regular file.)
655 os.lstat('dangling_symlink')!
656 // the exists function says false in this scenario... on linux and linux-like systems.
657 // it says true on windows!
658 $if windows {
659 assert os.exists('dangling_symlink') == true
660 } $else {
661 assert os.exists('dangling_symlink') == false
662 }
663}
664
665fn test_is_executable_writable_readable() {
666 file_name := 'rwxfile.exe'
667 create_file(file_name)!
668 $if !windows {
669 os.chmod(file_name, 0o600) or {} // mark as readable && writable, but NOT executable
670 assert os.is_writable(file_name)
671 assert os.is_readable(file_name)
672 assert !os.is_executable(file_name)
673 os.chmod(file_name, 0o700) or {} // mark as executable too
674 assert os.is_executable(file_name)
675 } $else {
676 assert os.is_writable(file_name)
677 assert os.is_readable(file_name)
678 assert os.is_executable(file_name)
679 for ext in ['exe', 'com', 'bat', 'cmd'] {
680 mut executable_file_name := 'executable.${ext}'
681 create_file(executable_file_name)!
682 assert os.is_executable(executable_file_name)
683 os.rm(executable_file_name) or { panic(err) }
684 }
685 }
686 // We finally delete the test file.
687 os.rm(file_name) or { panic(err) }
688}
689
690fn test_file_ext() {
691 assert os.file_ext('') == ''
692 assert os.file_ext('file.v') == '.v'
693 assert os.file_ext('file.js.v') == '.v'
694 assert os.file_ext('file.ext1.ext2.ext3') == '.ext3'
695 assert os.file_ext('.ignore_me.v') == '.v'
696 assert os.file_ext('file') == ''
697 assert os.file_ext('.git') == ''
698 assert os.file_ext('file.') == ''
699 assert os.file_ext('.') == ''
700 assert os.file_ext('..') == ''
701 assert os.file_ext('file...') == ''
702 assert os.file_ext('.file.') == ''
703 assert os.file_ext('..file..') == ''
704 assert os.file_ext('./.git') == ''
705 assert os.file_ext('./.git/') == ''
706 assert os.file_ext('\\.git') == ''
707 assert os.file_ext('\\.git\\') == ''
708}
709
710fn test_rmdir_all() {
711 mut dirs := ['some/dir', 'some/.hidden/directory']
712 $if windows {
713 for mut d in dirs {
714 d = d.replace('/', '\\')
715 }
716 }
717 for d in dirs {
718 os.mkdir_all(d) or { panic(err) }
719 assert os.is_dir(d)
720 }
721 os.rmdir_all('some') or { assert false }
722 assert !os.exists('some')
723}
724
725fn test_rmdir_not_exist() ! {
726 dir := 'non_existing_dir'
727 assert !os.exists(dir)
728 os.rmdir(dir) or {
729 // 0x00000002 is both ENOENT in POSIX and ERROR_FILE_NOT_FOUND in Win32 API
730 assert err.code() == 0x00000002
731 }
732 assert !os.exists(dir)
733}
734
735fn test_dir() {
736 assert os.dir('') == '.'
737 assert os.dir('\\') == '\\'
738 assert os.dir('C:\\a\\b\\c') == 'C:\\a\\b'
739 assert os.dir('C:\\a\\b\\') == 'C:\\a\\b'
740 assert os.dir('C:/a/b/c') == 'C:/a/b'
741 assert os.dir('C:/a/b/') == 'C:/a/b'
742 assert os.dir('/') == '/'
743 assert os.dir('/abc') == '/'
744 assert os.dir('/var/tmp/foo') == '/var/tmp'
745 assert os.dir('/var/tmp/') == '/var/tmp'
746 assert os.dir('os') == '.'
747}
748
749fn test_base() {
750 assert os.base('') == '.'
751 assert os.base('v\\vlib\\os') == 'os'
752 assert os.base('v\\vlib\\os\\') == 'os'
753 assert os.base('v/vlib/os') == 'os'
754 assert os.base('v/vlib/os/') == 'os'
755 assert os.base('v/vlib/os') == 'os'
756 assert os.base('v/vlib/os/') == 'os'
757 assert os.base('v\\vlib\\os') == 'os'
758 assert os.base('v\\vlib\\os\\') == 'os'
759 assert os.base('filename') == 'filename'
760}
761
762fn test_file_name() {
763 assert os.file_name('') == ''
764 assert os.file_name('v\\vlib\\os\\os.v') == 'os.v'
765 assert os.file_name('v\\vlib\\os\\') == ''
766 assert os.file_name('v\\vlib\\os') == 'os'
767 assert os.file_name('v/vlib/os/os.v') == 'os.v'
768 assert os.file_name('v/vlib/os/') == ''
769 assert os.file_name('v/vlib/os') == 'os'
770 assert os.file_name('filename') == 'filename'
771}
772
773fn test_split_path() {
774 mut dir := ''
775 mut filename := ''
776 mut ext := ''
777
778 dir, filename, ext = os.split_path('')
779 assert [dir, filename, ext] == ['.', '', '']
780
781 dir, filename, ext = os.split_path('a')
782 assert [dir, filename, ext] == ['.', 'a', '']
783
784 dir, filename, ext = os.split_path('.')
785 assert [dir, filename, ext] == ['.', '', '']
786
787 dir, filename, ext = os.split_path('..')
788 assert [dir, filename, ext] == ['..', '', '']
789
790 dir, filename, ext = os.split_path('\\')
791 assert [dir, filename, ext] == ['\\', '', '']
792
793 dir, filename, ext = os.split_path('\\x.c.v')
794 assert [dir, filename, ext] == ['\\', 'x.c', '.v']
795
796 dir, filename, ext = os.split_path('.\\x.c.v')
797 assert [dir, filename, ext] == ['.', 'x.c', '.v']
798
799 dir, filename, ext = os.split_path('x.c.v')
800 assert [dir, filename, ext] == ['.', 'x.c', '.v']
801
802 dir, filename, ext = os.split_path('..\\x.c.v')
803 assert [dir, filename, ext] == ['..', 'x.c', '.v']
804
805 dir, filename, ext = os.split_path('\\lib\\x.c.v')
806 assert [dir, filename, ext] == ['\\lib', 'x.c', '.v']
807
808 dir, filename, ext = os.split_path('\\lib\\x.c.v\\')
809 assert [dir, filename, ext] == ['\\lib\\x.c.v', '', '']
810
811 dir, filename, ext = os.split_path('\\lib\\x.c.')
812 assert [dir, filename, ext] == ['\\lib', 'x.c.', '']
813
814 dir, filename, ext = os.split_path('C:\\lib\\x.c.')
815 assert [dir, filename, ext] == ['C:\\lib', 'x.c.', '']
816
817 dir, filename, ext = os.split_path('/')
818 assert [dir, filename, ext] == ['/', '', '']
819
820 dir, filename, ext = os.split_path('/x.c.v')
821 assert [dir, filename, ext] == ['/', 'x.c', '.v']
822
823 dir, filename, ext = os.split_path('./x.c.v')
824 assert [dir, filename, ext] == ['.', 'x.c', '.v']
825
826 dir, filename, ext = os.split_path('../x.c.v')
827 assert [dir, filename, ext] == ['..', 'x.c', '.v']
828
829 dir, filename, ext = os.split_path('/lib/x.c.v')
830 assert [dir, filename, ext] == ['/lib', 'x.c', '.v']
831
832 dir, filename, ext = os.split_path('/lib/x.c.v/')
833 assert [dir, filename, ext] == ['/lib/x.c.v', '', '']
834
835 dir, filename, ext = os.split_path('/lib/../x.c.v/')
836 assert [dir, filename, ext] == ['/lib/../x.c.v', '', '']
837
838 dir, filename, ext = os.split_path('/lib/x.c.')
839 assert [dir, filename, ext] == ['/lib', 'x.c.', '']
840}
841
842fn test_uname() {
843 u := os.uname()
844 assert u.sysname.len > 0
845 assert u.nodename.len > 0
846 assert u.release.len > 0
847 assert u.version.len > 0
848 assert u.machine.len > 0
849}
850
851// tests for write_file_array and read_file_array[T]:
852const maxn = 3
853
854struct IntPoint {
855 x int
856 y int
857}
858
859fn test_write_file_array_bytes() {
860 fpath := './abytes.bin'
861 mut arr := []u8{len: maxn}
862 for i in 0 .. maxn {
863 arr[i] = 65 + u8(i)
864 }
865 os.write_file_array(fpath, arr) or { panic(err) }
866 rarr := os.read_bytes(fpath) or { panic(err) }
867 assert arr == rarr
868 // eprintln(arr.str())
869 // eprintln(rarr.str())
870}
871
872fn test_write_bytes() {
873 fpath := './wbytes.bin'
874 for arr in [[u8(65), 66, 67, 68, 69, 70], [u8(3), 2, 1], []u8{}] {
875 os.write_bytes(fpath, arr)!
876 rarr := os.read_bytes(fpath)!
877 assert arr == rarr
878 }
879}
880
881fn test_write_file_array_structs() {
882 fpath := './astructs.bin'
883 mut arr := []IntPoint{len: maxn}
884 for i in 0 .. maxn {
885 arr[i] = IntPoint{65 + i, 65 + i + 10}
886 }
887 os.write_file_array(fpath, arr) or { panic(err) }
888 rarr := os.read_file_array[IntPoint](fpath)
889 assert rarr == arr
890 assert rarr.len == maxn
891 // eprintln( rarr.str().replace('\n', ' ').replace('},', '},\n'))
892}
893
894fn test_stdout_capture() {
895 /*
896 mut cmd := os.Command{
897 path:'cat'
898 redirect_stdout: true
899}
900cmd.start()
901for !cmd.eof {
902 line := cmd.read_line()
903 println('line="${line}"')
904}
905cmd.close()
906 */
907}
908
909fn test_posix_set_bit() {
910 $if windows {
911 assert true
912 } $else {
913 fpath := 'permtest'
914 create_file(fpath)!
915 os.chmod(fpath, 0o0777) or { panic(err) }
916 c_fpath := &char(fpath.str)
917 mut s := C.stat{}
918 unsafe {
919 C.stat(c_fpath, &s)
920 }
921 // Take the permissions part of the mode
922 mut mode := u32(s.st_mode) & 0o0777
923 assert mode == 0o0777
924 // `chmod u-r`
925 os.posix_set_permission_bit(fpath, os.s_irusr, false)
926 unsafe {
927 C.stat(c_fpath, &s)
928 }
929 mode = u32(s.st_mode) & 0o0777
930 assert mode == 0o0377
931 // `chmod u+r`
932 os.posix_set_permission_bit(fpath, os.s_irusr, true)
933 unsafe {
934 C.stat(c_fpath, &s)
935 }
936 mode = u32(s.st_mode) & 0o0777
937 assert mode == 0o0777
938 // Note: setting the sticky bit is platform dependent
939 // `chmod -s -g -t`
940 os.posix_set_permission_bit(fpath, os.s_isuid, false)
941 os.posix_set_permission_bit(fpath, os.s_isgid, false)
942 os.posix_set_permission_bit(fpath, os.s_isvtx, false)
943 unsafe {
944 C.stat(c_fpath, &s)
945 }
946 mode = u32(s.st_mode) & 0o0777
947 assert mode == 0o0777
948 // `chmod g-w o-w`
949 os.posix_set_permission_bit(fpath, os.s_iwgrp, false)
950 os.posix_set_permission_bit(fpath, os.s_iwoth, false)
951 unsafe {
952 C.stat(c_fpath, &s)
953 }
954 mode = u32(s.st_mode) & 0o7777
955 assert mode == 0o0755
956 os.rm(fpath) or {}
957 }
958}
959
960fn test_exists_in_system_path() {
961 assert os.exists_in_system_path('') == false
962 $if windows {
963 assert os.exists_in_system_path('cmd.exe')
964 return
965 }
966 assert os.exists_in_system_path('ls')
967}
968
969fn test_truncate() {
970 filename := './test_trunc.txt'
971 hello := 'hello world!'
972 mut f := os.create(filename)!
973 f.write_string(hello)!
974 f.close()
975 assert u64(hello.len) == os.file_size(filename)
976 newlen := u64(40000)
977 os.truncate(filename, newlen) or { panic(err) }
978 assert newlen == os.file_size(filename)
979 os.rm(filename) or { panic(err) }
980}
981
982fn test_hostname() {
983 hostname := os.hostname() or { '' }
984 assert hostname.len > 2
985}
986
987// fn test_loginname() {
988// loginname := os.loginname() or { '' }
989// assert loginname.len > 2
990//}
991
992fn test_glob() {
993 os.mkdir('test_dir') or { panic(err) }
994 for i in 0 .. 4 {
995 if i == 3 {
996 create_file('test_dir/test0_another')!
997 create_file('test_dir/test')!
998 } else {
999 create_file('test_dir/test' + i.str())!
1000 }
1001 }
1002 files := os.glob('test_dir/t*') or { panic(err) }
1003 assert files.len == 5
1004 assert os.base(files[0]) == 'test'
1005
1006 for i in 0 .. 3 {
1007 os.rm('test_dir/test' + i.str()) or { panic(err) }
1008 }
1009 os.rm('test_dir/test0_another') or { panic(err) }
1010 os.rm('test_dir/test') or { panic(err) }
1011 os.rmdir_all('test_dir') or { panic(err) }
1012}
1013
1014fn test_utime() {
1015 filename := './test_utime.txt'
1016 hello := 'hello world!'
1017 mut f := os.create(filename) or { panic(err) }
1018 defer {
1019 f.close()
1020 os.rm(filename) or { panic(err) }
1021 }
1022 f.write_string(hello) or { panic(err) }
1023 atime := i64(2_147_483_648)
1024 mtime := i64(2_306_102_495)
1025 os.utime(filename, atime, mtime) or { panic(err) }
1026 assert os.file_last_mod_unix(filename) == mtime
1027}
1028
1029fn test_execute() {
1030 print0script := os.join_path_single(tfolder, 'print0.v')
1031 // The output of the next command contains a 0 byte in the middle.
1032 // Nevertheless, the execute function *should* return a string that
1033 // contains it.
1034 os.write_file(print0script, 'C.printf(c"start%cMIDDLE%cfinish\nxx", 0, 0)\n')!
1035 defer {
1036 os.rm(print0script) or {}
1037 }
1038 result := os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(print0script)}')
1039 hexresult := result.output.hex()
1040 // println('exit_code: ${result.exit_code}')
1041 // println('output: |${result.output}|')
1042 // println('output.len: ${result.output.len}')
1043 // println('output hexresult: ${hexresult}')
1044 assert result.exit_code == 0
1045 assert hexresult.contains('7374617274004d4944444c450066696e697368')
1046 assert hexresult.ends_with('0a7878')
1047}
1048
1049fn test_exec_with_args() {
1050 source_path := os.join_path_single(tfolder, 'exec_args.v')
1051 output_arg := os.join_path_single(tfolder, 'exec_args')
1052 exe_path := if os.user_os() == 'windows' {
1053 output_arg + '.exe'
1054 } else {
1055 output_arg
1056 }
1057 os.write_file(source_path,
1058 "import os\n\nfn main() {\n\tprintln(os.args[1..].join('|'))\n\teprintln('stderr-ok')\n}\n")!
1059 defer {
1060 os.rm(source_path) or {}
1061 os.rm(exe_path) or {}
1062 os.rm(output_arg + '.c') or {}
1063 }
1064 compile_result :=
1065 os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(output_arg)} ${os.quoted_path(source_path)}')
1066 assert compile_result.exit_code == 0, compile_result.output
1067
1068 result := os.exec([exe_path, 'one two', 'semi;colon'])
1069 normalized_output := result.output.replace('\r\n', '\n')
1070 assert result.exit_code == 0, result.output
1071 assert normalized_output.contains('one two|semi;colon\n'), result.output
1072 assert normalized_output.contains('stderr-ok\n'), result.output
1073
1074 shell_metachar_result := os.exec([exe_path, 'first; echo injected'])
1075 normalized_shell_metachar_output := shell_metachar_result.output.replace('\r\n', '\n')
1076 assert shell_metachar_result.exit_code == 0, shell_metachar_result.output
1077 assert normalized_shell_metachar_output.contains('first; echo injected\n'), shell_metachar_result.output
1078}
1079
1080fn test_execute_with_stderr_redirection() {
1081 result := os.execute('${os.quoted_path(@VEXE)} wrong_command')
1082 assert result.exit_code == 1
1083 assert result.output.contains('unknown command `wrong_command`')
1084
1085 stderr_path := os.join_path_single(tfolder, 'stderr.txt')
1086 result2 :=
1087 os.execute('${os.quoted_path(@VEXE)} wrong_command 2> ${os.quoted_path(stderr_path)}')
1088 assert result2.exit_code == 1
1089 assert result2.output == ''
1090 assert os.exists(stderr_path)
1091}
1092
1093fn test_execute_with_linefeeds() {
1094 if os.user_os() == 'windows' {
1095 return
1096 }
1097 result := os.execute('true\n')
1098 assert result.exit_code == 0
1099 result2 := os.execute('false\n')
1100 assert result2.exit_code == 1
1101}
1102
1103fn test_execute_with_semicolon_inside_quoted_string_on_windows() {
1104 if os.user_os() != 'windows' {
1105 return
1106 }
1107 result := os.execute('echo "hello;"')
1108 assert result.exit_code == 0, result.output
1109 assert result.output.trim_space() == '"hello;"'
1110}
1111
1112fn test_execute_pipe_into_vfmt() {
1113 producer_script := os.join_path_single(tfolder, 'pipe_into_vfmt.v')
1114 os.write_file(producer_script, "fn main() {\n\tprint('fn main(){println(1)}\\n')\n}\n")!
1115 defer {
1116 os.rm(producer_script) or {}
1117 }
1118 result :=
1119 os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(producer_script)} | ${os.quoted_path(@VEXE)} fmt')
1120 assert result.exit_code == 0, result.output
1121 assert result.output.replace('\r\n', '\n') == 'fn main() {\n\tprintln(1)\n}\n'
1122}
1123
1124fn test_execute_fc_get_output() {
1125 if os.user_os() != 'windows' {
1126 return
1127 }
1128 result := os.execute('c:\\windows\\system32\\fc.exe /?')
1129 assert result.output.contains('filename')
1130 assert result.exit_code == -1
1131}
1132
1133fn test_execute_decodes_utf16le_output() {
1134 if os.user_os() != 'windows' {
1135 return
1136 }
1137 source_path := os.join_path_single(tfolder, 'utf16le_stdout.v')
1138 os.write_file(source_path, utf16le_stdout_source_code)!
1139 defer {
1140 os.rm(source_path) or {}
1141 }
1142 result := os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(source_path)}')
1143 assert result.exit_code == 0, result.output
1144 assert result.output == 'OK\n', result.output
1145}
1146
1147fn test_reading_from_proc_cpuinfo() {
1148 // This test is only for plain linux systems (they have a /proc virtual filesystem).
1149 $if android {
1150 assert true
1151 return
1152 }
1153 $if !linux {
1154 assert true
1155 return
1156 }
1157 info := os.read_file('/proc/cpuinfo')!
1158 assert info.len > 0
1159 assert info.contains('processor')
1160 // dump(info)
1161 // assert info.ends_with('\n\n') // fails on QEMU for s390x
1162
1163 info_bytes := os.read_bytes('/proc/cpuinfo')!
1164 assert info_bytes.len > 0
1165 assert info.len == info_bytes.len
1166}
1167
1168fn test_reading_from_empty_file() {
1169 empty_file := os.join_path_single(tfolder, 'empty_file.txt')
1170 os.rm(empty_file) or {}
1171 assert !os.exists(empty_file)
1172 os.write_file(empty_file, '')!
1173 assert os.exists(empty_file)
1174 content := os.read_file(empty_file)!
1175 assert content.len == 0
1176 content_bytes := os.read_bytes(empty_file)!
1177 assert content_bytes.len == 0
1178 os.rm(empty_file)!
1179}
1180
1181fn move_across_partitions_using_function(f fn (src string, dst string, opts os.MvParams) !) ! {
1182 bindfs := os.find_abs_path_of_executable('bindfs') or {
1183 eprintln('skipping test_mv_by_cp, because bindfs was not present')
1184 return
1185 }
1186 // eprintln('>> ${bindfs}')
1187 pfolder := os.join_path(tfolder, 'parent')
1188 cfolder := os.join_path(pfolder, 'child')
1189 mfolder := os.join_path(pfolder, 'mountpoint')
1190 cdeepfolder := os.join_path(cfolder, 'deep', 'folder')
1191 os.mkdir_all(mfolder)!
1192 os.mkdir_all(cfolder)!
1193 os.mkdir_all(cdeepfolder)!
1194
1195 original_path := os.join_path(pfolder, 'original.txt')
1196 target_path := os.join_path(cdeepfolder, 'target.txt')
1197 os.write_file(original_path, 'text')!
1198 os.write_file(os.join_path(cdeepfolder, 'x.txt'), 'some text')!
1199 // os.system('tree ${pfolder}')
1200 /*
1201 /tmp/v_1000/v/tests/os_test/parent
1202 ├── child
1203 │   └── deep
1204 │   └── folder
1205 │   └── x.txt
1206 ├── mountpoint
1207 └── original.txt
1208 */
1209 os.system('${bindfs} --no-allow-other ${cfolder} ${mfolder}')
1210 defer {
1211 os.system('sync; umount ${mfolder}')
1212 }
1213 // os.system('tree ${pfolder}')
1214 /*
1215 /tmp/v_1000/v/tests/os_test/parent
1216 ├── child
1217 │   └── deep
1218 │   └── folder
1219 │   └── x.txt
1220 ├── mountpoint
1221 │ └── deep
1222 │ └── folder
1223 │ └── x.txt
1224 └── original.txt
1225 */
1226
1227 f(original_path, target_path)!
1228
1229 assert os.exists(target_path)
1230 assert !os.exists(original_path)
1231}
1232
1233fn test_mv_by_cp_across_partitions() {
1234 move_across_partitions_using_function(os.mv_by_cp)!
1235}
1236
1237fn test_mv_across_partitions() {
1238 move_across_partitions_using_function(os.mv)!
1239}
1240
1241fn test_page_size() {
1242 assert os.page_size() >= 4096 // this is normal and assumed.
1243}
1244
1245fn test_mkdir_at_file_dst() {
1246 f3 := os.join_path('myfolder', 'f1', 'f2', 'f3')
1247 os.mkdir_all(f3)!
1248 assert os.is_dir(f3)
1249 path := os.join_path(f3, 'no_ext_doc')
1250 os.write_file(path, '')!
1251 assert os.exists(path) && os.is_file(path)
1252 os.mkdir_all(path) or {
1253 assert err.msg() == 'path `${path}` already exists, and is not a folder', err.msg()
1254 return
1255 }
1256 assert false
1257}
1258
1259fn test_disk_usage() {
1260 usage := os.disk_usage('.')!
1261 assert usage.total > 0
1262 assert usage.available > 0
1263 assert usage.used > 0
1264}
1265