test(tui): unreliable "TUI exits immediately when stdin is closed" #41367

Problem:
Unreliable test on slow CI (ASAN/TSAN):

    FAILED  .../tui_spec.lua @ 3054: TUI exits immediately when stdin is closed
    retry() attempts: 1
    Expected: vim.NIL
    Actual: { name = "nvim", pid = 33201, ppid = -1 }

The test asserts "immediate" exit of the Nvim process, but this may be
subject to OS delays outside of our control.

Solution:
Make the "timed out waiting for DA1" log conditional on the actual
timeout, and assert the logs in the test.
This commit is contained in:
Justin M. Keyes
2026-08-18 09:22:05 -04:00
committed by GitHub
parent ab82c2c6b0
commit 0c091cedc2
2 changed files with 25 additions and 12 deletions

View File

@@ -35,6 +35,7 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
#include "nvim/strings.h"
#include "nvim/tui/input.h"
#include "nvim/tui/termdef_field_defs.h"
@@ -765,9 +766,10 @@ void tui_stop(TUIData *tui)
terminfo_disable(tui);
// Wait until DA1 response is received, or stdin is closed (#35744).
uint64_t wait_start = os_hrtime();
LOOP_PROCESS_EVENTS_UNTIL(tui->loop, tui->loop->events, EXIT_TIMEOUT_MS,
tui->stopped || tui->input.read_stream.did_eof);
if (!tui->stopped && !tui->input.read_stream.did_eof) {
if (!tui->stopped && (os_hrtime() - wait_start) / 1000000 >= EXIT_TIMEOUT_MS) {
WLOG("TUI: timed out waiting for DA1 response");
}
tui->stopped = true;

View File

@@ -3047,17 +3047,6 @@ describe('TUI', function()
screen:expect({ any = vim.pesc('[Process exited 1]') })
end)
it('exits immediately when stdin is closed #35744', function()
local chan = api.nvim_get_option_value('channel', { buf = 0 })
local pid = fn.jobpid(chan)
fn.chanclose(chan)
retry(nil, 50, function()
eq(vim.NIL, api.nvim_get_proc(pid))
end)
-- FIXME: SIGHUP sometimes isn't caught with ASAN.
screen:expect({ any = t.is_asan() and '%[Process exited %d+%]' or '%[Process exited 1%]' })
end)
it('exits properly when :quit non-last window in event handler #14379', function()
local code = [[
vim.defer_fn(function()
@@ -3663,6 +3652,28 @@ describe('TUI', function()
end
end)
end)
it('exits immediately when stdin is closed #35744', function()
local screen = tt.setup_child_nvim(
{ '--clean', '--cmd', 'set laststatus=2' },
{ env = vim.tbl_extend('force', env_notermguicolors, { NVIM_LOG_FILE = testlog }) }
)
finally(function()
os.remove(testlog)
end)
screen:expect({ any = '%[No Name%]' })
local chan = api.nvim_get_option_value('channel', { buf = 0 })
local pid = fn.jobpid(chan)
fn.chanclose(chan)
retry(nil, 2000, function()
eq(vim.NIL, api.nvim_get_proc(pid))
end)
-- FIXME: SIGHUP sometimes isn't caught with ASAN.
screen:expect({ any = t.is_asan() and '%[Process exited %d+%]' or '%[Process exited 1%]' })
-- Closing stdin must skip the DA1 wait.
t.assert_nolog('timed out waiting for DA1 response', testlog, 100)
end)
end)
describe('TUI UIEnter/UILeave', function()