diff options
Diffstat (limited to 'test/functional')
18 files changed, 561 insertions, 62 deletions
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 71ec213b97..3d3a2bb046 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1,4 +1,3 @@ --- Sanity checks for buffer_* API calls via msgpack-rpc local helpers = require('test.functional.helpers')(after_each) local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq @@ -6,7 +5,7 @@ local curbufmeths, ok = helpers.curbufmeths, helpers.ok local funcs, request = helpers.funcs, helpers.request local NIL = helpers.NIL -describe('buffer_* functions', function() +describe('api/buf', function() before_each(clear) -- access deprecated functions diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 88e8c60560..78639d7ed7 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -1,4 +1,3 @@ --- Tests for nvim notifications local helpers = require('test.functional.helpers')(after_each) local eq, clear, eval, execute, nvim, next_message = helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.nvim, diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua index 47dede8b44..e10f30085f 100644 --- a/test/functional/api/tabpage_spec.lua +++ b/test/functional/api/tabpage_spec.lua @@ -1,4 +1,3 @@ --- Sanity checks for tabpage_* API calls via msgpack-rpc local helpers = require('test.functional.helpers')(after_each) local clear, nvim, tabpage, curtab, eq, ok = helpers.clear, helpers.nvim, helpers.tabpage, helpers.curtab, helpers.eq, @@ -8,7 +7,7 @@ local funcs = helpers.funcs local request = helpers.request local NIL = helpers.NIL -describe('tabpage_* functions', function() +describe('api/tabpage', function() before_each(clear) describe('list_wins and get_win', function() @@ -51,6 +50,22 @@ describe('tabpage_* functions', function() end) end) + describe('get_number', function() + it('works', function() + local tabs = nvim('list_tabpages') + eq(1, tabpage('get_number', tabs[1])) + + nvim('command', 'tabnew') + local tab1, tab2 = unpack(nvim('list_tabpages')) + eq(1, tabpage('get_number', tab1)) + eq(2, tabpage('get_number', tab2)) + + nvim('command', '-tabmove') + eq(2, tabpage('get_number', tab1)) + eq(1, tabpage('get_number', tab2)) + end) + end) + describe('is_valid', function() it('works', function() nvim('command', 'tabnew') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 6b30c0e81c..ce6c52e334 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,4 +1,3 @@ --- Sanity checks for vim_* API calls via msgpack-rpc local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local NIL = helpers.NIL @@ -9,10 +8,10 @@ local meths = helpers.meths local funcs = helpers.funcs local request = helpers.request -describe('vim_* functions', function() +describe('api', function() before_each(clear) - describe('command', function() + describe('nvim_command', function() it('works', function() local fname = helpers.tmpname() nvim('command', 'new') @@ -29,9 +28,18 @@ describe('vim_* functions', function() f:close() os.remove(fname) end) + + it("VimL error: fails (VimL error), does NOT update v:errmsg", function() + -- Most API methods return generic errors (or no error) if a VimL + -- expression fails; nvim_command returns the VimL error details. + local status, rv = pcall(nvim, "command", "bogus_command") + eq(false, status) -- nvim_command() failed. + eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned. + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + end) end) - describe('eval', function() + describe('nvim_eval', function() it('works', function() nvim('command', 'let g:v1 = "a"') nvim('command', 'let g:v2 = [1, 2, {"v3": 3}]') @@ -46,18 +54,41 @@ describe('vim_* functions', function() it('works under deprecated name', function() eq(2, request("vim_eval", "1+1")) end) + + it("VimL error: fails (generic error), does NOT update v:errmsg", function() + local status, rv = pcall(nvim, "eval", "bogus expression") + eq(false, status) -- nvim_eval() failed. + ok(nil ~= string.find(rv, "Failed to evaluate expression")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + end) end) - describe('call_function', function() + describe('nvim_call_function', function() it('works', function() nvim('call_function', 'setqflist', {{{ filename = 'something', lnum = 17}}, 'r'}) eq(17, nvim('call_function', 'getqflist', {})[1].lnum) eq(17, nvim('call_function', 'eval', {17})) eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'})) end) + + it("VimL error: fails (generic error), does NOT update v:errmsg", function() + local status, rv = pcall(nvim, "call_function", "bogus function", {"arg1"}) + eq(false, status) -- nvim_call_function() failed. + ok(nil ~= string.find(rv, "Error calling function")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + end) end) - describe('strwidth', function() + describe('nvim_input', function() + it("VimL error: does NOT fail, updates v:errmsg", function() + local status, _ = pcall(nvim, "input", ":call bogus_fn()<CR>") + local v_errnum = string.match(nvim("eval", "v:errmsg"), "E%d*:") + eq(true, status) -- nvim_input() did not fail. + eq("E117:", v_errnum) -- v:errmsg was updated. + end) + end) + + describe('nvim_strwidth', function() it('works', function() eq(3, nvim('strwidth', 'abc')) -- 6 + (neovim) @@ -70,7 +101,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_current_line', function() + describe('nvim_get_current_line, nvim_set_current_line', function() it('works', function() eq('', nvim('get_current_line')) nvim('set_current_line', 'abc') @@ -78,7 +109,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set,del}_var', function() + describe('nvim_get_var, nvim_set_var, nvim_del_var', function() it('works', function() nvim('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua')) @@ -109,7 +140,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_option', function() + describe('nvim_get_option, nvim_set_option', function() it('works', function() ok(nvim('get_option', 'equalalways')) nvim('set_option', 'equalalways', false) @@ -117,7 +148,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_current_buf and list_bufs', function() + describe('nvim_{get,set}_current_buf, nvim_list_bufs', function() it('works', function() eq(1, #nvim('list_bufs')) eq(nvim('list_bufs')[1], nvim('get_current_buf')) @@ -129,7 +160,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_current_win and list_wins', function() + describe('nvim_{get,set}_current_win, nvim_list_wins', function() it('works', function() eq(1, #nvim('list_wins')) eq(nvim('list_wins')[1], nvim('get_current_win')) @@ -142,7 +173,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_current_tabpage and list_tabpages', function() + describe('nvim_{get,set}_current_tabpage, nvim_list_tabpages', function() it('works', function() eq(1, #nvim('list_tabpages')) eq(nvim('list_tabpages')[1], nvim('get_current_tabpage')) @@ -161,7 +192,7 @@ describe('vim_* functions', function() end) end) - describe('replace_termcodes', function() + describe('nvim_replace_termcodes', function() it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function() eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true)) end) @@ -183,7 +214,7 @@ describe('vim_* functions', function() end) end) - describe('feedkeys', function() + describe('nvim_feedkeys', function() it('CSI escaping', function() local function on_setup() -- notice the special char(…) \xe2\80\xa6 @@ -210,7 +241,7 @@ describe('vim_* functions', function() end) end) - describe('err_write', function() + describe('nvim_err_write', function() local screen before_each(function() @@ -298,6 +329,94 @@ describe('vim_* functions', function() end) end) + describe('nvim_call_atomic', function() + it('works', function() + meths.buf_set_lines(0, 0, -1, true, {'first'}) + local req = { + {'nvim_get_current_line', {}}, + {'nvim_set_current_line', {'second'}}, + } + eq({{'first', NIL}, NIL}, meths.call_atomic(req)) + eq({'second'}, meths.buf_get_lines(0, 0, -1, true)) + end) + + it('allows multiple return values', function() + local req = { + {'nvim_set_var', {'avar', true}}, + {'nvim_set_var', {'bvar', 'string'}}, + {'nvim_get_var', {'avar'}}, + {'nvim_get_var', {'bvar'}}, + } + eq({{NIL, NIL, true, 'string'}, NIL}, meths.call_atomic(req)) + end) + + it('is aborted by errors in call', function() + local error_types = meths.get_api_info()[2].error_types + local req = { + {'nvim_set_var', {'one', 1}}, + {'nvim_buf_set_lines', {}}, + {'nvim_set_var', {'two', 2}}, + } + eq({{NIL}, {1, error_types.Exception.id, + 'Wrong number of arguments: expecting 5 but got 0'}}, + meths.call_atomic(req)) + eq(1, meths.get_var('one')) + eq(false, pcall(meths.get_var, 'two')) + + -- still returns all previous successful calls + req = { + {'nvim_set_var', {'avar', 5}}, + {'nvim_set_var', {'bvar', 'string'}}, + {'nvim_get_var', {'avar'}}, + {'nvim_buf_get_lines', {0, 10, 20, true}}, + {'nvim_get_var', {'bvar'}}, + } + eq({{NIL, NIL, 5}, {3, error_types.Validation.id, 'Index out of bounds'}}, + meths.call_atomic(req)) + + req = { + {'i_am_not_a_method', {'xx'}}, + {'nvim_set_var', {'avar', 10}}, + } + eq({{}, {0, error_types.Exception.id, 'Invalid method name'}}, + meths.call_atomic(req)) + eq(5, meths.get_var('avar')) + end) + + it('throws error on malformated arguments', function() + local req = { + {'nvim_set_var', {'avar', 1}}, + {'nvim_set_var'}, + {'nvim_set_var', {'avar', 2}}, + } + local status, err = pcall(meths.call_atomic, req) + eq(false, status) + ok(err:match(' All items in calls array must be arrays of size 2') ~= nil) + -- call before was done, but not after + eq(1, meths.get_var('avar')) + + req = { + {'nvim_set_var', {'bvar', {2,3}}}, + 12, + } + status, err = pcall(meths.call_atomic, req) + eq(false, status) + ok(err:match('All items in calls array must be arrays') ~= nil) + eq({2,3}, meths.get_var('bvar')) + + req = { + {'nvim_set_current_line', 'little line'}, + {'nvim_set_var', {'avar', 3}}, + } + status, err = pcall(meths.call_atomic, req) + eq(false, status) + ok(err:match('args must be Array') ~= nil) + -- call before was done, but not after + eq(1, meths.get_var('avar')) + eq({''}, meths.buf_get_lines(0, 0, -1, true)) + end) + end) + it('can throw exceptions', function() local status, err = pcall(nvim, 'get_option', 'invalid-option') eq(false, status) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 2c43e4db2c..465bda6bc9 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -1,4 +1,3 @@ --- Sanity checks for window_* API calls via msgpack-rpc local helpers = require('test.functional.helpers')(after_each) local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, @@ -29,7 +28,7 @@ local function is_visible(str) return false end -describe('window_* functions', function() +describe('api/win', function() before_each(clear) describe('get_buf', function() @@ -200,6 +199,30 @@ describe('window_* functions', function() end) end) + describe('get_number', function() + it('works', function() + local wins = nvim('list_wins') + eq(1, window('get_number', wins[1])) + + nvim('command', 'split') + local win1, win2 = unpack(nvim('list_wins')) + eq(1, window('get_number', win1)) + eq(2, window('get_number', win2)) + + nvim('command', 'wincmd J') + eq(2, window('get_number', win1)) + eq(1, window('get_number', win2)) + + nvim('command', 'tabnew') + local win3 = nvim('list_wins')[3] + -- First tab page + eq(2, window('get_number', win1)) + eq(1, window('get_number', win2)) + -- Second tab page + eq(1, window('get_number', win3)) + end) + end) + describe('is_valid', function() it('works', function() nvim('command', 'split') diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index adbe17d4b5..5872ebe8ee 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -121,14 +121,14 @@ describe('jobs', function() eq({'notification', 'exit', {0, 0}}, next_msg()) end) - it('can preserve newlines', function() + it('preserves newlines', function() nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") nvim('command', 'call jobsend(j, "a\\n\\nc\\n\\n\\n\\nb\\n\\n")') eq({'notification', 'stdout', {0, {'a', '', 'c', '', '', '', 'b', '', ''}}}, next_msg()) end) - it('can preserve NULs', function() + it('preserves NULs', function() nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") nvim('command', 'call jobsend(j, ["\n123\n", "abc\\nxyz\n", ""])') eq({'notification', 'stdout', {0, {'\n123\n', 'abc\nxyz\n', ''}}}, @@ -137,7 +137,7 @@ describe('jobs', function() eq({'notification', 'exit', {0, 0}}, next_msg()) end) - it('can avoid sending final newline', function() + it('avoids sending final newline', function() nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") nvim('command', 'call jobsend(j, ["some data", "without\nfinal nl"])') eq({'notification', 'stdout', {0, {'some data', 'without\nfinal nl'}}}, @@ -146,7 +146,7 @@ describe('jobs', function() eq({'notification', 'exit', {0, 0}}, next_msg()) end) - it('can close the job streams with jobclose', function() + it('closes the job streams with jobclose', function() nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") nvim('command', 'call jobclose(j, "stdin")') eq({'notification', 'exit', {0, 0}}, next_msg()) diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua index 10a6de6eb4..b5b481435a 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/eval/execute_spec.lua @@ -70,16 +70,16 @@ describe('execute()', function() end) it('silences command run inside', function() - local screen = Screen.new(20, 5) + local screen = Screen.new(40, 5) screen:attach() screen:set_default_attr_ids( {[0] = {bold=true, foreground=255}} ) feed(':let g:mes = execute("echon 42")<CR>') screen:expect([[ - ^ | - {0:~ }| - {0:~ }| - {0:~ }| - | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + :let g:mes = execute("echon 42") | ]]) eq('42', eval('g:mes')) end) diff --git a/test/functional/shell/viml_system_spec.lua b/test/functional/eval/system_spec.lua index b8de7cc86f..6393477260 100644 --- a/test/functional/shell/viml_system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -1,10 +1,7 @@ --- Specs for --- - `system()` --- - `systemlist()` - local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, feed, nvim = - helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.nvim +local eq, clear, eval, execute, feed, nvim = + helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.feed, + helpers.nvim local Screen = require('test.functional.ui.screen') @@ -120,12 +117,30 @@ describe('system()', function() it('returns the program output', function() eq("echoed", eval('system("echo -n echoed")')) end) + it('to backgrounded command does not crash', function() + -- This is indeterminate, just exercise the codepath. May get E5677. + execute('call system("echo -n echoed &")') + local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") + if v_errnum then + eq("E5677:", v_errnum) + end + eq(2, eval("1+1")) -- Still alive? + end) end) describe('passing input', function() it('returns the program output', function() eq("input", eval('system("cat -", "input")')) end) + it('to backgrounded command does not crash', function() + -- This is indeterminate, just exercise the codepath. May get E5677. + execute('call system("cat - &")') + local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") + if v_errnum then + eq("E5677:", v_errnum) + end + eq(2, eval("1+1")) -- Still alive? + end) end) describe('passing a lot of input', function() diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua index 295c763d74..fba9466b78 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/eval/timer_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run local clear, execute, funcs = helpers.clear, helpers.execute, helpers.funcs +local curbufmeths = helpers.curbufmeths describe('timers', function() before_each(function() @@ -62,19 +63,76 @@ describe('timers', function() end) it('are paused when event processing is disabled', function() - -- this is not the intended behavior, but at least there will - -- not be a burst of queued up callbacks - execute("call timer_start(50, 'MyHandler', {'repeat': 2})") + execute("call timer_start(50, 'MyHandler', {'repeat': -1})") run(nil, nil, nil, 100) local count = eval("g:val") + -- shows two line error message and thus invokes the return prompt. + -- if we start to allow event processing here, we need to change this test. + execute("throw 'fatal error'") + run(nil, nil, nil, 300) + feed("<cr>") + local diff = eval("g:val") - count + ok(0 <= diff and diff <= 4) + end) + + it('are triggered in blocking getchar() call', function() + execute("call timer_start(50, 'MyHandler', {'repeat': -1})") nvim_async("command", "let g:c = getchar()") run(nil, nil, nil, 300) feed("c") - local diff = eval("g:val") - count - ok(0 <= diff and diff <= 2) + local count = eval("g:val") + ok(count >= 5) eq(99, eval("g:c")) end) + it('can invoke redraw in blocking getchar() call', function() + local screen = Screen.new(40, 6) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold=true, foreground=Screen.colors.Blue}, + }) + + curbufmeths.set_lines(0, -1, true, {"ITEM 1", "ITEM 2"}) + source([[ + func! AddItem(timer) + call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3']) + redraw + endfunc + call timer_start(200, 'AddItem') + ]]) + nvim_async("command", "let g:c2 = getchar()") + + screen:expect([[ + ITEM 1 | + ITEM 2 | + {1:~ }| + {1:~ }| + {1:~ }| + ^ | + ]]) + + screen:sleep(200) + screen:expect([[ + ITEM 1 | + ITEM 2 | + ITEM 3 | + {1:~ }| + {1:~ }| + ^ | + ]]) + + feed("3") + eq(51, eval("g:c2")) + screen:expect([[ + ^ITEM 1 | + ITEM 2 | + ITEM 3 | + {1:~ }| + {1:~ }| + | + ]]) + end) + it('can be stopped', function() local t = eval("timer_start(50, 'MyHandler', {'repeat': -1})") eq(0,eval("g:val")) diff --git a/test/functional/shell/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua index a320e6d018..a320e6d018 100644 --- a/test/functional/shell/bang_filter_spec.lua +++ b/test/functional/ex_cmds/bang_filter_spec.lua diff --git a/test/functional/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index dc87312911..dc87312911 100644 --- a/test/functional/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index c1bc5d3140..4ac9f312ef 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -1,20 +1,26 @@ --- Specs for :write - local helpers = require('test.functional.helpers')(after_each) -local eq, eval, clear, write_file, execute, source = - helpers.eq, helpers.eval, helpers.clear, helpers.write_file, - helpers.execute, helpers.source +local eq, eval, clear, write_file, execute, source, insert = + helpers.eq, helpers.eval, helpers.clear, helpers.write_file, + helpers.execute, helpers.source, helpers.insert if helpers.pending_win32(pending) then return end describe(':write', function() - after_each(function() + local function cleanup() os.remove('test_bkc_file.txt') os.remove('test_bkc_link.txt') + os.remove('test_fifo') + end + before_each(function() + clear() + cleanup() + end) + after_each(function() + cleanup() end) it('&backupcopy=auto preserves symlinks', function() - clear('--cmd', 'set backupcopy=auto') + execute('set backupcopy=auto') write_file('test_bkc_file.txt', 'content0') execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") source([[ @@ -27,7 +33,7 @@ describe(':write', function() end) it('&backupcopy=no replaces symlink with new file', function() - clear('--cmd', 'set backupcopy=no') + execute('set backupcopy=no') write_file('test_bkc_file.txt', 'content0') execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt") source([[ @@ -38,4 +44,23 @@ describe(':write', function() eq(eval("['content0']"), eval("readfile('test_bkc_file.txt')")) eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')")) end) + + it("appends FIFO file", function() + if eval("executable('mkfifo')") == 0 then + pending('missing "mkfifo" command', function()end) + return + end + + local text = "some fifo text from write_spec" + assert(os.execute("mkfifo test_fifo")) + insert(text) + + -- Blocks until a consumer reads the FIFO. + execute("write >> test_fifo") + + -- Read the FIFO, this will unblock the :write above. + local fifo = assert(io.open("test_fifo")) + eq(text.."\n", fifo:read("*all")) + fifo:close() + end) end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 325f41e506..ff62b4de86 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -1,11 +1,13 @@ require('coxpcall') local lfs = require('lfs') -local ChildProcessStream = require('nvim.child_process_stream') -local SocketStream = require('nvim.socket_stream') -local TcpStream = require('nvim.tcp_stream') -local Session = require('nvim.session') local global_helpers = require('test.helpers') +-- nvim client: Found in .deps/usr/share/lua/<version>/nvim/ if "bundled". +local Session = require('nvim.session') +local TcpStream = require('nvim.tcp_stream') +local SocketStream = require('nvim.socket_stream') +local ChildProcessStream = require('nvim.child_process_stream') + local check_logs = global_helpers.check_logs local neq = global_helpers.neq local eq = global_helpers.eq @@ -134,10 +136,14 @@ local function stop() session:stop() end +-- Executes an ex-command. VimL errors manifest as client (lua) errors, but +-- v:errmsg will not be updated. local function nvim_command(cmd) request('nvim_command', cmd) end +-- Evaluates a VimL expression. +-- Fails on VimL error, but does not update v:errmsg. local function nvim_eval(expr) return request('nvim_eval', expr) end @@ -158,10 +164,14 @@ local os_name = (function() end) end)() +-- Executes a VimL function. +-- Fails on VimL error, but does not update v:errmsg. local function nvim_call(name, ...) return request('nvim_call_function', name, {...}) end +-- Sends user input to Nvim. +-- Does not fail on VimL error, but v:errmsg will be updated. local function nvim_feed(input) while #input > 0 do local written = request('nvim_input', input) @@ -279,6 +289,8 @@ local function insert(...) nvim_feed('<ESC>') end +-- Executes an ex-command by user input. Because nvim_input() is used, VimL +-- errors will not manifest as client (lua) errors. Use command() for that. local function execute(...) for _, v in ipairs({...}) do if v:sub(1, 1) ~= '/' then diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua index 42dd25023a..8a042be7f7 100644 --- a/test/functional/legacy/assert_spec.lua +++ b/test/functional/legacy/assert_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local nvim, call = helpers.meths, helpers.call local clear, eq = helpers.clear, helpers.eq local source, execute = helpers.source, helpers.execute +local exc_exec = helpers.exc_exec local function expected_errors(errors) eq(errors, nvim.get_vvar('errors')) @@ -62,6 +63,20 @@ describe('assert function:', function() call('assert_equal', 'true', 'false') expected_errors({"Expected 'true' but got 'false'"}) end) + + it('should change v:errors when expected is not equal to actual', function() + source([[ + function CheckAssert() + let s:v = {} + let s:x = {"a": s:v} + let s:v["b"] = s:x + let s:w = {"c": s:x, "d": ''} + call assert_equal(s:w, '') + endfunction + ]]) + eq('Vim(call):E724: unable to correctly dump variable with self-referencing container', + exc_exec('call CheckAssert()')) + end) end) -- assert_notequal({expected}, {actual}[, {msg}]) diff --git a/test/functional/legacy/quickfix_spec.lua b/test/functional/legacy/quickfix_spec.lua index df8f2625db..7657b8641b 100644 --- a/test/functional/legacy/quickfix_spec.lua +++ b/test/functional/legacy/quickfix_spec.lua @@ -277,6 +277,118 @@ describe('helpgrep', function() augroup! testgroup endfunction + + " This will test for problems in quickfix: + " A. incorrectly copying location lists which caused the location list to show + " a different name than the file that was actually being displayed. + " B. not reusing the window for which the location list window is opened but + " instead creating new windows. + " C. make sure that the location list window is not reused instead of the + " window it belongs to. + " + " Set up the test environment: + function! ReadTestProtocol(name) + let base = substitute(a:name, '\v^test://(.*)%(\.[^.]+)?', '\1', '') + let word = substitute(base, '\v(.*)\..*', '\1', '') + + setl modifiable + setl noreadonly + setl noswapfile + setl bufhidden=delete + %del _ + " For problem 2: + " 'buftype' has to be set to reproduce the constant opening of new windows + setl buftype=nofile + + call setline(1, word) + + setl nomodified + setl nomodifiable + setl readonly + exe 'doautocmd BufRead ' . substitute(a:name, '\v^test://(.*)', '\1', '') + endfunction + + function Test_locationlist() + enew + + augroup testgroup + au! + autocmd BufReadCmd test://* call ReadTestProtocol(expand("<amatch>")) + augroup END + + let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ] + + let qflist = [] + for word in words + call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', }) + " NOTE: problem 1: + " intentionally not setting 'lnum' so that the quickfix entries are not + " valid + call setloclist(0, qflist, ' ') + endfor + + " Test A + lrewind + enew + lopen + lnext + lnext + lnext + lnext + vert split + wincmd L + lopen + wincmd p + lnext + let fileName = expand("%") + wincmd p + let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '') + let fileName = substitute(fileName, '\\', '/', 'g') + let locationListFileName = substitute(locationListFileName, '\\', '/', 'g') + call assert_equal("test://bar.txt", fileName) + call assert_equal("test://bar.txt", locationListFileName) + + wincmd n | only + + " Test B: + lrewind + lopen + 2 + exe "normal \<CR>" + wincmd p + 3 + exe "normal \<CR>" + wincmd p + 4 + exe "normal \<CR>" + call assert_equal(2, winnr('$')) + wincmd n | only + + " Test C: + lrewind + lopen + " Let's move the location list window to the top to check whether it (the + " first window found) will be reused when we try to open new windows: + wincmd K + 2 + exe "normal \<CR>" + wincmd p + 3 + exe "normal \<CR>" + wincmd p + 4 + exe "normal \<CR>" + 1wincmd w + call assert_equal('quickfix', &buftype) + 2wincmd w + let bufferName = expand("%") + let bufferName = substitute(bufferName, '\\', '/', 'g') + call assert_equal('test://quux.txt', bufferName) + + wincmd n | only + + augroup! testgroup + endfunction ]]) end) @@ -339,4 +451,9 @@ describe('helpgrep', function() call('Test_locationlist_curwin_was_closed') expected_empty() end) + + it('checks locationlist protocol read', function() + call('Test_locationlist') + expected_empty() + end) end) diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua index 58d6c75940..045bdb0749 100644 --- a/test/functional/terminal/api_spec.lua +++ b/test/functional/terminal/api_spec.lua @@ -22,7 +22,7 @@ describe('api', function() -- Start the socket from the child nvim. child_session.feed_data(":echo serverstart('"..socket_name.."')\n") - -- Wait for socket creation by abusing expect(). + -- Wait for socket creation. screen:expect([[ {1: } | {4:~ }| @@ -37,6 +37,16 @@ describe('api', function() local socket_session2 = helpers.connect(socket_name) child_session.feed_data("i[tui] insert-mode") + -- Wait for stdin to be processed. + screen:expect([[ + [tui] insert-mode{1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) ok(socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true})) ok(socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true})) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 09b4eaa8d5..4247be0417 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -13,6 +13,42 @@ describe(':terminal', function() clear() screen = Screen.new(50, 4) screen:attach({rgb=false}) + end) + + it("does not interrupt Press-ENTER prompt #2748", function() + -- Ensure that :messages shows Press-ENTER. + source([[ + echomsg "msg1" + echomsg "msg2" + ]]) + -- Invoke a command that emits frequent terminal activity. + execute([[terminal while true; do echo X; done]]) + helpers.feed([[<C-\><C-N>]]) + screen:expect([[ + X | + X | + ^X | + | + ]]) + helpers.sleep(10) -- Let some terminal activity happen. + execute("messages") + screen:expect([[ + X | + msg1 | + msg2 | + Press ENTER or type command to continue^ | + ]]) + end) + +end) + +describe(':terminal (with fake shell)', function() + local screen + + before_each(function() + clear() + screen = Screen.new(50, 4) + screen:attach({rgb=false}) -- shell-test.c is a fake shell that prints its arguments and exits. nvim('set_option', 'shell', nvim_dir..'/shell-test') nvim('set_option', 'shellcmdflag', 'EXE') @@ -20,12 +56,12 @@ describe(':terminal', function() -- Invokes `:terminal {cmd}` using a fake shell (shell-test.c) which prints -- the {cmd} and exits immediately . - local function terminal_run_fake_shell_cmd(cmd) + local function terminal_with_fake_shell(cmd) execute("terminal "..(cmd and cmd or "")) end it('with no argument, acts like termopen()', function() - terminal_run_fake_shell_cmd() + terminal_with_fake_shell() wait() screen:expect([[ ready $ | @@ -36,7 +72,7 @@ describe(':terminal', function() end) it('executes a given command through the shell', function() - terminal_run_fake_shell_cmd('echo hi') + terminal_with_fake_shell('echo hi') wait() screen:expect([[ ready $ echo hi | @@ -47,7 +83,7 @@ describe(':terminal', function() end) it('allows quotes and slashes', function() - terminal_run_fake_shell_cmd([[echo 'hello' \ "world"]]) + terminal_with_fake_shell([[echo 'hello' \ "world"]]) wait() screen:expect([[ ready $ echo 'hello' \ "world" | @@ -66,14 +102,14 @@ describe(':terminal', function() end) it('ignores writes if the backing stream closes', function() - terminal_run_fake_shell_cmd() + terminal_with_fake_shell() helpers.feed('iiXXXXXXX') wait() -- Race: Though the shell exited (and streams were closed by SIGCHLD -- handler), :terminal cleanup is pending on the main-loop. -- This write should be ignored (not crash, #5445). helpers.feed('iiYYYYYYY') - wait() + eq(2, eval("1+1")) -- Still alive? end) end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index 0897c2d836..da3400fb27 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -317,6 +317,62 @@ describe('completion', function() end) end) + describe('completeopt+=noinsert does not add blank undo items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, ['foo', 'bar']) + return '' + endfunction + ]]) + execute('set completeopt+=noselect,noinsert') + execute('inoremap <right> <c-r>=TestComplete()<cr>') + end) + + local tests = { + ['<up>, <down>, <cr>'] = {'<down><cr>', '<up><cr>'}, + ['<c-n>, <c-p>, <c-y>'] = {'<c-n><c-y>', '<c-p><c-y>'}, + } + + for name, seq in pairs(tests) do + it('using ' .. name, function() + feed('iaaa<esc>') + feed('A<right>' .. seq[1] .. '<esc>') + feed('A<right><esc>A<right><esc>') + feed('A<cr>bbb<esc>') + feed('A<right>' .. seq[2] .. '<esc>') + feed('A<right><esc>A<right><esc>') + feed('A<cr>ccc<esc>') + feed('A<right>' .. seq[1] .. '<esc>') + feed('A<right><esc>A<right><esc>') + + local expected = { + {'foo', 'bar', 'foo'}, + {'foo', 'bar', 'ccc'}, + {'foo', 'bar'}, + {'foo', 'bbb'}, + {'foo'}, + {'aaa'}, + {''}, + } + + for i = 1, #expected do + if i > 1 then + feed('u') + end + eq(expected[i], eval('getline(1, "$")')) + end + + for i = #expected, 1, -1 do + if i < #expected then + feed('<c-r>') + end + eq(expected[i], eval('getline(1, "$")')) + end + end) + end + end) + describe("refresh:always", function() before_each(function() source([[ |