mirror of
https://github.com/neovim/neovim.git
synced 2025-09-08 04:18:18 +00:00

test: replace lfs with luv luv already pretty much does everything lfs does, so this duplication of dependencies isn't needed.
479 lines
14 KiB
Lua
479 lines
14 KiB
Lua
local luv = require('luv')
|
|
|
|
local helpers = require('test.unit.helpers')(after_each)
|
|
local itp = helpers.gen_itp(it)
|
|
|
|
local eq = helpers.eq
|
|
local ffi = helpers.ffi
|
|
local cimport = helpers.cimport
|
|
local cppimport = helpers.cppimport
|
|
local mkdir = helpers.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()
|
|
|
|
luv.fs_symlink('file1.dat', linkf)
|
|
luv.fs_symlink('broken.dat', linkb)
|
|
end)
|
|
|
|
after_each(function()
|
|
os.remove(file1)
|
|
os.remove(file2)
|
|
os.remove(linkf)
|
|
os.remove(linkb)
|
|
os.remove(filec)
|
|
luv.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_new(fname, flags, mode)
|
|
local ret1 = ffi.new('int[?]', 1, {0})
|
|
local ret2 = ffi.gc(m.file_open_new(ret1, fname, flags, mode), nil)
|
|
return ret1[0], 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_open_fd_new(fd, flags)
|
|
local ret1 = ffi.new('int[?]', 1, {0})
|
|
local ret2 = ffi.gc(m.file_open_fd_new(ret1, fd, flags), nil)
|
|
return ret1[0], ret2
|
|
end
|
|
|
|
local function file_write(fp, buf)
|
|
return m.file_write(fp, buf, #buf)
|
|
end
|
|
|
|
local function msgpack_file_write(fp, buf)
|
|
return m.msgpack_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, luv.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, luv.fs_stat(filec).size)
|
|
eq('test', io.open(filec):read('*a'))
|
|
end)
|
|
end)
|
|
|
|
describe('file_open_fd_new', 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_new(fd, m.kFileReadOnly)
|
|
eq(0, err)
|
|
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
|
eq(0, m.file_free(fp, false))
|
|
end)
|
|
itp('can use file descriptor returned by os_open for writing', function()
|
|
eq(nil, luv.fs_stat(filec))
|
|
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
|
|
local err, fp = file_open_fd_new(fd, m.kFileWriteOnly)
|
|
eq(0, err)
|
|
eq(4, file_write(fp, 'test'))
|
|
eq(0, m.file_free(fp, false))
|
|
eq(4, luv.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 = luv.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 = luv.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 = luv.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 = luv.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 = luv.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 = luv.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 = luv.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 = luv.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_open_new', function()
|
|
itp('can open a file read-only', function()
|
|
local err, fp = file_open_new(file1, 0, 384)
|
|
eq(0, err)
|
|
eq(false, fp.wr)
|
|
eq(0, m.file_free(fp, false))
|
|
end)
|
|
|
|
itp('fails to open an existing file with kFileCreateOnly', function()
|
|
local err, fp = file_open_new(file1, m.kFileCreateOnly, 384)
|
|
eq(m.UV_EEXIST, err)
|
|
eq(nil, fp)
|
|
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, luv.fs_stat(filec).size)
|
|
eq(0, m.file_close(fp, true))
|
|
eq(wsize, luv.fs_stat(filec).size)
|
|
end)
|
|
end)
|
|
|
|
describe('file_free', function()
|
|
itp('can flush writes to disk also with true argument', function()
|
|
local err, fp = file_open_new(filec, m.kFileCreateOnly, 384)
|
|
eq(0, err)
|
|
local wsize = file_write(fp, 'test')
|
|
eq(4, wsize)
|
|
eq(0, luv.fs_stat(filec).size)
|
|
eq(0, m.file_free(fp, true))
|
|
eq(wsize, luv.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, luv.fs_stat(filec).size)
|
|
local wsize = file_write(fp, 'test')
|
|
eq(4, wsize)
|
|
eq(0, luv.fs_stat(filec).size)
|
|
eq(0, file_fsync(fp))
|
|
eq(wsize, luv.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, luv.fs_stat(filec).size)
|
|
local wsize = file_write(fp, 'test')
|
|
eq(4, wsize)
|
|
eq(0, luv.fs_stat(filec).size)
|
|
eq(0, file_flush(fp))
|
|
eq(wsize, luv.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, luv.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, luv.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, luv.fs_stat(filec).size)
|
|
eq(fcontents, io.open(filec):read('*a'))
|
|
end)
|
|
end)
|
|
|
|
describe('msgpack_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 = msgpack_file_write(fp, fcontents)
|
|
eq(0, wr)
|
|
eq(0, m.file_close(fp, false))
|
|
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)
|