mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
test: add type annotations
This commit is contained in:
parent
5a16d08a63
commit
26835d8d9c
@ -22,6 +22,9 @@ function Response:send(value, is_error)
|
||||
self._msgpack_rpc_stream._stream:write(data)
|
||||
end
|
||||
|
||||
--- @class test.MsgpackRpcStream
|
||||
--- @field private _stream test.Stream
|
||||
--- @field private __pack table
|
||||
local MsgpackRpcStream = {}
|
||||
MsgpackRpcStream.__index = MsgpackRpcStream
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
local uv = vim.uv
|
||||
local MsgpackRpcStream = require('test.client.msgpack_rpc_stream')
|
||||
|
||||
--- @class test.Session
|
||||
--- @field private _pending_messages string[]
|
||||
--- @field private _msgpack_rpc_stream test.MsgpackRpcStream
|
||||
--- @field private _prepare uv.uv_prepare_t
|
||||
--- @field private _timer uv.uv_timer_t
|
||||
--- @field private _is_running boolean
|
||||
local Session = {}
|
||||
Session.__index = Session
|
||||
if package.loaded['jit'] then
|
||||
@ -26,7 +32,7 @@ end
|
||||
|
||||
local function coroutine_exec(func, ...)
|
||||
local args = { ... }
|
||||
local on_complete
|
||||
local on_complete --- @type function?
|
||||
|
||||
if #args > 0 and type(args[#args]) == 'function' then
|
||||
-- completion callback
|
||||
@ -54,6 +60,8 @@ function Session.new(stream)
|
||||
}, Session)
|
||||
end
|
||||
|
||||
--- @param timeout integer?
|
||||
--- @return string?
|
||||
function Session:next_message(timeout)
|
||||
local function on_request(method, args, response)
|
||||
table.insert(self._pending_messages, { 'request', method, args, response })
|
||||
@ -86,6 +94,10 @@ function Session:notify(method, ...)
|
||||
self._msgpack_rpc_stream:write(method, { ... })
|
||||
end
|
||||
|
||||
--- @param method string
|
||||
--- @param ... any
|
||||
--- @return boolean
|
||||
--- @return table
|
||||
function Session:request(method, ...)
|
||||
local args = { ... }
|
||||
local err, result
|
||||
|
@ -1,18 +1,28 @@
|
||||
local uv = vim.uv
|
||||
|
||||
--- @class test.Stream
|
||||
--- @field write fun(self, data: string|string[])
|
||||
--- @field read_start fun(self, cb: fun(chunk: string))
|
||||
--- @field read_stop fun(self)
|
||||
--- @field close fun(self, signal?: string)
|
||||
|
||||
--- @class vim.StdioStream : test.Stream
|
||||
--- @field private _in uv.uv_pipe_t
|
||||
--- @field private _out uv.uv_pipe_t
|
||||
local StdioStream = {}
|
||||
StdioStream.__index = StdioStream
|
||||
|
||||
function StdioStream.open()
|
||||
local self = setmetatable({
|
||||
_in = uv.new_pipe(false),
|
||||
_out = uv.new_pipe(false),
|
||||
_in = assert(uv.new_pipe(false)),
|
||||
_out = assert(uv.new_pipe(false)),
|
||||
}, StdioStream)
|
||||
self._in:open(0)
|
||||
self._out:open(1)
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param data string|string[]
|
||||
function StdioStream:write(data)
|
||||
self._out:write(data)
|
||||
end
|
||||
@ -35,11 +45,14 @@ function StdioStream:close()
|
||||
self._out:close()
|
||||
end
|
||||
|
||||
--- @class test.SocketStream : test.Stream
|
||||
--- @field package _stream_error? string
|
||||
--- @field package _socket uv.uv_pipe_t
|
||||
local SocketStream = {}
|
||||
SocketStream.__index = SocketStream
|
||||
|
||||
function SocketStream.open(file)
|
||||
local socket = uv.new_pipe(false)
|
||||
local socket = assert(uv.new_pipe(false))
|
||||
local self = setmetatable({
|
||||
_socket = socket,
|
||||
_stream_error = nil,
|
||||
@ -51,7 +64,7 @@ function SocketStream.open(file)
|
||||
end
|
||||
|
||||
function SocketStream.connect(host, port)
|
||||
local socket = uv.new_tcp()
|
||||
local socket = assert(uv.new_tcp())
|
||||
local self = setmetatable({
|
||||
_socket = socket,
|
||||
_stream_error = nil,
|
||||
@ -96,9 +109,20 @@ function SocketStream:close()
|
||||
uv.close(self._socket)
|
||||
end
|
||||
|
||||
--- @class test.ChildProcessStream : test.Stream
|
||||
--- @field private _proc uv.uv_process_t
|
||||
--- @field private _pid integer
|
||||
--- @field private _child_stdin uv.uv_pipe_t
|
||||
--- @field private _child_stdout uv.uv_pipe_t
|
||||
--- @field status integer
|
||||
--- @field signal integer
|
||||
local ChildProcessStream = {}
|
||||
ChildProcessStream.__index = ChildProcessStream
|
||||
|
||||
--- @param argv string[]
|
||||
--- @param env string[]?
|
||||
--- @param io_extra uv.uv_pipe_t?
|
||||
--- @return test.ChildProcessStream
|
||||
function ChildProcessStream.spawn(argv, env, io_extra)
|
||||
local self = setmetatable({
|
||||
_child_stdin = uv.new_pipe(false),
|
||||
@ -106,13 +130,15 @@ function ChildProcessStream.spawn(argv, env, io_extra)
|
||||
_exiting = false,
|
||||
}, ChildProcessStream)
|
||||
local prog = argv[1]
|
||||
local args = {}
|
||||
local args = {} --- @type string[]
|
||||
for i = 2, #argv do
|
||||
args[#args + 1] = argv[i]
|
||||
end
|
||||
--- @diagnostic disable-next-line:missing-fields
|
||||
self._proc, self._pid = uv.spawn(prog, {
|
||||
stdio = { self._child_stdin, self._child_stdout, 2, io_extra },
|
||||
args = args,
|
||||
--- @diagnostic disable-next-line:assign-type-mismatch
|
||||
env = env,
|
||||
}, function(status, signal)
|
||||
self.status = status
|
||||
|
@ -55,7 +55,7 @@ if module.nvim_dir == module.nvim_prog then
|
||||
module.nvim_dir = '.'
|
||||
end
|
||||
|
||||
local prepend_argv
|
||||
local prepend_argv --- @type string[]?
|
||||
|
||||
if os.getenv('VALGRIND') then
|
||||
local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log'
|
||||
@ -79,7 +79,7 @@ elseif os.getenv('GDB') then
|
||||
end
|
||||
|
||||
if prepend_argv then
|
||||
local new_nvim_argv = {}
|
||||
local new_nvim_argv = {} --- @type string[]
|
||||
local len = #prepend_argv
|
||||
for i = 1, len do
|
||||
new_nvim_argv[i] = prepend_argv[i]
|
||||
@ -91,10 +91,13 @@ if prepend_argv then
|
||||
module.prepend_argv = prepend_argv
|
||||
end
|
||||
|
||||
local session, loop_running, last_error, method_error
|
||||
local session --- @type test.Session?
|
||||
local loop_running --- @type boolean?
|
||||
local last_error --- @type string?
|
||||
local method_error --- @type string?
|
||||
|
||||
if not is_os('win') then
|
||||
local sigpipe_handler = uv.new_signal()
|
||||
local sigpipe_handler = assert(uv.new_signal())
|
||||
uv.signal_start(sigpipe_handler, 'sigpipe', function()
|
||||
print('warning: got SIGPIPE signal. Likely related to a crash in nvim')
|
||||
end)
|
||||
@ -108,10 +111,15 @@ function module.set_session(s)
|
||||
session = s
|
||||
end
|
||||
|
||||
--- @param method string
|
||||
--- @param ... any
|
||||
--- @return any
|
||||
function module.request(method, ...)
|
||||
assert(session)
|
||||
local status, rv = session:request(method, ...)
|
||||
if not status then
|
||||
if loop_running then
|
||||
--- @type string
|
||||
last_error = rv[2]
|
||||
session:stop()
|
||||
else
|
||||
@ -121,12 +129,18 @@ function module.request(method, ...)
|
||||
return rv
|
||||
end
|
||||
|
||||
--- @param method string
|
||||
--- @param ... any
|
||||
--- @return any
|
||||
function module.request_lua(method, ...)
|
||||
return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...)
|
||||
end
|
||||
|
||||
--- @param timeout? integer
|
||||
--- @return string?
|
||||
function module.next_msg(timeout)
|
||||
return session:next_message(timeout and timeout or 10000)
|
||||
assert(session)
|
||||
return session:next_message(timeout or 10000)
|
||||
end
|
||||
|
||||
function module.expect_twostreams(msgs1, msgs2)
|
||||
@ -164,6 +178,7 @@ function module.expect_msg_seq(...)
|
||||
error('invalid args')
|
||||
end
|
||||
local ignore = arg1['ignore'] and arg1['ignore'] or {}
|
||||
--- @type string[]
|
||||
local seqs = arg1['seqs'] and arg1['seqs'] or { ... }
|
||||
if type(ignore) ~= 'table' then
|
||||
error("'ignore' arg must be a list of strings")
|
||||
@ -213,6 +228,7 @@ function module.expect_msg_seq(...)
|
||||
local message = result
|
||||
if type(result) == 'table' then
|
||||
-- 'eq' returns several things
|
||||
--- @type string
|
||||
message = result.message
|
||||
end
|
||||
final_error = cat_err(final_error, message)
|
||||
@ -234,8 +250,16 @@ function module.set_method_error(err)
|
||||
method_error = err
|
||||
end
|
||||
|
||||
--- @param lsession test.Session
|
||||
--- @param request_cb function
|
||||
--- @param notification_cb function
|
||||
--- @param setup_cb function
|
||||
--- @param timeout integer
|
||||
--- @return {[1]: integer, [2]: string}
|
||||
function module.run_session(lsession, request_cb, notification_cb, setup_cb, timeout)
|
||||
local on_request, on_notification, on_setup
|
||||
local on_request --- @type function?
|
||||
local on_notification --- @type function?
|
||||
local on_setup --- @type function?
|
||||
|
||||
if request_cb then
|
||||
function on_request(method, args)
|
||||
@ -273,11 +297,12 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
|
||||
end
|
||||
|
||||
function module.run(request_cb, notification_cb, setup_cb, timeout)
|
||||
assert(session)
|
||||
return module.run_session(session, request_cb, notification_cb, setup_cb, timeout)
|
||||
end
|
||||
|
||||
function module.stop()
|
||||
session:stop()
|
||||
assert(session):stop()
|
||||
end
|
||||
|
||||
function module.nvim_prog_abs()
|
||||
@ -301,6 +326,7 @@ function module.expect_exit(fn_or_timeout, ...)
|
||||
eof_err_msg,
|
||||
module.pcall_err(function(timeout, fn, ...)
|
||||
fn(...)
|
||||
assert(session)
|
||||
while session:next_message(timeout) do
|
||||
end
|
||||
if session.eof_err then
|
||||
@ -311,14 +337,18 @@ function module.expect_exit(fn_or_timeout, ...)
|
||||
end
|
||||
end
|
||||
|
||||
-- Executes a Vimscript function via Lua.
|
||||
-- Fails on Vimscript error, but does not update v:errmsg.
|
||||
--- Executes a Vimscript function via Lua.
|
||||
--- Fails on Vimscript error, but does not update v:errmsg.
|
||||
--- @param name string
|
||||
--- @param ... any
|
||||
--- @return any
|
||||
function module.call_lua(name, ...)
|
||||
return module.exec_lua([[return vim.call(...)]], name, ...)
|
||||
end
|
||||
|
||||
-- Sends user input to Nvim.
|
||||
-- Does not fail on Vimscript error, but v:errmsg will be updated.
|
||||
--- Sends user input to Nvim.
|
||||
--- Does not fail on Vimscript error, but v:errmsg will be updated.
|
||||
--- @param input string
|
||||
local function nvim_feed(input)
|
||||
while #input > 0 do
|
||||
local written = module.request('nvim_input', input)
|
||||
@ -330,22 +360,27 @@ local function nvim_feed(input)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param ... string
|
||||
function module.feed(...)
|
||||
for _, v in ipairs({ ... }) do
|
||||
nvim_feed(dedent(v))
|
||||
end
|
||||
end
|
||||
|
||||
--- @param ... string
|
||||
function module.rawfeed(...)
|
||||
for _, v in ipairs({ ... }) do
|
||||
nvim_feed(dedent(v))
|
||||
end
|
||||
end
|
||||
|
||||
---@param ... string[]?
|
||||
---@return string[]
|
||||
function module.merge_args(...)
|
||||
local i = 1
|
||||
local argv = {}
|
||||
local argv = {} --- @type string[]
|
||||
for anum = 1, select('#', ...) do
|
||||
--- @type string[]?
|
||||
local args = select(anum, ...)
|
||||
if args then
|
||||
for _, arg in ipairs(args) do
|
||||
@ -357,26 +392,29 @@ function module.merge_args(...)
|
||||
return argv
|
||||
end
|
||||
|
||||
-- Removes Nvim startup args from `args` matching items in `args_rm`.
|
||||
--
|
||||
-- - Special case: "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
|
||||
-- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', }
|
||||
--
|
||||
-- Example:
|
||||
-- args={'--headless', '-u', 'NONE'}
|
||||
-- args_rm={'--cmd', '-u'}
|
||||
-- Result:
|
||||
-- {'--headless'}
|
||||
--
|
||||
-- All matching cases are removed.
|
||||
--
|
||||
-- Example:
|
||||
-- args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
|
||||
-- args_rm={'--cmd', '-u'}
|
||||
-- Result:
|
||||
-- {'-N'}
|
||||
--- Removes Nvim startup args from `args` matching items in `args_rm`.
|
||||
---
|
||||
--- - Special case: "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
|
||||
--- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', }
|
||||
---
|
||||
--- Example:
|
||||
--- args={'--headless', '-u', 'NONE'}
|
||||
--- args_rm={'--cmd', '-u'}
|
||||
--- Result:
|
||||
--- {'--headless'}
|
||||
---
|
||||
--- All matching cases are removed.
|
||||
---
|
||||
--- Example:
|
||||
--- args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
|
||||
--- args_rm={'--cmd', '-u'}
|
||||
--- Result:
|
||||
--- {'-N'}
|
||||
--- @param args string[]
|
||||
--- @param args_rm string[]
|
||||
--- @return string[]
|
||||
local function remove_args(args, args_rm)
|
||||
local new_args = {}
|
||||
local new_args = {} --- @type string[]
|
||||
local skip_following = { '-u', '-i', '-c', '--cmd', '-s', '--listen' }
|
||||
if not args_rm or #args_rm == 0 then
|
||||
return { unpack(args) }
|
||||
@ -421,7 +459,12 @@ function module.check_close()
|
||||
session = nil
|
||||
end
|
||||
|
||||
--- @param io_extra used for stdin_fd, see :help ui-option
|
||||
--- @param argv string[]
|
||||
--- @param merge boolean?
|
||||
--- @param env string[]?
|
||||
--- @param keep boolean
|
||||
--- @param io_extra uv.uv_pipe_t? used for stdin_fd, see :help ui-option
|
||||
--- @return test.Session
|
||||
function module.spawn(argv, merge, env, keep, io_extra)
|
||||
if not keep then
|
||||
module.check_close()
|
||||
@ -457,16 +500,27 @@ function module.clear(...)
|
||||
return module.get_session()
|
||||
end
|
||||
|
||||
-- same params as clear, but does returns the session instead
|
||||
-- of replacing the default session
|
||||
--- same params as clear, but does returns the session instead
|
||||
--- of replacing the default session
|
||||
--- @return test.Session
|
||||
function module.spawn_argv(keep, ...)
|
||||
local argv, env, io_extra = module.new_argv(...)
|
||||
return module.spawn(argv, nil, env, keep, io_extra)
|
||||
end
|
||||
|
||||
-- Builds an argument list for use in clear().
|
||||
--
|
||||
---@see clear() for parameters.
|
||||
--- @class test.new_argv.Opts
|
||||
--- @field args? string[]
|
||||
--- @field args_rm? string[]
|
||||
--- @field env? table<string,string>
|
||||
--- @field io_extra? uv.uv_pipe_t
|
||||
|
||||
--- Builds an argument list for use in clear().
|
||||
---
|
||||
--- @see clear() for parameters.
|
||||
--- @param ... string
|
||||
--- @return string[]
|
||||
--- @return string[]?
|
||||
--- @return uv.uv_pipe_t?
|
||||
function module.new_argv(...)
|
||||
local args = { unpack(module.nvim_argv) }
|
||||
table.insert(args, '--headless')
|
||||
@ -475,16 +529,17 @@ function module.new_argv(...)
|
||||
table.insert(args, '--listen')
|
||||
table.insert(args, _G._nvim_test_id)
|
||||
end
|
||||
local new_args
|
||||
local io_extra
|
||||
local env = nil
|
||||
local new_args --- @type string[]
|
||||
local io_extra --- @type uv.uv_pipe_t?
|
||||
local env --- @type string[]?
|
||||
--- @type test.new_argv.Opts|string
|
||||
local opts = select(1, ...)
|
||||
if type(opts) ~= 'table' then
|
||||
new_args = { ... }
|
||||
else
|
||||
args = remove_args(args, opts.args_rm)
|
||||
if opts.env then
|
||||
local env_opt = {}
|
||||
local env_opt = {} --- @type table<string,string>
|
||||
for k, v in pairs(opts.env) do
|
||||
assert(type(k) == 'string')
|
||||
assert(type(v) == 'string')
|
||||
@ -523,6 +578,7 @@ function module.new_argv(...)
|
||||
return args, env, io_extra
|
||||
end
|
||||
|
||||
--- @param ... string
|
||||
function module.insert(...)
|
||||
nvim_feed('i')
|
||||
for _, v in ipairs({ ... }) do
|
||||
@ -532,8 +588,9 @@ function module.insert(...)
|
||||
nvim_feed('<ESC>')
|
||||
end
|
||||
|
||||
-- Executes an ex-command by user input. Because nvim_input() is used, Vimscript
|
||||
-- errors will not manifest as client (lua) errors. Use command() for that.
|
||||
--- Executes an ex-command by user input. Because nvim_input() is used, Vimscript
|
||||
--- errors will not manifest as client (lua) errors. Use command() for that.
|
||||
--- @param ... string
|
||||
function module.feed_command(...)
|
||||
for _, v in ipairs({ ... }) do
|
||||
if v:sub(1, 1) ~= '/' then
|
||||
@ -587,6 +644,9 @@ end
|
||||
|
||||
function module.create_callindex(func)
|
||||
return setmetatable({}, {
|
||||
--- @param tbl table<any,function>
|
||||
--- @param arg1 string
|
||||
--- @return function
|
||||
__index = function(tbl, arg1)
|
||||
local ret = function(...)
|
||||
return func(arg1, ...)
|
||||
@ -597,12 +657,17 @@ function module.create_callindex(func)
|
||||
})
|
||||
end
|
||||
|
||||
--- @param method string
|
||||
--- @param ... any
|
||||
function module.nvim_async(method, ...)
|
||||
session:notify(method, ...)
|
||||
assert(session):notify(method, ...)
|
||||
end
|
||||
|
||||
-- Executes a Vimscript function via RPC.
|
||||
-- Fails on Vimscript error, but does not update v:errmsg.
|
||||
--- Executes a Vimscript function via RPC.
|
||||
--- Fails on Vimscript error, but does not update v:errmsg.
|
||||
--- @param name string
|
||||
--- @param ... any
|
||||
--- @return any
|
||||
function module.call(name, ...)
|
||||
return module.request('nvim_call_function', name, { ... })
|
||||
end
|
||||
@ -637,6 +702,7 @@ module.api = vim.api
|
||||
module.fn = vim.fn
|
||||
|
||||
for name, fns in pairs(module.rpc) do
|
||||
--- @diagnostic disable-next-line:no-unknown
|
||||
module[name] = fns
|
||||
end
|
||||
|
||||
@ -673,6 +739,10 @@ function module.expect_any(contents)
|
||||
return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true))
|
||||
end
|
||||
|
||||
--- @param expected any[]
|
||||
--- @param received any[]
|
||||
--- @param kind string
|
||||
--- @return any
|
||||
function module.expect_events(expected, received, kind)
|
||||
if not pcall(eq, expected, received) then
|
||||
local msg = 'unexpected ' .. kind .. ' received.\n\n'
|
||||
@ -712,6 +782,7 @@ function module.assert_visible(bufnr, visible)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param path string
|
||||
local function do_rmdir(path)
|
||||
local stat = uv.fs_stat(path)
|
||||
if stat == nil then
|
||||
@ -779,14 +850,17 @@ function module.exc_exec(cmd)
|
||||
return ret
|
||||
end
|
||||
|
||||
--- @param cond boolean
|
||||
--- @param reason string
|
||||
--- @return boolean
|
||||
function module.skip(cond, reason)
|
||||
if cond then
|
||||
--- @type fun(reason: string)
|
||||
local pending = getfenv(2).pending
|
||||
pending(reason or 'FIXME')
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Calls pending() and returns `true` if the system is too slow to
|
||||
@ -809,6 +883,8 @@ function module.exec(code)
|
||||
module.api.nvim_exec2(code, {})
|
||||
end
|
||||
|
||||
--- @param code string
|
||||
--- @return string
|
||||
function module.exec_capture(code)
|
||||
return module.api.nvim_exec2(code, { output = true }).output
|
||||
end
|
||||
@ -855,19 +931,24 @@ function module.new_pipename()
|
||||
return pipename
|
||||
end
|
||||
|
||||
--- @param provider string
|
||||
--- @return string|false?
|
||||
function module.missing_provider(provider)
|
||||
if provider == 'ruby' or provider == 'node' or provider == 'perl' then
|
||||
--- @type string?
|
||||
local e = module.fn['provider#' .. provider .. '#Detect']()[2]
|
||||
return e ~= '' and e or false
|
||||
elseif provider == 'python' or provider == 'python3' then
|
||||
local py_major_version = (provider == 'python3' and 3 or 2)
|
||||
--- @type string?
|
||||
local e = module.fn['provider#pythonx#Detect'](py_major_version)[2]
|
||||
return e ~= '' and e or false
|
||||
else
|
||||
assert(false, 'Unknown provider: ' .. provider)
|
||||
end
|
||||
assert(false, 'Unknown provider: ' .. provider)
|
||||
end
|
||||
|
||||
--- @param obj string|table
|
||||
--- @return any
|
||||
function module.alter_slashes(obj)
|
||||
if not is_os('win') then
|
||||
return obj
|
||||
@ -876,14 +957,14 @@ function module.alter_slashes(obj)
|
||||
local ret = obj:gsub('/', '\\')
|
||||
return ret
|
||||
elseif type(obj) == 'table' then
|
||||
local ret = {}
|
||||
--- @cast obj table<any,any>
|
||||
local ret = {} --- @type table<any,any>
|
||||
for k, v in pairs(obj) do
|
||||
ret[k] = module.alter_slashes(v)
|
||||
end
|
||||
return ret
|
||||
else
|
||||
assert(false, 'expected string or table of strings, got ' .. type(obj))
|
||||
end
|
||||
assert(false, 'expected string or table of strings, got ' .. type(obj))
|
||||
end
|
||||
|
||||
local load_factor = 1
|
||||
@ -893,18 +974,25 @@ if global_helpers.is_ci() then
|
||||
module.request('nvim_command', 'source test/old/testdir/load.vim')
|
||||
load_factor = module.request('nvim_eval', 'g:test_load_factor')
|
||||
end
|
||||
|
||||
--- @param num number
|
||||
--- @return number
|
||||
function module.load_adjust(num)
|
||||
return math.ceil(num * load_factor)
|
||||
end
|
||||
|
||||
--- @param ctx table<string,any>
|
||||
--- @return table
|
||||
function module.parse_context(ctx)
|
||||
local parsed = {}
|
||||
local parsed = {} --- @type table<string,any>
|
||||
for _, item in ipairs({ 'regs', 'jumps', 'bufs', 'gvars' }) do
|
||||
--- @param v any
|
||||
parsed[item] = vim.tbl_filter(function(v)
|
||||
return type(v) == 'table'
|
||||
end, module.call('msgpackparse', ctx[item]))
|
||||
end
|
||||
parsed['bufs'] = parsed['bufs'][1]
|
||||
--- @param v any
|
||||
return vim.tbl_map(function(v)
|
||||
if #v == 0 then
|
||||
return nil
|
||||
@ -918,7 +1006,9 @@ function module.add_builddir_to_rtp()
|
||||
module.command(string.format([[set rtp+=%s/runtime]], module.paths.test_build_dir))
|
||||
end
|
||||
|
||||
-- Kill (reap) a process by PID.
|
||||
--- Kill (reap) a process by PID.
|
||||
--- @param pid string
|
||||
--- @return boolean?
|
||||
function module.os_kill(pid)
|
||||
return os.execute(
|
||||
(
|
||||
@ -928,7 +1018,9 @@ function module.os_kill(pid)
|
||||
)
|
||||
end
|
||||
|
||||
-- Create folder with non existing parents
|
||||
--- Create folder with non existing parents
|
||||
--- @param path string
|
||||
--- @return boolean?
|
||||
function module.mkdir_p(path)
|
||||
return os.execute((is_os('win') and 'mkdir ' .. path or 'mkdir -p ' .. path))
|
||||
end
|
||||
|
102
test/helpers.lua
102
test/helpers.lua
@ -63,8 +63,12 @@ function module.popen_r(...)
|
||||
return io.popen(module.argss_to_cmd(...), 'r')
|
||||
end
|
||||
|
||||
-- Calls fn() until it succeeds, up to `max` times or until `max_ms`
|
||||
-- milliseconds have passed.
|
||||
--- Calls fn() until it succeeds, up to `max` times or until `max_ms`
|
||||
--- milliseconds have passed.
|
||||
--- @param max integer?
|
||||
--- @param max_ms integer?
|
||||
--- @param fn function
|
||||
--- @return any
|
||||
function module.retry(max, max_ms, fn)
|
||||
luaassert(max == nil or max > 0)
|
||||
luaassert(max_ms == nil or max_ms > 0)
|
||||
@ -72,6 +76,7 @@ function module.retry(max, max_ms, fn)
|
||||
local timeout = (max_ms and max_ms or 10000)
|
||||
local start_time = uv.now()
|
||||
while true do
|
||||
--- @type boolean, any
|
||||
local status, result = pcall(fn)
|
||||
if status then
|
||||
return result
|
||||
@ -121,6 +126,9 @@ function module.fail(msg)
|
||||
return luaassert.epicfail(msg)
|
||||
end
|
||||
|
||||
--- @param pat string
|
||||
--- @param actual string
|
||||
--- @return boolean
|
||||
function module.matches(pat, actual)
|
||||
if nil ~= string.match(actual, pat) then
|
||||
return true
|
||||
@ -170,10 +178,16 @@ end
|
||||
--- Asserts that `pat` does NOT match any line in the tail of `logfile`.
|
||||
---
|
||||
--- @see assert_log
|
||||
--- @param pat (string) Lua pattern to match lines in the log file
|
||||
--- @param logfile? (string) Full path to log file (default=$NVIM_LOG_FILE)
|
||||
--- @param nrlines? (number) Search up to this many log lines
|
||||
function module.assert_nolog(pat, logfile, nrlines)
|
||||
return module.assert_log(pat, logfile, nrlines, true)
|
||||
end
|
||||
|
||||
--- @param fn fun(...): any
|
||||
--- @param ... any
|
||||
--- @return boolean, any
|
||||
function module.pcall(fn, ...)
|
||||
luaassert(type(fn) == 'function')
|
||||
local status, rv = pcall(fn, ...)
|
||||
@ -221,6 +235,8 @@ end
|
||||
-- -- Match Lua pattern.
|
||||
-- matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2'))
|
||||
--
|
||||
--- @param fn function
|
||||
--- @return string
|
||||
function module.pcall_err_withfile(fn, ...)
|
||||
luaassert(type(fn) == 'function')
|
||||
local status, rv = module.pcall(fn, ...)
|
||||
@ -230,19 +246,29 @@ function module.pcall_err_withfile(fn, ...)
|
||||
return rv
|
||||
end
|
||||
|
||||
--- @param fn function
|
||||
--- @param ... any
|
||||
--- @return string
|
||||
function module.pcall_err_withtrace(fn, ...)
|
||||
local errmsg = module.pcall_err_withfile(fn, ...)
|
||||
|
||||
return errmsg
|
||||
:gsub('^%.%.%./helpers%.lua:0: ', '')
|
||||
:gsub('^Error executing lua:- ', '')
|
||||
:gsub('^%[string "<nvim>"%]:0: ', '')
|
||||
return (
|
||||
errmsg
|
||||
:gsub('^%.%.%./helpers%.lua:0: ', '')
|
||||
:gsub('^Error executing lua:- ', '')
|
||||
:gsub('^%[string "<nvim>"%]:0: ', '')
|
||||
)
|
||||
end
|
||||
|
||||
function module.pcall_err(...)
|
||||
return module.remove_trace(module.pcall_err_withtrace(...))
|
||||
--- @param fn function
|
||||
--- @param ... any
|
||||
--- @return string
|
||||
function module.pcall_err(fn, ...)
|
||||
return module.remove_trace(module.pcall_err_withtrace(fn, ...))
|
||||
end
|
||||
|
||||
--- @param s string
|
||||
--- @return string
|
||||
function module.remove_trace(s)
|
||||
return (s:gsub('\n%s*stack traceback:.*', ''))
|
||||
end
|
||||
@ -252,9 +278,9 @@ end
|
||||
-- exc_re: exclude pattern(s) (string or table)
|
||||
function module.glob(initial_path, re, exc_re)
|
||||
exc_re = type(exc_re) == 'table' and exc_re or { exc_re }
|
||||
local paths_to_check = { initial_path }
|
||||
local ret = {}
|
||||
local checked_files = {}
|
||||
local paths_to_check = { initial_path } --- @type string[]
|
||||
local ret = {} --- @type string[]
|
||||
local checked_files = {} --- @type table<string,true>
|
||||
local function is_excluded(path)
|
||||
for _, pat in pairs(exc_re) do
|
||||
if path:match(pat) then
|
||||
@ -301,7 +327,7 @@ function module.check_logs()
|
||||
local file = log_dir .. '/' .. tail
|
||||
local fd = assert(io.open(file))
|
||||
local start_msg = ('='):rep(20) .. ' File ' .. file .. ' ' .. ('='):rep(20)
|
||||
local lines = {}
|
||||
local lines = {} --- @type string[]
|
||||
local warning_line = 0
|
||||
for line in fd:lines() do
|
||||
local cur_warning_line = check_logs_useless_lines[line]
|
||||
@ -313,6 +339,7 @@ function module.check_logs()
|
||||
end
|
||||
fd:close()
|
||||
if #lines > 0 then
|
||||
--- @type boolean?, file*?
|
||||
local status, f
|
||||
local out = io.stdout
|
||||
if os.getenv('SYMBOLIZER') then
|
||||
@ -320,6 +347,7 @@ function module.check_logs()
|
||||
end
|
||||
out:write(start_msg .. '\n')
|
||||
if status then
|
||||
assert(f)
|
||||
for line in f:lines() do
|
||||
out:write('= ' .. line .. '\n')
|
||||
end
|
||||
@ -364,9 +392,11 @@ local function tmpdir_get()
|
||||
return os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP')
|
||||
end
|
||||
|
||||
-- Is temp directory `dir` defined local to the project workspace?
|
||||
--- Is temp directory `dir` defined local to the project workspace?
|
||||
--- @param dir string?
|
||||
--- @return boolean
|
||||
local function tmpdir_is_local(dir)
|
||||
return not not (dir and string.find(dir, 'Xtest'))
|
||||
return not not (dir and dir:find('Xtest'))
|
||||
end
|
||||
|
||||
--- Creates a new temporary file for use by tests.
|
||||
@ -410,6 +440,7 @@ function module.check_cores(app, force) -- luacheck: ignore
|
||||
return
|
||||
end
|
||||
app = app or 'build/bin/nvim' -- luacheck: ignore
|
||||
--- @type string, string?, string[]
|
||||
local initial_path, re, exc_re
|
||||
local gdb_db_cmd =
|
||||
'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"'
|
||||
@ -422,14 +453,14 @@ function module.check_cores(app, force) -- luacheck: ignore
|
||||
and relpath(tmpdir_get()):gsub('^[ ./]+', ''):gsub('%/+$', ''):gsub('([^%w])', '%%%1')
|
||||
or nil
|
||||
)
|
||||
local db_cmd
|
||||
local db_cmd --- @type string
|
||||
local test_glob_dir = os.getenv('NVIM_TEST_CORE_GLOB_DIRECTORY')
|
||||
if test_glob_dir and test_glob_dir ~= '' then
|
||||
initial_path = test_glob_dir
|
||||
re = os.getenv('NVIM_TEST_CORE_GLOB_RE')
|
||||
exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir }
|
||||
db_cmd = os.getenv('NVIM_TEST_CORE_DB_CMD') or gdb_db_cmd
|
||||
random_skip = os.getenv('NVIM_TEST_CORE_RANDOM_SKIP')
|
||||
random_skip = os.getenv('NVIM_TEST_CORE_RANDOM_SKIP') ~= ''
|
||||
elseif module.is_os('mac') then
|
||||
initial_path = '/cores'
|
||||
re = nil
|
||||
@ -487,17 +518,24 @@ function module.repeated_read_cmd(...)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- @generic T
|
||||
--- @param orig T
|
||||
--- @return T
|
||||
function module.shallowcopy(orig)
|
||||
if type(orig) ~= 'table' then
|
||||
return orig
|
||||
end
|
||||
local copy = {}
|
||||
--- @cast orig table<any,any>
|
||||
local copy = {} --- @type table<any,any>
|
||||
for orig_key, orig_value in pairs(orig) do
|
||||
copy[orig_key] = orig_value
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
--- @param d1 table<any,any>
|
||||
--- @param d2 table<any,any>
|
||||
--- @return table<any,any>
|
||||
function module.mergedicts_copy(d1, d2)
|
||||
local ret = module.shallowcopy(d1)
|
||||
for k, v in pairs(d2) do
|
||||
@ -512,11 +550,13 @@ function module.mergedicts_copy(d1, d2)
|
||||
return ret
|
||||
end
|
||||
|
||||
-- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2
|
||||
--
|
||||
-- Note: does not do copies of d2 values used.
|
||||
--- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2
|
||||
---
|
||||
--- Note: does not do copies of d2 values used.
|
||||
--- @param d1 table<any,any>
|
||||
--- @param d2 table<any,any>
|
||||
function module.dictdiff(d1, d2)
|
||||
local ret = {}
|
||||
local ret = {} --- @type table<any,any>
|
||||
local hasdiff = false
|
||||
for k, v in pairs(d1) do
|
||||
if d2[k] == nil then
|
||||
@ -554,8 +594,9 @@ end
|
||||
|
||||
-- Concat list-like tables.
|
||||
function module.concat_tables(...)
|
||||
local ret = {}
|
||||
local ret = {} --- @type table<any,any>
|
||||
for i = 1, select('#', ...) do
|
||||
--- @type table<any,any>
|
||||
local tbl = select(i, ...)
|
||||
if tbl then
|
||||
for _, v in ipairs(tbl) do
|
||||
@ -787,10 +828,10 @@ function module.hexdump(str)
|
||||
return dump .. hex .. string.rep(' ', 8 - len % 8) .. asc
|
||||
end
|
||||
|
||||
-- Reads text lines from `filename` into a table.
|
||||
--
|
||||
-- filename: path to file
|
||||
-- start: start line (1-indexed), negative means "lines before end" (tail)
|
||||
--- Reads text lines from `filename` into a table.
|
||||
--- @param filename string path to file
|
||||
--- @param start? integer start line (1-indexed), negative means "lines before end" (tail)
|
||||
--- @return string[]?
|
||||
function module.read_file_list(filename, start)
|
||||
local lnum = (start ~= nil and type(start) == 'number') and start or 1
|
||||
local tail = (lnum < 0)
|
||||
@ -826,9 +867,9 @@ function module.read_file_list(filename, start)
|
||||
return lines
|
||||
end
|
||||
|
||||
-- Reads the entire contents of `filename` into a string.
|
||||
--
|
||||
-- filename: path to file
|
||||
--- Reads the entire contents of `filename` into a string.
|
||||
--- @param filename string
|
||||
--- @return string?
|
||||
function module.read_file(filename)
|
||||
local file = io.open(filename, 'r')
|
||||
if not file then
|
||||
@ -844,6 +885,7 @@ function module.write_file(name, text, no_dedent, append)
|
||||
local file = assert(io.open(name, (append and 'a' or 'w')))
|
||||
if type(text) == 'table' then
|
||||
-- Byte blob
|
||||
--- @type string[]
|
||||
local bytes = text
|
||||
text = ''
|
||||
for _, char in ipairs(bytes) do
|
||||
@ -857,6 +899,8 @@ function module.write_file(name, text, no_dedent, append)
|
||||
file:close()
|
||||
end
|
||||
|
||||
--- @param name? 'cirrus'|'github'
|
||||
--- @return boolean
|
||||
function module.is_ci(name)
|
||||
local any = (name == nil)
|
||||
luaassert(any or name == 'github' or name == 'cirrus')
|
||||
|
Loading…
Reference in New Issue
Block a user