123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- local uv = vim.uv
- local t = require('test.unit.testutil')
- local itp = t.gen_itp(it)
- local eq = t.eq
- local ffi = t.ffi
- local cimport = t.cimport
- local cppimport = t.cppimport
- local mkdir = t.mkdir
- local m = cimport('./src/nvim/os/os.h', './src/nvim/os/fileio.h')
- cppimport('fcntl.h')
- local fcontents = ''
- for i = 0, 255 do
- fcontents = fcontents .. (i == 0 and '\0' or ('%c'):format(i))
- end
- fcontents = fcontents:rep(16)
- local dir = 'Xtest-unit-file_spec.d'
- local file1 = dir .. '/file1.dat'
- local file2 = dir .. '/file2.dat'
- local linkf = dir .. '/file.lnk'
- local linkb = dir .. '/broken.lnk'
- local filec = dir .. '/created-file.dat'
- before_each(function()
- mkdir(dir)
- local f1 = io.open(file1, 'w')
- f1:write(fcontents)
- f1:close()
- local f2 = io.open(file2, 'w')
- f2:write(fcontents)
- f2:close()
- uv.fs_symlink('file1.dat', linkf)
- uv.fs_symlink('broken.dat', linkb)
- end)
- after_each(function()
- os.remove(file1)
- os.remove(file2)
- os.remove(linkf)
- os.remove(linkb)
- os.remove(filec)
- uv.fs_rmdir(dir)
- end)
- local function file_open(fname, flags, mode)
- local ret2 = ffi.new('FileDescriptor')
- local ret1 = m.file_open(ret2, fname, flags, mode)
- return ret1, ret2
- end
- local function file_open_fd(fd, flags)
- local ret2 = ffi.new('FileDescriptor')
- local ret1 = m.file_open_fd(ret2, fd, flags)
- return ret1, ret2
- end
- local function file_write(fp, buf)
- return m.file_write(fp, buf, #buf)
- end
- local function file_read(fp, size)
- local buf = nil
- if size == nil then
- size = 0
- else
- -- For some reason if length of NUL-bytes-string is the same as `char[?]`
- -- size luajit garbage collector crashes. But it does not do so in
- -- os_read[v] tests in os/fs_spec.lua.
- buf = ffi.new('char[?]', size + 1, ('\0'):rep(size))
- end
- local ret1 = m.file_read(fp, buf, size)
- local ret2 = ''
- if buf ~= nil then
- ret2 = ffi.string(buf, size)
- end
- return ret1, ret2
- end
- local function file_flush(fp)
- return m.file_flush(fp)
- end
- local function file_fsync(fp)
- return m.file_fsync(fp)
- end
- local function file_skip(fp, size)
- return m.file_skip(fp, size)
- end
- describe('file_open_fd', function()
- itp('can use file descriptor returned by os_open for reading', function()
- local fd = m.os_open(file1, m.kO_RDONLY, 0)
- local err, fp = file_open_fd(fd, m.kFileReadOnly)
- eq(0, err)
- eq({ #fcontents, fcontents }, { file_read(fp, #fcontents) })
- eq(0, m.file_close(fp, false))
- end)
- itp('can use file descriptor returned by os_open for writing', function()
- eq(nil, uv.fs_stat(filec))
- local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
- local err, fp = file_open_fd(fd, m.kFileWriteOnly)
- eq(0, err)
- eq(4, file_write(fp, 'test'))
- eq(0, m.file_close(fp, false))
- eq(4, uv.fs_stat(filec).size)
- eq('test', io.open(filec):read('*a'))
- end)
- end)
- describe('file_open', function()
- itp('can create a rwx------ file with kFileCreate', function()
- local err, fp = file_open(filec, m.kFileCreate, 448)
- eq(0, err)
- local attrs = uv.fs_stat(filec)
- eq(33216, attrs.mode)
- eq(0, m.file_close(fp, false))
- end)
- itp('can create a rw------- file with kFileCreate', function()
- local err, fp = file_open(filec, m.kFileCreate, 384)
- eq(0, err)
- local attrs = uv.fs_stat(filec)
- eq(33152, attrs.mode)
- eq(0, m.file_close(fp, false))
- end)
- itp('can create a rwx------ file with kFileCreateOnly', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 448)
- eq(0, err)
- local attrs = uv.fs_stat(filec)
- eq(33216, attrs.mode)
- eq(0, m.file_close(fp, false))
- end)
- itp('can create a rw------- file with kFileCreateOnly', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 384)
- eq(0, err)
- local attrs = uv.fs_stat(filec)
- eq(33152, attrs.mode)
- eq(0, m.file_close(fp, false))
- end)
- itp('fails to open an existing file with kFileCreateOnly', function()
- local err, _ = file_open(file1, m.kFileCreateOnly, 384)
- eq(m.UV_EEXIST, err)
- end)
- itp('fails to open an symlink with kFileNoSymlink', function()
- local err, _ = file_open(linkf, m.kFileNoSymlink, 384)
- -- err is UV_EMLINK in FreeBSD, but if I use `ok(err == m.UV_ELOOP or err ==
- -- m.UV_EMLINK)`, then I loose the ability to see actual `err` value.
- if err ~= m.UV_ELOOP then
- eq(m.UV_EMLINK, err)
- end
- end)
- itp('can open an existing file write-only with kFileCreate', function()
- local err, fp = file_open(file1, m.kFileCreate, 384)
- eq(0, err)
- eq(true, fp.wr)
- eq(0, m.file_close(fp, false))
- end)
- itp('can open an existing file read-only with zero', function()
- local err, fp = file_open(file1, 0, 384)
- eq(0, err)
- eq(false, fp.wr)
- eq(0, m.file_close(fp, false))
- end)
- itp('can open an existing file read-only with kFileReadOnly', function()
- local err, fp = file_open(file1, m.kFileReadOnly, 384)
- eq(0, err)
- eq(false, fp.wr)
- eq(0, m.file_close(fp, false))
- end)
- itp('can open an existing file read-only with kFileNoSymlink', function()
- local err, fp = file_open(file1, m.kFileNoSymlink, 384)
- eq(0, err)
- eq(false, fp.wr)
- eq(0, m.file_close(fp, false))
- end)
- itp('can truncate an existing file with kFileTruncate', function()
- local err, fp = file_open(file1, m.kFileTruncate, 384)
- eq(0, err)
- eq(true, fp.wr)
- eq(0, m.file_close(fp, false))
- local attrs = uv.fs_stat(file1)
- eq(0, attrs.size)
- end)
- itp('can open an existing file write-only with kFileWriteOnly', function()
- local err, fp = file_open(file1, m.kFileWriteOnly, 384)
- eq(0, err)
- eq(true, fp.wr)
- eq(0, m.file_close(fp, false))
- local attrs = uv.fs_stat(file1)
- eq(4096, attrs.size)
- end)
- itp('fails to create a file with just kFileWriteOnly', function()
- local err, _ = file_open(filec, m.kFileWriteOnly, 384)
- eq(m.UV_ENOENT, err)
- local attrs = uv.fs_stat(filec)
- eq(nil, attrs)
- end)
- itp('can truncate an existing file with kFileTruncate when opening a symlink', function()
- local err, fp = file_open(linkf, m.kFileTruncate, 384)
- eq(0, err)
- eq(true, fp.wr)
- eq(0, m.file_close(fp, false))
- local attrs = uv.fs_stat(file1)
- eq(0, attrs.size)
- end)
- itp('fails to open a directory write-only', function()
- local err, _ = file_open(dir, m.kFileWriteOnly, 384)
- eq(m.UV_EISDIR, err)
- end)
- itp('fails to open a broken symbolic link write-only', function()
- local err, _ = file_open(linkb, m.kFileWriteOnly, 384)
- eq(m.UV_ENOENT, err)
- end)
- itp('fails to open a broken symbolic link read-only', function()
- local err, _ = file_open(linkb, m.kFileReadOnly, 384)
- eq(m.UV_ENOENT, err)
- end)
- end)
- describe('file_close', function()
- itp('can flush writes to disk also with true argument', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 384)
- eq(0, err)
- local wsize = file_write(fp, 'test')
- eq(4, wsize)
- eq(0, uv.fs_stat(filec).size)
- eq(0, m.file_close(fp, true))
- eq(wsize, uv.fs_stat(filec).size)
- end)
- end)
- describe('file_fsync', function()
- itp('can flush writes to disk', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 384)
- eq(0, file_fsync(fp))
- eq(0, err)
- eq(0, uv.fs_stat(filec).size)
- local wsize = file_write(fp, 'test')
- eq(4, wsize)
- eq(0, uv.fs_stat(filec).size)
- eq(0, file_fsync(fp))
- eq(wsize, uv.fs_stat(filec).size)
- eq(0, m.file_close(fp, false))
- end)
- end)
- describe('file_flush', function()
- itp('can flush writes to disk', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 384)
- eq(0, file_flush(fp))
- eq(0, err)
- eq(0, uv.fs_stat(filec).size)
- local wsize = file_write(fp, 'test')
- eq(4, wsize)
- eq(0, uv.fs_stat(filec).size)
- eq(0, file_flush(fp))
- eq(wsize, uv.fs_stat(filec).size)
- eq(0, m.file_close(fp, false))
- end)
- end)
- describe('file_read', function()
- itp('can read small chunks of input until eof', function()
- local err, fp = file_open(file1, 0, 384)
- eq(0, err)
- eq(false, fp.wr)
- local shift = 0
- while shift < #fcontents do
- local size = 3
- local exp_err = size
- local exp_s = fcontents:sub(shift + 1, shift + size)
- if shift + size >= #fcontents then
- exp_err = #fcontents - shift
- exp_s = (fcontents:sub(shift + 1, shift + size) .. (('\0'):rep(size - exp_err)))
- end
- eq({ exp_err, exp_s }, { file_read(fp, size) })
- shift = shift + size
- end
- eq(0, m.file_close(fp, false))
- end)
- itp('can read the whole file at once', function()
- local err, fp = file_open(file1, 0, 384)
- eq(0, err)
- eq(false, fp.wr)
- eq({ #fcontents, fcontents }, { file_read(fp, #fcontents) })
- eq({ 0, ('\0'):rep(#fcontents) }, { file_read(fp, #fcontents) })
- eq(0, m.file_close(fp, false))
- end)
- itp('can read more then 1024 bytes after reading a small chunk', function()
- local err, fp = file_open(file1, 0, 384)
- eq(0, err)
- eq(false, fp.wr)
- eq({ 5, fcontents:sub(1, 5) }, { file_read(fp, 5) })
- eq({ #fcontents - 5, fcontents:sub(6) .. (('\0'):rep(5)) }, { file_read(fp, #fcontents) })
- eq(0, m.file_close(fp, false))
- end)
- itp('can read file by 768-byte-chunks', function()
- local err, fp = file_open(file1, 0, 384)
- eq(0, err)
- eq(false, fp.wr)
- local shift = 0
- while shift < #fcontents do
- local size = 768
- local exp_err = size
- local exp_s = fcontents:sub(shift + 1, shift + size)
- if shift + size >= #fcontents then
- exp_err = #fcontents - shift
- exp_s = (fcontents:sub(shift + 1, shift + size) .. (('\0'):rep(size - exp_err)))
- end
- eq({ exp_err, exp_s }, { file_read(fp, size) })
- shift = shift + size
- end
- eq(0, m.file_close(fp, false))
- end)
- end)
- describe('file_write', function()
- itp('can write the whole file at once', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 384)
- eq(0, err)
- eq(true, fp.wr)
- local wr = file_write(fp, fcontents)
- eq(#fcontents, wr)
- eq(0, m.file_close(fp, false))
- eq(wr, uv.fs_stat(filec).size)
- eq(fcontents, io.open(filec):read('*a'))
- end)
- itp('can write the whole file by small chunks', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 384)
- eq(0, err)
- eq(true, fp.wr)
- local shift = 0
- while shift < #fcontents do
- local size = 3
- local s = fcontents:sub(shift + 1, shift + size)
- local wr = file_write(fp, s)
- eq(wr, #s)
- shift = shift + size
- end
- eq(0, m.file_close(fp, false))
- eq(#fcontents, uv.fs_stat(filec).size)
- eq(fcontents, io.open(filec):read('*a'))
- end)
- itp('can write the whole file by 768-byte-chunks', function()
- local err, fp = file_open(filec, m.kFileCreateOnly, 384)
- eq(0, err)
- eq(true, fp.wr)
- local shift = 0
- while shift < #fcontents do
- local size = 768
- local s = fcontents:sub(shift + 1, shift + size)
- local wr = file_write(fp, s)
- eq(wr, #s)
- shift = shift + size
- end
- eq(0, m.file_close(fp, false))
- eq(#fcontents, uv.fs_stat(filec).size)
- eq(fcontents, io.open(filec):read('*a'))
- end)
- end)
- describe('file_skip', function()
- itp('can skip 3 bytes', function()
- local err, fp = file_open(file1, 0, 384)
- eq(0, err)
- eq(false, fp.wr)
- eq(3, file_skip(fp, 3))
- local rd, s = file_read(fp, 3)
- eq(3, rd)
- eq(fcontents:sub(4, 6), s)
- eq(0, m.file_close(fp, false))
- end)
- end)
|