Merge pull request #27024 from lewis6991/test_followup

test: big cleanup followup + typing
This commit is contained in:
Lewis Russell
2024-01-17 13:46:55 +00:00
committed by GitHub
23 changed files with 546 additions and 441 deletions

View File

@@ -190,12 +190,18 @@ function vim._os_proc_children(ppid)
return children return children
end end
--- @class vim.inspect.Opts
--- @field depth? integer
--- @field newline? string
--- @field process? fun(item:any, path: string[]): any
--- Gets a human-readable representation of the given object. --- Gets a human-readable representation of the given object.
--- ---
---@see |vim.print()| ---@see |vim.print()|
---@see https://github.com/kikito/inspect.lua ---@see https://github.com/kikito/inspect.lua
---@see https://github.com/mpeterv/vinspect ---@see https://github.com/mpeterv/vinspect
---@return string ---@return string
---@overload fun(x: any, opts?: vim.inspect.Opts): string
vim.inspect = vim.inspect vim.inspect = vim.inspect
do do

View File

@@ -22,6 +22,9 @@ function Response:send(value, is_error)
self._msgpack_rpc_stream._stream:write(data) self._msgpack_rpc_stream._stream:write(data)
end end
--- @class test.MsgpackRpcStream
--- @field private _stream test.Stream
--- @field private __pack table
local MsgpackRpcStream = {} local MsgpackRpcStream = {}
MsgpackRpcStream.__index = MsgpackRpcStream MsgpackRpcStream.__index = MsgpackRpcStream

View File

@@ -1,6 +1,12 @@
local uv = vim.uv local uv = vim.uv
local MsgpackRpcStream = require('test.client.msgpack_rpc_stream') 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 = {} local Session = {}
Session.__index = Session Session.__index = Session
if package.loaded['jit'] then if package.loaded['jit'] then
@@ -26,7 +32,7 @@ end
local function coroutine_exec(func, ...) local function coroutine_exec(func, ...)
local args = { ... } local args = { ... }
local on_complete local on_complete --- @type function?
if #args > 0 and type(args[#args]) == 'function' then if #args > 0 and type(args[#args]) == 'function' then
-- completion callback -- completion callback
@@ -54,6 +60,8 @@ function Session.new(stream)
}, Session) }, Session)
end end
--- @param timeout integer?
--- @return string?
function Session:next_message(timeout) function Session:next_message(timeout)
local function on_request(method, args, response) local function on_request(method, args, response)
table.insert(self._pending_messages, { '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, { ... }) self._msgpack_rpc_stream:write(method, { ... })
end end
--- @param method string
--- @param ... any
--- @return boolean
--- @return table
function Session:request(method, ...) function Session:request(method, ...)
local args = { ... } local args = { ... }
local err, result local err, result

View File

@@ -1,18 +1,28 @@
local uv = vim.uv 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 = {} local StdioStream = {}
StdioStream.__index = StdioStream StdioStream.__index = StdioStream
function StdioStream.open() function StdioStream.open()
local self = setmetatable({ local self = setmetatable({
_in = uv.new_pipe(false), _in = assert(uv.new_pipe(false)),
_out = uv.new_pipe(false), _out = assert(uv.new_pipe(false)),
}, StdioStream) }, StdioStream)
self._in:open(0) self._in:open(0)
self._out:open(1) self._out:open(1)
return self return self
end end
--- @param data string|string[]
function StdioStream:write(data) function StdioStream:write(data)
self._out:write(data) self._out:write(data)
end end
@@ -35,11 +45,14 @@ function StdioStream:close()
self._out:close() self._out:close()
end end
--- @class test.SocketStream : test.Stream
--- @field package _stream_error? string
--- @field package _socket uv.uv_pipe_t
local SocketStream = {} local SocketStream = {}
SocketStream.__index = SocketStream SocketStream.__index = SocketStream
function SocketStream.open(file) function SocketStream.open(file)
local socket = uv.new_pipe(false) local socket = assert(uv.new_pipe(false))
local self = setmetatable({ local self = setmetatable({
_socket = socket, _socket = socket,
_stream_error = nil, _stream_error = nil,
@@ -51,7 +64,7 @@ function SocketStream.open(file)
end end
function SocketStream.connect(host, port) function SocketStream.connect(host, port)
local socket = uv.new_tcp() local socket = assert(uv.new_tcp())
local self = setmetatable({ local self = setmetatable({
_socket = socket, _socket = socket,
_stream_error = nil, _stream_error = nil,
@@ -96,9 +109,20 @@ function SocketStream:close()
uv.close(self._socket) uv.close(self._socket)
end 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 = {} local ChildProcessStream = {}
ChildProcessStream.__index = 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) function ChildProcessStream.spawn(argv, env, io_extra)
local self = setmetatable({ local self = setmetatable({
_child_stdin = uv.new_pipe(false), _child_stdin = uv.new_pipe(false),
@@ -106,13 +130,15 @@ function ChildProcessStream.spawn(argv, env, io_extra)
_exiting = false, _exiting = false,
}, ChildProcessStream) }, ChildProcessStream)
local prog = argv[1] local prog = argv[1]
local args = {} local args = {} --- @type string[]
for i = 2, #argv do for i = 2, #argv do
args[#args + 1] = argv[i] args[#args + 1] = argv[i]
end end
--- @diagnostic disable-next-line:missing-fields
self._proc, self._pid = uv.spawn(prog, { self._proc, self._pid = uv.spawn(prog, {
stdio = { self._child_stdin, self._child_stdout, 2, io_extra }, stdio = { self._child_stdin, self._child_stdout, 2, io_extra },
args = args, args = args,
--- @diagnostic disable-next-line:assign-type-mismatch
env = env, env = env,
}, function(status, signal) }, function(status, signal)
self.status = status self.status = status

View File

@@ -1,9 +0,0 @@
-- Island of Misfit Toys
local M = {}
function M.redir_exec()
error('redir_exec is deprecated, use nvim_exec2() or pcall_err()')
end
return M

168
test/format_string.lua Normal file
View File

@@ -0,0 +1,168 @@
local luaassert = require('luassert')
local M = {}
local SUBTBL = {
'\\000',
'\\001',
'\\002',
'\\003',
'\\004',
'\\005',
'\\006',
'\\007',
'\\008',
'\\t',
'\\n',
'\\011',
'\\012',
'\\r',
'\\014',
'\\015',
'\\016',
'\\017',
'\\018',
'\\019',
'\\020',
'\\021',
'\\022',
'\\023',
'\\024',
'\\025',
'\\026',
'\\027',
'\\028',
'\\029',
'\\030',
'\\031',
}
--- @param v any
--- @return string
local function format_float(v)
-- On windows exponent appears to have three digits and not two
local ret = ('%.6e'):format(v)
local l, f, es, e = ret:match('^(%-?%d)%.(%d+)e([+%-])0*(%d%d+)$')
return l .. '.' .. f .. 'e' .. es .. e
end
-- Formats Lua value `v`.
--
-- TODO(justinmk): redundant with vim.inspect() ?
--
-- "Nice table formatting similar to screen:snapshot_util()".
-- Commit: 520c0b91a528
function M.format_luav(v, indent, opts)
opts = opts or {}
local linesep = '\n'
local next_indent_arg = nil
local indent_shift = opts.indent_shift or ' '
local next_indent
local nl = '\n'
if indent == nil then
indent = ''
linesep = ''
next_indent = ''
nl = ' '
else
next_indent_arg = indent .. indent_shift
next_indent = indent .. indent_shift
end
local ret = ''
if type(v) == 'string' then
if opts.literal_strings then
ret = v
else
local quote = opts.dquote_strings and '"' or "'"
ret = quote
.. tostring(v)
:gsub(opts.dquote_strings and '["\\]' or "['\\]", '\\%0')
:gsub('[%z\1-\31]', function(match)
return SUBTBL[match:byte() + 1]
end)
.. quote
end
elseif type(v) == 'table' then
if v == vim.NIL then
ret = 'REMOVE_THIS'
else
local processed_keys = {}
ret = '{' .. linesep
local non_empty = false
local format_luav = M.format_luav
for i, subv in ipairs(v) do
ret = ('%s%s%s,%s'):format(ret, next_indent, format_luav(subv, next_indent_arg, opts), nl)
processed_keys[i] = true
non_empty = true
end
for k, subv in pairs(v) do
if not processed_keys[k] then
if type(k) == 'string' and k:match('^[a-zA-Z_][a-zA-Z0-9_]*$') then
ret = ret .. next_indent .. k .. ' = '
else
ret = ('%s%s[%s] = '):format(ret, next_indent, format_luav(k, nil, opts))
end
ret = ret .. format_luav(subv, next_indent_arg, opts) .. ',' .. nl
non_empty = true
end
end
if nl == ' ' and non_empty then
ret = ret:sub(1, -3)
end
ret = ret .. indent .. '}'
end
elseif type(v) == 'number' then
if v % 1 == 0 then
ret = ('%d'):format(v)
else
ret = format_float(v)
end
elseif type(v) == 'nil' then
ret = 'nil'
elseif type(v) == 'boolean' then
ret = (v and 'true' or 'false')
else
print(type(v))
-- Not implemented yet
luaassert(false)
end
return ret
end
-- Like Python repr(), "{!r}".format(s)
--
-- Commit: 520c0b91a528
function M.format_string(fmt, ...)
local i = 0
local args = { ... }
local function getarg()
i = i + 1
return args[i]
end
local ret = fmt:gsub('%%[0-9*]*%.?[0-9*]*[cdEefgGiouXxqsr%%]', function(match)
local subfmt = match:gsub('%*', function()
return tostring(getarg())
end)
local arg = nil
if subfmt:sub(-1) ~= '%' then
arg = getarg()
end
if subfmt:sub(-1) == 'r' or subfmt:sub(-1) == 'q' then
-- %r is like built-in %q, but it is supposed to single-quote strings and
-- not double-quote them, and also work not only for strings.
-- Builtin %q is replaced here as it gives invalid and inconsistent with
-- luajit results for e.g. "\e" on lua: luajit transforms that into `\27`,
-- lua leaves as-is.
arg = M.format_luav(arg, nil, { dquote_strings = (subfmt:sub(-1) == 'q') })
subfmt = subfmt:sub(1, -2) .. 's'
end
if subfmt == '%e' then
return format_float(arg)
else
return subfmt:format(arg)
end
end)
return ret
end
return M

View File

@@ -8,7 +8,7 @@ local assert_alive = helpers.assert_alive
local NIL = vim.NIL local NIL = vim.NIL
local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq
local command = helpers.command local command = helpers.command
local command_output = helpers.api.command_output local command_output = helpers.api.nvim_command_output
local exec = helpers.exec local exec = helpers.exec
local exec_capture = helpers.exec_capture local exec_capture = helpers.exec_capture
local eval = helpers.eval local eval = helpers.eval
@@ -19,6 +19,7 @@ local matches = helpers.matches
local pesc = vim.pesc local pesc = vim.pesc
local mkdir_p = helpers.mkdir_p local mkdir_p = helpers.mkdir_p
local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed
local async_meths = helpers.async_meths
local is_os = helpers.is_os local is_os = helpers.is_os
local parse_context = helpers.parse_context local parse_context = helpers.parse_context
local request = helpers.request local request = helpers.request
@@ -33,7 +34,7 @@ local insert = helpers.insert
local skip = helpers.skip local skip = helpers.skip
local pcall_err = helpers.pcall_err local pcall_err = helpers.pcall_err
local format_string = helpers.format_string local format_string = require('test.format_string').format_string
local intchar2lua = helpers.intchar2lua local intchar2lua = helpers.intchar2lua
local mergedicts_copy = helpers.mergedicts_copy local mergedicts_copy = helpers.mergedicts_copy
local endswith = vim.endswith local endswith = vim.endswith
@@ -76,7 +77,7 @@ describe('API', function()
eq({ eq({
'notification', 'notification',
'nvim_error_event', 'nvim_error_event',
{ error_types.Exception.id, 'Invalid method: nvim_bogus' }, { error_types.Exception.id, 'Invalid method: bogus' },
}, next_msg()) }, next_msg())
-- error didn't close channel. -- error didn't close channel.
assert_alive() assert_alive()
@@ -84,7 +85,7 @@ describe('API', function()
it('failed async request emits nvim_error_event', function() it('failed async request emits nvim_error_event', function()
local error_types = api.nvim_get_api_info()[2].error_types local error_types = api.nvim_get_api_info()[2].error_types
nvim_async('command', 'bogus') async_meths.nvim_command('bogus')
eq({ eq({
'notification', 'notification',
'nvim_error_event', 'nvim_error_event',
@@ -2081,13 +2082,13 @@ describe('API', function()
{ ['rc'] = { 'hjkl' }, ['n'] = 97 }, { ['rc'] = { 'hjkl' }, ['n'] = 97 },
}, },
['jumps'] = eval(([[ ['jumps'] = eval((([[
filter(map(add( filter(map(add(
getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
'filter( 'filter(
{ "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
{ k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
]]):gsub('\n', '')), ]]):gsub('\n', ''))),
['bufs'] = eval([[ ['bufs'] = eval([[
filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)') filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)')
@@ -2301,7 +2302,7 @@ describe('API', function()
end) end)
it('can show one line', function() it('can show one line', function()
nvim_async('err_write', 'has bork\n') async_meths.nvim_err_write('has bork\n')
screen:expect([[ screen:expect([[
^ | ^ |
{0:~ }|*6 {0:~ }|*6
@@ -2310,7 +2311,7 @@ describe('API', function()
end) end)
it('shows return prompt when more than &cmdheight lines', function() it('shows return prompt when more than &cmdheight lines', function()
nvim_async('err_write', 'something happened\nvery bad\n') async_meths.nvim_err_write('something happened\nvery bad\n')
screen:expect([[ screen:expect([[
| |
{0:~ }|*3 {0:~ }|*3
@@ -2322,7 +2323,7 @@ describe('API', function()
end) end)
it('shows return prompt after all lines are shown', function() it('shows return prompt after all lines are shown', function()
nvim_async('err_write', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n') async_meths.nvim_err_write('FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n')
screen:expect([[ screen:expect([[
| |
{0:~ }| {0:~ }|
@@ -2337,8 +2338,8 @@ describe('API', function()
it('handles multiple calls', function() it('handles multiple calls', function()
-- without linebreak text is joined to one line -- without linebreak text is joined to one line
nvim_async('err_write', 'very ') async_meths.nvim_err_write('very ')
nvim_async('err_write', 'fail\n') async_meths.nvim_err_write('fail\n')
screen:expect([[ screen:expect([[
^ | ^ |
{0:~ }|*6 {0:~ }|*6
@@ -2347,7 +2348,7 @@ describe('API', function()
helpers.poke_eventloop() helpers.poke_eventloop()
-- shows up to &cmdheight lines -- shows up to &cmdheight lines
nvim_async('err_write', 'more fail\ntoo fail\n') async_meths.nvim_err_write('more fail\ntoo fail\n')
screen:expect([[ screen:expect([[
| |
{0:~ }|*3 {0:~ }|*3
@@ -2360,7 +2361,7 @@ describe('API', function()
end) end)
it('NUL bytes in message', function() it('NUL bytes in message', function()
nvim_async('err_write', 'aaa\0bbb\0\0ccc\nddd\0\0\0eee\n') async_meths.nvim_err_write('aaa\0bbb\0\0ccc\nddd\0\0\0eee\n')
screen:expect { screen:expect {
grid = [[ grid = [[
| |
@@ -2389,7 +2390,7 @@ describe('API', function()
end) end)
it('shows only one return prompt after all lines are shown', function() it('shows only one return prompt after all lines are shown', function()
nvim_async('err_writeln', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK') async_meths.nvim_err_writeln('FAILURE\nERROR\nEXCEPTION\nTRACEBACK')
screen:expect([[ screen:expect([[
| |
{0:~ }| {0:~ }|
@@ -3394,8 +3395,7 @@ describe('API', function()
end) end)
it('can show highlighted line', function() it('can show highlighted line', function()
nvim_async( async_meths.nvim_echo(
'echo',
{ { 'msg_a' }, { 'msg_b', 'Statement' }, { 'msg_c', 'Special' } }, { { 'msg_a' }, { 'msg_b', 'Statement' }, { 'msg_c', 'Special' } },
true, true,
{} {}
@@ -3410,7 +3410,7 @@ describe('API', function()
end) end)
it('can show highlighted multiline', function() it('can show highlighted multiline', function()
nvim_async('echo', { { 'msg_a\nmsg_a', 'Statement' }, { 'msg_b', 'Special' } }, true, {}) async_meths.nvim_echo({ { 'msg_a\nmsg_a', 'Statement' }, { 'msg_b', 'Special' } }, true, {})
screen:expect { screen:expect {
grid = [[ grid = [[
| |
@@ -3431,7 +3431,7 @@ describe('API', function()
it('can disable saving message history', function() it('can disable saving message history', function()
command('set cmdheight=2') -- suppress Press ENTER command('set cmdheight=2') -- suppress Press ENTER
nvim_async('echo', { { 'msg\nmsg' }, { 'msg' } }, false, {}) async_meths.nvim_echo({ { 'msg\nmsg' }, { 'msg' } }, false, {})
eq('', exec_capture('messages')) eq('', exec_capture('messages'))
end) end)
end) end)

View File

@@ -222,7 +222,7 @@ describe('swapfile detection', function()
screen2:expect(expected_no_dialog) screen2:expect(expected_no_dialog)
-- With API call and shortmess+=F -- With API call and shortmess+=F
async_meths.command('edit %') async_meths.nvim_command('edit %')
screen2:expect { screen2:expect {
any = [[Found a swap file by the name ".*]] any = [[Found a swap file by the name ".*]]
.. [[Xtest_swapdialog_dir[/\].*]] .. [[Xtest_swapdialog_dir[/\].*]]

View File

@@ -18,7 +18,7 @@ local fail = global_helpers.fail
local module = {} local module = {}
local runtime_set = 'set runtimepath^=./build/lib/nvim/' local runtime_set = 'set runtimepath^=./build/lib/nvim/'
module.nvim_prog = (os.getenv('NVIM_PRG') or global_helpers.test_build_dir .. '/bin/nvim') module.nvim_prog = (os.getenv('NVIM_PRG') or global_helpers.paths.test_build_dir .. '/bin/nvim')
-- Default settings for the test session. -- Default settings for the test session.
module.nvim_set = ( module.nvim_set = (
'set shortmess+=IS background=light termguicolors noswapfile noautoindent startofline' 'set shortmess+=IS background=light termguicolors noswapfile noautoindent startofline'
@@ -55,7 +55,7 @@ if module.nvim_dir == module.nvim_prog then
module.nvim_dir = '.' module.nvim_dir = '.'
end end
local prepend_argv local prepend_argv --- @type string[]?
if os.getenv('VALGRIND') then if os.getenv('VALGRIND') then
local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log' local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log'
@@ -79,7 +79,7 @@ elseif os.getenv('GDB') then
end end
if prepend_argv then if prepend_argv then
local new_nvim_argv = {} local new_nvim_argv = {} --- @type string[]
local len = #prepend_argv local len = #prepend_argv
for i = 1, len do for i = 1, len do
new_nvim_argv[i] = prepend_argv[i] new_nvim_argv[i] = prepend_argv[i]
@@ -91,10 +91,13 @@ if prepend_argv then
module.prepend_argv = prepend_argv module.prepend_argv = prepend_argv
end 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 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() uv.signal_start(sigpipe_handler, 'sigpipe', function()
print('warning: got SIGPIPE signal. Likely related to a crash in nvim') print('warning: got SIGPIPE signal. Likely related to a crash in nvim')
end) end)
@@ -108,10 +111,15 @@ function module.set_session(s)
session = s session = s
end end
--- @param method string
--- @param ... any
--- @return any
function module.request(method, ...) function module.request(method, ...)
assert(session)
local status, rv = session:request(method, ...) local status, rv = session:request(method, ...)
if not status then if not status then
if loop_running then if loop_running then
--- @type string
last_error = rv[2] last_error = rv[2]
session:stop() session:stop()
else else
@@ -121,12 +129,18 @@ function module.request(method, ...)
return rv return rv
end end
--- @param method string
--- @param ... any
--- @return any
function module.request_lua(method, ...) function module.request_lua(method, ...)
return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...) return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...)
end end
--- @param timeout? integer
--- @return string?
function module.next_msg(timeout) function module.next_msg(timeout)
return session:next_message(timeout and timeout or 10000) assert(session)
return session:next_message(timeout or 10000)
end end
function module.expect_twostreams(msgs1, msgs2) function module.expect_twostreams(msgs1, msgs2)
@@ -164,6 +178,7 @@ function module.expect_msg_seq(...)
error('invalid args') error('invalid args')
end end
local ignore = arg1['ignore'] and arg1['ignore'] or {} local ignore = arg1['ignore'] and arg1['ignore'] or {}
--- @type string[]
local seqs = arg1['seqs'] and arg1['seqs'] or { ... } local seqs = arg1['seqs'] and arg1['seqs'] or { ... }
if type(ignore) ~= 'table' then if type(ignore) ~= 'table' then
error("'ignore' arg must be a list of strings") error("'ignore' arg must be a list of strings")
@@ -213,6 +228,7 @@ function module.expect_msg_seq(...)
local message = result local message = result
if type(result) == 'table' then if type(result) == 'table' then
-- 'eq' returns several things -- 'eq' returns several things
--- @type string
message = result.message message = result.message
end end
final_error = cat_err(final_error, message) final_error = cat_err(final_error, message)
@@ -234,8 +250,16 @@ function module.set_method_error(err)
method_error = err method_error = err
end 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) 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 if request_cb then
function on_request(method, args) function on_request(method, args)
@@ -273,11 +297,12 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
end end
function module.run(request_cb, notification_cb, setup_cb, timeout) function module.run(request_cb, notification_cb, setup_cb, timeout)
assert(session)
return module.run_session(session, request_cb, notification_cb, setup_cb, timeout) return module.run_session(session, request_cb, notification_cb, setup_cb, timeout)
end end
function module.stop() function module.stop()
session:stop() assert(session):stop()
end end
function module.nvim_prog_abs() function module.nvim_prog_abs()
@@ -301,6 +326,7 @@ function module.expect_exit(fn_or_timeout, ...)
eof_err_msg, eof_err_msg,
module.pcall_err(function(timeout, fn, ...) module.pcall_err(function(timeout, fn, ...)
fn(...) fn(...)
assert(session)
while session:next_message(timeout) do while session:next_message(timeout) do
end end
if session.eof_err then if session.eof_err then
@@ -311,26 +337,18 @@ function module.expect_exit(fn_or_timeout, ...)
end end
end end
-- Evaluates a Vimscript expression. --- Executes a Vimscript function via Lua.
-- Fails on Vimscript error, but does not update v:errmsg. --- Fails on Vimscript error, but does not update v:errmsg.
function module.eval(expr) --- @param name string
return module.request('nvim_eval', expr) --- @param ... any
end --- @return any
-- Executes a Vimscript function via RPC.
-- Fails on Vimscript error, but does not update v:errmsg.
function module.call(name, ...)
return module.request('nvim_call_function', name, { ... })
end
-- Executes a Vimscript function via Lua.
-- Fails on Vimscript error, but does not update v:errmsg.
function module.call_lua(name, ...) function module.call_lua(name, ...)
return module.exec_lua([[return vim.call(...)]], name, ...) return module.exec_lua([[return vim.call(...)]], name, ...)
end end
-- Sends user input to Nvim. --- Sends user input to Nvim.
-- Does not fail on Vimscript error, but v:errmsg will be updated. --- Does not fail on Vimscript error, but v:errmsg will be updated.
--- @param input string
local function nvim_feed(input) local function nvim_feed(input)
while #input > 0 do while #input > 0 do
local written = module.request('nvim_input', input) local written = module.request('nvim_input', input)
@@ -342,22 +360,27 @@ local function nvim_feed(input)
end end
end end
--- @param ... string
function module.feed(...) function module.feed(...)
for _, v in ipairs({ ... }) do for _, v in ipairs({ ... }) do
nvim_feed(dedent(v)) nvim_feed(dedent(v))
end end
end end
--- @param ... string
function module.rawfeed(...) function module.rawfeed(...)
for _, v in ipairs({ ... }) do for _, v in ipairs({ ... }) do
nvim_feed(dedent(v)) nvim_feed(dedent(v))
end end
end end
---@param ... string[]?
---@return string[]
function module.merge_args(...) function module.merge_args(...)
local i = 1 local i = 1
local argv = {} local argv = {} --- @type string[]
for anum = 1, select('#', ...) do for anum = 1, select('#', ...) do
--- @type string[]?
local args = select(anum, ...) local args = select(anum, ...)
if args then if args then
for _, arg in ipairs(args) do for _, arg in ipairs(args) do
@@ -369,26 +392,29 @@ function module.merge_args(...)
return argv return argv
end end
-- Removes Nvim startup args from `args` matching items in `args_rm`. --- 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: "-u", "-i", "--cmd" are treated specially: their "values" are also removed.
-- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', } --- - Special case: "runtimepath" will remove only { '--cmd', 'set runtimepath^=…', }
-- ---
-- Example: --- Example:
-- args={'--headless', '-u', 'NONE'} --- args={'--headless', '-u', 'NONE'}
-- args_rm={'--cmd', '-u'} --- args_rm={'--cmd', '-u'}
-- Result: --- Result:
-- {'--headless'} --- {'--headless'}
-- ---
-- All matching cases are removed. --- All matching cases are removed.
-- ---
-- Example: --- Example:
-- args={'--cmd', 'foo', '-N', '--cmd', 'bar'} --- args={'--cmd', 'foo', '-N', '--cmd', 'bar'}
-- args_rm={'--cmd', '-u'} --- args_rm={'--cmd', '-u'}
-- Result: --- Result:
-- {'-N'} --- {'-N'}
--- @param args string[]
--- @param args_rm string[]
--- @return string[]
local function remove_args(args, args_rm) local function remove_args(args, args_rm)
local new_args = {} local new_args = {} --- @type string[]
local skip_following = { '-u', '-i', '-c', '--cmd', '-s', '--listen' } local skip_following = { '-u', '-i', '-c', '--cmd', '-s', '--listen' }
if not args_rm or #args_rm == 0 then if not args_rm or #args_rm == 0 then
return { unpack(args) } return { unpack(args) }
@@ -433,7 +459,12 @@ function module.check_close()
session = nil session = nil
end 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) function module.spawn(argv, merge, env, keep, io_extra)
if not keep then if not keep then
module.check_close() module.check_close()
@@ -469,16 +500,27 @@ function module.clear(...)
return module.get_session() return module.get_session()
end end
-- same params as clear, but does returns the session instead --- same params as clear, but does returns the session instead
-- of replacing the default session --- of replacing the default session
--- @return test.Session
function module.spawn_argv(keep, ...) function module.spawn_argv(keep, ...)
local argv, env, io_extra = module.new_argv(...) local argv, env, io_extra = module.new_argv(...)
return module.spawn(argv, nil, env, keep, io_extra) return module.spawn(argv, nil, env, keep, io_extra)
end end
-- Builds an argument list for use in clear(). --- @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. --- @see clear() for parameters.
--- @param ... string
--- @return string[]
--- @return string[]?
--- @return uv.uv_pipe_t?
function module.new_argv(...) function module.new_argv(...)
local args = { unpack(module.nvim_argv) } local args = { unpack(module.nvim_argv) }
table.insert(args, '--headless') table.insert(args, '--headless')
@@ -487,16 +529,17 @@ function module.new_argv(...)
table.insert(args, '--listen') table.insert(args, '--listen')
table.insert(args, _G._nvim_test_id) table.insert(args, _G._nvim_test_id)
end end
local new_args local new_args --- @type string[]
local io_extra local io_extra --- @type uv.uv_pipe_t?
local env = nil local env --- @type string[]?
--- @type test.new_argv.Opts|string
local opts = select(1, ...) local opts = select(1, ...)
if type(opts) ~= 'table' then if type(opts) ~= 'table' then
new_args = { ... } new_args = { ... }
else else
args = remove_args(args, opts.args_rm) args = remove_args(args, opts.args_rm)
if opts.env then if opts.env then
local env_opt = {} local env_opt = {} --- @type table<string,string>
for k, v in pairs(opts.env) do for k, v in pairs(opts.env) do
assert(type(k) == 'string') assert(type(k) == 'string')
assert(type(v) == 'string') assert(type(v) == 'string')
@@ -535,6 +578,7 @@ function module.new_argv(...)
return args, env, io_extra return args, env, io_extra
end end
--- @param ... string
function module.insert(...) function module.insert(...)
nvim_feed('i') nvim_feed('i')
for _, v in ipairs({ ... }) do for _, v in ipairs({ ... }) do
@@ -544,8 +588,9 @@ function module.insert(...)
nvim_feed('<ESC>') nvim_feed('<ESC>')
end end
-- Executes an ex-command by user input. Because nvim_input() is used, Vimscript --- 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. --- errors will not manifest as client (lua) errors. Use command() for that.
--- @param ... string
function module.feed_command(...) function module.feed_command(...)
for _, v in ipairs({ ... }) do for _, v in ipairs({ ... }) do
if v:sub(1, 1) ~= '/' then if v:sub(1, 1) ~= '/' then
@@ -598,8 +643,10 @@ function module.set_shell_powershell(fake)
end end
function module.create_callindex(func) function module.create_callindex(func)
local table = {} return setmetatable({}, {
setmetatable(table, { --- @param tbl table<any,function>
--- @param arg1 string
--- @return function
__index = function(tbl, arg1) __index = function(tbl, arg1)
local ret = function(...) local ret = function(...)
return func(arg1, ...) return func(arg1, ...)
@@ -608,36 +655,34 @@ function module.create_callindex(func)
return ret return ret
end, end,
}) })
return table
end
local function ui(method, ...)
return module.request('nvim_ui_' .. method, ...)
end end
--- @param method string
--- @param ... any
function module.nvim_async(method, ...) function module.nvim_async(method, ...)
session:notify('nvim_' .. method, ...) assert(session):notify(method, ...)
end
--- 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 end
module.async_meths = module.create_callindex(module.nvim_async) module.async_meths = module.create_callindex(module.nvim_async)
module.uimeths = module.create_callindex(ui)
local function create_bridge(request, call) module.rpc = {
local function nvim(method, ...) fn = module.create_callindex(module.call),
if vim.startswith(method, 'nvim_') then api = module.create_callindex(module.request),
return request(method, ...)
end
return request('nvim_' .. method, ...)
end
return {
fn = module.create_callindex(call),
api = module.create_callindex(nvim),
} }
end
module.rpc = create_bridge(module.request, module.call) module.lua = {
module.lua = create_bridge(module.request_lua, module.call_lua) fn = module.create_callindex(module.call_lua),
api = module.create_callindex(module.request_lua),
}
module.describe_lua_and_rpc = function(describe) module.describe_lua_and_rpc = function(describe)
return function(what, tests) return function(what, tests)
@@ -657,6 +702,7 @@ module.api = vim.api
module.fn = vim.fn module.fn = vim.fn
for name, fns in pairs(module.rpc) do for name, fns in pairs(module.rpc) do
--- @diagnostic disable-next-line:no-unknown
module[name] = fns module[name] = fns
end end
@@ -664,10 +710,14 @@ end
-- v:errmsg will not be updated. -- v:errmsg will not be updated.
module.command = module.api.nvim_command module.command = module.api.nvim_command
-- Evaluates a Vimscript expression.
-- Fails on Vimscript error, but does not update v:errmsg.
module.eval = module.api.nvim_eval
function module.poke_eventloop() function module.poke_eventloop()
-- Execute 'nvim_eval' (a deferred function) to -- Execute 'nvim_eval' (a deferred function) to
-- force at least one main_loop iteration -- force at least one main_loop iteration
session:request('nvim_eval', '1') module.api.nvim_eval('1')
end end
function module.buf_lines(bufnr) function module.buf_lines(bufnr)
@@ -689,6 +739,10 @@ function module.expect_any(contents)
return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true)) return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true))
end end
--- @param expected any[]
--- @param received any[]
--- @param kind string
--- @return any
function module.expect_events(expected, received, kind) function module.expect_events(expected, received, kind)
if not pcall(eq, expected, received) then if not pcall(eq, expected, received) then
local msg = 'unexpected ' .. kind .. ' received.\n\n' local msg = 'unexpected ' .. kind .. ' received.\n\n'
@@ -728,6 +782,7 @@ function module.assert_visible(bufnr, visible)
end end
end end
--- @param path string
local function do_rmdir(path) local function do_rmdir(path)
local stat = uv.fs_stat(path) local stat = uv.fs_stat(path)
if stat == nil then if stat == nil then
@@ -795,14 +850,17 @@ function module.exc_exec(cmd)
return ret return ret
end end
--- @param cond boolean
--- @param reason string
--- @return boolean
function module.skip(cond, reason) function module.skip(cond, reason)
if cond then if cond then
--- @type fun(reason: string)
local pending = getfenv(2).pending local pending = getfenv(2).pending
pending(reason or 'FIXME') pending(reason or 'FIXME')
return true return true
else
return false
end end
return false
end end
-- Calls pending() and returns `true` if the system is too slow to -- Calls pending() and returns `true` if the system is too slow to
@@ -825,6 +883,8 @@ function module.exec(code)
module.api.nvim_exec2(code, {}) module.api.nvim_exec2(code, {})
end end
--- @param code string
--- @return string
function module.exec_capture(code) function module.exec_capture(code)
return module.api.nvim_exec2(code, { output = true }).output return module.api.nvim_exec2(code, { output = true }).output
end end
@@ -832,7 +892,7 @@ end
--- @param code string --- @param code string
--- @return any --- @return any
function module.exec_lua(code, ...) function module.exec_lua(code, ...)
return module.api.exec_lua(code, { ... }) return module.api.nvim_exec_lua(code, { ... })
end end
function module.get_pathsep() function module.get_pathsep()
@@ -871,19 +931,24 @@ function module.new_pipename()
return pipename return pipename
end end
--- @param provider string
--- @return string|false?
function module.missing_provider(provider) function module.missing_provider(provider)
if provider == 'ruby' or provider == 'node' or provider == 'perl' then if provider == 'ruby' or provider == 'node' or provider == 'perl' then
--- @type string?
local e = module.fn['provider#' .. provider .. '#Detect']()[2] local e = module.fn['provider#' .. provider .. '#Detect']()[2]
return e ~= '' and e or false return e ~= '' and e or false
elseif provider == 'python' or provider == 'python3' then elseif provider == 'python' or provider == 'python3' then
local py_major_version = (provider == 'python3' and 3 or 2) local py_major_version = (provider == 'python3' and 3 or 2)
--- @type string?
local e = module.fn['provider#pythonx#Detect'](py_major_version)[2] local e = module.fn['provider#pythonx#Detect'](py_major_version)[2]
return e ~= '' and e or false return e ~= '' and e or false
else end
assert(false, 'Unknown provider: ' .. provider) assert(false, 'Unknown provider: ' .. provider)
end end
end
--- @param obj string|table
--- @return any
function module.alter_slashes(obj) function module.alter_slashes(obj)
if not is_os('win') then if not is_os('win') then
return obj return obj
@@ -892,14 +957,14 @@ function module.alter_slashes(obj)
local ret = obj:gsub('/', '\\') local ret = obj:gsub('/', '\\')
return ret return ret
elseif type(obj) == 'table' then 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 for k, v in pairs(obj) do
ret[k] = module.alter_slashes(v) ret[k] = module.alter_slashes(v)
end end
return ret return ret
else
assert(false, 'expected string or table of strings, got ' .. type(obj))
end end
assert(false, 'expected string or table of strings, got ' .. type(obj))
end end
local load_factor = 1 local load_factor = 1
@@ -909,18 +974,25 @@ if global_helpers.is_ci() then
module.request('nvim_command', 'source test/old/testdir/load.vim') module.request('nvim_command', 'source test/old/testdir/load.vim')
load_factor = module.request('nvim_eval', 'g:test_load_factor') load_factor = module.request('nvim_eval', 'g:test_load_factor')
end end
--- @param num number
--- @return number
function module.load_adjust(num) function module.load_adjust(num)
return math.ceil(num * load_factor) return math.ceil(num * load_factor)
end end
--- @param ctx table<string,any>
--- @return table
function module.parse_context(ctx) function module.parse_context(ctx)
local parsed = {} local parsed = {} --- @type table<string,any>
for _, item in ipairs({ 'regs', 'jumps', 'bufs', 'gvars' }) do for _, item in ipairs({ 'regs', 'jumps', 'bufs', 'gvars' }) do
--- @param v any
parsed[item] = vim.tbl_filter(function(v) parsed[item] = vim.tbl_filter(function(v)
return type(v) == 'table' return type(v) == 'table'
end, module.call('msgpackparse', ctx[item])) end, module.call('msgpackparse', ctx[item]))
end end
parsed['bufs'] = parsed['bufs'][1] parsed['bufs'] = parsed['bufs'][1]
--- @param v any
return vim.tbl_map(function(v) return vim.tbl_map(function(v)
if #v == 0 then if #v == 0 then
return nil return nil
@@ -931,10 +1003,12 @@ end
function module.add_builddir_to_rtp() function module.add_builddir_to_rtp()
-- Add runtime from build dir for doc/tags (used with :help). -- Add runtime from build dir for doc/tags (used with :help).
module.command(string.format([[set rtp+=%s/runtime]], module.test_build_dir)) module.command(string.format([[set rtp+=%s/runtime]], module.paths.test_build_dir))
end end
-- Kill (reap) a process by PID. --- Kill (reap) a process by PID.
--- @param pid string
--- @return boolean?
function module.os_kill(pid) function module.os_kill(pid)
return os.execute( return os.execute(
( (
@@ -944,7 +1018,9 @@ function module.os_kill(pid)
) )
end end
-- Create folder with non existing parents --- Create folder with non existing parents
--- @param path string
--- @return boolean?
function module.mkdir_p(path) function module.mkdir_p(path)
return os.execute((is_os('win') and 'mkdir ' .. path or 'mkdir -p ' .. path)) return os.execute((is_os('win') and 'mkdir ' .. path or 'mkdir -p ' .. path))
end end

View File

@@ -6,8 +6,8 @@ local eq = helpers.eq
local mkdir_p = helpers.mkdir_p local mkdir_p = helpers.mkdir_p
local rmdir = helpers.rmdir local rmdir = helpers.rmdir
local nvim_dir = helpers.nvim_dir local nvim_dir = helpers.nvim_dir
local test_build_dir = helpers.test_build_dir local test_build_dir = helpers.paths.test_build_dir
local test_source_path = helpers.test_source_path local test_source_path = helpers.paths.test_source_path
local nvim_prog = helpers.nvim_prog local nvim_prog = helpers.nvim_prog
local is_os = helpers.is_os local is_os = helpers.is_os
local mkdir = helpers.mkdir local mkdir = helpers.mkdir

View File

@@ -162,7 +162,7 @@ describe('luaeval()', function()
return sp('map', '[' .. val .. ']') return sp('map', '[' .. val .. ']')
end end
local function luaevalarg(argexpr, expr) local function luaevalarg(argexpr, expr)
return eval(([=[ return eval((([=[
[ [
extend(g:, {'_ret': luaeval(%s, %s)})._ret, extend(g:, {'_ret': luaeval(%s, %s)})._ret,
type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE') type(g:_ret)==type({})&&has_key(g:_ret, '_TYPE')
@@ -172,7 +172,7 @@ describe('luaeval()', function()
get(g:_ret, '_VAL', g:_ret) get(g:_ret, '_VAL', g:_ret)
] ]
: [0, g:_ret]][1] : [0, g:_ret]][1]
]=]):format(expr or '"_A"', argexpr):gsub('\n', '')) ]=]):format(expr or '"_A"', argexpr):gsub('\n', '')))
end end
it('correctly passes special dictionaries', function() it('correctly passes special dictionaries', function()

View File

@@ -525,7 +525,7 @@ describe('ShaDa marks support code', function()
local found = 0 local found = 0
for _, v in ipairs(read_shada_file(shada_fname)) do for _, v in ipairs(read_shada_file(shada_fname)) do
if v.type == 7 and v.value.f == mock_file_path .. '-' then if v.type == 7 and v.value.f == mock_file_path .. '-' then
print(require('test.helpers').format_luav(v)) print(require('test.format_string').format_luav(v))
found = found + 1 found = found + 1
end end
end end

View File

@@ -7,7 +7,7 @@ local is_os = helpers.is_os
local skip = helpers.skip local skip = helpers.skip
local uv = vim.uv local uv = vim.uv
local paths = require('test.cmakeconfig.paths') local paths = helpers.paths
local shada_helpers = require('test.functional.shada.helpers') local shada_helpers = require('test.functional.shada.helpers')
local reset, clear, get_shada_rw = local reset, clear, get_shada_rw =

View File

@@ -151,7 +151,7 @@ describe('no crash when TermOpen autocommand', function()
it('processes job exit event when using termopen()', function() it('processes job exit event when using termopen()', function()
command([[autocmd TermOpen * call input('')]]) command([[autocmd TermOpen * call input('')]])
async_meths.command('terminal foobar') async_meths.nvim_command('terminal foobar')
screen:expect { screen:expect {
grid = [[ grid = [[
| |
@@ -181,7 +181,7 @@ describe('no crash when TermOpen autocommand', function()
it('wipes buffer and processes events when using termopen()', function() it('wipes buffer and processes events when using termopen()', function()
command([[autocmd TermOpen * bwipe! | call input('')]]) command([[autocmd TermOpen * bwipe! | call input('')]])
async_meths.command('terminal foobar') async_meths.nvim_command('terminal foobar')
screen:expect { screen:expect {
grid = [[ grid = [[
| |
@@ -202,7 +202,7 @@ describe('no crash when TermOpen autocommand', function()
it('wipes buffer and processes events when using nvim_open_term()', function() it('wipes buffer and processes events when using nvim_open_term()', function()
command([[autocmd TermOpen * bwipe! | call input('')]]) command([[autocmd TermOpen * bwipe! | call input('')]])
async_meths.open_term(0, {}) async_meths.nvim_open_term(0, {})
screen:expect { screen:expect {
grid = [[ grid = [[
| |

View File

@@ -7,7 +7,7 @@ local command = helpers.command
local set_method_error = helpers.set_method_error local set_method_error = helpers.set_method_error
local api = helpers.api local api = helpers.api
local async_meths = helpers.async_meths local async_meths = helpers.async_meths
local test_build_dir = helpers.test_build_dir local test_build_dir = helpers.paths.test_build_dir
local nvim_prog = helpers.nvim_prog local nvim_prog = helpers.nvim_prog
local exec = helpers.exec local exec = helpers.exec
local exec_capture = helpers.exec_capture local exec_capture = helpers.exec_capture
@@ -1039,7 +1039,7 @@ stack traceback:
end) end)
it('supports nvim_echo messages with multiple attrs', function() it('supports nvim_echo messages with multiple attrs', function()
async_meths.echo( async_meths.nvim_echo(
{ { 'wow, ', 'Search' }, { 'such\n\nvery ', 'ErrorMsg' }, { 'color', 'LineNr' } }, { { 'wow, ', 'Search' }, { 'such\n\nvery ', 'ErrorMsg' }, { 'color', 'LineNr' } },
true, true,
{} {}
@@ -1403,7 +1403,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
end) end)
it('supports nvim_echo messages with multiple attrs', function() it('supports nvim_echo messages with multiple attrs', function()
async_meths.echo( async_meths.nvim_echo(
{ { 'wow, ', 'Search' }, { 'such\n\nvery ', 'ErrorMsg' }, { 'color', 'LineNr' } }, { { 'wow, ', 'Search' }, { 'such\n\nvery ', 'ErrorMsg' }, { 'color', 'LineNr' } },
true, true,
{} {}
@@ -1521,7 +1521,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim
end) end)
it('consecutive calls to win_move_statusline() work after multiline message #21014', function() it('consecutive calls to win_move_statusline() work after multiline message #21014', function()
async_meths.exec( async_meths.nvim_exec(
[[ [[
echo "\n" echo "\n"
call win_move_statusline(0, -4) call win_move_statusline(0, -4)
@@ -2196,7 +2196,7 @@ aliquip ex ea commodo consequat.]]
} }
-- not processed while command is executing -- not processed while command is executing
async_meths.ui_try_resize(35, 5) async_meths.nvim_ui_try_resize(35, 5)
-- TODO(bfredl): ideally it should be processed just -- TODO(bfredl): ideally it should be processed just
-- before the "press ENTER" prompt though -- before the "press ENTER" prompt though

View File

@@ -2527,7 +2527,7 @@ describe('builtin popupmenu', function()
]]) ]])
end end
feed('<C-E>') feed('<C-E>')
async_meths.call_function('input', { '', '', 'sign' }) async_meths.nvim_call_function('input', { '', '', 'sign' })
if multigrid then if multigrid then
screen:expect { screen:expect {
grid = [[ grid = [[

View File

@@ -314,13 +314,13 @@ describe('context functions', function()
} }
local with_jumps = { local with_jumps = {
['jumps'] = eval(([[ ['jumps'] = eval((([[
filter(map(add( filter(map(add(
getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }),
'filter( 'filter(
{ "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
{ k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
]]):gsub('\n', '')), ]]):gsub('\n', ''))),
} }
local with_bufs = { local with_bufs = {

View File

@@ -414,19 +414,19 @@ describe('confirm()', function()
-- screen:expect() calls are needed to avoid feeding input too early -- screen:expect() calls are needed to avoid feeding input too early
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
async_meths.command([[let a = confirm('Press O to proceed')]]) async_meths.nvim_command([[let a = confirm('Press O to proceed')]])
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('o') feed('o')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
eq(1, api.nvim_get_var('a')) eq(1, api.nvim_get_var('a'))
async_meths.command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]]) async_meths.nvim_command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]])
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('y') feed('y')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
eq(1, api.nvim_get_var('a')) eq(1, api.nvim_get_var('a'))
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('n') feed('n')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
@@ -435,26 +435,26 @@ describe('confirm()', function()
-- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim. -- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim.
-- confirm() should return 0 when pressing ESC. -- confirm() should return 0 when pressing ESC.
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('<Esc>') feed('<Esc>')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
eq(0, api.nvim_get_var('a')) eq(0, api.nvim_get_var('a'))
-- Default choice is returned when pressing <CR>. -- Default choice is returned when pressing <CR>.
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]]) async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('<CR>') feed('<CR>')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
eq(1, api.nvim_get_var('a')) eq(1, api.nvim_get_var('a'))
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]]) async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]])
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('<CR>') feed('<CR>')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
eq(2, api.nvim_get_var('a')) eq(2, api.nvim_get_var('a'))
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]]) async_meths.nvim_command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]])
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('<CR>') feed('<CR>')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
@@ -462,7 +462,9 @@ describe('confirm()', function()
-- Test with the {type} 4th argument -- Test with the {type} 4th argument
for _, type in ipairs({ 'Error', 'Question', 'Info', 'Warning', 'Generic' }) do for _, type in ipairs({ 'Error', 'Question', 'Info', 'Warning', 'Generic' }) do
async_meths.command(([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type)) async_meths.nvim_command(
([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type)
)
screen:expect({ any = '{CONFIRM:.+: }' }) screen:expect({ any = '{CONFIRM:.+: }' })
feed('y') feed('y')
screen:expect({ any = '%[No Name%]' }) screen:expect({ any = '%[No Name%]' })
@@ -518,7 +520,7 @@ describe('confirm()', function()
feed(':call nvim_command("edit x")<cr>') feed(':call nvim_command("edit x")<cr>')
check_and_clear(':call nvim_command("edit |\n') check_and_clear(':call nvim_command("edit |\n')
async_meths.command('edit x') async_meths.nvim_command('edit x')
check_and_clear(' |\n') check_and_clear(' |\n')
end) end)
end) end)

View File

@@ -1,7 +1,7 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local feed, eq, eval, ok = helpers.feed, helpers.eq, helpers.eval, helpers.ok local feed, eq, eval, ok = helpers.feed, helpers.eq, helpers.eval, helpers.ok
local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run local source, async_meths, run = helpers.source, helpers.async_meths, helpers.run
local clear, command, fn = helpers.clear, helpers.command, helpers.fn local clear, command, fn = helpers.clear, helpers.command, helpers.fn
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local api = helpers.api local api = helpers.api
@@ -52,9 +52,9 @@ describe('timers', function()
endfunc endfunc
]]) ]])
eval("timer_start(10, 'MyHandler', {'repeat': -1})") eval("timer_start(10, 'MyHandler', {'repeat': -1})")
nvim_async('command', 'sleep 10') async_meths.nvim_command('sleep 10')
eq(-1, eval('g:val')) -- timer did nothing yet. eq(-1, eval('g:val')) -- timer did nothing yet.
nvim_async('command', 'let g:val = 0') async_meths.nvim_command('let g:val = 0')
run(nil, nil, nil, load_adjust(20)) run(nil, nil, nil, load_adjust(20))
retry(nil, nil, function() retry(nil, nil, function()
eq(2, eval('g:val')) eq(2, eval('g:val'))
@@ -70,7 +70,7 @@ describe('timers', function()
end) end)
it('can be started during sleep', function() it('can be started during sleep', function()
nvim_async('command', 'sleep 10') async_meths.nvim_command('sleep 10')
-- this also tests that remote requests works during sleep -- this also tests that remote requests works during sleep
eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]")) eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]"))
run(nil, nil, nil, load_adjust(20)) run(nil, nil, nil, load_adjust(20))
@@ -94,7 +94,7 @@ describe('timers', function()
it('are triggered in blocking getchar() call', function() it('are triggered in blocking getchar() call', function()
command("call timer_start(5, 'MyHandler', {'repeat': -1})") command("call timer_start(5, 'MyHandler', {'repeat': -1})")
nvim_async('command', 'let g:val = 0 | let g:c = getchar()') async_meths.nvim_command('let g:val = 0 | let g:c = getchar()')
retry(nil, nil, function() retry(nil, nil, function()
local val = eval('g:val') local val = eval('g:val')
ok(val >= 2, '>= 2', tostring(val)) ok(val >= 2, '>= 2', tostring(val))
@@ -128,8 +128,10 @@ describe('timers', function()
redraw redraw
endfunc endfunc
]]) ]])
nvim_async('command', 'let g:c2 = getchar()') async_meths.nvim_command('let g:c2 = getchar()')
nvim_async('command', 'call timer_start(' .. load_adjust(100) .. ", 'AddItem', {'repeat': -1})") async_meths.nvim_command(
'call timer_start(' .. load_adjust(100) .. ", 'AddItem', {'repeat': -1})"
)
screen:expect([[ screen:expect([[
^ITEM 1 | ^ITEM 1 |
@@ -137,7 +139,7 @@ describe('timers', function()
{1:~ }|*3 {1:~ }|*3
| |
]]) ]])
nvim_async('command', 'let g:cont = 1') async_meths.nvim_command('let g:cont = 1')
screen:expect([[ screen:expect([[
^ITEM 1 | ^ITEM 1 |

View File

@@ -18,15 +18,14 @@ end
--- @class test.helpers --- @class test.helpers
local module = { local module = {
REMOVE_THIS = {}, paths = Paths,
} }
--- @param p string --- @param p string
--- @return string --- @return string
local function relpath(p) local function relpath(p)
p = vim.fs.normalize(p) p = vim.fs.normalize(p)
local cwd = uv.cwd() return (p:gsub('^' .. uv.cwd, ''))
return p:gsub('^' .. cwd)
end end
--- @param path string --- @param path string
@@ -42,41 +41,34 @@ function module.isdir(path)
return stat.type == 'directory' return stat.type == 'directory'
end end
--- @param path string --- @param ... string|string[]
--- @return boolean
function module.isfile(path)
if not path then
return false
end
local stat = uv.fs_stat(path)
if not stat then
return false
end
return stat.type == 'file'
end
--- @return string --- @return string
function module.argss_to_cmd(...) function module.argss_to_cmd(...)
local cmd = '' local cmd = {} --- @type string[]
for i = 1, select('#', ...) do for i = 1, select('#', ...) do
local arg = select(i, ...) local arg = select(i, ...)
if type(arg) == 'string' then if type(arg) == 'string' then
cmd = cmd .. ' ' .. shell_quote(arg) cmd[#cmd + 1] = shell_quote(arg)
else else
--- @cast arg string[]
for _, subarg in ipairs(arg) do for _, subarg in ipairs(arg) do
cmd = cmd .. ' ' .. shell_quote(subarg) cmd[#cmd + 1] = shell_quote(subarg)
end end
end end
end end
return cmd return table.concat(cmd, ' ')
end end
function module.popen_r(...) function module.popen_r(...)
return io.popen(module.argss_to_cmd(...), 'r') return io.popen(module.argss_to_cmd(...), 'r')
end end
-- Calls fn() until it succeeds, up to `max` times or until `max_ms` --- Calls fn() until it succeeds, up to `max` times or until `max_ms`
-- milliseconds have passed. --- milliseconds have passed.
--- @param max integer?
--- @param max_ms integer?
--- @param fn function
--- @return any
function module.retry(max, max_ms, fn) function module.retry(max, max_ms, fn)
luaassert(max == nil or max > 0) luaassert(max == nil or max > 0)
luaassert(max_ms == nil or max_ms > 0) luaassert(max_ms == nil or max_ms > 0)
@@ -84,6 +76,7 @@ function module.retry(max, max_ms, fn)
local timeout = (max_ms and max_ms or 10000) local timeout = (max_ms and max_ms or 10000)
local start_time = uv.now() local start_time = uv.now()
while true do while true do
--- @type boolean, any
local status, result = pcall(fn) local status, result = pcall(fn)
if status then if status then
return result return result
@@ -133,6 +126,9 @@ function module.fail(msg)
return luaassert.epicfail(msg) return luaassert.epicfail(msg)
end end
--- @param pat string
--- @param actual string
--- @return boolean
function module.matches(pat, actual) function module.matches(pat, actual)
if nil ~= string.match(actual, pat) then if nil ~= string.match(actual, pat) then
return true return true
@@ -182,10 +178,16 @@ end
--- Asserts that `pat` does NOT match any line in the tail of `logfile`. --- Asserts that `pat` does NOT match any line in the tail of `logfile`.
--- ---
--- @see assert_log --- @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) function module.assert_nolog(pat, logfile, nrlines)
return module.assert_log(pat, logfile, nrlines, true) return module.assert_log(pat, logfile, nrlines, true)
end end
--- @param fn fun(...): any
--- @param ... any
--- @return boolean, any
function module.pcall(fn, ...) function module.pcall(fn, ...)
luaassert(type(fn) == 'function') luaassert(type(fn) == 'function')
local status, rv = pcall(fn, ...) local status, rv = pcall(fn, ...)
@@ -233,6 +235,8 @@ end
-- -- Match Lua pattern. -- -- Match Lua pattern.
-- matches('e[or]+$', pcall_err(function(a, b) error('some error') end, 'arg1', 'arg2')) -- 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, ...) function module.pcall_err_withfile(fn, ...)
luaassert(type(fn) == 'function') luaassert(type(fn) == 'function')
local status, rv = module.pcall(fn, ...) local status, rv = module.pcall(fn, ...)
@@ -242,19 +246,29 @@ function module.pcall_err_withfile(fn, ...)
return rv return rv
end end
--- @param fn function
--- @param ... any
--- @return string
function module.pcall_err_withtrace(fn, ...) function module.pcall_err_withtrace(fn, ...)
local errmsg = module.pcall_err_withfile(fn, ...) local errmsg = module.pcall_err_withfile(fn, ...)
return errmsg return (
errmsg
:gsub('^%.%.%./helpers%.lua:0: ', '') :gsub('^%.%.%./helpers%.lua:0: ', '')
:gsub('^Error executing lua:- ', '') :gsub('^Error executing lua:- ', '')
:gsub('^%[string "<nvim>"%]:0: ', '') :gsub('^%[string "<nvim>"%]:0: ', '')
)
end end
function module.pcall_err(...) --- @param fn function
return module.remove_trace(module.pcall_err_withtrace(...)) --- @param ... any
--- @return string
function module.pcall_err(fn, ...)
return module.remove_trace(module.pcall_err_withtrace(fn, ...))
end end
--- @param s string
--- @return string
function module.remove_trace(s) function module.remove_trace(s)
return (s:gsub('\n%s*stack traceback:.*', '')) return (s:gsub('\n%s*stack traceback:.*', ''))
end end
@@ -264,9 +278,9 @@ end
-- exc_re: exclude pattern(s) (string or table) -- exc_re: exclude pattern(s) (string or table)
function module.glob(initial_path, re, exc_re) function module.glob(initial_path, re, exc_re)
exc_re = type(exc_re) == 'table' and exc_re or { exc_re } exc_re = type(exc_re) == 'table' and exc_re or { exc_re }
local paths_to_check = { initial_path } local paths_to_check = { initial_path } --- @type string[]
local ret = {} local ret = {} --- @type string[]
local checked_files = {} local checked_files = {} --- @type table<string,true>
local function is_excluded(path) local function is_excluded(path)
for _, pat in pairs(exc_re) do for _, pat in pairs(exc_re) do
if path:match(pat) then if path:match(pat) then
@@ -313,7 +327,7 @@ function module.check_logs()
local file = log_dir .. '/' .. tail local file = log_dir .. '/' .. tail
local fd = assert(io.open(file)) local fd = assert(io.open(file))
local start_msg = ('='):rep(20) .. ' File ' .. file .. ' ' .. ('='):rep(20) local start_msg = ('='):rep(20) .. ' File ' .. file .. ' ' .. ('='):rep(20)
local lines = {} local lines = {} --- @type string[]
local warning_line = 0 local warning_line = 0
for line in fd:lines() do for line in fd:lines() do
local cur_warning_line = check_logs_useless_lines[line] local cur_warning_line = check_logs_useless_lines[line]
@@ -325,6 +339,7 @@ function module.check_logs()
end end
fd:close() fd:close()
if #lines > 0 then if #lines > 0 then
--- @type boolean?, file*?
local status, f local status, f
local out = io.stdout local out = io.stdout
if os.getenv('SYMBOLIZER') then if os.getenv('SYMBOLIZER') then
@@ -332,6 +347,7 @@ function module.check_logs()
end end
out:write(start_msg .. '\n') out:write(start_msg .. '\n')
if status then if status then
assert(f)
for line in f:lines() do for line in f:lines() do
out:write('= ' .. line .. '\n') out:write('= ' .. line .. '\n')
end end
@@ -353,10 +369,7 @@ function module.check_logs()
end end
function module.sysname() function module.sysname()
local platform = uv.os_uname() return uv.os_uname().sysname:lower()
if platform and platform.sysname then
return platform.sysname:lower()
end
end end
function module.is_os(s) function module.is_os(s)
@@ -372,26 +385,15 @@ function module.is_os(s)
) )
end end
function module.is_arch(s)
local machine = uv.os_uname().machine
if s == 'arm64' or s == 'aarch64' then
return machine == 'arm64' or machine == 'aarch64'
end
if s == 'x86' or s == 'x86_64' or s == 'amd64' then
return machine == 'x86_64'
end
return machine == s
end
local function tmpdir_get() local function tmpdir_get()
return os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP') return os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP')
end 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) local function tmpdir_is_local(dir)
return not not (dir and string.find(dir, 'Xtest')) return not not (dir and dir:find('Xtest'))
end end
--- Creates a new temporary file for use by tests. --- Creates a new temporary file for use by tests.
@@ -422,14 +424,6 @@ module.tmpname = (function()
end end
end)() end)()
function module.hasenv(name)
local env = os.getenv(name)
if env and env ~= '' then
return env
end
return nil
end
local function deps_prefix() local function deps_prefix()
local env = os.getenv('DEPS_PREFIX') local env = os.getenv('DEPS_PREFIX')
return (env and env ~= '') and env or '.deps/usr' return (env and env ~= '') and env or '.deps/usr'
@@ -443,6 +437,7 @@ function module.check_cores(app, force) -- luacheck: ignore
return return
end end
app = app or 'build/bin/nvim' -- luacheck: ignore app = app or 'build/bin/nvim' -- luacheck: ignore
--- @type string, string?, string[]
local initial_path, re, exc_re local initial_path, re, exc_re
local gdb_db_cmd = local gdb_db_cmd =
'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"' 'gdb -n -batch -ex "thread apply all bt full" "$_NVIM_TEST_APP" -c "$_NVIM_TEST_CORE"'
@@ -455,13 +450,14 @@ function module.check_cores(app, force) -- luacheck: ignore
and relpath(tmpdir_get()):gsub('^[ ./]+', ''):gsub('%/+$', ''):gsub('([^%w])', '%%%1') and relpath(tmpdir_get()):gsub('^[ ./]+', ''):gsub('%/+$', ''):gsub('([^%w])', '%%%1')
or nil or nil
) )
local db_cmd local db_cmd --- @type string
if module.hasenv('NVIM_TEST_CORE_GLOB_DIRECTORY') then local test_glob_dir = os.getenv('NVIM_TEST_CORE_GLOB_DIRECTORY')
initial_path = 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') re = os.getenv('NVIM_TEST_CORE_GLOB_RE')
exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir } exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir }
db_cmd = os.getenv('NVIM_TEST_CORE_DB_CMD') or gdb_db_cmd 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 elseif module.is_os('mac') then
initial_path = '/cores' initial_path = '/cores'
re = nil re = nil
@@ -519,21 +515,28 @@ function module.repeated_read_cmd(...)
return nil return nil
end end
--- @generic T
--- @param orig T
--- @return T
function module.shallowcopy(orig) function module.shallowcopy(orig)
if type(orig) ~= 'table' then if type(orig) ~= 'table' then
return orig return orig
end end
local copy = {} --- @cast orig table<any,any>
local copy = {} --- @type table<any,any>
for orig_key, orig_value in pairs(orig) do for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value copy[orig_key] = orig_value
end end
return copy return copy
end end
--- @param d1 table<any,any>
--- @param d2 table<any,any>
--- @return table<any,any>
function module.mergedicts_copy(d1, d2) function module.mergedicts_copy(d1, d2)
local ret = module.shallowcopy(d1) local ret = module.shallowcopy(d1)
for k, v in pairs(d2) do for k, v in pairs(d2) do
if d2[k] == module.REMOVE_THIS then if d2[k] == vim.NIL then
ret[k] = nil ret[k] = nil
elseif type(d1[k]) == 'table' and type(v) == 'table' then elseif type(d1[k]) == 'table' and type(v) == 'table' then
ret[k] = module.mergedicts_copy(d1[k], v) ret[k] = module.mergedicts_copy(d1[k], v)
@@ -544,16 +547,18 @@ function module.mergedicts_copy(d1, d2)
return ret return ret
end end
-- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2 --- dictdiff: find a diff so that mergedicts_copy(d1, diff) is equal to d2
-- ---
-- Note: does not do copies of d2 values used. --- Note: does not do copies of d2 values used.
--- @param d1 table<any,any>
--- @param d2 table<any,any>
function module.dictdiff(d1, d2) function module.dictdiff(d1, d2)
local ret = {} local ret = {} --- @type table<any,any>
local hasdiff = false local hasdiff = false
for k, v in pairs(d1) do for k, v in pairs(d1) do
if d2[k] == nil then if d2[k] == nil then
hasdiff = true hasdiff = true
ret[k] = module.REMOVE_THIS ret[k] = vim.NIL
elseif type(v) == type(d2[k]) then elseif type(v) == type(d2[k]) then
if type(v) == 'table' then if type(v) == 'table' then
local subdiff = module.dictdiff(v, d2[k]) local subdiff = module.dictdiff(v, d2[k])
@@ -584,17 +589,11 @@ function module.dictdiff(d1, d2)
end end
end end
function module.updated(d, d2)
for k, v in pairs(d2) do
d[k] = v
end
return d
end
-- Concat list-like tables. -- Concat list-like tables.
function module.concat_tables(...) function module.concat_tables(...)
local ret = {} local ret = {} --- @type table<any,any>
for i = 1, select('#', ...) do for i = 1, select('#', ...) do
--- @type table<any,any>
local tbl = select(i, ...) local tbl = select(i, ...)
if tbl then if tbl then
for _, v in ipairs(tbl) do for _, v in ipairs(tbl) do
@@ -633,192 +632,13 @@ function module.dedent(str, leave_indent)
return str return str
end end
local function format_float(v)
-- On windows exponent appears to have three digits and not two
local ret = ('%.6e'):format(v)
local l, f, es, e = ret:match('^(%-?%d)%.(%d+)e([+%-])0*(%d%d+)$')
return l .. '.' .. f .. 'e' .. es .. e
end
local SUBTBL = {
'\\000',
'\\001',
'\\002',
'\\003',
'\\004',
'\\005',
'\\006',
'\\007',
'\\008',
'\\t',
'\\n',
'\\011',
'\\012',
'\\r',
'\\014',
'\\015',
'\\016',
'\\017',
'\\018',
'\\019',
'\\020',
'\\021',
'\\022',
'\\023',
'\\024',
'\\025',
'\\026',
'\\027',
'\\028',
'\\029',
'\\030',
'\\031',
}
-- Formats Lua value `v`.
--
-- TODO(justinmk): redundant with vim.inspect() ?
--
-- "Nice table formatting similar to screen:snapshot_util()".
-- Commit: 520c0b91a528
function module.format_luav(v, indent, opts)
opts = opts or {}
local linesep = '\n'
local next_indent_arg = nil
local indent_shift = opts.indent_shift or ' '
local next_indent
local nl = '\n'
if indent == nil then
indent = ''
linesep = ''
next_indent = ''
nl = ' '
else
next_indent_arg = indent .. indent_shift
next_indent = indent .. indent_shift
end
local ret = ''
if type(v) == 'string' then
if opts.literal_strings then
ret = v
else
local quote = opts.dquote_strings and '"' or "'"
ret = quote
.. tostring(v)
:gsub(opts.dquote_strings and '["\\]' or "['\\]", '\\%0')
:gsub('[%z\1-\31]', function(match)
return SUBTBL[match:byte() + 1]
end)
.. quote
end
elseif type(v) == 'table' then
if v == module.REMOVE_THIS then
ret = 'REMOVE_THIS'
else
local processed_keys = {}
ret = '{' .. linesep
local non_empty = false
local format_luav = module.format_luav
for i, subv in ipairs(v) do
ret = ('%s%s%s,%s'):format(ret, next_indent, format_luav(subv, next_indent_arg, opts), nl)
processed_keys[i] = true
non_empty = true
end
for k, subv in pairs(v) do
if not processed_keys[k] then
if type(k) == 'string' and k:match('^[a-zA-Z_][a-zA-Z0-9_]*$') then
ret = ret .. next_indent .. k .. ' = '
else
ret = ('%s%s[%s] = '):format(ret, next_indent, format_luav(k, nil, opts))
end
ret = ret .. format_luav(subv, next_indent_arg, opts) .. ',' .. nl
non_empty = true
end
end
if nl == ' ' and non_empty then
ret = ret:sub(1, -3)
end
ret = ret .. indent .. '}'
end
elseif type(v) == 'number' then
if v % 1 == 0 then
ret = ('%d'):format(v)
else
ret = format_float(v)
end
elseif type(v) == 'nil' then
ret = 'nil'
elseif type(v) == 'boolean' then
ret = (v and 'true' or 'false')
else
print(type(v))
-- Not implemented yet
luaassert(false)
end
return ret
end
-- Like Python repr(), "{!r}".format(s)
--
-- Commit: 520c0b91a528
function module.format_string(fmt, ...)
local i = 0
local args = { ... }
local function getarg()
i = i + 1
return args[i]
end
local ret = fmt:gsub('%%[0-9*]*%.?[0-9*]*[cdEefgGiouXxqsr%%]', function(match)
local subfmt = match:gsub('%*', function()
return tostring(getarg())
end)
local arg = nil
if subfmt:sub(-1) ~= '%' then
arg = getarg()
end
if subfmt:sub(-1) == 'r' or subfmt:sub(-1) == 'q' then
-- %r is like built-in %q, but it is supposed to single-quote strings and
-- not double-quote them, and also work not only for strings.
-- Builtin %q is replaced here as it gives invalid and inconsistent with
-- luajit results for e.g. "\e" on lua: luajit transforms that into `\27`,
-- lua leaves as-is.
arg = module.format_luav(arg, nil, { dquote_strings = (subfmt:sub(-1) == 'q') })
subfmt = subfmt:sub(1, -2) .. 's'
end
if subfmt == '%e' then
return format_float(arg)
else
return subfmt:format(arg)
end
end)
return ret
end
function module.intchar2lua(ch) function module.intchar2lua(ch)
ch = tonumber(ch) ch = tonumber(ch)
return (20 <= ch and ch < 127) and ('%c'):format(ch) or ch return (20 <= ch and ch < 127) and ('%c'):format(ch) or ch
end end
local fixtbl_metatable = { --- @param str string
__newindex = function() --- @return string
luaassert(false)
end,
}
function module.fixtbl(tbl)
return setmetatable(tbl, fixtbl_metatable)
end
function module.fixtbl_rec(tbl)
local fixtbl_rec = module.fixtbl_rec
for _, v in pairs(tbl) do
if type(v) == 'table' then
fixtbl_rec(v)
end
end
return module.fixtbl(tbl)
end
function module.hexdump(str) function module.hexdump(str)
local len = string.len(str) local len = string.len(str)
local dump = '' local dump = ''
@@ -844,10 +664,10 @@ function module.hexdump(str)
return dump .. hex .. string.rep(' ', 8 - len % 8) .. asc return dump .. hex .. string.rep(' ', 8 - len % 8) .. asc
end end
-- Reads text lines from `filename` into a table. --- Reads text lines from `filename` into a table.
-- --- @param filename string path to file
-- filename: path to file --- @param start? integer start line (1-indexed), negative means "lines before end" (tail)
-- start: start line (1-indexed), negative means "lines before end" (tail) --- @return string[]?
function module.read_file_list(filename, start) function module.read_file_list(filename, start)
local lnum = (start ~= nil and type(start) == 'number') and start or 1 local lnum = (start ~= nil and type(start) == 'number') and start or 1
local tail = (lnum < 0) local tail = (lnum < 0)
@@ -883,9 +703,9 @@ function module.read_file_list(filename, start)
return lines return lines
end end
-- Reads the entire contents of `filename` into a string. --- Reads the entire contents of `filename` into a string.
-- --- @param filename string
-- filename: path to file --- @return string?
function module.read_file(filename) function module.read_file(filename)
local file = io.open(filename, 'r') local file = io.open(filename, 'r')
if not file then if not file then
@@ -901,6 +721,7 @@ function module.write_file(name, text, no_dedent, append)
local file = assert(io.open(name, (append and 'a' or 'w'))) local file = assert(io.open(name, (append and 'a' or 'w')))
if type(text) == 'table' then if type(text) == 'table' then
-- Byte blob -- Byte blob
--- @type string[]
local bytes = text local bytes = text
text = '' text = ''
for _, char in ipairs(bytes) do for _, char in ipairs(bytes) do
@@ -914,6 +735,8 @@ function module.write_file(name, text, no_dedent, append)
file:close() file:close()
end end
--- @param name? 'cirrus'|'github'
--- @return boolean
function module.is_ci(name) function module.is_ci(name)
local any = (name == nil) local any = (name == nil)
luaassert(any or name == 'github' or name == 'cirrus') luaassert(any or name == 'github' or name == 'cirrus')
@@ -946,12 +769,10 @@ function module.read_nvim_log(logfile, ci_rename)
end end
--- @param path string --- @param path string
--- @return string --- @return boolean?
function module.mkdir(path) function module.mkdir(path)
-- 493 is 0755 in decimal -- 493 is 0755 in decimal
return uv.fs_mkdir(path, 493) return (uv.fs_mkdir(path, 493))
end end
module = vim.tbl_extend('error', module, Paths, require('test.deprecated'))
return module return module

View File

@@ -2,8 +2,8 @@ local ffi = require('ffi')
local formatc = require('test.unit.formatc') local formatc = require('test.unit.formatc')
local Set = require('test.unit.set') local Set = require('test.unit.set')
local Preprocess = require('test.unit.preprocess') local Preprocess = require('test.unit.preprocess')
local Paths = require('test.cmakeconfig.paths')
local global_helpers = require('test.helpers') local global_helpers = require('test.helpers')
local paths = global_helpers.paths
local assert = require('luassert') local assert = require('luassert')
local say = require('say') local say = require('say')
@@ -15,7 +15,7 @@ local eq = global_helpers.eq
local trim = vim.trim local trim = vim.trim
-- add some standard header locations -- add some standard header locations
for _, p in ipairs(Paths.include_paths) do for _, p in ipairs(paths.include_paths) do
Preprocess.add_to_include_path(p) Preprocess.add_to_include_path(p)
end end
@@ -728,7 +728,7 @@ local function check_child_err(rd)
--- @type string --- @type string
err = err .. '\nNo end of trace occurred' err = err .. '\nNo end of trace occurred'
end end
local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true) local cc_err, cc_emsg = pcall(check_cores, paths.test_luajit_prg, true)
if not cc_err then if not cc_err then
--- @type string --- @type string
err = err .. '\ncheck_cores failed: ' .. cc_emsg err = err .. '\ncheck_cores failed: ' .. cc_emsg
@@ -749,7 +749,7 @@ local function itp_parent(rd, pid, allow_failure, location)
io.stderr:write('Errorred out (' .. status .. '):\n' .. tostring(emsg) .. '\n') io.stderr:write('Errorred out (' .. status .. '):\n' .. tostring(emsg) .. '\n')
os.execute([[ os.execute([[
sh -c "source ci/common/test.sh sh -c "source ci/common/test.sh
check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]]) check_core_dumps --delete \"]] .. paths.test_luajit_prg .. [[\""]])
else else
error(tostring(emsg) .. '\nexit code: ' .. status) error(tostring(emsg) .. '\nexit code: ' .. status)
end end
@@ -797,7 +797,7 @@ local function gen_itp(it)
end end
local function cppimport(path) local function cppimport(path)
return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path) return cimport(paths.test_source_path .. '/test/includes/pre/' .. path)
end end
cimport( cimport(

View File

@@ -14,8 +14,8 @@ local ffi = helpers.ffi
local neq = helpers.neq local neq = helpers.neq
local eq = helpers.eq local eq = helpers.eq
local mergedicts_copy = helpers.mergedicts_copy local mergedicts_copy = helpers.mergedicts_copy
local format_string = helpers.format_string local format_string = require('test.format_string').format_string
local format_luav = helpers.format_luav local format_luav = require('test.format_string').format_luav
local intchar2lua = helpers.intchar2lua local intchar2lua = helpers.intchar2lua
local dictdiff = helpers.dictdiff local dictdiff = helpers.dictdiff

View File

@@ -1,6 +1,4 @@
local global_helpers = require('test.helpers') local REMOVE_THIS = vim.NIL
local REMOVE_THIS = global_helpers.REMOVE_THIS
return function(itp, _check_parsing, hl, fmtn) return function(itp, _check_parsing, hl, fmtn)
local function check_parsing(...) local function check_parsing(...)