import os // tfolder will contain all the temporary files/subfolders made by // the different tests. It would be removed in testsuite_end(), so // individual os tests do not need to clean up after themselves. const tfolder = os.join_path(os.vtmp_dir(), 'os_tests') const utf16le_stdout_source_code = ' module main import os fn main() { payload := [u8(`O`), 0, `K`, 0, u8(10), 0] mut out := os.stdout() out.write(payload) or { panic(err) } } ' // os.args has to be *already initialized* with the program's argc/argv at this point // thus it can be used for other consts too: const args_at_start = os.args.clone() fn testsuite_begin() { eprintln('testsuite_begin, tfolder = ${tfolder}') os.rmdir_all(tfolder) or {} assert !os.is_dir(tfolder) os.mkdir_all(tfolder) or { panic(err) } os.chdir(tfolder) or {} assert os.is_dir(tfolder) // println('args_at_start: ${args_at_start}') assert args_at_start.len > 0 assert args_at_start == os.args } fn testsuite_end() { os.chdir(os.wd_at_startup) or {} os.rmdir_all(tfolder) or {} // assert !os.is_dir(tfolder) // eprintln('testsuite_end , tfolder = ${tfolder} removed.') } fn test_open_file() { filename := './test1.txt' hello := 'hello world!' os.open_file(filename, 'r+', 0o666) or { assert err.msg() == 'No such file or directory' os.File{} } mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) } file.write_string(hello) or { panic(err) } file.close() assert u64(hello.len) == os.file_size(filename) read_hello := os.read_file(filename) or { panic('error reading file ' + filename) } assert hello == read_hello os.rm(filename) or { panic(err) } } fn test_read_file_from_virtual_filesystem() { $if linux { mounts := os.read_file('/proc/mounts')! // it is not empty, contains some mounting such as root filesystem: /dev/x / ext4 rw 0 0 assert mounts.len > 20 assert mounts.contains('/') assert mounts.contains(' ') } } fn test_read_binary_from_virtual_filesystem() { $if linux { mounts_raw := os.read_bytes('/proc/mounts')! mounts := mounts_raw.bytestr() // it is not empty, contains some mounting such as root filesystem: /dev/x / ext4 rw 0 0 assert mounts.len > 20 assert mounts.contains('/') assert mounts.contains(' ') } } fn test_open_file_binary() { filename := './test1.dat' hello := 'hello \n world!' os.open_file(filename, 'r+', 0o666) or { assert err.msg() == 'No such file or directory' os.File{} } mut file := os.open_file(filename, 'wb+', 0o666) or { panic(err) } bytes := hello.bytes() unsafe { file.write_ptr(bytes.data, bytes.len) } file.close() assert u64(hello.len) == os.file_size(filename) read_hello := os.read_bytes(filename) or { panic('error reading file ' + filename) } assert bytes == read_hello os.rm(filename) or { panic(err) } } // fn test_file_get_line() { // filename := './fgetline.txt' // os.write_file(filename, 'line 1\nline 2') // mut f := os.open_file(filename, 'r', 0) or { // assert false // return // } // line1 := f.get_line() or { // '' // } // line2 := f.get_line() or { // '' // } // f.close() // // // eprintln('line1: ${line1} ${line1.bytes()}') // eprintln('line2: ${line2} ${line2.bytes()}') // assert line1 == 'line 1\n' // assert line2 == 'line 2' // } fn create_file(fpath string) ! { mut f := os.create(fpath)! f.close() } fn create_and_write_to_file(fpath string, content string) ! { mut f := os.create(fpath)! f.write_string(content)! f.close() } fn test_create_file() { filename := './test1.txt' hello := 'hello world!' create_and_write_to_file(filename, hello)! assert u64(hello.len) == os.file_size(filename) os.rm(filename) or { panic(err) } } fn test_is_file() { // Setup work_dir := os.join_path_single(tfolder, 'is_file_test') os.mkdir_all(work_dir) or { panic(err) } tfile := os.join_path_single(work_dir, 'tmp_file') // Test things that shouldn't be a file assert os.is_file(work_dir) == false assert os.is_file('non-existent_file.tmp') == false // Test file tfile_content := 'temporary file' os.write_file(tfile, tfile_content) or { panic(err) } assert os.is_file(tfile) // Test dir symlinks $if windows { assert true } $else { dsymlink := os.join_path_single(work_dir, 'dir_symlink') os.symlink(work_dir, dsymlink) or { panic(err) } assert os.is_file(dsymlink) == false } // Test file symlinks $if windows { assert true } $else { fsymlink := os.join_path_single(work_dir, 'file_symlink') os.symlink(tfile, fsymlink) or { panic(err) } assert os.is_file(fsymlink) } } fn test_write_and_read_string_to_file() { filename := './test1.txt' hello := 'hello world!' os.write_file(filename, hello) or { panic(err) } assert u64(hello.len) == os.file_size(filename) read_hello := os.read_file(filename) or { panic('error reading file ' + filename) } assert hello == read_hello os.rm(filename) or { panic(err) } } // test_write_and_read_bytes checks for regressions made in the functions // read_bytes, read_bytes_at and write_bytes. fn test_write_and_read_bytes() { file_name := './byte_reader_writer.tst' payload := [u8(`I`), `D`, `D`, `Q`, `D`] mut file_write := os.create(os.real_path(file_name)) or { eprintln('failed to create file ${file_name}') return } // We use the standard write_bytes function to write the payload and // compare the length of the array with the file size (have to match). unsafe { file_write.write_ptr(payload.data, 5) } file_write.close() assert u64(payload.len) == os.file_size(file_name) mut file_read := os.open(os.real_path(file_name)) or { eprintln('failed to open file ${file_name}') return } // We only need to test read_bytes because this function calls // read_bytes_at with second parameter zeroed (size, 0). rbytes := file_read.read_bytes(5) // eprintln('rbytes: ${rbytes}') // eprintln('payload: ${payload}') assert rbytes == payload // check that trying to read data from EOF doesn't error and returns 0 mut a := []u8{len: 5} nread := file_read.read_bytes_into(5, mut a) or { n := if err is os.Eof { int(0) } else { eprintln(err) int(-1) } n } assert nread == 0 file_read.close() // We finally delete the test file. os.rm(file_name) or { panic(err) } } fn test_ls() { if x := os.ls('') { assert false } else { assert true } if x := os.ls('.') { assert x.len > 0 } else { assert false } } fn create_tree() ! { os.mkdir_all('myfolder/f1/f2/f3')! os.mkdir_all('myfolder/a1/a2/a3', mode: 0o700)! f3 := os.real_path('myfolder/f1/f2/f3') assert os.is_dir(f3) create_file('myfolder/f1/f2/f3/.hfile1')! create_file('myfolder/f1/f2/f3/a.txt')! create_file('myfolder/f1/f2/f3/b.txt')! create_file('myfolder/f1/f2/f3/c.txt')! create_file('myfolder/f1/f2/f3/d.md')! create_file('myfolder/f1/0.txt')! create_file('myfolder/another.md')! create_file('myfolder/a1/a2/a3/x.txt')! create_file('myfolder/a1/a2/a3/y.txt')! create_file('myfolder/a1/a2/a3/z.txt')! create_file('myfolder/a1/1.txt')! create_file('myfolder/a1/.hfile2')! create_file('myfolder/xyz.ini')! } fn remove_tree() { os.rmdir_all('myfolder') or {} } fn normalise_paths(paths []string) []string { mut res := paths.map(it.replace(os.path_separator, '/')) res.sort() return res } fn test_walk_ext() { create_tree()! defer { remove_tree() } all := os.walk_ext('.', '') assert all.len > 10 top := normalise_paths(os.walk_ext('myfolder', '.txt')) assert top == [ 'myfolder/a1/1.txt', 'myfolder/a1/a2/a3/x.txt', 'myfolder/a1/a2/a3/y.txt', 'myfolder/a1/a2/a3/z.txt', 'myfolder/f1/0.txt', 'myfolder/f1/f2/f3/a.txt', 'myfolder/f1/f2/f3/b.txt', 'myfolder/f1/f2/f3/c.txt', ] subfolder_txts := normalise_paths(os.walk_ext('myfolder/a1/a2', '.txt')) assert subfolder_txts == [ 'myfolder/a1/a2/a3/x.txt', 'myfolder/a1/a2/a3/y.txt', 'myfolder/a1/a2/a3/z.txt', ] mut mds := normalise_paths(os.walk_ext('myfolder', '.md')) assert mds == ['myfolder/another.md', 'myfolder/f1/f2/f3/d.md'] all_with_hidden := os.walk_ext('.', '', hidden: true) assert all_with_hidden.len > all.len hidden := normalise_paths(all_with_hidden.filter(it !in all)) assert hidden == ['./myfolder/a1/.hfile2', './myfolder/f1/f2/f3/.hfile1'] } fn test_walk_with_context() { create_tree()! defer { remove_tree() } mut res := []string{} os.walk_with_context('myfolder', &res, fn (ctx voidptr, fpath string) { mut res := unsafe { &[]string(ctx) } res << fpath }) res = normalise_paths(res) assert 'myfolder/f1/f2/f3/b.txt' in res assert 'myfolder/another.md' in res } fn test_create_and_delete_folder() { folder := './test1' os.mkdir(folder) or { panic(err) } assert os.is_dir(folder) folder_contents := os.ls(folder) or { panic(err) } assert folder_contents.len == 0 os.rmdir(folder) or { panic(err) } folder_exists := os.is_dir(folder) assert folder_exists == false } fn walk_callback(file string) { if file == '.' || file == '..' { return } assert file == 'test_walk' + os.path_separator + 'test1' } fn test_walk() { folder := 'test_walk' os.mkdir(folder) or { panic(err) } file1 := folder + os.path_separator + 'test1' os.write_file(file1, 'test-1') or { panic(err) } os.walk(folder, walk_callback) os.rm(file1) or { panic(err) } os.rmdir(folder) or { panic(err) } } fn test_cp() { old_file_name := 'cp_example.txt' new_file_name := 'cp_new_example.txt' os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐')! os.cp(old_file_name, new_file_name)! old_file := os.read_file(old_file_name)! new_file := os.read_file(new_file_name)! assert old_file == new_file os.rm(old_file_name)! os.rm(new_file_name)! } fn test_cp_to_folder() { file_name := 'cp_to_folder_example.txt' folder := 'test_cp_to_folder' os.mkdir(folder)! os.write_file(file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐')! os.cp(file_name, folder)! new_file_path := os.join_path_single(folder, file_name) old_file := os.read_file(file_name)! new_file := os.read_file(new_file_path)! assert old_file == new_file os.rm(file_name)! os.rm(new_file_path)! os.rmdir(folder)! } fn test_cp_fail_if_exists() { file_name := 'cp_fail_if_exists_example.txt' folder := 'test_cp_fail_if_exists' os.mkdir(folder)! os.write_file(file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐')! os.cp(file_name, folder)! new_file_path := os.join_path_single(folder, file_name) if _ := os.cp(file_name, folder, fail_if_exists: true) { assert false } else { assert err.str().starts_with('cp: failed to '), 'cp err: ${err}' } os.rm(file_name)! os.rm(new_file_path)! os.rmdir(folder)! } fn test_mv() { work_dir := os.join_path_single(tfolder, 'mv_test') os.mkdir_all(work_dir) or { panic(err) } // Setup test files tfile1 := os.join_path_single(work_dir, 'file') tfile2 := os.join_path_single(work_dir, 'file.test') tfile3 := os.join_path_single(work_dir, 'file.3') tfile_content := 'temporary file' os.write_file(tfile1, tfile_content) or { panic(err) } os.write_file(tfile2, tfile_content) or { panic(err) } // Setup test dirs tdir1 := os.join_path_single(work_dir, 'dir') tdir2 := os.join_path_single(work_dir, 'dir2') tdir3 := os.join_path_single(work_dir, 'dir3') os.mkdir(tdir1) or { panic(err) } os.mkdir(tdir2) or { panic(err) } // Move file with no extension to dir os.mv(tfile1, tdir1) or { panic(err) } mut expected := os.join_path_single(tdir1, 'file') assert os.exists(expected) assert !os.is_dir(expected) // Move dir with contents to other dir os.mv(tdir1, tdir2) or { panic(err) } expected = os.join_path_single(tdir2, 'dir') assert os.exists(expected) assert os.is_dir(expected) expected = os.join_path(tdir2, 'dir', 'file') assert os.exists(expected) assert !os.is_dir(expected) // Move dir with contents to other dir (by renaming) os.mv(os.join_path_single(tdir2, 'dir'), tdir3) or { panic(err) } expected = tdir3 assert os.exists(expected) assert os.is_dir(expected) assert os.is_dir_empty(tdir2) // Move file with extension to dir os.mv(tfile2, tdir2) or { panic(err) } expected = os.join_path_single(tdir2, 'file.test') assert os.exists(expected) assert !os.is_dir(expected) // Move file to dir (by renaming) os.mv(os.join_path_single(tdir2, 'file.test'), tfile3) or { panic(err) } expected = tfile3 assert os.exists(expected) assert !os.is_dir(expected) } fn test_is_dir_empty() { // Test that is_dir_empty returns true on // non-existent directories ***as stated in it's doc string*** assert os.is_dir_empty('dir that does not exist at all') } fn test_cp_all() { // fileX -> dir/fileX // Note: clean up of the files happens inside the cleanup_leftovers function os.write_file('ex1.txt', 'wow!') or { panic(err) } os.mkdir('ex') or { panic(err) } os.cp_all('ex1.txt', 'ex', false) or { panic(err) } old := os.read_file('ex1.txt') or { panic(err) } new := os.read_file('ex/ex1.txt') or { panic(err) } assert old == new os.mkdir('ex/ex2') or { panic(err) } os.write_file('ex2.txt', 'great!') or { panic(err) } os.cp_all('ex2.txt', 'ex/ex2', false) or { panic(err) } old2 := os.read_file('ex2.txt') or { panic(err) } new2 := os.read_file('ex/ex2/ex2.txt') or { panic(err) } assert old2 == new2 // recurring on dir -> local dir os.cp_all('ex', './', true) or { panic(err) } // regression test for executive runs with overwrite := true os.cp_all('ex', './', true) or { panic(err) } os.cp_all('ex', 'nonexisting', true) or { panic(err) } assert os.exists(os.join_path_single('nonexisting', 'ex1.txt')) } fn test_realpath_of_empty_string_works() { assert os.real_path('') == '' } fn test_realpath_non_existing() { non_existing_path := 'sdyfuisd_non_existing_file' rpath := os.real_path(non_existing_path) $if windows { // on windows, the workdir is prepended, so the result is absolute: assert rpath.len > non_existing_path.len } $if !windows { // on unix, the workdir is NOT prepended for now, so the result remains the same. // TODO: the windows behaviour seems saner, think about normalising the unix case to do the same. assert os.real_path(non_existing_path) == non_existing_path } } fn test_realpath_existing() { existing_file_name := 'existing_file.txt' existing_file := os.join_path_single(tfolder, existing_file_name) os.rm(existing_file) or {} os.write_file(existing_file, 'abc') or {} assert os.exists(existing_file) rpath := os.real_path(existing_file) assert os.is_abs_path(rpath) assert rpath.ends_with(existing_file_name) $if windows { assert !rpath.starts_with('UNC\\') assert !rpath.starts_with('\\\\?\\') } os.rm(existing_file) or {} } fn test_realpath_removes_dots() { examples_folder := os.join_path(@VEXEROOT, 'vlib', 'v', '..', '..', 'cmd', '.', '..', 'examples') real_path_of_examples_folder := os.real_path(examples_folder) assert real_path_of_examples_folder.len < examples_folder.len assert !real_path_of_examples_folder.contains('..') } fn test_realpath_absolutizes_existing_relative_paths() { old_wd := os.getwd() defer { os.chdir(old_wd) or { panic(err) } } os.chdir(@VEXEROOT) or { panic(err) } examples_folder := os.join_path('vlib', 'v', '..', '..', 'cmd', '.', '..', 'examples') real_path_of_examples_folder := os.real_path(examples_folder) assert os.is_abs_path(real_path_of_examples_folder) } // TODO: think much more about whether this is desirable: fn test_realpath_does_not_absolutize_non_existing_relative_paths() { relative_path := os.join_path('one', 'nonexisting_folder', '..', 'something') $if !windows { assert os.real_path(relative_path).contains('..') assert os.real_path(relative_path) == relative_path } } fn handle_privilege_error(err IError) ! { if err.msg().contains('required privilege is not held by the client') { eprintln('skipping ${@METHOD} on windows, since the user is not administrator, err:\n ${err}') return err } panic(err) } fn test_realpath_absolutepath_symlink() ! { file_name := 'tolink_file.txt' symlink_name := 'symlink.txt' create_file(file_name)! os.symlink(file_name, symlink_name) or { handle_privilege_error(err) or { return } } rpath := os.real_path(symlink_name) println(rpath) assert os.is_abs_path(rpath) assert rpath.ends_with(file_name) $if windows { assert !rpath.starts_with('UNC\\') assert !rpath.starts_with('\\\\?\\') } os.rm(symlink_name) or {} os.rm(file_name) or {} } fn test_make_symlink_check_is_link_and_remove_symlink() { folder := 'tfolder' symlink := 'tsymlink' // windows creates a directory symlink, so delete it with rmdir() $if windows { os.rmdir(symlink) or {} } $else { os.rm(symlink) or {} } os.rmdir(folder) or {} os.mkdir(folder) or { panic(err) } folder_contents := os.ls(folder) or { panic(err) } assert folder_contents.len == 0 os.symlink(folder, symlink) or { handle_privilege_error(err) or { return } } assert os.is_link(symlink) $if windows { os.rmdir(symlink) or { panic(err) } } $else { os.rm(symlink) or { panic(err) } } os.rmdir(folder) or { panic(err) } folder_exists := os.is_dir(folder) assert folder_exists == false symlink_exists := os.is_link(symlink) assert symlink_exists == false } fn test_make_symlink_check_is_link_and_remove_symlink_with_file() { file := 'tfile' symlink := 'tsymlink' os.rm(symlink) or {} os.rm(file) or {} create_file(file)! os.symlink(file, symlink) or { handle_privilege_error(err) or { return } } assert os.is_link(symlink) os.rm(symlink) or { panic(err) } os.rm(file) or { panic(err) } symlink_exists := os.is_link(symlink) assert symlink_exists == false } fn test_make_hardlink_check_is_link_and_remove_hardlink_with_file() { file := 'tfile' symlink := 'tsymlink' os.rm(symlink) or {} os.rm(file) or {} create_file(file)! os.link(file, symlink) or { panic(err) } assert os.exists(symlink) os.rm(symlink) or { panic(err) } os.rm(file) or { panic(err) } symlink_exists := os.is_link(symlink) assert symlink_exists == false } // fn test_fork() { // pid := os.fork() // if pid == 0 { // println('Child') // } // else { // println('Parent') // } // } // fn test_wait() { // pid := os.fork() // if pid == 0 { // println('Child') // exit(0) // } // else { // cpid := os.wait() // println('Parent') // println(cpid) // } // } fn test_symlink() { os.mkdir('symlink') or { panic(err) } os.symlink('symlink', 'symlink2') or { handle_privilege_error(err) or { return } } assert os.exists('symlink2') // cleanup os.rmdir('symlink') or { panic(err) } $if windows { os.rmdir('symlink2') or { panic(err) } } $else { os.rm('symlink2') or { panic(err) } } } fn test_readlink() { $if windows { eprintln('skipping ${@METHOD} on windows, api not supported') return } os.symlink('some_target_string', 'some_symlink')! defer { os.rm('some_symlink') or { panic(err) } } assert os.readlink('some_symlink')! == 'some_target_string' } fn test_exists_symlink_dangling() { $if msvc { eprintln('skipping ${@METHOD} on windows + msvc; TODO: investigate why os.lstat/1 behaves differently than for gcc/clang') return } os.symlink('nonexistent', 'dangling_symlink') or { handle_privilege_error(err) or { return } } // sanity check that the symlink truly does exist. the lack of error alone is the check. // (on linux, `.get_filetype() == os.FileType.symbolic_link` is true, but on windows, a dangling symlink is reported as a regular file.) os.lstat('dangling_symlink')! // the exists function says false in this scenario... on linux and linux-like systems. // it says true on windows! $if windows { assert os.exists('dangling_symlink') == true } $else { assert os.exists('dangling_symlink') == false } } fn test_is_executable_writable_readable() { file_name := 'rwxfile.exe' create_file(file_name)! $if !windows { os.chmod(file_name, 0o600) or {} // mark as readable && writable, but NOT executable assert os.is_writable(file_name) assert os.is_readable(file_name) assert !os.is_executable(file_name) os.chmod(file_name, 0o700) or {} // mark as executable too assert os.is_executable(file_name) } $else { assert os.is_writable(file_name) assert os.is_readable(file_name) assert os.is_executable(file_name) for ext in ['exe', 'com', 'bat', 'cmd'] { mut executable_file_name := 'executable.${ext}' create_file(executable_file_name)! assert os.is_executable(executable_file_name) os.rm(executable_file_name) or { panic(err) } } } // We finally delete the test file. os.rm(file_name) or { panic(err) } } fn test_file_ext() { assert os.file_ext('') == '' assert os.file_ext('file.v') == '.v' assert os.file_ext('file.js.v') == '.v' assert os.file_ext('file.ext1.ext2.ext3') == '.ext3' assert os.file_ext('.ignore_me.v') == '.v' assert os.file_ext('file') == '' assert os.file_ext('.git') == '' assert os.file_ext('file.') == '' assert os.file_ext('.') == '' assert os.file_ext('..') == '' assert os.file_ext('file...') == '' assert os.file_ext('.file.') == '' assert os.file_ext('..file..') == '' assert os.file_ext('./.git') == '' assert os.file_ext('./.git/') == '' assert os.file_ext('\\.git') == '' assert os.file_ext('\\.git\\') == '' } fn test_rmdir_all() { mut dirs := ['some/dir', 'some/.hidden/directory'] $if windows { for mut d in dirs { d = d.replace('/', '\\') } } for d in dirs { os.mkdir_all(d) or { panic(err) } assert os.is_dir(d) } os.rmdir_all('some') or { assert false } assert !os.exists('some') } fn test_rmdir_not_exist() ! { dir := 'non_existing_dir' assert !os.exists(dir) os.rmdir(dir) or { // 0x00000002 is both ENOENT in POSIX and ERROR_FILE_NOT_FOUND in Win32 API assert err.code() == 0x00000002 } assert !os.exists(dir) } fn test_dir() { assert os.dir('') == '.' assert os.dir('\\') == '\\' assert os.dir('C:\\a\\b\\c') == 'C:\\a\\b' assert os.dir('C:\\a\\b\\') == 'C:\\a\\b' assert os.dir('C:/a/b/c') == 'C:/a/b' assert os.dir('C:/a/b/') == 'C:/a/b' assert os.dir('/') == '/' assert os.dir('/abc') == '/' assert os.dir('/var/tmp/foo') == '/var/tmp' assert os.dir('/var/tmp/') == '/var/tmp' assert os.dir('os') == '.' } fn test_base() { assert os.base('') == '.' assert os.base('v\\vlib\\os') == 'os' assert os.base('v\\vlib\\os\\') == 'os' assert os.base('v/vlib/os') == 'os' assert os.base('v/vlib/os/') == 'os' assert os.base('v/vlib/os') == 'os' assert os.base('v/vlib/os/') == 'os' assert os.base('v\\vlib\\os') == 'os' assert os.base('v\\vlib\\os\\') == 'os' assert os.base('filename') == 'filename' } fn test_file_name() { assert os.file_name('') == '' assert os.file_name('v\\vlib\\os\\os.v') == 'os.v' assert os.file_name('v\\vlib\\os\\') == '' assert os.file_name('v\\vlib\\os') == 'os' assert os.file_name('v/vlib/os/os.v') == 'os.v' assert os.file_name('v/vlib/os/') == '' assert os.file_name('v/vlib/os') == 'os' assert os.file_name('filename') == 'filename' } fn test_split_path() { mut dir := '' mut filename := '' mut ext := '' dir, filename, ext = os.split_path('') assert [dir, filename, ext] == ['.', '', ''] dir, filename, ext = os.split_path('a') assert [dir, filename, ext] == ['.', 'a', ''] dir, filename, ext = os.split_path('.') assert [dir, filename, ext] == ['.', '', ''] dir, filename, ext = os.split_path('..') assert [dir, filename, ext] == ['..', '', ''] dir, filename, ext = os.split_path('\\') assert [dir, filename, ext] == ['\\', '', ''] dir, filename, ext = os.split_path('\\x.c.v') assert [dir, filename, ext] == ['\\', 'x.c', '.v'] dir, filename, ext = os.split_path('.\\x.c.v') assert [dir, filename, ext] == ['.', 'x.c', '.v'] dir, filename, ext = os.split_path('x.c.v') assert [dir, filename, ext] == ['.', 'x.c', '.v'] dir, filename, ext = os.split_path('..\\x.c.v') assert [dir, filename, ext] == ['..', 'x.c', '.v'] dir, filename, ext = os.split_path('\\lib\\x.c.v') assert [dir, filename, ext] == ['\\lib', 'x.c', '.v'] dir, filename, ext = os.split_path('\\lib\\x.c.v\\') assert [dir, filename, ext] == ['\\lib\\x.c.v', '', ''] dir, filename, ext = os.split_path('\\lib\\x.c.') assert [dir, filename, ext] == ['\\lib', 'x.c.', ''] dir, filename, ext = os.split_path('C:\\lib\\x.c.') assert [dir, filename, ext] == ['C:\\lib', 'x.c.', ''] dir, filename, ext = os.split_path('/') assert [dir, filename, ext] == ['/', '', ''] dir, filename, ext = os.split_path('/x.c.v') assert [dir, filename, ext] == ['/', 'x.c', '.v'] dir, filename, ext = os.split_path('./x.c.v') assert [dir, filename, ext] == ['.', 'x.c', '.v'] dir, filename, ext = os.split_path('../x.c.v') assert [dir, filename, ext] == ['..', 'x.c', '.v'] dir, filename, ext = os.split_path('/lib/x.c.v') assert [dir, filename, ext] == ['/lib', 'x.c', '.v'] dir, filename, ext = os.split_path('/lib/x.c.v/') assert [dir, filename, ext] == ['/lib/x.c.v', '', ''] dir, filename, ext = os.split_path('/lib/../x.c.v/') assert [dir, filename, ext] == ['/lib/../x.c.v', '', ''] dir, filename, ext = os.split_path('/lib/x.c.') assert [dir, filename, ext] == ['/lib', 'x.c.', ''] } fn test_uname() { u := os.uname() assert u.sysname.len > 0 assert u.nodename.len > 0 assert u.release.len > 0 assert u.version.len > 0 assert u.machine.len > 0 } // tests for write_file_array and read_file_array[T]: const maxn = 3 struct IntPoint { x int y int } fn test_write_file_array_bytes() { fpath := './abytes.bin' mut arr := []u8{len: maxn} for i in 0 .. maxn { arr[i] = 65 + u8(i) } os.write_file_array(fpath, arr) or { panic(err) } rarr := os.read_bytes(fpath) or { panic(err) } assert arr == rarr // eprintln(arr.str()) // eprintln(rarr.str()) } fn test_write_bytes() { fpath := './wbytes.bin' for arr in [[u8(65), 66, 67, 68, 69, 70], [u8(3), 2, 1], []u8{}] { os.write_bytes(fpath, arr)! rarr := os.read_bytes(fpath)! assert arr == rarr } } fn test_write_file_array_structs() { fpath := './astructs.bin' mut arr := []IntPoint{len: maxn} for i in 0 .. maxn { arr[i] = IntPoint{65 + i, 65 + i + 10} } os.write_file_array(fpath, arr) or { panic(err) } rarr := os.read_file_array[IntPoint](fpath) assert rarr == arr assert rarr.len == maxn // eprintln( rarr.str().replace('\n', ' ').replace('},', '},\n')) } fn test_stdout_capture() { /* mut cmd := os.Command{ path:'cat' redirect_stdout: true } cmd.start() for !cmd.eof { line := cmd.read_line() println('line="${line}"') } cmd.close() */ } fn test_posix_set_bit() { $if windows { assert true } $else { fpath := 'permtest' create_file(fpath)! os.chmod(fpath, 0o0777) or { panic(err) } c_fpath := &char(fpath.str) mut s := C.stat{} unsafe { C.stat(c_fpath, &s) } // Take the permissions part of the mode mut mode := u32(s.st_mode) & 0o0777 assert mode == 0o0777 // `chmod u-r` os.posix_set_permission_bit(fpath, os.s_irusr, false) unsafe { C.stat(c_fpath, &s) } mode = u32(s.st_mode) & 0o0777 assert mode == 0o0377 // `chmod u+r` os.posix_set_permission_bit(fpath, os.s_irusr, true) unsafe { C.stat(c_fpath, &s) } mode = u32(s.st_mode) & 0o0777 assert mode == 0o0777 // Note: setting the sticky bit is platform dependent // `chmod -s -g -t` os.posix_set_permission_bit(fpath, os.s_isuid, false) os.posix_set_permission_bit(fpath, os.s_isgid, false) os.posix_set_permission_bit(fpath, os.s_isvtx, false) unsafe { C.stat(c_fpath, &s) } mode = u32(s.st_mode) & 0o0777 assert mode == 0o0777 // `chmod g-w o-w` os.posix_set_permission_bit(fpath, os.s_iwgrp, false) os.posix_set_permission_bit(fpath, os.s_iwoth, false) unsafe { C.stat(c_fpath, &s) } mode = u32(s.st_mode) & 0o7777 assert mode == 0o0755 os.rm(fpath) or {} } } fn test_exists_in_system_path() { assert os.exists_in_system_path('') == false $if windows { assert os.exists_in_system_path('cmd.exe') return } assert os.exists_in_system_path('ls') } fn test_truncate() { filename := './test_trunc.txt' hello := 'hello world!' mut f := os.create(filename)! f.write_string(hello)! f.close() assert u64(hello.len) == os.file_size(filename) newlen := u64(40000) os.truncate(filename, newlen) or { panic(err) } assert newlen == os.file_size(filename) os.rm(filename) or { panic(err) } } fn test_hostname() { hostname := os.hostname() or { '' } assert hostname.len > 2 } // fn test_loginname() { // loginname := os.loginname() or { '' } // assert loginname.len > 2 //} fn test_glob() { os.mkdir('test_dir') or { panic(err) } for i in 0 .. 4 { if i == 3 { create_file('test_dir/test0_another')! create_file('test_dir/test')! } else { create_file('test_dir/test' + i.str())! } } files := os.glob('test_dir/t*') or { panic(err) } assert files.len == 5 assert os.base(files[0]) == 'test' for i in 0 .. 3 { os.rm('test_dir/test' + i.str()) or { panic(err) } } os.rm('test_dir/test0_another') or { panic(err) } os.rm('test_dir/test') or { panic(err) } os.rmdir_all('test_dir') or { panic(err) } } fn test_utime() { filename := './test_utime.txt' hello := 'hello world!' mut f := os.create(filename) or { panic(err) } defer { f.close() os.rm(filename) or { panic(err) } } f.write_string(hello) or { panic(err) } atime := i64(2_147_483_648) mtime := i64(2_306_102_495) os.utime(filename, atime, mtime) or { panic(err) } assert os.file_last_mod_unix(filename) == mtime } fn test_execute() { print0script := os.join_path_single(tfolder, 'print0.v') // The output of the next command contains a 0 byte in the middle. // Nevertheless, the execute function *should* return a string that // contains it. os.write_file(print0script, 'C.printf(c"start%cMIDDLE%cfinish\nxx", 0, 0)\n')! defer { os.rm(print0script) or {} } result := os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(print0script)}') hexresult := result.output.hex() // println('exit_code: ${result.exit_code}') // println('output: |${result.output}|') // println('output.len: ${result.output.len}') // println('output hexresult: ${hexresult}') assert result.exit_code == 0 assert hexresult.contains('7374617274004d4944444c450066696e697368') assert hexresult.ends_with('0a7878') } fn test_exec_with_args() { source_path := os.join_path_single(tfolder, 'exec_args.v') output_arg := os.join_path_single(tfolder, 'exec_args') exe_path := if os.user_os() == 'windows' { output_arg + '.exe' } else { output_arg } os.write_file(source_path, "import os\n\nfn main() {\n\tprintln(os.args[1..].join('|'))\n\teprintln('stderr-ok')\n}\n")! defer { os.rm(source_path) or {} os.rm(exe_path) or {} os.rm(output_arg + '.c') or {} } compile_result := os.execute('${os.quoted_path(@VEXE)} -o ${os.quoted_path(output_arg)} ${os.quoted_path(source_path)}') assert compile_result.exit_code == 0, compile_result.output result := os.exec([exe_path, 'one two', 'semi;colon']) normalized_output := result.output.replace('\r\n', '\n') assert result.exit_code == 0, result.output assert normalized_output.contains('one two|semi;colon\n'), result.output assert normalized_output.contains('stderr-ok\n'), result.output shell_metachar_result := os.exec([exe_path, 'first; echo injected']) normalized_shell_metachar_output := shell_metachar_result.output.replace('\r\n', '\n') assert shell_metachar_result.exit_code == 0, shell_metachar_result.output assert normalized_shell_metachar_output.contains('first; echo injected\n'), shell_metachar_result.output } fn test_execute_with_stderr_redirection() { result := os.execute('${os.quoted_path(@VEXE)} wrong_command') assert result.exit_code == 1 assert result.output.contains('unknown command `wrong_command`') stderr_path := os.join_path_single(tfolder, 'stderr.txt') result2 := os.execute('${os.quoted_path(@VEXE)} wrong_command 2> ${os.quoted_path(stderr_path)}') assert result2.exit_code == 1 assert result2.output == '' assert os.exists(stderr_path) } fn test_execute_with_linefeeds() { if os.user_os() == 'windows' { return } result := os.execute('true\n') assert result.exit_code == 0 result2 := os.execute('false\n') assert result2.exit_code == 1 } fn test_execute_with_semicolon_inside_quoted_string_on_windows() { if os.user_os() != 'windows' { return } result := os.execute('echo "hello;"') assert result.exit_code == 0, result.output assert result.output.trim_space() == '"hello;"' } fn test_execute_pipe_into_vfmt() { producer_script := os.join_path_single(tfolder, 'pipe_into_vfmt.v') os.write_file(producer_script, "fn main() {\n\tprint('fn main(){println(1)}\\n')\n}\n")! defer { os.rm(producer_script) or {} } result := os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(producer_script)} | ${os.quoted_path(@VEXE)} fmt') assert result.exit_code == 0, result.output assert result.output.replace('\r\n', '\n') == 'fn main() {\n\tprintln(1)\n}\n' } fn test_execute_fc_get_output() { if os.user_os() != 'windows' { return } result := os.execute('c:\\windows\\system32\\fc.exe /?') assert result.output.contains('filename') assert result.exit_code == -1 } fn test_execute_decodes_utf16le_output() { if os.user_os() != 'windows' { return } source_path := os.join_path_single(tfolder, 'utf16le_stdout.v') os.write_file(source_path, utf16le_stdout_source_code)! defer { os.rm(source_path) or {} } result := os.execute('${os.quoted_path(@VEXE)} run ${os.quoted_path(source_path)}') assert result.exit_code == 0, result.output assert result.output == 'OK\n', result.output } fn test_reading_from_proc_cpuinfo() { // This test is only for plain linux systems (they have a /proc virtual filesystem). $if android { assert true return } $if !linux { assert true return } info := os.read_file('/proc/cpuinfo')! assert info.len > 0 assert info.contains('processor') // dump(info) // assert info.ends_with('\n\n') // fails on QEMU for s390x info_bytes := os.read_bytes('/proc/cpuinfo')! assert info_bytes.len > 0 assert info.len == info_bytes.len } fn test_reading_from_empty_file() { empty_file := os.join_path_single(tfolder, 'empty_file.txt') os.rm(empty_file) or {} assert !os.exists(empty_file) os.write_file(empty_file, '')! assert os.exists(empty_file) content := os.read_file(empty_file)! assert content.len == 0 content_bytes := os.read_bytes(empty_file)! assert content_bytes.len == 0 os.rm(empty_file)! } fn move_across_partitions_using_function(f fn (src string, dst string, opts os.MvParams) !) ! { bindfs := os.find_abs_path_of_executable('bindfs') or { eprintln('skipping test_mv_by_cp, because bindfs was not present') return } // eprintln('>> ${bindfs}') pfolder := os.join_path(tfolder, 'parent') cfolder := os.join_path(pfolder, 'child') mfolder := os.join_path(pfolder, 'mountpoint') cdeepfolder := os.join_path(cfolder, 'deep', 'folder') os.mkdir_all(mfolder)! os.mkdir_all(cfolder)! os.mkdir_all(cdeepfolder)! original_path := os.join_path(pfolder, 'original.txt') target_path := os.join_path(cdeepfolder, 'target.txt') os.write_file(original_path, 'text')! os.write_file(os.join_path(cdeepfolder, 'x.txt'), 'some text')! // os.system('tree ${pfolder}') /* /tmp/v_1000/v/tests/os_test/parent ├── child │   └── deep │   └── folder │   └── x.txt ├── mountpoint └── original.txt */ os.system('${bindfs} --no-allow-other ${cfolder} ${mfolder}') defer { os.system('sync; umount ${mfolder}') } // os.system('tree ${pfolder}') /* /tmp/v_1000/v/tests/os_test/parent ├── child │   └── deep │   └── folder │   └── x.txt ├── mountpoint │ └── deep │ └── folder │ └── x.txt └── original.txt */ f(original_path, target_path)! assert os.exists(target_path) assert !os.exists(original_path) } fn test_mv_by_cp_across_partitions() { move_across_partitions_using_function(os.mv_by_cp)! } fn test_mv_across_partitions() { move_across_partitions_using_function(os.mv)! } fn test_page_size() { assert os.page_size() >= 4096 // this is normal and assumed. } fn test_mkdir_at_file_dst() { f3 := os.join_path('myfolder', 'f1', 'f2', 'f3') os.mkdir_all(f3)! assert os.is_dir(f3) path := os.join_path(f3, 'no_ext_doc') os.write_file(path, '')! assert os.exists(path) && os.is_file(path) os.mkdir_all(path) or { assert err.msg() == 'path `${path}` already exists, and is not a folder', err.msg() return } assert false } fn test_disk_usage() { usage := os.disk_usage('.')! assert usage.total > 0 assert usage.available > 0 assert usage.used > 0 }