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.lua369
1 files changed, 238 insertions, 131 deletions
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 5eec3afe65..848f1ef477 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -8,35 +8,39 @@ 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 Paths = require('test.config.paths')
+local check_cores = global_helpers.check_cores
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 dedent = global_helpers.dedent
local start_dir = lfs.currentdir()
-local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim'
+-- XXX: NVIM_PROG takes precedence, QuickBuild sets it.
+local nvim_prog = (
+ os.getenv('NVIM_PROG')
+ or os.getenv('NVIM_PRG')
+ or Paths.test_build_dir .. '/bin/nvim'
+)
+-- Default settings for the test session.
+local nvim_set = 'set shortmess+=I background=light noswapfile noautoindent'
+ ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.'
+ ..' belloff= noshowcmd noruler nomore'
local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
- '--cmd', 'set shortmess+=I background=light noswapfile noautoindent laststatus=1 undodir=. directory=. viewdir=. backupdir=.',
- '--embed'}
-
-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.
+ '--cmd', nvim_set, '--embed'}
+-- Directory containing nvim.
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 mpack = require('mpack')
+local tmpname = global_helpers.tmpname
+local uname = global_helpers.uname
local prepend_argv
if os.getenv('VALGRIND') then
@@ -72,8 +76,8 @@ end
local session, loop_running, last_error
-local function set_session(s)
- if session then
+local function set_session(s, keep)
+ if session and not keep then
session:close()
end
session = s
@@ -169,6 +173,10 @@ local os_name = (function()
end)
end)()
+local function iswin()
+ return package.config:sub(1,1) == '\\'
+end
+
-- Executes a VimL function.
-- Fails on VimL error, but does not update v:errmsg.
local function nvim_call(name, ...)
@@ -184,28 +192,6 @@ local function nvim_feed(input)
end
end
-local function dedent(str)
- -- find minimum common indent across lines
- local indent = nil
- for line in str:gmatch('[^\n]+') do
- local line_indent = line:match('^%s+') or ''
- if indent == nil or #line_indent < #indent then
- indent = line_indent
- end
- end
- if #indent == 0 then
- -- no minimum common indent
- return str
- end
- -- create a pattern for the indent
- indent = indent:gsub('%s', '[ \t]')
- -- strip it from the first line
- str = str:gsub('^'..indent, '')
- -- strip it from the remaining lines
- str = str:gsub('[\n]'..indent, '\n')
- return str
-end
-
local function feed(...)
for _, v in ipairs({...}) do
nvim_feed(dedent(v))
@@ -260,12 +246,13 @@ local function retry(max, max_ms, fn)
return result
end
if (max and tries >= max) or (luv.now() - start_time > timeout) then
- break
+ if type(result) == "string" then
+ result = "\nretry() attempts: "..tostring(tries).."\n"..result
+ end
+ error(result)
end
tries = tries + 1
end
- -- Do not use pcall() for the final attempt, let the failure bubble up.
- return fn()
end
local function clear(...)
@@ -288,7 +275,9 @@ local function clear(...)
'NVIM_LOG_FILE',
'NVIM_RPLUGIN_MANIFEST',
}) do
- env_tbl[k] = os.getenv(k)
+ if not env_tbl[k] then
+ env_tbl[k] = os.getenv(k)
+ end
end
env = {}
for k, v in pairs(env_tbl) do
@@ -316,7 +305,7 @@ 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(...)
+local function feed_command(...)
for _, v in ipairs({...}) do
if v:sub(1, 1) ~= '/' then
-- not a search command, prefix with colon
@@ -330,7 +319,14 @@ end
-- Dedent the given text and write it to the file name.
local function write_file(name, text, dont_dedent)
local file = io.open(name, 'w')
- if not dont_dedent then
+ if type(text) == 'table' then
+ -- Byte blob
+ local bytes = text
+ text = ''
+ for _, char in ipairs(bytes) do
+ text = ('%s%c'):format(text, char)
+ end
+ elseif not dont_dedent then
text = dedent(text)
end
file:write(text)
@@ -338,14 +334,44 @@ local function write_file(name, text, dont_dedent)
file:close()
end
+local function read_file(name)
+ local file = io.open(name, 'r')
+ if not file then
+ return nil
+ end
+ local ret = file:read('*a')
+ file:close()
+ return ret
+end
+
+local sourced_fnames = {}
local function source(code)
local fname = tmpname()
write_file(fname, code)
nvim_command('source '..fname)
- os.remove(fname)
+ -- DO NOT REMOVE FILE HERE.
+ -- do_source() has a habit of checking whether files are “same” by using inode
+ -- and device IDs. If you run two source() calls in quick succession there is
+ -- a good chance that underlying filesystem will reuse the inode, making files
+ -- appear as “symlinks” to do_source when it checks FileIDs. With current
+ -- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse
+ -- inodes, Mac OS machines (again, both QB and travis) do not.
+ --
+ -- Files appearing as “symlinks” mean that both the first and the second
+ -- source() calls will use same SID, which may fail some tests which check for
+ -- exact numbers after `<SNR>` in e.g. function names.
+ sourced_fnames[#sourced_fnames + 1] = fname
return fname
end
+local function set_shell_powershell()
+ source([[
+ set shell=powershell shellquote=\" shellpipe=\| shellredir=>
+ set shellcmdflag=\ -NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command
+ let &shellxquote=' '
+ ]])
+end
+
local function nvim(method, ...)
return request('nvim_'..method, ...)
end
@@ -378,14 +404,21 @@ local function curbuf(method, ...)
end
local function wait()
- -- Execute 'vim_eval' (a deferred function) to block
+ -- Execute 'nvim_eval' (a deferred function) to block
-- until all pending input is processed.
- session:request('vim_eval', '1')
+ session:request('nvim_eval', '1')
end
-- sleeps the test runner (_not_ the nvim instance)
local function sleep(ms)
- run(nil, nil, nil, ms)
+ local function notification_cb(method, _)
+ if method == "redraw" then
+ error("Screen is attached; use screen:sleep() instead.")
+ end
+ return true
+ end
+
+ run(nil, notification_cb, nil, ms)
end
local function curbuf_contents()
@@ -411,23 +444,34 @@ local function expect(contents)
return eq(dedent(contents), curbuf_contents())
end
+local function expect_any(contents)
+ contents = dedent(contents)
+ return ok(nil ~= string.find(curbuf_contents(), contents, 1, true))
+end
+
local function do_rmdir(path)
if lfs.attributes(path, 'mode') ~= 'directory' then
- return nil
+ return -- Don't complain.
end
for file in lfs.dir(path) do
if file ~= '.' and file ~= '..' then
local abspath = path..'/'..file
if lfs.attributes(abspath, 'mode') == 'directory' then
- local ret = do_rmdir(abspath) -- recurse
- if not ret then
- return nil
- end
+ do_rmdir(abspath) -- recurse
else
local ret, err = os.remove(abspath)
if not ret then
- error('os.remove: '..err)
- return nil
+ if not session then
+ error('os.remove: '..err)
+ else
+ -- Try Nvim delete(): it handles `readonly` attribute on Windows,
+ -- and avoids Lua cross-version/platform incompatibilities.
+ if -1 == nvim_call('delete', abspath) then
+ local hint = (os_name() == 'windows'
+ and ' (hint: try :%bwipeout! before rmdir())' or '')
+ error('delete() failed'..hint..': '..abspath)
+ end
+ end
end
end
end
@@ -436,7 +480,6 @@ local function do_rmdir(path)
if not ret then
error('lfs.rmdir('..path..'): '..err)
end
- return ret
end
local function rmdir(path)
@@ -468,17 +511,6 @@ local exc_exec = function(cmd)
return ret
end
-local function redir_exec(cmd)
- nvim_command(([[
- redir => g:__output
- silent! execute "%s"
- redir END
- ]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0')))
- local ret = nvim_eval('get(g:, "__output", 0)')
- nvim_command('unlet! g:__output')
- return ret
-end
-
local function create_callindex(func)
local table = {}
setmetatable(table, {
@@ -494,7 +526,6 @@ end
-- Helper to skip tests. Returns true in Windows systems.
-- pending_fn is pending() from busted
local function pending_win32(pending_fn)
- clear()
if uname() == 'Windows' then
if pending_fn ~= nil then
pending_fn('FIXME: Windows', function() end)
@@ -521,6 +552,14 @@ local function skip_fragile(pending_fn, cond)
return false
end
+local function meth_pcall(...)
+ local ret = {pcall(...)}
+ if type(ret[2]) == 'string' then
+ ret[2] = ret[2]:gsub('^[^:]+:%d+: ', '')
+ end
+ return ret
+end
+
local funcs = create_callindex(nvim_call)
local meths = create_callindex(nvim)
local uimeths = create_callindex(ui)
@@ -531,66 +570,134 @@ local curbufmeths = create_callindex(curbuf)
local curwinmeths = create_callindex(curwin)
local curtabmeths = create_callindex(curtab)
+local function redir_exec(cmd)
+ meths.set_var('__redir_exec_cmd', cmd)
+ nvim_command([[
+ redir => g:__redir_exec_output
+ silent! execute g:__redir_exec_cmd
+ redir END
+ ]])
+ local ret = meths.get_var('__redir_exec_output')
+ meths.del_var('__redir_exec_output')
+ meths.del_var('__redir_exec_cmd')
+ return ret
+end
+
+local function get_pathsep()
+ return funcs.fnamemodify('.', ':p'):sub(-1)
+end
+
+local function missing_provider(provider)
+ if provider == 'ruby' then
+ local prog = funcs['provider#' .. provider .. '#Detect']()
+ return prog == '' and (provider .. ' not detected') or false
+ elseif provider == 'python' or provider == 'python3' then
+ local py_major_version = (provider == 'python3' and 3 or 2)
+ local errors = funcs['provider#pythonx#Detect'](py_major_version)[2]
+ return errors ~= '' and errors or false
+ else
+ assert(false, 'Unknown provider: ' .. provider)
+ end
+end
+
+local function alter_slashes(obj)
+ if not iswin() then
+ return obj
+ end
+ if type(obj) == 'string' then
+ local ret = obj:gsub('/', '\\')
+ return ret
+ elseif type(obj) == 'table' then
+ local ret = {}
+ for k, v in pairs(obj) do
+ ret[k] = alter_slashes(v)
+ end
+ return ret
+ else
+ assert(false, 'Could only alter slashes for tables of strings and strings')
+ end
+end
+
+local module = {
+ prepend_argv = prepend_argv,
+ clear = clear,
+ connect = connect,
+ retry = retry,
+ spawn = spawn,
+ dedent = dedent,
+ source = source,
+ rawfeed = rawfeed,
+ insert = insert,
+ iswin = iswin,
+ feed = feed,
+ feed_command = feed_command,
+ eval = nvim_eval,
+ call = nvim_call,
+ command = nvim_command,
+ request = request,
+ next_message = next_message,
+ run = run,
+ stop = stop,
+ eq = eq,
+ neq = neq,
+ expect = expect,
+ expect_any = expect_any,
+ ok = ok,
+ map = map,
+ filter = filter,
+ nvim = nvim,
+ nvim_async = nvim_async,
+ nvim_prog = nvim_prog,
+ nvim_argv = nvim_argv,
+ nvim_set = nvim_set,
+ nvim_dir = nvim_dir,
+ buffer = buffer,
+ window = window,
+ tabpage = tabpage,
+ curbuf = curbuf,
+ curwin = curwin,
+ curtab = curtab,
+ curbuf_contents = curbuf_contents,
+ wait = wait,
+ sleep = sleep,
+ set_session = set_session,
+ write_file = write_file,
+ read_file = read_file,
+ os_name = os_name,
+ rmdir = rmdir,
+ mkdir = lfs.mkdir,
+ exc_exec = exc_exec,
+ redir_exec = redir_exec,
+ merge_args = merge_args,
+ funcs = funcs,
+ meths = meths,
+ bufmeths = bufmeths,
+ winmeths = winmeths,
+ tabmeths = tabmeths,
+ uimeths = uimeths,
+ curbufmeths = curbufmeths,
+ curwinmeths = curwinmeths,
+ curtabmeths = curtabmeths,
+ pending_win32 = pending_win32,
+ skip_fragile = skip_fragile,
+ set_shell_powershell = set_shell_powershell,
+ tmpname = tmpname,
+ meth_pcall = meth_pcall,
+ NIL = mpack.NIL,
+ get_pathsep = get_pathsep,
+ missing_provider = missing_provider,
+ alter_slashes = alter_slashes,
+}
+
return function(after_each)
if after_each then
- after_each(check_logs)
- end
- return {
- prepend_argv = prepend_argv,
- clear = clear,
- connect = connect,
- retry = retry,
- spawn = spawn,
- dedent = dedent,
- source = source,
- rawfeed = rawfeed,
- insert = insert,
- feed = feed,
- execute = execute,
- eval = nvim_eval,
- call = nvim_call,
- command = nvim_command,
- request = request,
- next_message = next_message,
- run = run,
- stop = stop,
- eq = eq,
- neq = neq,
- expect = expect,
- ok = ok,
- nvim = nvim,
- nvim_async = nvim_async,
- nvim_prog = nvim_prog,
- nvim_dir = nvim_dir,
- buffer = buffer,
- window = window,
- tabpage = tabpage,
- curbuf = curbuf,
- curwin = curwin,
- curtab = curtab,
- curbuf_contents = curbuf_contents,
- wait = wait,
- sleep = sleep,
- set_session = set_session,
- write_file = write_file,
- os_name = os_name,
- rmdir = rmdir,
- mkdir = lfs.mkdir,
- exc_exec = exc_exec,
- redir_exec = redir_exec,
- merge_args = merge_args,
- funcs = funcs,
- meths = meths,
- bufmeths = bufmeths,
- winmeths = winmeths,
- tabmeths = tabmeths,
- uimeths = uimeths,
- curbufmeths = curbufmeths,
- curwinmeths = curwinmeths,
- curtabmeths = curtabmeths,
- pending_win32 = pending_win32,
- skip_fragile = skip_fragile,
- tmpname = tmpname,
- NIL = mpack.NIL,
- }
+ after_each(function()
+ for _, fname in ipairs(sourced_fnames) do
+ os.remove(fname)
+ end
+ check_logs()
+ check_cores('build/bin/nvim')
+ end)
+ end
+ return module
end