aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/functional/api/command_spec.lua80
-rw-r--r--test/functional/api/highlight_spec.lua9
-rw-r--r--test/functional/api/keymap_spec.lua2
-rw-r--r--test/functional/api/proc_spec.lua81
-rw-r--r--test/functional/api/server_requests_spec.lua18
-rw-r--r--test/functional/api/ui_spec.lua37
-rw-r--r--test/functional/api/vim_spec.lua175
-rw-r--r--test/functional/autocmd/cmdline_spec.lua9
-rw-r--r--test/functional/autocmd/dirchanged_spec.lua7
-rw-r--r--test/functional/autocmd/termclose_spec.lua31
-rw-r--r--test/functional/clipboard/clipboard_provider_spec.lua28
-rw-r--r--test/functional/core/exit_spec.lua13
-rw-r--r--test/functional/core/fileio_spec.lua68
-rw-r--r--test/functional/core/job_spec.lua209
-rw-r--r--test/functional/core/main_spec.lua131
-rw-r--r--test/functional/eval/buf_functions_spec.lua5
-rw-r--r--test/functional/eval/execute_spec.lua16
-rw-r--r--test/functional/eval/input_spec.lua25
-rw-r--r--test/functional/eval/match_functions_spec.lua7
-rw-r--r--test/functional/eval/server_spec.lua92
-rw-r--r--test/functional/eval/sort_spec.lua15
-rw-r--r--test/functional/eval/system_spec.lua40
-rw-r--r--test/functional/eval/uniq_spec.lua31
-rw-r--r--test/functional/ex_cmds/cmd_map_spec.lua772
-rw-r--r--test/functional/ex_cmds/digraphs_spec.lua38
-rw-r--r--test/functional/ex_cmds/oldfiles_spec.lua1
-rw-r--r--test/functional/helpers.lua89
-rw-r--r--test/functional/insert/insert_spec.lua41
-rw-r--r--test/functional/insert/last_inserted_spec.lua22
-rw-r--r--test/functional/legacy/077_mf_hash_grow_spec.lua3
-rw-r--r--test/functional/legacy/file_perm_spec.lua2
-rw-r--r--test/functional/lua/luaeval_spec.lua8
-rw-r--r--test/functional/lua/overrides_spec.lua1
-rw-r--r--test/functional/options/defaults_spec.lua276
-rw-r--r--test/functional/options/num_options_spec.lua97
-rw-r--r--test/functional/provider/nodejs_spec.lua16
-rw-r--r--test/functional/provider/python3_spec.lua19
-rw-r--r--test/functional/provider/ruby_spec.lua22
-rw-r--r--test/functional/shada/merging_spec.lua109
-rw-r--r--test/functional/shada/shada_spec.lua6
-rw-r--r--test/functional/terminal/edit_spec.lua1
-rw-r--r--test/functional/terminal/ex_terminal_spec.lua5
-rw-r--r--test/functional/terminal/mouse_spec.lua16
-rw-r--r--test/functional/terminal/tui_spec.lua24
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua17
-rw-r--r--test/functional/ui/cmdline_spec.lua21
-rw-r--r--test/functional/ui/cursor_spec.lua4
-rw-r--r--test/functional/ui/highlight_spec.lua135
-rw-r--r--test/functional/ui/inccommand_spec.lua103
-rw-r--r--test/functional/ui/input_spec.lua6
-rw-r--r--test/functional/ui/mouse_spec.lua1
-rw-r--r--test/functional/ui/output_spec.lua18
-rw-r--r--test/functional/ui/screen_basic_spec.lua157
-rw-r--r--test/functional/ui/searchhl_spec.lua34
-rw-r--r--test/helpers.lua137
-rw-r--r--test/includes/pre/sys/stat.h4
-rw-r--r--test/unit/helpers.lua2
-rw-r--r--test/unit/os/fileio_spec.lua8
-rw-r--r--test/unit/os/fs_spec.lua24
-rw-r--r--test/unit/path_spec.lua22
-rw-r--r--test/unit/preprocess.lua58
-rw-r--r--test/unit/undo_spec.lua215
62 files changed, 3193 insertions, 470 deletions
diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua
new file mode 100644
index 0000000000..f3f9f93649
--- /dev/null
+++ b/test/functional/api/command_spec.lua
@@ -0,0 +1,80 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local NIL = helpers.NIL
+local clear = helpers.clear
+local command = helpers.command
+local curbufmeths = helpers.curbufmeths
+local eq = helpers.eq
+local expect_err = helpers.expect_err
+local meths = helpers.meths
+local source = helpers.source
+
+describe('nvim_get_commands', function()
+ local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, }
+ local cmd_dict2 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='pwd', name='Pwd', nargs='?', range=NIL, register=false, script_id=0, }
+ before_each(clear)
+
+ it('gets empty list if no commands were defined', function()
+ eq({}, meths.get_commands({builtin=false}))
+ end)
+
+ it('validates input', function()
+ expect_err('builtin=true not implemented', meths.get_commands,
+ {builtin=true})
+ expect_err('unexpected key: foo', meths.get_commands,
+ {foo='blah'})
+ end)
+
+ it('gets global user-defined commands', function()
+ -- Define a command.
+ command('command -nargs=1 Hello echo "Hello World"')
+ eq({Hello=cmd_dict}, meths.get_commands({builtin=false}))
+ -- Define another command.
+ command('command -nargs=? Pwd pwd');
+ eq({Hello=cmd_dict, Pwd=cmd_dict2}, meths.get_commands({builtin=false}))
+ -- Delete a command.
+ command('delcommand Pwd')
+ eq({Hello=cmd_dict}, meths.get_commands({builtin=false}))
+ end)
+
+ it('gets buffer-local user-defined commands', function()
+ -- Define a buffer-local command.
+ command('command -buffer -nargs=1 Hello echo "Hello World"')
+ eq({Hello=cmd_dict}, curbufmeths.get_commands({builtin=false}))
+ -- Define another buffer-local command.
+ command('command -buffer -nargs=? Pwd pwd')
+ eq({Hello=cmd_dict, Pwd=cmd_dict2}, curbufmeths.get_commands({builtin=false}))
+ -- Delete a command.
+ command('delcommand Pwd')
+ eq({Hello=cmd_dict}, curbufmeths.get_commands({builtin=false}))
+
+ -- {builtin=true} always returns empty for buffer-local case.
+ eq({}, curbufmeths.get_commands({builtin=true}))
+ end)
+
+ it('gets various command attributes', function()
+ local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='0', range='10', register=false, script_id=0, }
+ local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, script_id=1, }
+ local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253Q2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, }
+ local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253Q3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, }
+ local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253Q4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, script_id=4, }
+ source([[
+ command -complete=custom,ListUsers -nargs=+ Finger !finger <args>
+ ]])
+ eq({Finger=cmd1}, meths.get_commands({builtin=false}))
+ command('command -complete=dir -addr=arguments -count=10 TestCmd pwd <args>')
+ eq({Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
+
+ source([[
+ command -bang -nargs=* Cmd2 call <SID>foo(<q-args>)
+ ]])
+ source([[
+ command -bar -nargs=0 Cmd3 call <SID>ohyeah()
+ ]])
+ source([[
+ command -register Cmd4 call <SID>just_great()
+ ]])
+ -- TODO(justinmk): Order is stable but undefined. Sort before return?
+ eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
+ end)
+end)
diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua
index 2297a0760f..fed53a3dfd 100644
--- a/test/functional/api/highlight_spec.lua
+++ b/test/functional/api/highlight_spec.lua
@@ -99,5 +99,14 @@ describe('highlight api',function()
eq(false, err)
eq('Invalid highlight name: ',
string.match(emsg, 'Invalid.*'))
+
+ -- Test "standout" attribute. #8054
+ eq({ underline = true, },
+ meths.get_hl_by_name('cursorline', 0));
+ command('hi CursorLine cterm=standout,underline term=standout,underline gui=standout,underline')
+ command('set cursorline')
+ eq({ underline = true, standout = true, },
+ meths.get_hl_by_name('cursorline', 0));
+
end)
end)
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua
index 3a10f9c60f..f52372bee3 100644
--- a/test/functional/api/keymap_spec.lua
+++ b/test/functional/api/keymap_spec.lua
@@ -11,7 +11,7 @@ local source = helpers.source
local shallowcopy = global_helpers.shallowcopy
-describe('get_keymap', function()
+describe('nvim_get_keymap', function()
before_each(clear)
-- Basic mapping and table to be used to describe results
diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua
new file mode 100644
index 0000000000..d99c26b6c2
--- /dev/null
+++ b/test/functional/api/proc_spec.lua
@@ -0,0 +1,81 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local eq = helpers.eq
+local funcs = helpers.funcs
+local iswin = helpers.iswin
+local nvim_argv = helpers.nvim_argv
+local ok = helpers.ok
+local request = helpers.request
+local retry = helpers.retry
+local NIL = helpers.NIL
+
+describe('api', function()
+ before_each(clear)
+
+ describe('nvim_get_proc_children', function()
+ it('returns child process ids', function()
+ local this_pid = funcs.getpid()
+
+ local job1 = funcs.jobstart(nvim_argv)
+ retry(nil, nil, function()
+ eq(1, #request('nvim_get_proc_children', this_pid))
+ end)
+
+ local job2 = funcs.jobstart(nvim_argv)
+ retry(nil, nil, function()
+ eq(2, #request('nvim_get_proc_children', this_pid))
+ end)
+
+ funcs.jobstop(job1)
+ retry(nil, nil, function()
+ eq(1, #request('nvim_get_proc_children', this_pid))
+ end)
+
+ funcs.jobstop(job2)
+ retry(nil, nil, function()
+ eq(0, #request('nvim_get_proc_children', this_pid))
+ end)
+ end)
+
+ it('validates input', function()
+ local status, rv = pcall(request, "nvim_get_proc_children", -1)
+ eq(false, status)
+ eq("Invalid pid: -1", string.match(rv, "Invalid.*"))
+
+ status, rv = pcall(request, "nvim_get_proc_children", 0)
+ eq(false, status)
+ eq("Invalid pid: 0", string.match(rv, "Invalid.*"))
+
+ -- Assume PID 99999 does not exist.
+ status, rv = pcall(request, "nvim_get_proc_children", 99999)
+ eq(true, status)
+ eq({}, rv)
+ end)
+ end)
+
+ describe('nvim_get_proc', function()
+ it('returns process info', function()
+ local pid = funcs.getpid()
+ local pinfo = request('nvim_get_proc', pid)
+ eq((iswin() and 'nvim.exe' or 'nvim'), pinfo.name)
+ ok(pinfo.pid == pid)
+ ok(type(pinfo.ppid) == 'number' and pinfo.ppid ~= pid)
+ end)
+
+ it('validates input', function()
+ local status, rv = pcall(request, "nvim_get_proc", -1)
+ eq(false, status)
+ eq("Invalid pid: -1", string.match(rv, "Invalid.*"))
+
+ status, rv = pcall(request, "nvim_get_proc", 0)
+ eq(false, status)
+ eq("Invalid pid: 0", string.match(rv, "Invalid.*"))
+
+ -- Assume PID 99999 does not exist.
+ status, rv = pcall(request, "nvim_get_proc", 99999)
+ eq(true, status)
+ eq(NIL, rv)
+ end)
+ end)
+end)
diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua
index 18229b54ff..e79a60fb10 100644
--- a/test/functional/api/server_requests_spec.lua
+++ b/test/functional/api/server_requests_spec.lua
@@ -11,6 +11,7 @@ local ok = helpers.ok
local meths = helpers.meths
local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv
local set_session = helpers.set_session
+local expect_err = helpers.expect_err
describe('server -> client', function()
local cid
@@ -221,9 +222,8 @@ describe('server -> client', function()
end)
it('returns an error if the request failed', function()
- local status, err = pcall(eval, "rpcrequest(vim, 'does-not-exist')")
- eq(false, status)
- ok(nil ~= string.match(err, 'Failed to evaluate expression'))
+ expect_err('Vim:Invalid method name',
+ eval, "rpcrequest(vim, 'does-not-exist')")
end)
end)
@@ -250,7 +250,7 @@ describe('server -> client', function()
end)
after_each(function()
- funcs.jobstop(jobid)
+ pcall(funcs.jobstop, jobid)
end)
if helpers.pending_win32(pending) then return end
@@ -261,7 +261,7 @@ describe('server -> client', function()
eq({'notification', 'pong', {}}, next_msg())
eq("done!",funcs.rpcrequest(jobid, "write_stderr", "fluff\n"))
eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_msg())
- funcs.rpcrequest(jobid, "exit")
+ pcall(funcs.rpcrequest, jobid, "exit")
eq({'notification', 'stderr', {0, {''}}}, next_msg())
eq({'notification', 'exit', {0, 0}}, next_msg())
end)
@@ -308,8 +308,8 @@ describe('server -> client', function()
it('via ipv4 address', function()
local server = spawn(nvim_argv)
set_session(server)
- local address = funcs.serverstart("127.0.0.1:")
- if #address == 0 then
+ local status, address = pcall(funcs.serverstart, "127.0.0.1:")
+ if not status then
pending('no ipv4 stack', function() end)
return
end
@@ -320,8 +320,8 @@ describe('server -> client', function()
it('via ipv6 address', function()
local server = spawn(nvim_argv)
set_session(server)
- local address = funcs.serverstart('::1:')
- if #address == 0 then
+ local status, address = pcall(funcs.serverstart, '::1:')
+ if not status then
pending('no ipv6 stack', function() end)
return
end
diff --git a/test/functional/api/ui_spec.lua b/test/functional/api/ui_spec.lua
new file mode 100644
index 0000000000..b028a50b02
--- /dev/null
+++ b/test/functional/api/ui_spec.lua
@@ -0,0 +1,37 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local eq = helpers.eq
+local eval = helpers.eval
+local expect_err = helpers.expect_err
+local meths = helpers.meths
+local request = helpers.request
+
+describe('nvim_ui_attach()', function()
+ before_each(function()
+ clear()
+ end)
+ it('handles very large width/height #2180', function()
+ local screen = Screen.new(999, 999)
+ screen:attach()
+ eq(999, eval('&lines'))
+ eq(999, eval('&columns'))
+ end)
+ it('invalid option returns error', function()
+ expect_err('No such UI option: foo',
+ meths.ui_attach, 80, 24, { foo={'foo'} })
+ end)
+ it('validates channel arg', function()
+ expect_err('UI not attached to channel: 1',
+ request, 'nvim_ui_try_resize', 40, 10)
+ expect_err('UI not attached to channel: 1',
+ request, 'nvim_ui_set_option', 'rgb', true)
+ expect_err('UI not attached to channel: 1',
+ request, 'nvim_ui_detach')
+
+ local screen = Screen.new()
+ screen:attach({rgb=false})
+ expect_err('UI already attached to channel: 1',
+ request, 'nvim_ui_attach', 40, 10, { rgb=false })
+ end)
+end)
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index bd56161a54..71fa6632c7 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -4,17 +4,20 @@ local global_helpers = require('test.helpers')
local NIL = helpers.NIL
local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq
+local command = helpers.command
+local eval = helpers.eval
+local funcs = helpers.funcs
+local iswin = helpers.iswin
+local meth_pcall = helpers.meth_pcall
+local meths = helpers.meths
local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed
local os_name = helpers.os_name
-local meths = helpers.meths
-local funcs = helpers.funcs
local request = helpers.request
-local meth_pcall = helpers.meth_pcall
-local command = helpers.command
-local iswin = helpers.iswin
+local source = helpers.source
-local intchar2lua = global_helpers.intchar2lua
+local expect_err = global_helpers.expect_err
local format_string = global_helpers.format_string
+local intchar2lua = global_helpers.intchar2lua
local mergedicts_copy = global_helpers.mergedicts_copy
describe('api', function()
@@ -38,20 +41,20 @@ describe('api', function()
os.remove(fname)
end)
- it("parse error: fails (specific 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.
+ it('VimL validation error: fails with specific error', function()
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.
+ eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated.
+ eq('', eval('v:exception'))
end)
- it("runtime error: fails (specific error)", function()
+ it('VimL execution error: fails with specific error', function()
local status, rv = pcall(nvim, "command_output", "buffer 23487")
eq(false, status) -- nvim_command() failed.
eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*"))
- eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ eq('', eval('v:errmsg')) -- v:errmsg was not updated.
+ eq('', eval('v:exception'))
end)
end)
@@ -73,6 +76,8 @@ describe('api', function()
it('captures command output', function()
eq('this is\nspinal tap',
nvim('command_output', [[echo "this is\nspinal tap"]]))
+ eq('no line ending!',
+ nvim('command_output', [[echon "no line ending!"]]))
end)
it('captures empty command output', function()
@@ -105,21 +110,21 @@ describe('api', function()
eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]]))
end)
- it("parse error: fails (specific error), does NOT update v:errmsg", function()
+ it('VimL validation error: fails with specific error', function()
local status, rv = pcall(nvim, "command_output", "bogus commannnd")
eq(false, status) -- nvim_command_output() failed.
eq("E492: Not an editor command: bogus commannnd",
string.match(rv, "E%d*:.*"))
- eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ eq('', eval('v:errmsg')) -- v:errmsg was not updated.
-- Verify NO hit-enter prompt.
eq({mode='n', blocking=false}, nvim("get_mode"))
end)
- it("runtime error: fails (specific error)", function()
+ it('VimL execution error: fails with specific error', function()
local status, rv = pcall(nvim, "command_output", "buffer 42")
eq(false, status) -- nvim_command_output() failed.
eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*"))
- eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated.
+ eq('', eval('v:errmsg')) -- v:errmsg was not updated.
-- Verify NO hit-enter prompt.
eq({mode='n', blocking=false}, nvim("get_mode"))
end)
@@ -141,11 +146,10 @@ describe('api', 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.
+ it("VimL error: returns error details, does NOT update v:errmsg", function()
+ expect_err('E121: Undefined variable: bogus', request,
+ 'nvim_eval', 'bogus expression')
+ eq('', eval('v:errmsg')) -- v:errmsg was not updated.
end)
end)
@@ -157,11 +161,100 @@ describe('api', function()
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.
+ it("VimL validation error: returns specific error, does NOT update v:errmsg", function()
+ expect_err('E117: Unknown function: bogus function', request,
+ 'nvim_call_function', 'bogus function', {'arg1'})
+ expect_err('E119: Not enough arguments for function: atan', request,
+ 'nvim_call_function', 'atan', {})
+ eq('', eval('v:exception'))
+ eq('', eval('v:errmsg')) -- v:errmsg was not updated.
+ end)
+
+ it("VimL error: returns error details, does NOT update v:errmsg", function()
+ expect_err('E808: Number or Float required', request,
+ 'nvim_call_function', 'atan', {'foo'})
+ expect_err('Invalid channel stream "xxx"', request,
+ 'nvim_call_function', 'chanclose', {999, 'xxx'})
+ expect_err('E900: Invalid channel id', request,
+ 'nvim_call_function', 'chansend', {999, 'foo'})
+ eq('', eval('v:exception'))
+ eq('', eval('v:errmsg')) -- v:errmsg was not updated.
+ end)
+
+ it("VimL exception: returns exception details, does NOT update v:errmsg", function()
+ source([[
+ function! Foo() abort
+ throw 'wtf'
+ endfunction
+ ]])
+ expect_err('wtf', request,
+ 'nvim_call_function', 'Foo', {})
+ eq('', eval('v:exception'))
+ eq('', eval('v:errmsg')) -- v:errmsg was not updated.
+ end)
+
+ it('validates args', function()
+ local too_many_args = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' }
+ source([[
+ function! Foo(...) abort
+ echo a:000
+ endfunction
+ ]])
+ -- E740
+ expect_err('Function called with too many arguments', request,
+ 'nvim_call_function', 'Foo', too_many_args)
+ end)
+ end)
+
+ describe('nvim_call_dict_function', function()
+ it('invokes VimL dict function', function()
+ source([[
+ function! F(name) dict
+ return self.greeting.', '.a:name.'!'
+ endfunction
+ let g:test_dict_fn = { 'greeting':'Hello', 'F':function('F') }
+
+ let g:test_dict_fn2 = { 'greeting':'Hi' }
+ function g:test_dict_fn2.F2(name)
+ return self.greeting.', '.a:name.' ...'
+ endfunction
+ ]])
+
+ -- :help Dictionary-function
+ eq('Hello, World!', nvim('call_dict_function', 'g:test_dict_fn', 'F', {'World'}))
+ -- Funcref is sent as NIL over RPC.
+ eq({ greeting = 'Hello', F = NIL }, nvim('get_var', 'test_dict_fn'))
+
+ -- :help numbered-function
+ eq('Hi, Moon ...', nvim('call_dict_function', 'g:test_dict_fn2', 'F2', {'Moon'}))
+ -- Funcref is sent as NIL over RPC.
+ eq({ greeting = 'Hi', F2 = NIL }, nvim('get_var', 'test_dict_fn2'))
+
+ -- Function specified via RPC dict.
+ source('function! G() dict\n return "@".(self.result)."@"\nendfunction')
+ eq('@it works@', nvim('call_dict_function', { result = 'it works', G = 'G'}, 'G', {}))
+ end)
+
+ it('validates args', function()
+ command('let g:d={"baz":"zub","meep":[]}')
+ expect_err('Not found: bogus', request,
+ 'nvim_call_dict_function', 'g:d', 'bogus', {1,2})
+ expect_err('Not a function: baz', request,
+ 'nvim_call_dict_function', 'g:d', 'baz', {1,2})
+ expect_err('Not a function: meep', request,
+ 'nvim_call_dict_function', 'g:d', 'meep', {1,2})
+ expect_err('E117: Unknown function: f', request,
+ 'nvim_call_dict_function', { f = '' }, 'f', {1,2})
+ expect_err('Not a function: f', request,
+ 'nvim_call_dict_function', "{ 'f': '' }", 'f', {1,2})
+ expect_err('dict argument type must be String or Dictionary', request,
+ 'nvim_call_dict_function', 42, 'f', {1,2})
+ expect_err('Failed to evaluate dict expression', request,
+ 'nvim_call_dict_function', 'foo', 'f', {1,2})
+ expect_err('dict not found', request,
+ 'nvim_call_dict_function', '42', 'f', {1,2})
+ expect_err('Invalid %(empty%) function name', request,
+ 'nvim_call_dict_function', "{ 'f': '' }", '', {1,2})
end)
end)
@@ -289,6 +382,21 @@ describe('api', function()
eq(false, status)
ok(err:match('Invalid option name') ~= nil)
end)
+
+ it('updates where the option was last set from', function()
+ nvim('set_option', 'equalalways', false)
+ local status, rv = pcall(nvim, 'command_output',
+ 'verbose set equalalways?')
+ eq(true, status)
+ ok(nil ~= string.find(rv, 'noequalalways\n'..
+ '\tLast set from API client %(channel id %d+%)'))
+
+ nvim('execute_lua', 'vim.api.nvim_set_option("equalalways", true)', {})
+ status, rv = pcall(nvim, 'command_output',
+ 'verbose set equalalways?')
+ eq(true, status)
+ eq(' equalalways\n\tLast set from Lua', rv)
+ end)
end)
describe('nvim_{get,set}_current_buf, nvim_list_bufs', function()
@@ -579,7 +687,8 @@ describe('api', function()
screen:set_default_attr_ids({
[0] = {bold=true, foreground=Screen.colors.Blue},
[1] = {foreground = Screen.colors.White, background = Screen.colors.Red},
- [2] = {bold = true, foreground = Screen.colors.SeaGreen}
+ [2] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [3] = {bold = true, reverse = true},
})
end)
@@ -600,11 +709,11 @@ describe('api', function()
it('shows return prompt when more than &cmdheight lines', function()
nvim_async('err_write', 'something happened\nvery bad\n')
screen:expect([[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {3: }|
{1:something happened} |
{1:very bad} |
{2:Press ENTER or type command to continue}^ |
@@ -614,9 +723,9 @@ describe('api', function()
it('shows return prompt after all lines are shown', function()
nvim_async('err_write', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n')
screen:expect([[
+ |
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {3: }|
{1:FAILURE} |
{1:ERROR} |
{1:EXCEPTION} |
@@ -644,11 +753,11 @@ describe('api', function()
-- shows up to &cmdheight lines
nvim_async('err_write', 'more fail\ntoo fail\n')
screen:expect([[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {3: }|
{1:more fail} |
{1:too fail} |
{2:Press ENTER or type command to continue}^ |
diff --git a/test/functional/autocmd/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua
index 8ea086fb46..3f0504d02f 100644
--- a/test/functional/autocmd/cmdline_spec.lua
+++ b/test/functional/autocmd/cmdline_spec.lua
@@ -59,24 +59,25 @@ describe('cmdline autocommands', function()
[1] = {bold = true, foreground = Screen.colors.Blue1},
[2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
[3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [4] = {bold = true, reverse = true},
})
command("autocmd CmdlineEnter * echoerr 'FAIL'")
command("autocmd CmdlineLeave * echoerr 'very error'")
feed(':')
screen:expect([[
+ |
{1:~ }|
{1:~ }|
{1:~ }|
- {1:~ }|
- {1:~ }|
+ {4: }|
: |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} |
:^ |
]])
feed("put ='lorem ipsum'<cr>")
screen:expect([[
- {1:~ }|
- {1:~ }|
+ |
+ {4: }|
: |
{2:E5500: autocmd has thrown an exception: Vim(echoerr):FAIL} |
:put ='lorem ipsum' |
diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua
index 63cf0bc410..7979e91a4c 100644
--- a/test/functional/autocmd/dirchanged_spec.lua
+++ b/test/functional/autocmd/dirchanged_spec.lua
@@ -154,4 +154,11 @@ describe('autocmd DirChanged', function()
eq('Failed to change directory', string.match(err, ': (.*)'))
eq({cwd=dirs[2], scope='global'}, eval('g:ev'))
end)
+
+ it('works when local to buffer', function()
+ command('let g:triggered = 0')
+ command('autocmd DirChanged <buffer> let g:triggered = 1')
+ command('cd '..dirs[1])
+ eq(1, eval('g:triggered'))
+ end)
end)
diff --git a/test/functional/autocmd/termclose_spec.lua b/test/functional/autocmd/termclose_spec.lua
index 9918cbe4fa..db4e5379d0 100644
--- a/test/functional/autocmd/termclose_spec.lua
+++ b/test/functional/autocmd/termclose_spec.lua
@@ -1,3 +1,4 @@
+local luv = require('luv')
local helpers = require('test.functional.helpers')(after_each)
local clear, command, nvim, nvim_dir =
@@ -31,7 +32,7 @@ describe('TermClose event', function()
end)
it('kills job trapping SIGTERM', function()
- if helpers.pending_win32(pending) then return end
+ if iswin() then return end
nvim('set_option', 'shell', 'sh')
nvim('set_option', 'shellcmdflag', '-c')
command([[ let g:test_job = jobstart('trap "" TERM && echo 1 && sleep 60', { ]]
@@ -39,17 +40,19 @@ describe('TermClose event', function()
.. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]])
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end)
- local start = os.time()
+ luv.update_time()
+ local start = luv.now()
command('call jobstop(g:test_job)')
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end)
- local duration = os.time() - start
- -- nvim starts sending SIGTERM after KILL_TIMEOUT_MS
- ok(duration >= 2)
- ok(duration <= 4) -- <= 2 + delta because of slow CI
+ luv.update_time()
+ local duration = luv.now() - start
+ -- Nvim begins SIGTERM after KILL_TIMEOUT_MS.
+ ok(duration >= 2000)
+ ok(duration <= 4000) -- Epsilon for slow CI
end)
- it('kills pty job trapping SIGHUP and SIGTERM', function()
- if helpers.pending_win32(pending) then return end
+ it('kills PTY job trapping SIGHUP and SIGTERM', function()
+ if iswin() then return end
nvim('set_option', 'shell', 'sh')
nvim('set_option', 'shellcmdflag', '-c')
command([[ let g:test_job = jobstart('trap "" HUP TERM && echo 1 && sleep 60', { ]]
@@ -58,13 +61,15 @@ describe('TermClose event', function()
.. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]])
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end)
- local start = os.time()
+ luv.update_time()
+ local start = luv.now()
command('call jobstop(g:test_job)')
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end)
- local duration = os.time() - start
- -- nvim starts sending kill after 2*KILL_TIMEOUT_MS
- ok(duration >= 4)
- ok(duration <= 7) -- <= 4 + delta because of slow CI
+ luv.update_time()
+ local duration = luv.now() - start
+ -- Nvim begins SIGKILL after (2 * KILL_TIMEOUT_MS).
+ ok(duration >= 4000)
+ ok(duration <= 7000) -- Epsilon for slow CI
end)
it('reports the correct <abuf>', function()
diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua
index a3ea3b568f..a40c080a6d 100644
--- a/test/functional/clipboard/clipboard_provider_spec.lua
+++ b/test/functional/clipboard/clipboard_provider_spec.lua
@@ -83,7 +83,14 @@ local function basic_register_test(noblock)
end
describe('clipboard', function()
- before_each(clear)
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(72, 4)
+ screen:attach()
+ command("set display-=msgsep")
+ end)
it('unnamed register works without provider', function()
eq('"', eval('v:register'))
@@ -92,8 +99,6 @@ describe('clipboard', function()
it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184',
function()
- local screen = Screen.new(72, 4)
- screen:attach()
command("let g:clipboard = 'bogus'")
feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END')
screen:expect([[
@@ -106,8 +111,6 @@ describe('clipboard', function()
it('`:redir @+>|bogus_cmd|redir END` + invalid g:clipboard must not recurse #7184',
function()
- local screen = Screen.new(72, 4)
- screen:attach()
command("let g:clipboard = 'bogus'")
feed_command('redir @+> | bogus_cmd | redir END')
screen:expect([[
@@ -123,8 +126,6 @@ describe('clipboard', function()
eq('', eval('provider#clipboard#Executable()'))
eq('clipboard: invalid g:clipboard', eval('provider#clipboard#Error()'))
- local screen = Screen.new(72, 4)
- screen:attach()
command("let g:clipboard = 'bogus'")
-- Explicit clipboard attempt, should show a hint message.
feed_command('let @+="foo"')
@@ -493,10 +494,10 @@ describe('clipboard', function()
feed_command("let g:test_clip['+'] = ['such', 'plus', 'stuff']")
feed_command("registers")
screen:expect([[
- ~ |
- ~ |
- ~ |
- ~ |
+ |
+ {0:~ }|
+ {0:~ }|
+ {4: }|
:registers |
{1:--- Registers ---} |
"* some{2:^J}star data{2:^J} |
@@ -504,10 +505,11 @@ describe('clipboard', function()
": let g:test_clip['+'] = ['such', 'plus', 'stuff'] |
{3:Press ENTER or type command to continue}^ |
]], {
+ [0] = {bold = true, foreground = Screen.colors.Blue},
[1] = {bold = true, foreground = Screen.colors.Fuchsia},
[2] = {foreground = Screen.colors.Blue},
- [3] = {bold = true, foreground = Screen.colors.SeaGreen}},
- {{bold = true, foreground = Screen.colors.Blue}})
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [4] = {bold = true, reverse = true}})
feed('<cr>') -- clear out of Press ENTER screen
end)
diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua
index 188a6a2c11..80c65e4544 100644
--- a/test/functional/core/exit_spec.lua
+++ b/test/functional/core/exit_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local command = helpers.command
+local feed_command = helpers.feed_command
local eval = helpers.eval
local eq = helpers.eq
local run = helpers.run
@@ -33,6 +34,18 @@ describe('v:exiting', function()
end
run(on_request, nil, on_setup)
end)
+ it('is 0 on exit from ex-mode involving try-catch', function()
+ local function on_setup()
+ command('autocmd VimLeavePre * call rpcrequest('..cid..', "")')
+ command('autocmd VimLeave * call rpcrequest('..cid..', "")')
+ feed_command('call feedkey("Q")','try', 'call NoFunction()', 'catch', 'echo "bye"', 'endtry', 'quit')
+ end
+ local function on_request()
+ eq(0, eval('v:exiting'))
+ return ''
+ end
+ run(on_request, nil, on_setup)
+ end)
end)
describe(':cquit', function()
diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua
new file mode 100644
index 0000000000..09533e4e60
--- /dev/null
+++ b/test/functional/core/fileio_spec.lua
@@ -0,0 +1,68 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local feed = helpers.feed
+local funcs = helpers.funcs
+local nvim_prog = helpers.nvim_prog
+local request = helpers.request
+local retry = helpers.retry
+local rmdir = helpers.rmdir
+local sleep = helpers.sleep
+
+describe('fileio', function()
+ before_each(function()
+ end)
+ after_each(function()
+ command(':qall!')
+ os.remove('Xtest_startup_shada')
+ os.remove('Xtest_startup_file1')
+ os.remove('Xtest_startup_file2')
+ rmdir('Xtest_startup_swapdir')
+ end)
+
+ it('fsync() codepaths #8304', function()
+ clear({ args={ '-i', 'Xtest_startup_shada',
+ '--cmd', 'set directory=Xtest_startup_swapdir' } })
+
+ -- These cases ALWAYS force fsync (regardless of 'fsync' option):
+
+ -- 1. Idle (CursorHold) with modified buffers (+ 'swapfile').
+ command('write Xtest_startup_file1')
+ feed('ifoo<esc>h')
+ command('write')
+ eq(0, request('nvim__stats').fsync) -- 'nofsync' is the default.
+ command('set swapfile')
+ command('set updatetime=1')
+ feed('izub<esc>h') -- File is 'modified'.
+ sleep(3) -- Allow 'updatetime' to expire.
+ retry(3, nil, function()
+ eq(1, request('nvim__stats').fsync)
+ end)
+ command('set updatetime=9999')
+
+ -- 2. Exit caused by deadly signal (+ 'swapfile').
+ local j = funcs.jobstart({ nvim_prog, '-u', 'NONE', '-i',
+ 'Xtest_startup_shada', '--headless',
+ '-c', 'set swapfile',
+ '-c', 'write Xtest_startup_file2',
+ '-c', 'put =localtime()', })
+ sleep(10) -- Let Nvim start.
+ funcs.jobstop(j) -- Send deadly signal.
+
+ -- 3. SIGPWR signal.
+ -- ??
+
+ -- 4. Explicit :preserve command.
+ command('preserve')
+ eq(2, request('nvim__stats').fsync)
+
+ -- 5. Enable 'fsync' option, write file.
+ command('set fsync')
+ feed('ibaz<esc>h')
+ command('write')
+ eq(4, request('nvim__stats').fsync)
+ end)
+end)
+
diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua
index c5326aedfe..e90339b0cd 100644
--- a/test/functional/core/job_spec.lua
+++ b/test/functional/core/job_spec.lua
@@ -6,6 +6,10 @@ local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim
helpers.nvim_dir, helpers.ok, helpers.source,
helpers.write_file, helpers.mkdir, helpers.rmdir
local command = helpers.command
+local funcs = helpers.funcs
+local retry = helpers.retry
+local meths = helpers.meths
+local NIL = helpers.NIL
local wait = helpers.wait
local iswin = helpers.iswin
local get_pathsep = helpers.get_pathsep
@@ -15,14 +19,18 @@ local expect_twostreams = helpers.expect_twostreams
local expect_msg_seq = helpers.expect_msg_seq
local Screen = require('test.functional.ui.screen')
+-- Kill process with given pid
+local function os_kill(pid)
+ return os.execute((iswin()
+ and 'taskkill /f /t /pid '..pid..' > nul'
+ or 'kill -9 '..pid..' > /dev/null'))
+end
+
describe('jobs', function()
local channel
before_each(function()
clear()
- if iswin() then
- helpers.set_shell_powershell()
- end
channel = nvim('get_api_info')[1]
nvim('set_var', 'channel', channel)
source([[
@@ -48,7 +56,7 @@ describe('jobs', function()
it('uses &shell and &shellcmdflag if passed a string', function()
nvim('command', "let $VAR = 'abc'")
if iswin() then
- nvim('command', "let j = jobstart('echo $env:VAR', g:job_opts)")
+ nvim('command', "let j = jobstart('echo %VAR%', g:job_opts)")
else
nvim('command', "let j = jobstart('echo $VAR', g:job_opts)")
end
@@ -60,7 +68,7 @@ describe('jobs', function()
it('changes to given / directory', function()
nvim('command', "let g:job_opts.cwd = '/'")
if iswin() then
- nvim('command', "let j = jobstart('(Get-Location).Path', g:job_opts)")
+ nvim('command', "let j = jobstart('cd', g:job_opts)")
else
nvim('command', "let j = jobstart('pwd', g:job_opts)")
end
@@ -75,7 +83,7 @@ describe('jobs', function()
mkdir(dir)
nvim('command', "let g:job_opts.cwd = '" .. dir .. "'")
if iswin() then
- nvim('command', "let j = jobstart('(Get-Location).Path', g:job_opts)")
+ nvim('command', "let j = jobstart('cd', g:job_opts)")
else
nvim('command', "let j = jobstart('pwd', g:job_opts)")
end
@@ -99,7 +107,7 @@ describe('jobs', function()
local _, err = pcall(function()
nvim('command', "let g:job_opts.cwd = '" .. dir .. "'")
if iswin() then
- nvim('command', "let j = jobstart('pwd|%{$_.Path}', g:job_opts)")
+ nvim('command', "let j = jobstart('cd', g:job_opts)")
else
nvim('command', "let j = jobstart('pwd', g:job_opts)")
end
@@ -130,10 +138,8 @@ describe('jobs', function()
end)
it('invokes callbacks when the job writes and exits', function()
- -- TODO: hangs on Windows
- if helpers.pending_win32(pending) then return end
nvim('command', "let g:job_opts.on_stderr = function('OnEvent')")
- nvim('command', [[call jobstart('echo ""', g:job_opts)]])
+ nvim('command', [[call jobstart(has('win32') ? 'echo:' : 'echo', g:job_opts)]])
expect_twostreams({{'notification', 'stdout', {0, {'', ''}}},
{'notification', 'stdout', {0, {''}}}},
{{'notification', 'stderr', {0, {''}}}})
@@ -141,15 +147,28 @@ describe('jobs', function()
end)
it('allows interactive commands', function()
- if helpers.pending_win32(pending) then return end -- TODO: Need `cat`.
- nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
+ nvim('command', "let j = jobstart(has('win32') ? ['find', '/v', ''] : ['cat', '-'], g:job_opts)")
neq(0, eval('j'))
nvim('command', 'call jobsend(j, "abc\\n")')
eq({'notification', 'stdout', {0, {'abc', ''}}}, next_msg())
nvim('command', 'call jobsend(j, "123\\nxyz\\n")')
- eq({'notification', 'stdout', {0, {'123', 'xyz', ''}}}, next_msg())
+ expect_msg_seq(
+ { {'notification', 'stdout', {0, {'123', 'xyz', ''}}}
+ },
+ -- Alternative sequence:
+ { {'notification', 'stdout', {0, {'123', ''}}},
+ {'notification', 'stdout', {0, {'xyz', ''}}}
+ }
+ )
nvim('command', 'call jobsend(j, [123, "xyz", ""])')
- eq({'notification', 'stdout', {0, {'123', 'xyz', ''}}}, next_msg())
+ expect_msg_seq(
+ { {'notification', 'stdout', {0, {'123', 'xyz', ''}}}
+ },
+ -- Alternative sequence:
+ { {'notification', 'stdout', {0, {'123', ''}}},
+ {'notification', 'stdout', {0, {'xyz', ''}}}
+ }
+ )
nvim('command', "call jobstop(j)")
eq({'notification', 'stdout', {0, {''}}}, next_msg())
eq({'notification', 'exit', {0, 0}}, next_msg())
@@ -222,16 +241,14 @@ describe('jobs', function()
end)
it('closes the job streams with jobclose', function()
- if helpers.pending_win32(pending) then return end -- TODO: Need `cat`.
- nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
+ nvim('command', "let j = jobstart(has('win32') ? ['find', '/v', ''] : ['cat', '-'], g:job_opts)")
nvim('command', 'call jobclose(j, "stdin")')
eq({'notification', 'stdout', {0, {''}}}, next_msg())
- eq({'notification', 'exit', {0, 0}}, next_msg())
+ eq({'notification', 'exit', {0, iswin() and 1 or 0}}, next_msg())
end)
it("disallows jobsend on a job that closed stdin", function()
- if helpers.pending_win32(pending) then return end -- TODO: Need `cat`.
- nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
+ nvim('command', "let j = jobstart(has('win32') ? ['find', '/v', ''] : ['cat', '-'], g:job_opts)")
nvim('command', 'call jobclose(j, "stdin")')
eq(false, pcall(function()
nvim('command', 'call jobsend(j, ["some data"])')
@@ -244,8 +261,7 @@ describe('jobs', function()
end)
it('disallows jobstop twice on the same job', function()
- if helpers.pending_win32(pending) then return end -- TODO: Need `cat`.
- nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
+ nvim('command', "let j = jobstart(has('win32') ? ['find', '/v', ''] : ['cat', '-'], g:job_opts)")
neq(0, eval('j'))
eq(true, pcall(eval, "jobstop(j)"))
eq(false, pcall(eval, "jobstop(j)"))
@@ -256,41 +272,49 @@ describe('jobs', function()
end)
it('can get the pid value using getpid', function()
- if helpers.pending_win32(pending) then return end -- TODO: Need `cat`.
- nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)")
+ nvim('command', "let j = jobstart(has('win32') ? ['find', '/v', ''] : ['cat', '-'], g:job_opts)")
local pid = eval('jobpid(j)')
- eq(0,os.execute('ps -p '..pid..' > /dev/null'))
+ neq(NIL, meths.get_proc(pid))
nvim('command', 'call jobstop(j)')
eq({'notification', 'stdout', {0, {''}}}, next_msg())
- eq({'notification', 'exit', {0, 0}}, next_msg())
- neq(0,os.execute('ps -p '..pid..' > /dev/null'))
+ if iswin() then
+ expect_msg_seq(
+ -- win64
+ { {'notification', 'exit', {0, 1}}
+ },
+ -- win32
+ { {'notification', 'exit', {0, 15}}
+ }
+ )
+ else
+ eq({'notification', 'exit', {0, 0}}, next_msg())
+ end
+ eq(NIL, meths.get_proc(pid))
end)
it("do not survive the exit of nvim", function()
- if helpers.pending_win32(pending) then return end
-- use sleep, which doesn't die on stdin close
- nvim('command', "let g:j = jobstart(['sleep', '1000'], g:job_opts)")
+ nvim('command', "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)")
local pid = eval('jobpid(g:j)')
- eq(0,os.execute('ps -p '..pid..' > /dev/null'))
+ neq(NIL, meths.get_proc(pid))
clear()
- neq(0,os.execute('ps -p '..pid..' > /dev/null'))
+ eq(NIL, meths.get_proc(pid))
end)
it('can survive the exit of nvim with "detach"', function()
- if helpers.pending_win32(pending) then return end
nvim('command', 'let g:job_opts.detach = 1')
- nvim('command', "let g:j = jobstart(['sleep', '1000'], g:job_opts)")
+ nvim('command', "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)")
local pid = eval('jobpid(g:j)')
- eq(0,os.execute('ps -p '..pid..' > /dev/null'))
+ neq(NIL, meths.get_proc(pid))
clear()
- eq(0,os.execute('ps -p '..pid..' > /dev/null'))
+ neq(NIL, meths.get_proc(pid))
-- clean up after ourselves
- os.execute('kill -9 '..pid..' > /dev/null')
+ eq(0, os_kill(pid))
end)
it('can pass user data to the callback', function()
nvim('command', 'let g:job_opts.user = {"n": 5, "s": "str", "l": [1]}')
- nvim('command', [[call jobstart('echo "foo"', g:job_opts)]])
+ nvim('command', [[call jobstart('echo foo', g:job_opts)]])
local data = {n = 5, s = 'str', l = {1}}
expect_msg_seq(
{ {'notification', 'stdout', {data, {'foo', ''}}},
@@ -308,14 +332,14 @@ describe('jobs', function()
it('can omit data callbacks', function()
nvim('command', 'unlet g:job_opts.on_stdout')
nvim('command', 'let g:job_opts.user = 5')
- nvim('command', [[call jobstart('echo "foo"', g:job_opts)]])
+ nvim('command', [[call jobstart('echo foo', g:job_opts)]])
eq({'notification', 'exit', {5, 0}}, next_msg())
end)
it('can omit exit callback', function()
nvim('command', 'unlet g:job_opts.on_exit')
nvim('command', 'let g:job_opts.user = 5')
- nvim('command', [[call jobstart('echo "foo"', g:job_opts)]])
+ nvim('command', [[call jobstart('echo foo', g:job_opts)]])
expect_msg_seq(
{ {'notification', 'stdout', {5, {'foo', ''} } },
{'notification', 'stdout', {5, {''} } },
@@ -397,15 +421,14 @@ describe('jobs', function()
end)
it('does not repeat output with slow output handlers', function()
- if helpers.pending_win32(pending) then return end
source([[
let d = {'data': []}
function! d.on_stdout(job, data, event) dict
- call add(self.data, a:data)
+ call add(self.data, Normalize(a:data))
sleep 200m
endfunction
if has('win32')
- let cmd = '1,2,3,4,5 | foreach-object -process {echo $_; sleep 0.1}'
+ let cmd = 'for /L %I in (1,1,5) do @(echo %I& ping -n 2 127.0.0.1 > nul)'
else
let cmd = ['sh', '-c', 'for i in $(seq 1 5); do echo $i; sleep 0.1; done']
endif
@@ -424,7 +447,7 @@ describe('jobs', function()
endfunction
let Callback = function('PrintArgs', ["foo", "bar"])
let g:job_opts = {'on_stdout': Callback}
- call jobstart('echo "some text"', g:job_opts)
+ call jobstart('echo some text', g:job_opts)
]])
expect_msg_seq(
{ {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}},
@@ -444,7 +467,7 @@ describe('jobs', function()
return {id, data, event -> rpcnotify(g:channel, '1', a1, a2, Normalize(data), event)}
endfun
let g:job_opts = {'on_stdout': MkFun()}
- call jobstart('echo "some text"', g:job_opts)
+ call jobstart('echo some text', g:job_opts)
]])
expect_msg_seq(
{ {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}},
@@ -459,7 +482,7 @@ describe('jobs', function()
it('jobstart() works when closure passed directly to `jobstart`', function()
source([[
let g:job_opts = {'on_stdout': {id, data, event -> rpcnotify(g:channel, '1', 'foo', 'bar', Normalize(data), event)}}
- call jobstart('echo "some text"', g:job_opts)
+ call jobstart('echo some text', g:job_opts)
]])
expect_msg_seq(
{ {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}},
@@ -472,9 +495,20 @@ describe('jobs', function()
end)
describe('jobwait', function()
+ before_each(function()
+ if iswin() then
+ helpers.set_shell_powershell()
+ end
+ end)
+
it('returns a list of status codes', function()
source([[
- call rpcnotify(g:channel, 'wait', jobwait([
+ call rpcnotify(g:channel, 'wait', jobwait(has('win32') ? [
+ \ jobstart('Start-Sleep -Milliseconds 100; exit 4'),
+ \ jobstart('Start-Sleep -Milliseconds 300; exit 5'),
+ \ jobstart('Start-Sleep -Milliseconds 500; exit 6'),
+ \ jobstart('Start-Sleep -Milliseconds 700; exit 7')
+ \ ] : [
\ jobstart('sleep 0.10; exit 4'),
\ jobstart('sleep 0.110; exit 5'),
\ jobstart('sleep 0.210; exit 6'),
@@ -494,7 +528,12 @@ describe('jobs', function()
endif
let g:exits += 1
endfunction
- call jobwait([
+ call jobwait(has('win32') ? [
+ \ jobstart('Start-Sleep -Milliseconds 100; exit 5', g:dict),
+ \ jobstart('Start-Sleep -Milliseconds 300; exit 5', g:dict),
+ \ jobstart('Start-Sleep -Milliseconds 500; exit 5', g:dict),
+ \ jobstart('Start-Sleep -Milliseconds 700; exit 5', g:dict)
+ \ ] : [
\ jobstart('sleep 0.010; exit 5', g:dict),
\ jobstart('sleep 0.030; exit 5', g:dict),
\ jobstart('sleep 0.050; exit 5', g:dict),
@@ -507,7 +546,12 @@ describe('jobs', function()
it('will return status codes in the order of passed ids', function()
source([[
- call rpcnotify(g:channel, 'wait', jobwait([
+ call rpcnotify(g:channel, 'wait', jobwait(has('win32') ? [
+ \ jobstart('Start-Sleep -Milliseconds 700; exit 4'),
+ \ jobstart('Start-Sleep -Milliseconds 500; exit 5'),
+ \ jobstart('Start-Sleep -Milliseconds 300; exit 6'),
+ \ jobstart('Start-Sleep -Milliseconds 100; exit 7')
+ \ ] : [
\ jobstart('sleep 0.070; exit 4'),
\ jobstart('sleep 0.050; exit 5'),
\ jobstart('sleep 0.030; exit 6'),
@@ -521,7 +565,7 @@ describe('jobs', function()
source([[
call rpcnotify(g:channel, 'wait', jobwait([
\ -10,
- \ jobstart('sleep 0.01; exit 5'),
+ \ jobstart((has('win32') ? 'Start-Sleep -Milliseconds 100' : 'sleep 0.01').'; exit 5'),
\ ]))
]])
eq({'notification', 'wait', {{-3, 5}}}, next_msg())
@@ -530,7 +574,9 @@ describe('jobs', function()
it('will return -2 when interrupted without timeout', function()
feed_command('call rpcnotify(g:channel, "ready") | '..
'call rpcnotify(g:channel, "wait", '..
- 'jobwait([jobstart("sleep 10; exit 55")]))')
+ 'jobwait([jobstart("'..
+ (iswin() and 'Start-Sleep 10' or 'sleep 10')..
+ '; exit 55")]))')
eq({'notification', 'ready', {}}, next_msg())
feed('<c-c>')
eq({'notification', 'wait', {{-2}}}, next_msg())
@@ -539,7 +585,9 @@ describe('jobs', function()
it('will return -2 when interrupted with timeout', function()
feed_command('call rpcnotify(g:channel, "ready") | '..
'call rpcnotify(g:channel, "wait", '..
- 'jobwait([jobstart("sleep 10; exit 55")], 10000))')
+ 'jobwait([jobstart("'..
+ (iswin() and 'Start-Sleep 10' or 'sleep 10')..
+ '; exit 55")], 10000))')
eq({'notification', 'ready', {}}, next_msg())
feed('<c-c>')
eq({'notification', 'wait', {{-2}}}, next_msg())
@@ -594,20 +642,22 @@ describe('jobs', function()
end)
describe('with timeout argument', function()
- if helpers.pending_win32(pending) then return end
it('will return -1 if the wait timed out', function()
source([[
call rpcnotify(g:channel, 'wait', jobwait([
\ jobstart('exit 4'),
- \ jobstart('sleep 10; exit 5'),
- \ ], 100))
+ \ jobstart((has('win32') ? 'Start-Sleep 10' : 'sleep 10').'; exit 5'),
+ \ ], has('win32') ? 3000 : 100))
]])
eq({'notification', 'wait', {{4, -1}}}, next_msg())
end)
it('can pass 0 to check if a job exists', function()
source([[
- call rpcnotify(g:channel, 'wait', jobwait([
+ call rpcnotify(g:channel, 'wait', jobwait(has('win32') ? [
+ \ jobstart('Start-Sleep -Milliseconds 50; exit 4'),
+ \ jobstart('Start-Sleep -Milliseconds 300; exit 5'),
+ \ ] : [
\ jobstart('sleep 0.05; exit 4'),
\ jobstart('sleep 0.3; exit 5'),
\ ], 0))
@@ -629,13 +679,60 @@ describe('jobs', function()
end)
it('cannot have both rpc and pty options', function()
- if helpers.pending_win32(pending) then return end -- TODO: Need `cat`.
command("let g:job_opts.pty = v:true")
command("let g:job_opts.rpc = v:true")
- local _, err = pcall(command, "let j = jobstart(['cat', '-'], g:job_opts)")
+ local _, err = pcall(command, "let j = jobstart(has('win32') ? ['find', '/v', ''] : ['cat', '-'], g:job_opts)")
ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil)
end)
+ it('does not crash when repeatedly failing to start shell', function()
+ source([[
+ set shell=nosuchshell
+ func! DoIt()
+ call jobstart('true')
+ call jobstart('true')
+ endfunc
+ ]])
+ -- The crash only triggered if both jobs are cleaned up on the same event
+ -- loop tick. This is also prevented by try-block, so feed must be used.
+ feed_command("call DoIt()")
+ feed('<cr>') -- press RETURN
+ eq(2,eval('1+1'))
+ end)
+
+ it('jobstop() kills entire process tree #6530', function()
+ -- XXX: Using `nvim` isn't a good test, it reaps its children on exit.
+ -- local c = 'call jobstart([v:progpath, "-u", "NONE", "-i", "NONE", "--headless"])'
+ -- local j = eval("jobstart([v:progpath, '-u', 'NONE', '-i', 'NONE', '--headless', '-c', '"
+ -- ..c.."', '-c', '"..c.."'])")
+
+ -- Create child with several descendants.
+ local sleep_cmd = (iswin()
+ and 'ping -n 31 127.0.0.1'
+ or 'sleep 30')
+ local j = eval("jobstart('"..sleep_cmd..' | '..sleep_cmd..' | '..sleep_cmd.."')")
+ local ppid = funcs.jobpid(j)
+ local children
+ retry(nil, nil, function()
+ children = meths.get_proc_children(ppid)
+ eq(3, #children)
+ end)
+ -- Assert that nvim_get_proc() sees the children.
+ for _, child_pid in ipairs(children) do
+ local info = meths.get_proc(child_pid)
+ -- eq((iswin() and 'nvim.exe' or 'nvim'), info.name)
+ eq(ppid, info.ppid)
+ end
+ -- Kill the root of the tree.
+ funcs.jobstop(j)
+ -- Assert that the children were killed.
+ retry(nil, nil, function()
+ for _, child_pid in ipairs(children) do
+ eq(NIL, meths.get_proc(child_pid))
+ end
+ end)
+ end)
+
describe('running tty-test program', function()
if helpers.pending_win32(pending) then return end
local function next_chunk()
diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua
new file mode 100644
index 0000000000..cd396ef820
--- /dev/null
+++ b/test/functional/core/main_spec.lua
@@ -0,0 +1,131 @@
+local lfs = require('lfs')
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local eq = helpers.eq
+local feed = helpers.feed
+local eval = helpers.eval
+local clear = helpers.clear
+local funcs = helpers.funcs
+local nvim_prog = helpers.nvim_prog
+local write_file = helpers.write_file
+
+local function nvim_prog_abs()
+ -- system(['build/bin/nvim']) does not work for whatever reason. It needs to
+ -- either be executable searched in $PATH or something starting with / or ./.
+ if nvim_prog:match('[/\\]') then
+ return funcs.fnamemodify(nvim_prog, ':p')
+ else
+ return nvim_prog
+ end
+end
+
+describe('Command-line option', function()
+ describe('-s', function()
+ local fname = 'Xtest-functional-core-main-s'
+ local fname_2 = fname .. '.2'
+ local nonexistent_fname = fname .. '.nonexistent'
+ local dollar_fname = '$' .. fname
+ before_each(function()
+ clear()
+ os.remove(fname)
+ os.remove(dollar_fname)
+ end)
+ after_each(function()
+ os.remove(fname)
+ os.remove(dollar_fname)
+ end)
+ it('treats - as stdin', function()
+ eq(nil, lfs.attributes(fname))
+ funcs.system(
+ {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless',
+ '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
+ '-s', '-', fname},
+ {':call setline(1, "42")', ':wqall!', ''})
+ eq(0, eval('v:shell_error'))
+ local attrs = lfs.attributes(fname)
+ eq(#('42\n'), attrs.size)
+ end)
+ it('does not expand $VAR', function()
+ eq(nil, lfs.attributes(fname))
+ eq(true, not not dollar_fname:find('%$%w+'))
+ write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n')
+ funcs.system(
+ {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless',
+ '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
+ '-s', dollar_fname, fname})
+ eq(0, eval('v:shell_error'))
+ local attrs = lfs.attributes(fname)
+ eq(#('100500\n'), attrs.size)
+ end)
+ it('does not crash after reading from stdin in non-headless mode', function()
+ if helpers.pending_win32(pending) then return end
+ local screen = Screen.new(40, 8)
+ screen:attach()
+ funcs.termopen({
+ nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE',
+ '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
+ '-s', '-'
+ })
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] 0,0-1 All}|
+ |
+ |
+ ]], {
+ [1] = {foreground = 4210943, special = Screen.colors.Grey0},
+ [2] = {special = Screen.colors.Grey0, bold = true, reverse = true}
+ })
+ feed('i:cq<CR>')
+ screen:expect([[
+ |
+ [Process exited 1] |
+ |
+ |
+ |
+ |
+ |
+ -- TERMINAL -- |
+ ]])
+ --[=[ Example of incorrect output:
+ screen:expect([[
+ ^nvim: /var/tmp/portage/dev-libs/libuv-1.|
+ 10.2/work/libuv-1.10.2/src/unix/core.c:5|
+ 19: uv__close: Assertion `fd > STDERR_FI|
+ LENO' failed. |
+ |
+ [Process exited 6] |
+ |
+ |
+ ]])
+ ]=]
+ end)
+ it('errors out when trying to use nonexistent file with -s', function()
+ eq(
+ 'Cannot open for reading: "'..nonexistent_fname..'": no such file or directory\n',
+ funcs.system(
+ {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless',
+ '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
+ '--cmd', 'language C',
+ '-s', nonexistent_fname}))
+ eq(2, eval('v:shell_error'))
+ end)
+ it('errors out when trying to use -s twice', function()
+ write_file(fname, ':call setline(1, "1")\n:wqall!\n')
+ write_file(dollar_fname, ':call setline(1, "2")\n:wqall!\n')
+ eq(
+ 'Attempt to open script file again: "-s '..dollar_fname..'"\n',
+ funcs.system(
+ {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless',
+ '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
+ '--cmd', 'language C',
+ '-s', fname, '-s', dollar_fname, fname_2}))
+ eq(2, eval('v:shell_error'))
+ eq(nil, lfs.attributes(fname_2))
+ end)
+ end)
+end)
diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua
index 7de58766b9..c0e264d4c9 100644
--- a/test/functional/eval/buf_functions_spec.lua
+++ b/test/functional/eval/buf_functions_spec.lua
@@ -15,6 +15,7 @@ local curwinmeths = helpers.curwinmeths
local curtabmeths = helpers.curtabmeths
local get_pathsep = helpers.get_pathsep
local rmdir = helpers.rmdir
+local expect_err = helpers.expect_err
local fname = 'Xtest-functional-eval-buf_functions'
local fname2 = fname .. '.2'
@@ -296,8 +297,8 @@ describe('setbufvar() function', function()
eq('Vim(call):E461: Illegal variable name: b:',
exc_exec('call setbufvar(1, "", 0)'))
eq(true, bufmeths.get_var(buf1, 'number'))
- funcs.setbufvar(1, 'changedtick', true)
- -- eq(true, bufmeths.get_var(buf1, 'changedtick'))
+ expect_err('Vim:E46: Cannot change read%-only variable "b:changedtick"',
+ funcs.setbufvar, 1, 'changedtick', true)
eq(2, funcs.getbufvar(1, 'changedtick'))
end)
end)
diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua
index 183884a51e..925e311c7d 100644
--- a/test/functional/eval/execute_spec.lua
+++ b/test/functional/eval/execute_spec.lua
@@ -106,16 +106,22 @@ describe('execute()', function()
end)
it('does not corrupt the command display #5422', function()
- local screen = Screen.new(70, 5)
+ local screen = Screen.new(70, 7)
screen:attach()
feed(':echo execute("hi ErrorMsg")<CR>')
screen:expect([[
- ~ |
- ~ |
+ |
+ {1:~ }|
+ {1:~ }|
+ {2: }|
:echo execute("hi ErrorMsg") |
ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red |
- Press ENTER or type command to continue^ |
- ]])
+ {3:Press ENTER or type command to continue}^ |
+ ]], {
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {bold = true, reverse = true},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ })
feed('<CR>')
end)
diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua
index 1e6b107c60..777f49462d 100644
--- a/test/functional/eval/input_spec.lua
+++ b/test/functional/eval/input_spec.lua
@@ -58,6 +58,7 @@ before_each(function()
RBP2={background=Screen.colors.Yellow},
RBP3={background=Screen.colors.Green},
RBP4={background=Screen.colors.Blue},
+ SEP={bold = true, reverse = true},
})
end)
@@ -65,9 +66,9 @@ describe('input()', function()
it('works with multiline prompts', function()
feed([[:call input("Test\nFoo")<CR>]])
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {SEP: }|
Test |
Foo^ |
]])
@@ -75,9 +76,9 @@ describe('input()', function()
it('works with multiline prompts and :echohl', function()
feed([[:echohl Test | call input("Test\nFoo")<CR>]])
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {SEP: }|
{T:Test} |
{T:Foo}^ |
]])
@@ -242,17 +243,17 @@ describe('input()', function()
it('is not hidden by :silent', function()
feed([[:silent call input('Foo: ')<CR>]])
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {SEP: }|
Foo: ^ |
|
]])
feed('Bar')
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {SEP: }|
Foo: Bar^ |
|
]])
@@ -263,9 +264,9 @@ describe('inputdialog()', function()
it('works with multiline prompts', function()
feed([[:call inputdialog("Test\nFoo")<CR>]])
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {SEP: }|
Test |
Foo^ |
]])
@@ -273,9 +274,9 @@ describe('inputdialog()', function()
it('works with multiline prompts and :echohl', function()
feed([[:echohl Test | call inputdialog("Test\nFoo")<CR>]])
screen:expect([[
+ |
{EOB:~ }|
- {EOB:~ }|
- {EOB:~ }|
+ {SEP: }|
{T:Test} |
{T:Foo}^ |
]])
diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua
index 7989b22b5e..0ec465a34c 100644
--- a/test/functional/eval/match_functions_spec.lua
+++ b/test/functional/eval/match_functions_spec.lua
@@ -6,6 +6,7 @@ local clear = helpers.clear
local funcs = helpers.funcs
local command = helpers.command
local exc_exec = helpers.exc_exec
+local expect_err = helpers.expect_err
before_each(clear)
@@ -40,9 +41,11 @@ describe('setmatches()', function()
end)
it('fails with -1 if highlight group is not defined', function()
- eq(-1, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}}))
+ expect_err('E28: No such highlight group name: 1', funcs.setmatches,
+ {{group=1, pattern=2, id=3, priority=4}})
eq({}, funcs.getmatches())
- eq(-1, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}}))
+ expect_err('E28: No such highlight group name: 1', funcs.setmatches,
+ {{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})
eq({}, funcs.getmatches())
end)
end)
diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua
index 393616838e..563e619b39 100644
--- a/test/functional/eval/server_spec.lua
+++ b/test/functional/eval/server_spec.lua
@@ -1,31 +1,41 @@
-
local helpers = require('test.functional.helpers')(after_each)
local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval
local command = helpers.command
local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths
-local os_name = helpers.os_name
+local iswin = helpers.iswin
+local ok = helpers.ok
+local matches = helpers.matches
+local expect_err = helpers.expect_err
local function clear_serverlist()
- for _, server in pairs(funcs.serverlist()) do
- funcs.serverstop(server)
- end
+ for _, server in pairs(funcs.serverlist()) do
+ funcs.serverstop(server)
+ end
end
-describe('serverstart(), serverstop()', function()
+describe('server', function()
before_each(clear)
- it('sets $NVIM_LISTEN_ADDRESS on first invocation', function()
+ it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function()
-- Unset $NVIM_LISTEN_ADDRESS
command('let $NVIM_LISTEN_ADDRESS = ""')
local s = eval('serverstart()')
assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
eq(s, eval('$NVIM_LISTEN_ADDRESS'))
- command("call serverstop('"..s.."')")
+ eq(1, eval("serverstop('"..s.."')"))
eq('', eval('$NVIM_LISTEN_ADDRESS'))
end)
- it('sets v:servername _only_ on nvim startup unless all servers are stopped',
+ it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function()
+ clear({env={NVIM_LISTEN_ADDRESS='.'}})
+ eq('.', eval('$NVIM_LISTEN_ADDRESS'))
+ local servers = funcs.serverlist()
+ eq(1, #servers)
+ ok(string.len(servers[1]) > 4) -- Like /tmp/nvim…/… or \\.\pipe\…
+ end)
+
+ it('sets v:servername at startup or if all servers were stopped',
function()
local initial_server = meths.get_vvar('servername')
assert(initial_server ~= nil and initial_server:len() > 0,
@@ -38,24 +48,23 @@ describe('serverstart(), serverstop()', function()
neq(initial_server, s)
-- serverstop() does _not_ modify v:servername...
- funcs.serverstop(s)
+ eq(1, funcs.serverstop(s))
eq(initial_server, meths.get_vvar('servername'))
-- ...unless we stop _all_ servers.
- funcs.serverstop(funcs.serverlist()[1])
+ eq(1, funcs.serverstop(funcs.serverlist()[1]))
eq('', meths.get_vvar('servername'))
-- v:servername will take the next available server.
- local servername = (os_name() == 'windows'
- and [[\\.\pipe\Xtest-functional-server-pipe]]
- or 'Xtest-functional-server-socket')
+ local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]]
+ or 'Xtest-functional-server-socket')
funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername'))
end)
- it('serverstop() ignores invalid input', function()
- command("call serverstop('')")
- command("call serverstop('bogus-socket-name')")
+ it('serverstop() returns false for invalid input', function()
+ eq(0, eval("serverstop('')"))
+ eq(0, eval("serverstop('bogus-socket-name')"))
end)
it('parses endpoints correctly', function()
@@ -78,35 +87,32 @@ describe('serverstart(), serverstop()', function()
local expected = {}
local v4 = '127.0.0.1:12345'
- s = funcs.serverstart(v4)
- if #s > 0 then
+ local status, _ = pcall(funcs.serverstart, v4)
+ if status then
table.insert(expected, v4)
- funcs.serverstart(v4) -- exists already; ignore
+ pcall(funcs.serverstart, v4) -- exists already; ignore
end
local v6 = '::1:12345'
- s = funcs.serverstart(v6)
- if #s > 0 then
+ status, _ = pcall(funcs.serverstart, v6)
+ if status then
table.insert(expected, v6)
- funcs.serverstart(v6) -- exists already; ignore
+ pcall(funcs.serverstart, v6) -- exists already; ignore
end
eq(expected, funcs.serverlist())
clear_serverlist()
- funcs.serverstart('127.0.0.1:65536') -- invalid port
+ expect_err('Failed to start server: invalid argument',
+ funcs.serverstart, '127.0.0.1:65536') -- invalid port
eq({}, funcs.serverlist())
end)
-end)
-
-describe('serverlist()', function()
- before_each(clear)
- it('returns the list of servers', function()
+ it('serverlist() returns the list of servers', function()
-- There should already be at least one server.
local n = eval('len(serverlist())')
- -- Add a few
- local servs = (os_name() == 'windows'
+ -- Add some servers.
+ local servs = (iswin()
and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] })
for _, s in ipairs(servs) do
@@ -120,9 +126,31 @@ describe('serverlist()', function()
-- The new servers should be at the end of the list.
for i = 1, #servs do
eq(servs[i], new_servs[i + n])
- command("call serverstop('"..servs[i].."')")
+ eq(1, eval("serverstop('"..servs[i].."')"))
end
-- After serverstop() the servers should NOT be in the list.
eq(n, eval('len(serverlist())'))
end)
end)
+
+describe('startup --listen', function()
+ it('validates', function()
+ clear()
+
+ local cmd = { unpack(helpers.nvim_argv) }
+ table.insert(cmd, '--listen')
+ matches('nvim.*: Argument missing after: "%-%-listen"', funcs.system(cmd))
+
+ cmd = { unpack(helpers.nvim_argv) }
+ table.insert(cmd, '--listen2')
+ matches('nvim.*: Garbage after option argument: "%-%-listen2"', funcs.system(cmd))
+ end)
+
+ it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
+ local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]]
+ or 'Xtest-listen-pipe')
+ clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' },
+ args={ '--listen', addr } })
+ eq(addr, meths.get_vvar('servername'))
+ end)
+end)
diff --git a/test/functional/eval/sort_spec.lua b/test/functional/eval/sort_spec.lua
index 4e5a0afba4..82557575ce 100644
--- a/test/functional/eval/sort_spec.lua
+++ b/test/functional/eval/sort_spec.lua
@@ -8,6 +8,7 @@ local meths = helpers.meths
local funcs = helpers.funcs
local command = helpers.command
local exc_exec = helpers.exc_exec
+local redir_exec = helpers.redir_exec
before_each(clear)
@@ -38,4 +39,18 @@ describe('sort()', function()
eq('[-1.0e-4, function(\'tr\'), v:true, v:false, v:null, [], {\'a\': 42}, \'check\', 1.0e-4]',
eval('string(g:list)'))
end)
+
+ it('can yield E702 and stop sorting after that', function()
+ command([[
+ function Cmp(a, b)
+ if type(a:a) == type([]) || type(a:b) == type([])
+ return []
+ endif
+ return (a:a > a:b) - (a:a < a:b)
+ endfunction
+ ]])
+ eq('\nE745: Using a List as a Number\nE702: Sort compare function failed',
+ redir_exec('let sl = sort([1, 0, [], 3, 2], "Cmp")'))
+ eq({1, 0, {}, 3, 2}, meths.get_var('sl'))
+ end)
end)
diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua
index 446afefb59..23cea4c038 100644
--- a/test/functional/eval/system_spec.lua
+++ b/test/functional/eval/system_spec.lua
@@ -120,33 +120,47 @@ describe('system()', function()
end
end)
- describe('executes shell function if passed a string', function()
+ describe('executes shell function', function()
local screen
before_each(function()
- clear()
- screen = Screen.new()
- screen:attach()
+ clear()
+ screen = Screen.new()
+ screen:attach()
end)
after_each(function()
- screen:detach()
+ screen:detach()
end)
if iswin() then
+ local function test_more()
+ eq('root = true', eval([[get(split(system('"more" ".editorconfig"'), "\n"), 0, '')]]))
+ end
+ local function test_shell_unquoting()
+ eval([[system('"ping" "-n" "1" "127.0.0.1"')]])
+ eq(0, eval('v:shell_error'))
+ eq('"a b"\n', eval([[system('cmd /s/c "cmd /s/c "cmd /s/c "echo "a b""""')]]))
+ eq('"a b"\n', eval([[system('powershell -NoProfile -NoLogo -ExecutionPolicy RemoteSigned -Command echo ''\^"a b\^"''')]]))
+ end
+
it('with shell=cmd.exe', function()
command('set shell=cmd.exe')
eq('""\n', eval([[system('echo ""')]]))
eq('"a b"\n', eval([[system('echo "a b"')]]))
eq('a \nb\n', eval([[system('echo a & echo b')]]))
eq('a \n', eval([[system('echo a 2>&1')]]))
+ test_more()
eval([[system('cd "C:\Program Files"')]])
eq(0, eval('v:shell_error'))
+ test_shell_unquoting()
end)
it('with shell=cmd', function()
command('set shell=cmd')
eq('"a b"\n', eval([[system('echo "a b"')]]))
+ test_more()
+ test_shell_unquoting()
end)
it('with shell=$COMSPEC', function()
@@ -154,6 +168,8 @@ describe('system()', function()
if comspecshell == 'cmd.exe' then
command('set shell=$COMSPEC')
eq('"a b"\n', eval([[system('echo "a b"')]]))
+ test_more()
+ test_shell_unquoting()
else
pending('$COMSPEC is not cmd.exe: ' .. comspecshell)
end
@@ -187,7 +203,7 @@ describe('system()', function()
]])
end)
- it('`yes` and is interrupted with CTRL-C', function()
+ it('`yes` interrupted with CTRL-C', function()
feed(':call system("' .. (iswin()
and 'for /L %I in (1,0,2) do @echo y'
or 'yes') .. '")<cr>')
@@ -240,7 +256,7 @@ describe('system()', function()
end)
it('to backgrounded command does not crash', function()
-- This is indeterminate, just exercise the codepath. May get E5677.
- feed_command('call system("echo -n echoed &")')
+ feed_command('call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "echo -n echoed &")')
local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
if v_errnum then
eq("E5677:", v_errnum)
@@ -255,7 +271,7 @@ describe('system()', function()
end)
it('to backgrounded command does not crash', function()
-- This is indeterminate, just exercise the codepath. May get E5677.
- feed_command('call system("cat - &", "input")')
+ feed_command('call system(has("win32") ? "start /b /wait more" : "cat - &", "input")')
local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
if v_errnum then
eq("E5677:", v_errnum)
@@ -299,7 +315,7 @@ describe('system()', function()
after_each(delete_file(fname))
it('replaces NULs by SOH characters', function()
- eq('part1\001part2\001part3\n', eval('system("cat '..fname..'")'))
+ eq('part1\001part2\001part3\n', eval([[system('"cat" "]]..fname..[["')]]))
end)
end)
@@ -366,7 +382,7 @@ describe('systemlist()', function()
end
end)
- describe('exectues shell function', function()
+ describe('executes shell function', function()
local screen
before_each(function()
@@ -399,7 +415,7 @@ describe('systemlist()', function()
]])
end)
- it('`yes` and is interrupted with CTRL-C', function()
+ it('`yes` interrupted with CTRL-C', function()
feed(':call systemlist("yes | xargs")<cr>')
screen:expect([[
|
@@ -464,7 +480,7 @@ describe('systemlist()', function()
after_each(delete_file(fname))
it('replaces NULs by newline characters', function()
- eq({'part1\npart2\npart3'}, eval('systemlist("cat '..fname..'")'))
+ eq({'part1\npart2\npart3'}, eval([[systemlist('"cat" "]]..fname..[["')]]))
end)
end)
diff --git a/test/functional/eval/uniq_spec.lua b/test/functional/eval/uniq_spec.lua
new file mode 100644
index 0000000000..0e7a013e32
--- /dev/null
+++ b/test/functional/eval/uniq_spec.lua
@@ -0,0 +1,31 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local eq = helpers.eq
+local clear = helpers.clear
+local meths = helpers.meths
+local command = helpers.command
+local exc_exec = helpers.exc_exec
+local redir_exec = helpers.redir_exec
+
+before_each(clear)
+
+describe('uniq()', function()
+ it('errors out when processing special values', function()
+ eq('Vim(call):E907: Using a special value as a Float',
+ exc_exec('call uniq([v:true, v:false], "f")'))
+ end)
+
+ it('can yield E882 and stop filtering after that', function()
+ command([[
+ function Cmp(a, b)
+ if type(a:a) == type([]) || type(a:b) == type([])
+ return []
+ endif
+ return (a:a > a:b) - (a:a < a:b)
+ endfunction
+ ]])
+ eq('\nE745: Using a List as a Number\nE882: Uniq compare function failed',
+ redir_exec('let fl = uniq([0, 0, [], 1, 1], "Cmp")'))
+ eq({0, {}, 1, 1}, meths.get_var('fl'))
+ end)
+end)
diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua
new file mode 100644
index 0000000000..77d025dcc7
--- /dev/null
+++ b/test/functional/ex_cmds/cmd_map_spec.lua
@@ -0,0 +1,772 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local feed_command = helpers.feed_command
+local feed = helpers.feed
+local eq = helpers.eq
+local expect = helpers.expect
+local eval = helpers.eval
+local funcs = helpers.funcs
+local insert = helpers.insert
+local exc_exec = helpers.exc_exec
+local Screen = require('test.functional.ui.screen')
+
+describe('mappings with <Cmd>', function()
+ local screen
+ local function cmdmap(lhs, rhs)
+ feed_command('noremap '..lhs..' <Cmd>'..rhs..'<cr>')
+ feed_command('noremap! '..lhs..' <Cmd>'..rhs..'<cr>')
+ end
+
+ before_each(function()
+ clear()
+ screen = Screen.new(65, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [4] = {bold = true},
+ [5] = {background = Screen.colors.LightGrey},
+ [6] = {foreground = Screen.colors.Blue1},
+ [7] = {bold = true, reverse = true},
+ })
+ screen:attach()
+
+ cmdmap('<F3>', 'let m = mode(1)')
+ cmdmap('<F4>', 'normal! ww')
+ cmdmap('<F5>', 'normal! "ay')
+ cmdmap('<F6>', 'throw "very error"')
+ feed_command([[
+ function! TextObj()
+ if mode() !=# "v"
+ normal! v
+ end
+ call cursor(1,3)
+ normal! o
+ call cursor(2,4)
+ endfunction]])
+ cmdmap('<F7>', 'call TextObj()')
+ insert([[
+ some short lines
+ of test text]])
+ feed('gg')
+ cmdmap('<F8>', 'startinsert')
+ cmdmap('<F9>', 'stopinsert')
+ feed_command("abbr foo <Cmd>let g:y = 17<cr>bar")
+ end)
+
+ it('can be displayed', function()
+ feed_command('map <F3>')
+ screen:expect([[
+ ^some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {6:<F3>} {6:*} {6:<Cmd>}let m = mode(1){6:<CR>} |
+ ]])
+ end)
+
+ it('handles invalid mappings', function()
+ feed_command('let x = 0')
+ feed_command('noremap <F3> <Cmd><Cmd>let x = 1<cr>')
+ feed('<F3>')
+ screen:expect([[
+ ^some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:E5521: <Cmd> mapping must end with <CR> before second <Cmd>} |
+ ]])
+
+ feed_command('noremap <F3> <Cmd><F3>let x = 2<cr>')
+ feed('<F3>')
+ screen:expect([[
+ ^some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:E5522: <Cmd> mapping must not include <F3> key} |
+ ]])
+
+ feed_command('noremap <F3> <Cmd>let x = 3')
+ feed('<F3>')
+ screen:expect([[
+ ^some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:E5520: <Cmd> mapping must end with <CR>} |
+ ]])
+ eq(0, eval('x'))
+ end)
+
+ it('works in various modes and sees correct `mode()` value', function()
+ -- normal mode
+ feed('<F3>')
+ eq('n', eval('m'))
+
+ -- visual mode
+ feed('v<F3>')
+ eq('v', eval('m'))
+ -- didn't leave visual mode
+ eq('v', eval('mode(1)'))
+ feed('<esc>')
+ eq('n', eval('mode(1)'))
+
+ -- visual mapping in select mode
+ feed('gh<F3>')
+ eq('v', eval('m'))
+ -- didn't leave select mode
+ eq('s', eval('mode(1)'))
+ feed('<esc>')
+ eq('n', eval('mode(1)'))
+
+ -- select mode mapping
+ feed_command('snoremap <F3> <Cmd>let m = mode(1)<cr>')
+ feed('gh<F3>')
+ eq('s', eval('m'))
+ -- didn't leave select mode
+ eq('s', eval('mode(1)'))
+ feed('<esc>')
+ eq('n', eval('mode(1)'))
+
+ -- operator-pending mode
+ feed("d<F3>")
+ eq('no', eval('m'))
+ -- did leave operator-pending mode
+ eq('n', eval('mode(1)'))
+
+ --insert mode
+ feed('i<F3>')
+ eq('i', eval('m'))
+ eq('i', eval('mode(1)'))
+
+ -- replace mode
+ feed("<Ins><F3>")
+ eq('R', eval('m'))
+ eq('R', eval('mode(1)'))
+ feed('<esc>')
+ eq('n', eval('mode(1)'))
+
+ -- virtual replace mode
+ feed("gR<F3>")
+ eq('Rv', eval('m'))
+ eq('Rv', eval('mode(1)'))
+ feed('<esc>')
+ eq('n', eval('mode(1)'))
+
+ -- langmap works, but is not distinguished in mode(1)
+ feed(":set iminsert=1<cr>i<F3>")
+ eq('i', eval('m'))
+ eq('i', eval('mode(1)'))
+ feed('<esc>')
+ eq('n', eval('mode(1)'))
+
+ feed(':<F3>')
+ eq('c', eval('m'))
+ eq('c', eval('mode(1)'))
+ feed('<esc>')
+ eq('n', eval('mode(1)'))
+
+ -- terminal mode
+ feed_command('tnoremap <F3> <Cmd>let m = mode(1)<cr>')
+ feed_command('split | terminal')
+ feed('i')
+ eq('t', eval('mode(1)'))
+ feed('<F3>')
+ eq('t', eval('m'))
+ eq('t', eval('mode(1)'))
+ end)
+
+ it('works in normal mode', function()
+ cmdmap('<F2>', 'let s = [mode(1), v:count, v:register]')
+
+ -- check v:count and v:register works
+ feed('<F2>')
+ eq({'n', 0, '"'}, eval('s'))
+ feed('7<F2>')
+ eq({'n', 7, '"'}, eval('s'))
+ feed('"e<F2>')
+ eq({'n', 0, 'e'}, eval('s'))
+ feed('5"k<F2>')
+ eq({'n', 5, 'k'}, eval('s'))
+ feed('"+2<F2>')
+ eq({'n', 2, '+'}, eval('s'))
+
+ -- text object enters visual mode
+ feed('<F7>')
+ screen:expect([[
+ so{5:me short lines} |
+ {5:of }^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- VISUAL --} |
+ ]])
+ feed('<esc>')
+
+ -- startinsert
+ feed('<F8>')
+ eq('i', eval('mode(1)'))
+ feed('<esc>')
+
+ eq('n', eval('mode(1)'))
+ cmdmap(',a', 'call feedkeys("aalpha") \\| let g:a = getline(2)')
+ cmdmap(',b', 'call feedkeys("abeta", "x") \\| let g:b = getline(2)')
+
+ feed(',a<F3>')
+ screen:expect([[
+ some short lines |
+ of alpha^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+ -- feedkeys were not executed immediately
+ eq({'n', 'of test text'}, eval('[m,a]'))
+ eq('i', eval('mode(1)'))
+ feed('<esc>')
+
+ feed(',b<F3>')
+ screen:expect([[
+ some short lines |
+ of alphabet^atest text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ -- feedkeys(..., 'x') was executed immediately, but insert mode gets aborted
+ eq({'n', 'of alphabetatest text'}, eval('[m,b]'))
+ eq('n', eval('mode(1)'))
+ end)
+
+ it('works in :normal command', function()
+ feed_command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>')
+ feed_command('noremap ,f <Cmd>nosuchcommand<cr>')
+ feed_command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>')
+ feed_command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>')
+ feed_command('noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>')
+
+ feed(":normal ,x<cr>")
+ screen:expect([[
+ ^some short lines |
+ aa |
+ xx |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ eq('Vim:E492: Not an editor command: nosuchcommand', exc_exec("normal ,f"))
+ eq('very error', exc_exec("normal ,e"))
+ eq('Vim(echoerr):The message.', exc_exec("normal ,m"))
+ feed('w')
+ screen:expect([[
+ some ^short lines |
+ aa |
+ xx |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed_command(':%d')
+ eq('Vim(echoerr):Err', exc_exec("normal ,w"))
+ screen:expect([[
+ ^ |
+ 0 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ --No lines in buffer-- |
+ ]])
+
+ feed_command(':%d')
+ feed_command(':normal ,w')
+ screen:expect([[
+ ^ |
+ 4 |
+ 3 |
+ 2 |
+ 1 |
+ 0 |
+ {1:~ }|
+ {2:Err} |
+ ]])
+ end)
+
+ it('works in visual mode', function()
+ -- can extend visual mode
+ feed('v<F4>')
+ screen:expect([[
+ {5:some short }^lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- VISUAL --} |
+ ]])
+ eq('v', funcs.mode(1))
+
+ -- can invoke operator, ending visual mode
+ feed('<F5>')
+ eq('n', funcs.mode(1))
+ eq({'some short l'}, funcs.getreg('a',1,1))
+
+ -- error doesn't interrupt visual mode
+ feed('ggvw<F6>')
+ screen:expect([[
+ {5:some }short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {7: }|
+ {2:Error detected while processing :} |
+ {2:E605: Exception not caught: very error} |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ eq('E605: Exception not caught: very error', eval('v:errmsg'))
+ -- still in visual mode, <cr> was consumed by the error prompt
+ screen:expect([[
+ {5:some }^short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- VISUAL --} |
+ ]])
+ eq('v', funcs.mode(1))
+ feed('<F7>')
+ screen:expect([[
+ so{5:me short lines} |
+ {5:of }^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- VISUAL --} |
+ ]])
+ eq('v', funcs.mode(1))
+
+ -- startinsert gives "-- (insert) VISUAL --" mode
+ feed('<F8>')
+ screen:expect([[
+ so{5:me short lines} |
+ {5:of }^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- (insert) VISUAL --} |
+ ]])
+ eq('v', eval('mode(1)'))
+ feed('<esc>')
+ eq('i', eval('mode(1)'))
+ end)
+
+ it('works in select mode', function()
+ feed_command('snoremap <F1> <cmd>throw "very error"<cr>')
+ feed_command('snoremap <F2> <cmd>normal! <c-g>"by<cr>')
+ -- can extend select mode
+ feed('gh<F4>')
+ screen:expect([[
+ {5:some short }^lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- SELECT --} |
+ ]])
+ eq('s', funcs.mode(1))
+
+ -- visual mapping in select mode restart selct mode after operator
+ feed('<F5>')
+ eq('s', funcs.mode(1))
+ eq({'some short l'}, funcs.getreg('a',1,1))
+
+ -- select mode mapping works, and does not restart select mode
+ feed('<F2>')
+ eq('n', funcs.mode(1))
+ eq({'some short l'}, funcs.getreg('b',1,1))
+
+ -- error doesn't interrupt temporary visual mode
+ feed('<esc>ggvw<c-g><F6>')
+ screen:expect([[
+ {5:some }short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {7: }|
+ {2:Error detected while processing :} |
+ {2:E605: Exception not caught: very error} |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ eq('E605: Exception not caught: very error', eval('v:errmsg'))
+ -- still in visual mode, <cr> was consumed by the error prompt
+ screen:expect([[
+ {5:some }^short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- VISUAL --} |
+ ]])
+ -- quirk: restoration of select mode is not performed
+ eq('v', funcs.mode(1))
+
+ -- error doesn't interrupt select mode
+ feed('<esc>ggvw<c-g><F1>')
+ screen:expect([[
+ {5:some }short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {7: }|
+ {2:Error detected while processing :} |
+ {2:E605: Exception not caught: very error} |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ eq('E605: Exception not caught: very error', eval('v:errmsg'))
+ -- still in select mode, <cr> was consumed by the error prompt
+ screen:expect([[
+ {5:some }^short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- SELECT --} |
+ ]])
+ -- quirk: restoration of select mode is not performed
+ eq('s', funcs.mode(1))
+
+ feed('<F7>')
+ screen:expect([[
+ so{5:me short lines} |
+ {5:of }^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- SELECT --} |
+ ]])
+ eq('s', funcs.mode(1))
+
+ -- startinsert gives "-- SELECT (insert) --" mode
+ feed('<F8>')
+ screen:expect([[
+ so{5:me short lines} |
+ {5:of }^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- (insert) SELECT --} |
+ ]])
+ eq('s', eval('mode(1)'))
+ feed('<esc>')
+ eq('i', eval('mode(1)'))
+ end)
+
+
+ it('works in operator-pending mode', function()
+ feed('d<F4>')
+ expect([[
+ lines
+ of test text]])
+ eq({'some short '}, funcs.getreg('"',1,1))
+ feed('.')
+ expect([[
+ test text]])
+ eq({'lines', 'of '}, funcs.getreg('"',1,1))
+ feed('uu')
+ expect([[
+ some short lines
+ of test text]])
+
+ -- error aborts operator-pending, operator not performed
+ feed('d<F6>')
+ screen:expect([[
+ some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {7: }|
+ {2:Error detected while processing :} |
+ {2:E605: Exception not caught: very error} |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ eq('E605: Exception not caught: very error', eval('v:errmsg'))
+ expect([[
+ some short lines
+ of test text]])
+
+ feed('"bd<F7>')
+ expect([[
+ soest text]])
+ eq(funcs.getreg('b',1,1), {'me short lines', 'of t'})
+
+ -- startinsert aborts operator
+ feed('d<F8>')
+ eq('i', eval('mode(1)'))
+ expect([[
+ soest text]])
+ end)
+
+ it('works in insert mode', function()
+
+ -- works the same as <c-o>w<c-o>w
+ feed('iindeed <F4>little ')
+ screen:expect([[
+ indeed some short little ^lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+
+ feed('<F6>')
+ screen:expect([[
+ indeed some short little lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {7: }|
+ {2:Error detected while processing :} |
+ {2:E605: Exception not caught: very error} |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+
+ feed('<cr>')
+ eq('E605: Exception not caught: very error', eval('v:errmsg'))
+ -- still in insert
+ screen:expect([[
+ indeed some short little ^lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+ eq('i', eval('mode(1)'))
+
+ -- When entering visual mode from InsertEnter autocmd, an async event, or
+ -- a <cmd> mapping, vim ends up in undocumented "INSERT VISUAL" mode. If a
+ -- vim patch decides to disable this mode, this test is expected to fail.
+ feed('<F7>stuff ')
+ screen:expect([[
+ in{5:deed some short little lines} |
+ {5:of stuff }^test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT VISUAL --} |
+ ]])
+ expect([[
+ indeed some short little lines
+ of stuff test text]])
+
+ feed('<F5>')
+ eq(funcs.getreg('a',1,1), {'deed some short little lines', 'of stuff t'})
+
+ -- still in insert
+ screen:expect([[
+ in^deed some short little lines |
+ of stuff test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+ eq('i', eval('mode(1)'))
+
+ -- also works as part of abbreviation
+ feed('<space>foo ')
+ screen:expect([[
+ in bar ^deed some short little lines |
+ of stuff test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+ eq(17, eval('g:y'))
+
+ -- :startinsert does nothing
+ feed('<F8>')
+ eq('i', eval('mode(1)'))
+
+ -- :stopinsert works
+ feed('<F9>')
+ eq('n', eval('mode(1)'))
+ end)
+
+ it('works in cmdline mode', function()
+ cmdmap('<F2>', 'call setcmdpos(2)')
+ feed(':text<F3>')
+ eq('c', eval('m'))
+ -- didn't leave cmdline mode
+ eq('c', eval('mode(1)'))
+ feed('<cr>')
+ eq('n', eval('mode(1)'))
+ screen:expect([[
+ ^some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:E492: Not an editor command: text} |
+ ]])
+
+ feed(':echo 2<F6>')
+ screen:expect([[
+ some short lines |
+ of test text |
+ {1:~ }|
+ {7: }|
+ :echo 2 |
+ {2:Error detected while processing :} |
+ {2:E605: Exception not caught: very error} |
+ :echo 2^ |
+ ]])
+ eq('E605: Exception not caught: very error', eval('v:errmsg'))
+ -- didn't leave cmdline mode
+ eq('c', eval('mode(1)'))
+ feed('+2<cr>')
+ screen:expect([[
+ some short lines |
+ of test text |
+ {7: }|
+ :echo 2 |
+ {2:Error detected while processing :} |
+ {2:E605: Exception not caught: very error} |
+ 4 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ -- however, message scrolling may cause extra CR prompt
+ -- This is consistent with output from async events.
+ feed('<cr>')
+ screen:expect([[
+ ^some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ eq('n', eval('mode(1)'))
+
+ feed(':let g:x = 3<F4>')
+ screen:expect([[
+ some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :let g:x = 3^ |
+ ]])
+ feed('+2<cr>')
+ -- cursor was moved in the background
+ screen:expect([[
+ some short ^lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :let g:x = 3+2 |
+ ]])
+ eq(5, eval('g:x'))
+
+ feed(':let g:y = 7<F8>')
+ screen:expect([[
+ some short lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :let g:y = 7^ |
+ ]])
+ eq('c', eval('mode(1)'))
+ feed('+2<cr>')
+ -- startinsert takes effect after leaving cmdline mode
+ screen:expect([[
+ some short ^lines |
+ of test text |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+ eq('i', eval('mode(1)'))
+ eq(9, eval('g:y'))
+
+ end)
+
+end)
+
diff --git a/test/functional/ex_cmds/digraphs_spec.lua b/test/functional/ex_cmds/digraphs_spec.lua
new file mode 100644
index 0000000000..f2d0b76739
--- /dev/null
+++ b/test/functional/ex_cmds/digraphs_spec.lua
@@ -0,0 +1,38 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local feed = helpers.feed
+local Screen = require('test.functional.ui.screen')
+
+describe(':digraphs', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(65, 8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [4] = {bold = true},
+ [5] = {background = Screen.colors.LightGrey},
+ [6] = {foreground = Screen.colors.Blue1},
+ [7] = {bold = true, reverse = true},
+ })
+ screen:attach()
+ end)
+
+ it('displays digraphs', function()
+ feed(':digraphs<CR>')
+ screen:expect([[
+ A@ {6:Å} 197 E` {6:È} 200 E^ {6:Ê} 202 E" {6:Ë} 203 I` {6:Ì} 204 |
+ I^ {6:Î} 206 I" {6:Ï} 207 N~ {6:Ñ} 209 O` {6:Ò} 210 O^ {6:Ô} 212 |
+ O~ {6:Õ} 213 /\ {6:×} 215 U` {6:Ù} 217 U^ {6:Û} 219 Ip {6:Þ} 222 |
+ a` {6:à} 224 a^ {6:â} 226 a~ {6:ã} 227 a" {6:ä} 228 a@ {6:å} 229 |
+ e` {6:è} 232 e^ {6:ê} 234 e" {6:ë} 235 i` {6:ì} 236 i^ {6:î} 238 |
+ n~ {6:ñ} 241 o` {6:ò} 242 o^ {6:ô} 244 o~ {6:õ} 245 u` {6:ù} 249 |
+ u^ {6:û} 251 y" {6:ÿ} 255 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+
+end)
+
diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua
index 4002855c24..448326cdfb 100644
--- a/test/functional/ex_cmds/oldfiles_spec.lua
+++ b/test/functional/ex_cmds/oldfiles_spec.lua
@@ -29,6 +29,7 @@ describe(':oldfiles', function()
it('shows most recently used files', function()
local screen = Screen.new(100, 5)
screen:attach()
+ feed_command("set display-=msgsep")
feed_command('edit testfile1')
feed_command('edit testfile2')
feed_command('wshada')
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index b8d912114d..9895281773 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -12,13 +12,18 @@ local Paths = require('test.config.paths')
local check_cores = global_helpers.check_cores
local check_logs = global_helpers.check_logs
-local neq = global_helpers.neq
+local dedent = global_helpers.dedent
local eq = global_helpers.eq
-local ok = global_helpers.ok
-local map = global_helpers.map
+local expect_err = global_helpers.expect_err
local filter = global_helpers.filter
-local dedent = global_helpers.dedent
+local map = global_helpers.map
+local matches = global_helpers.matches
+local neq = global_helpers.neq
+local ok = global_helpers.ok
+local read_file = global_helpers.read_file
+local sleep = global_helpers.sleep
local table_flatten = global_helpers.table_flatten
+local write_file = global_helpers.write_file
local start_dir = lfs.currentdir()
-- XXX: NVIM_PROG takes precedence, QuickBuild sets it.
@@ -304,12 +309,10 @@ local function retry(max, max_ms, fn)
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
- if type(result) == "string" then
- result = "\nretry() attempts: "..tostring(tries).."\n"..result
- end
- error(result)
+ error("\nretry() attempts: "..tostring(tries).."\n"..tostring(result))
end
tries = tries + 1
+ luv.sleep(20) -- Avoid hot loop...
end
end
@@ -374,34 +377,6 @@ local function feed_command(...)
end
end
--- Dedent the given text and write it to the file name.
-local function write_file(name, text, no_dedent, append)
- local file = io.open(name, (append and 'a' or 'w'))
- if type(text) == 'table' then
- -- Byte blob
- local bytes = text
- text = ''
- for _, char in ipairs(bytes) do
- text = ('%s%c'):format(text, char)
- end
- elseif not no_dedent then
- text = dedent(text)
- end
- file:write(text)
- file:flush()
- file:close()
-end
-
-local function read_file(name)
- local file = io.open(name, 'r')
- if not file then
- return nil
- end
- local ret = file:read('*a')
- file:close()
- return ret
-end
-
local sourced_fnames = {}
local function source(code)
local fname = tmpname()
@@ -425,7 +400,7 @@ end
local function set_shell_powershell()
source([[
set shell=powershell shellquote=( shellpipe=\| shellredir=> shellxquote=
- set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command
+ let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command Remove-Item -Force alias:sleep;'
]])
end
@@ -466,18 +441,6 @@ local function wait()
session:request('nvim_eval', '1')
end
--- sleeps the test runner (_not_ the nvim instance)
-local function sleep(ms)
- local function notification_cb(method, _)
- if method == "redraw" then
- error("Screen is attached; use screen:sleep() instead.")
- end
- return true
- end
-
- run(nil, notification_cb, nil, ms)
-end
-
local function curbuf_contents()
wait() -- Before inspecting the buffer, process all input.
return table.concat(curbuf('get_lines', 0, -1, true), '\n')
@@ -689,31 +652,6 @@ local function alter_slashes(obj)
end
end
-local function hexdump(str)
- local len = string.len(str)
- local dump = ""
- local hex = ""
- local asc = ""
-
- for i = 1, len do
- if 1 == i % 8 then
- dump = dump .. hex .. asc .. "\n"
- hex = string.format("%04x: ", i - 1)
- asc = ""
- end
-
- local ord = string.byte(str, i)
- hex = hex .. string.format("%02x ", ord)
- if ord >= 32 and ord <= 126 then
- asc = asc .. string.char(ord)
- else
- asc = asc .. "."
- end
- end
-
- return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc
-end
-
local module = {
NIL = mpack.NIL,
alter_slashes = alter_slashes,
@@ -736,6 +674,7 @@ local module = {
exc_exec = exc_exec,
expect = expect,
expect_any = expect_any,
+ expect_err = expect_err,
expect_msg_seq = expect_msg_seq,
expect_twostreams = expect_twostreams,
feed = feed,
@@ -743,10 +682,10 @@ local module = {
filter = filter,
funcs = funcs,
get_pathsep = get_pathsep,
- hexdump = hexdump,
insert = insert,
iswin = iswin,
map = map,
+ matches = matches,
merge_args = merge_args,
meth_pcall = meth_pcall,
meths = meths,
diff --git a/test/functional/insert/insert_spec.lua b/test/functional/insert/insert_spec.lua
new file mode 100644
index 0000000000..427954f5a6
--- /dev/null
+++ b/test/functional/insert/insert_spec.lua
@@ -0,0 +1,41 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
+local command = helpers.command
+local eq = helpers.eq
+local expect = helpers.expect
+local funcs = helpers.funcs
+
+describe('insert-mode', function()
+ before_each(function()
+ clear()
+ end)
+
+ it('CTRL-@', function()
+ -- Inserts last-inserted text, leaves insert-mode.
+ insert('hello')
+ feed('i<C-@>x')
+ expect('hellhello')
+
+ -- C-Space is the same as C-@.
+ -- CTRL-SPC inserts last-inserted text, leaves insert-mode.
+ feed('i<C-Space>x')
+ expect('hellhellhello')
+
+ -- CTRL-A inserts last inserted text
+ feed('i<C-A>x')
+ expect('hellhellhellhelloxo')
+ end)
+
+ it('ALT/META #8213', function()
+ -- Mapped ALT-chord behaves as mapped.
+ command('inoremap <M-l> meta-l')
+ command('inoremap <A-j> alt-j')
+ feed('i<M-l> xxx <A-j><M-h>a<A-h>')
+ expect('meta-l xxx alt-j')
+ eq({ 0, 1, 14, 0, }, funcs.getpos('.'))
+ -- Unmapped ALT-chord behaves as ESC+c.
+ command('iunmap <M-l>')
+ feed('0i<M-l>')
+ eq({ 0, 1, 2, 0, }, funcs.getpos('.'))
+ end)
+end)
diff --git a/test/functional/insert/last_inserted_spec.lua b/test/functional/insert/last_inserted_spec.lua
deleted file mode 100644
index dce23a3790..0000000000
--- a/test/functional/insert/last_inserted_spec.lua
+++ /dev/null
@@ -1,22 +0,0 @@
-local helpers = require('test.functional.helpers')(after_each)
-local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local expect = helpers.expect
-
-clear()
-
-describe('insert-mode', function()
- it('CTRL-@ inserts last-inserted text, leaves insert-mode', function()
- insert('hello')
- feed('i<C-@>x')
- expect('hellhello')
- end)
- -- C-Space is the same as C-@
- it('CTRL-SPC inserts last-inserted text, leaves insert-mode', function()
- feed('i<C-Space>x')
- expect('hellhellhello')
- end)
- it('CTRL-A inserts last inserted text', function()
- feed('i<C-A>x')
- expect('hellhellhellhelloxo')
- end)
-end)
diff --git a/test/functional/legacy/077_mf_hash_grow_spec.lua b/test/functional/legacy/077_mf_hash_grow_spec.lua
index c692127213..4719a3ecbf 100644
--- a/test/functional/legacy/077_mf_hash_grow_spec.lua
+++ b/test/functional/legacy/077_mf_hash_grow_spec.lua
@@ -18,7 +18,8 @@ describe('mf_hash_grow()', function()
setup(clear)
-- Check to see if cksum exists, otherwise skip the test
- if os.execute('which cksum 2>&1 > /dev/null') ~= 0 then
+ local null = helpers.iswin() and 'nul' or '/dev/null'
+ if os.execute('cksum --help >' .. null .. ' 2>&1') ~= 0 then
pending('was not tested because cksum was not found', function() end)
else
it('is working', function()
diff --git a/test/functional/legacy/file_perm_spec.lua b/test/functional/legacy/file_perm_spec.lua
index 77e82352c5..d61fdc9b83 100644
--- a/test/functional/legacy/file_perm_spec.lua
+++ b/test/functional/legacy/file_perm_spec.lua
@@ -15,7 +15,7 @@ describe('Test getting and setting file permissions', function()
it('file permissions', function()
eq('', call('getfperm', tempfile))
- eq(0, call('setfperm', tempfile, 'r------'))
+ eq(0, call('setfperm', tempfile, 'r--------'))
call('writefile', {'one'}, tempfile)
eq(9, call('len', call('getfperm', tempfile)))
diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua
index 6da0001cea..760105df6b 100644
--- a/test/functional/lua/luaeval_spec.lua
+++ b/test/functional/lua/luaeval_spec.lua
@@ -51,12 +51,12 @@ describe('luaeval()', function()
end)
describe('recursive lua values', function()
it('are successfully transformed', function()
- funcs.luaeval('rawset(_G, "d", {})')
- funcs.luaeval('rawset(d, "d", d)')
+ command('lua rawset(_G, "d", {})')
+ command('lua rawset(d, "d", d)')
eq('\n{\'d\': {...@0}}', funcs.execute('echo luaeval("d")'))
- funcs.luaeval('rawset(_G, "l", {})')
- funcs.luaeval('table.insert(l, l)')
+ command('lua rawset(_G, "l", {})')
+ command('lua table.insert(l, l)')
eq('\n[[...@0]]', funcs.execute('echo luaeval("l")'))
end)
end)
diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua
index 8ca5fe57ba..007d40874f 100644
--- a/test/functional/lua/overrides_spec.lua
+++ b/test/functional/lua/overrides_spec.lua
@@ -87,6 +87,7 @@ describe('debug.debug', function()
E = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
cr = {bold = true, foreground = Screen.colors.SeaGreen4},
})
+ command("set display-=msgsep")
end)
it('works', function()
command([[lua
diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua
index 9e29baba2d..f452cafd22 100644
--- a/test/functional/options/defaults_spec.lua
+++ b/test/functional/options/defaults_spec.lua
@@ -5,12 +5,15 @@ local Screen = require('test.functional.ui.screen')
local meths = helpers.meths
local command = helpers.command
local clear = helpers.clear
+local exc_exec = helpers.exc_exec
local eval = helpers.eval
local eq = helpers.eq
+local funcs = helpers.funcs
local insert = helpers.insert
local neq = helpers.neq
local mkdir = helpers.mkdir
local rmdir = helpers.rmdir
+local alter_slashes = helpers.alter_slashes
describe('startup defaults', function()
describe(':filetype', function()
@@ -422,3 +425,276 @@ describe('XDG-based defaults', function()
end)
end)
end)
+
+
+describe('stdpath()', function()
+ context('returns a String', function()
+ describe('with "config"' , function ()
+ it('knows XDG_CONFIG_HOME', function()
+ clear({env={
+ XDG_CONFIG_HOME=alter_slashes('/home/docwhat/.config'),
+ }})
+ eq(alter_slashes('/home/docwhat/.config/nvim'), funcs.stdpath('config'))
+ end)
+
+ it('handles changes during runtime', function()
+ clear({env={
+ XDG_CONFIG_HOME=alter_slashes('/home/original'),
+ }})
+ eq(alter_slashes('/home/original/nvim'), funcs.stdpath('config'))
+ command("let $XDG_CONFIG_HOME='"..alter_slashes('/home/new').."'")
+ eq(alter_slashes('/home/new/nvim'), funcs.stdpath('config'))
+ end)
+
+ it("doesn't expand $VARIABLES", function()
+ clear({env={
+ XDG_CONFIG_HOME='$VARIABLES',
+ VARIABLES='this-should-not-happen',
+ }})
+ eq(alter_slashes('$VARIABLES/nvim'), funcs.stdpath('config'))
+ end)
+
+ it("doesn't expand ~/", function()
+ clear({env={
+ XDG_CONFIG_HOME=alter_slashes('~/frobnitz'),
+ }})
+ eq(alter_slashes('~/frobnitz/nvim'), funcs.stdpath('config'))
+ end)
+ end)
+
+ describe('with "data"' , function ()
+ local appended_dir
+ setup(function()
+ -- Windows appends 'nvim-data' instead of just 'nvim' to
+ -- prevent collisions due to XDG_CONFIG_HOME and XDG_DATA_HOME
+ -- being the same.
+ if helpers.iswin() then
+ appended_dir = '/nvim-data'
+ else
+ appended_dir = '/nvim'
+ end
+ end)
+
+ it('knows XDG_DATA_HOME', function()
+ clear({env={
+ XDG_DATA_HOME=alter_slashes('/home/docwhat/.local'),
+ }})
+ eq(alter_slashes('/home/docwhat/.local' .. appended_dir), funcs.stdpath('data'))
+ end)
+
+ it('handles changes during runtime', function()
+ clear({env={
+ XDG_DATA_HOME=alter_slashes('/home/original'),
+ }})
+ eq(alter_slashes('/home/original' .. appended_dir), funcs.stdpath('data'))
+ command("let $XDG_DATA_HOME='"..alter_slashes('/home/new').."'")
+ eq(alter_slashes('/home/new' .. appended_dir), funcs.stdpath('data'))
+ end)
+
+ it("doesn't expand $VARIABLES", function()
+ clear({env={
+ XDG_DATA_HOME='$VARIABLES',
+ VARIABLES='this-should-not-happen',
+ }})
+ eq(alter_slashes('$VARIABLES' .. appended_dir), funcs.stdpath('data'))
+ end)
+
+ it("doesn't expand ~/", function()
+ clear({env={
+ XDG_DATA_HOME=alter_slashes('~/frobnitz'),
+ }})
+ eq(alter_slashes('~/frobnitz' .. appended_dir), funcs.stdpath('data'))
+ end)
+ end)
+
+ describe('with "cache"' , function ()
+ it('knows XDG_CACHE_HOME', function()
+ clear({env={
+ XDG_CACHE_HOME=alter_slashes('/home/docwhat/.cache'),
+ }})
+ eq(alter_slashes('/home/docwhat/.cache/nvim'), funcs.stdpath('cache'))
+ end)
+
+ it('handles changes during runtime', function()
+ clear({env={
+ XDG_CACHE_HOME=alter_slashes('/home/original'),
+ }})
+ eq(alter_slashes('/home/original/nvim'), funcs.stdpath('cache'))
+ command("let $XDG_CACHE_HOME='"..alter_slashes('/home/new').."'")
+ eq(alter_slashes('/home/new/nvim'), funcs.stdpath('cache'))
+ end)
+
+ it("doesn't expand $VARIABLES", function()
+ clear({env={
+ XDG_CACHE_HOME='$VARIABLES',
+ VARIABLES='this-should-not-happen',
+ }})
+ eq(alter_slashes('$VARIABLES/nvim'), funcs.stdpath('cache'))
+ end)
+
+ it("doesn't expand ~/", function()
+ clear({env={
+ XDG_CACHE_HOME=alter_slashes('~/frobnitz'),
+ }})
+ eq(alter_slashes('~/frobnitz/nvim'), funcs.stdpath('cache'))
+ end)
+ end)
+ end)
+
+ context('returns a List', function()
+ -- Some OS specific variables the system would have set.
+ local function base_env()
+ if helpers.iswin() then
+ return {
+ HOME='C:\\Users\\docwhat', -- technically, is not a usual PATH
+ HOMEDRIVE='C:',
+ HOMEPATH='\\Users\\docwhat',
+ LOCALAPPDATA='C:\\Users\\docwhat\\AppData\\Local',
+ TEMP='C:\\Users\\docwhat\\AppData\\Local\\Temp',
+ TMPDIR='C:\\Users\\docwhat\\AppData\\Local\\Temp',
+ TMP='C:\\Users\\docwhat\\AppData\\Local\\Temp',
+ }
+ else
+ return {
+ HOME='/home/docwhat',
+ HOMEDRIVE='HOMEDRIVE-should-be-ignored',
+ HOMEPATH='HOMEPATH-should-be-ignored',
+ LOCALAPPDATA='LOCALAPPDATA-should-be-ignored',
+ TEMP='TEMP-should-be-ignored',
+ TMPDIR='TMPDIR-should-be-ignored',
+ TMP='TMP-should-be-ignored',
+ }
+ end
+ end
+
+ local function set_paths_via_system(var_name, paths)
+ local env = base_env()
+ env[var_name] = table.concat(paths, ':')
+ clear({env=env})
+ end
+
+ local function set_paths_at_runtime(var_name, paths)
+ clear({env=base_env()})
+ meths.set_var('env_val', table.concat(paths, ':'))
+ command(('let $%s=g:env_val'):format(var_name))
+ end
+
+ local function behaves_like_dir_list_env(msg, stdpath_arg, env_var_name, paths, expected_paths)
+ describe(msg, function()
+ it('set via system', function()
+ set_paths_via_system(env_var_name, paths)
+ eq(expected_paths, funcs.stdpath(stdpath_arg))
+ end)
+
+ it('set at runtime', function()
+ set_paths_at_runtime(env_var_name, paths)
+ eq(expected_paths, funcs.stdpath(stdpath_arg))
+ end)
+ end)
+ end
+
+ describe('with "config_dirs"' , function ()
+ behaves_like_dir_list_env(
+ 'handles XDG_CONFIG_DIRS with one path',
+ 'config_dirs', 'XDG_CONFIG_DIRS',
+ {
+ alter_slashes('/home/docwhat/.config')
+ },
+ {
+ alter_slashes('/home/docwhat/.config/nvim')
+ })
+
+ behaves_like_dir_list_env(
+ 'handles XDG_CONFIG_DIRS with two paths',
+ 'config_dirs', 'XDG_CONFIG_DIRS',
+ {
+ alter_slashes('/home/docwhat/.config'),
+ alter_slashes('/etc/config')
+ },
+ {
+ alter_slashes('/home/docwhat/.config/nvim'),
+ alter_slashes('/etc/config/nvim')
+ })
+
+ behaves_like_dir_list_env(
+ "doesn't expand $VAR and $IBLES",
+ 'config_dirs', 'XDG_CONFIG_DIRS',
+ { '$HOME', '$TMP' },
+ {
+ alter_slashes('$HOME/nvim'),
+ alter_slashes('$TMP/nvim')
+ })
+
+
+ behaves_like_dir_list_env(
+ "doesn't expand ~/",
+ 'config_dirs', 'XDG_CONFIG_DIRS',
+ {
+ alter_slashes('~/.oldconfig'),
+ alter_slashes('~/.olderconfig')
+ },
+ {
+ alter_slashes('~/.oldconfig/nvim'),
+ alter_slashes('~/.olderconfig/nvim')
+ })
+ end)
+
+ describe('with "data_dirs"' , function ()
+ behaves_like_dir_list_env(
+ 'knows XDG_DATA_DIRS with one path',
+ 'data_dirs', 'XDG_DATA_DIRS',
+ {
+ alter_slashes('/home/docwhat/.data')
+ },
+ {
+ alter_slashes('/home/docwhat/.data/nvim')
+ })
+
+ behaves_like_dir_list_env(
+ 'knows XDG_DATA_DIRS with two paths',
+ 'data_dirs', 'XDG_DATA_DIRS',
+ {
+ alter_slashes('/home/docwhat/.data'),
+ alter_slashes('/etc/local')
+ },
+ {
+ alter_slashes('/home/docwhat/.data/nvim'),
+ alter_slashes('/etc/local/nvim'),
+ })
+
+ behaves_like_dir_list_env(
+ "doesn't expand $VAR and $IBLES",
+ 'data_dirs', 'XDG_DATA_DIRS',
+ { '$HOME', '$TMP' },
+ {
+ alter_slashes('$HOME/nvim'),
+ alter_slashes('$TMP/nvim')
+ })
+
+ behaves_like_dir_list_env(
+ "doesn't expand ~/",
+ 'data_dirs', 'XDG_DATA_DIRS',
+ {
+ alter_slashes('~/.oldconfig'),
+ alter_slashes('~/.olderconfig')
+ },
+ {
+ alter_slashes('~/.oldconfig/nvim'),
+ alter_slashes('~/.olderconfig/nvim'),
+ })
+ end)
+ end)
+
+ describe('errors', function()
+ it('on unknown strings', function()
+ eq('Vim(call):E6100: "capybara" is not a valid stdpath', exc_exec('call stdpath("capybara")'))
+ eq('Vim(call):E6100: "" is not a valid stdpath', exc_exec('call stdpath("")'))
+ eq('Vim(call):E6100: "23" is not a valid stdpath', exc_exec('call stdpath(23)'))
+ end)
+
+ it('on non-strings', function()
+ eq('Vim(call):E731: using Dictionary as a String', exc_exec('call stdpath({"eris": 23})'))
+ eq('Vim(call):E730: using List as a String', exc_exec('call stdpath([23])'))
+ end)
+ end)
+end)
diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua
new file mode 100644
index 0000000000..ed17ffdd3c
--- /dev/null
+++ b/test/functional/options/num_options_spec.lua
@@ -0,0 +1,97 @@
+-- Tests for :setlocal and :setglobal
+
+local helpers = require('test.functional.helpers')(after_each)
+local clear, feed_command, eval, eq, meths =
+ helpers.clear, helpers.feed_command, helpers.eval, helpers.eq, helpers.meths
+
+local function should_fail(opt, value, errmsg)
+ feed_command('setglobal ' .. opt .. '=' .. value)
+ eq(errmsg, eval("v:errmsg"):match("E%d*"))
+ feed_command('let v:errmsg = ""')
+ feed_command('setlocal ' .. opt .. '=' .. value)
+ eq(errmsg, eval("v:errmsg"):match("E%d*"))
+ feed_command('let v:errmsg = ""')
+ local status, err = pcall(meths.set_option, opt, value)
+ eq(status, false)
+ eq(errmsg, err:match("E%d*"))
+ eq('', eval("v:errmsg"))
+end
+
+local function should_succeed(opt, value)
+ feed_command('setglobal ' .. opt .. '=' .. value)
+ feed_command('setlocal ' .. opt .. '=' .. value)
+ meths.set_option(opt, value)
+ eq(value, meths.get_option(opt))
+ eq('', eval("v:errmsg"))
+end
+
+describe(':setlocal', function()
+ before_each(clear)
+
+ it('setlocal sets only local value', function()
+ eq(0, meths.get_option('iminsert'))
+ feed_command('setlocal iminsert=1')
+ eq(0, meths.get_option('iminsert'))
+ eq(0, meths.get_option('imsearch'))
+ feed_command('setlocal imsearch=1')
+ eq(0, meths.get_option('imsearch'))
+ end)
+end)
+
+describe(':set validation', function()
+ before_each(clear)
+
+ it('setlocal and setglobal validate values', function()
+ should_fail('shiftwidth', -10, 'E487')
+ should_succeed('shiftwidth', 0)
+ should_fail('tabstop', -10, 'E487')
+ should_fail('winheight', -10, 'E487')
+ should_fail('winheight', 0, 'E487')
+ should_fail('winminheight', -1, 'E487')
+ should_succeed('winminheight', 0)
+ should_fail('winwidth', 0, 'E487')
+ should_fail('helpheight', -1, 'E487')
+ should_fail('maxcombine', 7, 'E474')
+ should_fail('iminsert', 3, 'E474')
+ should_fail('imsearch', 3, 'E474')
+ should_fail('titlelen', -1, 'E487')
+ should_fail('cmdheight', 0, 'E487')
+ should_fail('updatecount', -1, 'E487')
+ should_fail('textwidth', -1, 'E487')
+ should_fail('tabstop', 0, 'E487')
+ should_fail('timeoutlen', -1, 'E487')
+ should_fail('history', 1000000, 'E474')
+ should_fail('regexpengine', -1, 'E474')
+ should_fail('regexpengine', 3, 'E474')
+ should_succeed('regexpengine', 2)
+ should_fail('report', -1, 'E487')
+ should_succeed('report', 0)
+ should_fail('scrolloff', -1, 'E49')
+ should_fail('sidescrolloff', -1, 'E487')
+ should_fail('sidescroll', -1, 'E487')
+ should_fail('cmdwinheight', 0, 'E487')
+ should_fail('updatetime', -1, 'E487')
+
+ should_fail('foldlevel', -5, 'E487')
+ should_fail('foldcolumn', 13, 'E474')
+ should_fail('conceallevel', 4, 'E474')
+ should_fail('numberwidth', 11, 'E474')
+ should_fail('numberwidth', 0, 'E487')
+
+ -- If smaller than 1 this one is set to 'lines'-1
+ feed_command('setglobal window=-10')
+ meths.set_option('window', -10)
+ eq(23, meths.get_option('window'))
+ eq('', eval("v:errmsg"))
+ end)
+
+ it('set wmh/wh wmw/wiw checks', function()
+ feed_command('set winheight=2')
+ feed_command('set winminheight=3')
+ eq('E591', eval("v:errmsg"):match("E%d*"))
+
+ feed_command('set winwidth=2')
+ feed_command('set winminwidth=3')
+ eq('E592', eval("v:errmsg"):match("E%d*"))
+ end)
+end)
diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua
index 0a12b1a154..f69c3e7c78 100644
--- a/test/functional/provider/nodejs_spec.lua
+++ b/test/functional/provider/nodejs_spec.lua
@@ -35,7 +35,7 @@ describe('nodejs host', function()
nvim.command('call jobstop(g:job_id)');
]])
command('let g:job_id = jobstart(["node", "'..fname..'"])')
- retry(nil, 1000, function() eq('hello', eval('g:job_out')) end)
+ retry(nil, 2000, function() eq('hello', eval('g:job_out')) end)
end)
it('plugin works', function()
local fname = 'Xtest-nodejs-hello-plugin.js'
@@ -45,17 +45,15 @@ describe('nodejs host', function()
const nvim = neovim.attach({socket: socket});
class TestPlugin {
- hello() {
- this.nvim.command('let g:job_out = "hello-plugin"')
- }
+ hello() {
+ this.nvim.command('let g:job_out = "hello-plugin"');
+ }
}
-
const PluginClass = neovim.Plugin(TestPlugin);
- const plugin = new PluginClass(nvim);
- plugin.hello();
- nvim.command('call jobstop(g:job_id)');
+ const plugin = new neovim.NvimPlugin(null, PluginClass, nvim);
+ plugin.instance.hello();
]])
command('let g:job_id = jobstart(["node", "'..fname..'"])')
- retry(nil, 1000, function() eq('hello-plugin', eval('g:job_out')) end)
+ retry(nil, 2000, function() eq('hello-plugin', eval('g:job_out')) end)
end)
end)
diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua
index f06728ec0e..93ac3ae017 100644
--- a/test/functional/provider/python3_spec.lua
+++ b/test/functional/provider/python3_spec.lua
@@ -3,6 +3,7 @@ local eval, command, feed = helpers.eval, helpers.command, helpers.feed
local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert
local expect, write_file = helpers.expect, helpers.write_file
local feed_command = helpers.feed_command
+local source = helpers.source
local missing_provider = helpers.missing_provider
do
@@ -13,7 +14,7 @@ do
end
end
-describe('python3 commands and functions', function()
+describe('python3 provider', function()
before_each(function()
clear()
command('python3 import vim')
@@ -82,4 +83,20 @@ describe('python3 commands and functions', function()
it('py3eval', function()
eq({1, 2, {['key'] = 'val'}}, eval([[py3eval('[1, 2, {"key": "val"}]')]]))
end)
+
+ it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function()
+ source([=[
+ python3 << EOF
+ import vim
+ def foo():
+ vim.eval('expand("<afile>:p")')
+ vim.eval('bufnr(expand("<afile>:p"))')
+ EOF
+ autocmd BufDelete * python3 foo()
+ autocmd BufUnload * python3 foo()]=])
+ feed_command("exe 'split' tempname()")
+ feed_command("bwipeout!")
+ feed_command('help help')
+ eq(2, eval('1+1')) -- Still alive?
+ end)
end)
diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua
index a2c6c6a10e..e049ac7c41 100644
--- a/test/functional/provider/ruby_spec.lua
+++ b/test/functional/provider/ruby_spec.lua
@@ -1,16 +1,18 @@
local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local command = helpers.command
+local curbufmeths = helpers.curbufmeths
local eq = helpers.eq
+local eval = helpers.eval
+local expect = helpers.expect
local feed = helpers.feed
-local clear = helpers.clear
+local feed_command = helpers.feed_command
local funcs = helpers.funcs
-local meths = helpers.meths
local insert = helpers.insert
-local expect = helpers.expect
-local command = helpers.command
-local write_file = helpers.write_file
-local curbufmeths = helpers.curbufmeths
+local meths = helpers.meths
local missing_provider = helpers.missing_provider
+local write_file = helpers.write_file
do
clear()
@@ -90,3 +92,11 @@ describe(':rubydo command', function()
eq(false, curbufmeths.get_option('modified'))
end)
end)
+
+describe('ruby provider', function()
+ it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function()
+ command([=[autocmd BufDelete * ruby VIM::evaluate('expand("<afile>")')]=])
+ feed_command('help help')
+ eq(2, eval('1+1')) -- Still alive?
+ end)
+end)
diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua
index 7a15c8908b..a628baff53 100644
--- a/test/functional/shada/merging_spec.lua
+++ b/test/functional/shada/merging_spec.lua
@@ -525,6 +525,85 @@ describe('ShaDa marks support code', function()
eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t'))
end)
+ it('can merge with file with mark 9 as the only numeric mark', function()
+ wshada('\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9')
+ eq(0, exc_exec(sdrcmd()))
+ nvim_command('normal! `9oabc')
+ eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t'))
+ eq(0, exc_exec('wshada ' .. shada_fname))
+ local found = {}
+ for _, v in ipairs(read_shada_file(shada_fname)) do
+ if v.type == 7 and v.value.f == mock_file_path .. '-' then
+ local name = ('%c'):format(v.value.n)
+ found[name] = (found[name] or 0) + 1
+ end
+ end
+ eq({['0']=1, ['1']=1}, found)
+ end)
+
+ it('removes duplicates while merging', function()
+ wshada('\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9'
+ .. '\007\001\014\130\161f\196\006' .. mock_file_path .. '-\161n9')
+ eq(0, exc_exec(sdrcmd()))
+ eq(0, exc_exec('wshada ' .. shada_fname))
+ local found = 0
+ for _, v in ipairs(read_shada_file(shada_fname)) do
+ if v.type == 7 and v.value.f == mock_file_path .. '-' then
+ print(require('test.helpers').format_luav(v))
+ found = found + 1
+ end
+ end
+ eq(1, found)
+ end)
+
+ it('does not leak when no append is performed due to too many marks',
+ function()
+ wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'a\161n0'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'b\161n1'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161n2'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'd\161n3'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'e\161n4'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'f\161n5'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'g\161n6'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'h\161n7'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'i\161n8'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'j\161n9'
+ .. '\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'k\161n9')
+ eq(0, exc_exec(sdrcmd()))
+ eq(0, exc_exec('wshada ' .. shada_fname))
+ local found = {}
+ for _, v in ipairs(read_shada_file(shada_fname)) do
+ if v.type == 7 and v.value.f:sub(1, #mock_file_path) == mock_file_path then
+ found[#found + 1] = v.value.f:sub(#v.value.f)
+ end
+ end
+ eq({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}, found)
+ end)
+
+ it('does not leak when last mark in file removes some of the earlier ones',
+ function()
+ wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'a\161n0'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'b\161n1'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161n2'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'd\161n3'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'e\161n4'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'f\161n5'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'g\161n6'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'h\161n7'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'i\161n8'
+ .. '\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'j\161n9'
+ .. '\007\003\018\131\162mX\195\161f\196\006' .. mock_file_path .. 'k\161n9')
+ eq(0, exc_exec(sdrcmd()))
+ eq(0, exc_exec('wshada ' .. shada_fname))
+ local found = {}
+ for _, v in ipairs(read_shada_file(shada_fname)) do
+ if v.type == 7 and v.value.f:sub(1, #mock_file_path) == mock_file_path then
+ found[#found + 1] = v.value.f:sub(#v.value.f)
+ end
+ end
+ eq({'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'}, found)
+ end)
+
it('uses last A mark with gt timestamp from file when reading with !',
function()
wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161nA')
@@ -563,13 +642,14 @@ describe('ShaDa marks support code', function()
nvim_command('normal! `A')
eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t'))
eq(0, exc_exec('wshada ' .. shada_fname))
- local found = 0
+ local found = {}
for _, v in ipairs(read_shada_file(shada_fname)) do
- if v.type == 7 and v.value.f == '' .. mock_file_path .. '-' then
- found = found + 1
+ if v.type == 7 and v.value.f == mock_file_path .. '-' then
+ local name = ('%c'):format(v.value.n)
+ found[name] = (found[name] or 0) + 1
end
end
- eq(1, found)
+ eq({['0']=1, A=1}, found)
end)
it('uses last A mark with eq timestamp from instance when writing',
@@ -580,30 +660,33 @@ describe('ShaDa marks support code', function()
nvim_command('normal! `A')
eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t'))
eq(0, exc_exec('wshada ' .. shada_fname))
- local found = 0
+ local found = {}
for _, v in ipairs(read_shada_file(shada_fname)) do
if v.type == 7 and v.value.f == mock_file_path .. '-' then
- found = found + 1
+ local name = ('%c'):format(v.value.n)
+ found[name] = (found[name] or 0) + 1
end
end
- eq(1, found)
+ eq({['0']=1, A=1}, found)
end)
- it('uses last A mark with gt timestamp from file when writing',
- function()
+ it('uses last A mark with gt timestamp from file when writing', function()
wshada('\007\001\018\131\162mX\195\161f\196\006' .. mock_file_path .. '-\161nA')
eq(0, exc_exec(sdrcmd()))
wshada('\007\002\018\131\162mX\195\161f\196\006' .. mock_file_path .. '?\161nA')
nvim_command('normal! `A')
eq('-', funcs.fnamemodify(curbufmeths.get_name(), ':t'))
eq(0, exc_exec('wshada ' .. shada_fname))
- local found = 0
+ local found = {}
for _, v in ipairs(read_shada_file(shada_fname)) do
- if v.type == 7 and v.value.f == '' .. mock_file_path .. '?' then
- found = found + 1
+ if v.type == 7 then
+ local name = ('%c'):format(v.value.n)
+ local t = found[name] or {}
+ t[v.value.f] = (t[v.value.f] or 0) + 1
+ found[name] = t
end
end
- eq(1, found)
+ eq({['0']={[mock_file_path .. '-']=1}, A={[mock_file_path .. '?']=1}}, found)
end)
it('uses last a mark with gt timestamp from instance when reading',
diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua
index ca44026852..720855860a 100644
--- a/test/functional/shada/shada_spec.lua
+++ b/test/functional/shada/shada_spec.lua
@@ -181,13 +181,13 @@ describe('ShaDa support code', function()
nvim_command('set shada+=%')
nvim_command('wshada! ' .. shada_fname)
local readme_fname = funcs.resolve(paths.test_source_path) .. '/README.md'
- eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname))
+ eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname))
nvim_command('set shada+=r~')
nvim_command('wshada! ' .. shada_fname)
eq({}, find_file(readme_fname))
nvim_command('set shada-=r~')
nvim_command('wshada! ' .. shada_fname)
- eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname))
+ eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname))
nvim_command('set shada+=r' .. funcs.escape(
funcs.escape(paths.test_source_path, '$~'), ' "\\,'))
nvim_command('wshada! ' .. shada_fname)
@@ -206,7 +206,7 @@ describe('ShaDa support code', function()
nvim_command('undo')
nvim_command('set shada+=%')
nvim_command('wshada! ' .. shada_fname)
- eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname))
+ eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname))
nvim_command('set shada+=r' .. pwd .. '/АБВ')
nvim_command('wshada! ' .. shada_fname)
eq({}, find_file(fname))
diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua
index d2b2d8a60c..84d7ae6e9c 100644
--- a/test/functional/terminal/edit_spec.lua
+++ b/test/functional/terminal/edit_spec.lua
@@ -36,6 +36,7 @@ describe(':edit term://*', function()
local scr = get_screen(columns, lines)
local rep = 'a'
meths.set_option('shellcmdflag', 'REP ' .. rep)
+ command('set shellxquote=') -- win: avoid extra quotes
local rep_size = rep:byte() -- 'a' => 97
local sb = 10
command('autocmd TermOpen * :setlocal scrollback='..tostring(sb)
diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua
index 0a30b9bb02..4f22f7385d 100644
--- a/test/functional/terminal/ex_terminal_spec.lua
+++ b/test/functional/terminal/ex_terminal_spec.lua
@@ -8,6 +8,7 @@ local funcs = helpers.funcs
local retry = helpers.retry
local ok = helpers.ok
local iswin = helpers.iswin
+local command = helpers.command
describe(':terminal', function()
local screen
@@ -143,6 +144,7 @@ describe(':terminal (with fake shell)', function()
end)
it('executes a given command through the shell', function()
+ command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell('echo hi')
screen:expect([[
^ready $ echo hi |
@@ -154,6 +156,7 @@ describe(':terminal (with fake shell)', function()
it("executes a given command through the shell, when 'shell' has arguments", function()
nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff')
+ command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell('echo hi')
screen:expect([[
^jeff $ echo hi |
@@ -164,6 +167,7 @@ describe(':terminal (with fake shell)', function()
end)
it('allows quotes and slashes', function()
+ command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell([[echo 'hello' \ "world"]])
screen:expect([[
^ready $ echo 'hello' \ "world" |
@@ -217,6 +221,7 @@ describe(':terminal (with fake shell)', function()
end)
it('works with gf', function()
+ command('set shellxquote=') -- win: avoid extra quotes
terminal_with_fake_shell([[echo "scripts/shadacat.py"]])
screen:expect([[
^ready $ echo "scripts/shadacat.py" |
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index a21d9f0a56..263a5ce79d 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -101,7 +101,7 @@ describe('terminal mouse', function()
line28 │line28 |
line29 │line29 |
line30 │line30 |
- rows: 5, cols: 24 │rows: 5, cols: 24 |
+ rows: 5, cols: 25 │rows: 5, cols: 25 |
{2:^ } │{2: } |
========== ========== |
:vsp |
@@ -111,7 +111,7 @@ describe('terminal mouse', function()
{7: 1 }^ │line28 |
{4:~ }│line29 |
{4:~ }│line30 |
- {4:~ }│rows: 5, cols: 24 |
+ {4:~ }│rows: 5, cols: 25 |
{4:~ }│{2: } |
========== ========== |
:enew | set number |
@@ -121,7 +121,7 @@ describe('terminal mouse', function()
{7: 27 }line │line28 |
{7: 28 }line │line29 |
{7: 29 }line │line30 |
- {7: 30 }line │rows: 5, cols: 24 |
+ {7: 30 }line │rows: 5, cols: 25 |
{7: 31 }^ │{2: } |
========== ========== |
|
@@ -131,7 +131,7 @@ describe('terminal mouse', function()
{7: 27 }line │line28 |
{7: 28 }line │line29 |
{7: 29 }line │line30 |
- {7: 30 }line │rows: 5, cols: 24 |
+ {7: 30 }line │rows: 5, cols: 25 |
{7: 31 } │{1: } |
========== ========== |
{3:-- TERMINAL --} |
@@ -142,7 +142,7 @@ describe('terminal mouse', function()
screen:expect([[
{7: 27 }line │line29 |
{7: 28 }line │line30 |
- {7: 29 }line │rows: 5, cols: 24 |
+ {7: 29 }line │rows: 5, cols: 25 |
{7: 30 }line │mouse enabled |
{7: 31 } │{1: } |
========== ========== |
@@ -155,7 +155,7 @@ describe('terminal mouse', function()
screen:expect([[
{7: 21 }line │line29 |
{7: 22 }line │line30 |
- {7: 23 }line │rows: 5, cols: 24 |
+ {7: 23 }line │rows: 5, cols: 25 |
{7: 24 }line │mouse enabled |
{7: 25 }line │{1: } |
========== ========== |
@@ -165,7 +165,7 @@ describe('terminal mouse', function()
screen:expect([[
{7: 26 }line │line29 |
{7: 27 }line │line30 |
- {7: 28 }line │rows: 5, cols: 24 |
+ {7: 28 }line │rows: 5, cols: 25 |
{7: 29 }line │mouse enabled |
{7: 30 }line │{1: } |
========== ========== |
@@ -178,7 +178,7 @@ describe('terminal mouse', function()
screen:expect([[
{7: 27 }line │line29 |
{7: 28 }l^ine │line30 |
- {7: 29 }line │rows: 5, cols: 24 |
+ {7: 29 }line │rows: 5, cols: 25 |
{7: 30 }line │mouse enabled |
{7: 31 } │{2: } |
========== ========== |
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 171745eb57..0ae5802a01 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -115,16 +115,12 @@ describe('tui', function()
]])
end)
- it('does not mangle unmapped ALT-key chord', function()
- -- Vim represents ALT/META by setting the "high bit" of the modified key;
- -- we do _not_. #3982
- --
- -- Example: for input ALT+j:
- -- * Vim (Nvim prior to #3982) sets high-bit, inserts "ê".
- -- * Nvim (after #3982) inserts "j".
- feed_data('i\027j')
+ it('interprets ESC+key as ALT chord', function()
+ -- Vim represents ALT/META by setting the "high bit" of the modified key:
+ -- ALT+j inserts "ê". Nvim does not (#3982).
+ feed_data('i\022\027j')
screen:expect([[
- j{1: } |
+ <M-j>{1: } |
{4:~ }|
{4:~ }|
{4:~ }|
@@ -391,15 +387,7 @@ describe('tui FocusGained/FocusLost', function()
-- Exit cmdline-mode. Redraws from timers/events are blocked during
-- cmdline-mode, so the buffer won't be updated until we exit cmdline-mode.
feed_data('\n')
- screen:expect([[
- {1: } |
- lost |
- gained |
- {4:~ }|
- {5:[No Name] [+] }|
- : |
- {3:-- TERMINAL --} |
- ]])
+ screen:expect('lost'..(' '):rep(46)..'\ngained', nil, nil, nil, true)
end)
end)
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index ffb6a26aef..3c316d1cfa 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -24,6 +24,7 @@ before_each(function()
clear()
screen = Screen.new(40, 8)
screen:attach()
+ command("set display-=msgsep")
source([[
highlight RBP1 guibg=Red
highlight RBP2 guibg=Yellow
@@ -737,6 +738,22 @@ describe('Command-line coloring', function()
feed('<CR><CR>')
eq('', meths.get_var('out'))
end)
+ it('does not crash when callback has caught not-a-editor-command exception',
+ function()
+ source([[
+ function CaughtExc(cmdline) abort
+ try
+ gibberish
+ catch
+ " Do nothing
+ endtry
+ return []
+ endfunction
+ ]])
+ set_color_cb('CaughtExc')
+ start_prompt('1')
+ eq(1, meths.eval('1'))
+ end)
end)
describe('Ex commands coloring support', function()
it('works', function()
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index f8680678ef..41c290a462 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -157,6 +157,27 @@ describe('external cmdline', function()
end)
end)
+ it("redraws statusline on entering", function()
+ command('set laststatus=2')
+ command('set statusline=%{mode()}')
+ feed(':')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {3:c^ }|
+ |
+ ]], nil, nil, function()
+ eq({{
+ content = { { {}, "" } },
+ firstc = ":",
+ indent = 0,
+ pos = 0,
+ prompt = ""
+ }}, cmdline)
+ end)
+ end)
+
it("works with input()", function()
feed(':call input("input", "default")<cr>')
screen:expect([[
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
index b47210a777..812c095add 100644
--- a/test/functional/ui/cursor_spec.lua
+++ b/test/functional/ui/cursor_spec.lua
@@ -194,8 +194,8 @@ describe('ui/cursor', function()
if m.blinkoff then m.blinkoff = 400 end
if m.blinkwait then m.blinkwait = 700 end
end
- if m.hl_id then m.hl_id = 48 end
- if m.id_lm then m.id_lm = 49 end
+ if m.hl_id then m.hl_id = 49 end
+ if m.id_lm then m.id_lm = 50 end
end
-- Assert the new expectation.
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 6f1b31964b..ab3b1c3cac 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -94,6 +94,7 @@ describe('highlight defaults', function()
clear()
screen = Screen.new()
screen:attach()
+ command("set display-=msgsep")
end)
after_each(function()
@@ -312,7 +313,7 @@ describe('highlight defaults', function()
end)
end)
-describe('guisp (special/undercurl)', function()
+describe('highlight', function()
local screen
before_each(function()
@@ -321,7 +322,31 @@ describe('guisp (special/undercurl)', function()
screen:attach()
end)
- it('can be set and is applied like foreground or background', function()
+ it('cterm=standout gui=standout', function()
+ screen:detach()
+ screen = Screen.new(20,5)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {standout = true, bold = true, underline = true,
+ background = Screen.colors.Gray90, foreground = Screen.colors.Blue1},
+ [3] = {standout = true, underline = true,
+ background = Screen.colors.Gray90}
+ })
+ feed_command('hi CursorLine cterm=standout,underline gui=standout,underline')
+ feed_command('set cursorline')
+ feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list')
+ feed('i\t abcd <cr>\t abcd <cr><esc>k')
+ screen:expect([[
+ {1:>-------.}abcd{1:*¬} |
+ {2:^>-------.}{3:abcd}{2:*¬}{3: }|
+ {1:¬} |
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('guisp (special/undercurl)', function()
feed_command('syntax on')
feed_command('syn keyword TmpKeyword neovim')
feed_command('syn keyword TmpKeyword1 special')
@@ -650,6 +675,76 @@ describe("'listchars' highlight", function()
end)
end)
+describe("MsgSeparator highlight and msgsep fillchar", function()
+ before_each(clear)
+ it("works", function()
+ local screen = Screen.new(50,5)
+ screen:set_default_attr_ids({
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ [2] = {bold=true, reverse=true},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [4] = {background = Screen.colors.Cyan, bold = true, reverse = true},
+ [5] = {bold = true, background = Screen.colors.Magenta}
+ })
+ screen:attach()
+
+ -- defaults
+ feed_command("ls")
+ screen:expect([[
+ |
+ {2: }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+
+ feed_command("set fillchars+=msgsep:-")
+ feed_command("ls")
+ screen:expect([[
+ |
+ {2:--------------------------------------------------}|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+ -- linked to StatusLine per default
+ feed_command("hi StatusLine guibg=Cyan")
+ feed_command("ls")
+ screen:expect([[
+ |
+ {4:--------------------------------------------------}|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+ -- but can be unlinked
+ feed_command("hi clear MsgSeparator")
+ feed_command("hi MsgSeparator guibg=Magenta gui=bold")
+ feed_command("ls")
+ screen:expect([[
+ |
+ {5:--------------------------------------------------}|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+ -- when display doesn't contain msgsep, these options have no effect
+ feed_command("set display-=msgsep")
+ feed_command("ls")
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+end)
+
describe("'winhighlight' highlight", function()
local screen
@@ -683,6 +778,9 @@ describe("'winhighlight' highlight", function()
[22] = {bold = true, foreground = Screen.colors.SeaGreen4},
[23] = {background = Screen.colors.LightMagenta},
[24] = {background = Screen.colors.WebGray},
+ [25] = {bold = true, foreground = Screen.colors.Green1},
+ [26] = {background = Screen.colors.Red},
+ [27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1},
})
command("hi Background1 guibg=DarkBlue")
command("hi Background2 guibg=DarkGreen")
@@ -952,6 +1050,39 @@ describe("'winhighlight' highlight", function()
]])
end)
+ it("background doesn't override syntax background", function()
+ command('syntax on')
+ command('syntax keyword Foobar foobar')
+ command('syntax keyword Article the')
+ command('hi Foobar guibg=#FF0000')
+ command('hi Article guifg=#00FF00 gui=bold')
+ insert('the foobar was foobar')
+ screen:expect([[
+ {25:the} {26:foobar} was {26:fooba}|
+ {26:^r} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ -- winhl=Normal:Group with background doesn't override syntax background,
+ -- but does combine with syntax foreground.
+ command('set winhl=Normal:Background1')
+ screen:expect([[
+ {27:the}{1: }{26:foobar}{1: was }{26:fooba}|
+ {26:^r}{1: }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+ end)
+
it('can override NonText, Conceal and EndOfBuffer', function()
curbufmeths.set_lines(0,-1,true, {"raa\000"})
command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})')
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index 8b0cb82365..ee1a3240a2 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -16,6 +16,8 @@ local retry = helpers.retry
local source = helpers.source
local wait = helpers.wait
local nvim = helpers.nvim
+local iswin = helpers.iswin
+local sleep = helpers.sleep
local default_text = [[
Inc substitution on
@@ -63,6 +65,7 @@ local function common_setup(screen, inccommand, text)
command("syntax on")
command("set nohlsearch")
command("hi Substitute guifg=red guibg=yellow")
+ command("set display-=msgsep")
screen:attach()
screen:set_default_attr_ids({
[1] = {foreground = Screen.colors.Fuchsia},
@@ -1353,6 +1356,23 @@ describe("inccommand=nosplit", function()
:echo 'foo'^ |
]])
end)
+
+ it("does not execute trailing bar-separated commands #7494", function()
+ feed(':%s/two/three/g|q!')
+ screen:expect([[
+ Inc substitution on |
+ {12:three} lines |
+ Inc substitution on |
+ {12:three} lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/two/three/g|q!^ |
+ ]])
+ eq(eval('v:null'), eval('v:exiting'))
+ end)
end)
describe(":substitute, 'inccommand' with a failing expression", function()
@@ -1832,7 +1852,7 @@ describe(":substitute", function()
clear()
end)
- it(", inccommand=split, highlights multiline substitutions", function()
+ it("inccommand=split, highlights multiline substitutions", function()
common_setup(screen, "split", multiline_text)
feed("gg")
@@ -1894,7 +1914,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=nosplit, highlights multiline substitutions", function()
+ it("inccommand=nosplit, highlights multiline substitutions", function()
common_setup(screen, "nosplit", multiline_text)
feed("gg")
@@ -1937,7 +1957,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, highlights multiple matches on a line", function()
+ it("inccommand=split, highlights multiple matches on a line", function()
common_setup(screen, "split", multimatch_text)
command("set gdefault")
feed("gg")
@@ -1962,7 +1982,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=nosplit, highlights multiple matches on a line", function()
+ it("inccommand=nosplit, highlights multiple matches on a line", function()
common_setup(screen, "nosplit", multimatch_text)
command("set gdefault")
feed("gg")
@@ -1987,7 +2007,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, with \\zs", function()
+ it("inccommand=split, with \\zs", function()
common_setup(screen, "split", multiline_text)
feed("gg")
@@ -2011,7 +2031,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=nosplit, with \\zs", function()
+ it("inccommand=nosplit, with \\zs", function()
common_setup(screen, "nosplit", multiline_text)
feed("gg")
@@ -2035,7 +2055,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, substitutions of different length",
+ it("inccommand=split, substitutions of different length",
function()
common_setup(screen, "split", "T T123 T2T TTT T090804\nx")
@@ -2059,7 +2079,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=nosplit, substitutions of different length", function()
+ it("inccommand=nosplit, substitutions of different length", function()
common_setup(screen, "nosplit", "T T123 T2T TTT T090804\nx")
feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g")
@@ -2082,7 +2102,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, contraction of lines", function()
+ it("inccommand=split, contraction of lines", function()
local text = [[
T T123 T T123 T2T TT T23423424
x
@@ -2131,7 +2151,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=nosplit, contraction of lines", function()
+ it("inccommand=nosplit, contraction of lines", function()
local text = [[
T T123 T T123 T2T TT T23423424
x
@@ -2161,7 +2181,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, multibyte text", function()
+ it("inccommand=split, multibyte text", function()
common_setup(screen, "split", multibyte_text)
feed(":%s/£.*ѫ/X¥¥")
screen:expect([[
@@ -2202,7 +2222,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=nosplit, multibyte text", function()
+ it("inccommand=nosplit, multibyte text", function()
common_setup(screen, "nosplit", multibyte_text)
feed(":%s/£.*ѫ/X¥¥")
screen:expect([[
@@ -2243,7 +2263,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, small cmdwinheight", function()
+ it("inccommand=split, small cmdwinheight", function()
common_setup(screen, "split", long_multiline_text)
command("set cmdwinheight=2")
@@ -2305,7 +2325,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, large cmdwinheight", function()
+ it("inccommand=split, large cmdwinheight", function()
common_setup(screen, "split", long_multiline_text)
command("set cmdwinheight=11")
@@ -2367,7 +2387,7 @@ describe(":substitute", function()
]])
end)
- it(", inccommand=split, lookaround", function()
+ it("inccommand=split, lookaround", function()
common_setup(screen, "split", "something\neverything\nsomeone")
feed([[:%s/\(some\)\@<lt>=thing/one/]])
screen:expect([[
@@ -2451,4 +2471,57 @@ describe(":substitute", function()
:%s/some\(thing\)\@!/every/^ |
]])
end)
+
+ it('with inccommand during :terminal activity', function()
+ command("set cmdwinheight=3")
+ if iswin() then
+ feed([[:terminal for /L \%I in (1,1,5000) do @(echo xxx & echo xxx & echo xxx)<cr>]])
+ else
+ feed([[:terminal for i in $(seq 1 5000); do printf 'xxx\nxxx\nxxx\n'; done<cr>]])
+ end
+ command('file term')
+ command('new')
+ common_setup(screen, 'split', 'foo bar baz\nbar baz fox\nbar foo baz')
+ command('wincmd =')
+
+ -- Allow some terminal output.
+ screen:expect([[
+ bar baz fox |
+ bar foo ba^z |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ xxx |
+ xxx |
+ xxx |
+ xxx |
+ xxx |
+ xxx |
+ {10:term }|
+ |
+ ]])
+
+ feed('gg')
+ feed(':%s/foo/ZZZ')
+ sleep(50) -- Allow some terminal activity.
+ screen:expect([[
+ {12:ZZZ} bar baz |
+ bar baz fox |
+ bar {12:ZZZ} baz |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ xxx |
+ xxx |
+ {10:term }|
+ |1| {12:ZZZ} bar baz |
+ |3| bar {12:ZZZ} baz |
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/foo/ZZZ^ |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 8e62a37ef1..3dd9a2506e 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim
local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq
+local command = helpers.command
local expect = helpers.expect
local write_file = helpers.write_file
local Screen = require('test.functional.ui.screen')
@@ -127,6 +128,10 @@ describe('input utf sequences that contain CSI/K_SPECIAL', function()
end)
describe('input non-printable chars', function()
+ after_each(function()
+ os.remove('Xtest-overwrite')
+ end)
+
it("doesn't crash when echoing them back", function()
write_file("Xtest-overwrite", [[foobar]])
clear()
@@ -137,6 +142,7 @@ describe('input non-printable chars', function()
[3] = {bold = true, foreground = Screen.colors.SeaGreen4}
})
screen:attach()
+ command("set display-=msgsep")
feed_command("e Xtest-overwrite")
screen:expect([[
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index 05d5a6409a..debd324977 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -26,6 +26,7 @@ describe('ui/mouse/input', function()
[4] = {reverse = true},
[5] = {bold = true, reverse = true},
})
+ command("set display-=msgsep")
feed('itesting<cr>mouse<cr>support and selection<esc>')
screen:expect([[
testing |
diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua
index 4246020fab..02ca566bd8 100644
--- a/test/functional/ui/output_spec.lua
+++ b/test/functional/ui/output_spec.lua
@@ -40,10 +40,10 @@ describe("shell command :!", function()
-- to avoid triggering a UI flush.
child_session.feed_data(":!printf foo; sleep 200\n")
screen:expect([[
+ |
{4:~ }|
{4:~ }|
- {4:~ }|
- {4:~ }|
+ {5: }|
:!printf foo; sleep 200 |
foo |
{3:-- TERMINAL --} |
@@ -99,6 +99,7 @@ describe("shell command :!", function()
end
local screen = Screen.new(50, 4)
screen:attach()
+ command("set display-=msgsep")
-- Print TAB chars. #2958
feed([[:!printf '1\t2\t3'<CR>]])
screen:expect([[
@@ -153,6 +154,7 @@ describe("shell command :!", function()
[1] = {bold = true, foreground = Screen.colors.Blue1},
[2] = {foreground = Screen.colors.Blue1},
[3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [4] = {bold = true, reverse = true},
})
screen:attach()
end)
@@ -170,10 +172,10 @@ describe("shell command :!", function()
or [[:!ls bang_filter_spec ]])
feed([[\l]])
screen:expect([[
+ |
{1:~ }|
{1:~ }|
- {1:~ }|
- {1:~ }|
+ {4: }|
]]..result..[[ |
f1 |
f2 |
@@ -187,9 +189,9 @@ describe("shell command :!", function()
feed_command('!cat test/functional/fixtures/shell_data.txt')
screen.bell = false
screen:expect([[
+ |
{1:~ }|
- {1:~ }|
- {1:~ }|
+ {4: }|
:!cat test/functional/fixtures/shell_data.txt |
{2:^@^A^B^C^D^E^F^H} |
{2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} |
@@ -213,8 +215,8 @@ describe("shell command :!", function()
feed_command(cmd)
-- Note: only the first example of split composed char works
screen:expect([[
- {1:~ }|
- {1:~ }|
+ |
+ {4: }|
:]]..cmd..[[ |
å |
ref: å̲ |
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index bbdf576641..06e4a19354 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -76,11 +76,26 @@ describe('Screen', function()
local function check()
eq(true, screen.suspended)
end
+
+ command('let g:ev = []')
+ command('autocmd VimResume * :call add(g:ev, "r")')
+ command('autocmd VimSuspend * :call add(g:ev, "s")')
+
+ eq(false, screen.suspended)
command('suspend')
+ eq({ 's', 'r' }, eval('g:ev'))
+
screen:expect(check)
screen.suspended = false
+
feed('<c-z>')
+ eq({ 's', 'r', 's', 'r' }, eval('g:ev'))
+
screen:expect(check)
+ screen.suspended = false
+
+ command('suspend')
+ eq({ 's', 'r', 's', 'r', 's', 'r' }, eval('g:ev'))
end)
end)
@@ -225,8 +240,8 @@ describe('Screen', function()
end)
end)
- describe('tabnew', function()
- it('creates a new buffer', function()
+ describe('tabs', function()
+ it('tabnew creates a new buffer', function()
command('sp')
command('vsp')
command('vsp')
@@ -284,6 +299,62 @@ describe('Screen', function()
|
]])
end)
+
+ it('tabline is redrawn after messages', function()
+ command('tabnew')
+ screen:expect([[
+ {4: [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed(':echo "'..string.rep('x\\n', 11)..'"<cr>')
+ screen:expect([[
+ {1: }|
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ |
+ {7:Press ENTER or type command to continue}^ |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ {4: [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
end)
describe('insert mode', function()
@@ -355,7 +426,8 @@ describe('Screen', function()
]])
end)
- it('execute command with multi-line output', function()
+ it('execute command with multi-line output without msgsep', function()
+ command("set display-=msgsep")
feed(':ls<cr>')
screen:expect([[
{0:~ }|
@@ -375,6 +447,28 @@ describe('Screen', function()
]])
feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
end)
+
+ it('execute command with multi-line output and with msgsep', function()
+ command("set display+=msgsep")
+ feed(':ls<cr>')
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1: }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {7:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
+ end)
end)
describe('scrolling and clearing', function()
@@ -573,6 +667,7 @@ describe('Screen', function()
command('nnoremap <F1> :echo "TEST"<CR>')
feed(':ls<CR>')
screen:expect([[
+ |
{0:~ }|
{0:~ }|
{0:~ }|
@@ -582,8 +677,7 @@ describe('Screen', function()
{0:~ }|
{0:~ }|
{0:~ }|
- {0:~ }|
- {0:~ }|
+ {1: }|
:ls |
1 %a "[No Name]" line 1 |
{7:Press ENTER or type command to continue}^ |
@@ -607,22 +701,43 @@ describe('Screen', function()
]])
end)
end)
-end)
-describe('nvim_ui_attach()', function()
- before_each(function()
- clear()
- end)
- it('handles very large width/height #2180', function()
- local screen = Screen.new(999, 999)
- screen:attach()
- eq(999, eval('&lines'))
- eq(999, eval('&columns'))
- end)
- it('invalid option returns error', function()
- local screen = Screen.new()
- local status, rv = pcall(function() screen:attach({foo={'foo'}}) end)
- eq(false, status)
- eq('No such ui option', rv:match("No such .*"))
+ -- Regression test for #8357
+ it('does not have artifacts after temporary chars in insert mode', function()
+ command('inoremap jk <esc>')
+ feed('ifooj')
+ screen:expect([[
+ foo^j |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
+ ]])
+ feed('k')
+ screen:expect([[
+ fo^o |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
end)
end)
diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua
index 9f273e8dc9..168080a092 100644
--- a/test/functional/ui/searchhl_spec.lua
+++ b/test/functional/ui/searchhl_spec.lua
@@ -4,6 +4,8 @@ local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local feed_command = helpers.feed_command
local eq = helpers.eq
local eval = helpers.eval
+local iswin = helpers.iswin
+local sleep = helpers.sleep
describe('search highlighting', function()
local screen
@@ -91,6 +93,38 @@ describe('search highlighting', function()
]])
end)
+ it('is preserved during :terminal activity', function()
+ if iswin() then
+ feed([[:terminal for /L \%I in (1,1,5000) do @(echo xxx & echo xxx & echo xxx)<cr>]])
+ else
+ feed([[:terminal for i in $(seq 1 5000); do printf 'xxx\nxxx\nxxx\n'; done<cr>]])
+ end
+
+ feed(':file term<CR>')
+ feed(':vnew<CR>')
+ insert([[
+ foo bar baz
+ bar baz foo
+ bar foo baz
+ ]])
+ feed('/foo')
+ sleep(50) -- Allow some terminal activity.
+ screen:expect([[
+ {3:foo} bar baz {3:│}xxx |
+ bar baz {2:foo} {3:│}xxx |
+ bar {2:foo} baz {3:│}xxx |
+ {3:│}xxx |
+ {1:~ }{3:│}xxx |
+ {5:[No Name] [+] }{3:term }|
+ /foo^ |
+ ]], { [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {background = Screen.colors.Yellow},
+ [3] = {reverse = true},
+ [4] = {foreground = Screen.colors.Red},
+ [5] = {bold = true, reverse = true},
+ })
+ end)
+
it('works with incsearch', function()
feed_command('set hlsearch')
feed_command('set incsearch')
diff --git a/test/helpers.lua b/test/helpers.lua
index 91ceed4df1..0d3fe1316b 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -1,6 +1,44 @@
local assert = require('luassert')
+local luv = require('luv')
local lfs = require('lfs')
+local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote)
+local function shell_quote(str)
+ if string.find(str, quote_me) or str == '' then
+ return '"' .. str:gsub('[$%%"\\]', '\\%0') .. '"'
+ else
+ return str
+ end
+end
+
+local function argss_to_cmd(...)
+ local cmd = ''
+ for i = 1, select('#', ...) do
+ local arg = select(i, ...)
+ if type(arg) == 'string' then
+ cmd = cmd .. ' ' ..shell_quote(arg)
+ else
+ for _, subarg in ipairs(arg) do
+ cmd = cmd .. ' ' .. shell_quote(subarg)
+ end
+ end
+ end
+ return cmd
+end
+
+local function popen_r(...)
+ return io.popen(argss_to_cmd(...), 'r')
+end
+
+local function popen_w(...)
+ return io.popen(argss_to_cmd(...), 'w')
+end
+
+-- sleeps the test runner (_not_ the nvim instance)
+local function sleep(ms)
+ luv.sleep(ms)
+end
+
local check_logs_useless_lines = {
['Warning: noted but unhandled ioctl']=1,
['could cause spurious value errors to appear']=2,
@@ -16,6 +54,19 @@ end
local function ok(res)
return assert.is_true(res)
end
+local function matches(pat, actual)
+ if nil ~= string.match(actual, pat) then
+ return true
+ end
+ error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual))
+end
+-- Expect an error matching pattern `pat`.
+local function expect_err(pat, ...)
+ local fn = select(1, ...)
+ local fn_args = {...}
+ table.remove(fn_args, 1)
+ assert.error_matches(function() return fn(unpack(fn_args)) end, pat)
+end
-- initial_path: directory to recurse into
-- re: include pattern (string)
@@ -108,7 +159,7 @@ local uname = (function()
return platform
end
- local status, f = pcall(io.popen, "uname -s")
+ local status, f = pcall(popen_r, 'uname', '-s')
if status then
platform = f:read("*l")
f:close()
@@ -240,7 +291,7 @@ local function check_cores(app, force)
end
local function which(exe)
- local pipe = io.popen('which ' .. exe, 'r')
+ local pipe = popen_r('which', exe)
local ret = pipe:read('*a')
pipe:close()
if ret == '' then
@@ -250,6 +301,19 @@ local function which(exe)
end
end
+local function repeated_read_cmd(...)
+ for _ = 1, 10 do
+ local stream = popen_r(...)
+ local ret = stream:read('*a')
+ stream:close()
+ if ret then
+ return ret
+ end
+ end
+ print('ERROR: Failed to execute ' .. argss_to_cmd(...) .. ': nil return after 10 attempts')
+ return nil
+end
+
local function shallowcopy(orig)
if type(orig) ~= 'table' then
return orig
@@ -469,6 +533,8 @@ format_luav = function(v, indent, opts)
end
elseif type(v) == 'nil' then
ret = 'nil'
+ elseif type(v) == 'boolean' then
+ ret = (v and 'true' or 'false')
else
print(type(v))
-- Not implemented yet
@@ -552,8 +618,62 @@ local function table_flatten(arr)
return result
end
-return {
+local function hexdump(str)
+ local len = string.len(str)
+ local dump = ""
+ local hex = ""
+ local asc = ""
+
+ for i = 1, len do
+ if 1 == i % 8 then
+ dump = dump .. hex .. asc .. "\n"
+ hex = string.format("%04x: ", i - 1)
+ asc = ""
+ end
+
+ local ord = string.byte(str, i)
+ hex = hex .. string.format("%02x ", ord)
+ if ord >= 32 and ord <= 126 then
+ asc = asc .. string.char(ord)
+ else
+ asc = asc .. "."
+ end
+ end
+
+ return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc
+end
+
+local function read_file(name)
+ local file = io.open(name, 'r')
+ if not file then
+ return nil
+ end
+ local ret = file:read('*a')
+ file:close()
+ return ret
+end
+
+-- Dedent the given text and write it to the file name.
+local function write_file(name, text, no_dedent, append)
+ local file = io.open(name, (append and 'a' or 'w'))
+ if type(text) == 'table' then
+ -- Byte blob
+ local bytes = text
+ text = ''
+ for _, char in ipairs(bytes) do
+ text = ('%s%c'):format(text, char)
+ end
+ elseif not no_dedent then
+ text = dedent(text)
+ end
+ file:write(text)
+ file:flush()
+ file:close()
+end
+
+local module = {
REMOVE_THIS = REMOVE_THIS,
+ argss_to_cmd = argss_to_cmd,
check_cores = check_cores,
check_logs = check_logs,
concat_tables = concat_tables,
@@ -561,6 +681,7 @@ return {
deepcopy = deepcopy,
dictdiff = dictdiff,
eq = eq,
+ expect_err = expect_err,
filter = filter,
fixtbl = fixtbl,
fixtbl_rec = fixtbl_rec,
@@ -568,15 +689,25 @@ return {
format_string = format_string,
glob = glob,
hasenv = hasenv,
+ hexdump = hexdump,
intchar2lua = intchar2lua,
map = map,
+ matches = matches,
mergedicts_copy = mergedicts_copy,
neq = neq,
ok = ok,
+ popen_r = popen_r,
+ popen_w = popen_w,
+ read_file = read_file,
+ repeated_read_cmd = repeated_read_cmd,
+ sleep = sleep,
shallowcopy = shallowcopy,
table_flatten = table_flatten,
tmpname = tmpname,
uname = uname,
updated = updated,
which = which,
+ write_file = write_file,
}
+
+return module
diff --git a/test/includes/pre/sys/stat.h b/test/includes/pre/sys/stat.h
index c6cac80913..1cb811d579 100644
--- a/test/includes/pre/sys/stat.h
+++ b/test/includes/pre/sys/stat.h
@@ -1,4 +1,6 @@
-#define _GNU_SOURCE
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
#include <sys/stat.h>
static const mode_t kS_IFMT = S_IFMT;
diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua
index 5cc2be50b1..f8143a0125 100644
--- a/test/unit/helpers.lua
+++ b/test/unit/helpers.lua
@@ -779,7 +779,7 @@ local function gen_itp(it)
end
local function cppimport(path)
- return cimport(Paths.test_include_path .. '/' .. path)
+ return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path)
end
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua
index d9c98e8afa..4d58a8934e 100644
--- a/test/unit/os/fileio_spec.lua
+++ b/test/unit/os/fileio_spec.lua
@@ -113,7 +113,7 @@ end
describe('file_open_fd', function()
itp('can use file descriptor returned by os_open for reading', function()
local fd = m.os_open(file1, m.kO_RDONLY, 0)
- local err, fp = file_open_fd(fd, false)
+ local err, fp = file_open_fd(fd, m.kFileReadOnly)
eq(0, err)
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
eq(0, m.file_close(fp, false))
@@ -121,7 +121,7 @@ describe('file_open_fd', function()
itp('can use file descriptor returned by os_open for writing', function()
eq(nil, lfs.attributes(filec))
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
- local err, fp = file_open_fd(fd, true)
+ local err, fp = file_open_fd(fd, m.kFileWriteOnly)
eq(0, err)
eq(4, file_write(fp, 'test'))
eq(0, m.file_close(fp, false))
@@ -133,7 +133,7 @@ end)
describe('file_open_fd_new', function()
itp('can use file descriptor returned by os_open for reading', function()
local fd = m.os_open(file1, m.kO_RDONLY, 0)
- local err, fp = file_open_fd_new(fd, false)
+ local err, fp = file_open_fd_new(fd, m.kFileReadOnly)
eq(0, err)
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
eq(0, m.file_free(fp, false))
@@ -141,7 +141,7 @@ describe('file_open_fd_new', function()
itp('can use file descriptor returned by os_open for writing', function()
eq(nil, lfs.attributes(filec))
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
- local err, fp = file_open_fd_new(fd, true)
+ local err, fp = file_open_fd_new(fd, m.kFileWriteOnly)
eq(0, err)
eq(4, file_write(fp, 'test'))
eq(0, m.file_free(fp, false))
diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua
index bc39507c08..ae6dfe6423 100644
--- a/test/unit/os/fs_spec.lua
+++ b/test/unit/os/fs_spec.lua
@@ -199,7 +199,7 @@ describe('fs.c', function()
itp('returns the absolute path when given an executable inside $PATH', function()
local fullpath = exe('ls')
- eq(1, fs.path_is_absolute_path(to_cstr(fullpath)))
+ eq(1, fs.path_is_absolute(to_cstr(fullpath)))
end)
itp('returns the absolute path when given an executable relative to the current dir', function()
@@ -390,7 +390,7 @@ describe('fs.c', function()
buf = ffi.new('char[?]', size + 1, ('\0'):rep(size))
end
local eof = ffi.new('bool[?]', 1, {true})
- local ret2 = fs.os_read(fd, eof, buf, size)
+ local ret2 = fs.os_read(fd, eof, buf, size, false)
local ret1 = eof[0]
local ret3 = ''
if buf ~= nil then
@@ -408,7 +408,7 @@ describe('fs.c', function()
end
local iov = ffi.new('struct iovec[?]', #sizes, bufs)
local eof = ffi.new('bool[?]', 1, {true})
- local ret2 = fs.os_readv(fd, eof, iov, #sizes)
+ local ret2 = fs.os_readv(fd, eof, iov, #sizes, false)
local ret1 = eof[0]
local ret3 = {}
for i = 1,#sizes do
@@ -418,7 +418,7 @@ describe('fs.c', function()
return ret1, ret2, ret3
end
local function os_write(fd, data)
- return fs.os_write(fd, data, data and #data or 0)
+ return fs.os_write(fd, data, data and #data or 0, false)
end
describe('os_path_exists', function()
@@ -491,6 +491,22 @@ describe('fs.c', function()
end)
end)
+ describe('os_dup', function()
+ itp('returns new file descriptor', function()
+ local dup0 = fs.os_dup(0)
+ local dup1 = fs.os_dup(1)
+ local dup2 = fs.os_dup(2)
+ local tbl = {[0]=true, [1]=true, [2]=true,
+ [tonumber(dup0)]=true, [tonumber(dup1)]=true,
+ [tonumber(dup2)]=true}
+ local i = 0
+ for _, _ in pairs(tbl) do
+ i = i + 1
+ end
+ eq(i, 6) -- All fds must be unique
+ end)
+ end)
+
describe('os_open', function()
local new_file = 'test_new_file'
local existing_file = 'unit-test-directory/test_existing.file'
diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua
index ed597eaed7..e8ce660ce1 100644
--- a/test/unit/path_spec.lua
+++ b/test/unit/path_spec.lua
@@ -261,7 +261,7 @@ describe('path.c', function()
end)
end)
-describe('path_shorten_fname_if_possible', function()
+describe('path_try_shorten_fname', function()
local cwd = lfs.currentdir()
before_each(function()
@@ -273,22 +273,22 @@ describe('path_shorten_fname_if_possible', function()
lfs.rmdir('ut_directory')
end)
- describe('path_shorten_fname_if_possible', function()
+ describe('path_try_shorten_fname', function()
itp('returns shortened path if possible', function()
lfs.chdir('ut_directory')
local full = to_cstr(lfs.currentdir() .. '/subdir/file.txt')
- eq('subdir/file.txt', (ffi.string(cimp.path_shorten_fname_if_possible(full))))
+ eq('subdir/file.txt', (ffi.string(cimp.path_try_shorten_fname(full))))
end)
itp('returns `full_path` if a shorter version is not possible', function()
local old = lfs.currentdir()
lfs.chdir('ut_directory')
local full = old .. '/subdir/file.txt'
- eq(full, (ffi.string(cimp.path_shorten_fname_if_possible(to_cstr(full)))))
+ eq(full, (ffi.string(cimp.path_try_shorten_fname(to_cstr(full)))))
end)
itp('returns NULL if `full_path` is NULL', function()
- eq(NULL, (cimp.path_shorten_fname_if_possible(NULL)))
+ eq(NULL, (cimp.path_try_shorten_fname(NULL)))
end)
end)
end)
@@ -585,22 +585,22 @@ describe('path.c', function()
end)
end)
- describe('path_is_absolute_path', function()
- local function path_is_absolute_path(filename)
+ describe('path_is_absolute', function()
+ local function path_is_absolute(filename)
filename = to_cstr(filename)
- return cimp.path_is_absolute_path(filename)
+ return cimp.path_is_absolute(filename)
end
itp('returns true if filename starts with a slash', function()
- eq(OK, path_is_absolute_path('/some/directory/'))
+ eq(OK, path_is_absolute('/some/directory/'))
end)
itp('returns true if filename starts with a tilde', function()
- eq(OK, path_is_absolute_path('~/in/my/home~/directory'))
+ eq(OK, path_is_absolute('~/in/my/home~/directory'))
end)
itp('returns false if filename starts not with slash nor tilde', function()
- eq(FAIL, path_is_absolute_path('not/in/my/home~/directory'))
+ eq(FAIL, path_is_absolute('not/in/my/home~/directory'))
end)
end)
end)
diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua
index 363358d134..1073855a7d 100644
--- a/test/unit/preprocess.lua
+++ b/test/unit/preprocess.lua
@@ -2,6 +2,10 @@
-- windows, will probably need quite a bit of adjustment to run there.
local ffi = require("ffi")
+local global_helpers = require('test.helpers')
+
+local argss_to_cmd = global_helpers.argss_to_cmd
+local repeated_read_cmd = global_helpers.repeated_read_cmd
local ccs = {}
@@ -22,15 +26,6 @@ table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.7"}, type = "gcc"})
table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"})
table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"})
-local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote)
-local function shell_quote(str)
- if string.find(str, quote_me) or str == '' then
- return "'" .. string.gsub(str, "'", [['"'"']]) .. "'"
- else
- return str
- end
-end
-
-- parse Makefile format dependencies into a Lua table
local function parse_make_deps(deps)
-- remove line breaks and line concatenators
@@ -149,16 +144,6 @@ function Gcc:add_to_include_path(...)
end
end
-local function argss_to_cmd(...)
- local cmd = ''
- for i = 1, select('#', ...) do
- for _, arg in ipairs(select(i, ...)) do
- cmd = cmd .. ' ' .. shell_quote(arg)
- end
- end
- return cmd
-end
-
-- returns a list of the headers files upon which this file relies
function Gcc:dependencies(hdr)
local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1'
@@ -172,29 +157,15 @@ function Gcc:dependencies(hdr)
end
end
-local function repeated_call(...)
- local cmd = argss_to_cmd(...)
- for _ = 1, 10 do
- local stream = io.popen(cmd)
- local ret = stream:read('*a')
- stream:close()
- if ret then
- return ret
- end
- end
- print('ERROR: preprocess.lua: Failed to execute ' .. cmd .. ': nil return after 10 attempts')
- return nil
-end
-
function Gcc:filter_standard_defines(defines)
if not self.standard_defines then
local pseudoheader_fname = 'tmp_empty_pseudoheader.h'
local pseudoheader_file = io.open(pseudoheader_fname, 'w')
pseudoheader_file:close()
- local standard_defines = repeated_call(self.path,
- self.preprocessor_extra_flags,
- self.get_defines_extra_flags,
- {pseudoheader_fname})
+ local standard_defines = repeated_read_cmd(self.path,
+ self.preprocessor_extra_flags,
+ self.get_defines_extra_flags,
+ {pseudoheader_fname})
os.remove(pseudoheader_fname)
self.standard_defines = {}
for line in standard_defines:gmatch('[^\n]+') do
@@ -223,9 +194,9 @@ function Gcc:preprocess(previous_defines, ...)
pseudoheader_file:flush()
pseudoheader_file:close()
- local defines = repeated_call(self.path, self.preprocessor_extra_flags,
- self.get_defines_extra_flags,
- {pseudoheader_fname})
+ local defines = repeated_read_cmd(self.path, self.preprocessor_extra_flags,
+ self.get_defines_extra_flags,
+ {pseudoheader_fname})
defines = self:filter_standard_defines(defines)
-- lfs = require("lfs")
@@ -234,9 +205,10 @@ function Gcc:preprocess(previous_defines, ...)
-- io.stderr\write("CWD: #{lfs.currentdir!}\n")
-- io.stderr\write("CMD: #{cmd}\n")
- local declarations = repeated_call(self.path, self.preprocessor_extra_flags,
- self.get_declarations_extra_flags,
- {pseudoheader_fname})
+ local declarations = repeated_read_cmd(self.path,
+ self.preprocessor_extra_flags,
+ self.get_declarations_extra_flags,
+ {pseudoheader_fname})
os.remove(pseudoheader_fname)
diff --git a/test/unit/undo_spec.lua b/test/unit/undo_spec.lua
new file mode 100644
index 0000000000..f23110b329
--- /dev/null
+++ b/test/unit/undo_spec.lua
@@ -0,0 +1,215 @@
+local helpers = require('test.unit.helpers')(after_each)
+local itp = helpers.gen_itp(it)
+local lfs = require('lfs')
+local child_call_once = helpers.child_call_once
+
+local global_helpers = require('test.helpers')
+local sleep = global_helpers.sleep
+
+local ffi = helpers.ffi
+local cimport = helpers.cimport
+local to_cstr = helpers.to_cstr
+local neq = helpers.neq
+local eq = helpers.eq
+
+cimport('./src/nvim/ex_cmds_defs.h')
+cimport('./src/nvim/buffer_defs.h')
+local options = cimport('./src/nvim/option_defs.h')
+-- TODO: remove: local vim = cimport('./src/nvim/vim.h')
+local undo = cimport('./src/nvim/undo.h')
+local buffer = cimport('./src/nvim/buffer.h')
+
+local old_p_udir = nil
+
+-- Values expected by tests. Set in the setup function and destroyed in teardown
+local file_buffer = nil
+local buffer_hash = nil
+
+child_call_once(function()
+ if old_p_udir == nil then
+ old_p_udir = options.p_udir -- save the old value of p_udir (undodir)
+ end
+
+ -- create a new buffer
+ local c_file = to_cstr('Xtest-unit-undo')
+ file_buffer = buffer.buflist_new(c_file, c_file, 1, buffer.BLN_LISTED)
+ file_buffer.b_u_numhead = 1 -- Pretend that the buffer has been changed
+
+ -- TODO(christopher.waldon.dev@gmail.com): replace the 32 with UNDO_HASH_SIZE
+ -- requires refactor of UNDO_HASH_SIZE into constant/enum for ffi
+ --
+ -- compute a hash for this undofile
+ buffer_hash = ffi.new('char_u[32]')
+ undo.u_compute_hash(buffer_hash)
+end)
+
+
+describe('u_write_undo', function()
+ setup(function()
+ lfs.mkdir('unit-test-directory')
+ lfs.chdir('unit-test-directory')
+ options.p_udir = to_cstr(lfs.currentdir()) -- set p_udir to be the test dir
+ end)
+
+ teardown(function()
+ lfs.chdir('..')
+ local success, err = lfs.rmdir('unit-test-directory')
+ if not success then
+ print(err) -- inform tester if directory fails to delete
+ end
+ options.p_udir = old_p_udir --restore old p_udir
+ end)
+
+ -- Lua wrapper for u_write_undo
+ local function u_write_undo(name, forceit, buf, buf_hash)
+ if name ~= nil then
+ name = to_cstr(name)
+ end
+
+ return undo.u_write_undo(name, forceit, buf, buf_hash)
+ end
+
+ itp('writes an undo file to undodir given a buffer and hash', function()
+ u_write_undo(nil, false, file_buffer, buffer_hash)
+ local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
+ local undo_file = io.open(correct_name, "r")
+
+ neq(undo_file, nil)
+ local success, err = os.remove(correct_name) -- delete the file now that we're done with it.
+ if not success then
+ print(err) -- inform tester if undofile fails to delete
+ end
+ end)
+
+ itp('writes a correctly-named undo file to undodir given a name, buffer, and hash', function()
+ local correct_name = "undofile.test"
+ u_write_undo(correct_name, false, file_buffer, buffer_hash)
+ local undo_file = io.open(correct_name, "r")
+
+ neq(undo_file, nil)
+ local success, err = os.remove(correct_name) -- delete the file now that we're done with it.
+ if not success then
+ print(err) -- inform tester if undofile fails to delete
+ end
+ end)
+
+ itp('does not write an undofile when the buffer has no valid undofile name', function()
+ -- TODO(christopher.waldon.dev@gmail.com): Figure out how to test this.
+ -- it's hard because u_get_undo_file_name() would need to return null
+ end)
+
+ itp('writes the undofile with the same permissions as the original file', function()
+ -- Create Test file and set permissions
+ local test_file_name = "./test.file"
+ local test_permission_file = io.open(test_file_name, "w")
+ test_permission_file:write("testing permissions")
+ test_permission_file:close()
+ local test_permissions = lfs.attributes(test_file_name).permissions
+
+ -- Create vim buffer
+ local c_file = to_cstr(test_file_name)
+ file_buffer = buffer.buflist_new(c_file, c_file, 1, buffer.BLN_LISTED)
+ file_buffer.b_u_numhead = 1 -- Pretend that the buffer has been changed
+
+ u_write_undo(nil, false, file_buffer, buffer_hash)
+
+ -- Find out the correct name of the undofile
+ local undo_file_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
+
+ -- Find out the permissions of the new file
+ local permissions = lfs.attributes(undo_file_name).permissions
+ eq(test_permissions, permissions)
+
+ -- delete the file now that we're done with it.
+ local success, err = os.remove(test_file_name)
+ if not success then
+ print(err) -- inform tester if undofile fails to delete
+ end
+ success, err = os.remove(undo_file_name)
+ if not success then
+ print(err) -- inform tester if undofile fails to delete
+ end
+ end)
+
+ itp('writes an undofile only readable by the user if the buffer is unnamed', function()
+ local correct_permissions = "rw-------"
+ local undo_file_name = "test.undo"
+
+ -- Create vim buffer
+ file_buffer = buffer.buflist_new(nil, nil, 1, buffer.BLN_LISTED)
+ file_buffer.b_u_numhead = 1 -- Pretend that the buffer has been changed
+
+ u_write_undo(undo_file_name, false, file_buffer, buffer_hash)
+
+ -- Find out the permissions of the new file
+ local permissions = lfs.attributes(undo_file_name).permissions
+ eq(correct_permissions, permissions)
+
+ -- delete the file now that we're done with it.
+ local success, err = os.remove(undo_file_name)
+ if not success then
+ print(err) -- inform tester if undofile fails to delete
+ end
+ end)
+
+ itp('forces writing undo file for :wundo! command', function()
+ local file_contents = "testing permissions"
+ -- Write a text file where the undofile should go
+ local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
+ global_helpers.write_file(correct_name, file_contents, true, false)
+
+ -- Call with `forceit`.
+ u_write_undo(correct_name, true, file_buffer, buffer_hash)
+
+ local undo_file_contents = global_helpers.read_file(correct_name)
+
+ neq(file_contents, undo_file_contents)
+ local success, deletion_err = os.remove(correct_name) -- delete the file now that we're done with it.
+ if not success then
+ print(deletion_err) -- inform tester if undofile fails to delete
+ end
+ end)
+
+ itp('overwrites an existing undo file', function()
+ u_write_undo(nil, false, file_buffer, buffer_hash)
+ local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
+
+ local file_last_modified = lfs.attributes(correct_name).modification
+
+ sleep(1000) -- Ensure difference in timestamps.
+ file_buffer.b_u_numhead = 1 -- Mark it as if there are changes
+ u_write_undo(nil, false, file_buffer, buffer_hash)
+
+ local file_last_modified_2 = lfs.attributes(correct_name).modification
+
+ -- print(file_last_modified, file_last_modified_2)
+ neq(file_last_modified, file_last_modified_2)
+ local success, err = os.remove(correct_name) -- delete the file now that we're done with it.
+ if not success then
+ print(err) -- inform tester if undofile fails to delete
+ end
+ end)
+
+ itp('does not overwrite an existing file that is not an undo file', function()
+ -- TODO: write test
+ end)
+
+ itp('does not overwrite an existing file that has the wrong permissions', function()
+ -- TODO: write test
+ end)
+
+ itp('does not write an undo file if there is no undo information for the buffer', function()
+ file_buffer.b_u_numhead = 0 -- Mark it as if there is no undo information
+ local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
+
+ local existing_file = io.open(correct_name,"r")
+ if existing_file then
+ existing_file:close()
+ os.remove(correct_name)
+ end
+ u_write_undo(nil, false, file_buffer, buffer_hash)
+ local undo_file = io.open(correct_name, "r")
+
+ eq(undo_file, nil)
+ end)
+end)