unittests: Do not import libnvim or headers in main process

Slows down unit tests much, but gets rid of as much preserved state as possible.
This commit is contained in:
ZyX 2017-03-11 16:02:47 +03:00
parent 9400466282
commit e2a578f40d
12 changed files with 361 additions and 313 deletions

View File

@ -19,7 +19,8 @@ local api = cimport('./src/nvim/api/private/defs.h',
local obj2lua local obj2lua
local obj2lua_tab = { obj2lua = function(obj)
local obj2lua_tab = {
[tonumber(api.kObjectTypeArray)] = function(obj) [tonumber(api.kObjectTypeArray)] = function(obj)
local ret = {[type_key]=list_type} local ret = {[type_key]=list_type}
for i = 1,tonumber(obj.data.array.size) do for i = 1,tonumber(obj.data.array.size) do
@ -57,9 +58,7 @@ local obj2lua_tab = {
[tonumber(api.kObjectTypeString)] = function(obj) [tonumber(api.kObjectTypeString)] = function(obj)
return ffi.string(obj.data.string.data, obj.data.string.size) return ffi.string(obj.data.string.data, obj.data.string.size)
end, end,
} }
obj2lua = function(obj)
return ((obj2lua_tab[tonumber(obj['type'])] or function(obj_inner) return ((obj2lua_tab[tonumber(obj['type'])] or function(obj_inner)
assert(false, 'Converting ' .. tostring(tonumber(obj_inner['type'])) .. ' is not implementing yet') assert(false, 'Converting ' .. tostring(tonumber(obj_inner['type'])) .. ' is not implementing yet')
end)(obj)) end)(obj))

View File

@ -12,20 +12,6 @@ local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval_defs.h',
'./src/nvim/message.h') './src/nvim/message.h')
describe('json_decode_string()', function() describe('json_decode_string()', function()
local saved_p_enc = nil
before_each(function()
saved_p_enc = decode.p_enc
end)
after_each(function()
decode.emsg_silent = 0
decode.p_enc = saved_p_enc
while decode.delete_first_msg() == 1 do
-- Delete all messages
end
end)
local char = function(c) local char = function(c)
return ffi.gc(decode.xmemdup(c, 1), decode.xfree) return ffi.gc(decode.xmemdup(c, 1), decode.xfree)
end end

View File

@ -46,12 +46,6 @@ local function list(...)
return ret return ret
end end
local special_tab = {
[eval.kSpecialVarFalse] = false,
[eval.kSpecialVarNull] = nil_value,
[eval.kSpecialVarTrue] = true,
}
local ptr2key = function(ptr) local ptr2key = function(ptr)
return tostring(ptr) return tostring(ptr)
end end
@ -60,11 +54,19 @@ local lst2tbl
local dct2tbl local dct2tbl
local typvalt2lua local typvalt2lua
local typvalt2lua_tab local typvalt2lua_tab = nil
typvalt2lua_tab = { local function typvalt2lua_tab_init()
if typvalt2lua_tab then
return
end
typvalt2lua_tab = {
[tonumber(eval.VAR_SPECIAL)] = function(t) [tonumber(eval.VAR_SPECIAL)] = function(t)
return special_tab[t.vval.v_special] return ({
[eval.kSpecialVarFalse] = false,
[eval.kSpecialVarNull] = nil_value,
[eval.kSpecialVarTrue] = true,
})[t.vval.v_special]
end, end,
[tonumber(eval.VAR_NUMBER)] = function(t) [tonumber(eval.VAR_NUMBER)] = function(t)
return {[type_key]=int_type, value=tonumber(t.vval.v_number)} return {[type_key]=int_type, value=tonumber(t.vval.v_number)}
@ -115,9 +117,11 @@ typvalt2lua_tab = {
dict=dict, dict=dict,
} }
end, end,
} }
end
typvalt2lua = function(t, processed) typvalt2lua = function(t, processed)
typvalt2lua_tab_init()
return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner) return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner)
assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet') assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet')
end)(t, processed or {})) end)(t, processed or {}))
@ -169,9 +173,10 @@ lst2tbl = function(l, processed)
return ret return ret
end end
local hi_key_removed = eval._hash_key_removed() local hi_key_removed = nil
local function dict_iter(d, return_hi) local function dict_iter(d, return_hi)
hi_key_removed = hi_key_removed or eval._hash_key_removed()
local init_s = { local init_s = {
todo=d.dv_hashtab.ht_used, todo=d.dv_hashtab.ht_used,
hi=d.dv_hashtab.ht_array, hi=d.dv_hashtab.ht_array,
@ -320,25 +325,28 @@ local lua2typvalt_type_tab = {
end, end,
} }
local special_vals = { local special_vals = nil
[null_string] = {eval.VAR_STRING, {v_string=ffi.cast('char_u*', nil)}},
[null_list] = {eval.VAR_LIST, {v_list=ffi.cast('list_T*', nil)}},
[null_dict] = {eval.VAR_DICT, {v_dict=ffi.cast('dict_T*', nil)}},
[nil_value] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull}},
[true] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarTrue}},
[false] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarFalse}},
}
for k, v in pairs(special_vals) do lua2typvalt = function(l, processed)
if not special_vals then
special_vals = {
[null_string] = {'VAR_STRING', {v_string=ffi.cast('char_u*', nil)}},
[null_list] = {'VAR_LIST', {v_list=ffi.cast('list_T*', nil)}},
[null_dict] = {'VAR_DICT', {v_dict=ffi.cast('dict_T*', nil)}},
[nil_value] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarNull}},
[true] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarTrue}},
[false] = {'VAR_SPECIAL', {v_special=eval.kSpecialVarFalse}},
}
for k, v in pairs(special_vals) do
local tmp = function(typ, vval) local tmp = function(typ, vval)
special_vals[k] = function() special_vals[k] = function()
return typvalt(typ, vval) return typvalt(eval[typ], vval)
end end
end end
tmp(v[1], v[2]) tmp(v[1], v[2])
end end
end
lua2typvalt = function(l, processed)
processed = processed or {} processed = processed or {}
if l == nil or l == nil_value then if l == nil or l == nil_value then
return special_vals[nil_value]() return special_vals[nil_value]()
@ -360,7 +368,7 @@ lua2typvalt = function(l, processed)
return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)}) return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)})
elseif type(l) == 'cdata' then elseif type(l) == 'cdata' then
local tv = typvalt(eval.VAR_UNKNOWN) local tv = typvalt(eval.VAR_UNKNOWN)
eval.tv_copy(l, tv) eval.copy_tv(l, tv)
return tv return tv
end end
end end

View File

@ -9,7 +9,7 @@ local ffi = helpers.ffi
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local NULL = helpers.NULL local NULL = helpers.NULL
local garray = cimport('stdlib.h', './src/nvim/garray.h') local garray = cimport('./src/nvim/garray.h')
local itemsize = 14 local itemsize = 14
local growsize = 95 local growsize = 95
@ -157,7 +157,7 @@ local ga_append_ints = function(garr, ...)
end end
-- enhanced constructors -- enhanced constructors
local garray_ctype = ffi.typeof('garray_T[1]') local garray_ctype = function(...) return ffi.typeof('garray_T[1]')(...) end
local new_garray = function() local new_garray = function()
local garr = garray_ctype() local garr = garray_ctype()
return ffi.gc(garr, ga_clear) return ffi.gc(garr, ga_clear)

View File

@ -13,6 +13,7 @@ local syscall = nil
local check_cores = global_helpers.check_cores local check_cores = global_helpers.check_cores
local which = global_helpers.which local which = global_helpers.which
local neq = global_helpers.neq local neq = global_helpers.neq
local map = global_helpers.map
local eq = global_helpers.eq local eq = global_helpers.eq
local ok = global_helpers.ok local ok = global_helpers.ok
@ -22,13 +23,82 @@ local NULL = ffi.cast('void*', 0)
local OK = 1 local OK = 1
local FAIL = 0 local FAIL = 0
local cimport
-- add some standard header locations -- add some standard header locations
for _, p in ipairs(Paths.include_paths) do for _, p in ipairs(Paths.include_paths) do
Preprocess.add_to_include_path(p) Preprocess.add_to_include_path(p)
end end
-- load neovim shared library local pid = nil
local libnvim = ffi.load(Paths.test_libnvim_path) local function only_separate(func)
return function(...)
if pid ~= 0 then
eq(0, 'This function must be run in a separate process only')
end
return func(...)
end
end
local deferred_calls_init = {}
local deferred_calls_mod = nil
local function deferred_call(func, ret)
return function(...)
local deferred_calls = deferred_calls_mod or deferred_calls_init
if pid ~= 0 then
deferred_calls[#deferred_calls + 1] = {func=func, args={...}}
return ret
else
return func(...)
end
end
end
local separate_cleanups_mod = nil
local function separate_cleanup(func)
return function(...)
local separate_cleanups = separate_cleanups_mod
if pid ~= 0 then
separate_cleanups[#separate_cleanups + 1] = {args={...}}
else
func(...)
end
end
end
local libnvim = nil
local lib = setmetatable({}, {
__index = only_separate(function(tbl, idx)
return libnvim[idx]
end),
__newindex = deferred_call(function(tbl, idx, val)
libnvim[idx] = val
end),
})
local init = only_separate(function()
-- load neovim shared library
libnvim = ffi.load(Paths.test_libnvim_path)
for _, c in ipairs(deferred_calls_init) do
c.func(unpack(c.args))
end
libnvim.time_init()
libnvim.early_init()
libnvim.event_init()
if deferred_calls_mod then
for _, c in ipairs(deferred_calls_mod) do
c.func(unpack(c.args))
end
end
end)
local deinit = only_separate(function()
if separate_cleanups_mod then
for _, c in ipairs(separate_cleanups_mod) do
c.func(unpack(c.args))
end
end
end)
local function trim(s) local function trim(s)
return s:match('^%s*(.*%S)') or '' return s:match('^%s*(.*%S)') or ''
@ -58,40 +128,37 @@ local function filter_complex_blocks(body)
return table.concat(result, "\n") return table.concat(result, "\n")
end end
local previous_defines = ''
local cdef = ffi.cdef local cdef = ffi.cdef
local cimportstr local cimportstr
local previous_defines_init = ''
local preprocess_cache_init = {}
local previous_defines_mod = ''
local preprocess_cache_mod = nil
-- use this helper to import C files, you can pass multiple paths at once, -- use this helper to import C files, you can pass multiple paths at once,
-- this helper will return the C namespace of the nvim library. -- this helper will return the C namespace of the nvim library.
local function cimport(...) cimport = function(...)
local paths = {} local previous_defines, preprocess_cache
local args = {...} if preprocess_cache_mod then
preprocess_cache = preprocess_cache_mod
-- filter out paths we've already imported previous_defines = previous_defines_mod
for _,path in pairs(args) do else
if path ~= nil and not imported:contains(path) then preprocess_cache = preprocess_cache_init
paths[#paths + 1] = path previous_defines = previous_defines_init
end end
for _, path in ipairs({...}) do
if not (path:sub(1, 1) == '/' or path:sub(1, 1) == '.'
or path:sub(2, 2) == ':') then
path = './' .. path
end end
for _,path in pairs(paths) do
imported:add(path)
end
if #paths == 0 then
return libnvim
end
local body local body
body, previous_defines = Preprocess.preprocess(previous_defines, unpack(paths)) if preprocess_cache[path] then
body = preprocess_cache[path]
return cimportstr(body) else
end body, previous_defines = Preprocess.preprocess(previous_defines, path)
cimportstr = function(body)
-- format it (so that the lines are "unique" statements), also filter out -- format it (so that the lines are "unique" statements), also filter out
-- Objective-C blocks -- Objective-C blocks
if os.getenv('NVIM_TEST_PRINT_I') == '1' then if os.getenv('NVIM_TEST_PRINT_I') == '1' then
@ -103,7 +170,32 @@ cimportstr = function(body)
end end
body = formatc(body) body = formatc(body)
body = filter_complex_blocks(body) body = filter_complex_blocks(body)
preprocess_cache[path] = body
end
cimportstr(preprocess_cache, path)
end
return lib
end
local cimport_immediate = function(...)
local saved_pid = pid
pid = 0
local err, emsg = pcall(cimport, ...)
pid = saved_pid
if not err then
emsg = tostring(emsg)
io.stderr:write(emsg .. '\n')
assert(false)
else
return lib
end
end
cimportstr = deferred_call(function(preprocess_cache, path)
if imported:contains(path) then
return lib
end
local body = preprocess_cache[path]
-- add the formatted lines to a set -- add the formatted lines to a set
local new_cdefs = Set:new() local new_cdefs = Set:new()
for line in body:gmatch("[^\r\n]+") do for line in body:gmatch("[^\r\n]+") do
@ -126,7 +218,7 @@ cimportstr = function(body)
if new_cdefs:size() == 0 then if new_cdefs:size() == 0 then
-- if there's no new lines, just return -- if there's no new lines, just return
return libnvim return lib
end end
-- request a sorted version of the new lines (same relative order as the -- request a sorted version of the new lines (same relative order as the
@ -138,13 +230,10 @@ cimportstr = function(body)
end end
end end
cdef(table.concat(new_lines, "\n")) cdef(table.concat(new_lines, "\n"))
imported:add(path)
return libnvim return lib
end end, lib)
local function cppimport(path)
return cimport(Paths.test_include_path .. '/' .. path)
end
local function alloc_log_new() local function alloc_log_new()
local log = { local log = {
@ -156,9 +245,12 @@ local function alloc_log_new()
local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'} local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'}
function log:save_original_functions() function log:save_original_functions()
for _, funcname in ipairs(allocator_functions) do for _, funcname in ipairs(allocator_functions) do
if not self.original_functions[funcname] then
self.original_functions[funcname] = self.lib['mem_' .. funcname] self.original_functions[funcname] = self.lib['mem_' .. funcname]
end end
end end
end
log.save_original_functions = deferred_call(log.save_original_functions)
function log:set_mocks() function log:set_mocks()
for _, k in ipairs(allocator_functions) do for _, k in ipairs(allocator_functions) do
do do
@ -185,6 +277,7 @@ local function alloc_log_new()
end end
end end
end end
log.set_mocks = deferred_call(log.set_mocks)
function log:clear() function log:clear()
self.log = {} self.log = {}
end end
@ -193,22 +286,28 @@ local function alloc_log_new()
self:clear() self:clear()
end end
function log:restore_original_functions() function log:restore_original_functions()
for k, v in pairs(self.original_functions) do -- Do nothing: set mocks live in a separate process
self.lib['mem_' .. k] = v return
--[[
[ for k, v in pairs(self.original_functions) do
[ self.lib['mem_' .. k] = v
[ end
]]
end end
end function log:setup()
function log:before_each()
log:save_original_functions() log:save_original_functions()
log:set_mocks() log:set_mocks()
end end
function log:before_each()
return
end
function log:after_each() function log:after_each()
log:restore_original_functions() log:restore_original_functions()
end end
log:setup()
return log return log
end end
cimport('./src/nvim/types.h')
-- take a pointer to a C-allocated string and return an interned -- take a pointer to a C-allocated string and return an interned
-- version while also freeing the memory -- version while also freeing the memory
local function internalize(cdata, len) local function internalize(cdata, len)
@ -221,16 +320,6 @@ local function to_cstr(string)
return cstr(#string + 1, string) return cstr(#string + 1, string)
end end
-- initialize some global variables, this is still necessary to unit test
-- functions that rely on global state.
do
local main = cimport('./src/nvim/main.h')
local time = cimport('./src/nvim/os/time.h')
time.time_init()
main.early_init()
main.event_init()
end
local sc local sc
if posix ~= nil then if posix ~= nil then
@ -263,7 +352,7 @@ elseif syscall ~= nil then
exit = syscall.exit, exit = syscall.exit,
} }
else else
cimport('./test/unit/fixtures/posix.h') cimport_immediate('./test/unit/fixtures/posix.h')
sc = { sc = {
fork = function() fork = function()
return tonumber(ffi.C.fork()) return tonumber(ffi.C.fork())
@ -291,7 +380,7 @@ else
len - total_bytes_read)) len - total_bytes_read))
if bytes_read == -1 then if bytes_read == -1 then
local err = ffi.errno(0) local err = ffi.errno(0)
if err ~= libnvim.kPOSIXErrnoEINTR then if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("read() error: %u: %s"):format( assert(false, ("read() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err)))) err, ffi.string(ffi.C.strerror(err))))
end end
@ -314,7 +403,7 @@ else
#s - total_bytes_written)) #s - total_bytes_written))
if bytes_written == -1 then if bytes_written == -1 then
local err = ffi.errno(0) local err = ffi.errno(0)
if err ~= libnvim.kPOSIXErrnoEINTR then if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("write() error: %u: %s"):format( assert(false, ("write() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err)))) err, ffi.string(ffi.C.strerror(err))))
end end
@ -330,12 +419,12 @@ else
wait = function(pid) wait = function(pid)
ffi.errno(0) ffi.errno(0)
while true do while true do
local r = ffi.C.waitpid(pid, nil, libnvim.kPOSIXWaitWUNTRACED) local r = ffi.C.waitpid(pid, nil, ffi.C.kPOSIXWaitWUNTRACED)
if r == -1 then if r == -1 then
local err = ffi.errno(0) local err = ffi.errno(0)
if err == libnvim.kPOSIXErrnoECHILD then if err == ffi.C.kPOSIXErrnoECHILD then
break break
elseif err ~= libnvim.kPOSIXErrnoEINTR then elseif err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("waitpid() error: %u: %s"):format( assert(false, ("waitpid() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err)))) err, ffi.string(ffi.C.strerror(err))))
end end
@ -371,6 +460,10 @@ if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then
end end
local function gen_itp(it) local function gen_itp(it)
deferred_calls_mod = {}
deferred_cleanups_mod = {}
preprocess_cache_mod = map(function(v) return v end, preprocess_cache_init)
previous_defines_mod = previous_defines_init
local function just_fail(_) local function just_fail(_)
return false return false
end end
@ -386,8 +479,9 @@ local function gen_itp(it)
end end
it(name, function() it(name, function()
local rd, wr = sc.pipe() local rd, wr = sc.pipe()
local pid = sc.fork() pid = sc.fork()
if pid == 0 then if pid == 0 then
init()
sc.close(rd) sc.close(rd)
collectgarbage('stop') collectgarbage('stop')
local err, emsg = pcall(func) local err, emsg = pcall(func)
@ -395,16 +489,19 @@ local function gen_itp(it)
emsg = tostring(emsg) emsg = tostring(emsg)
if not err then if not err then
sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg)) sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
deinit()
sc.close(wr) sc.close(wr)
sc.exit(1) sc.exit(1)
else else
sc.write(wr, '+\n') sc.write(wr, '+\n')
deinit()
sc.close(wr) sc.close(wr)
sc.exit(0) sc.exit(0)
end end
else else
sc.close(wr) sc.close(wr)
sc.wait(pid) sc.wait(pid)
pid = nil
local function check() local function check()
local res = sc.read(rd, 2) local res = sc.read(rd, 2)
eq(2, #res) eq(2, #res)
@ -433,6 +530,12 @@ local function gen_itp(it)
return itp return itp
end end
local function cppimport(path)
return cimport(Paths.test_include_path .. '/' .. path)
end
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
local module = { local module = {
cimport = cimport, cimport = cimport,
cppimport = cppimport, cppimport = cppimport,
@ -441,7 +544,7 @@ local module = {
eq = eq, eq = eq,
neq = neq, neq = neq,
ffi = ffi, ffi = ffi,
lib = libnvim, lib = lib,
cstr = cstr, cstr = cstr,
to_cstr = to_cstr, to_cstr = to_cstr,
NULL = NULL, NULL = NULL,
@ -449,6 +552,9 @@ local module = {
FAIL = FAIL, FAIL = FAIL,
alloc_log_new = alloc_log_new, alloc_log_new = alloc_log_new,
gen_itp = gen_itp, gen_itp = gen_itp,
only_separate = only_separate,
deferred_call = deferred_call,
separate_cleanup = separate_cleanup,
} }
return function(after_each) return function(after_each)
if after_each then if after_each then

View File

@ -1,10 +1,12 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local deferred_call = helpers.deferred_call
local cimport = helpers.cimport
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local multiqueue = helpers.cimport("./test/unit/fixtures/multiqueue.h") local multiqueue = cimport("./test/unit/fixtures/multiqueue.h")
describe("multiqueue (multi-level event-queue)", function() describe("multiqueue (multi-level event-queue)", function()
local parent, child1, child2, child3 local parent, child1, child2, child3
@ -21,7 +23,7 @@ describe("multiqueue (multi-level event-queue)", function()
multiqueue.multiqueue_free(q) multiqueue.multiqueue_free(q)
end end
before_each(function() before_each(deferred_call(function()
parent = multiqueue.multiqueue_new_parent(ffi.NULL, ffi.NULL) parent = multiqueue.multiqueue_new_parent(ffi.NULL, ffi.NULL)
child1 = multiqueue.multiqueue_new_child(parent) child1 = multiqueue.multiqueue_new_child(parent)
child2 = multiqueue.multiqueue_new_child(parent) child2 = multiqueue.multiqueue_new_child(parent)
@ -35,7 +37,7 @@ describe("multiqueue (multi-level event-queue)", function()
put(child2, 'c2i4') put(child2, 'c2i4')
put(child3, 'c3i1') put(child3, 'c3i1')
put(child3, 'c3i2') put(child3, 'c3i2')
end) end))
itp('keeps count of added events', function() itp('keeps count of added events', function()
eq(3, multiqueue.multiqueue_size(child1)) eq(3, multiqueue.multiqueue_size(child1))

View File

@ -16,10 +16,10 @@ local to_cstr = helpers.to_cstr
local OK = helpers.OK local OK = helpers.OK
local FAIL = helpers.FAIL local FAIL = helpers.FAIL
local NULL = helpers.NULL local NULL = helpers.NULL
local NODE_NORMAL = 0 local NODE_NORMAL = 0
local NODE_WRITABLE = 1 local NODE_WRITABLE = 1
cimport('unistd.h')
cimport('./src/nvim/os/shell.h') cimport('./src/nvim/os/shell.h')
cimport('./src/nvim/option_defs.h') cimport('./src/nvim/option_defs.h')
cimport('./src/nvim/main.h') cimport('./src/nvim/main.h')
@ -66,13 +66,10 @@ local function os_getperm(filename)
end end
describe('fs function', function() describe('fs function', function()
local orig_test_file_perm before_each(function()
setup(function()
lfs.mkdir('unit-test-directory'); lfs.mkdir('unit-test-directory');
io.open('unit-test-directory/test.file', 'w').close() io.open('unit-test-directory/test.file', 'w').close()
orig_test_file_perm = os_getperm('unit-test-directory/test.file')
io.open('unit-test-directory/test_2.file', 'w').close() io.open('unit-test-directory/test_2.file', 'w').close()
lfs.link('test.file', 'unit-test-directory/test_link.file', true) lfs.link('test.file', 'unit-test-directory/test_link.file', true)
@ -84,7 +81,7 @@ describe('fs function', function()
directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$') directory, executable_name = string.match(absolute_executable, '^(.*)/(.*)$')
end) end)
teardown(function() after_each(function()
os.remove('unit-test-directory/test.file') os.remove('unit-test-directory/test.file')
os.remove('unit-test-directory/test_2.file') os.remove('unit-test-directory/test_2.file')
os.remove('unit-test-directory/test_link.file') os.remove('unit-test-directory/test_link.file')
@ -217,10 +214,6 @@ describe('fs function', function()
end) end)
describe('file permissions', function() describe('file permissions', function()
before_each(function()
os_setperm('unit-test-directory/test.file', orig_test_file_perm)
end)
local function os_fchown(filename, user_id, group_id) local function os_fchown(filename, user_id, group_id)
local fd = ffi.C.open(filename, 0) local fd = ffi.C.open(filename, 0)
local res = fs.os_fchown(fd, user_id, group_id) local res = fs.os_fchown(fd, user_id, group_id)

View File

@ -11,6 +11,7 @@ local ffi, eq = helpers.ffi, helpers.eq
local intern = helpers.internalize local intern = helpers.internalize
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local NULL = ffi.cast('void *', 0) local NULL = ffi.cast('void *', 0)
local deferred_call = deferred_call
describe('shell functions', function() describe('shell functions', function()
before_each(function() before_each(function()
@ -73,25 +74,13 @@ describe('shell functions', function()
eq(0, status) eq(0, status)
end) end)
it ('returns non-zero exit code', function() itp('returns non-zero exit code', function()
local status = os_system('exit 2') local status = os_system('exit 2')
eq(2, status) eq(2, status)
end) end)
end) end)
describe('shell_build_argv', function() describe('shell_build_argv', function()
local saved_opts = {}
setup(function()
saved_opts.p_sh = cimported.p_sh
saved_opts.p_shcf = cimported.p_shcf
end)
teardown(function()
cimported.p_sh = saved_opts.p_sh
cimported.p_shcf = saved_opts.p_shcf
end)
itp('works with NULL arguments', function() itp('works with NULL arguments', function()
eq({'/bin/bash'}, shell_build_argv(nil, nil)) eq({'/bin/bash'}, shell_build_argv(nil, nil))
end) end)

View File

@ -15,13 +15,6 @@ local FAIL = helpers.FAIL
cimport('string.h') cimport('string.h')
local path = cimport('./src/nvim/path.h') local path = cimport('./src/nvim/path.h')
-- import constants parsed by ffi
local kEqualFiles = path.kEqualFiles
local kDifferentFiles = path.kDifferentFiles
local kBothFilesMissing = path.kBothFilesMissing
local kOneFileMissing = path.kOneFileMissing
local kEqualFileNames = path.kEqualFileNames
local length = 0 local length = 0
local buffer = nil local buffer = nil
@ -93,25 +86,25 @@ describe('path function', function()
end) end)
itp('returns kEqualFiles when passed the same file', function() itp('returns kEqualFiles when passed the same file', function()
eq(kEqualFiles, (path_full_compare(f1, f1))) eq(path.kEqualFiles, (path_full_compare(f1, f1)))
end) end)
itp('returns kEqualFileNames when files that dont exist and have same name', function() itp('returns kEqualFileNames when files that dont exist and have same name', function()
eq(kEqualFileNames, (path_full_compare('null.txt', 'null.txt', true))) eq(path.kEqualFileNames, (path_full_compare('null.txt', 'null.txt', true)))
end) end)
itp('returns kBothFilesMissing when files that dont exist', function() itp('returns kBothFilesMissing when files that dont exist', function()
eq(kBothFilesMissing, (path_full_compare('null.txt', 'null.txt'))) eq(path.kBothFilesMissing, (path_full_compare('null.txt', 'null.txt')))
end) end)
itp('returns kDifferentFiles when passed different files', function() itp('returns kDifferentFiles when passed different files', function()
eq(kDifferentFiles, (path_full_compare(f1, f2))) eq(path.kDifferentFiles, (path_full_compare(f1, f2)))
eq(kDifferentFiles, (path_full_compare(f2, f1))) eq(path.kDifferentFiles, (path_full_compare(f2, f1)))
end) end)
itp('returns kOneFileMissing if only one does not exist', function() itp('returns kOneFileMissing if only one does not exist', function()
eq(kOneFileMissing, (path_full_compare(f1, 'null.txt'))) eq(path.kOneFileMissing, (path_full_compare(f1, 'null.txt')))
eq(kOneFileMissing, (path_full_compare('null.txt', f1))) eq(path.kOneFileMissing, (path_full_compare('null.txt', f1)))
end) end)
end) end)

View File

@ -1,11 +1,13 @@
local helpers = require 'test.unit.helpers' local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local prof = helpers.cimport './src/nvim/profile.h' local cimport = helpers.cimport
local ffi = helpers.ffi local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local neq = helpers.neq local neq = helpers.neq
local prof = cimport('./src/nvim/profile.h')
local function split(inputstr, sep) local function split(inputstr, sep)
if sep == nil then if sep == nil then
sep = "%s" sep = "%s"

View File

@ -1,10 +1,11 @@
local helpers = require("test.unit.helpers")(after_each) local helpers = require("test.unit.helpers")(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local ffi = helpers.ffi
local cstr = helpers.cstr local cstr = helpers.cstr
local to_cstr = helpers.to_cstr local to_cstr = helpers.to_cstr
local deferred_call = helpers.deferred_call
local rbuffer = helpers.cimport("./test/unit/fixtures/rbuffer.h") local rbuffer = helpers.cimport("./test/unit/fixtures/rbuffer.h")
@ -31,11 +32,11 @@ describe('rbuffer functions', function()
return ffi.string(rbuffer.rbuffer_get(rbuf, idx), 1) return ffi.string(rbuffer.rbuffer_get(rbuf, idx), 1)
end end
before_each(function() before_each(deferred_call(function()
rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free) rbuf = ffi.gc(rbuffer.rbuffer_new(capacity), rbuffer.rbuffer_free)
-- fill the internal buffer with the character '0' to simplify inspecting -- fill the internal buffer with the character '0' to simplify inspecting
ffi.C.memset(rbuf.start_ptr, string.byte('0'), capacity) ffi.C.memset(rbuf.start_ptr, string.byte('0'), capacity)
end) end))
describe('RBUFFER_UNTIL_FULL', function() describe('RBUFFER_UNTIL_FULL', function()
local chunks local chunks
@ -58,59 +59,44 @@ describe('rbuffer functions', function()
end) end)
describe('with partially empty buffer in one contiguous chunk', function() describe('with partially empty buffer in one contiguous chunk', function()
before_each(function()
write('string')
end)
itp('is called once with the empty chunk', function() itp('is called once with the empty chunk', function()
write('string')
collect_write_chunks() collect_write_chunks()
eq({'0000000000'}, chunks) eq({'0000000000'}, chunks)
end) end)
end) end)
describe('with filled buffer in one contiguous chunk', function() describe('with filled buffer in one contiguous chunk', function()
before_each(function()
write('abcdefghijklmnopq')
end)
itp('is not called', function() itp('is not called', function()
write('abcdefghijklmnopq')
collect_write_chunks() collect_write_chunks()
eq({}, chunks) eq({}, chunks)
end) end)
end) end)
describe('with buffer partially empty in two contiguous chunks', function() describe('with buffer partially empty in two contiguous chunks', function()
before_each(function() itp('is called twice with each filled chunk', function()
write('1234567890') write('1234567890')
read(8) read(8)
end)
itp('is called twice with each filled chunk', function()
collect_write_chunks() collect_write_chunks()
eq({'000000', '12345678'}, chunks) eq({'000000', '12345678'}, chunks)
end) end)
end) end)
describe('with buffer empty in two contiguous chunks', function() describe('with buffer empty in two contiguous chunks', function()
before_each(function() itp('is called twice with each filled chunk', function()
write('12345678') write('12345678')
read(8) read(8)
end)
itp('is called twice with each filled chunk', function()
collect_write_chunks() collect_write_chunks()
eq({'00000000', '12345678'}, chunks) eq({'00000000', '12345678'}, chunks)
end) end)
end) end)
describe('with buffer filled in two contiguous chunks', function() describe('with buffer filled in two contiguous chunks', function()
before_each(function() itp('is not called', function()
write('12345678') write('12345678')
read(8) read(8)
write('abcdefghijklmnopq') write('abcdefghijklmnopq')
end)
itp('is not called', function()
collect_write_chunks() collect_write_chunks()
eq({}, chunks) eq({}, chunks)
end) end)
@ -138,48 +124,36 @@ describe('rbuffer functions', function()
end) end)
describe('with partially filled buffer in one contiguous chunk', function() describe('with partially filled buffer in one contiguous chunk', function()
before_each(function()
write('string')
end)
itp('is called once with the filled chunk', function() itp('is called once with the filled chunk', function()
write('string')
collect_read_chunks() collect_read_chunks()
eq({'string'}, chunks) eq({'string'}, chunks)
end) end)
end) end)
describe('with filled buffer in one contiguous chunk', function() describe('with filled buffer in one contiguous chunk', function()
before_each(function()
write('abcdefghijklmnopq')
end)
itp('is called once with the filled chunk', function() itp('is called once with the filled chunk', function()
write('abcdefghijklmnopq')
collect_read_chunks() collect_read_chunks()
eq({'abcdefghijklmnop'}, chunks) eq({'abcdefghijklmnop'}, chunks)
end) end)
end) end)
describe('with buffer partially filled in two contiguous chunks', function() describe('with buffer partially filled in two contiguous chunks', function()
before_each(function() itp('is called twice with each filled chunk', function()
write('1234567890') write('1234567890')
read(10) read(10)
write('long string') write('long string')
end)
itp('is called twice with each filled chunk', function()
collect_read_chunks() collect_read_chunks()
eq({'long s', 'tring'}, chunks) eq({'long s', 'tring'}, chunks)
end) end)
end) end)
describe('with buffer filled in two contiguous chunks', function() describe('with buffer filled in two contiguous chunks', function()
before_each(function() itp('is called twice with each filled chunk', function()
write('12345678') write('12345678')
read(8) read(8)
write('abcdefghijklmnopq') write('abcdefghijklmnopq')
end)
itp('is called twice with each filled chunk', function()
collect_read_chunks() collect_read_chunks()
eq({'abcdefgh', 'ijklmnop'}, chunks) eq({'abcdefgh', 'ijklmnop'}, chunks)
end) end)
@ -206,13 +180,10 @@ describe('rbuffer functions', function()
end) end)
describe('with buffer filled in two contiguous chunks', function() describe('with buffer filled in two contiguous chunks', function()
before_each(function() itp('collects each character and index', function()
write('1234567890') write('1234567890')
read(10) read(10)
write('long string') write('long string')
end)
itp('collects each character and index', function()
collect_chars() collect_chars()
eq({{'l', 0}, {'o', 1}, {'n', 2}, {'g', 3}, {' ', 4}, {'s', 5}, eq({{'l', 0}, {'o', 1}, {'n', 2}, {'g', 3}, {' ', 4}, {'s', 5},
{'t', 6}, {'r', 7}, {'i', 8}, {'n', 9}, {'g', 10}}, chars) {'t', 6}, {'r', 7}, {'i', 8}, {'n', 9}, {'g', 10}}, chars)
@ -240,13 +211,10 @@ describe('rbuffer functions', function()
end) end)
describe('with buffer filled in two contiguous chunks', function() describe('with buffer filled in two contiguous chunks', function()
before_each(function() itp('collects each character and index', function()
write('1234567890') write('1234567890')
read(10) read(10)
write('long string') write('long string')
end)
itp('collects each character and index', function()
collect_chars() collect_chars()
eq({{'g', 10}, {'n', 9}, {'i', 8}, {'r', 7}, {'t', 6}, {'s', 5}, eq({{'g', 10}, {'n', 9}, {'i', 8}, {'r', 7}, {'t', 6}, {'s', 5},
{' ', 4}, {'g', 3}, {'n', 2}, {'o', 1}, {'l', 0}}, chars) {' ', 4}, {'g', 3}, {'n', 2}, {'o', 1}, {'l', 0}}, chars)
@ -265,13 +233,10 @@ describe('rbuffer functions', function()
end end
describe('with buffer filled in two contiguous chunks', function() describe('with buffer filled in two contiguous chunks', function()
before_each(function() itp('compares the common longest sequence', function()
write('1234567890') write('1234567890')
read(10) read(10)
write('long string') write('long string')
end)
itp('compares the common longest sequence', function()
eq(0, cmp('long string')) eq(0, cmp('long string'))
eq(0, cmp('long strin')) eq(0, cmp('long strin'))
eq(-1, cmp('long striM')) eq(-1, cmp('long striM'))

View File

@ -1,20 +1,25 @@
local lfs = require 'lfs' local lfs = require('lfs')
local helpers = require 'test.unit.helpers' local helpers = require('test.unit.helpers')(after_each)
local itp = helpers.gen_itp(it) local itp = helpers.gen_itp(it)
local os = helpers.cimport './src/nvim/os/os.h' local eq = helpers.eq
local tempfile = helpers.cimport './src/nvim/fileio.h' local neq = helpers.neq
local cimport = helpers.cimport
local deferred_call = helpers.deferred_call
local separate_cleanup = helpers.separate_cleanup
local lib = cimport('./src/nvim/os/os.h', './src/nvim/fileio.h')
describe('tempfile related functions', function() describe('tempfile related functions', function()
before_each(function() before_each(deferred_call(function()
tempfile.vim_deltempdir() lib.vim_deltempdir()
end) end))
after_each(function() separate_cleanup(function()
tempfile.vim_deltempdir() lib.vim_deltempdir()
end) end)
local vim_gettempdir = function() local vim_gettempdir = function()
return helpers.ffi.string(tempfile.vim_gettempdir()) return helpers.ffi.string(lib.vim_gettempdir())
end end
describe('vim_gettempdir', function() describe('vim_gettempdir', function()
@ -23,7 +28,7 @@ describe('tempfile related functions', function()
assert.True(dir ~= nil and dir:len() > 0) assert.True(dir ~= nil and dir:len() > 0)
-- os_file_is_writable returns 2 for a directory which we have rights -- os_file_is_writable returns 2 for a directory which we have rights
-- to write into. -- to write into.
assert.equals(os.os_file_is_writable(helpers.to_cstr(dir)), 2) eq(lib.os_file_is_writable(helpers.to_cstr(dir)), 2)
for entry in lfs.dir(dir) do for entry in lfs.dir(dir) do
assert.True(entry == '.' or entry == '..') assert.True(entry == '.' or entry == '..')
end end
@ -32,25 +37,25 @@ describe('tempfile related functions', function()
itp('returns the same directory on each call', function() itp('returns the same directory on each call', function()
local dir1 = vim_gettempdir() local dir1 = vim_gettempdir()
local dir2 = vim_gettempdir() local dir2 = vim_gettempdir()
assert.equals(dir1, dir2) eq(dir1, dir2)
end) end)
end) end)
describe('vim_tempname', function() describe('vim_tempname', function()
local vim_tempname = function() local vim_tempname = function()
return helpers.ffi.string(tempfile.vim_tempname()) return helpers.ffi.string(lib.vim_tempname())
end end
itp('generate name of non-existing file', function() itp('generate name of non-existing file', function()
local file = vim_tempname() local file = vim_tempname()
assert.truthy(file) assert.truthy(file)
assert.False(os.os_path_exists(file)) assert.False(lib.os_path_exists(file))
end) end)
itp('generate different names on each call', function() itp('generate different names on each call', function()
local fst = vim_tempname() local fst = vim_tempname()
local snd = vim_tempname() local snd = vim_tempname()
assert.not_equals(fst, snd) neq(fst, snd)
end) end)
itp('generate file name in Neovim own temp directory', function() itp('generate file name in Neovim own temp directory', function()