mirror of
https://github.com/neovim/neovim.git
synced 2026-04-21 06:45:37 +00:00
test(lsp): fix fake LSP server timeout not working (#37970)
Problem: Fake LSP server does not timeout or respond to SIGTERM as it
does not run the event loop.
Solution: Instead of io.read(), use stdioopen()'s on_stdin callback to
accumulate input and use vim.wait() to wait for input.
Also, in the test suite, don't stop a session when it's not running, as
calling uv.stop() outside uv.run() will instead cause the next uv.run()
to stop immediately, which cancels the next RPC request.
This commit is contained in:
@@ -1,10 +1,22 @@
|
||||
local protocol = require 'vim.lsp.protocol'
|
||||
|
||||
local pid = vim.uv.os_getpid()
|
||||
local stdin = ''
|
||||
vim.fn.stdioopen({
|
||||
on_stdin = function(_, data, _)
|
||||
stdin = stdin .. table.concat(data, '\n')
|
||||
end,
|
||||
})
|
||||
|
||||
-- Logs to $NVIM_LOG_FILE.
|
||||
--
|
||||
-- TODO(justinmk): remove after https://github.com/neovim/neovim/pull/7062
|
||||
local function log(loglevel, area, msg)
|
||||
vim.fn.writefile({ string.format('%s %s: %s', loglevel, area, msg) }, vim.env.NVIM_LOG_FILE, 'a')
|
||||
vim.fn.writefile(
|
||||
{ string.format('%d %s %s: %s', pid, loglevel, area, msg) },
|
||||
vim.env.NVIM_LOG_FILE,
|
||||
'a'
|
||||
)
|
||||
end
|
||||
|
||||
local function message_parts(sep, ...)
|
||||
@@ -46,10 +58,30 @@ local function format_message_with_content_length(encoded_message)
|
||||
}
|
||||
end
|
||||
|
||||
local function read_line()
|
||||
vim.wait(math.huge, function()
|
||||
return stdin:find('\n') ~= nil
|
||||
end, 1)
|
||||
local eol = assert(stdin:find('\n'))
|
||||
local line = stdin:sub(1, eol - 1)
|
||||
stdin = stdin:sub(eol + 1)
|
||||
return line
|
||||
end
|
||||
|
||||
--- @param len integer
|
||||
local function read_len(len)
|
||||
vim.wait(math.huge, function()
|
||||
return stdin:len() >= len
|
||||
end, 1)
|
||||
local content = stdin:sub(1, len)
|
||||
stdin = stdin:sub(len + 1)
|
||||
return content
|
||||
end
|
||||
|
||||
local function read_message()
|
||||
local line = io.read('*l')
|
||||
local line = read_line()
|
||||
local length = line:lower():match('content%-length:%s*(%d+)')
|
||||
return vim.json.decode(io.read(2 + length):sub(2))
|
||||
return vim.json.decode(read_len(2 + length):sub(2))
|
||||
end
|
||||
|
||||
local function send(payload)
|
||||
@@ -1045,21 +1077,24 @@ end
|
||||
|
||||
-- Tests will be indexed by test_name
|
||||
local test_name = arg[1]
|
||||
local timeout = arg[2]
|
||||
local timeout = tonumber(arg[2])
|
||||
assert(type(test_name) == 'string', 'test_name must be specified as first arg.')
|
||||
|
||||
local kill_timer = assert(vim.uv.new_timer())
|
||||
kill_timer:start(timeout or 1e3, 0, function()
|
||||
kill_timer:stop()
|
||||
kill_timer:close()
|
||||
local kill_timer = vim.defer_fn(function()
|
||||
log('ERROR', 'LSP', 'TIMEOUT')
|
||||
io.stderr:write('TIMEOUT')
|
||||
os.exit(100)
|
||||
end)
|
||||
end, timeout or 1e3)
|
||||
|
||||
-- Close the timer on exit (deadly signal or :cquit) to avoid delay with ASAN/TSAN.
|
||||
vim.api.nvim_create_autocmd('VimLeave', {
|
||||
callback = function()
|
||||
kill_timer:stop()
|
||||
kill_timer:close()
|
||||
end,
|
||||
})
|
||||
|
||||
local status, err = pcall(assert(tests[test_name], 'Test not found'))
|
||||
kill_timer:stop()
|
||||
kill_timer:close()
|
||||
if not status then
|
||||
log('ERROR', 'LSP', tostring(err))
|
||||
io.stderr:write(err)
|
||||
|
||||
@@ -110,6 +110,7 @@ M.create_server_definition = function()
|
||||
|
||||
function srv.terminate()
|
||||
closing = true
|
||||
dispatchers.on_exit(0, 15)
|
||||
end
|
||||
|
||||
return srv
|
||||
|
||||
@@ -88,7 +88,11 @@ describe('LSP', function()
|
||||
|
||||
after_each(function()
|
||||
stop()
|
||||
exec_lua('vim.iter(lsp.get_clients()):each(function(client) client:stop(true) end)')
|
||||
exec_lua(function()
|
||||
vim.iter(vim.lsp.get_clients({ _uninitialized = true })):each(function(client)
|
||||
client:stop(true)
|
||||
end)
|
||||
end)
|
||||
api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
|
||||
end)
|
||||
|
||||
@@ -206,18 +210,18 @@ describe('LSP', function()
|
||||
it('does not reuse an already-stopping client #33616', function()
|
||||
-- we immediately try to start a second client with the same name/root
|
||||
-- before the first one has finished shutting down; we must get a new id.
|
||||
local clients = exec_lua([[
|
||||
local client1 = vim.lsp.start({
|
||||
local clients = exec_lua(function()
|
||||
local client1 = assert(vim.lsp.start({
|
||||
name = 'dup-test',
|
||||
cmd = { vim.v.progpath, '-l', fake_lsp_code, 'basic_init' },
|
||||
}, { attach = false })
|
||||
}, { attach = false }))
|
||||
vim.lsp.get_client_by_id(client1):stop()
|
||||
local client2 = vim.lsp.start({
|
||||
local client2 = assert(vim.lsp.start({
|
||||
name = 'dup-test',
|
||||
cmd = { vim.v.progpath, '-l', fake_lsp_code, 'basic_init' },
|
||||
}, { attach = false })
|
||||
}, { attach = false }))
|
||||
return { client1, client2 }
|
||||
]])
|
||||
end)
|
||||
local c1, c2 = clients[1], clients[2]
|
||||
eq(false, c1 == c2, 'Expected a fresh client while the old one is stopping')
|
||||
end)
|
||||
@@ -320,14 +324,8 @@ describe('LSP', function()
|
||||
)
|
||||
|
||||
it('should succeed with manual shutdown', function()
|
||||
if is_ci() then
|
||||
pending('hangs the build on CI #14028, re-enable with freeze timeout #14204')
|
||||
return
|
||||
elseif t.skip_fragile(pending) then
|
||||
return
|
||||
end
|
||||
local expected_handlers = {
|
||||
{ NIL, {}, { method = 'shutdown', bufnr = 1, client_id = 1, version = 0 } },
|
||||
{ NIL, {}, { method = 'shutdown', bufnr = 1, client_id = 1, request_id = 2, version = 0 } },
|
||||
{ NIL, {}, { method = 'test', client_id = 1 } },
|
||||
}
|
||||
test_rpc_server {
|
||||
|
||||
@@ -320,7 +320,9 @@ function M.run(request_cb, notification_cb, setup_cb, timeout)
|
||||
end
|
||||
|
||||
function M.stop()
|
||||
assert(session):stop()
|
||||
if loop_running then
|
||||
assert(session):stop()
|
||||
end
|
||||
end
|
||||
|
||||
-- Use for commands which expect nvim to quit.
|
||||
|
||||
Reference in New Issue
Block a user