diff options
Diffstat (limited to 'test')
42 files changed, 1087 insertions, 653 deletions
diff --git a/test/busted_runner.lua b/test/busted_runner.lua new file mode 100644 index 0000000000..b195ce3cc5 --- /dev/null +++ b/test/busted_runner.lua @@ -0,0 +1 @@ +require 'busted.runner'({ standalone = false }) diff --git a/test/cmakeconfig/paths.lua.in b/test/cmakeconfig/paths.lua.in index e3979981ba..a35dbe8901 100644 --- a/test/cmakeconfig/paths.lua.in +++ b/test/cmakeconfig/paths.lua.in @@ -6,8 +6,6 @@ for p in ("${TEST_INCLUDE_DIRS}" .. ";"):gmatch("[^;]+") do end module.test_build_dir = "${CMAKE_BINARY_DIR}" -module.test_include_path = module.test_build_dir .. "/test/includes/post" -module.test_libnvim_path = "${TEST_LIBNVIM_PATH}" module.test_source_path = "${CMAKE_SOURCE_DIR}" module.test_lua_prg = "${LUA_PRG}" module.test_luajit_prg = "" diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 541ac0ab63..7f044977cb 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -32,13 +32,16 @@ describe('API: highlight',function() italic = true, reverse = true, underline = true, - undercurl = true, - underdouble = true, - underdotted = true, - underdashed = true, strikethrough = true, + altfont = true, nocombine = true, } + local expected_undercurl = { + background = Screen.colors.Yellow, + foreground = Screen.colors.Red, + special = Screen.colors.Blue, + undercurl = true, + } before_each(function() clear() @@ -59,9 +62,13 @@ describe('API: highlight',function() eq('Invalid highlight id: 30000', string.match(emsg, 'Invalid.*')) -- Test all highlight properties. - command('hi NewHighlight gui=underline,bold,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough,nocombine') + command('hi NewHighlight gui=underline,bold,italic,reverse,strikethrough,altfont,nocombine') eq(expected_rgb2, nvim("get_hl_by_id", hl_id, true)) + -- Test undercurl + command('hi NewHighlight gui=undercurl') + eq(expected_undercurl, nvim("get_hl_by_id", hl_id, true)) + -- Test nil argument. err, emsg = pcall(meths.get_hl_by_id, { nil }, false) eq(false, err) @@ -207,17 +214,14 @@ describe("API: set highlight", function() bold = true, italic = true, reverse = true, - undercurl = true, - underline = true, underdashed = true, - underdotted = true, - underdouble = true, strikethrough = true, + altfont = true, cterm = { italic = true, reverse = true, - undercurl = true, strikethrough = true, + altfont = true, nocombine = true, } } @@ -227,20 +231,17 @@ describe("API: set highlight", function() bold = true, italic = true, reverse = true, - undercurl = true, - underline = true, underdashed = true, - underdotted = true, - underdouble = true, strikethrough = true, + altfont = true, } local highlight3_result_cterm = { background = highlight_color.ctermbg, foreground = highlight_color.ctermfg, italic = true, reverse = true, - undercurl = true, strikethrough = true, + altfont = true, nocombine = true, } @@ -296,7 +297,7 @@ describe("API: set highlight", function() exec_capture('highlight Test_hl')) meths.set_hl(0, 'Test_hl2', highlight3_config) - eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough,nocombine ctermfg=8 ctermbg=15 gui=bold,underline,undercurl,underdouble,underdotted,underdashed,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa', + eq('Test_hl2 xxx cterm=italic,reverse,strikethrough,altfont,nocombine ctermfg=8 ctermbg=15 gui=bold,underdashed,italic,reverse,strikethrough,altfont guifg=#ff0000 guibg=#0032aa', exec_capture('highlight Test_hl2')) -- Colors are stored with the name they are defined, but diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 833d54396b..53642858b2 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_log = helpers.assert_log local eq, clear, eval, command, nvim, next_msg = helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.nvim, helpers.next_msg @@ -9,6 +10,8 @@ local is_ci = helpers.is_ci local assert_alive = helpers.assert_alive local skip = helpers.skip +local testlog = 'Xtest-server-notify-log' + describe('notify', function() local channel @@ -17,6 +20,10 @@ describe('notify', function() channel = nvim('get_api_info')[1] end) + after_each(function() + os.remove(testlog) + end) + describe('passing a valid channel id', function() it('sends the notification/args to the corresponding channel', function() eval('rpcnotify('..channel..', "test-event", 1, 2, 3)') @@ -72,8 +79,12 @@ describe('notify', function() end) it('unsubscribe non-existing event #8745', function() + clear{env={ + NVIM_LOG_FILE=testlog, + }} nvim('subscribe', 'event1') nvim('unsubscribe', 'doesnotexist') + assert_log("tried to unsubscribe unknown event 'doesnotexist'", testlog, 10) nvim('unsubscribe', 'event1') assert_alive() end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index aa2f46bb59..8fcdd9620b 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -166,7 +166,7 @@ describe('API', function() echo nvim_exec('echo Avast_ye_hades(''ahoy!'')', 1) ]], true)) - eq('Vim(call):E5555: API call: Vim(echo):E121: Undefined variable: s:pirate', + matches('Vim%(echo%):E121: Undefined variable: s:pirate$', pcall_err(request, 'nvim_exec', [[ let s:pirate = 'script-scoped varrrrr' call nvim_exec('echo s:pirate', 1) @@ -208,12 +208,12 @@ describe('API', function() end) it('execution error', function() - eq('Vim:E492: Not an editor command: bogus_command', + eq('nvim_exec(): Vim:E492: Not an editor command: bogus_command', pcall_err(request, 'nvim_exec', 'bogus_command', false)) eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:exception')) - eq('Vim(buffer):E86: Buffer 23487 does not exist', + eq('nvim_exec(): Vim(buffer):E86: Buffer 23487 does not exist', pcall_err(request, 'nvim_exec', 'buffer 23487', false)) eq('', eval('v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:exception')) @@ -485,7 +485,7 @@ describe('API', function() throw 'wtf' endfunction ]]) - eq('wtf', pcall_err(request, 'nvim_call_function', 'Foo', {})) + eq('function Foo, line 1: wtf', pcall_err(request, 'nvim_call_function', 'Foo', {})) eq('', eval('v:exception')) eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) @@ -1807,9 +1807,11 @@ describe('API', function() }, ['jumps'] = eval(([[ - filter(map(getjumplist()[0], 'filter( - { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, - { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') + filter(map(add( + getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), + 'filter( + { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, + { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') ]]):gsub('\n', '')), ['bufs'] = eval([[ @@ -3211,6 +3213,17 @@ describe('API', function() 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight', { use_winbar = true, highlights = true })) end) + it('no memory leak with click functions', function() + meths.eval_statusline('%@ClickFunc@StatusLineStringWithClickFunc%T', {}) + eq({ + str = 'StatusLineStringWithClickFunc', + width = 29 + }, + meths.eval_statusline( + '%@ClickFunc@StatusLineStringWithClickFunc%T', + {}) + ) + end) end) end) describe('nvim_parse_cmd', function() diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index 90254b7415..fb5bab445c 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -9,6 +9,7 @@ local neq = helpers.neq local eval = helpers.eval local feed = helpers.feed local clear = helpers.clear +local matches = helpers.matches local meths = helpers.meths local pcall_err = helpers.pcall_err local funcs = helpers.funcs @@ -424,17 +425,50 @@ describe('autocmd', function() end) it('gives E814 when there are no other floating windows', function() - eq('Vim(close):E814: Cannot close window, only autocmd window would remain', + eq('BufAdd Autocommands for "Xa.txt": Vim(close):E814: Cannot close window, only autocmd window would remain', pcall_err(command, 'doautoall BufAdd')) end) it('gives E814 when there are other floating windows', function() meths.open_win(0, true, {width = 10, height = 10, relative = 'editor', row = 10, col = 10}) - eq('Vim(close):E814: Cannot close window, only autocmd window would remain', + eq('BufAdd Autocommands for "Xa.txt": Vim(close):E814: Cannot close window, only autocmd window would remain', pcall_err(command, 'doautoall BufAdd')) end) end) + it('closing `aucmd_win` using API gives E813', function() + exec_lua([[ + vim.cmd('tabnew') + _G.buf = vim.api.nvim_create_buf(true, true) + ]]) + matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[ + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.api.nvim_win_close(win, true) + end) + ]])) + matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[ + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.cmd('tabnext') + vim.api.nvim_win_close(win, true) + end) + ]])) + matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[ + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.api.nvim_win_hide(win) + end) + ]])) + matches('Vim:E813: Cannot close autocmd window$', pcall_err(exec_lua, [[ + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.cmd('tabnext') + vim.api.nvim_win_hide(win) + end) + ]])) + end) + it(':doautocmd does not warn "No matching autocommands" #10689', function() local screen = Screen.new(32, 3) screen:attach() @@ -476,14 +510,14 @@ describe('autocmd', function() it('during RecordingLeave event', function() command([[autocmd RecordingLeave * let v:event.regname = '']]) - eq('Vim(let):E46: Cannot change read-only variable "v:event.regname"', + eq('RecordingLeave Autocommands for "*": Vim(let):E46: Cannot change read-only variable "v:event.regname"', pcall_err(command, 'normal! qqq')) end) it('during TermClose event', function() command('autocmd TermClose * let v:event.status = 0') command('terminal') - eq('Vim(let):E46: Cannot change read-only variable "v:event.status"', + eq('TermClose Autocommands for "*": Vim(let):E46: Cannot change read-only variable "v:event.status"', pcall_err(command, 'bdelete!')) end) end) diff --git a/test/functional/autocmd/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua index 60c29170e2..82fb9b9444 100644 --- a/test/functional/autocmd/cmdline_spec.lua +++ b/test/functional/autocmd/cmdline_spec.lua @@ -73,7 +73,7 @@ describe('cmdline autocommands', function() {1:~ }| {4: }| : | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | + {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} | :^ | ]]) @@ -82,9 +82,9 @@ describe('cmdline autocommands', function() | {4: }| : | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | + {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} | :put ='lorem ipsum' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):very error} | + {2:CmdlineLeave Autocommands for "*": Vim(echoerr):very error} | | {3:Press ENTER or type command to continue}^ | ]]) @@ -111,9 +111,9 @@ describe('cmdline autocommands', function() lorem ipsum | {4: }| : | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | + {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} | :put ='lorem ipsum' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | + {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} | :put ='lorem ipsum'^ | ]]) @@ -123,9 +123,9 @@ describe('cmdline autocommands', function() lorem ipsum | {4: }| : | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | + {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} | :put ='lorem ipsum' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | + {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} | :put ='lorem ipsum^' | ]]) @@ -134,22 +134,22 @@ describe('cmdline autocommands', function() screen:expect([[ {4: }| : | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} | + {2:CmdlineEnter Autocommands for "*": Vim(echoerr):FAIL} | :put ='lorem ipsum' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | + {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} | :put ='lorem ipsum.' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | + {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} | :put ='lorem ipsum.^' | ]]) feed('<cr>') screen:expect([[ :put ='lorem ipsum' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | + {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} | :put ='lorem ipsum.' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):change erreor} | + {2:CmdlineChanged Autocommands for "*": Vim(echoerr):change erreor} | :put ='lorem ipsum.' | - {2:E5500: autocmd has thrown an exception: Vim(echoerr):very error} | + {2:CmdlineLeave Autocommands for "*": Vim(echoerr):very error} | | {3:Press ENTER or type command to continue}^ | ]]) diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index 4717b1fa2e..0a33f1b2ac 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -24,7 +24,7 @@ describe('autocmd TermClose', function() local function test_termclose_delete_own_buf() command('autocmd TermClose * bdelete!') command('terminal') - matches('^Vim%(bdelete%):E937: Attempt to delete a buffer that is in use: term://', + matches('^TermClose Autocommands for "%*": Vim%(bdelete%):E937: Attempt to delete a buffer that is in use: term://', pcall_err(command, 'bdelete!')) assert_alive() end diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 8cad7adfa6..05a69e1992 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -89,14 +89,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, 'Vim(cquit):E488: Trailing characters: 2: cquit 1 2') + test_cq('cquit 1 2', nil, 'nvim_exec(): 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, 'Vim(cquit):E488: Trailing characters: X: cquit X') + test_cq('cquit X', nil, 'nvim_exec(): 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, 'Vim(cquit):E488: Trailing characters: -1: cquit -1') + test_cq('cquit -1', nil, 'nvim_exec(): 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 ed37032f25..4e9891a4de 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -22,6 +22,7 @@ local tmpname = helpers.tmpname local trim = helpers.trim local currentdir = helpers.funcs.getcwd local assert_alive = helpers.assert_alive +local check_close = helpers.check_close local expect_exit = helpers.expect_exit local write_file = helpers.write_file local Screen = require('test.functional.ui.screen') @@ -34,7 +35,7 @@ describe('fileio', function() before_each(function() end) after_each(function() - expect_exit(command, ':qall!') + check_close() os.remove('Xtest_startup_shada') os.remove('Xtest_startup_file1') os.remove('Xtest_startup_file1~') @@ -270,7 +271,7 @@ describe('tmpdir', function() end) after_each(function() - expect_exit(command, ':qall!') + check_close() os.remove(testlog) end) @@ -294,9 +295,7 @@ describe('tmpdir', function() clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } }) matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). -- Assert that broken tmpdir root was handled. - retry(nil, 1000, function() - assert_log('tempdir root not a directory', testlog, 100) - end) + assert_log('tempdir root not a directory', testlog, 100) -- "…/nvim.<user>/" has wrong permissions: skip(is_os('win'), 'TODO(justinmk): need setfperm/getfperm on Windows. #8244') @@ -307,9 +306,7 @@ describe('tmpdir', function() clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } }) matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). -- Assert that broken tmpdir root was handled. - retry(nil, 1000, function() - assert_log('tempdir root has invalid permissions', testlog, 100) - end) + assert_log('tempdir root has invalid permissions', testlog, 100) end) it('too long', function() diff --git a/test/functional/core/log_spec.lua b/test/functional/core/log_spec.lua index 3b1ccd9559..f682df4155 100644 --- a/test/functional/core/log_spec.lua +++ b/test/functional/core/log_spec.lua @@ -6,7 +6,6 @@ local eq = helpers.eq local exec_lua = helpers.exec_lua local expect_exit = helpers.expect_exit local request = helpers.request -local retry = helpers.retry describe('log', function() local testlog = 'Xtest_logging' @@ -40,9 +39,7 @@ describe('log', function() }}) local tid = _G._nvim_test_id - retry(nil, 1000, function() - assert_log(tid..'%.%d+%.%d +server_init:%d+: test log message', testlog, 100) - end) + assert_log(tid..'%.%d+%.%d +server_init:%d+: test log message', testlog, 100) exec_lua([[ local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1', '+foochild', '+qa!' }, vim.empty_dict()) @@ -50,8 +47,6 @@ describe('log', function() ]]) -- Child Nvim spawned by jobstart() appends "/c" to parent name. - retry(nil, 1000, function() - assert_log('%.%d+%.%d/c +server_init:%d+: test log message', testlog, 100) - end) + assert_log('%.%d+%.%d/c +server_init:%d+: test log message', testlog, 100) end) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 9dabcd28b3..e9b47a0251 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -43,10 +43,8 @@ describe('startup', function() it('--startuptime', function() clear({ args = {'--startuptime', testfile}}) - retry(nil, 1000, function() - assert_log('sourcing', testfile, 100) - assert_log("require%('vim%._editor'%)", testfile, 100) - end) + assert_log('sourcing', testfile, 100) + assert_log("require%('vim%._editor'%)", testfile, 100) end) it('-D does not hang #12647', function() @@ -104,6 +102,13 @@ describe('startup', function() end) 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') + end + ]] -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ bufs: diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua index c503d7ebfb..22857efe5b 100644 --- a/test/functional/editor/completion_spec.lua +++ b/test/functional/editor/completion_spec.lua @@ -935,6 +935,9 @@ describe('completion', function() eq({'api'}, funcs.getcompletion('vim.ap', 'lua')) eq({'tbl_filter'}, funcs.getcompletion('vim.tbl_fil', 'lua')) eq({'vim'}, funcs.getcompletion('print(vi', 'lua')) + -- fuzzy completion is not supported, so the result should be the same + command('set wildoptions+=fuzzy') + eq({'vim'}, funcs.getcompletion('vi', 'lua')) end) end) diff --git a/test/functional/editor/mark_spec.lua b/test/functional/editor/mark_spec.lua index f300fea3a0..b3b190ef79 100644 --- a/test/functional/editor/mark_spec.lua +++ b/test/functional/editor/mark_spec.lua @@ -40,59 +40,59 @@ describe('named marks', function() it("errors when set out of range with :mark", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "1000mark x") - eq("Vim(mark):E16: Invalid range: 1000mark x", err) + eq("nvim_exec(): Vim(mark):E16: Invalid range: 1000mark x", err) end) it("errors when set out of range with :k", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "1000kx") - eq("Vim(k):E16: Invalid range: 1000kx", err) + eq("nvim_exec(): Vim(k):E16: Invalid range: 1000kx", err) end) it("errors on unknown mark name with :mark", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "mark #") - eq("Vim(mark):E191: Argument must be a letter or forward/backward quote", err) + eq("nvim_exec(): Vim(mark):E191: Argument must be a letter or forward/backward quote", err) end) it("errors on unknown mark name with '", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "normal! '#") - eq("Vim(normal):E78: Unknown mark", err) + eq("nvim_exec(): Vim(normal):E78: Unknown mark", err) end) it("errors on unknown mark name with `", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "normal! `#") - eq("Vim(normal):E78: Unknown mark", err) + eq("nvim_exec(): Vim(normal):E78: Unknown mark", err) end) it("errors when moving to a mark that is not set with '", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "normal! 'z") - eq("Vim(normal):E20: Mark not set", err) + eq("nvim_exec(): Vim(normal):E20: Mark not set", err) err = pcall_err(helpers.exec_capture, "normal! '.") - eq("Vim(normal):E20: Mark not set", err) + eq("nvim_exec(): Vim(normal):E20: Mark not set", err) end) it("errors when moving to a mark that is not set with `", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "normal! `z") - eq("Vim(normal):E20: Mark not set", err) + eq("nvim_exec(): Vim(normal):E20: Mark not set", err) err = pcall_err(helpers.exec_capture, "normal! `>") - eq("Vim(normal):E20: Mark not set", err) + eq("nvim_exec(): Vim(normal):E20: Mark not set", err) end) it("errors when moving to a global mark that is not set with '", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "normal! 'Z") - eq("Vim(normal):E20: Mark not set", err) + eq("nvim_exec(): Vim(normal):E20: Mark not set", err) end) it("errors when moving to a global mark that is not set with `", function() command("edit " .. file1) local err = pcall_err(helpers.exec_capture, "normal! `Z") - eq("Vim(normal):E20: Mark not set", err) + eq("nvim_exec(): Vim(normal):E20: Mark not set", err) end) it("can move to them using '", function() @@ -153,7 +153,7 @@ describe('named marks', function() command("next") command("bw! " .. file1 ) local err = pcall_err(helpers.exec_capture, "normal! 'A") - eq("Vim(normal):E92: Buffer 1 not found", err) + eq("nvim_exec(): Vim(normal):E92: Buffer 1 not found", err) os.remove(file1) end) diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index aa47198f7a..db0c8c0c3f 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -927,10 +927,13 @@ function tests.basic_formatting() } end --- Tests will be indexed by TEST_NAME +-- Tests will be indexed by test_name +local test_name = arg[1] +local timeout = arg[2] +assert(type(test_name) == 'string', 'test_name must be specified as first arg.') local kill_timer = vim.loop.new_timer() -kill_timer:start(_G.TIMEOUT or 1e3, 0, function() +kill_timer:start(timeout or 1e3, 0, function() kill_timer:stop() kill_timer:close() log('ERROR', 'LSP', 'TIMEOUT') @@ -938,14 +941,11 @@ kill_timer:start(_G.TIMEOUT or 1e3, 0, function() os.exit(100) end) -local test_name = _G.TEST_NAME -- lualint workaround -assert(type(test_name) == 'string', 'TEST_NAME must be specified.') local status, err = pcall(assert(tests[test_name], "Test not found")) kill_timer:stop() kill_timer:close() if not status then log('ERROR', 'LSP', tostring(err)) io.stderr:write(err) - os.exit(101) + vim.cmd [[101cquit]] end -os.exit(0) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index a14bedbbbd..6400db9f87 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,5 +1,4 @@ require('coxpcall') -local busted = require('busted') local luv = require('luv') local lfs = require('lfs') local mpack = require('mpack') @@ -397,9 +396,12 @@ local function remove_args(args, args_rm) return new_args end -function module.check_close(old_session) +function module.check_close() + if not session then + return + end local start_time = luv.now() - old_session:close() + session:close() luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). local end_time = luv.now() local delta = end_time - start_time @@ -408,12 +410,13 @@ function module.check_close(old_session) "This indicates a likely problem with the test even if it passed!\n") io.stdout:flush() end + session = nil end --- @param io_extra used for stdin_fd, see :help ui-option function module.spawn(argv, merge, env, keep, io_extra) - if session and not keep then - module.check_close(session) + if not keep then + module.check_close() end local child_stream = ChildProcessStream.spawn( @@ -430,28 +433,6 @@ function module.connect(file_or_address) return Session.new(stream) end --- Calls fn() until it succeeds, up to `max` times or until `max_ms` --- milliseconds have passed. -function module.retry(max, max_ms, fn) - assert(max == nil or max > 0) - assert(max_ms == nil or max_ms > 0) - local tries = 1 - local timeout = (max_ms and max_ms or 10000) - local start_time = luv.now() - while true do - local status, result = pcall(fn) - if status then - return result - end - luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). - if (max and tries >= max) or (luv.now() - start_time > timeout) then - busted.fail(string.format("retry() attempts: %d\n%s", tries, tostring(result)), 2) - end - tries = tries + 1 - luv.sleep(20) -- Avoid hot loop... - end -end - -- Starts a new global Nvim session. -- -- Parameters are interpreted as startup args, OR a map with these keys: diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua index c0ff3ed17a..75294b3786 100644 --- a/test/functional/legacy/055_list_and_dict_types_spec.lua +++ b/test/functional/legacy/055_list_and_dict_types_spec.lua @@ -330,214 +330,6 @@ describe('list and dictionary types', function() same list: 1]]) end) - it('locked variables (part 1)', function() - source([=[ - let l = [] - for depth in range(5) - $put ='depth is ' . depth - for u in range(3) - unlet l - let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] - exe "lockvar " . depth . " l" - if u == 1 - exe "unlockvar l" - elseif u == 2 - exe "unlockvar " . depth . " l" - endif - let ps = islocked("l") . islocked("l[1]") . islocked("l[1][1]") . - \ islocked("l[1][1][0]") . '-' . islocked("l[2]") . - \ islocked("l[2]['6']") . islocked("l[2]['6'][7]") - $put =ps - let ps = '' - try - let l[1][1][0] = 99 - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - let l[1][1] = [99] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - let l[1] = [99] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - let l[2]['6'][7] = 99 - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - let l[2][6] = {99: 99} - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - let l[2] = {99: 99} - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - let l = [99] - let ps .= 'p' - catch - let ps .= 'F' - endtry - $put =ps - endfor - endfor]=]) - expect([[ - - depth is 0 - 0000-000 - ppppppp - 0000-000 - ppppppp - 0000-000 - ppppppp - depth is 1 - 1000-000 - ppppppF - 0000-000 - ppppppp - 0000-000 - ppppppp - depth is 2 - 1100-100 - ppFppFF - 0000-000 - ppppppp - 0000-000 - ppppppp - depth is 3 - 1110-110 - pFFpFFF - 0010-010 - pFppFpp - 0000-000 - ppppppp - depth is 4 - 1111-111 - FFFFFFF - 0011-011 - FFpFFpp - 0000-000 - ppppppp]]) - end) - - -- TODO In the original test the 5th line of this source() call was used. - -- But now the test only passes if I comment it. - it('unletting locked variables', function() - source([=[ - let l = [] - for depth in range(5) - $put ='depth is ' . depth - for u in range(3) - "unlet l - let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] - exe "lockvar " . depth . " l" - if u == 1 - exe "unlockvar l" - elseif u == 2 - exe "unlockvar " . depth . " l" - endif - let ps = islocked("l") . islocked("l[1]") . islocked("l[1][1]") . - \ islocked("l[1][1][0]") . '-' . islocked("l[2]") . - \ islocked("l[2]['6']") . islocked("l[2]['6'][7]") - $put =ps - let ps = '' - try - unlet l[2]['6'][7] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - unlet l[2][6] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - unlet l[2] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - unlet l[1][1][0] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - unlet l[1][1] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - unlet l[1] - let ps .= 'p' - catch - let ps .= 'F' - endtry - try - unlet l - let ps .= 'p' - catch - let ps .= 'F' - endtry - $put =ps - endfor - endfor]=]) - expect([[ - - depth is 0 - 0000-000 - ppppppp - 0000-000 - ppppppp - 0000-000 - ppppppp - depth is 1 - 1000-000 - ppFppFp - 0000-000 - ppppppp - 0000-000 - ppppppp - depth is 2 - 1100-100 - pFFpFFp - 0000-000 - ppppppp - 0000-000 - ppppppp - depth is 3 - 1110-110 - FFFFFFp - 0010-010 - FppFppp - 0000-000 - ppppppp - depth is 4 - 1111-111 - FFFFFFp - 0011-011 - FppFppp - 0000-000 - ppppppp]]) - end) - it('locked variables and :unlet or list / dict functions', function() source([[ $put ='Locks and commands or functions:' @@ -676,30 +468,6 @@ describe('list and dictionary types', function() ['a', 'b', 3]]=]) end) - it('locked variables (part 2)', function() - feed_command( - 'let l = [1, 2, 3, 4]', - 'lockvar! l', - '$put =string(l)', - 'unlockvar l[1]', - 'unlet l[0:1]', - '$put =string(l)', - 'unlet l[1:2]', - '$put =string(l)', - 'unlockvar l[1]', - 'let l[0:1] = [0, 1]', - '$put =string(l)', - 'let l[1:2] = [0, 1]', - '$put =string(l)') - expect([=[ - - [1, 2, 3, 4] - [1, 2, 3, 4] - [1, 2, 3, 4] - [1, 2, 3, 4] - [1, 2, 3, 4]]=]) - end) - it(':lockvar/islocked() triggering script autoloading.', function() source([[ set rtp+=test/functional/fixtures diff --git a/test/functional/legacy/gf_spec.lua b/test/functional/legacy/gf_spec.lua index f1b1790ba1..9f725446be 100644 --- a/test/functional/legacy/gf_spec.lua +++ b/test/functional/legacy/gf_spec.lua @@ -10,6 +10,7 @@ describe('gf', function() it('is not allowed when buffer is locked', function() command('au OptionSet diff norm! gf') command([[call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])]]) - eq('Vim(normal):E788: Not allowed to edit another buffer now', pcall_err(command, 'diffthis')) + eq('OptionSet Autocommands for "diff": Vim(normal):E788: Not allowed to edit another buffer now', + pcall_err(command, 'diffthis')) end) end) diff --git a/test/functional/legacy/syn_attr_spec.lua b/test/functional/legacy/syn_attr_spec.lua index 06e8427e27..e6573da5d3 100644 --- a/test/functional/legacy/syn_attr_spec.lua +++ b/test/functional/legacy/syn_attr_spec.lua @@ -4,10 +4,10 @@ local command = helpers.command local eq = helpers.eq local eval = helpers.eval -before_each(clear) - -- oldtest: Test_missing_attr() -it('synIDattr() works', function() +describe('synIDattr()', function() + setup(clear) + local bool_attrs = { 'bold', 'italic', @@ -22,39 +22,55 @@ it('synIDattr() works', function() 'nocombine', } - command('hi Mine cterm=NONE gui=NONE') - eq('Mine', eval([[synIDattr(hlID("Mine"), "name")]])) - for _, mode in ipairs({'cterm', 'gui'}) do - eq('', eval(([[synIDattr("Mine"->hlID(), "bg", '%s')]]):format(mode))) - eq('', eval(([[synIDattr("Mine"->hlID(), "fg", '%s')]]):format(mode))) - eq('', eval(([[synIDattr("Mine"->hlID(), "sp", '%s')]]):format(mode))) - for _, attr in ipairs(bool_attrs) do - eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode))) - eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode))) - eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode))) + describe(':hi Mine cterm=NONE gui=NONE', function() + setup(function() + command(':hi Mine cterm=NONE gui=NONE') + end) + + it('"name"', function() + eq('Mine', eval([[synIDattr(hlID("Mine"), "name")]])) + end) + + local function none_test(attr, mode) + it(('"%s"'):format(attr), function() + eq('', eval(([[synIDattr(hlID("Mine"), "%s", '%s')]]):format(attr, mode))) + end) end - eq('', eval(([[synIDattr(hlID("Mine"), "inverse", '%s')]]):format(mode))) + + for _, mode in ipairs({'cterm', 'gui'}) do + describe(('"%s"'):format(mode), function() + for _, attr in ipairs(bool_attrs) do + none_test(attr, mode) + end + for _, attr in ipairs({'inverse', 'bg', 'fg', 'sp'}) do + none_test(attr, mode) + end + end) + end + end) + + local function attr_test(attr1, attr2) + local cmd = (':hi Mine cterm=%s gui=%s'):format(attr1, attr2) + it(cmd, function() + command(cmd) + eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr1))) + eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr2))) + eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr1))) + eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr2))) + end) end for i, attr1 in ipairs(bool_attrs) do local attr2 = bool_attrs[i - 1] or bool_attrs[#bool_attrs] - - command(('hi Mine cterm=%s gui=%s'):format(attr1, attr2)) - eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr1))) - eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'cterm')]]):format(attr2))) - eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr1))) - eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'gui')]]):format(attr2))) - - command(('hi Mine cterm=%s gui=%s'):format(attr2, attr1)) - eq('', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr1))) - eq('1', eval(([[synIDattr("Mine"->hlID(), "%s", 'cterm')]]):format(attr2))) - eq('1', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr1))) - eq('', eval(([[synIDattr(hlID("Mine"), "%s", 'gui')]]):format(attr2))) + attr_test(attr1, attr2) + attr_test(attr2, attr1) end - command('hi Mine cterm=reverse gui=inverse') - eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'cterm')]])) - eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'cterm')]])) - eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'gui')]])) - eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'gui')]])) + it(':hi Mine cterm=reverse gui=inverse', function() + command(':hi Mine cterm=reverse gui=inverse') + eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'cterm')]])) + eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'cterm')]])) + eq('1', eval([[synIDattr(hlID("Mine"), "reverse", 'gui')]])) + eq('1', eval([[synIDattr(hlID("Mine"), "inverse", 'gui')]])) + end) end) diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua index e9c34c9228..884ef3ef8e 100644 --- a/test/functional/lua/runtime_spec.lua +++ b/test/functional/lua/runtime_spec.lua @@ -4,6 +4,7 @@ local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval local exec = helpers.exec +local funcs = helpers.funcs local mkdir_p = helpers.mkdir_p local rmdir = helpers.rmdir local write_file = helpers.write_file @@ -17,6 +18,7 @@ describe('runtime:', function() io.open(init, 'w'):close() -- touch init file clear{args = {'-u', init}} exec('set rtp+=' .. plug_dir) + exec('set completeslash=slash') end) teardown(function() @@ -29,6 +31,7 @@ describe('runtime:', function() after_each(function() rmdir(plug_dir) + exec('bwipe!') end) describe('colors', function() @@ -39,6 +42,9 @@ describe('runtime:', function() mkdir_p(colorscheme_folder) write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]]) + eq({'new_colorscheme'}, funcs.getcompletion('new_c', 'color')) + eq({'colors/new_colorscheme.lua'}, funcs.getcompletion('colors/new_c', 'runtime')) + exec('colorscheme new_colorscheme') eq(1, eval('g:lua_colorscheme')) @@ -64,23 +70,26 @@ describe('runtime:', function() it('loads lua compilers', function() local compiler_file = compiler_folder .. sep .. 'new_compiler.lua' mkdir_p(compiler_folder) - write_file(compiler_file, [[vim.g.lua_compiler = 1]]) + write_file(compiler_file, [[vim.b.lua_compiler = 1]]) + + eq({'new_compiler'}, funcs.getcompletion('new_c', 'compiler')) + eq({'compiler/new_compiler.lua'}, funcs.getcompletion('compiler/new_c', 'runtime')) exec('compiler new_compiler') - eq(1, eval('g:lua_compiler')) + eq(1, eval('b:lua_compiler')) rmdir(compiler_folder) end) it('loads vim compilers when both lua and vim version exist', function() local compiler_file = compiler_folder .. sep .. 'new_compiler' mkdir_p(compiler_folder) - write_file(compiler_file..'.vim', [[let g:compiler = 'vim']]) - write_file(compiler_file..'.lua', [[vim.g.compiler = 'lua']]) + write_file(compiler_file..'.vim', [[let b:compiler = 'vim']]) + write_file(compiler_file..'.lua', [[vim.b.compiler = 'lua']]) exec('compiler new_compiler') - eq('vim', eval('g:compiler')) + eq('vim', eval('b:compiler')) rmdir(compiler_folder) end) end) @@ -91,10 +100,13 @@ describe('runtime:', function() it('loads lua ftplugins', function() local ftplugin_file = table.concat({ftplugin_folder , 'new-ft.lua'}, sep) mkdir_p(ftplugin_folder) - write_file(ftplugin_file , [[vim.g.lua_ftplugin = 1]]) + write_file(ftplugin_file , [[vim.b.lua_ftplugin = 1]]) + + eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype')) + eq({'ftplugin/new-ft.lua'}, funcs.getcompletion('ftplugin/new-f', 'runtime')) exec [[set filetype=new-ft]] - eq(1, eval('g:lua_ftplugin')) + eq(1, eval('b:lua_ftplugin')) rmdir(ftplugin_folder) end) end) @@ -105,10 +117,13 @@ describe('runtime:', function() it('loads lua indents', function() local indent_file = table.concat({indent_folder , 'new-ft.lua'}, sep) mkdir_p(indent_folder) - write_file(indent_file , [[vim.g.lua_indent = 1]]) + write_file(indent_file , [[vim.b.lua_indent = 1]]) + + eq({'new-ft'}, funcs.getcompletion('new-f', 'filetype')) + eq({'indent/new-ft.lua'}, funcs.getcompletion('indent/new-f', 'runtime')) exec [[set filetype=new-ft]] - eq(1, eval('g:lua_indent')) + eq(1, eval('b:lua_indent')) rmdir(indent_folder) end) end) @@ -116,24 +131,33 @@ describe('runtime:', function() describe('syntax', function() local syntax_folder = table.concat({plug_dir, 'syntax'}, sep) - it('loads lua syntaxes on filetype change', function() + before_each(function() local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep) mkdir_p(syntax_folder) - write_file(syntax_file , [[vim.g.lua_syntax = 1]]) + write_file(syntax_file , [[vim.b.current_syntax = 'my-lang']]) + exec([[let b:current_syntax = '']]) + end) + it('loads lua syntaxes on filetype change', function() exec('set filetype=my-lang') - eq(1, eval('g:lua_syntax')) - rmdir(syntax_folder) + eq('my-lang', eval('b:current_syntax')) end) it('loads lua syntaxes on syntax change', function() - local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep) - mkdir_p(syntax_folder) - write_file(syntax_file , [[vim.g.lua_syntax = 5]]) - exec('set syntax=my-lang') - eq(5, eval('g:lua_syntax')) - rmdir(syntax_folder) + eq('my-lang', eval('b:current_syntax')) + end) + + it('loads lua syntaxes for :ownsyntax', function() + exec('ownsyntax my-lang') + eq('my-lang', eval('w:current_syntax')) + eq('', eval('b:current_syntax')) + end) + + it('lua syntaxes are included in cmdline completion', function() + eq({'my-lang'}, funcs.getcompletion('my-l', 'filetype')) + eq({'my-lang'}, funcs.getcompletion('my-l', 'syntax')) + eq({'syntax/my-lang.lua'}, funcs.getcompletion('syntax/my-l', 'runtime')) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 90eccc49c8..867f366d06 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local nvim_prog = helpers.nvim_prog local funcs = helpers.funcs local meths = helpers.meths local command = helpers.command @@ -22,7 +23,6 @@ local remove_trace = helpers.remove_trace local mkdir_p = helpers.mkdir_p local rmdir = helpers.rmdir local write_file = helpers.write_file -local expect_exit = helpers.expect_exit local poke_eventloop = helpers.poke_eventloop local assert_alive = helpers.assert_alive @@ -763,6 +763,20 @@ describe('lua stdlib', function() pcall_err(exec_lua, code)) end) + it('vim.spairs', function() + local res = '' + local table = { + ccc=1, + bbb=2, + ddd=3, + aaa=4 + } + for key, _ in vim.spairs(table) do + res = res .. key + end + matches('aaabbbcccddd', res) + end) + it('vim.call, vim.fn', function() eq(true, exec_lua([[return vim.call('sin', 0.0) == 0.0 ]])) eq(true, exec_lua([[return vim.fn.sin(0.0) == 0.0 ]])) @@ -2910,9 +2924,14 @@ describe('lua: builtin modules', function() end) - it('does not work when disabled without runtime', function() - clear{args={'--luamod-dev'}, env={VIMRUNTIME='fixtures/a'}} - expect_exit(exec_lua, [[return vim.tbl_count {x=1,y=2}]]) + it('fails when disabled without runtime', function() + clear() + command("let $VIMRUNTIME='fixtures/a'") + -- Use system([nvim,…]) instead of clear() to avoid stderr noise. #21844 + local out = funcs.system({nvim_prog, '--clean', '--luamod-dev', + [[+call nvim_exec_lua('return vim.tbl_count {x=1,y=2}')]], '+qa!'}):gsub('\r\n', '\n') + eq(1, eval('v:shell_error')) + matches("'vim%.shared' not found", out) end) end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index c0d820f40e..84ec43f4cb 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local assert_alive = helpers.assert_alive +local assert_log = helpers.assert_log local meths = helpers.meths local command = helpers.command local clear = helpers.clear @@ -20,6 +21,8 @@ local tbl_contains = helpers.tbl_contains local expect_exit = helpers.expect_exit local is_os = helpers.is_os +local testlog = 'Xtest-defaults-log' + describe('startup defaults', function() describe(':filetype', function() local function expect_filetype(expected) @@ -275,6 +278,10 @@ describe('XDG defaults', function() -- Need separate describe() blocks to not run clear() twice. -- Do not put before_each() here for the same reasons. + after_each(function() + os.remove(testlog) + end) + it("&runtimepath data-dir matches stdpath('data') #9910", function() clear() local rtp = eval('split(&runtimepath, ",")') @@ -337,6 +344,7 @@ describe('XDG defaults', function() clear({ args_rm={'runtimepath'}, env={ + NVIM_LOG_FILE=testlog, XDG_CONFIG_HOME=(root_path .. ('/x'):rep(4096)), XDG_CONFIG_DIRS=(root_path .. ('/a'):rep(2048) .. env_sep.. root_path .. ('/b'):rep(2048) @@ -351,6 +359,10 @@ describe('XDG defaults', function() end) it('are correctly set', function() + if not is_os('win') then + assert_log('Failed to start server: no such file or directory: /X/X/X', testlog, 10) + end + local vimruntime, libdir = vimruntime_and_libdir() eq(((root_path .. ('/x'):rep(4096) .. '/nvim' @@ -412,6 +424,7 @@ describe('XDG defaults', function() clear({ args_rm={'runtimepath'}, env={ + NVIM_LOG_FILE=testlog, XDG_CONFIG_HOME='$XDG_DATA_HOME', XDG_CONFIG_DIRS='$XDG_DATA_DIRS', XDG_DATA_HOME='$XDG_CONFIG_HOME', @@ -422,6 +435,10 @@ describe('XDG defaults', function() end) it('are not expanded', function() + if not is_os('win') then + assert_log('Failed to start server: no such file or directory: %$XDG_RUNTIME_DIR%/', testlog, 10) + end + local vimruntime, libdir = vimruntime_and_libdir() eq((('$XDG_DATA_HOME/nvim' .. ',$XDG_DATA_DIRS/nvim' diff --git a/test/functional/plugin/lsp/helpers.lua b/test/functional/plugin/lsp/helpers.lua index 028ccb9e2c..caab174b4d 100644 --- a/test/functional/plugin/lsp/helpers.lua +++ b/test/functional/plugin/lsp/helpers.lua @@ -80,17 +80,14 @@ M.fake_lsp_logfile = 'Xtest-fake-lsp.log' local function fake_lsp_server_setup(test_name, timeout_ms, options, settings) exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename, logfile, timeout, options, settings = ... + local test_name, fake_lsp_code, fake_lsp_logfile, timeout, options, settings = ... TEST_RPC_CLIENT_ID = lsp.start_client { cmd_env = { - NVIM_LOG_FILE = logfile; + NVIM_LOG_FILE = fake_lsp_logfile; NVIM_LUA_NOTRACK = "1"; }; cmd = { - vim.v.progpath, '-Es', '-u', 'NONE', '--headless', - "-c", string.format("lua TEST_NAME = %q", test_name), - "-c", string.format("lua TIMEOUT = %d", timeout), - "-c", "luafile "..fixture_filename, + vim.v.progpath, '-l', fake_lsp_code, test_name, tostring(timeout), }; handlers = setmetatable({}, { __index = function(t, method) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 93fada8a50..fd162961ff 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -46,16 +46,14 @@ describe('LSP', function() local test_name = "basic_init" exec_lua([=[ lsp = require('vim.lsp') - local test_name, fixture_filename, logfile = ... + local test_name, fake_lsp_code, fake_lsp_logfile = ... function test__start_client() return lsp.start_client { cmd_env = { - NVIM_LOG_FILE = logfile; + NVIM_LOG_FILE = fake_lsp_logfile; }; cmd = { - vim.v.progpath, '-Es', '-u', 'NONE', '--headless', - "-c", string.format("lua TEST_NAME = %q", test_name), - "-c", "luafile "..fixture_filename; + vim.v.progpath, '-l', fake_lsp_code, test_name; }; workspace_folders = {{ uri = 'file://' .. vim.loop.cwd(), @@ -3432,6 +3430,38 @@ describe('LSP', function() } eq(expected_range, result[3].params.range) end) + it('Aborts with notify if no clients support requested method', function() + exec_lua(create_server_definition) + exec_lua([[ + vim.notify = function(msg, _) + notify_msg = msg + end + ]]) + local fail_msg = "[LSP] Format request failed, no matching language servers." + local function check_notify(name, formatting, range_formatting) + local timeout_msg = "[LSP][" .. name .. "] timeout" + exec_lua([[ + local formatting, range_formatting, name = ... + local server = _create_server({ capabilities = { + documentFormattingProvider = formatting, + documentRangeFormattingProvider = range_formatting, + }}) + vim.lsp.start({ name = name, cmd = server.cmd }) + notify_msg = nil + vim.lsp.buf.format({ name = name, timeout_ms = 1 }) + ]], formatting, range_formatting, name) + eq(formatting and timeout_msg or fail_msg, exec_lua('return notify_msg')) + exec_lua([[ + notify_msg = nil + vim.lsp.buf.format({ name = name, timeout_ms = 1, range = {start={1, 0}, ['end']={1, 0}}}) + ]]) + eq(range_formatting and timeout_msg or fail_msg, exec_lua('return notify_msg')) + end + check_notify("none", false, false) + check_notify("formatting", true, false) + check_notify("rangeFormatting", false, true) + check_notify("both", true, true) + end) end) describe('cmd', function() it('can connect to lsp server via rpc.connect', function() diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index c6c7d2b03d..58da059be6 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -59,7 +59,7 @@ describe(':Man', function() screen:expect([[ ^this {b:is} {b:a} test | - with {u:overstruck} text | + with {i:overstruck} text | {eob:~ }| {eob:~ }| | @@ -98,7 +98,7 @@ describe(':Man', function() screen:expect([[ ^this {b:is} {b:あ} test | - with {u:överstrũck} te{i:xt¶} | + with {i:överstrũck} te{i:xt¶} | {eob:~ }| {eob:~ }| | @@ -115,7 +115,7 @@ describe(':Man', function() screen:expect([[ {b:^_begins} | {b:mid_dle} | - {u:mid_dle} | + {i:mid_dle} | {eob:~ }| | ]]) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 228aaa6b23..1d9e7b8e11 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -7,7 +7,6 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive local eq = helpers.eq local feed_command = helpers.feed_command local feed_data = thelpers.feed_data @@ -15,6 +14,7 @@ local clear = helpers.clear local command = helpers.command local dedent = helpers.dedent local exec = helpers.exec +local exec_lua = helpers.exec_lua local testprg = helpers.testprg local retry = helpers.retry local nvim_prog = helpers.nvim_prog @@ -77,7 +77,16 @@ describe('TUI', function() it('rapid resize #7572 #7628', function() -- Need buffer rows to provoke the behavior. - feed_data(":edit test/functional/fixtures/bigfile.txt:") + feed_data(":edit test/functional/fixtures/bigfile.txt\n") + screen:expect([[ + {1:0}000;<control>;Cc;0;BN;;;;;N;NULL;;;; | + 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | + 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | + 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + {5:test/functional/fixtures/bigfile.txt }| + :edit test/functional/fixtures/bigfile.txt | + {3:-- TERMINAL --} | + ]]) command('call jobresize(b:terminal_job_id, 58, 9)') command('call jobresize(b:terminal_job_id, 62, 13)') command('call jobresize(b:terminal_job_id, 100, 42)') @@ -94,7 +103,9 @@ describe('TUI', function() command('call jobresize(b:terminal_job_id, 1, 4)') screen:try_resize(57, 17) command('call jobresize(b:terminal_job_id, 57, 17)') - assert_alive() + retry(nil, nil, function() + eq({true, 57}, {child_session:request('nvim_win_get_width', 0)}) + end) end) it('accepts resize while pager is active', function() @@ -1298,6 +1309,7 @@ describe('TUI', function() [7] = {reverse = true, foreground = Screen.colors.SeaGreen4}, [8] = {foreground = Screen.colors.SeaGreen4}, [9] = {bold = true, foreground = Screen.colors.Blue1}, + [10] = {foreground = Screen.colors.Blue}, }) feed_data(':hi SpecialKey ctermfg=3 guifg=SeaGreen\n') @@ -1318,9 +1330,9 @@ describe('TUI', function() feed_data(':set termguicolors\n') screen:expect([[ {7:^}{8:G} | - {9:~ }| - {9:~ }| - {9:~ }| + {9:~}{10: }| + {9:~}{10: }| + {9:~}{10: }| {3:[No Name] [+] }| :set termguicolors | {4:-- TERMINAL --} | @@ -1495,6 +1507,11 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]} end) + + it('no assert failure on deadly signal #21896', function() + exec_lua([[vim.loop.kill(vim.fn.jobpid(vim.bo.channel), 'sigterm')]]) + screen:expect({any = '%[Process exited 1%]'}) + end) end) describe('TUI', function() @@ -2399,9 +2416,7 @@ describe("TUI as a client", function() clear() local screen = thelpers.screen_setup(0, string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "127.0.0.1:2436546", "--remote-ui"]]=], - nvim_prog)) - - screen:try_resize(60, 7) + nvim_prog), 60) screen:expect([[ Remote ui failed to start: {MATCH:.*}| diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 1db8381a93..df45c9b384 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -17,7 +17,7 @@ describe('treesitter language API', function() pcall_err(exec_lua, "parser = vim.treesitter.get_parser(0, 'borklang')")) -- actual message depends on platform - matches("Failed to load parser: uv_dlopen: .+", + matches("Failed to load parser for language 'borklang': uv_dlopen: .+", pcall_err(exec_lua, "parser = vim.treesitter.require_language('borklang', 'borkbork.so')")) -- Should not throw an error when silent @@ -31,6 +31,11 @@ describe('treesitter language API', function() pcall_err(exec_lua, 'vim.treesitter.require_language("c", nil, false, "borklang")')) end) + it('shows error for invalid language name', function() + eq(".../language.lua:0: '/foo/' is not a valid language name", + pcall_err(exec_lua, 'vim.treesitter.require_language("/foo/", nil, false)')) + end) + it('inspects language', function() local keys, fields, symbols = unpack(exec_lua([[ local lang = vim.treesitter.inspect_language('c') diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 33e375760e..eb5de693bd 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -335,17 +335,17 @@ describe('Command-line coloring', function() :echo "«^ | ]]) end) - it('does the right thing when errorring', function() + it('does the right thing when erroring', function() set_color_cb('Echoerring') start_prompt('e') screen:expect([[ | {EOB:~ }| - {EOB:~ }| {MSEP: }| : | {ERR:E5407: Callback has thrown an exception:}| - {ERR: Vim(echoerr):HERE} | + {ERR: function DoPrompt[3]..Echoerring, line }| + {ERR:1: Vim(echoerr):HERE} | :e^ | ]]) end) @@ -400,10 +400,10 @@ describe('Command-line coloring', function() screen:expect([[ | {EOB:~ }| - {EOB:~ }| {MSEP: }| : | {ERR:E5407: Callback has thrown an exception:}| + {ERR: function DoPrompt[3]..Throwing, line 1:}| {ERR: ABC} | :e^ | ]]) diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index bfa7167100..46a478c1ea 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -8,6 +8,7 @@ local expect = helpers.expect local funcs = helpers.funcs local meths = helpers.meths local exec = helpers.exec +local exec_lua = helpers.exec_lua local assert_alive = helpers.assert_alive @@ -1852,6 +1853,128 @@ describe("folded lines", function() ]]) end end) + + it('fold attached virtual lines are drawn correctly #21837', function() + funcs.setline(1, 'line 1') + funcs.setline(2, 'line 2') + funcs.setline(3, 'line 3') + funcs.setline(4, 'line 4') + feed("zfj") + exec_lua([[ + local ns = vim.api.nvim_create_namespace("ns") + vim.api.nvim_buf_set_extmark(0, ns, 0, 0, { virt_lines_above = true, virt_lines = {{{"virt_line above line 1", ""}}} }) + vim.api.nvim_buf_set_extmark(0, ns, 1, 0, { virt_lines = {{{"virt_line below line 2", ""}}} }) + vim.api.nvim_buf_set_extmark(0, ns, 2, 0, { virt_lines_above = true, virt_lines = {{{"virt_line above line 3", ""}}} }) + vim.api.nvim_buf_set_extmark(0, ns, 3, 0, { virt_lines = {{{"virt_line below line 4", ""}}} }) + ]]) + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {5:^+-- 2 lines: line 1·························}| + virt_line above line 3 | + line 3 | + line 4 | + virt_line below line 4 | + {1:~ }| + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {5:^+-- 2 lines: line 1·························}| + virt_line above line 3 | + line 3 | + line 4 | + virt_line below line 4 | + {1:~ }| + {1:~ }| + | + ]]) + end + + feed('jzfj') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {5:+-- 2 lines: line 1·························}| + {5:^+-- 2 lines: line 3·························}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {5:+-- 2 lines: line 1·························}| + {5:^+-- 2 lines: line 3·························}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end + + feed('kzo<C-Y>') + funcs.setline(5, 'line 5') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + virt_line above line 1 | + ^line 1 | + line 2 | + virt_line below line 2 | + {5:+-- 2 lines: line 3·························}| + line 5 | + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + virt_line above line 1 | + ^line 1 | + line 2 | + virt_line below line 2 | + {5:+-- 2 lines: line 3·························}| + line 5 | + {1:~ }| + | + ]]) + end + end) end describe("with ext_multigrid", function() diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 78b2f52c1f..c681453294 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -945,6 +945,82 @@ describe('ui/ext_popupmenu', function() }} end) + + it('does not interfere with mousemodel=popup', function() + exec([[ + set mouse=a mousemodel=popup + + aunmenu PopUp + menu PopUp.foo :let g:menustr = 'foo'<CR> + menu PopUp.bar :let g:menustr = 'bar'<CR> + menu PopUp.baz :let g:menustr = 'baz'<CR> + ]]) + feed('o<C-r>=TestComplete()<CR>') + screen:expect{grid=[[ + | + foo^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=0, + anchor={1,1,0}, + }} + + feed('<c-p>') + screen:expect{grid=[[ + | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- INSERT --} | + ]], popupmenu={ + items=expected, + pos=-1, + anchor={1,1,0}, + }} + + feed('<esc>') + screen:expect{grid=[[ + | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + feed('<RightMouse><0,0>') + screen:expect([[ + | + {7:^foo } | + {7:bar }{1: }| + {7:baz }{1: }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + feed('<esc>') + screen:expect([[ + | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end) end) @@ -968,6 +1044,7 @@ describe('builtin popupmenu', function() [5] = {bold = true, foreground = Screen.colors.SeaGreen}, [6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [7] = {background = Screen.colors.Yellow}, -- Search + [8] = {foreground = Screen.colors.Red}, }) end) @@ -1115,6 +1192,66 @@ describe('builtin popupmenu', function() ]]) end) + -- oldtest: Test_pum_with_preview_win() + it('preview window opened during completion', function() + exec([[ + funct Omni_test(findstart, base) + if a:findstart + return col(".") - 1 + endif + return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three", info: "3info"}] + endfunc + set omnifunc=Omni_test + set completeopt+=longest + ]]) + feed('Gi<C-X><C-O>') + screen:expect([[ + ^ | + {n:one }{1: }| + {n:two }{1: }| + {n:three }{1: }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:-- }{8:Back at original} | + ]]) + feed('<C-N>') + screen:expect([[ + 1info | + | + {1:~ }| + {3:[Scratch] [Preview] }| + one^ | + {s:one }{1: }| + {n:two }{1: }| + {n:three }{1: }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {4:[No Name] [+] }| + {2:-- }{5:match 1 of 3} | + ]]) + end) + it('with vsplits', function() insert('aaa aab aac\n') feed(':vsplit<cr>') @@ -2388,8 +2525,130 @@ describe('builtin popupmenu', function() {1:~ }{n: xyz }{1: }| :e あいう/123^ | ]]) + feed('<Esc>') - feed('<esc>') + -- Pressing <PageDown> should scroll the menu downward + feed(':sign <Tab><PageDown>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{s: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign undefine^ | + ]]) + feed('<PageDown>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{s: unplace }{1: }| + :sign unplace^ | + ]]) + feed('<PageDown>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign ^ | + ]]) + feed('<PageDown>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{s: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign define^ | + ]]) + feed('<C-U>sign <Tab><Right><Right><PageDown>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{s: unplace }{1: }| + :sign unplace^ | + ]]) + + -- Pressing <PageUp> should scroll the menu upward + feed('<C-U>sign <Tab><PageUp>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign ^ | + ]]) + feed('<PageUp>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{s: unplace }{1: }| + :sign unplace^ | + ]]) + feed('<PageUp>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{n: define }{1: }| + {1:~ }{s: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign jump^ | + ]]) + feed('<PageUp>') + screen:expect([[ + | + {1:~ }| + {1:~ }| + {1:~ }{s: define }{1: }| + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign define^ | + ]]) + + feed('<Esc>') -- check positioning with multibyte char in pattern command("e långfile1") @@ -2583,6 +2842,26 @@ describe('builtin popupmenu', function() ]]} end) + it('wildoptions=pum with a wrapped line in buffer vim-patch:8.2.4655', function() + screen:try_resize(32, 10) + meths.buf_set_lines(0, 0, -1, true, { ('a'):rep(100) }) + command('set wildoptions+=pum') + feed('$') + feed(':sign <Tab>') + screen:expect([[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + aaaa {s: define } | + {1:~ }{n: jump }{1: }| + {1:~ }{n: list }{1: }| + {1:~ }{n: place }{1: }| + {1:~ }{n: undefine }{1: }| + {1:~ }{n: unplace }{1: }| + :sign define^ | + ]]) + end) + -- oldtest: Test_wildmenu_pum_clear_entries() it('wildoptions=pum when using Ctrl-E as wildchar vim-patch:9.0.1030', function() screen:try_resize(30, 10) diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index a9d796c10b..3233e6cd19 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -4,6 +4,8 @@ local clear = helpers.clear local command = helpers.command local eq = helpers.eq local eval = helpers.eval +local exec_lua = helpers.exec_lua +local feed = helpers.feed local meths = helpers.meths local pcall_err = helpers.pcall_err @@ -15,8 +17,8 @@ describe('statuscolumn', function() screen:attach() end) - it('fails with invalid \'statuscolumn\'', function() - command('set stc=%{v:relnum?v:relnum:(v:lnum==5?invalid:v:lnum)}\\ ') + it("fails with invalid 'statuscolumn'", function() + command([[set stc=%{v:relnum?v:relnum:(v:lnum==5?invalid:v:lnum)}\ ]]) screen:expect([[ 4 aaaaa | 3 aaaaa | @@ -38,8 +40,8 @@ describe('statuscolumn', function() eq('', eval('&statuscolumn')) end) - it('widens with irregular \'statuscolumn\' width', function() - command('set stc=%{v:relnum?v:relnum:(v:lnum==5?\'bbbbb\':v:lnum)}') + it("widens with irregular 'statuscolumn' width", function() + command([[set stc=%{v:relnum?v:relnum:(v:lnum==5?'bbbbb':v:lnum)}]]) command('norm 5G | redraw!') screen:expect([[ 1 aaaaa | @@ -59,7 +61,7 @@ describe('statuscolumn', function() ]]) end) - it('works with \'statuscolumn\'', function() + it("works with 'number' and 'relativenumber'", function() command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) screen:expect([[ 4 │aaaaa | @@ -122,7 +124,7 @@ describe('statuscolumn', function() command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) end) - it('works with highlighted \'statuscolumn\'', function() + it("works with highlighted 'statuscolumn'", function() command([[set stc=%#NonText#%{&nu?v:lnum:''}]] .. [[%=%{&rnu&&(v:lnum%2)?'\ '.v:relnum:''}]] .. [[%#LineNr#%{&rnu&&!(v:lnum%2)?'\ '.v:relnum:''}│]]) @@ -183,7 +185,7 @@ describe('statuscolumn', function() end) it('works with wrapped lines, signs and folds', function() - command("set stc=%C%s%=%{v:wrap?'':v:lnum}│\\ ") + command([[set stc=%C%s%=%{v:virtnum?'':v:lnum}│\ ]]) command("call setline(1,repeat([repeat('aaaaa',10)],16))") screen:set_default_attr_ids({ [0] = {bold = true, foreground = Screen.colors.Blue}, @@ -209,6 +211,8 @@ describe('statuscolumn', function() {1:10│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{0:@@@}| | ]]) + command("set stc=%C%s%=%l│\\ ") + screen:expect_unchanged() command('set signcolumn=auto:2 foldcolumn=auto') command('sign define piet1 text=>> texthl=LineNr') command('sign define piet2 text=>! texthl=NonText') @@ -234,7 +238,7 @@ describe('statuscolumn', function() ]]) command('norm zf$') -- Check that alignment works properly with signs after %= - command("set stc=%C%=%{v:wrap?'':v:lnum}│%s\\ ") + command([[set stc=%C%=%{v:virtnum?'':v:lnum}│%s\ ]]) screen:expect([[ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | @@ -269,7 +273,7 @@ describe('statuscolumn', function() | ]]) -- v:lnum is the same value on wrapped lines - command("set stc=%C%=%{v:lnum}│%s\\ ") + command([[set stc=%C%=%{v:lnum}│%s\ ]]) screen:expect([[ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 4│}{2: }{1: }aaaaaa | @@ -287,7 +291,7 @@ describe('statuscolumn', function() | ]]) -- v:relnum is the same value on wrapped lines - command("set stc=%C%=\\ %{v:relnum}│%s\\ ") + command([[set stc=%C%=\ %{v:relnum}│%s\ ]]) screen:expect([[ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 4│}{2: }{1: }aaaaaa | @@ -304,7 +308,7 @@ describe('statuscolumn', function() {2: }{1: 2│}{2: }{1: }aaaaaa | | ]]) - command("set stc=%C%=\\ %{v:wrap?'':v:relnum}│%s\\ ") + command([[set stc=%C%=\ %{v:virtnum?'':v:relnum}│%s\ ]]) screen:expect([[ {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | @@ -346,9 +350,34 @@ describe('statuscolumn', function() {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa | | ]]) + -- Status column is re-evaluated for virt_lines, buffer line, and wrapped line + exec_lua([[ + local ns = vim.api.nvim_create_namespace("ns") + vim.api.nvim_buf_set_extmark(0, ns, 5, 0, { + virt_lines_above = true, virt_lines = {{{"virt_line above", ""}}} }) + vim.api.nvim_buf_set_extmark(0, ns, 4, 0, { virt_lines = {{{"virt_line", ""}}} }) + ]]) + command('set foldcolumn=0 signcolumn=no') + command([[set stc=%{v:virtnum<0?'virtual':(!v:virtnum?'buffer':'wrapped')}%=%{'\ '.v:virtnum.'\ '.v:lnum}]]) + screen:expect([[ + {1:buffer 0 4}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:wrapped 1 4}aaaaaaaa | + {1:buffer 0 5}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:wrapped 1 5}aaaaaaaa | + {1:virtual-2 5}virt_line | + {1:virtual-2 5}virt_line above | + {1:buffer 0 6}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:wrapped 1 6}aaaaaaaa | + {1:buffer 0 7}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:wrapped 1 7}aaaaaaaa | + {4:buffer 0 8}{5:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {1:buffer 0 9}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:wrapped 1 9}aaaaaaaa | + | + ]]) end) - it('works with \'statuscolumn\' clicks', function() + it("works with 'statuscolumn' clicks", function() command('set mousemodel=extend') command([[ function! MyClickFunc(minwid, clicks, button, mods) @@ -385,25 +414,68 @@ describe('statuscolumn', function() eq('', eval("g:testvar")) end) - it('fits maximum multibyte foldcolumn #21759', function() - command('set stc=%C fdc=9 fillchars=foldsep:𒀀') + it('click labels do not leak memory', function() + command([[ + set laststatus=2 + setlocal statuscolumn=%0@MyClickFunc@abcd%T + 4vsplit + setlocal statusline=abcd + redrawstatus + setlocal statusline= + only + redraw + ]]) + end) + + it('works with foldcolumn', function() + -- Fits maximum multibyte foldcolumn #21759 + command([[set stc=%C%=%l\ fdc=9 fillchars=foldsep:𒀀]]) for _ = 0,8 do command('norm zfjzo') end + -- 'statuscolumn' is not drawn for `virt_lines_leftcol` lines + exec_lua([[ + local ns = vim.api.nvim_create_namespace("ns") + vim.api.nvim_buf_set_extmark(0, ns, 6, 0, { + virt_lines_leftcol = true, virt_lines = {{{"virt", ""}}} }) + vim.api.nvim_buf_set_extmark(0, ns, 7, 0, { + virt_lines_leftcol = true, virt_lines = {{{"virt", ""}}} }) + ]]) + feed('lh') -- force update cursor row screen:expect([[ - aaaaa | - aaaaa | - aaaaa | - aaaaa | - --------- ^aaaaa | - 𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀 aaaaa | - aaaaa | - aaaaa | - aaaaa | - aaaaa | - aaaaa | - aaaaa | - aaaaa | + 4 aaaaa | + 5 aaaaa | + 6 aaaaa | + 7 aaaaa | + virt | + --------- 8 ^aaaaa | + virt | + 𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀 9 aaaaa | + 10 aaaaa | + 11 aaaaa | + 12 aaaaa | + 13 aaaaa | + 14 aaaaa | + | + ]]) + command('set stc=') -- also for the default sign column + screen:expect_unchanged() + -- 'statuscolumn' is not too wide with custom (bogus) fold column + command([[set stc=%{foldlevel(v:lnum)>0?repeat('-',foldlevel(v:lnum)):''}%=%l\ ]]) + feed('Gd10Ggg<C-l>') + screen:expect([[ + 1 ^aaaaa | + 2 aaaaa | + 3 aaaaa | + 4 aaaaa | + 5 aaaaa | + 6 aaaaa | + 7 aaaaa | + virt | + ---------8 aaaaa | + virt | + ---------9 aaaaa | + ~ | + ~ | | ]]) end) - end) diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index 549ebbde06..1c184ff27d 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -164,6 +164,24 @@ describe('statusline clicks', function() meths.input_mouse('right', 'press', '', 0, 6, 5) eq('0 1 r', eval("g:testvar")) end) + + it('no memory leak with zero-width click labels', function() + command([[ + let &stl = '%@Test@%T%@MyClickFunc@%=%T%@Test@' + ]]) + meths.input_mouse('left', 'press', '', 0, 6, 0) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 39) + eq('0 1 r', eval("g:testvar")) + end) + + it('no memory leak with truncated click labels', function() + command([[ + let &stl = '%@MyClickFunc@foo%X' .. repeat('a', 40) .. '%<t%@Test@bar%X%@Test@baz' + ]]) + meths.input_mouse('left', 'press', '', 0, 6, 2) + eq('0 1 l', eval("g:testvar")) + end) end) describe('global statusline', function() diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua index 0e35a03557..2cdec62d01 100644 --- a/test/functional/ui/tabline_spec.lua +++ b/test/functional/ui/tabline_spec.lua @@ -119,4 +119,10 @@ describe("tabline", function() [2] = {bold = true, foreground = Screen.colors.Blue}; }} end) + + it('click definitions do not leak memory #21765', function() + command('set tabline=%@MyClickFunc@MyClickText%T') + command('set showtabline=2') + command('redrawtabline') + end) end) diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua index 8ca245f61a..c032ac3030 100644 --- a/test/functional/vimscript/api_functions_spec.lua +++ b/test/functional/vimscript/api_functions_spec.lua @@ -5,6 +5,7 @@ local neq, eq, command = helpers.neq, helpers.eq, helpers.command local clear, curbufmeths = helpers.clear, helpers.curbufmeths local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval local insert, pcall_err = helpers.insert, helpers.pcall_err +local matches = helpers.matches local meths = helpers.meths describe('eval-API', function() @@ -49,7 +50,7 @@ describe('eval-API', function() it('cannot change texts if textlocked', function() command("autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])") - eq('Vim(call):E5555: API call: E565: Not allowed to change text or change window', + matches('Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$', pcall_err(command, "normal! yy")) end) diff --git a/test/functional/vimscript/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua index d92a81c55b..5ee84a6d13 100644 --- a/test/functional/vimscript/ctx_functions_spec.lua +++ b/test/functional/vimscript/ctx_functions_spec.lua @@ -173,9 +173,9 @@ describe('context functions', function() call('SaveSFuncs') call('DeleteSFuncs') - eq('Vim(call):E117: Unknown function: s:greet', + eq('function Greet, line 1: Vim(call):E117: Unknown function: s:greet', pcall_err(command, [[call Greet('World')]])) - eq('Vim(call):E117: Unknown function: s:greet_all', + eq('function GreetAll, line 1: Vim(call):E117: Unknown function: s:greet_all', pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]])) call('RestoreFuncs') @@ -287,9 +287,11 @@ describe('context functions', function() local with_jumps = { ['jumps'] = eval(([[ - filter(map(getjumplist()[0], 'filter( - { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, - { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') + filter(map(add( + getjumplist()[0], { 'bufnr': bufnr('%'), 'lnum': getcurpos()[1] }), + 'filter( + { "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum }, + { k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)') ]]):gsub('\n', '')) } diff --git a/test/functional/vimscript/exepath_spec.lua b/test/functional/vimscript/exepath_spec.lua index 056f67e0ad..da3d61cbe0 100644 --- a/test/functional/vimscript/exepath_spec.lua +++ b/test/functional/vimscript/exepath_spec.lua @@ -5,21 +5,21 @@ local command = helpers.command local exc_exec = helpers.exc_exec local matches = helpers.matches local is_os = helpers.is_os +local set_shell_powershell = helpers.set_shell_powershell +local eval = helpers.eval + +local find_dummies = function(ext_pat) + local tmp_path = eval('$PATH') + command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') + matches('null' .. ext_pat, call('exepath', 'null')) + matches('true' .. ext_pat, call('exepath', 'true')) + matches('false' .. ext_pat, call('exepath', 'false')) + command("let $PATH = '"..tmp_path.."'") +end describe('exepath()', function() before_each(clear) - it('returns 1 for commands in $PATH', function() - local exe = is_os('win') and 'ping' or 'ls' - local ext_pat = is_os('win') and '%.EXE$' or '$' - matches(exe .. ext_pat, call('exepath', exe)) - command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")') - ext_pat = is_os('win') and '%.CMD$' or '$' - matches('null' .. ext_pat, call('exepath', 'null')) - matches('true' .. ext_pat, call('exepath', 'true')) - matches('false' .. ext_pat, call('exepath', 'false')) - end) - it('fails for invalid values', function() for _, input in ipairs({'v:null', 'v:true', 'v:false', '{}', '[]'}) do eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')')) @@ -32,11 +32,32 @@ describe('exepath()', function() end) if is_os('win') then + it('returns 1 for commands in $PATH (Windows)', function() + local exe = 'ping' + matches(exe .. '%.EXE$', call('exepath', exe)) + end) + it('append extension if omitted', function() local filename = 'cmd' local pathext = '.exe' clear({env={PATHEXT=pathext}}) eq(call('exepath', filename..pathext), call('exepath', filename)) end) + + it('returns file WITH extension if files both with and without extension exist in $PATH', function() + local ext_pat = '%.CMD$' + find_dummies(ext_pat) + set_shell_powershell() + find_dummies(ext_pat) + end) + else + it('returns 1 for commands in $PATH (not Windows)', function() + local exe = 'ls' + matches(exe .. '$', call('exepath', exe)) + end) + + it('returns file WITHOUT extension if files both with and without extension exist in $PATH', function() + find_dummies('$') + end) end end) diff --git a/test/functional/vimscript/server_spec.lua b/test/functional/vimscript/server_spec.lua index 14c87d9d93..c89a0c4e93 100644 --- a/test/functional/vimscript/server_spec.lua +++ b/test/functional/vimscript/server_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_log = helpers.assert_log local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths local ok = helpers.ok @@ -7,6 +8,8 @@ local pcall_err = helpers.pcall_err local mkdir = helpers.mkdir local is_os = helpers.is_os +local testlog = 'Xtest-server-log' + local function clear_serverlist() for _, server in pairs(funcs.serverlist()) do funcs.serverstop(server) @@ -14,6 +17,10 @@ local function clear_serverlist() end describe('server', function() + after_each(function() + os.remove(testlog) + end) + it('serverstart() stores sockets in $XDG_RUNTIME_DIR', function() local dir = 'Xtest_xdg_run' mkdir(dir) @@ -74,13 +81,20 @@ describe('server', function() end) it('serverstop() returns false for invalid input', function() - clear() + clear{env={ + NVIM_LOG_FILE=testlog, + NVIM_LISTEN_ADDRESS='.', + }} eq(0, eval("serverstop('')")) eq(0, eval("serverstop('bogus-socket-name')")) + assert_log('Not listening on bogus%-socket%-name', testlog, 10) end) it('parses endpoints', function() - clear() + clear{env={ + NVIM_LOG_FILE=testlog, + NVIM_LISTEN_ADDRESS='.', + }} clear_serverlist() eq({}, funcs.serverlist()) @@ -104,6 +118,7 @@ describe('server', function() if status then table.insert(expected, v4) pcall(funcs.serverstart, v4) -- exists already; ignore + assert_log('Failed to start server: address already in use: 127%.0%.0%.1', testlog, 10) end local v6 = '::1:12345' @@ -111,6 +126,7 @@ describe('server', function() if status then table.insert(expected, v6) pcall(funcs.serverstart, v6) -- exists already; ignore + assert_log('Failed to start server: address already in use: ::1', testlog, 10) end eq(expected, funcs.serverlist()) clear_serverlist() diff --git a/test/helpers.lua b/test/helpers.lua index 3fe4322501..82ff23bef8 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -1,6 +1,7 @@ require('test.compat') local shared = require('vim.shared') local assert = require('luassert') +local busted = require('busted') local luv = require('luv') local lfs = require('lfs') local relpath = require('pl.path').relpath @@ -45,6 +46,28 @@ function module.sleep(ms) luv.sleep(ms) end +-- Calls fn() until it succeeds, up to `max` times or until `max_ms` +-- milliseconds have passed. +function module.retry(max, max_ms, fn) + assert(max == nil or max > 0) + assert(max_ms == nil or max_ms > 0) + local tries = 1 + local timeout = (max_ms and max_ms or 10000) + local start_time = luv.now() + while true do + local status, result = pcall(fn) + if status then + return result + end + luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). + if (max and tries >= max) or (luv.now() - start_time > timeout) then + busted.fail(string.format("retry() attempts: %d\n%s", tries, tostring(result)), 2) + end + tries = tries + 1 + luv.sleep(20) -- Avoid hot loop... + end +end + local check_logs_useless_lines = { ['Warning: noted but unhandled ioctl']=1, ['could cause spurious value errors to appear']=2, @@ -87,6 +110,8 @@ end --- Asserts that `pat` matches (or *not* if inverse=true) any line in the tail of `logfile`. --- +--- Retries for 1 second in case of filesystem delay. +--- ---@param pat (string) Lua pattern to match lines in the log file ---@param logfile (string) Full path to log file (default=$NVIM_LOG_FILE) ---@param nrlines (number) Search up to this many log lines @@ -96,18 +121,21 @@ function module.assert_log(pat, logfile, nrlines, inverse) assert(logfile ~= nil, 'no logfile') nrlines = nrlines or 10 inverse = inverse or false - local lines = module.read_file_list(logfile, -nrlines) or {} - local msg = string.format('Pattern %q %sfound in log (last %d lines): %s:\n%s', - pat, (inverse and '' or 'not '), nrlines, logfile, ' '..table.concat(lines, '\n ')) - for _,line in ipairs(lines) do - if line:match(pat) then - if inverse then error(msg) else return end + + module.retry(nil, 1000, function() + local lines = module.read_file_list(logfile, -nrlines) or {} + local msg = string.format('Pattern %q %sfound in log (last %d lines): %s:\n%s', + pat, (inverse and '' or 'not '), nrlines, logfile, ' '..table.concat(lines, '\n ')) + for _,line in ipairs(lines) do + if line:match(pat) then + if inverse then error(msg) else return end + end end - end - if not inverse then error(msg) end + if not inverse then error(msg) end + end) end ---- Asserts that `pat` does NOT matche any line in the tail of `logfile`. +--- Asserts that `pat` does NOT match any line in the tail of `logfile`. --- --- @see assert_log function module.assert_nolog(pat, logfile, nrlines) diff --git a/test/includes/CMakeLists.txt b/test/includes/CMakeLists.txt deleted file mode 100644 index 0d30736091..0000000000 --- a/test/includes/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -file(GLOB_RECURSE PRE_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} pre/*.h) - -# We need to add the SDK directories on OS X, and perhaps other operating -# systems. -set(gen_cflags) -foreach(gen_include ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}) - list(APPEND gen_cflags ${CMAKE_INCLUDE_FLAG_C}${gen_include}) -endforeach() - -get_directory_property(gen_cdefs COMPILE_DEFINITIONS) -foreach(gen_cdef ${gen_cdefs}) - if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS") - list(APPEND gen_cflags "-D${gen_cdef}") - endif() -endforeach() - -foreach(hfile ${PRE_HEADERS}) - string(REGEX REPLACE ^pre/ post/ post_hfile ${hfile}) - get_filename_component(hdir ${CMAKE_CURRENT_BINARY_DIR}/${post_hfile} DIRECTORY) - file(MAKE_DIRECTORY ${hdir}) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${post_hfile} - COMMAND ${CMAKE_C_COMPILER} -std=c99 -E -P - ${CMAKE_CURRENT_SOURCE_DIR}/${hfile} - ${gen_cflags} - -o ${CMAKE_CURRENT_BINARY_DIR}/${post_hfile}) - list(APPEND POST_HEADERS ${post_hfile}) -endforeach() - -add_custom_target(unittest-headers DEPENDS ${POST_HEADERS}) -set_target_properties(unittest-headers PROPERTIES FOLDER test) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 34dbf592a5..825377813d 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1677,7 +1677,7 @@ describe('typval.c', function() eq(nil, lib.tv_dict_find(nil, 'test', -1)) eq(nil, lib.tv_dict_find(nil, nil, 0)) end) - itp('works with NULL key', function() + itp('works with empty key', function() local lua_d = { ['']=0, t=1, @@ -1692,7 +1692,6 @@ describe('typval.c', function() alloc_log:check({}) local dis = dict_items(d) eq({0, '', dis['']}, {tv_dict_find(d, '', 0)}) - eq({0, '', dis['']}, {tv_dict_find(d, nil, 0)}) end) itp('works with len properly', function() local lua_d = { @@ -1911,8 +1910,6 @@ describe('typval.c', function() local d = dict(lua_d) eq(lua_d, dct2tbl(d)) eq({{type='fref', fref='tr'}, true}, - {tv_dict_get_callback(d, nil, 0)}) - eq({{type='fref', fref='tr'}, true}, {tv_dict_get_callback(d, '', -1)}) eq({{type='none'}, true}, {tv_dict_get_callback(d, 'x', -1)}) @@ -2175,7 +2172,8 @@ describe('typval.c', function() eq({a='TSET'}, dct2tbl(d1)) eq({a='TSET'}, dct2tbl(d2)) end) - itp('disallows overriding builtin or user functions', function() + pending('disallows overriding builtin or user functions: here be the dragons', function() + -- pending: see TODO below local d = dict() d.dv_scope = lib.VAR_DEF_SCOPE local f_lua = { @@ -2196,6 +2194,7 @@ describe('typval.c', function() local d5 = dict({Test=f_tv}) local d6 = dict({Test=p_tv}) eval0([[execute("function Test()\nendfunction")]]) + -- TODO: test breaks at this point tv_dict_extend(d, d2, 'force', 'E704: Funcref variable name must start with a capital: tr') tv_dict_extend(d, d3, 'force', @@ -2623,7 +2622,7 @@ describe('typval.c', function() describe('check_lock()', function() local function tv_check_lock(lock, name, name_len, emsg) return check_emsg(function() - return lib.var_check_lock(lock, name, name_len) + return lib.value_check_lock(lock, name, name_len) end, emsg) end itp('works', function() diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 29ea0235be..686af3b461 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -7,9 +7,6 @@ local global_helpers = require('test.helpers') local assert = require('luassert') local say = require('say') -local posix = nil -local syscall = nil - local check_cores = global_helpers.check_cores local dedent = global_helpers.dedent local neq = global_helpers.neq @@ -78,7 +75,8 @@ local function child_cleanup_once(func, ...) end end -local libnvim = nil +-- Unittests are run from debug nvim binary in lua interpreter mode. +local libnvim = ffi.C local lib = setmetatable({}, { __index = only_separate(function(_, idx) @@ -90,8 +88,6 @@ local lib = setmetatable({}, { }) local init = only_separate(function() - -- load neovim shared library - libnvim = ffi.load(Paths.test_libnvim_path) for _, c in ipairs(child_calls_init) do c.func(unpack(c.args)) end @@ -373,122 +369,91 @@ local function to_cstr(string) return cstr(#string + 1, string) end -local sc - -if posix ~= nil then - sc = { - fork = posix.fork, - pipe = posix.pipe, - read = posix.read, - write = posix.write, - close = posix.close, - wait = posix.wait, - exit = posix._exit, - } -elseif syscall ~= nil then - sc = { - fork = syscall.fork, - pipe = function() - local ret = {syscall.pipe()} - return ret[3], ret[4] - end, - read = function(rd, len) - return rd:read(nil, len) - end, - write = function(wr, s) - return wr:write(s) - end, - close = function(p) - return p:close() - end, - wait = syscall.wait, - exit = syscall.exit, - } -else - cimport_immediate('./test/unit/fixtures/posix.h') - sc = { - fork = function() - return tonumber(ffi.C.fork()) - end, - pipe = function() - local ret = ffi.new('int[2]', {-1, -1}) - ffi.errno(0) - local res = ffi.C.pipe(ret) - if (res ~= 0) then +cimport_immediate('./test/unit/fixtures/posix.h') +local sc = { + fork = function() + return tonumber(ffi.C.fork()) + end, + pipe = function() + local ret = ffi.new('int[2]', {-1, -1}) + ffi.errno(0) + local res = ffi.C.pipe(ret) + if (res ~= 0) then + local err = ffi.errno(0) + assert(res == 0, ("pipe() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) + end + assert(ret[0] ~= -1 and ret[1] ~= -1) + return ret[0], ret[1] + end, + read = function(rd, len) + local ret = ffi.new('char[?]', len, {0}) + local total_bytes_read = 0 + ffi.errno(0) + while total_bytes_read < len do + local bytes_read = tonumber(ffi.C.read( + rd, + ffi.cast('void*', ret + total_bytes_read), + len - total_bytes_read)) + if bytes_read == -1 then local err = ffi.errno(0) - assert(res == 0, ("pipe() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - assert(ret[0] ~= -1 and ret[1] ~= -1) - return ret[0], ret[1] - end, - read = function(rd, len) - local ret = ffi.new('char[?]', len, {0}) - local total_bytes_read = 0 - ffi.errno(0) - while total_bytes_read < len do - local bytes_read = tonumber(ffi.C.read( - rd, - ffi.cast('void*', ret + total_bytes_read), - len - total_bytes_read)) - if bytes_read == -1 then - local err = ffi.errno(0) - if err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("read() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - elseif bytes_read == 0 then - break - else - total_bytes_read = total_bytes_read + bytes_read + if err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("read() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) end + elseif bytes_read == 0 then + break + else + total_bytes_read = total_bytes_read + bytes_read end - return ffi.string(ret, total_bytes_read) - end, - write = function(wr, s) - local wbuf = to_cstr(s) - local total_bytes_written = 0 - ffi.errno(0) - while total_bytes_written < #s do - local bytes_written = tonumber(ffi.C.write( - wr, - ffi.cast('void*', wbuf + total_bytes_written), - #s - total_bytes_written)) - if bytes_written == -1 then - local err = ffi.errno(0) - if err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("write() error: %u: %s ('%s')"):format( - err, ffi.string(ffi.C.strerror(err)), s)) - end - elseif bytes_written == 0 then - break - else - total_bytes_written = total_bytes_written + bytes_written + end + return ffi.string(ret, total_bytes_read) + end, + write = function(wr, s) + local wbuf = to_cstr(s) + local total_bytes_written = 0 + ffi.errno(0) + while total_bytes_written < #s do + local bytes_written = tonumber(ffi.C.write( + wr, + ffi.cast('void*', wbuf + total_bytes_written), + #s - total_bytes_written)) + if bytes_written == -1 then + local err = ffi.errno(0) + if err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("write() error: %u: %s ('%s')"):format( + err, ffi.string(ffi.C.strerror(err)), s)) end + elseif bytes_written == 0 then + break + else + total_bytes_written = total_bytes_written + bytes_written end - return total_bytes_written - end, - close = ffi.C.close, - wait = function(pid) - ffi.errno(0) - while true do - local r = ffi.C.waitpid(pid, nil, ffi.C.kPOSIXWaitWUNTRACED) - if r == -1 then - local err = ffi.errno(0) - if err == ffi.C.kPOSIXErrnoECHILD then - break - elseif err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("waitpid() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - else - assert(r == pid) + end + return total_bytes_written + end, + close = ffi.C.close, + wait = function(pid) + ffi.errno(0) + local stat_loc = ffi.new('int[1]', {0}) + while true do + local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED) + if r == -1 then + local err = ffi.errno(0) + if err == ffi.C.kPOSIXErrnoECHILD then + break + elseif err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("waitpid() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) end + else + assert(r == pid) end - end, - exit = ffi.C._exit, - } -end + end + return stat_loc[0] + end, + exit = ffi.C._exit, +} local function format_list(lst) local ret = '' @@ -730,18 +695,22 @@ local function check_child_err(rd) end end -local function itp_parent(rd, pid, allow_failure) - local err, emsg = pcall(check_child_err, rd) - sc.wait(pid) +local function itp_parent(rd, pid, allow_failure, location) + local ok, emsg = pcall(check_child_err, rd) + local status = sc.wait(pid) sc.close(rd) - if not err then + if not ok then if allow_failure then - io.stderr:write('Errorred out:\n' .. tostring(emsg) .. '\n') + io.stderr:write('Errorred out ('..status..'):\n' .. tostring(emsg) .. '\n') os.execute([[ sh -c "source ci/common/test.sh check_core_dumps --delete \"]] .. Paths.test_luajit_prg .. [[\""]]) else - error(emsg) + error(tostring(emsg)..'\nexit code: '..status) + end + elseif status ~= 0 then + if not allow_failure then + error("child process errored out with status "..status.."!\n\n"..location) end end end @@ -758,6 +727,11 @@ local function gen_itp(it) -- FIXME Fix tests with this true return end + + -- Pre-emptively calculating error location, wasteful, ugh! + -- But the way this code messes around with busted implies the real location is strictly + -- not available in the parent when an actual error occurs. so we have to do this here. + local location = debug.traceback() it(name, function() local rd, wr = sc.pipe() child_pid = sc.fork() @@ -768,7 +742,7 @@ local function gen_itp(it) sc.close(wr) local saved_child_pid = child_pid child_pid = nil - itp_parent(rd, saved_child_pid, allow_failure) + itp_parent(rd, saved_child_pid, allow_failure, location) end end) end diff --git a/test/unit/tui_spec.lua b/test/unit/tui_spec.lua index 15b019edd1..192e35a485 100644 --- a/test/unit/tui_spec.lua +++ b/test/unit/tui_spec.lua @@ -9,9 +9,10 @@ local cinput = cimport("./src/nvim/tui/input.h") local rbuffer = cimport("./test/unit/fixtures/rbuffer.h") local globals = cimport("./src/nvim/globals.h") local multiqueue = cimport("./test/unit/fixtures/multiqueue.h") +local ui_client = cimport("./src/nvim/ui_client.h") itp('handle_background_color', function() - local handle_background_color = cinput.ut_handle_background_color + local handle_background_color = cinput.handle_background_color local term_input = ffi.new('TermInput', {}) local events = globals.main_loop.thread_events local kIncomplete = cinput.kIncomplete @@ -34,10 +35,8 @@ itp('handle_background_color', function() eq(kComplete, handle_background_color(term_input)) eq(0, term_input.waiting_for_bg_response) eq(0, multiqueue.multiqueue_size(events)) - - local event = multiqueue.multiqueue_get(events) - local bg_event = ffi.cast("Event*", event.argv[1]) - eq(bg, ffi.string(bg_event.argv[0])) + eq(bg, ({[0]="light", [1] = "dark", [-1] = "none"}) + [tonumber(ui_client.ui_client_bg_response)]) -- Buffer has been consumed. eq(0, rbuf.size) @@ -114,9 +113,7 @@ itp('handle_background_color', function() eq(kComplete, handle_background_color(term_input)) eq(0, term_input.waiting_for_bg_response) - local event = multiqueue.multiqueue_get(events) - local bg_event = ffi.cast("Event*", event.argv[1]) - eq('light', ffi.string(bg_event.argv[0])) + eq(0, tonumber(ui_client.ui_client_bg_response)) eq(0, multiqueue.multiqueue_size(events)) eq(0, rbuf.size) @@ -133,9 +130,7 @@ itp('handle_background_color', function() eq(kComplete, handle_background_color(term_input)) eq(0, term_input.waiting_for_bg_response) - event = multiqueue.multiqueue_get(events) - bg_event = ffi.cast("Event*", event.argv[1]) - eq('light', ffi.string(bg_event.argv[0])) + eq(0, tonumber(ui_client.ui_client_bg_response)) eq(0, multiqueue.multiqueue_size(events)) eq(0, rbuf.size) @@ -161,7 +156,7 @@ itp('handle_background_color', function() eq(kComplete, handle_background_color(term_input)) eq(0, term_input.waiting_for_bg_response) - eq(1, multiqueue.multiqueue_size(events)) + eq(0, multiqueue.multiqueue_size(events)) eq(3, rbuf.size) rbuffer.rbuffer_consumed(rbuf, rbuf.size) end) |