fix(client): avoid :connect race with server detach

Also fix some warnings and flakiness in :restart/:connect tests.
This commit is contained in:
zeertzjq
2025-08-18 09:17:35 +08:00
parent 2211953266
commit f5cbf11644
2 changed files with 47 additions and 57 deletions

View File

@@ -290,6 +290,8 @@ void ui_client_event_connect(Array args)
char *server_addr = args.items[0].data.string.data; char *server_addr = args.items[0].data.string.data;
multiqueue_put(main_loop.fast_events, channel_connect_event, server_addr); multiqueue_put(main_loop.fast_events, channel_connect_event, server_addr);
// Set a dummy channel ID to prevent client exit when server detaches.
ui_client_channel_id = UINT64_MAX;
} }
static void channel_connect_event(void **argv) static void channel_connect_event(void **argv)
@@ -302,15 +304,16 @@ static void channel_connect_event(void **argv)
uint64_t chan = channel_connect(is_tcp, server_addr, true, on_data, 50, &err); uint64_t chan = channel_connect(is_tcp, server_addr, true, on_data, 50, &err);
if (!strequal(err, "")) { if (!strequal(err, "")) {
ELOG("Error handling UI event 'connect': %s", err); ELOG("Cannot connect to server %s: %s", server_addr, err);
return; ui_client_exit_status = 1;
os_exit(1);
} }
ui_client_channel_id = chan; ui_client_channel_id = chan;
ui_client_is_remote = true; ui_client_is_remote = true;
ui_client_attach(tui_width, tui_height, tui_term, tui_rgb); ui_client_attach(tui_width, tui_height, tui_term, tui_rgb);
ELOG("Connected to channel: %" PRId64, chan); ILOG("Connected to server %s on channel %" PRId64, server_addr, chan);
} }
/// When a "restart" UI event is received, its arguments are saved here when /// When a "restart" UI event is received, its arguments are saved here when

View File

@@ -318,6 +318,7 @@ describe('TUI :restart', function()
-- Cancel the operation (abandons restart). -- Cancel the operation (abandons restart).
tt.feed_data('C\013') tt.feed_data('C\013')
screen:expect({ any = vim.pesc('[No Name]') })
-- Check ":confirm restart <cmd>" on a modified buffer. -- Check ":confirm restart <cmd>" on a modified buffer.
tt.feed_data(':confirm restart echo "Hello"\013') tt.feed_data(':confirm restart echo "Hello"\013')
@@ -370,6 +371,12 @@ describe('TUI :connect', function()
return return
end end
local screen_empty = [[
^ |
{100:~ }|*5
|
]]
it('leaves the current server running', function() it('leaves the current server running', function()
n.clear() n.clear()
finally(function() finally(function()
@@ -377,61 +384,45 @@ describe('TUI :connect', function()
end) end)
local server1 = new_pipename() local server1 = new_pipename()
local screen = tt.setup_child_nvim({ local screen1 = tt.setup_child_nvim({ '--listen', server1, '--clean' })
'--listen', screen1:expect({ any = vim.pesc('[No Name]') })
server1,
'-u',
'NONE',
})
tt.feed_data(':connect\013') tt.feed_data(':connect\013')
screen:expect([[ screen1:expect({ any = 'E471: Argument required' })
^ |
~ |*3
[No Name] 0,0-1 All|
E471: Argument required |
{5:-- TERMINAL --} |
]])
screen:detach() tt.feed_data('iThis is server 1.\027')
screen1:expect({ any = vim.pesc('This is server 1^.') })
-- Prevent screen2 from receiving the old terminal state.
command('enew')
screen1:expect(screen_empty)
screen1:detach()
local server2 = new_pipename() local server2 = new_pipename()
local screen2 = tt.setup_child_nvim({ local screen2 = tt.setup_child_nvim({ '--listen', server2, '--clean' })
'--listen', screen2:expect({ any = vim.pesc('[No Name]') })
server2,
'-u',
'NONE',
})
tt.feed_data('iThis is server 2.\027')
tt.feed_data(':connect ' .. server1 .. '\013')
screen2:expect({ tt.feed_data('iThis is server 2.\027')
any = [[Process exited]], screen2:expect({ any = vim.pesc('This is server 2^.') })
})
tt.feed_data(':connect ' .. server1 .. '\013')
screen2:expect({ any = vim.pesc('This is server 1^.') })
local server1_session = n.connect(server1) local server1_session = n.connect(server1)
server1_session:request('nvim_command', 'qall!') server1_session:request('nvim_command', 'qall!')
screen2:expect({ any = [[Process exited]] })
screen2:detach() screen2:detach()
local server2_session = n.connect(server2) local server2_session = n.connect(server2)
local screen3 = tt.setup_child_nvim({ local screen3 = tt.setup_child_nvim({ '--remote-ui', '--server', server2 })
'--remote-ui', screen3:expect({ any = vim.pesc('This is server 2^.') })
'--server',
server2,
})
screen3:expect([[
This is server 2^. |
~ |*3
{2:[No Name] [+] 1,17 All}|
|
{5:-- TERMINAL --} |
]])
screen3:detach() screen3:detach()
server2_session:request('nvim_command', 'qall!') server2_session:request('nvim_command', 'qall!')
end) end)
it('! stops the current server', function() it('! stops the current server', function()
n.clear() n.clear()
finally(function() finally(function()
@@ -439,27 +430,23 @@ describe('TUI :connect', function()
end) end)
local server1 = new_pipename() local server1 = new_pipename()
local screen1 = tt.setup_child_nvim({ local screen1 = tt.setup_child_nvim({ '--listen', server1, '--clean' })
'--listen', screen1:expect({ any = vim.pesc('[No Name]') })
server1,
})
tt.feed_data('iThis is server 1')
tt.feed_data('iThis is server 1.\027')
screen1:expect({ any = vim.pesc('This is server 1^.') })
-- Prevent screen2 from receiving the old terminal state.
command('enew')
screen1:expect(screen_empty)
screen1:detach() screen1:detach()
local server2 = new_pipename() local server2 = new_pipename()
local screen2 = tt.setup_child_nvim({ local screen2 = tt.setup_child_nvim({ '--listen', server2, '--clean' })
'--listen', screen2:expect({ any = vim.pesc('[No Name]') })
server2,
}) tt.feed_data(':connect! ' .. server1 .. '\013')
tt.feed_data('\027:connect! ' .. server1 .. '\013') screen2:expect({ any = vim.pesc('This is server 1^.') })
screen2:expect([[
This is server 1^ |
~ |*3
[No Name] [+] 1,17 All|
-- INSERT -- |
{5:-- TERMINAL --} |
]])
local server1_session = n.connect(server1) local server1_session = n.connect(server1)
server1_session:request('nvim_command', 'qall!') server1_session:request('nvim_command', 'qall!')