diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:39:54 +0000 |
commit | 21cb7d04c387e4198ca8098a884c78b56ffcf4c2 (patch) | |
tree | 84fe5690df1551f0bb2bdfe1a13aacd29ebc1de7 /test/functional/vimscript | |
parent | d9c904f85a23a496df4eb6be42aa43f007b22d50 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-colorcolchar.tar.gz rneovim-colorcolchar.tar.bz2 rneovim-colorcolchar.zip |
Merge remote-tracking branch 'upstream/master' into colorcolcharcolorcolchar
Diffstat (limited to 'test/functional/vimscript')
21 files changed, 438 insertions, 131 deletions
diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua index c032ac3030..0a7e7c1137 100644 --- a/test/functional/vimscript/api_functions_spec.lua +++ b/test/functional/vimscript/api_functions_spec.lua @@ -1,12 +1,13 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local lfs = require('lfs') +local luv = require('luv') 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 +local feed = helpers.feed describe('eval-API', function() before_each(clear) @@ -32,8 +33,8 @@ describe('eval-API', function() local err = exc_exec('call nvim_get_current_buf("foo")') eq('Vim(call):E118: Too many arguments for function: nvim_get_current_buf', err) - err = exc_exec('call nvim_set_option("hlsearch")') - eq('Vim(call):E119: Not enough arguments for function: nvim_set_option', err) + err = exc_exec('call nvim_set_option_value("hlsearch")') + eq('Vim(call):E119: Not enough arguments for function: nvim_set_option_value', err) err = exc_exec('call nvim_buf_set_lines(1, 0, -1, [], ["list"])') eq('Vim(call):E5555: API call: Wrong type for argument 4 when calling nvim_buf_set_lines, expecting Boolean', err) @@ -48,10 +49,47 @@ describe('eval-API', function() eq('Vim(call):E5555: API call: Invalid buffer id: 17', err) end) - it('cannot change texts if textlocked', function() + it('cannot change text or window if textlocked', function() command("autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])") matches('Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$', pcall_err(command, "normal! yy")) + + command("autocmd TextYankPost <buffer> ++once call nvim_open_term(0, {})") + matches('Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$', + pcall_err(command, "normal! yy")) + + -- Functions checking textlock should also not be usable from <expr> mappings. + command("inoremap <expr> <f2> nvim_win_close(0, 1)") + eq('Vim(normal):E5555: API call: E565: Not allowed to change text or change window', + pcall_err(command, [[execute "normal i\<f2>"]])) + + -- Text-changing functions gave a "Failed to save undo information" error when called from an + -- <expr> mapping outside do_cmdline() (msg_list == NULL), so use feed() to test this. + command("inoremap <expr> <f2> nvim_buf_set_text(0, 0, 0, 0, 0, ['hi'])") + meths.set_vvar('errmsg', '') + feed("i<f2><esc>") + eq('E5555: API call: E565: Not allowed to change text or change window', + meths.get_vvar('errmsg')) + + -- Some functions checking textlock (usually those that may change the current window or buffer) + -- also ought to not be usable in the cmdwin. + local old_win = meths.get_current_win() + feed("q:") + eq('E11: Invalid in command-line window; <CR> executes, CTRL-C quits', + pcall_err(meths.set_current_win, old_win)) + + -- But others, like nvim_buf_set_lines(), which just changes text, is OK. + curbufmeths.set_lines(0, -1, 1, {"wow!"}) + eq({'wow!'}, curbufmeths.get_lines(0, -1, 1)) + + -- Turning the cmdwin buffer into a terminal buffer would be pretty weird. + eq('E11: Invalid in command-line window; <CR> executes, CTRL-C quits', + pcall_err(meths.open_term, 0, {})) + + -- But turning a different buffer into a terminal from the cmdwin is OK. + local term_buf = meths.create_buf(false, true) + meths.open_term(term_buf, {}) + eq('terminal', meths.get_option_value("buftype", {buf = term_buf})) end) it("use buffer numbers and windows ids as handles", function() @@ -118,7 +156,7 @@ describe('eval-API', function() end) it('are highlighted by vim.vim syntax file', function() - if lfs.attributes("build/runtime/syntax/vim/generated.vim",'uid') == nil then + if luv.fs_stat("build/runtime/syntax/vim/generated.vim").uid == nil then pending("runtime was not built, skipping test") return end @@ -133,7 +171,7 @@ describe('eval-API', function() }) command("set ft=vim") - command("let &rtp='build/runtime/,'.&rtp") + command("set rtp^=build/runtime/") command("syntax on") insert([[ call bufnr('%') diff --git a/test/functional/vimscript/buf_functions_spec.lua b/test/functional/vimscript/buf_functions_spec.lua index b521620320..2a5720fbd7 100644 --- a/test/functional/vimscript/buf_functions_spec.lua +++ b/test/functional/vimscript/buf_functions_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local lfs = require('lfs') +local luv = require('luv') local eq = helpers.eq local clear = helpers.clear @@ -9,13 +9,13 @@ local meths = helpers.meths local command = helpers.command local exc_exec = helpers.exc_exec local bufmeths = helpers.bufmeths -local winmeths = helpers.winmeths local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths local curtabmeths = helpers.curtabmeths local get_pathsep = helpers.get_pathsep local rmdir = helpers.rmdir local pcall_err = helpers.pcall_err +local mkdir = helpers.mkdir local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' @@ -62,7 +62,7 @@ describe('bufname() function', function() eq('', funcs.bufname('X')) end) before_each(function() - lfs.mkdir(dirname) + mkdir(dirname) end) after_each(function() rmdir(dirname) @@ -70,7 +70,7 @@ describe('bufname() function', function() it('returns expected buffer name', function() eq('', funcs.bufname('%')) -- Buffer has no name yet command('file ' .. fname) - local wd = lfs.currentdir() + local wd = luv.cwd() local sep = get_pathsep() local curdirname = funcs.fnamemodify(wd, ':t') for _, arg in ipairs({'%', 1, 'X', wd}) do @@ -103,7 +103,7 @@ describe('bufnr() function', function() it('returns expected buffer number', function() eq(1, funcs.bufnr('%')) command('file ' .. fname) - local wd = lfs.currentdir() + local wd = luv.cwd() local curdirname = funcs.fnamemodify(wd, ':t') eq(1, funcs.bufnr(fname)) eq(1, funcs.bufnr(wd)) @@ -144,7 +144,7 @@ describe('bufwinnr() function', function() eq(-1, funcs.bufwinnr('X')) end) before_each(function() - lfs.mkdir(dirname) + mkdir(dirname) end) after_each(function() rmdir(dirname) @@ -188,7 +188,7 @@ describe('getbufline() function', function() eq({}, funcs.getbufline(1, -1, 9999)) end) it('returns expected lines', function() - meths.set_option('hidden', true) + meths.set_option_value('hidden', true, {}) command('file ' .. fname) curbufmeths.set_lines(0, 1, false, {'foo\0', '\0bar', 'baz'}) command('edit ' .. fname2) @@ -268,24 +268,25 @@ describe('setbufvar() function', function() end) it('may set options, including window-local and global values', function() local buf1 = meths.get_current_buf() - eq(false, curwinmeths.get_option('number')) + eq(false, meths.get_option_value('number', {})) command('split') command('new') eq(2, bufmeths.get_number(curwinmeths.get_buf())) funcs.setbufvar(1, '&number', true) local windows = curtabmeths.list_wins() - eq(false, winmeths.get_option(windows[1], 'number')) - eq(true, winmeths.get_option(windows[2], 'number')) - eq(false, winmeths.get_option(windows[3], 'number')) - eq(false, winmeths.get_option(meths.get_current_win(), 'number')) + eq(false, meths.get_option_value('number', {win=windows[1].id})) + eq(true, meths.get_option_value('number', {win=windows[2].id})) + eq(false, meths.get_option_value('number', {win=windows[3].id})) + eq(false, meths.get_option_value('number', {win=meths.get_current_win().id})) - eq(true, meths.get_option('hidden')) + + eq(true, meths.get_option_value('hidden', {})) funcs.setbufvar(1, '&hidden', 0) - eq(false, meths.get_option('hidden')) + eq(false, meths.get_option_value('hidden', {})) - eq(false, bufmeths.get_option(buf1, 'autoindent')) + eq(false, meths.get_option_value('autoindent', {buf=buf1.id})) funcs.setbufvar(1, '&autoindent', true) - eq(true, bufmeths.get_option(buf1, 'autoindent')) + eq(true, meths.get_option_value('autoindent', {buf=buf1.id})) eq('Vim(call):E355: Unknown option: xxx', exc_exec('call setbufvar(1, "&xxx", 0)')) end) diff --git a/test/functional/vimscript/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua index 5ee84a6d13..17607f0794 100644 --- a/test/functional/vimscript/ctx_functions_spec.lua +++ b/test/functional/vimscript/ctx_functions_spec.lua @@ -375,6 +375,12 @@ describe('context functions', function() eq(outofbounds, pcall_err(call, 'ctxset', {dummy = 1}, 0)) end) + it('errors when context dictionary is invalid', function() + call('ctxpush') + eq('Vim:E474: Failed to convert list to msgpack string buffer', + pcall_err(call, 'ctxset', { regs = { {} }, jumps = { {} } })) + end) + it('sets context dictionary at index in context stack', function() nvim('set_var', 'one', 1) nvim('set_var', 'Two', 2) diff --git a/test/functional/vimscript/environ_spec.lua b/test/functional/vimscript/environ_spec.lua index 9e19568249..52218d3cc9 100644 --- a/test/functional/vimscript/environ_spec.lua +++ b/test/functional/vimscript/environ_spec.lua @@ -26,6 +26,8 @@ end) describe('empty $HOME', function() local original_home = os.getenv('HOME') + before_each(clear) + -- recover $HOME after each test after_each(function() if original_home ~= nil then diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua index a8a901042b..ab0ffccd4d 100644 --- a/test/functional/vimscript/eval_spec.lua +++ b/test/functional/vimscript/eval_spec.lua @@ -12,7 +12,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local lfs = require('lfs') +local mkdir = helpers.mkdir local clear = helpers.clear local eq = helpers.eq local exc_exec = helpers.exc_exec @@ -56,11 +56,11 @@ end) describe("backtick expansion", function() setup(function() clear() - lfs.mkdir("test-backticks") + mkdir("test-backticks") write_file("test-backticks/file1", "test file 1") write_file("test-backticks/file2", "test file 2") write_file("test-backticks/file3", "test file 3") - lfs.mkdir("test-backticks/subdir") + mkdir("test-backticks/subdir") write_file("test-backticks/subdir/file4", "test file 4") -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. command('silent cd test-backticks') @@ -153,11 +153,6 @@ end) describe("uncaught exception", function() before_each(clear) - after_each(function() - os.remove('throw1.vim') - os.remove('throw2.vim') - os.remove('throw3.vim') - end) it('is not forgotten #13490', function() command('autocmd BufWinEnter * throw "i am error"') @@ -173,10 +168,45 @@ describe("uncaught exception", function() let result ..= 'X' ]]):format(i, i)) end + finally(function() + for i = 1, 3 do + os.remove('throw' .. i .. '.vim') + end + end) + command('set runtimepath+=. | let result = ""') eq('throw1', exc_exec('try | runtime! throw*.vim | endtry')) eq('123', eval('result')) end) + + it('multiline exception remains multiline #25350', function() + local screen = Screen.new(80, 11) + screen:set_default_attr_ids({ + [1] = {bold = true, reverse = true}; -- MsgSeparator + [2] = {foreground = Screen.colors.White, background = Screen.colors.Red}; -- ErrorMsg + [3] = {bold = true, foreground = Screen.colors.SeaGreen}; -- MoreMsg + }) + screen:attach() + exec_lua([[ + function _G.Oops() + error("oops") + end + ]]) + feed(':try\rlua _G.Oops()\rendtry\r') + screen:expect{grid=[[ + {1: }| + :try | + : lua _G.Oops() | + : endtry | + {2:Error detected while processing :} | + {2:E5108: Error executing lua [string "<nvim>"]:2: oops} | + {2:stack traceback:} | + {2: [C]: in function 'error'} | + {2: [string "<nvim>"]:2: in function 'Oops'} | + {2: [string ":lua"]:1: in main chunk} | + {3:Press ENTER or type command to continue}^ | + ]]} + end) end) describe('listing functions using :function', function() @@ -191,11 +221,10 @@ describe('listing functions using :function', function() endfunction]]):format(num), exec_capture(('function <lambda>%s'):format(num))) end) - -- FIXME: If the same function is deleted, the crash still happens. #20790 it('does not crash if another function is deleted while listing', function() local screen = Screen.new(80, 24) screen:attach() - matches('Vim%(function%):E454: function list was modified', pcall_err(exec_lua, [=[ + matches('Vim%(function%):E454: Function list was modified$', pcall_err(exec_lua, [=[ vim.cmd([[ func Func1() endfunc @@ -219,4 +248,67 @@ describe('listing functions using :function', function() ]=])) assert_alive() end) + + it('does not crash if the same function is deleted while listing', function() + local screen = Screen.new(80, 24) + screen:attach() + matches('Vim%(function%):E454: Function list was modified$', pcall_err(exec_lua, [=[ + vim.cmd([[ + func Func1() + endfunc + func Func2() + endfunc + func Func3() + endfunc + ]]) + + local ns = vim.api.nvim_create_namespace('test') + + vim.ui_attach(ns, { ext_messages = true }, function(event, _, content) + if event == 'msg_show' and content[1][2] == 'function Func1()' then + vim.cmd('delfunc Func2') + end + end) + + vim.cmd('function') + + vim.ui_detach(ns) + ]=])) + assert_alive() + end) +end) + +it('no double-free in garbage collection #16287', function() + clear() + -- Don't use exec() here as using a named script reproduces the issue better. + write_file('Xgarbagecollect.vim', [[ + func Foo() abort + let s:args = [a:000] + let foo0 = "" + let foo1 = "" + let foo2 = "" + let foo3 = "" + let foo4 = "" + let foo5 = "" + let foo6 = "" + let foo7 = "" + let foo8 = "" + let foo9 = "" + let foo10 = "" + let foo11 = "" + let foo12 = "" + let foo13 = "" + let foo14 = "" + endfunc + + set updatetime=1 + call Foo() + call Foo() + ]]) + finally(function() + os.remove('Xgarbagecollect.vim') + end) + command('source Xgarbagecollect.vim') + sleep(10) + assert_alive() end) diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua index 43e4a29e1a..2b8e3f6218 100644 --- a/test/functional/vimscript/executable_spec.lua +++ b/test/functional/vimscript/executable_spec.lua @@ -137,12 +137,16 @@ describe('executable() (Windows)', function() end) it('system([…]), jobstart([…]) use $PATHEXT #9569', function() + -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd". + clear({env={PATHEXT=''}}) -- Invoking `cmdscript` should find/execute `cmdscript.cmd`. eq('much success\n', call('system', {'test/functional/fixtures/cmdscript'})) assert(0 < call('jobstart', {'test/functional/fixtures/cmdscript'})) end) it('full path with extension', function() + -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd". + clear({env={PATHEXT=''}}) -- Some executable we can expect in the test env. local exe = 'printargs-test' local exedir = eval("fnamemodify(v:progpath, ':h')") @@ -153,6 +157,8 @@ describe('executable() (Windows)', function() end) it('full path without extension', function() + -- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd". + clear({env={PATHEXT=''}}) -- Some executable we can expect in the test env. local exe = 'printargs-test' local exedir = eval("fnamemodify(v:progpath, ':h')") diff --git a/test/functional/vimscript/execute_spec.lua b/test/functional/vimscript/execute_spec.lua index 5fe3d787cb..bb28938708 100644 --- a/test/functional/vimscript/execute_spec.lua +++ b/test/functional/vimscript/execute_spec.lua @@ -4,6 +4,7 @@ local eval = helpers.eval local clear = helpers.clear local source = helpers.source local exc_exec = helpers.exc_exec +local pcall_err = helpers.pcall_err local funcs = helpers.funcs local Screen = require('test.functional.ui.screen') local command = helpers.command @@ -92,18 +93,14 @@ describe('execute()', function() it('captures errors', function() local ret - ret = exc_exec('call execute(0.0)') - eq('Vim(call):E806: using Float as a String', ret) ret = exc_exec('call execute(v:_null_dict)') - eq('Vim(call):E731: using Dictionary as a String', ret) + eq('Vim(call):E731: Using a Dictionary as a String', ret) ret = exc_exec('call execute(function("tr"))') - eq('Vim(call):E729: using Funcref as a String', ret) - ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])') - eq('Vim:E806: using Float as a String', ret) + eq('Vim(call):E729: Using a Funcref as a String', ret) ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])') - eq('Vim:E731: using Dictionary as a String', ret) + eq('Vim:E731: Using a Dictionary as a String', ret) ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])') - eq('Vim:E729: using Funcref as a String', ret) + eq('Vim:E729: Using a Funcref as a String', ret) end) it('captures output with highlights', function() @@ -284,6 +281,14 @@ describe('execute()', function() eq('42', eval('g:mes')) end) + it('gives E493 instead of prompting on backwards range for ""', function() + command('split') + eq('Vim(windo):E493: Backwards range given: 2,1windo echo', + pcall_err(funcs.execute, '2,1windo echo', '')) + eq('Vim(windo):E493: Backwards range given: 2,1windo echo', + pcall_err(funcs.execute, {'2,1windo echo'}, '')) + end) + it('captures but does not display output for "silent"', function() local screen = Screen.new(40, 5) screen:attach() @@ -321,11 +326,8 @@ describe('execute()', function() it('propagates errors for "" and "silent"', function() local ret - ret = exc_exec('call execute(0.0, "")') - eq('Vim(call):E806: using Float as a String', ret) - ret = exc_exec('call execute(v:_null_dict, "silent")') - eq('Vim(call):E731: using Dictionary as a String', ret) + eq('Vim(call):E731: Using a Dictionary as a String', ret) ret = exc_exec('call execute("echo add(1, 1)", "")') eq('Vim(echo):E897: List or Blob required', ret) 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/glob_spec.lua b/test/functional/vimscript/glob_spec.lua index b8807ecfcc..948a63f050 100644 --- a/test/functional/vimscript/glob_spec.lua +++ b/test/functional/vimscript/glob_spec.lua @@ -1,17 +1,18 @@ -local lfs = require('lfs') +local luv = require('luv') local helpers = require('test.functional.helpers')(after_each) local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq +local mkdir = helpers.mkdir before_each(function() clear() - lfs.mkdir('test-glob') + mkdir('test-glob') -- Long path might cause "Press ENTER" prompt; use :silent to avoid it. command('silent cd test-glob') end) after_each(function() - lfs.rmdir('test-glob') + luv.fs_rmdir('test-glob') end) describe('glob()', function() diff --git a/test/functional/vimscript/has_spec.lua b/test/functional/vimscript/has_spec.lua index 2e26d603b3..78a761d370 100644 --- a/test/functional/vimscript/has_spec.lua +++ b/test/functional/vimscript/has_spec.lua @@ -1,8 +1,11 @@ local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq +local Screen = require('test.functional.ui.screen') local clear = helpers.clear +local connect = helpers.connect +local eq = helpers.eq local funcs = helpers.funcs local is_os = helpers.is_os +local nvim_prog = helpers.nvim_prog describe('has()', function() before_each(clear) @@ -69,8 +72,22 @@ describe('has()', function() end end) + it('"gui_running"', function() + eq(0, funcs.has('gui_running')) + local tui = Screen.new(50,15) + local gui_session = connect(funcs.serverstart()) + local gui = Screen.new(50,15) + eq(0, funcs.has('gui_running')) + tui:attach({ext_linegrid=true, rgb=true, stdin_tty=true, stdout_tty=true}) + gui:attach({ext_multigrid=true, rgb=true}, gui_session) + eq(1, funcs.has('gui_running')) + tui:detach() + eq(1, funcs.has('gui_running')) + gui:detach() + eq(0, funcs.has('gui_running')) + end) + it('does not change v:shell_error', function() - local nvim_prog = helpers.nvim_prog funcs.system({nvim_prog, '-es', '+73cquit'}) funcs.has('python3') -- use a call whose implementation shells out eq(73, funcs.eval('v:shell_error')) diff --git a/test/functional/vimscript/input_spec.lua b/test/functional/vimscript/input_spec.lua index f50b39c2c5..e1179d29cc 100644 --- a/test/functional/vimscript/input_spec.lua +++ b/test/functional/vimscript/input_spec.lua @@ -222,17 +222,17 @@ describe('input()', function() eq('DEF2', meths.get_var('var')) end) it('errors out on invalid inputs', function() - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call input([])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", [])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", "", [])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"prompt": []})')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"default": []})')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"completion": []})')) eq('Vim(call):E5050: {opts} must be the only argument', exc_exec('call input({}, "default")')) @@ -418,17 +418,17 @@ describe('inputdialog()', function() eq('DEF2', meths.get_var('var')) end) it('errors out on invalid inputs', function() - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog([])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", [])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", "", [])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"prompt": []})')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"default": []})')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"completion": []})')) eq('Vim(call):E5050: {opts} must be the only argument', exc_exec('call inputdialog({}, "default")')) @@ -452,8 +452,8 @@ end) describe('confirm()', function() -- oldtest: Test_confirm() it('works', function() - meths.set_option('more', false) -- Avoid hit-enter prompt - meths.set_option('laststatus', 2) + meths.set_option_value('more', false, {}) -- Avoid hit-enter prompt + meths.set_option_value('laststatus', 2, {}) -- screen:expect() calls are needed to avoid feeding input too early screen:expect({any = '%[No Name%]'}) @@ -512,13 +512,13 @@ describe('confirm()', function() eq(1, meths.get_var('a')) end - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', pcall_err(command, 'call confirm([])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', pcall_err(command, 'call confirm("Are you sure?", [])')) eq('Vim(call):E745: Using a List as a Number', pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", [])')) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", 0, [])')) end) @@ -552,7 +552,7 @@ describe('confirm()', function() feed(':silent edit foo<cr>') check_and_clear(':silent edit foo |\n') - -- With API (via eval/VimL) call and shortmess+=F + -- With API (via eval/Vimscript) call and shortmess+=F feed(':call nvim_command("edit x")<cr>') check_and_clear(':call nvim_command("edit |\n') diff --git a/test/functional/vimscript/json_functions_spec.lua b/test/functional/vimscript/json_functions_spec.lua index 70d0934756..a9dab8431c 100644 --- a/test/functional/vimscript/json_functions_spec.lua +++ b/test/functional/vimscript/json_functions_spec.lua @@ -754,7 +754,7 @@ describe('json_encode() function', function() end) it('ignores improper values in &isprint', function() - meths.set_option('isprint', '1') + meths.set_option_value('isprint', '1', {}) eq(1, eval('"\1" =~# "\\\\p"')) eq('"\\u0001"', funcs.json_encode('\1')) end) diff --git a/test/functional/vimscript/let_spec.lua b/test/functional/vimscript/let_spec.lua index 164fa86452..11417c5846 100644 --- a/test/functional/vimscript/let_spec.lua +++ b/test/functional/vimscript/let_spec.lua @@ -92,6 +92,20 @@ describe(':let', function() ]]) eq(1, eval('1')) end) + + it('can apply operator to boolean option', function() + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways -= 1') + eq(false, meths.get_option_value('equalalways', {})) + command('let &equalalways += 1') + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways *= 1') + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways /= 1') + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways %= 1') + eq(false, meths.get_option_value('equalalways', {})) + end) end) describe(':let and :const', function() diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua index ba1b4d7a76..acba5e9708 100644 --- a/test/functional/vimscript/map_functions_spec.lua +++ b/test/functional/vimscript/map_functions_spec.lua @@ -26,9 +26,12 @@ describe('maparg()', function() rhs='bar', expr=0, sid=0, + scriptversion=1, buffer=0, nowait=0, mode='n', + mode_bits=0x01, + abbr=0, noremap=1, lnum=0, } @@ -155,10 +158,13 @@ describe('maparg()', function() buffer = 0, expr = 0, mode = 'n', + mode_bits = 0x01, + abbr = 0, noremap = 1, nowait = 0, - script=0, + script = 0, sid = 0, + scriptversion = 1, silent = 0, lnum = 0, } @@ -246,9 +252,9 @@ describe('mapset()', function() end) it('does not leak memory if lhs is missing', function() - eq('Vim:E460: entries missing in mapset() dict argument', + eq('Vim:E460: Entries missing in mapset() dict argument', pcall_err(exec_lua, [[vim.fn.mapset('n', false, {rhs = 'foo'})]])) - eq('Vim:E460: entries missing in mapset() dict argument', + eq('Vim:E460: Entries missing in mapset() dict argument', pcall_err(exec_lua, [[vim.fn.mapset('n', false, {callback = function() end})]])) end) end) diff --git a/test/functional/vimscript/null_spec.lua b/test/functional/vimscript/null_spec.lua index 1153baac46..d4c36d835b 100644 --- a/test/functional/vimscript/null_spec.lua +++ b/test/functional/vimscript/null_spec.lua @@ -53,19 +53,15 @@ describe('NULL', function() end) end describe('list', function() - -- Incorrect behaviour - -- FIXME Should error out with different message - null_test('makes :unlet act as if it is not a list', ':unlet L[0]', - 'Vim(unlet):E689: Can only index a List, Dictionary or Blob') - -- Subjectable behaviour - null_expr_test('is equal to empty list', 'L == []', 0, 1) null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 1) -- Correct behaviour + null_test('can be :unlet item with error message for empty list', ':unlet L[0]', + 'Vim(unlet):E684: List index out of range: 0') null_expr_test('can be indexed with error message for empty list', 'L[0]', - 'E684: list index out of range: 0', nil) + 'E684: List index out of range: 0', nil) null_expr_test('can be splice-indexed', 'L[:]', 0, {}) null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) @@ -80,7 +76,7 @@ describe('NULL', function() null_expr_test('can be copied', 'copy(L)', 0, {}) null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) null_expr_test('does not crash when indexed', 'L[1]', - 'E684: list index out of range: 1', nil) + 'E684: List index out of range: 1', nil) null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) null_expr_test('does not crash col()', 'col(L)', 0, 0) null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) diff --git a/test/functional/vimscript/screenpos_spec.lua b/test/functional/vimscript/screenpos_spec.lua index 75e5c02298..8b8276457d 100644 --- a/test/functional/vimscript/screenpos_spec.lua +++ b/test/functional/vimscript/screenpos_spec.lua @@ -1,12 +1,12 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, meths = helpers.clear, helpers.eq, helpers.meths local command, funcs = helpers.command, helpers.funcs +local feed = helpers.feed before_each(clear) describe('screenpos() function', function() it('works in floating window with border', function() - local bufnr = meths.create_buf(false, true) local opts = { relative='editor', height=8, @@ -18,34 +18,56 @@ describe('screenpos() function', function() border='none', focusable=1 } - local float = meths.open_win(bufnr, false, opts) + local float = meths.open_win(meths.create_buf(false, true), false, opts) command('redraw') - local pos = funcs.screenpos(bufnr, 1, 1) - eq(7, pos.row) - eq(9, pos.col) + eq({row = 7, col = 9, endcol = 9, curscol = 9}, funcs.screenpos(float, 1, 1)) -- only left border opts.border = {'', '', '', '', '', '', '', '|'} meths.win_set_config(float, opts) command('redraw') - pos = funcs.screenpos(bufnr, 1, 1) - eq(7, pos.row) - eq(10, pos.col) + eq({row = 7, col = 10, endcol = 10, curscol = 10}, funcs.screenpos(float, 1, 1)) -- only top border opts.border = {'', '_', '', '', '', '', '', ''} meths.win_set_config(float, opts) command('redraw') - pos = funcs.screenpos(bufnr, 1, 1) - eq(8, pos.row) - eq(9, pos.col) + eq({row = 8, col = 9, endcol = 9, curscol = 9}, funcs.screenpos(float, 1, 1)) -- both left and top border opts.border = 'single' meths.win_set_config(float, opts) command('redraw') - pos = funcs.screenpos(bufnr, 1, 1) - eq(8, pos.row) - eq(10, pos.col) + eq({row = 8, col = 10, endcol = 10, curscol = 10}, funcs.screenpos(float, 1, 1)) + end) + + it('works for folded line with virt_lines attached to line above', function() + meths.buf_set_lines(0, 0, -1, true, {'aaa', 'bbb', 'ccc', 'ddd'}) + local ns = meths.create_namespace('') + meths.buf_set_extmark(0, ns, 0, 0, { virt_lines = {{{'abb'}}, {{'acc'}}, {{'add'}}} }) + command('2,3fold') + eq({row = 5, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1)) + eq({row = 5, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1)) + eq({row = 6, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1)) + + feed('<C-E>') + eq({row = 4, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1)) + eq({row = 4, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1)) + eq({row = 5, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1)) + + feed('<C-E>') + eq({row = 3, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1)) + eq({row = 3, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1)) + eq({row = 4, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1)) + + feed('<C-E>') + eq({row = 2, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1)) + eq({row = 2, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1)) + eq({row = 3, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1)) + + feed('<C-E>') + eq({row = 1, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1)) + eq({row = 1, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1)) + eq({row = 2, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1)) end) end) diff --git a/test/functional/vimscript/special_vars_spec.lua b/test/functional/vimscript/special_vars_spec.lua index 14ccbc3827..217f0b2c2b 100644 --- a/test/functional/vimscript/special_vars_spec.lua +++ b/test/functional/vimscript/special_vars_spec.lua @@ -130,6 +130,12 @@ describe('Special values', function() eq("v:false", eval('"" . v:false')) end) + it('work with ?? (falsy operator)', function() + eq(true, eval('v:true ?? 42')) + eq(42, eval('v:false ?? 42')) + eq(42, eval('v:null ?? 42')) + end) + it('work with type()', function() eq(6, funcs.type(true)) eq(6, funcs.type(false)) diff --git a/test/functional/vimscript/state_spec.lua b/test/functional/vimscript/state_spec.lua new file mode 100644 index 0000000000..0508b8b1da --- /dev/null +++ b/test/functional/vimscript/state_spec.lua @@ -0,0 +1,86 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local eq = helpers.eq +local exec = helpers.exec +local exec_lua = helpers.exec_lua +local feed = helpers.feed +local meths = helpers.meths +local poke_eventloop = helpers.poke_eventloop + +before_each(clear) + +describe('state() function', function() + -- oldtest: Test_state() + it('works', function() + meths.ui_attach(80, 24, {}) -- Allow hit-enter-prompt + + exec_lua([[ + function _G.Get_state_mode() + _G.res = { vim.fn.state(), vim.api.nvim_get_mode().mode:sub(1, 1) } + end + function _G.Run_timer() + local timer = vim.uv.new_timer() + timer:start(0, 0, function() + _G.Get_state_mode() + timer:close() + end) + end + ]]) + exec([[ + call setline(1, ['one', 'two', 'three']) + map ;; gg + set complete=. + func RunTimer() + call timer_start(0, {id -> v:lua.Get_state_mode()}) + endfunc + au Filetype foobar call v:lua.Get_state_mode() + ]]) + + -- Using a ":" command Vim is busy, thus "S" is returned + feed([[:call v:lua.Get_state_mode()<CR>]]) + eq({ 'S', 'n' }, exec_lua('return _G.res')) + + -- Using a timer callback + feed([[:call RunTimer()<CR>]]) + poke_eventloop() -- Process pending input + poke_eventloop() -- Process time_event + eq({ 'c', 'n' }, exec_lua('return _G.res')) + + -- Halfway a mapping + feed([[:call v:lua.Run_timer()<CR>;]]) + meths.get_mode() -- Process pending input and luv timer callback + feed(';') + eq({ 'mS', 'n' }, exec_lua('return _G.res')) + + -- An operator is pending + feed([[:call RunTimer()<CR>y]]) + poke_eventloop() -- Process pending input + poke_eventloop() -- Process time_event + feed('y') + eq({ 'oSc', 'n' }, exec_lua('return _G.res')) + + -- A register was specified + feed([[:call RunTimer()<CR>"r]]) + poke_eventloop() -- Process pending input + poke_eventloop() -- Process time_event + feed('yy') + eq({ 'oSc', 'n' }, exec_lua('return _G.res')) + + -- Insert mode completion + feed([[:call RunTimer()<CR>Got<C-N>]]) + poke_eventloop() -- Process pending input + poke_eventloop() -- Process time_event + feed('<Esc>') + eq({ 'aSc', 'i' }, exec_lua('return _G.res')) + + -- Autocommand executing + feed([[:set filetype=foobar<CR>]]) + eq({ 'xS', 'n' }, exec_lua('return _G.res')) + + -- messages scrolled + feed([[:call v:lua.Run_timer() | echo "one\ntwo\nthree"<CR>]]) + meths.get_mode() -- Process pending input and luv timer callback + feed('<CR>') + eq({ 'Ss', 'r' }, exec_lua('return _G.res')) + end) +end) diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index 7ada1c4bea..90aab48d61 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -210,8 +210,8 @@ describe('system()', function() end) it('prints verbose information', function() - nvim('set_option', 'shell', 'fake_shell') - nvim('set_option', 'shellcmdflag', 'cmdflag') + nvim('set_option_value', 'shell', 'fake_shell', {}) + nvim('set_option_value', 'shellcmdflag', 'cmdflag', {}) screen:try_resize(72, 14) feed(':4verbose echo system("echo hi")<cr>') @@ -335,12 +335,12 @@ describe('system()', function() if is_os('win') then eq("echoed\n", eval('system("echo echoed")')) else - eq("echoed", eval('system("echo -n echoed")')) + eq("echoed", eval('system("printf echoed")')) end end) it('to backgrounded command does not crash', function() -- This is indeterminate, just exercise the codepath. May get E5677. - feed_command('call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "echo -n echoed &")') + feed_command('call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "printf echoed &")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") if v_errnum then eq("E5677:", v_errnum) @@ -393,7 +393,7 @@ describe('system()', function() end) describe('with output containing NULs', function() - local fname = 'Xtest' + local fname = 'Xtest_functional_vimscript_system_nuls' before_each(create_file_with_nuls(fname)) after_each(delete_file(fname)) @@ -549,7 +549,7 @@ describe('systemlist()', function() end) describe('with output containing NULs', function() - local fname = 'Xtest' + local fname = 'Xtest_functional_vimscript_systemlist_nuls' before_each(function() command('set ff=unix') @@ -644,12 +644,12 @@ describe('shell :!', function() if is_os('win') then feed(':4verbose %!sort /R<cr>') screen:expect{ - any=[[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | Out%-File %-Encoding UTF8 .*; exit $LastExitCode"]] + any=[[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]] } else feed(':4verbose %!sort -r<cr>') screen:expect{ - any=[[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | Out%-File %-Encoding UTF8 .*; exit $LastExitCode"]] + any=[[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]] } end feed('<CR>') diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua index 1818a71ea2..a58cd6ae7f 100644 --- a/test/functional/vimscript/timer_spec.lua +++ b/test/functional/vimscript/timer_spec.lua @@ -251,15 +251,7 @@ describe('timers', function() :good^ | ]]) command('let g:val = 1') - - screen:expect{grid=[[ - | - {0:~ }| - {0:~ }| - {0:~ }| - {0:~ }| - :good^ | - ]], intermediate=true, timeout=load_adjust(200)} + screen:expect_unchanged(true, load_adjust(200)) eq(2, eval('g:val')) end) diff --git a/test/functional/vimscript/writefile_spec.lua b/test/functional/vimscript/writefile_spec.lua index 8c8da9dc88..88c19bd839 100644 --- a/test/functional/vimscript/writefile_spec.lua +++ b/test/functional/vimscript/writefile_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) -local lfs = require('lfs') +local luv = require('luv') +local mkdir = helpers.mkdir local clear = helpers.clear local eq = helpers.eq local funcs = helpers.funcs @@ -19,16 +20,16 @@ local ddname_tail = '2' local ddname = dname .. '/' .. ddname_tail before_each(function() - lfs.mkdir(dname) - lfs.mkdir(ddname) + mkdir(dname) + mkdir(ddname) clear() end) after_each(function() os.remove(fname) os.remove(dfname) - lfs.rmdir(ddname) - lfs.rmdir(dname) + luv.fs_rmdir(ddname) + luv.fs_rmdir(dname) end) describe('writefile()', function() @@ -144,13 +145,11 @@ describe('writefile()', function() pcall_err(command, ('call writefile(%s, "%s", "b")'):format(arg, fname))) end for _, args in ipairs({'[], %s, "b"', '[], "' .. fname .. '", %s'}) do - eq('Vim(call):E806: using Float as a String', - pcall_err(command, ('call writefile(%s)'):format(args:format('0.0')))) - eq('Vim(call):E730: using List as a String', + eq('Vim(call):E730: Using a List as a String', pcall_err(command, ('call writefile(%s)'):format(args:format('[]')))) - eq('Vim(call):E731: using Dictionary as a String', + eq('Vim(call):E731: Using a Dictionary as a String', pcall_err(command, ('call writefile(%s)'):format(args:format('{}')))) - eq('Vim(call):E729: using Funcref as a String', + eq('Vim(call):E729: Using a Funcref as a String', pcall_err(command, ('call writefile(%s)'):format(args:format('function("tr")')))) end eq('Vim(call):E5060: Unknown flag: «»', |