mirror of
https://github.com/neovim/neovim.git
synced 2026-05-24 22:00:10 +00:00
Problem: The Lua test harness still ran through standalone -ll mode, so tests depended on the low-level Lua path instead of the regular Nvim Lua environment. That also meant os.exit() coverage had to carry an ASAN workaround because Lua's raw process exit skipped Nvim teardown and let LeakSanitizer interfere with the observed exit code. Solution: Run the harness and related fixtures with nvim -l. Patch os.exit() in the main Lua state to exit through getout(), so scripts observe normal Nvim shutdown while standalone -ll remains available for generator-style scripts. As a consequence, the startup test can assert os.exit() without disabling leak detection. AI-assisted: Codex
289 lines
8.0 KiB
Lua
289 lines
8.0 KiB
Lua
local t = require('test.testutil')
|
|
local n = require('test.functional.testnvim')()
|
|
local Screen = require('test.functional.ui.screen')
|
|
|
|
local clear = n.clear
|
|
local command = n.command
|
|
local eq = t.eq
|
|
local eval = n.eval
|
|
local exec = n.exec
|
|
local feed = n.feed
|
|
local api = n.api
|
|
local request = n.request
|
|
local poke_eventloop = n.poke_eventloop
|
|
local pcall_err = t.pcall_err
|
|
local uv = vim.uv
|
|
|
|
describe('nvim_ui_attach()', function()
|
|
before_each(function()
|
|
clear()
|
|
end)
|
|
|
|
it('handles very large width/height #2180', function()
|
|
local _ = Screen.new(999, 999)
|
|
eq(999, eval('&lines'))
|
|
eq(999, eval('&columns'))
|
|
end)
|
|
|
|
it('validation', function()
|
|
eq("Invalid UI option: 'foo'", pcall_err(api.nvim_ui_attach, 80, 24, { foo = { 'foo' } }))
|
|
|
|
eq(
|
|
"Invalid 'ext_linegrid': expected Boolean, got Array",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { ext_linegrid = {} })
|
|
)
|
|
eq(
|
|
"Invalid 'override': expected Boolean, got Array",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { override = {} })
|
|
)
|
|
eq(
|
|
"Invalid 'rgb': expected Boolean, got Array",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { rgb = {} })
|
|
)
|
|
eq(
|
|
"Invalid 'term_name': expected String, got Boolean",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { term_name = true })
|
|
)
|
|
eq(
|
|
"Invalid 'term_colors': expected Integer, got Boolean",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { term_colors = true })
|
|
)
|
|
eq(
|
|
"Invalid 'stdin_fd': expected Integer, got String",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { stdin_fd = 'foo' })
|
|
)
|
|
eq(
|
|
"Invalid 'stdin_tty': expected Boolean, got String",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { stdin_tty = 'foo' })
|
|
)
|
|
eq(
|
|
"Invalid 'stdout_tty': expected Boolean, got String",
|
|
pcall_err(api.nvim_ui_attach, 80, 24, { stdout_tty = 'foo' })
|
|
)
|
|
|
|
eq('UI not attached to channel: 1', pcall_err(request, 'nvim_ui_try_resize', 40, 10))
|
|
eq('UI not attached to channel: 1', pcall_err(request, 'nvim_ui_set_option', 'rgb', true))
|
|
eq('UI not attached to channel: 1', pcall_err(request, 'nvim_ui_detach'))
|
|
|
|
local _ = Screen.new(nil, nil, { rgb = false })
|
|
eq(
|
|
'UI already attached to channel: 1',
|
|
pcall_err(request, 'nvim_ui_attach', 40, 10, { rgb = false })
|
|
)
|
|
end)
|
|
|
|
it('does not crash if maximum UI count is reached', function()
|
|
local server = api.nvim_get_vvar('servername')
|
|
local screens = {} --- @type test.functional.ui.screen[]
|
|
for i = 1, 16 do
|
|
screens[i] = Screen.new(nil, nil, nil, n.connect(server))
|
|
end
|
|
eq(
|
|
-- 0 is kErrorTypeException
|
|
{ false, { 0, 'Maximum UI count reached' } },
|
|
{ n.connect(server):request('nvim_ui_attach', 80, 24, {}) }
|
|
)
|
|
for i = 1, 16 do
|
|
screens[i]:detach()
|
|
end
|
|
end)
|
|
end)
|
|
|
|
describe('nvim_ui_send', function()
|
|
before_each(function()
|
|
clear()
|
|
end)
|
|
|
|
local function close_pipe(pipe)
|
|
if not pipe:is_closing() then
|
|
pipe:read_stop()
|
|
pipe:close()
|
|
end
|
|
end
|
|
|
|
it('works with stdout_tty', function()
|
|
local fds = assert(uv.pipe())
|
|
|
|
local read_pipe = assert(uv.new_pipe())
|
|
read_pipe:open(fds.read)
|
|
|
|
local read_data = {}
|
|
read_pipe:read_start(function(err, data)
|
|
assert(not err, err)
|
|
if data then
|
|
table.insert(read_data, data)
|
|
end
|
|
end)
|
|
|
|
local screen = Screen.new(50, 10, { stdout_tty = true })
|
|
screen:set_stdout(fds.write)
|
|
finally(function()
|
|
screen:detach()
|
|
close_pipe(read_pipe)
|
|
end)
|
|
|
|
api.nvim_ui_send('Hello world')
|
|
|
|
poke_eventloop()
|
|
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*8
|
|
|
|
|
]])
|
|
|
|
eq('Hello world', table.concat(read_data))
|
|
end)
|
|
|
|
it('ignores ui_send event for UIs without stdout_tty', function()
|
|
local fds = assert(uv.pipe())
|
|
|
|
local read_pipe = assert(uv.new_pipe())
|
|
read_pipe:open(fds.read)
|
|
|
|
local read_data = {}
|
|
read_pipe:read_start(function(err, data)
|
|
assert(not err, err)
|
|
if data then
|
|
table.insert(read_data, data)
|
|
end
|
|
end)
|
|
|
|
local screen = Screen.new(50, 10)
|
|
screen:set_stdout(fds.write)
|
|
finally(function()
|
|
screen:detach()
|
|
close_pipe(read_pipe)
|
|
end)
|
|
|
|
api.nvim_ui_send('Hello world')
|
|
|
|
poke_eventloop()
|
|
|
|
screen:expect([[
|
|
^ |
|
|
{1:~ }|*8
|
|
|
|
|
]])
|
|
|
|
eq('', table.concat(read_data))
|
|
end)
|
|
end)
|
|
|
|
it('autocmds UIEnter/UILeave', function()
|
|
clear { args_rm = { '--headless' } }
|
|
exec([[
|
|
let g:evs = []
|
|
autocmd UIEnter * call add(g:evs, "UIEnter") | let g:uienter_ev = deepcopy(v:event)
|
|
autocmd UILeave * call add(g:evs, "UILeave") | let g:uileave_ev = deepcopy(v:event)
|
|
autocmd VimEnter * call add(g:evs, "VimEnter")
|
|
autocmd VimLeave * call add(g:evs, "VimLeave")
|
|
]])
|
|
|
|
local screen = Screen.new()
|
|
eq({ chan = 1 }, eval('g:uienter_ev'))
|
|
eq({ 'VimEnter', 'UIEnter' }, eval('g:evs'))
|
|
|
|
screen:detach()
|
|
eq({ chan = 1 }, eval('g:uileave_ev'))
|
|
eq({ 'VimEnter', 'UIEnter', 'UILeave' }, eval('g:evs'))
|
|
|
|
local servername = api.nvim_get_vvar('servername')
|
|
|
|
local session2 = n.connect(servername)
|
|
local status2, chan2 = session2:request('nvim_get_chan_info', 0)
|
|
t.ok(status2)
|
|
|
|
local session3 = n.connect(servername)
|
|
local status3, chan3 = session3:request('nvim_get_chan_info', 0)
|
|
t.ok(status3)
|
|
|
|
local screen2 = Screen.new(nil, nil, nil, session2)
|
|
eq({ chan = chan2.id }, eval('g:uienter_ev'))
|
|
eq({ 'VimEnter', 'UIEnter', 'UILeave', 'UIEnter' }, eval('g:evs'))
|
|
|
|
screen2:detach()
|
|
eq({ chan = chan2.id }, eval('g:uileave_ev'))
|
|
eq({ 'VimEnter', 'UIEnter', 'UILeave', 'UIEnter', 'UILeave' }, eval('g:evs'))
|
|
|
|
command('let g:evs = ["…"]')
|
|
|
|
screen2:attach(session2)
|
|
eq({ chan = chan2.id }, eval('g:uienter_ev'))
|
|
eq({ '…', 'UIEnter' }, eval('g:evs'))
|
|
|
|
Screen.new(nil, nil, nil, session3)
|
|
eq({ chan = chan3.id }, eval('g:uienter_ev'))
|
|
eq({ '…', 'UIEnter', 'UIEnter' }, eval('g:evs'))
|
|
|
|
screen:attach(n.get_session())
|
|
eq({ chan = 1 }, eval('g:uienter_ev'))
|
|
eq({ '…', 'UIEnter', 'UIEnter', 'UIEnter' }, eval('g:evs'))
|
|
|
|
session3:close()
|
|
t.retry(nil, 1000, function()
|
|
eq({}, api.nvim_get_chan_info(chan3.id))
|
|
end)
|
|
eq({ chan = chan3.id }, eval('g:uileave_ev'))
|
|
eq({ '…', 'UIEnter', 'UIEnter', 'UIEnter', 'UILeave' }, eval('g:evs'))
|
|
|
|
command('let g:evs = ["…"]')
|
|
command('autocmd UILeave * call writefile(g:evs, "Xevents.log")')
|
|
finally(function()
|
|
os.remove('Xevents.log')
|
|
end)
|
|
n.expect_exit(command, 'qall!')
|
|
n.check_close() -- Wait for process exit.
|
|
-- UILeave should have been triggered for both remaining UIs.
|
|
eq('…\nVimLeave\nUILeave\nUILeave\n', t.read_file('Xevents.log'))
|
|
end)
|
|
|
|
it('autocmds VimSuspend/VimResume #22041', function()
|
|
clear()
|
|
local screen = Screen.new()
|
|
exec([[
|
|
let g:ev = []
|
|
autocmd VimResume * :call add(g:ev, 'r')
|
|
autocmd VimSuspend * :call add(g:ev, 's')
|
|
]])
|
|
|
|
eq(false, screen.suspended)
|
|
feed('<C-Z>')
|
|
screen:expect(function()
|
|
eq(true, screen.suspended)
|
|
end)
|
|
eq({ 's' }, eval('g:ev'))
|
|
screen.suspended = false
|
|
feed('<Ignore>')
|
|
eq({ 's', 'r' }, eval('g:ev'))
|
|
|
|
command('suspend')
|
|
screen:expect(function()
|
|
eq(true, screen.suspended)
|
|
end)
|
|
eq({ 's', 'r', 's' }, eval('g:ev'))
|
|
screen.suspended = false
|
|
api.nvim_input_mouse('move', '', '', 0, 0, 0)
|
|
eq({ 's', 'r', 's', 'r' }, eval('g:ev'))
|
|
|
|
feed('<C-Z><C-Z><C-Z>')
|
|
screen:expect(function()
|
|
eq(true, screen.suspended)
|
|
end)
|
|
api.nvim_ui_set_focus(false)
|
|
eq({ 's', 'r', 's', 'r', 's' }, eval('g:ev'))
|
|
screen.suspended = false
|
|
api.nvim_ui_set_focus(true)
|
|
eq({ 's', 'r', 's', 'r', 's', 'r' }, eval('g:ev'))
|
|
|
|
command('suspend | suspend | suspend')
|
|
screen:expect(function()
|
|
eq(true, screen.suspended)
|
|
end)
|
|
screen:detach()
|
|
eq({ 's', 'r', 's', 'r', 's', 'r', 's' }, eval('g:ev'))
|
|
screen.suspended = false
|
|
screen:attach()
|
|
eq({ 's', 'r', 's', 'r', 's', 'r', 's', 'r' }, eval('g:ev'))
|
|
end)
|