aboutsummaryrefslogtreecommitdiff
path: root/test/functional/testnvim.lua
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/testnvim.lua')
-rw-r--r--test/functional/testnvim.lua281
1 files changed, 98 insertions, 183 deletions
diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua
index 60b2f872fc..59cb593cf7 100644
--- a/test/functional/testnvim.lua
+++ b/test/functional/testnvim.lua
@@ -4,7 +4,7 @@ local t = require('test.testutil')
local Session = require('test.client.session')
local uv_stream = require('test.client.uv_stream')
local SocketStream = uv_stream.SocketStream
-local ChildProcessStream = uv_stream.ChildProcessStream
+local ProcStream = uv_stream.ProcStream
local check_cores = t.check_cores
local check_logs = t.check_logs
@@ -48,6 +48,16 @@ M.nvim_argv = {
'unlet g:colors_name',
'--embed',
}
+if os.getenv('OSV_PORT') then
+ table.insert(M.nvim_argv, '--cmd')
+ table.insert(
+ M.nvim_argv,
+ string.format(
+ "lua require('osv').launch({ port = %s, blocking = true })",
+ os.getenv('OSV_PORT')
+ )
+ )
+end
-- Directory containing nvim.
M.nvim_dir = M.nvim_prog:gsub('[/\\][^/\\]+$', '')
@@ -308,24 +318,14 @@ function M.stop()
assert(session):stop()
end
-function M.nvim_prog_abs()
- -- system(['build/bin/nvim']) does not work for whatever reason. It must
- -- be executable searched in $PATH or something starting with / or ./.
- if M.nvim_prog:match('[/\\]') then
- return M.request('nvim_call_function', 'fnamemodify', { M.nvim_prog, ':p' })
- else
- return M.nvim_prog
- end
-end
-
-- Use for commands which expect nvim to quit.
-- The first argument can also be a timeout.
function M.expect_exit(fn_or_timeout, ...)
local eof_err_msg = 'EOF was received from Nvim. Likely the Nvim process crashed.'
if type(fn_or_timeout) == 'function' then
- eq(eof_err_msg, t.pcall_err(fn_or_timeout, ...))
+ t.matches(eof_err_msg, t.pcall_err(fn_or_timeout, ...))
else
- eq(
+ t.matches(
eof_err_msg,
t.pcall_err(function(timeout, fn, ...)
fn(...)
@@ -455,22 +455,6 @@ function M.check_close()
session = nil
end
---- @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 M.spawn(argv, merge, env, keep, io_extra)
- if not keep then
- M.check_close()
- end
-
- local child_stream =
- ChildProcessStream.spawn(merge and M.merge_args(prepend_argv, argv) or argv, env, io_extra)
- return Session.new(child_stream)
-end
-
-- Creates a new Session connected by domain socket (named pipe) or TCP.
function M.connect(file_or_address)
local addr, port = string.match(file_or_address, '(.*):(%d+)')
@@ -479,61 +463,112 @@ function M.connect(file_or_address)
return Session.new(stream)
end
--- Starts (and returns) a new global Nvim session.
---
--- Parameters are interpreted as startup args, OR a map with these keys:
--- args: List: Args appended to the default `nvim_argv` set.
--- args_rm: List: Args removed from the default set. All cases are
--- removed, e.g. args_rm={'--cmd'} removes all cases of "--cmd"
--- (and its value) from the default set.
--- env: Map: Defines the environment of the new session.
---
--- Example:
--- clear('-e')
--- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
+--- Starts a new, global Nvim session and clears the current one.
+---
+--- Note: Use `new_session()` to start a session without replacing the current one.
+---
+--- Parameters are interpreted as startup args, OR a map with these keys:
+--- - args: List: Args appended to the default `nvim_argv` set.
+--- - args_rm: List: Args removed from the default set. All cases are
+--- removed, e.g. args_rm={'--cmd'} removes all cases of "--cmd"
+--- (and its value) from the default set.
+--- - env: Map: Defines the environment of the new session.
+---
+--- Example:
+--- ```
+--- clear('-e')
+--- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
+--- ```
+---
+--- @param ... string Nvim CLI args
+--- @return test.Session
+--- @overload fun(opts: test.session.Opts): test.Session
function M.clear(...)
- M.set_session(M.spawn_argv(false, ...))
+ M.set_session(M.new_session(false, ...))
return M.get_session()
end
---- same params as clear, but does returns the session instead
---- of replacing the default session
+--- Starts a new Nvim process with the given args and returns a msgpack-RPC session.
+---
+--- Does not replace the current global session, unlike `clear()`.
+---
+--- @param keep boolean (default: false) Don't close the current global session.
+--- @param ... string Nvim CLI args (or see overload)
--- @return test.Session
-function M.spawn_argv(keep, ...)
- local argv, env, io_extra = M.new_argv(...)
- return M.spawn(argv, nil, env, keep, io_extra)
+--- @overload fun(keep: boolean, opts: test.session.Opts): test.Session
+function M.new_session(keep, ...)
+ if not keep then
+ M.check_close()
+ end
+
+ local argv, env, io_extra = M._new_argv(...)
+
+ local proc = ProcStream.spawn(argv, env, io_extra)
+ return Session.new(proc)
end
---- @class test.new_argv.Opts
+--- Starts a (non-RPC, `--headless --listen "Tx"`) Nvim process, waits for exit, and returns result.
+---
+--- @param ... string Nvim CLI args, or `test.session.Opts` table.
+--- @return test.ProcStream
+--- @overload fun(opts: test.session.Opts): test.ProcStream
+function M.spawn_wait(...)
+ local opts = type(...) == 'string' and { args = { ... } } or ...
+ opts.args_rm = opts.args_rm and opts.args_rm or {}
+ table.insert(opts.args_rm, '--embed')
+ local argv, env, io_extra = M._new_argv(opts)
+ local proc = ProcStream.spawn(argv, env, io_extra)
+ proc.collect_text = true
+ proc:read_start()
+ proc:wait()
+ proc:close()
+ return proc
+end
+
+--- @class test.session.Opts
+--- Nvim CLI args
--- @field args? string[]
+--- Remove these args from the default `nvim_argv` args set. Ignored if `merge=false`.
--- @field args_rm? string[]
+--- (default: true) Merge `args` with the default set. Else use only the provided `args`.
+--- @field merge? boolean
+--- Environment variables
--- @field env? table<string,string>
+--- Used for stdin_fd, see `:help ui-option`
--- @field io_extra? uv.uv_pipe_t
---- Builds an argument list for use in clear().
+--- @private
---
---- @see clear() for parameters.
---- @param ... string
+--- Builds an argument list for use in `new_session()`, `clear()`, and `spawn_wait()`.
+---
+--- @param ... string Nvim CLI args, or `test.session.Opts` table.
--- @return string[]
--- @return string[]?
--- @return uv.uv_pipe_t?
-function M.new_argv(...)
- local args = { unpack(M.nvim_argv) }
- table.insert(args, '--headless')
- if _G._nvim_test_id then
- -- Set the server name to the test-id for logging. #8519
- table.insert(args, '--listen')
- table.insert(args, _G._nvim_test_id)
+--- @overload fun(opts: test.session.Opts): string[], string[]?, uv.uv_pipe_t?
+function M._new_argv(...)
+ --- @type test.session.Opts|string
+ local opts = select(1, ...)
+ local merge = type(opts) ~= 'table' and true or opts.merge ~= false
+
+ local args = merge and { unpack(M.nvim_argv) } or { M.nvim_prog }
+ if merge then
+ table.insert(args, '--headless')
+ if _G._nvim_test_id then
+ -- Set the server name to the test-id for logging. #8519
+ table.insert(args, '--listen')
+ table.insert(args, _G._nvim_test_id)
+ end
end
+
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, ...)
+ local env --- @type string[]? List of "key=value" env vars.
+
if type(opts) ~= 'table' then
new_args = { ... }
else
- args = remove_args(args, opts.args_rm)
+ args = merge and remove_args(args, opts.args_rm) or args
if opts.env then
local env_opt = {} --- @type table<string,string>
for k, v in pairs(opts.env) do
@@ -800,81 +835,6 @@ function M.exec_capture(code)
return M.api.nvim_exec2(code, { output = true }).output
end
---- @param f function
---- @return table<string,any>
-local function get_upvalues(f)
- local i = 1
- local upvalues = {} --- @type table<string,any>
- while true do
- local n, v = debug.getupvalue(f, i)
- if not n then
- break
- end
- upvalues[n] = v
- i = i + 1
- end
- return upvalues
-end
-
---- @param f function
---- @param upvalues table<string,any>
-local function set_upvalues(f, upvalues)
- local i = 1
- while true do
- local n = debug.getupvalue(f, i)
- if not n then
- break
- end
- if upvalues[n] then
- debug.setupvalue(f, i, upvalues[n])
- end
- i = i + 1
- end
-end
-
---- @type fun(f: function): table<string,any>
-_G.__get_upvalues = nil
-
---- @type fun(f: function, upvalues: table<string,any>)
-_G.__set_upvalues = nil
-
---- @param self table<string,function>
---- @param bytecode string
---- @param upvalues table<string,any>
---- @param ... any[]
---- @return any[] result
---- @return table<string,any> upvalues
-local function exec_lua_handler(self, bytecode, upvalues, ...)
- local f = assert(loadstring(bytecode))
- self.set_upvalues(f, upvalues)
- local ret = { f(...) } --- @type any[]
- --- @type table<string,any>
- local new_upvalues = self.get_upvalues(f)
-
- do -- Check return value types for better error messages
- local invalid_types = {
- ['thread'] = true,
- ['function'] = true,
- ['userdata'] = true,
- }
-
- for k, v in pairs(ret) do
- if invalid_types[type(v)] then
- error(
- string.format(
- "Return index %d with value '%s' of type '%s' cannot be serialized over RPC",
- k,
- tostring(v),
- type(v)
- )
- )
- end
- end
- end
-
- return ret, new_upvalues
-end
-
--- Execute Lua code in the wrapped Nvim session.
---
--- When `code` is passed as a function, it is converted into Lua byte code.
@@ -921,52 +881,7 @@ function M.exec_lua(code, ...)
end
assert(session, 'no Nvim session')
-
- if not session.exec_lua_setup then
- assert(
- session:request(
- 'nvim_exec_lua',
- [[
- _G.__test_exec_lua = {
- get_upvalues = loadstring((select(1,...))),
- set_upvalues = loadstring((select(2,...))),
- handler = loadstring((select(3,...)))
- }
- setmetatable(_G.__test_exec_lua, { __index = _G.__test_exec_lua })
- ]],
- { string.dump(get_upvalues), string.dump(set_upvalues), string.dump(exec_lua_handler) }
- )
- )
- session.exec_lua_setup = true
- end
-
- local stat, rv = session:request(
- 'nvim_exec_lua',
- 'return { _G.__test_exec_lua:handler(...) }',
- { string.dump(code), get_upvalues(code), ... }
- )
-
- if not stat then
- error(rv[2])
- end
-
- --- @type any[], table<string,any>
- local ret, upvalues = unpack(rv)
-
- -- Update upvalues
- if next(upvalues) then
- local caller = debug.getinfo(2)
- local f = caller.func
- -- On PUC-Lua, if the function is a tail call, then func will be nil.
- -- In this case we need to use the current function.
- if not f then
- assert(caller.source == '=(tail call)')
- f = debug.getinfo(1).func
- end
- set_upvalues(f, upvalues)
- end
-
- return unpack(ret, 1, table.maxn(ret))
+ return require('test.functional.testnvim.exec_lua')(session, 2, code, ...)
end
function M.get_pathsep()