test: start test runners in Xtest_xdg dir (#37964)

This is a better way to prevent parallel tests from interfering with
each other, as there are many ways files can be created and deleted in
tests, so enforcing different file names is hard.

Using $TMPDIR can also work in most cases, but 'backipskip' etc. have
special defaults for $TMPDIR.

Symlink runtime/, src/, test/ and README.md to Xtest_xdg dir to make
tests more convenient (and symlinking test/ is required for busted).

Also, use README.md instead of test/README.md in the Ex mode inccommand
test, as test/README.md no longer contains 'N' char.
This commit is contained in:
zeertzjq
2026-02-20 06:53:33 +08:00
committed by GitHub
parent 785ac9f228
commit e3d46a6337
15 changed files with 79 additions and 39 deletions

View File

@@ -11,7 +11,7 @@ endif()
set(ENV{NVIM_TEST} "1")
# Set LC_ALL to meet expectations of some locale-sensitive tests.
set(ENV{LC_ALL} "en_US.UTF-8")
set(ENV{VIMRUNTIME} ${WORKING_DIR}/runtime)
set(ENV{VIMRUNTIME} ${ROOT_DIR}/runtime)
set(TEST_XDG_PREFIX ${BUILD_DIR}/Xtest_xdg${TEST_SUFFIX})
set(ENV{XDG_CONFIG_HOME} ${TEST_XDG_PREFIX}/config)
set(ENV{XDG_DATA_HOME} ${TEST_XDG_PREFIX}/share)
@@ -21,6 +21,13 @@ unset(ENV{XDG_DATA_DIRS})
unset(ENV{NVIM}) # Clear $NVIM in case tests are running from Nvim. #11009
unset(ENV{TMUX}) # Nvim TUI shouldn't think it's running in tmux. #34173
# Prepare for running tests in ${TEST_XDG_PREFIX}.
file(MAKE_DIRECTORY ${TEST_XDG_PREFIX})
file(CREATE_LINK ${ROOT_DIR}/runtime ${TEST_XDG_PREFIX}/runtime SYMBOLIC)
file(CREATE_LINK ${ROOT_DIR}/src ${TEST_XDG_PREFIX}/src SYMBOLIC)
file(CREATE_LINK ${ROOT_DIR}/test ${TEST_XDG_PREFIX}/test SYMBOLIC)
file(CREATE_LINK ${ROOT_DIR}/README.md ${TEST_XDG_PREFIX}/README.md SYMBOLIC)
# TODO(dundargoc): The CIRRUS_CI environment variable isn't passed to here from
# the main CMakeLists.txt, so we have to manually pass it to this script and
# re-set the environment variable. Investigate if we can avoid manually setting
@@ -50,7 +57,7 @@ endif()
# Force $TEST_PATH to workdir-relative path ("test/…").
if(IS_ABSOLUTE ${TEST_PATH})
file(RELATIVE_PATH TEST_PATH "${WORKING_DIR}" "${TEST_PATH}")
file(RELATIVE_PATH TEST_PATH "${ROOT_DIR}" "${TEST_PATH}")
endif()
separate_arguments(BUSTED_ARGS NATIVE_COMMAND $ENV{BUSTED_ARGS})
@@ -69,7 +76,7 @@ endif()
# TMPDIR: for testutil.tmpname() and Nvim tempname().
set(ENV{TMPDIR} "${BUILD_DIR}/Xtest_tmpdir${TEST_SUFFIX}")
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory $ENV{TMPDIR})
file(MAKE_DIRECTORY $ENV{TMPDIR})
# HISTFILE: do not write into user's ~/.bash_history
set(ENV{HISTFILE} "/dev/null")
@@ -88,17 +95,17 @@ endif()
execute_process(
# Note: because of "-ll" (low-level interpreter mode), some modules like
# _core/editor.lua are not loaded.
COMMAND ${NVIM_PRG} -ll ${WORKING_DIR}/test/lua_runner.lua ${DEPS_INSTALL_DIR}/share/lua/5.1/ busted -v -o test.busted.outputHandlers.nvim
COMMAND ${NVIM_PRG} -ll ${ROOT_DIR}/test/lua_runner.lua ${DEPS_INSTALL_DIR}/share/lua/5.1/ busted -v -o test.busted.outputHandlers.nvim
-Xoutput "{\"test_path\": \"${TEST_PATH}\", \"summary_file\": \"${TEST_SUMMARY_FILE}\"}"
--lazy --helper=${TEST_DIR}/${TEST_TYPE}/preload.lua
--lpath=${BUILD_DIR}/?.lua
--lpath=${WORKING_DIR}/src/?.lua
--lpath=${WORKING_DIR}/runtime/lua/?.lua
--lpath=${ROOT_DIR}/src/?.lua
--lpath=${ROOT_DIR}/runtime/lua/?.lua
--lpath=?.lua
${BUSTED_ARGS}
${TEST_PATH}
TIMEOUT $ENV{TEST_TIMEOUT}
WORKING_DIRECTORY ${WORKING_DIR}
WORKING_DIRECTORY ${TEST_XDG_PREFIX}
RESULT_VARIABLE res
${EXTRA_ARGS})

View File

@@ -10,7 +10,7 @@ set(TEST_OPTIONS
-D DEPS_INSTALL_DIR=${DEPS_INSTALL_DIR}
-D NVIM_PRG=$<TARGET_FILE:nvim_bin>
-D TEST_DIR=${TEST_DIR}
-D WORKING_DIR=${PROJECT_SOURCE_DIR})
-D ROOT_DIR=${PROJECT_SOURCE_DIR})
check_lua_module(${LUA_PRG} "ffi" LUA_HAS_FFI)
if(LUA_HAS_FFI)

View File

@@ -1780,7 +1780,7 @@ describe('inccommand on ex mode', function()
'-c',
'set termguicolors background=dark',
'-E',
'test/README.md',
'README.md',
}, {
term = true,
env = { VIMRUNTIME = os.getenv('VIMRUNTIME') },

View File

@@ -25,18 +25,18 @@ local function lua_includeexpr(module)
end
describe("ftplugin: Lua 'includeexpr'", function()
local repo_root = ''
local repo_root = vim.fs.normalize(t.paths.test_source_path)
local temp_dir = ''
setup(function()
repo_root = vim.fs.normalize(assert(vim.uv.cwd()))
temp_dir = t.tmpname(false)
n.clear()
n.api.nvim_set_current_dir(repo_root)
end)
teardown(function()
n.expect_exit(n.command, 'qall!')
n.rmdir('runtime/lua/foo/')
n.rmdir(repo_root .. '/runtime/lua/foo/')
end)
before_each(function()

View File

@@ -77,7 +77,7 @@ describe('autocommands that delete and unload buffers:', function()
command('set nohidden')
command('silent! edit Xxx2')
command('silent! edit Xxx1')
command('silent! edit Makefile') -- an existing file
command('silent! edit README.md') -- an existing file
command('silent! split new2')
expect_exit(command, 'silent! quit')
eq('VimLeave done', string.match(read_file(test_file), '^%s*(.-)%s*$'))

View File

@@ -9,16 +9,16 @@ local mkdir = t.mkdir
describe("'autochdir'", function()
it('given on the shell gets processed properly', function()
local targetdir = 'test/functional/fixtures'
local start_dir = vim.uv.cwd()
local target_dir = t.paths.test_source_path .. '/test/functional/fixtures'
-- By default 'autochdir' is off, thus getcwd() returns the repo root.
clear(targetdir .. '/tty-test.c')
local rootdir = fn.getcwd()
local expected = rootdir .. '/' .. targetdir
-- By default 'autochdir' is off, thus getcwd() returns the initial directory.
clear(target_dir .. '/tty-test.c')
eq(start_dir, fn.getcwd())
-- With 'autochdir' on, we should get the directory of tty-test.c.
clear('--cmd', 'set autochdir', targetdir .. '/tty-test.c')
eq(t.is_os('win') and expected:gsub('/', '\\') or expected, fn.getcwd())
clear('--cmd', 'set autochdir', target_dir .. '/tty-test.c')
eq(t.is_os('win') and target_dir:gsub('/', '\\') or target_dir, fn.getcwd())
end)
it('is not overwritten by getwinvar() call #17609', function()

View File

@@ -1083,8 +1083,8 @@ describe('vim.pack', function()
end)
it('is not affected by special environment variables', function()
fn.setenv('GIT_WORK_TREE', fn.getcwd())
fn.setenv('GIT_DIR', vim.fs.joinpath(fn.getcwd(), '.git'))
fn.setenv('GIT_WORK_TREE', t.paths.test_source_path)
fn.setenv('GIT_DIR', vim.fs.joinpath(t.paths.test_source_path, '.git'))
local ref_environ = fn.environ()
vim_pack_add({ repos_src.basic })
@@ -1889,8 +1889,8 @@ describe('vim.pack', function()
end)
it('is not affected by special environment variables', function()
fn.setenv('GIT_WORK_TREE', fn.getcwd())
fn.setenv('GIT_DIR', vim.fs.joinpath(fn.getcwd(), '.git'))
fn.setenv('GIT_WORK_TREE', t.paths.test_source_path)
fn.setenv('GIT_DIR', vim.fs.joinpath(t.paths.test_source_path, '.git'))
local ref_environ = fn.environ()
vim_pack_add({ repos_src.fetch })

View File

@@ -144,7 +144,11 @@ describe('clipboard', function()
it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184', function()
command("let g:clipboard = 'bogus'")
command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
n.exec([[
redir @+>
silent echo system("cat test/functional/fixtures/tty-test.c")
redir END
]])
screen:expect([[
^ |
{1:~ }|*2
@@ -301,8 +305,12 @@ describe('clipboard (with fake clipboard.vim)', function()
it('`:redir @+>` invokes clipboard once-per-message', function()
eq(0, eval('g:clip_called_set'))
command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
-- Assuming CONTRIBUTING.md has >100 lines.
n.exec([[
redir @+>
silent echo system("cat test/functional/fixtures/tty-test.c")
redir END
]])
-- Assuming tty-test.c has >100 lines.
assert(eval('g:clip_called_set') > 100)
end)
@@ -311,7 +319,11 @@ describe('clipboard (with fake clipboard.vim)', function()
-- NOT propagate to the clipboard. This is consistent with Vim.
command('set clipboard=unnamedplus')
eq(0, eval('g:clip_called_set'))
command('redir @"> | :silent echo system("cat CONTRIBUTING.md") | redir END')
n.exec([[
redir @">
silent echo system("cat test/functional/fixtures/tty-test.c")
redir END
]])
eq(0, eval('g:clip_called_set'))
end)

View File

@@ -202,6 +202,12 @@ local function test_terminal_with_fake_shell(backslash)
api.nvim_set_option_value('shell', shell_path, {})
api.nvim_set_option_value('shellcmdflag', 'EXE', {})
api.nvim_set_option_value('shellxquote', '', {}) -- win: avoid extra quotes
t.mkdir('Xsomedir')
t.write_file('Xsomedir/Xuniquefile', '')
end)
after_each(function()
n.rmdir('Xsomedir')
end)
it('with no argument, acts like jobstart(…,{term=true})', function()
@@ -290,7 +296,7 @@ local function test_terminal_with_fake_shell(backslash)
command('autocmd! nvim.terminal TermClose')
feed_command('terminal')
eq('term://', string.match(eval('bufname("%")'), '^term://'))
eq('scripts/shadacat.py', eval('findfile("scripts/shadacat.py", ".")'))
eq('Xsomedir/Xuniquefile', eval('findfile("Xsomedir/Xuniquefile", ".")'))
end)
it('works with :find', function()
@@ -304,26 +310,26 @@ local function test_terminal_with_fake_shell(backslash)
]])
eq('term://', string.match(eval('bufname("%")'), '^term://'))
feed([[<C-\><C-N>]])
feed_command([[find */shadacat.py]])
feed_command([[find */Xuniquefile]])
if is_os('win') then
eq('scripts\\shadacat.py', eval('bufname("%")'))
eq('Xsomedir\\Xuniquefile', eval('bufname("%")'))
else
eq('scripts/shadacat.py', eval('bufname("%")'))
eq('Xsomedir/Xuniquefile', eval('bufname("%")'))
end
end)
it('works with gf', function()
feed_command([[terminal echo "scripts/shadacat.py"]])
feed_command([[terminal echo "Xsomedir/Xuniquefile"]])
screen:expect([[
^ready $ echo "scripts/shadacat.py" |
^ready $ echo "Xsomedir/Xuniquefile" |
|
[Process exited 0] |
:terminal echo "scripts/shadacat.py" |
:terminal echo "Xsomedir/Xuniquefile" |
]])
feed([[<C-\><C-N>]])
eq('term://', string.match(eval('bufname("%")'), '^term://'))
feed([[ggf"lgf]])
eq('scripts/shadacat.py', eval('bufname("%")'))
eq('Xsomedir/Xuniquefile', eval('bufname("%")'))
end)
it('with bufhidden=delete #3958', function()

View File

@@ -1443,7 +1443,7 @@ end)
it('no nil index for missing highlight query', function()
clear()
local cqueries = vim.uv.cwd() .. '/runtime/queries/c/'
local cqueries = t.paths.test_source_path .. '/runtime/queries/c/'
os.rename(cqueries .. 'highlights.scm', cqueries .. '_highlights.scm')
finally(function()
os.rename(cqueries .. '_highlights.scm', cqueries .. 'highlights.scm')

View File

@@ -38,7 +38,7 @@ describe('vim.treesitter.inspect_tree', function()
it('sets correct buffer name', function()
t.skip(t.is_zig_build(), 'vim.treesitter not found after chdir with build.zig')
n.api.nvim_set_current_dir('test/functional/fixtures')
n.api.nvim_set_current_dir(t.paths.test_source_path .. '/test/functional/fixtures')
n.command('edit lua/syntax_error.lua')
eq('lua/syntax_error.lua', n.fn.bufname('%'))
local full_path = n.api.nvim_buf_get_name(0)

View File

@@ -236,6 +236,7 @@ describe('--embed UI', function()
it('updates cwd of attached UI #21771', function()
clear { args_rm = { '--headless' } }
api.nvim_set_current_dir(t.paths.test_source_path)
local screen = Screen.new(40, 8)

View File

@@ -20,6 +20,9 @@ describe('executable()', function()
if is_os('win') then
it('exepath respects shellslash', function()
-- test/ cannot be a symlink in this test.
n.api.nvim_set_current_dir(t.paths.test_source_path)
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
eq(
[[test\functional\fixtures\bin\null.CMD]],
@@ -33,6 +36,9 @@ describe('executable()', function()
end)
it('stdpath respects shellslash', function()
-- Needs to check paths relative to repo root dir.
n.api.nvim_set_current_dir(t.paths.test_source_path)
t.matches(
[[build\Xtest_xdg[%w_]*\share\nvim%-data]],
call('fnamemodify', call('stdpath', 'data'), ':.')

View File

@@ -45,6 +45,9 @@ describe('fnamemodify()', function()
end)
it('handles examples from ":help filename-modifiers"', function()
-- src/ cannot be a symlink in this test.
n.api.nvim_set_current_dir(t.paths.test_source_path)
local filename = 'src/version.c'
local cwd = getcwd()

View File

@@ -131,11 +131,16 @@ describe('system()', function()
before_each(function()
screen = Screen.new()
t.write_file('Xmorefile', ('line1\nline2\nline3\n'):rep(10))
end)
after_each(function()
os.remove('Xmorefile')
end)
if is_os('win') then
local function test_more()
eq('root = true', eval([[get(split(system('"more" ".editorconfig"'), "\n"), 0, '')]]))
eq('line1', eval([[get(split(system('"more" "Xmorefile"'), "\n"), 0, '')]]))
end
local function test_shell_unquoting()
eval([[system('"ping" "-n" "1" "127.0.0.1"')]])