diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /test/functional/testnvim.lua | |
parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
download | rneovim-rahm.tar.gz rneovim-rahm.tar.bz2 rneovim-rahm.zip |
Diffstat (limited to 'test/functional/testnvim.lua')
-rw-r--r-- | test/functional/testnvim.lua | 281 |
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() |