test: unreliable pack_spec.after_each: "EBUSY: resource busy or locked" #39606

Problem:
`EBUSY` during cleanup:
Windows CI can intermittently fail `pack_spec.lua` with `EBUSY` while removing
`site/pack/core/opt/plugindirs`.

This can happen because:
- the test Nvim session may still be alive when `after_each()` removes the pack
  directory
- Windows does not allow removing a directory while another process still has an
  open handle below it
- startup-time `vim.pack.add()` performs a real `git clone`, so process and file
  handle release timing can vary on slower runners

Startup timeout:
The startup tests can also fail before cleanup because they wait for `_G.done`
with a fixed timeout. That timeout includes the time needed for startup to run
`vim.pack.add()` and finish the local clone.

Solution:
Close before cleanup:
Capture the pack, lockfile, and log paths while the test Nvim session is still
available, then call `n.check_close()` before removing the pack directory.

Extend Windows startup wait:
Increase the `_G.done` retry budget only on Windows so startup-time
`vim.pack.add()` has more time to finish on slower CI runners.
This commit is contained in:
David Balatero
2026-05-05 13:57:56 -04:00
committed by GitHub
parent 78111e5371
commit 19a2ef5afa
+11 -3
View File
@@ -413,9 +413,17 @@ describe('vim.pack', function()
end)
after_each(function()
n.rmdir(pack_get_dir())
pcall(vim.fs.rm, get_lock_path(), { force = true })
local pack_dir = pack_get_dir()
local lock_path = get_lock_path()
local log_path = vim.fs.joinpath(fn.stdpath('log'), 'nvim-pack.log')
-- Wait for neovim to close before removing directories so it can release
-- the file handles it has open. We don't want to conflict with open files
-- when we remove dirs below.
n.check_close()
n.rmdir(pack_dir)
pcall(vim.fs.rm, lock_path, { force = true })
pcall(vim.fs.rm, log_path, { force = true })
end)
@@ -879,7 +887,7 @@ describe('vim.pack', function()
local function assert_works()
-- Should auto-install but wait before executing code after it
n.clear({ args_rm = { '-u' } })
t.retry(nil, 5000, function()
t.retry(nil, t.is_os('win') and 30000 or 5000, function()
eq(true, exec_lua('return _G.done'))
end)
assert_loaded()