aboutsummaryrefslogtreecommitdiff
path: root/test/functional/helpers.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/helpers.lua')
-rw-r--r--test/functional/helpers.lua282
1 files changed, 218 insertions, 64 deletions
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 37b7bf664c..880cb6546c 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -1,9 +1,22 @@
require('coxpcall')
+local luv = require('luv')
local lfs = require('lfs')
-local assert = require('luassert')
-local ChildProcessStream = require('nvim.child_process_stream')
+local global_helpers = require('test.helpers')
+
+-- nvim client: Found in .deps/usr/share/lua/<version>/nvim/ if "bundled".
local Session = require('nvim.session')
+local TcpStream = require('nvim.tcp_stream')
+local SocketStream = require('nvim.socket_stream')
+local ChildProcessStream = require('nvim.child_process_stream')
+local check_logs = global_helpers.check_logs
+local neq = global_helpers.neq
+local eq = global_helpers.eq
+local ok = global_helpers.ok
+local map = global_helpers.map
+local filter = global_helpers.filter
+
+local start_dir = lfs.currentdir()
local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim'
local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
'--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 undodir=. directory=. viewdir=. backupdir=.',
@@ -11,18 +24,17 @@ local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
local mpack = require('mpack')
+local tmpname = global_helpers.tmpname
+local uname = global_helpers.uname
+
-- Formulate a path to the directory containing nvim. We use this to
-- help run test executables. It helps to keep the tests working, even
-- when the build is not in the default location.
local nvim_dir = nvim_prog:gsub("[/\\][^/\\]+$", "")
if nvim_dir == nvim_prog then
- nvim_dir = "."
+ nvim_dir = "."
end
--- Nvim "Unit Under Test" http://en.wikipedia.org/wiki/Device_under_test
-local NvimUUT = {}
-NvimUUT.__index = NvimUUT
-
local prepend_argv
if os.getenv('VALGRIND') then
@@ -30,7 +42,7 @@ if os.getenv('VALGRIND') then
prepend_argv = {'valgrind', '-q', '--tool=memcheck',
'--leak-check=yes', '--track-origins=yes',
'--show-possibly-lost=no',
- '--suppressions=.valgrind.supp',
+ '--suppressions=src/.valgrind.supp',
'--log-file='..log_file}
if os.getenv('GDB') then
table.insert(prepend_argv, '--vgdb=yes')
@@ -127,12 +139,16 @@ local function stop()
session:stop()
end
+-- Executes an ex-command. VimL errors manifest as client (lua) errors, but
+-- v:errmsg will not be updated.
local function nvim_command(cmd)
- request('vim_command', cmd)
+ request('nvim_command', cmd)
end
+-- Evaluates a VimL expression.
+-- Fails on VimL error, but does not update v:errmsg.
local function nvim_eval(expr)
- return request('vim_eval', expr)
+ return request('nvim_eval', expr)
end
local os_name = (function()
@@ -151,13 +167,21 @@ local os_name = (function()
end)
end)()
+local function iswin()
+ return os_name() == 'windows'
+end
+
+-- Executes a VimL function.
+-- Fails on VimL error, but does not update v:errmsg.
local function nvim_call(name, ...)
- return request('vim_call_function', name, {...})
+ return request('nvim_call_function', name, {...})
end
+-- Sends user input to Nvim.
+-- Does not fail on VimL error, but v:errmsg will be updated.
local function nvim_feed(input)
while #input > 0 do
- local written = request('vim_input', input)
+ local written = request('nvim_input', input)
input = input:sub(written + 1)
end
end
@@ -171,7 +195,7 @@ local function dedent(str)
indent = line_indent
end
end
- if #indent == 0 then
+ if indent == nil or #indent == 0 then
-- no minimum common indent
return str
end
@@ -211,18 +235,76 @@ local function merge_args(...)
return argv
end
-local function spawn(argv, merge)
- local child_stream = ChildProcessStream.spawn(merge and merge_args(prepend_argv, argv) or argv)
+local function spawn(argv, merge, env)
+ local child_stream = ChildProcessStream.spawn(
+ merge and merge_args(prepend_argv, argv) or argv,
+ env)
return Session.new(child_stream)
end
-local function clear(extra_cmd)
+-- Creates a new Session connected by domain socket (named pipe) or TCP.
+local function connect(file_or_address)
+ local addr, port = string.match(file_or_address, "(.*):(%d+)")
+ local stream = (addr and port) and TcpStream.open(addr, port) or
+ SocketStream.open(file_or_address)
+ return Session.new(stream)
+end
+
+-- Calls fn() until it succeeds, up to `max` times or until `max_ms`
+-- milliseconds have passed.
+local function retry(max, max_ms, fn)
+ local tries = 1
+ local timeout = (max_ms and max_ms > 0) and max_ms or 10000
+ local start_time = luv.now()
+ while true do
+ local status, result = pcall(fn)
+ if status then
+ return result
+ end
+ if (max and tries >= max) or (luv.now() - start_time > timeout) then
+ break
+ end
+ tries = tries + 1
+ end
+ -- Do not use pcall() for the final attempt, let the failure bubble up.
+ return fn()
+end
+
+local function clear(...)
local args = {unpack(nvim_argv)}
- if extra_cmd ~= nil then
- table.insert(args, '--cmd')
- table.insert(args, extra_cmd)
+ local new_args
+ local env = nil
+ local opts = select(1, ...)
+ if type(opts) == 'table' then
+ if opts.env then
+ local env_tbl = {}
+ for k, v in pairs(opts.env) do
+ assert(type(k) == 'string')
+ assert(type(v) == 'string')
+ env_tbl[k] = v
+ end
+ for _, k in ipairs({
+ 'HOME',
+ 'ASAN_OPTIONS',
+ 'LD_LIBRARY_PATH', 'PATH',
+ 'NVIM_LOG_FILE',
+ 'NVIM_RPLUGIN_MANIFEST',
+ }) do
+ env_tbl[k] = os.getenv(k)
+ end
+ env = {}
+ for k, v in pairs(env_tbl) do
+ env[#env + 1] = k .. '=' .. v
+ end
+ end
+ new_args = opts.args or {}
+ else
+ new_args = {...}
end
- set_session(spawn(args))
+ for _, arg in ipairs(new_args) do
+ table.insert(args, arg)
+ end
+ set_session(spawn(args, nil, env))
end
local function insert(...)
@@ -234,6 +316,8 @@ local function insert(...)
nvim_feed('<ESC>')
end
+-- Executes an ex-command by user input. Because nvim_input() is used, VimL
+-- errors will not manifest as client (lua) errors. Use command() for that.
local function execute(...)
for _, v in ipairs({...}) do
if v:sub(1, 1) ~= '/' then
@@ -257,108 +341,130 @@ local function write_file(name, text, dont_dedent)
end
local function source(code)
- local tmpname = os.tmpname()
- if os_name() == 'osx' and string.match(tmpname, '^/tmp') then
- tmpname = '/private'..tmpname
- end
- write_file(tmpname, code)
- nvim_command('source '..tmpname)
- os.remove(tmpname)
- return tmpname
+ local fname = tmpname()
+ write_file(fname, code)
+ nvim_command('source '..fname)
+ os.remove(fname)
+ return fname
end
-local function eq(expected, actual)
- return assert.are.same(expected, actual)
+local function set_shell_powershell()
+ source([[
+ set shell=powershell shellquote=\" shellpipe=\| shellredir=>
+ set shellcmdflag=\ -ExecutionPolicy\ RemoteSigned\ -Command
+ let &shellxquote=' '
+ ]])
end
-local function neq(expected, actual)
- return assert.are_not.same(expected, actual)
+local function nvim(method, ...)
+ return request('nvim_'..method, ...)
end
-local function ok(expr)
- assert.is_true(expr)
-end
-
-local function nvim(method, ...)
- return request('vim_'..method, ...)
+local function ui(method, ...)
+ return request('nvim_ui_'..method, ...)
end
local function nvim_async(method, ...)
- session:notify('vim_'..method, ...)
+ session:notify('nvim_'..method, ...)
end
local function buffer(method, ...)
- return request('buffer_'..method, ...)
+ return request('nvim_buf_'..method, ...)
end
local function window(method, ...)
- return request('window_'..method, ...)
+ return request('nvim_win_'..method, ...)
end
local function tabpage(method, ...)
- return request('tabpage_'..method, ...)
+ return request('nvim_tabpage_'..method, ...)
end
local function curbuf(method, ...)
- local buf = nvim('get_current_buffer')
if not method then
- return buf
+ return nvim('get_current_buf')
end
- return buffer(method, buf, ...)
+ return buffer(method, 0, ...)
end
local function wait()
+ -- Execute 'vim_eval' (a deferred function) to block
+ -- until all pending input is processed.
session:request('vim_eval', '1')
end
+-- sleeps the test runner (_not_ the nvim instance)
+local function sleep(ms)
+ run(nil, nil, nil, ms)
+end
+
local function curbuf_contents()
- -- Before inspecting the buffer, execute 'vim_eval' to wait until all
- -- previously sent keys are processed(vim_eval is a deferred function, and
- -- only processed after all input)
- wait()
+ wait() -- Before inspecting the buffer, process all input.
return table.concat(curbuf('get_lines', 0, -1, true), '\n')
end
local function curwin(method, ...)
- local win = nvim('get_current_window')
if not method then
- return win
+ return nvim('get_current_win')
end
- return window(method, win, ...)
+ return window(method, 0, ...)
end
local function curtab(method, ...)
- local tab = nvim('get_current_tabpage')
if not method then
- return tab
+ return nvim('get_current_tabpage')
end
- return tabpage(method, tab, ...)
+ return tabpage(method, 0, ...)
end
local function expect(contents)
return eq(dedent(contents), curbuf_contents())
end
-local function rmdir(path)
+local function do_rmdir(path)
if lfs.attributes(path, 'mode') ~= 'directory' then
return nil
end
for file in lfs.dir(path) do
if file ~= '.' and file ~= '..' then
- local ret, err = os.remove(path..'/'..file)
- if not ret then
- error('os.remove: '..err)
- return nil
+ local abspath = path..'/'..file
+ if lfs.attributes(abspath, 'mode') == 'directory' then
+ local ret = do_rmdir(abspath) -- recurse
+ if not ret then
+ return nil
+ end
+ else
+ local ret, err = os.remove(abspath)
+ if not ret then
+ error('os.remove: '..err)
+ return nil
+ end
end
end
end
- local ret, err = os.remove(path)
+ local ret, err = lfs.rmdir(path)
if not ret then
- error('os.remove: '..err)
+ error('lfs.rmdir('..path..'): '..err)
end
return ret
end
+local function rmdir(path)
+ local ret, _ = pcall(do_rmdir, path)
+ if not ret and os_name() == "windows" then
+ -- Maybe "Permission denied"; try again after changing the nvim
+ -- process to the top-level directory.
+ nvim_command([[exe 'cd '.fnameescape(']]..start_dir.."')")
+ ret, _ = pcall(do_rmdir, path)
+ end
+ -- During teardown, the nvim process may not exit quickly enough, then rmdir()
+ -- will fail (on Windows).
+ if not ret then -- Try again.
+ sleep(1000)
+ do_rmdir(path)
+ end
+end
+
local exc_exec = function(cmd)
nvim_command(([[
try
@@ -395,8 +501,38 @@ local function create_callindex(func)
return table
end
+-- Helper to skip tests. Returns true in Windows systems.
+-- pending_fn is pending() from busted
+local function pending_win32(pending_fn)
+ if uname() == 'Windows' then
+ if pending_fn ~= nil then
+ pending_fn('FIXME: Windows', function() end)
+ end
+ return true
+ else
+ return false
+ end
+end
+
+-- Calls pending() and returns `true` if the system is too slow to
+-- run fragile or expensive tests. Else returns `false`.
+local function skip_fragile(pending_fn, cond)
+ if pending_fn == nil or type(pending_fn) ~= type(function()end) then
+ error("invalid pending_fn")
+ end
+ if cond then
+ pending_fn("skipped (test is fragile on this system)", function() end)
+ return true
+ elseif os.getenv("TEST_SKIP_FRAGILE") then
+ pending_fn("skipped (TEST_SKIP_FRAGILE)", function() end)
+ return true
+ end
+ return false
+end
+
local funcs = create_callindex(nvim_call)
local meths = create_callindex(nvim)
+local uimeths = create_callindex(ui)
local bufmeths = create_callindex(buffer)
local winmeths = create_callindex(window)
local tabmeths = create_callindex(tabpage)
@@ -404,14 +540,17 @@ local curbufmeths = create_callindex(curbuf)
local curwinmeths = create_callindex(curwin)
local curtabmeths = create_callindex(curtab)
-return {
+local M = {
prepend_argv = prepend_argv,
clear = clear,
+ connect = connect,
+ retry = retry,
spawn = spawn,
dedent = dedent,
source = source,
rawfeed = rawfeed,
insert = insert,
+ iswin = iswin,
feed = feed,
execute = execute,
eval = nvim_eval,
@@ -425,6 +564,8 @@ return {
neq = neq,
expect = expect,
ok = ok,
+ map = map,
+ filter = filter,
nvim = nvim,
nvim_async = nvim_async,
nvim_prog = nvim_prog,
@@ -437,6 +578,7 @@ return {
curtab = curtab,
curbuf_contents = curbuf_contents,
wait = wait,
+ sleep = sleep,
set_session = set_session,
write_file = write_file,
os_name = os_name,
@@ -450,8 +592,20 @@ return {
bufmeths = bufmeths,
winmeths = winmeths,
tabmeths = tabmeths,
+ uimeths = uimeths,
curbufmeths = curbufmeths,
curwinmeths = curwinmeths,
curtabmeths = curtabmeths,
- NIL = mpack.NIL
+ pending_win32 = pending_win32,
+ skip_fragile = skip_fragile,
+ set_shell_powershell = set_shell_powershell,
+ tmpname = tmpname,
+ NIL = mpack.NIL,
}
+
+return function(after_each)
+ if after_each then
+ after_each(check_logs)
+ end
+ return M
+end