aboutsummaryrefslogtreecommitdiff
path: root/test/unit/helpers.lua
diff options
context:
space:
mode:
authorZyX <kp-pav@yandex.ru>2017-03-11 16:02:47 +0300
committerZyX <kp-pav@yandex.ru>2017-03-11 23:23:49 +0300
commite2a578f40dbd4836330402a44844c7ef8a0df5c5 (patch)
treeab2b04b6d0228e7d5a4ef6d611971f063a17be17 /test/unit/helpers.lua
parent9400466282918396c814ef456d0f65dca51b8889 (diff)
downloadrneovim-e2a578f40dbd4836330402a44844c7ef8a0df5c5.tar.gz
rneovim-e2a578f40dbd4836330402a44844c7ef8a0df5c5.tar.bz2
rneovim-e2a578f40dbd4836330402a44844c7ef8a0df5c5.zip
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.
Diffstat (limited to 'test/unit/helpers.lua')
-rw-r--r--test/unit/helpers.lua238
1 files changed, 172 insertions, 66 deletions
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index b6fcc959ef..93eb01b21b 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -13,6 +13,7 @@ local syscall = nil
local check_cores = global_helpers.check_cores
local which = global_helpers.which
local neq = global_helpers.neq
+local map = global_helpers.map
local eq = global_helpers.eq
local ok = global_helpers.ok
@@ -22,13 +23,82 @@ local NULL = ffi.cast('void*', 0)
local OK = 1
local FAIL = 0
+local cimport
+
-- add some standard header locations
for _, p in ipairs(Paths.include_paths) do
Preprocess.add_to_include_path(p)
end
--- load neovim shared library
-local libnvim = ffi.load(Paths.test_libnvim_path)
+local pid = nil
+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)
return s:match('^%s*(.*%S)') or ''
@@ -58,52 +128,74 @@ local function filter_complex_blocks(body)
return table.concat(result, "\n")
end
-local previous_defines = ''
local cdef = ffi.cdef
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,
-- this helper will return the C namespace of the nvim library.
-local function cimport(...)
- local paths = {}
- local args = {...}
-
- -- filter out paths we've already imported
- for _,path in pairs(args) do
- if path ~= nil and not imported:contains(path) then
- paths[#paths + 1] = path
- end
+cimport = function(...)
+ local previous_defines, preprocess_cache
+ if preprocess_cache_mod then
+ preprocess_cache = preprocess_cache_mod
+ previous_defines = previous_defines_mod
+ else
+ preprocess_cache = preprocess_cache_init
+ previous_defines = previous_defines_init
end
-
- for _,path in pairs(paths) do
- imported:add(path)
+ 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
+ local body
+ if preprocess_cache[path] then
+ body = preprocess_cache[path]
+ else
+ body, previous_defines = Preprocess.preprocess(previous_defines, path)
+ -- format it (so that the lines are "unique" statements), also filter out
+ -- Objective-C blocks
+ if os.getenv('NVIM_TEST_PRINT_I') == '1' then
+ local lnum = 0
+ for line in body:gmatch('[^\n]+') do
+ lnum = lnum + 1
+ print(lnum, line)
+ end
+ end
+ body = formatc(body)
+ body = filter_complex_blocks(body)
+ preprocess_cache[path] = body
+ end
+ cimportstr(preprocess_cache, path)
end
+ return lib
+end
- if #paths == 0 then
- return libnvim
+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
-
- local body
- body, previous_defines = Preprocess.preprocess(previous_defines, unpack(paths))
-
- return cimportstr(body)
end
-cimportstr = function(body)
- -- format it (so that the lines are "unique" statements), also filter out
- -- Objective-C blocks
- if os.getenv('NVIM_TEST_PRINT_I') == '1' then
- local lnum = 0
- for line in body:gmatch('[^\n]+') do
- lnum = lnum + 1
- print(lnum, line)
- end
+cimportstr = deferred_call(function(preprocess_cache, path)
+ if imported:contains(path) then
+ return lib
end
- body = formatc(body)
- body = filter_complex_blocks(body)
-
+ local body = preprocess_cache[path]
-- add the formatted lines to a set
local new_cdefs = Set:new()
for line in body:gmatch("[^\r\n]+") do
@@ -126,7 +218,7 @@ cimportstr = function(body)
if new_cdefs:size() == 0 then
-- if there's no new lines, just return
- return libnvim
+ return lib
end
-- request a sorted version of the new lines (same relative order as the
@@ -138,13 +230,10 @@ cimportstr = function(body)
end
end
cdef(table.concat(new_lines, "\n"))
+ imported:add(path)
- return libnvim
-end
-
-local function cppimport(path)
- return cimport(Paths.test_include_path .. '/' .. path)
-end
+ return lib
+end, lib)
local function alloc_log_new()
local log = {
@@ -156,9 +245,12 @@ local function alloc_log_new()
local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'}
function log:save_original_functions()
for _, funcname in ipairs(allocator_functions) do
- self.original_functions[funcname] = self.lib['mem_' .. funcname]
+ if not self.original_functions[funcname] then
+ self.original_functions[funcname] = self.lib['mem_' .. funcname]
+ end
end
end
+ log.save_original_functions = deferred_call(log.save_original_functions)
function log:set_mocks()
for _, k in ipairs(allocator_functions) do
do
@@ -185,6 +277,7 @@ local function alloc_log_new()
end
end
end
+ log.set_mocks = deferred_call(log.set_mocks)
function log:clear()
self.log = {}
end
@@ -193,22 +286,28 @@ local function alloc_log_new()
self:clear()
end
function log:restore_original_functions()
- for k, v in pairs(self.original_functions) do
- self.lib['mem_' .. k] = v
- end
+ -- Do nothing: set mocks live in a separate process
+ return
+ --[[
+ [ for k, v in pairs(self.original_functions) do
+ [ self.lib['mem_' .. k] = v
+ [ end
+ ]]
end
- function log:before_each()
+ function log:setup()
log:save_original_functions()
log:set_mocks()
end
+ function log:before_each()
+ return
+ end
function log:after_each()
log:restore_original_functions()
end
+ log:setup()
return log
end
-cimport('./src/nvim/types.h')
-
-- take a pointer to a C-allocated string and return an interned
-- version while also freeing the memory
local function internalize(cdata, len)
@@ -221,16 +320,6 @@ local function to_cstr(string)
return cstr(#string + 1, string)
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
if posix ~= nil then
@@ -263,7 +352,7 @@ elseif syscall ~= nil then
exit = syscall.exit,
}
else
- cimport('./test/unit/fixtures/posix.h')
+ cimport_immediate('./test/unit/fixtures/posix.h')
sc = {
fork = function()
return tonumber(ffi.C.fork())
@@ -291,7 +380,7 @@ else
len - total_bytes_read))
if bytes_read == -1 then
local err = ffi.errno(0)
- if err ~= libnvim.kPOSIXErrnoEINTR then
+ if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("read() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err))))
end
@@ -314,7 +403,7 @@ else
#s - total_bytes_written))
if bytes_written == -1 then
local err = ffi.errno(0)
- if err ~= libnvim.kPOSIXErrnoEINTR then
+ if err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("write() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err))))
end
@@ -330,12 +419,12 @@ else
wait = function(pid)
ffi.errno(0)
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
local err = ffi.errno(0)
- if err == libnvim.kPOSIXErrnoECHILD then
+ if err == ffi.C.kPOSIXErrnoECHILD then
break
- elseif err ~= libnvim.kPOSIXErrnoEINTR then
+ elseif err ~= ffi.C.kPOSIXErrnoEINTR then
assert(false, ("waitpid() error: %u: %s"):format(
err, ffi.string(ffi.C.strerror(err))))
end
@@ -371,6 +460,10 @@ if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then
end
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(_)
return false
end
@@ -386,8 +479,9 @@ local function gen_itp(it)
end
it(name, function()
local rd, wr = sc.pipe()
- local pid = sc.fork()
+ pid = sc.fork()
if pid == 0 then
+ init()
sc.close(rd)
collectgarbage('stop')
local err, emsg = pcall(func)
@@ -395,16 +489,19 @@ local function gen_itp(it)
emsg = tostring(emsg)
if not err then
sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
+ deinit()
sc.close(wr)
sc.exit(1)
else
sc.write(wr, '+\n')
+ deinit()
sc.close(wr)
sc.exit(0)
end
else
sc.close(wr)
sc.wait(pid)
+ pid = nil
local function check()
local res = sc.read(rd, 2)
eq(2, #res)
@@ -433,6 +530,12 @@ local function gen_itp(it)
return itp
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 = {
cimport = cimport,
cppimport = cppimport,
@@ -441,7 +544,7 @@ local module = {
eq = eq,
neq = neq,
ffi = ffi,
- lib = libnvim,
+ lib = lib,
cstr = cstr,
to_cstr = to_cstr,
NULL = NULL,
@@ -449,6 +552,9 @@ local module = {
FAIL = FAIL,
alloc_log_new = alloc_log_new,
gen_itp = gen_itp,
+ only_separate = only_separate,
+ deferred_call = deferred_call,
+ separate_cleanup = separate_cleanup,
}
return function(after_each)
if after_each then