diff options
Diffstat (limited to 'test/functional/core')
-rw-r--r-- | test/functional/core/channels_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/core/exit_spec.lua | 38 | ||||
-rw-r--r-- | test/functional/core/fileio_spec.lua | 108 | ||||
-rw-r--r-- | test/functional/core/job_spec.lua | 41 | ||||
-rw-r--r-- | test/functional/core/main_spec.lua | 29 | ||||
-rw-r--r-- | test/functional/core/path_spec.lua | 114 | ||||
-rw-r--r-- | test/functional/core/remote_spec.lua | 41 | ||||
-rw-r--r-- | test/functional/core/spellfile_spec.lua | 26 | ||||
-rw-r--r-- | test/functional/core/startup_spec.lua | 401 |
9 files changed, 587 insertions, 213 deletions
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 8275575c24..5771ddcb94 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -230,6 +230,7 @@ describe('channels', function() end) it('can use buffered output mode', function() + skip(funcs.executable('grep') == 0, 'missing "grep" command') source([[ let g:job_opts = { \ 'on_stdout': function('OnEvent'), @@ -262,6 +263,7 @@ describe('channels', function() end) it('can use buffered output mode with no stream callback', function() + skip(funcs.executable('grep') == 0, 'missing "grep" command') source([[ function! OnEvent(id, data, event) dict call rpcnotify(1, a:event, a:id, a:data, self.stdout) diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 05a69e1992..d474b77806 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -25,30 +25,34 @@ describe('v:exiting', function() eq(1, eval('v:exiting is v:null')) end) - it('is 0 on normal exit', function() + local function test_exiting(setup_fn) local function on_setup() - command('autocmd VimLeavePre * call rpcrequest('..cid..', "")') - command('autocmd VimLeave * call rpcrequest('..cid..', "")') - command('quit') + command('autocmd VimLeavePre * call rpcrequest('..cid..', "exit", "VimLeavePre")') + command('autocmd VimLeave * call rpcrequest('..cid..', "exit", "VimLeave")') + setup_fn() end - local function on_request() + local requests_args = {} + local function on_request(name, args) + eq('exit', name) + table.insert(requests_args, args) eq(0, eval('v:exiting')) return '' end run(on_request, nil, on_setup) + eq({{'VimLeavePre'}, {'VimLeave'}}, requests_args) + end + + it('is 0 on normal exit', function() + test_exiting(function() + command('quit') + end) end) + it('is 0 on exit from Ex mode involving try-catch vim-patch:8.0.0184', function() - local function on_setup() - command('autocmd VimLeavePre * call rpcrequest('..cid..', "")') - command('autocmd VimLeave * call rpcrequest('..cid..', "")') + test_exiting(function() feed('gQ') feed_command('try', 'call NoFunction()', 'catch', 'echo "bye"', 'endtry', 'quit') - end - local function on_request() - eq(0, eval('v:exiting')) - return '' - end - run(on_request, nil, on_setup) + end) end) end) @@ -89,14 +93,14 @@ describe(':cquit', function() end) it('exits with redir msg for multiple exit codes after :cquit 1 2', function() - test_cq('cquit 1 2', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: 2: cquit 1 2') + test_cq('cquit 1 2', nil, 'nvim_exec2(): Vim(cquit):E488: Trailing characters: 2: cquit 1 2') end) it('exits with redir msg for non-number exit code after :cquit X', function() - test_cq('cquit X', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: X: cquit X') + test_cq('cquit X', nil, 'nvim_exec2(): Vim(cquit):E488: Trailing characters: X: cquit X') end) it('exits with redir msg for negative exit code after :cquit -1', function() - test_cq('cquit -1', nil, 'nvim_exec(): Vim(cquit):E488: Trailing characters: -1: cquit -1') + test_cq('cquit -1', nil, 'nvim_exec2(): Vim(cquit):E488: Trailing characters: -1: cquit -1') end) end) diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua index 4e9891a4de..65f947132e 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -1,4 +1,4 @@ -local lfs = require('lfs') +local luv = require('luv') local helpers = require('test.functional.helpers')(after_each) local assert_log = helpers.assert_log @@ -15,10 +15,10 @@ local request = helpers.request local retry = helpers.retry local rmdir = helpers.rmdir local matches = helpers.matches +local meths = helpers.meths local mkdir = helpers.mkdir local sleep = helpers.sleep local read_file = helpers.read_file -local tmpname = helpers.tmpname local trim = helpers.trim local currentdir = helpers.funcs.getcwd local assert_alive = helpers.assert_alive @@ -40,15 +40,18 @@ describe('fileio', function() os.remove('Xtest_startup_file1') os.remove('Xtest_startup_file1~') os.remove('Xtest_startup_file2') + os.remove('Xtest_startup_file2~') os.remove('Xtest_тест.md') os.remove('Xtest-u8-int-max') os.remove('Xtest-overwrite-forced') rmdir('Xtest_startup_swapdir') rmdir('Xtest_backupdir') + rmdir('Xtest_backupdir with spaces') end) it('fsync() codepaths #8304', function() clear({ args={ '-i', 'Xtest_startup_shada', + '--cmd', 'set nofsync', '--cmd', 'set directory=Xtest_startup_swapdir' } }) -- These cases ALWAYS force fsync (regardless of 'fsync' option): @@ -132,6 +135,28 @@ describe('fileio', function() eq('foo', foo_contents); end) + it('backup with full path with spaces', function() + skip(is_ci('cirrus')) + clear() + mkdir('Xtest_backupdir with spaces') + command('set backup') + command('set backupdir=Xtest_backupdir\\ with\\ spaces//') + command('write Xtest_startup_file1') + feed('ifoo<esc>') + command('write') + feed('Abar<esc>') + command('write') + + -- Backup filename = fullpath, separators replaced with "%". + local backup_file_name = string.gsub(currentdir()..'/Xtest_startup_file1', + is_os('win') and '[:/\\]' or '/', '%%') .. '~' + local foo_contents = trim(read_file('Xtest_backupdir with spaces/'..backup_file_name)) + local foobar_contents = trim(read_file('Xtest_startup_file1')) + + eq('foobar', foobar_contents); + eq('foo', foo_contents); + end) + it('backup symlinked files #11349', function() skip(is_ci('cirrus')) clear() @@ -141,7 +166,7 @@ describe('fileio', function() local backup_file_name = link_file_name .. '~' write_file('Xtest_startup_file1', initial_content, false) - lfs.link('Xtest_startup_file1', link_file_name, true) + luv.fs_symlink('Xtest_startup_file1', link_file_name) command('set backup') command('set backupcopy=yes') command('edit ' .. link_file_name) @@ -165,7 +190,7 @@ describe('fileio', function() local backup_file_name = backup_dir .. sep .. link_file_name .. '~' write_file('Xtest_startup_file1', initial_content, false) - lfs.link('Xtest_startup_file1', link_file_name, true) + luv.fs_symlink('Xtest_startup_file1', link_file_name) mkdir(backup_dir) command('set backup') command('set backupcopy=yes') @@ -235,8 +260,8 @@ describe('fileio', function() screen:expect([[ {2:WARNING: The file has been changed since}| {2: reading it!!!} | - {3:Do you really want to write to it (y/n)^?}| - | + {3:Do you really want to write to it (y/n)?}| + ^ | ]]) feed("n") @@ -261,13 +286,11 @@ end) describe('tmpdir', function() local tmproot_pat = [=[.*[/\\]nvim%.[^/\\]+]=] local testlog = 'Xtest_tmpdir_log' - local faketmp + local os_tmpdir before_each(function() -- Fake /tmp dir so that we can mess it up. - faketmp = tmpname() - os.remove(faketmp) - mkdir(faketmp) + os_tmpdir = vim.uv.fs_mkdtemp(vim.fs.dirname(helpers.tmpname()) .. '/nvim_XXXXXXXXXX') end) after_each(function() @@ -275,16 +298,21 @@ describe('tmpdir', function() os.remove(testlog) end) - it('failure modes', function() - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } }) - assert_nolog('tempdir is not a directory', testlog) - assert_nolog('tempdir has invalid permissions', testlog) - + local function get_tmproot() -- Tempfiles typically look like: "…/nvim.<user>/xxx/0". -- - "…/nvim.<user>/xxx/" is the per-process tmpdir, not shared with other Nvims. -- - "…/nvim.<user>/" is the tmpdir root, shared by all Nvims (normally). local tmproot = (funcs.tempname()):match(tmproot_pat) ok(tmproot:len() > 4, 'tmproot like "nvim.foo"', tmproot) + return tmproot + end + + it('failure modes', function() + clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) + assert_nolog('tempdir is not a directory', testlog) + assert_nolog('tempdir has invalid permissions', testlog) + + local tmproot = get_tmproot() -- Test how Nvim handles invalid tmpdir root (by hostile users or accidents). -- @@ -292,7 +320,7 @@ describe('tmpdir', function() expect_exit(command, ':qall!') rmdir(tmproot) write_file(tmproot, '') -- Not a directory, vim_mktempdir() should skip it. - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } }) + clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). -- Assert that broken tmpdir root was handled. assert_log('tempdir root not a directory', testlog, 100) @@ -303,18 +331,62 @@ describe('tmpdir', function() os.remove(tmproot) mkdir(tmproot) funcs.setfperm(tmproot, 'rwxr--r--') -- Invalid permissions, vim_mktempdir() should skip it. - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } }) + clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). -- Assert that broken tmpdir root was handled. assert_log('tempdir root has invalid permissions', testlog, 100) end) it('too long', function() - local bigname = ('%s/%s'):format(faketmp, ('x'):rep(666)) + local bigname = ('%s/%s'):format(os_tmpdir, ('x'):rep(666)) mkdir(bigname) clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=bigname, } }) matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). local len = (funcs.tempname()):len() ok(len > 4 and len < 256, '4 < len < 256', tostring(len)) end) + + it('disappeared #1432', function() + clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) + assert_nolog('tempdir disappeared', testlog) + + local function rm_tmpdir() + local tmpname1 = funcs.tempname() + local tmpdir1 = funcs.fnamemodify(tmpname1, ':h') + eq(funcs.stdpath('run'), tmpdir1) + + rmdir(tmpdir1) + retry(nil, 1000, function() + eq(0, funcs.isdirectory(tmpdir1)) + end) + local tmpname2 = funcs.tempname() + local tmpdir2 = funcs.fnamemodify(tmpname2, ':h') + neq(tmpdir1, tmpdir2) + end + + -- Your antivirus hates you... + rm_tmpdir() + assert_log('tempdir disappeared', testlog, 100) + funcs.tempname() + funcs.tempname() + funcs.tempname() + eq('', meths.get_vvar('errmsg')) + rm_tmpdir() + funcs.tempname() + funcs.tempname() + funcs.tempname() + eq('E5431: tempdir disappeared (2 times)', meths.get_vvar('errmsg')) + rm_tmpdir() + eq('E5431: tempdir disappeared (3 times)', meths.get_vvar('errmsg')) + end) + + it('$NVIM_APPNAME relative path', function() + clear({ env={ + NVIM_APPNAME='a/b', + NVIM_LOG_FILE=testlog, + TMPDIR=os_tmpdir, + } }) + matches([=[.*[/\\]a%%b%.[^/\\]+]=], funcs.tempname()) + end) + end) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 1bae626b98..038368c387 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -88,7 +88,6 @@ describe('jobs', function() end) it('append environment with pty #env', function() - skip(is_os('win')) nvim('command', "let $VAR = 'abc'") nvim('command', "let $TOTO = 'goodbye world'") nvim('command', "let g:job_opts.pty = v:true") @@ -701,7 +700,7 @@ describe('jobs', function() os.remove('Xtest_jobstart_env') end) - describe('jobwait', function() + describe('jobwait()', function() before_each(function() if is_os('win') then helpers.set_shell_powershell() @@ -806,7 +805,6 @@ describe('jobs', function() end) it('can be called recursively', function() - skip(is_os('win'), "TODO: Need `cat`") source([[ let g:opts = {} let g:counter = 0 @@ -876,6 +874,43 @@ describe('jobs', function() eq({'notification', 'wait', {{-1, -1}}}, next_msg()) end) end) + + it('hides cursor and flushes messages before blocking', function() + local screen = Screen.new(50, 6) + screen:set_default_attr_ids({ + [0] = {foreground = Screen.colors.Blue, bold = true}; -- NonText + [1] = {bold = true, reverse = true}; -- MsgSeparator + [2] = {bold = true, foreground = Screen.colors.SeaGreen}; -- MoreMsg + }) + screen:attach() + command([[let g:id = jobstart([v:progpath, '--clean', '--headless'])]]) + source([[ + func PrintAndWait() + echon "aaa\nbbb" + call jobwait([g:id], 300) + echon "\nccc" + endfunc + ]]) + feed_command('call PrintAndWait()') + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }| + {1: }| + aaa | + bbb | + ]], timeout=100} + screen:expect{grid=[[ + | + {1: }| + aaa | + bbb | + ccc | + {2:Press ENTER or type command to continue}^ | + ]]} + feed('<CR>') + funcs.jobstop(meths.get_var('id')) + end) end) pending('exit event follows stdout, stderr', function() diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index ab11e14a67..19c7a93730 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -1,8 +1,9 @@ -local lfs = require('lfs') +local luv = require('luv') local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq +local matches = helpers.matches local feed = helpers.feed local eval = helpers.eval local clear = helpers.clear @@ -12,34 +13,38 @@ local write_file = helpers.write_file local is_os = helpers.is_os local skip = helpers.skip -describe('Command-line option', function() +describe('command-line option', function() describe('-s', function() local fname = 'Xtest-functional-core-main-s' local fname_2 = fname .. '.2' local nonexistent_fname = fname .. '.nonexistent' local dollar_fname = '$' .. fname + before_each(function() clear() os.remove(fname) os.remove(dollar_fname) end) + after_each(function() os.remove(fname) os.remove(dollar_fname) end) + it('treats - as stdin', function() - eq(nil, lfs.attributes(fname)) + eq(nil, luv.fs_stat(fname)) funcs.system( {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', '-s', '-', fname}, {':call setline(1, "42")', ':wqall!', ''}) eq(0, eval('v:shell_error')) - local attrs = lfs.attributes(fname) + local attrs = luv.fs_stat(fname) eq(#('42\n'), attrs.size) end) + it('does not expand $VAR', function() - eq(nil, lfs.attributes(fname)) + eq(nil, luv.fs_stat(fname)) eq(true, not not dollar_fname:find('%$%w+')) write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') funcs.system( @@ -47,9 +52,10 @@ describe('Command-line option', function() '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', '-s', dollar_fname, fname}) eq(0, eval('v:shell_error')) - local attrs = lfs.attributes(fname) + local attrs = luv.fs_stat(fname) eq(#('100500\n'), attrs.size) end) + it('does not crash after reading from stdin in non-headless mode', function() skip(is_os('win')) local screen = Screen.new(40, 8) @@ -100,6 +106,7 @@ describe('Command-line option', function() ]]) ]=] end) + it('errors out when trying to use nonexistent file with -s', function() eq( 'Cannot open for reading: "'..nonexistent_fname..'": no such file or directory\n', @@ -110,6 +117,7 @@ describe('Command-line option', function() '-s', nonexistent_fname})) eq(2, eval('v:shell_error')) end) + it('errors out when trying to use -s twice', function() write_file(fname, ':call setline(1, "1")\n:wqall!\n') write_file(dollar_fname, ':call setline(1, "2")\n:wqall!\n') @@ -121,7 +129,14 @@ describe('Command-line option', function() '--cmd', 'language C', '-s', fname, '-s', dollar_fname, fname_2})) eq(2, eval('v:shell_error')) - eq(nil, lfs.attributes(fname_2)) + eq(nil, luv.fs_stat(fname_2)) end) end) + + it('nvim -v, :version', function() + matches('Run ":verbose version"', funcs.execute(':version')) + matches('Compilation: .*Run :checkhealth', funcs.execute(':verbose version')) + matches('Run "nvim %-V1 %-v"', funcs.system({nvim_prog_abs(), '-v'})) + matches('Compilation: .*Run :checkhealth', funcs.system({nvim_prog_abs(), '-V1', '-v'})) + end) end) diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index a786887bbd..97c32f7de6 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -1,21 +1,25 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear +local command = helpers.command local eq = helpers.eq local eval = helpers.eval -local command = helpers.command -local insert = helpers.insert local feed = helpers.feed +local funcs = helpers.funcs +local insert = helpers.insert local is_os = helpers.is_os +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir +local write_file = helpers.write_file + +local function join_path(...) + local pathsep = (is_os('win') and '\\' or '/') + return table.concat({...}, pathsep) +end describe('path collapse', function() local targetdir local expected_path - local function join_path(...) - local pathsep = (is_os('win') and '\\' or '/') - return table.concat({...}, pathsep) - end - before_each(function() targetdir = join_path('test', 'functional', 'fixtures') clear() @@ -57,14 +61,108 @@ describe('path collapse', function() end) end) +describe('expand wildcard', function() + before_each(clear) + + it('with special characters #24421', function() + local folders = is_os('win') and { + '{folder}', + 'folder$name' + } or { + 'folder-name', + 'folder#name' + } + for _, folder in ipairs(folders) do + mkdir(folder) + local file = join_path(folder, 'file.txt') + write_file(file, '') + eq(file, eval('expand("'..folder..'/*")')) + rmdir(folder) + end + end) +end) + describe('file search', function() + local testdir = 'Xtest_path_spec' + before_each(clear) - it('find multibyte file name in line #20517', function() + setup(function() + mkdir(testdir) + end) + + teardown(function() + rmdir(testdir) + end) + + it('gf finds multibyte filename in line #20517', function() command('cd test/functional/fixtures') insert('filename_with_unicode_ααα') eq('', eval('expand("%")')) feed('gf') eq('filename_with_unicode_ααα', eval('expand("%:t")')) end) + + it('gf/<cfile> matches Windows drive-letter filepaths (without ":" in &isfname)', function() + local iswin = is_os('win') + local function test_cfile(input, expected, expected_win) + expected = (iswin and expected_win or expected) or input + command('%delete') + insert(input) + command('norm! 0') + eq(expected, eval('expand("<cfile>")')) + end + + test_cfile([[c:/d:/foo/bar.txt]]) -- TODO(justinmk): should return "d:/foo/bar.txt" ? + test_cfile([[//share/c:/foo/bar/]]) + test_cfile([[file://c:/foo/bar]]) + test_cfile([[file://c:/foo/bar:42]]) + test_cfile([[file://c:/foo/bar:42:666]]) + test_cfile([[https://c:/foo/bar]]) + test_cfile([[\foo\bar]], [[foo]], [[\foo\bar]]) + test_cfile([[/foo/bar]], [[/foo/bar]]) + test_cfile([[c:\foo\bar]], [[c:]], [[c:\foo\bar]]) + test_cfile([[c:\foo\bar:42:666]], [[c:]], [[c:\foo\bar]]) + test_cfile([[c:/foo/bar]]) + test_cfile([[c:/foo/bar:42]], [[c:/foo/bar]]) + test_cfile([[c:/foo/bar:42:666]], [[c:/foo/bar]]) + test_cfile([[c:foo\bar]], [[c]]) + test_cfile([[c:foo/bar]], [[c]]) + test_cfile([[c:foo]], [[c]]) + -- Examples from: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#example-ways-to-refer-to-the-same-file + test_cfile([[c:\temp\test-file.txt]], [[c:]], [[c:\temp\test-file.txt]]) + test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) + test_cfile([[\\LOCALHOST\c$\temp\test-file.txt]], [[LOCALHOST]], [[\\LOCALHOST\c$\temp\test-file.txt]]) + -- not supported yet + test_cfile([[\\.\c:\temp\test-file.txt]], [[.]], [[\\.\c]]) + -- not supported yet + test_cfile([[\\?\c:\temp\test-file.txt]], [[c:]], [[\\]]) + test_cfile([[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]], [[.]], [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]]) + test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) + end) + + ---@param funcname 'finddir' | 'findfile' + local function test_find_func(funcname, folder, item) + local d = join_path(testdir, folder) + mkdir(d) + local expected = join_path(d, item) + if funcname == 'finddir' then + mkdir(expected) + else + write_file(expected, '') + end + eq(expected, funcs[funcname](item, d:gsub(' ', [[\ ]]))) + end + + it('finddir()', function() + test_find_func('finddir', 'directory', 'folder') + test_find_func('finddir', 'directory', 'folder name') + test_find_func('finddir', 'fold#er name', 'directory') + end) + + it('findfile()', function() + test_find_func('findfile', 'directory', 'file.txt') + test_find_func('findfile', 'directory', 'file name.txt') + test_find_func('findfile', 'fold#er name', 'file.txt') + end) end) diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua index 846d79abf3..a0ec748446 100644 --- a/test/functional/core/remote_spec.lua +++ b/test/functional/core/remote_spec.lua @@ -3,11 +3,12 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local command = helpers.command local eq = helpers.eq +local exec_capture = helpers.exec_capture local exec_lua = helpers.exec_lua local expect = helpers.expect local funcs = helpers.funcs local insert = helpers.insert -local meths = helpers.meths +local nvim_prog = helpers.nvim_prog local new_argv = helpers.new_argv local neq = helpers.neq local set_session = helpers.set_session @@ -38,10 +39,10 @@ describe('Remote', function() server:close() end) + -- Run a `nvim --remote*` command and return { stdout, stderr } of the process local function run_remote(...) set_session(server) local addr = funcs.serverlist()[1] - local client_argv = new_argv({args={'--server', addr, ...}}) -- Create an nvim instance just to run the remote-invoking nvim. We want -- to wait for the remote instance to exit and calling jobwait blocks @@ -50,32 +51,43 @@ describe('Remote', function() local client_starter = spawn(new_argv(), false, nil, true) set_session(client_starter) -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness. - eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], client_argv)) + eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart({...}, { + stdout_buffered = true, + stderr_buffered = true, + on_stdout = function(_, data, _) + _G.Remote_stdout = table.concat(data, '\n') + end, + on_stderr = function(_, data, _) + _G.Remote_stderr = table.concat(data, '\n') + end, + }) })]], nvim_prog, '--clean', '--headless', '--server', addr, ...)) + local res = exec_lua([[return { _G.Remote_stdout, _G.Remote_stderr }]]) client_starter:close() set_session(server) + return res end it('edit a single file', function() - run_remote('--remote', fname) + eq({ '', '' }, run_remote('--remote', fname)) expect(contents) eq(2, #funcs.getbufinfo()) end) it('tab edit a single file with a non-changed buffer', function() - run_remote('--remote-tab', fname) + eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) eq(1, #funcs.gettabinfo()) end) it('tab edit a single file with a changed buffer', function() insert('hello') - run_remote('--remote-tab', fname) + eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) eq(2, #funcs.gettabinfo()) end) it('edit multiple files', function() - run_remote('--remote', fname, other_fname) + eq({ '', '' }, run_remote('--remote', fname, other_fname)) expect(contents) command('next') expect(other_contents) @@ -83,7 +95,7 @@ describe('Remote', function() end) it('send keys', function() - run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v') + eq({ '', '' }, run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v')) expect(contents) eq(2, #funcs.getwininfo()) -- Only a single buffer as we're using edit and not drop like --remote does @@ -91,8 +103,13 @@ describe('Remote', function() end) it('evaluate expressions', function() - run_remote('--remote-expr', 'setline(1, "Yo")') + eq({ '0', '' }, run_remote('--remote-expr', 'setline(1, "Yo")')) + eq({ 'Yo', '' }, run_remote('--remote-expr', 'getline(1)')) expect('Yo') + eq({ ('k'):rep(1234), '' }, run_remote('--remote-expr', 'repeat("k", 1234)')) + eq({ '1.25', '' }, run_remote('--remote-expr', '1.25')) + eq({ 'no', '' }, run_remote('--remote-expr', '0z6E6F')) + eq({ '\t', '' }, run_remote('--remote-expr', '"\t"')) end) end) @@ -101,7 +118,7 @@ describe('Remote', function() expect(contents) eq(1, #funcs.getbufinfo()) -- Since we didn't pass silent, we should get a complaint - neq(nil, string.find(meths.exec('messages', true), 'E247')) + neq(nil, string.find(exec_capture('messages'), 'E247:')) end) it('creates server if not found with tabs', function() @@ -110,10 +127,10 @@ describe('Remote', function() eq(2, #funcs.gettabinfo()) eq(2, #funcs.getbufinfo()) -- We passed silent, so no message should be issued about the server not being found - eq(nil, string.find(meths.exec('messages', true), 'E247')) + eq(nil, string.find(exec_capture('messages'), 'E247:')) end) - pending('exits with error on', function() + describe('exits with error on', function() local function run_and_check_exit_code(...) local bogus_argv = new_argv(...) diff --git a/test/functional/core/spellfile_spec.lua b/test/functional/core/spellfile_spec.lua index afd2c1bce4..e3a59085cf 100644 --- a/test/functional/core/spellfile_spec.lua +++ b/test/functional/core/spellfile_spec.lua @@ -1,5 +1,4 @@ local helpers = require('test.functional.helpers')(after_each) -local lfs = require('lfs') local eq = helpers.eq local clear = helpers.clear @@ -7,6 +6,7 @@ local meths = helpers.meths local exc_exec = helpers.exc_exec local rmdir = helpers.rmdir local write_file = helpers.write_file +local mkdir = helpers.mkdir local testdir = 'Xtest-functional-spell-spellfile.d' @@ -14,8 +14,8 @@ describe('spellfile', function() before_each(function() clear() rmdir(testdir) - lfs.mkdir(testdir) - lfs.mkdir(testdir .. '/spell') + mkdir(testdir) + mkdir(testdir .. '/spell') end) after_each(function() rmdir(testdir) @@ -24,7 +24,7 @@ describe('spellfile', function() -- │ ┌ Spell file version (#VIMSPELLVERSION) local spellheader = 'VIMspell\050' it('errors out when prefcond section is truncated', function() - meths.set_option('runtimepath', testdir) + meths.set_option_value('runtimepath', testdir, {}) write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_PREFCOND) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -34,12 +34,12 @@ describe('spellfile', function() -- │ ┌ Condition length (1 byte) -- │ │ ┌ Condition regex (missing!) .. '\000\001\001') - meths.set_option('spelllang', 'en') + meths.set_option_value('spelllang', 'en', {}) eq('Vim(set):E758: Truncated spell file', exc_exec('set spell')) end) it('errors out when prefcond regexp contains NUL byte', function() - meths.set_option('runtimepath', testdir) + meths.set_option_value('runtimepath', testdir, {}) write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_PREFCOND) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -54,12 +54,12 @@ describe('spellfile', function() -- │ ┌ KWORDTREE tree length (4 bytes) -- │ │ ┌ PREFIXTREE tree length .. '\000\000\000\000\000\000\000\000\000\000\000\000') - meths.set_option('spelllang', 'en') + meths.set_option_value('spelllang', 'en', {}) eq('Vim(set):E759: Format error in spell file', exc_exec('set spell')) end) it('errors out when region contains NUL byte', function() - meths.set_option('runtimepath', testdir) + meths.set_option_value('runtimepath', testdir, {}) write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_REGION) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -71,12 +71,12 @@ describe('spellfile', function() -- │ ┌ KWORDTREE tree length (4 bytes) -- │ │ ┌ PREFIXTREE tree length .. '\000\000\000\000\000\000\000\000\000\000\000\000') - meths.set_option('spelllang', 'en') + meths.set_option_value('spelllang', 'en', {}) eq('Vim(set):E759: Format error in spell file', exc_exec('set spell')) end) it('errors out when SAL section contains NUL byte', function() - meths.set_option('runtimepath', testdir) + meths.set_option_value('runtimepath', testdir, {}) write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_SAL) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -95,15 +95,15 @@ describe('spellfile', function() -- │ ┌ KWORDTREE tree length (4 bytes) -- │ │ ┌ PREFIXTREE tree length .. '\000\000\000\000\000\000\000\000\000\000\000\000') - meths.set_option('spelllang', 'en') + meths.set_option_value('spelllang', 'en', {}) eq('Vim(set):E759: Format error in spell file', exc_exec('set spell')) end) it('errors out when spell header contains NUL bytes', function() - meths.set_option('runtimepath', testdir) + meths.set_option_value('runtimepath', testdir, {}) write_file(testdir .. '/spell/en.ascii.spl', spellheader:sub(1, -3) .. '\000\000') - meths.set_option('spelllang', 'en') + meths.set_option_value('spelllang', 'en', {}) eq('Vim(set):E757: This does not look like a spell file', exc_exec('set spell')) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index e9b47a0251..94ec3d4907 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -9,6 +9,8 @@ local ok = helpers.ok local eq = helpers.eq local matches = helpers.matches local eval = helpers.eval +local exec = helpers.exec +local exec_capture = helpers.exec_capture local exec_lua = helpers.exec_lua local feed = helpers.feed local funcs = helpers.funcs @@ -27,21 +29,36 @@ local meths = helpers.meths local alter_slashes = helpers.alter_slashes local is_os = helpers.is_os local dedent = helpers.dedent - -local testfile = 'Xtest_startuptime' -after_each(function() - os.remove(testfile) -end) +local tbl_map = helpers.tbl_map +local tbl_filter = helpers.tbl_filter +local endswith = helpers.endswith describe('startup', function() it('--clean', function() clear() - ok(string.find(alter_slashes(meths.get_option('runtimepath')), funcs.stdpath('config'), 1, true) ~= nil) + ok(string.find(alter_slashes(meths.get_option_value('runtimepath', {})), funcs.stdpath('config'), 1, true) ~= nil) clear('--clean') - ok(string.find(alter_slashes(meths.get_option('runtimepath')), funcs.stdpath('config'), 1, true) == nil) + ok(string.find(alter_slashes(meths.get_option_value('runtimepath', {})), funcs.stdpath('config'), 1, true) == nil) + end) + + it('prevents remote UI infinite loop', function() + clear() + local screen + screen = Screen.new(84, 3) + screen:attach() + funcs.termopen({ nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' }) + screen:expect([[ + ^Cannot attach UI of :terminal child to its parent. (Unset $NVIM to skip this check) | + | + | + ]]) end) it('--startuptime', function() + local testfile = 'Xtest_startuptime' + finally(function() + os.remove(testfile) + end) clear({ args = {'--startuptime', testfile}}) assert_log('sourcing', testfile, 100) assert_log("require%('vim%._editor'%)", testfile, 100) @@ -58,7 +75,7 @@ describe('startup', function() ^ | | Entering Debug mode. Type "cont" to continue. | - nvim_exec() | + nvim_exec2() | cmd: aunmenu * | > | | @@ -77,13 +94,7 @@ describe('startup', function() end) describe('startup', function() - before_each(function() - clear() - os.remove('Xtest_startup_ttyout') - end) - after_each(function() - os.remove('Xtest_startup_ttyout') - end) + before_each(clear) describe('-l Lua', function() local function assert_l_out(expected, nvim_args, lua_args, script, input) @@ -104,10 +115,11 @@ describe('startup', function() it('os.exit() sets Nvim exitcode', function() -- tricky: LeakSanitizer triggers on os.exit() and disrupts the return value, disable it exec_lua [[ - local asan_options = os.getenv 'ASAN_OPTIONS' - if asan_options ~= nil and asan_options ~= '' then - vim.loop.os_setenv('ASAN_OPTIONS', asan_options..':detect_leaks=0') + local asan_options = os.getenv('ASAN_OPTIONS') or '' + if asan_options ~= '' then + asan_options = asan_options .. ':' end + vim.uv.os_setenv('ASAN_OPTIONS', asan_options .. ':detect_leaks=0') ]] -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ @@ -133,12 +145,12 @@ describe('startup', function() end) it('executes stdin "-"', function() - assert_l_out('arg0=- args=2 whoa', + assert_l_out('arg0=- args=2 whoa\n', nil, { 'arg1', 'arg 2' }, '-', "print(('arg0=%s args=%d %s'):format(_G.arg[0], #_G.arg, 'whoa'))") - assert_l_out('biiig input: 1000042', + assert_l_out('biiig input: 1000042\n', nil, nil, '-', @@ -146,14 +158,37 @@ describe('startup', function() eq(0, eval('v:shell_error')) end) + it('does not truncate long print() message', function() + assert_l_out(('k'):rep(1234) .. '\n', nil, nil, '-', "print(('k'):rep(1234))") + end) + + it('does not add newline when unnecessary', function() + assert_l_out('', nil, nil, '-', '') + assert_l_out('foobar\n', nil, nil, '-', [[print('foobar\n')]]) + end) + it('sets _G.arg', function() + -- nvim -l foo.lua + assert_l_out([[ + bufs: + nvim args: 3 + lua args: { + [0] = "test/functional/fixtures/startup.lua" + } + ]], + {}, + {} + ) + eq(0, eval('v:shell_error')) + -- nvim -l foo.lua [args] assert_l_out([[ bufs: nvim args: 7 lua args: { "-arg1", "--arg2", "--", "arg3", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], {}, { '-arg1', '--arg2', '--', 'arg3' } ) @@ -165,7 +200,8 @@ describe('startup', function() nvim args: 10 lua args: { "-arg1", "arg 2", "--", "file3", "file4", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], { 'file1', 'file2', }, { '-arg1', 'arg 2', '--', 'file3', 'file4' } ) @@ -177,7 +213,8 @@ describe('startup', function() nvim args: 5 lua args: { "-c", "set wrap?", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], {}, { '-c', 'set wrap?' } ) @@ -193,7 +230,8 @@ describe('startup', function() nvim args: 7 lua args: { "-c", "set wrap?", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], { '-c', 'set wrap?' }, { '-c', 'set wrap?' } ) @@ -201,15 +239,24 @@ describe('startup', function() end) it('disables swapfile/shada/config/plugins', function() - assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scriptnames=1', + assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scripts=1\n', nil, nil, '-', - [[print(('updatecount=%d shadafile=%s loadplugins=%s scriptnames=%d'):format( - vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.split(vim.fn.execute('scriptnames'),'\n'))))]]) + [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format( + vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]]) end) end) + it('--cmd/-c/+ do not truncate long Lua print() message with --headless', function() + local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'lua print(("A"):rep(1234))', + '-c', 'lua print(("B"):rep(1234))', + '+lua print(("C"):rep(1234))', + '+q' }) + eq(('A'):rep(1234) .. '\r\n' .. ('B'):rep(1234) .. '\r\n' .. ('C'):rep(1234), out) + end) + it('pipe at both ends: has("ttyin")==0 has("ttyout")==0', function() -- system() puts a pipe at both ends. local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', @@ -254,6 +301,10 @@ describe('startup', function() if is_os('win') then command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end + os.remove('Xtest_startup_ttyout') + finally(function() + os.remove('Xtest_startup_ttyout') + end) -- Running in :terminal command([[exe printf("terminal %s -u NONE -i NONE --cmd \"]] ..nvim_set..[[\"]] @@ -271,6 +322,10 @@ describe('startup', function() if is_os('win') then command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end + os.remove('Xtest_startup_ttyout') + finally(function() + os.remove('Xtest_startup_ttyout') + end) -- Running in :terminal command([[exe printf("terminal echo foo | ]] -- Input from a pipe. ..[[%s -u NONE -i NONE --cmd \"]] @@ -330,28 +385,6 @@ describe('startup', function() { '' })) end) - it('-e/-E interactive #7679', function() - clear('-e') - local screen = Screen.new(25, 3) - screen:attach() - feed("put ='from -e'<CR>") - screen:expect([[ - :put ='from -e' | - from -e | - :^ | - ]]) - - clear('-E') - screen = Screen.new(25, 3) - screen:attach() - feed("put ='from -E'<CR>") - screen:expect([[ - :put ='from -E' | - from -E | - :^ | - ]]) - end) - it('stdin with -es/-Es #7679', function() local input = { 'append', 'line1', 'line2', '.', '%print', '' } local inputstr = table.concat(input, '\n') @@ -397,33 +430,16 @@ describe('startup', function() for _,arg in ipairs({'-es', '-Es'}) do local out = funcs.system({nvim_prog, arg, '+set swapfile? updatecount? shadafile?', - "+put =execute('scriptnames')", '+%print'}) + "+put =map(getscriptinfo(), {-> v:val.name})", '+%print'}) local line1 = string.match(out, '^.-\n') -- updatecount=0 means swapfile was disabled. eq(" swapfile updatecount=0 shadafile=\n", line1) -- Standard plugins were loaded, but not user config. - eq('health.vim', string.match(out, 'health.vim')) - eq(nil, string.match(out, 'init.vim')) + ok(string.find(out, 'man.lua') ~= nil) + ok(string.find(out, 'init.vim') == nil) end end) - it('-e sets ex mode', function() - local screen = Screen.new(25, 3) - clear('-e') - screen:attach() - -- Verify we set the proper mode both before and after :vi. - feed("put =mode(1)<CR>vi<CR>:put =mode(1)<CR>") - screen:expect([[ - cv | - ^n | - :put =mode(1) | - ]]) - - eq('cv\n', - funcs.system({nvim_prog, '-n', '-es' }, - { 'put =mode(1)', 'print', '' })) - end) - it('fails on --embed with -es/-Es/-l', function() matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', funcs.system({nvim_prog, '--embed', '-es' })) @@ -433,17 +449,6 @@ describe('startup', function() funcs.system({nvim_prog, '--embed', '-l', 'foo.lua' })) end) - it('does not crash if --embed is given twice', function() - clear{args={'--embed'}} - assert_alive() - end) - - it('does not crash when expanding cdpath during early_init', function() - clear{env={CDPATH='~doesnotexist'}} - assert_alive() - eq(',~doesnotexist', eval('&cdpath')) - end) - it('ENTER dismisses early message #7967', function() local screen screen = Screen.new(60, 6) @@ -469,22 +474,24 @@ describe('startup', function() ]]) end) - it("sets 'shortmess' when loading other tabs", function() - clear({args={'-p', 'a', 'b', 'c'}}) - local screen = Screen.new(25, 4) - screen:attach() - screen:expect({grid=[[ - {1: a }{2: b c }{3: }{2:X}| - ^ | - {4:~ }| - | - ]], - attr_ids={ - [1] = {bold = true}, - [2] = {background = Screen.colors.LightGrey, underline = true}, - [3] = {reverse = true}, - [4] = {bold = true, foreground = Screen.colors.Blue1}, - }}) + it('-r works without --headless in PTY #23294', function() + exec([[ + func Normalize(data) abort + " Windows: remove ^M and term escape sequences + return map(a:data, 'substitute(substitute(v:val, "\r", "", "g"), "\x1b\\%(\\]\\d\\+;.\\{-}\x07\\|\\[.\\{-}[\x40-\x7E]\\)", "", "g")') + endfunc + func OnOutput(id, data, event) dict + let g:stdout = Normalize(a:data) + endfunc + call jobstart([v:progpath, '-u', 'NONE', '-i', 'NONE', '-r'], { + \ 'pty': v:true, + \ 'stdout_buffered': v:true, + \ 'on_stdout': function('OnOutput'), + \ }) + ]]) + retry(nil, nil, function() + eq('Swap files found:', eval('g:stdout[0]')) + end) end) it('fixed hang issue with --headless (#11386)', function() @@ -512,7 +519,102 @@ describe('startup', function() '+q' }) eq('[\'+q\'] 1', out) end) +end) +describe('startup', function() + it('-e/-E interactive #7679', function() + clear('-e') + local screen = Screen.new(25, 3) + screen:attach() + feed("put ='from -e'<CR>") + screen:expect([[ + :put ='from -e' | + from -e | + :^ | + ]]) + + clear('-E') + screen = Screen.new(25, 3) + screen:attach() + feed("put ='from -E'<CR>") + screen:expect([[ + :put ='from -E' | + from -E | + :^ | + ]]) + end) + + it('-e sets ex mode', function() + local screen = Screen.new(25, 3) + clear('-e') + screen:attach() + -- Verify we set the proper mode both before and after :vi. + feed("put =mode(1)<CR>vi<CR>:put =mode(1)<CR>") + screen:expect([[ + cv | + ^n | + :put =mode(1) | + ]]) + + eq('cv\n', + funcs.system({nvim_prog, '-n', '-es' }, + { 'put =mode(1)', 'print', '' })) + end) + + it('-d does not diff non-arglist windows #13720 #21289', function() + write_file('Xdiff.vim', [[ + let bufnr = nvim_create_buf(0, 1) + let config = { + \ 'relative': 'editor', + \ 'focusable': v:false, + \ 'width': 1, + \ 'height': 1, + \ 'row': 3, + \ 'col': 3 + \ } + autocmd WinEnter * call nvim_open_win(bufnr, v:false, config)]]) + finally(function() + os.remove('Xdiff.vim') + end) + clear{args={'-u', 'Xdiff.vim', '-d', 'Xdiff.vim', 'Xdiff.vim'}} + eq(true, meths.get_option_value('diff', {win = funcs.win_getid(1)})) + eq(true, meths.get_option_value('diff', {win = funcs.win_getid(2)})) + local float_win = funcs.win_getid(3) + eq('editor', meths.win_get_config(float_win).relative) + eq(false, meths.get_option_value('diff', {win = float_win})) + end) + + it('does not crash if --embed is given twice', function() + clear{args={'--embed'}} + assert_alive() + end) + + it('does not crash when expanding cdpath during early_init', function() + clear{env={CDPATH='~doesnotexist'}} + assert_alive() + eq(',~doesnotexist', eval('&cdpath')) + end) + + it("sets 'shortmess' when loading other tabs", function() + clear({args={'-p', 'a', 'b', 'c'}}) + local screen = Screen.new(25, 4) + screen:attach() + screen:expect({grid=[[ + {1: a }{2: b c }{3: }{2:X}| + ^ | + {4:~ }| + | + ]], + attr_ids={ + [1] = {bold = true}, + [2] = {background = Screen.colors.LightGrey, underline = true}, + [3] = {reverse = true}, + [4] = {bold = true, foreground = Screen.colors.Blue1}, + }}) + end) +end) + +describe('startup', function() local function pack_clear(cmd) -- add packages after config dir in rtp but before config/after clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}, @@ -520,7 +622,6 @@ describe('startup', function() } end - it("handles &packpath during startup", function() pack_clear [[ let g:x = bar#test() @@ -585,7 +686,7 @@ describe('startup', function() ]] eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) - local rtp = meths.get_option'rtp' + local rtp = meths.get_option_value('rtp', {}) ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), 'startswith(…)', 'rtp='..rtp) end) @@ -627,13 +728,13 @@ describe('startup', function() end) it('window widths are correct when modelines set &columns with tabpages', function() - write_file('tab1.noft', 'vim: columns=81') - write_file('tab2.noft', 'vim: columns=81') + write_file('Xtab1.noft', 'vim: columns=81') + write_file('Xtab2.noft', 'vim: columns=81') finally(function() - os.remove('tab1.noft') - os.remove('tab2.noft') + os.remove('Xtab1.noft') + os.remove('Xtab2.noft') end) - clear({args = {'-p', 'tab1.noft', 'tab2.noft'}}) + clear({args = {'-p', 'Xtab1.noft', 'Xtab2.noft'}}) eq(81, meths.win_get_width(0)) command('tabnext') eq(81, meths.win_get_width(0)) @@ -691,7 +792,6 @@ describe('sysinit', function() eq('loaded 1 xdg 0 vim 1', eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))')) end) - end) describe('user config init', function() @@ -718,15 +818,16 @@ describe('user config init', function() end) it('loads init.lua from XDG config home by default', function() - clear{ args_rm={'-u' }, env=xenv } + clear{ args_rm={'-u'}, env=xenv } eq(1, eval('g:lua_rc')) eq(funcs.fnamemodify(init_lua_path, ':p'), eval('$MYVIMRC')) end) - describe('with existing .exrc in cwd', function() + describe('loads existing', function() local exrc_path = '.exrc' local xstate = 'Xstate' + local xstateenv = { XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata, XDG_STATE_HOME=xstate } local function setup_exrc_file(filename) exrc_path = filename @@ -756,10 +857,10 @@ describe('user config init', function() end) for _, filename in ipairs({ '.exrc', '.nvimrc', '.nvim.lua' }) do - it('loads ' .. filename, function () + it(filename .. ' in cwd', function() setup_exrc_file(filename) - clear{ args_rm = {'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_STATE_HOME=xstate } } + clear{ args_rm={'-u'}, env=xstateenv } -- The 'exrc' file is not trusted, and the prompt is skipped because there is no UI. eq('---', eval('g:exrc_file')) @@ -791,7 +892,7 @@ describe('user config init', function() -- TERMINAL -- | ]], filename, string.rep(' ', 50 - #filename))) - clear{ args_rm = {'-u'}, env={ XDG_CONFIG_HOME=xconfig, XDG_STATE_HOME=xstate } } + clear{ args_rm={'-u'}, env=xstateenv } -- The 'exrc' file is now trusted. eq(filename, eval('g:exrc_file')) end) @@ -824,7 +925,7 @@ describe('user config init', function() clear{ args_rm={'-u'}, env=xenv } feed('<cr><c-c>') -- Dismiss "Conflicting config …" message. eq(1, eval('g:lua_rc')) - matches('^E5422: Conflicting configs', meths.exec('messages', true)) + matches('^E5422: Conflicting configs', exec_capture('messages')) end) end) end) @@ -850,40 +951,41 @@ describe('runtime:', function() local plugin_folder_path = table.concat({xconfig, 'nvim', 'plugin'}, pathsep) local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) mkdir_p(plugin_folder_path) + finally(function() + rmdir(plugin_folder_path) + end) write_file(plugin_file_path, [[ vim.g.lua_plugin = 1 ]]) clear{ args_rm={'-u'}, env=xenv } eq(1, eval('g:lua_plugin')) - rmdir(plugin_folder_path) end) it('loads plugin/*.lua from start packages', function() - local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'category', - 'start', 'test_plugin'}, pathsep) + local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'category', 'start', 'test_plugin'}, pathsep) local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep) - local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, - pathsep) + local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) local profiler_file = 'test_startuptime.log' - mkdir_p(plugin_folder_path) + finally(function() + os.remove(profiler_file) + rmdir(plugin_path) + end) + write_file(plugin_file_path, [[vim.g.lua_plugin = 2]]) clear{ args_rm={'-u'}, args={'--startuptime', profiler_file}, env=xenv } eq(2, eval('g:lua_plugin')) - -- Check if plugin_file_path is listed in :scriptname - local scripts = meths.exec(':scriptnames', true) - assert(scripts:find(plugin_file_path)) + -- Check if plugin_file_path is listed in getscriptinfo() + local scripts = tbl_map(function(s) return s.name end, funcs.getscriptinfo()) + ok(#tbl_filter(function(s) return endswith(s, plugin_file_path) end, scripts) > 0) -- Check if plugin_file_path is listed in startup profile local profile_reader = io.open(profiler_file, 'r') local profile_log = profile_reader:read('*a') profile_reader:close() - assert(profile_log:find(plugin_file_path)) - - os.remove(profiler_file) - rmdir(plugin_path) + ok(profile_log:find(plugin_file_path) ~= nil) end) it('loads plugin/*.lua from site packages', function() @@ -893,30 +995,59 @@ describe('runtime:', function() local plugin_after_path = table.concat({plugin_path, 'after', 'plugin'}, pathsep) local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) local plugin_after_file_path = table.concat({plugin_after_path, 'helloo.lua'}, pathsep) - mkdir_p(plugin_folder_path) - write_file(plugin_file_path, [[table.insert(_G.lista, "unos")]]) mkdir_p(plugin_after_path) + finally(function() + rmdir(plugin_path) + end) + + write_file(plugin_file_path, [[table.insert(_G.lista, "unos")]]) write_file(plugin_after_file_path, [[table.insert(_G.lista, "dos")]]) clear{ args_rm={'-u'}, args={'--cmd', 'lua _G.lista = {}'}, env=xenv } eq({'unos', 'dos'}, exec_lua "return _G.lista") - - rmdir(plugin_path) end) + it('no crash setting &rtp in plugins with :packloadall called before #18315', function() + local plugin_folder_path = table.concat({xconfig, 'nvim', 'plugin'}, pathsep) + mkdir_p(plugin_folder_path) + finally(function() + rmdir(plugin_folder_path) + end) + + write_file(table.concat({plugin_folder_path, 'plugin.vim'}, pathsep), [[ + let &runtimepath = &runtimepath + let g:vim_plugin = 1 + ]]) + write_file(table.concat({plugin_folder_path, 'plugin.lua'}, pathsep), [[ + vim.o.runtimepath = vim.o.runtimepath + vim.g.lua_plugin = 1 + ]]) - it('loads ftdetect/*.lua', function() - local ftdetect_folder = table.concat({xconfig, 'nvim', 'ftdetect'}, pathsep) - local ftdetect_file = table.concat({ftdetect_folder , 'new-ft.lua'}, pathsep) - mkdir_p(ftdetect_folder) - write_file(ftdetect_file , [[vim.g.lua_ftdetect = 1]]) + clear{ args_rm={'-u'}, args = {'--cmd', 'packloadall'}, env=xenv } - clear{ args_rm={'-u'}, env=xenv } + eq(1, eval('g:vim_plugin')) + eq(1, eval('g:lua_plugin')) + end) - eq(1, eval('g:lua_ftdetect')) - rmdir(ftdetect_folder) + it("loads ftdetect/*.{vim,lua} respecting 'rtp' order", function() + local ftdetect_folder = table.concat({xconfig, 'nvim', 'ftdetect'}, pathsep) + local after_ftdetect_folder = table.concat({xconfig, 'nvim', 'after', 'ftdetect'}, pathsep) + mkdir_p(ftdetect_folder) + mkdir_p(after_ftdetect_folder) + finally(function() + rmdir(ftdetect_folder) + rmdir(after_ftdetect_folder) + end) + -- A .lua file is loaded after a .vim file if they only differ in extension. + -- All files in after/ftdetect/ are loaded after all files in ftdetect/. + write_file(table.concat({ftdetect_folder, 'new-ft.vim'}, pathsep), [[let g:seq ..= 'A']]) + write_file(table.concat({ftdetect_folder, 'new-ft.lua'}, pathsep), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(table.concat({after_ftdetect_folder, 'new-ft.vim'}, pathsep), [[let g:seq ..= 'a']]) + write_file(table.concat({after_ftdetect_folder, 'new-ft.lua'}, pathsep), [[vim.g.seq = vim.g.seq .. 'b']]) + clear{ args_rm={'-u'}, args = {'--cmd', 'let g:seq = ""'}, env=xenv } + eq('ABab', eval('g:seq')) end) end) |