aboutsummaryrefslogtreecommitdiff
path: root/test/functional/eval
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/eval')
-rw-r--r--test/functional/eval/api_functions_spec.lua147
-rw-r--r--test/functional/eval/execute_spec.lua197
-rw-r--r--test/functional/eval/glob_spec.lua2
-rw-r--r--test/functional/eval/has_spec.lua52
-rw-r--r--test/functional/eval/json_functions_spec.lua38
-rw-r--r--test/functional/eval/modeline_spec.lua19
-rw-r--r--test/functional/eval/msgpack_functions_spec.lua53
-rw-r--r--test/functional/eval/operators_spec.lua2
-rw-r--r--test/functional/eval/printf_spec.lua2
-rw-r--r--test/functional/eval/reltime_spec.lua2
-rw-r--r--test/functional/eval/server_spec.lua17
-rw-r--r--test/functional/eval/setpos_spec.lua64
-rw-r--r--test/functional/eval/special_vars_spec.lua2
-rw-r--r--test/functional/eval/string_spec.lua94
-rw-r--r--test/functional/eval/system_spec.lua451
-rw-r--r--test/functional/eval/timer_spec.lua217
-rw-r--r--test/functional/eval/vvar_event_spec.lua2
17 files changed, 1298 insertions, 63 deletions
diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua
new file mode 100644
index 0000000000..21dd228145
--- /dev/null
+++ b/test/functional/eval/api_functions_spec.lua
@@ -0,0 +1,147 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local lfs = require('lfs')
+local neq, eq, execute = helpers.neq, helpers.eq, helpers.execute
+local clear, curbufmeths = helpers.clear, helpers.curbufmeths
+local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval
+local insert = helpers.insert
+
+describe('api functions', function()
+ before_each(clear)
+
+ it("work", function()
+ execute("call nvim_command('let g:test = 1')")
+ eq(1, eval("nvim_get_var('test')"))
+
+ local buf = eval("nvim_get_current_buf()")
+ execute("call nvim_buf_set_lines("..buf..", 0, -1, v:true, ['aa', 'bb'])")
+ expect([[
+ aa
+ bb]])
+
+ execute("call nvim_win_set_cursor(0, [1, 1])")
+ execute("call nvim_input('ax<esc>')")
+ expect([[
+ aax
+ bb]])
+ end)
+
+ it("throw errors for invalid arguments", function()
+ local err = exc_exec('call nvim_get_current_buf("foo")')
+ eq('Vim(call):E118: Too many arguments for function: nvim_get_current_buf', err)
+
+ err = exc_exec('call nvim_set_option("hlsearch")')
+ eq('Vim(call):E119: Not enough arguments for function: nvim_set_option', err)
+
+ err = exc_exec('call nvim_buf_set_lines(1, 0, -1, [], ["list"])')
+ eq('Vim(call):Wrong type for argument 4, expecting Boolean', err)
+
+ err = exc_exec('call nvim_buf_set_lines(0, 0, -1, v:true, "string")')
+ eq('Vim(call):Wrong type for argument 5, expecting ArrayOf(String)', err)
+
+ err = exc_exec('call nvim_buf_get_number("0")')
+ eq('Vim(call):Wrong type for argument 1, expecting Buffer', err)
+
+ err = exc_exec('call nvim_buf_line_count(17)')
+ eq('Vim(call):Invalid buffer id', err)
+ end)
+
+
+ it("use buffer numbers and windows ids as handles", function()
+ local screen = Screen.new(40, 8)
+ screen:attach()
+ local bnr = eval("bufnr('')")
+ local bhnd = eval("nvim_get_current_buf()")
+ local wid = eval("win_getid()")
+ local whnd = eval("nvim_get_current_win()")
+ eq(bnr, bhnd)
+ eq(wid, whnd)
+
+ execute("new") -- creates new buffer and new window
+ local bnr2 = eval("bufnr('')")
+ local bhnd2 = eval("nvim_get_current_buf()")
+ local wid2 = eval("win_getid()")
+ local whnd2 = eval("nvim_get_current_win()")
+ eq(bnr2, bhnd2)
+ eq(wid2, whnd2)
+ neq(bnr, bnr2)
+ neq(wid, wid2)
+ -- 0 is synonymous to the current buffer
+ eq(bnr2, eval("nvim_buf_get_number(0)"))
+
+ execute("bn") -- show old buffer in new window
+ eq(bnr, eval("nvim_get_current_buf()"))
+ eq(bnr, eval("bufnr('')"))
+ eq(bnr, eval("nvim_buf_get_number(0)"))
+ eq(wid2, eval("win_getid()"))
+ eq(whnd2, eval("nvim_get_current_win()"))
+ end)
+
+ it("get_lines and set_lines use NL to represent NUL", function()
+ curbufmeths.set_lines(0, -1, true, {"aa\0", "b\0b"})
+ eq({'aa\n', 'b\nb'}, eval("nvim_buf_get_lines(0, 0, -1, 1)"))
+
+ execute('call nvim_buf_set_lines(0, 1, 2, v:true, ["xx", "\\nyy"])')
+ eq({'aa\0', 'xx', '\0yy'}, curbufmeths.get_lines(0, -1, 1))
+ end)
+
+ it("that are FUNC_ATTR_NOEVAL cannot be called", function()
+ -- Deprecated vim_ prefix is not exported.
+ local err = exc_exec('call vim_get_current_buffer("foo")')
+ eq('Vim(call):E117: Unknown function: vim_get_current_buffer', err)
+
+ -- Deprecated buffer_ prefix is not exported.
+ err = exc_exec('call buffer_line_count(0)')
+ eq('Vim(call):E117: Unknown function: buffer_line_count', err)
+
+ -- Functions deprecated before the api functions became available
+ -- in vimscript are not exported.
+ err = exc_exec('call buffer_get_line(0, 1)')
+ eq('Vim(call):E117: Unknown function: buffer_get_line', err)
+
+ -- some api functions are only useful from a msgpack-rpc channel
+ err = exc_exec('call nvim_subscribe("fancyevent")')
+ eq('Vim(call):E117: Unknown function: nvim_subscribe', err)
+ end)
+
+ it('have metadata accessible with api_info()', function()
+ local api_keys = eval("sort(keys(api_info()))")
+ eq({'error_types', 'functions', 'types', 'version'}, api_keys)
+ end)
+
+ it('are highlighted by vim.vim syntax file', function()
+ if lfs.attributes("build/runtime/syntax/vim/generated.vim",'uid') == nil then
+ pending("runtime was not built, skipping test")
+ return
+ end
+ local screen = Screen.new(40, 8)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Brown},
+ [2] = {foreground = Screen.colors.DarkCyan},
+ [3] = {foreground = Screen.colors.SlateBlue},
+ [4] = {foreground = Screen.colors.Fuchsia},
+ [5] = {bold = true, foreground = Screen.colors.Blue},
+ })
+
+ execute("set ft=vim")
+ execute("let &rtp='build/runtime/,'.&rtp")
+ execute("syntax on")
+ insert([[
+ call bufnr('%')
+ call nvim_input('typing...')
+ call not_a_function(42)]])
+
+ screen:expect([[
+ {1:call} {2:bufnr}{3:(}{4:'%'}{3:)} |
+ {1:call} {2:nvim_input}{3:(}{4:'typing...'}{3:)} |
+ {1:call} not_a_function{3:(}{4:42}{3:^)} |
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ |
+ ]])
+ screen:detach()
+ end)
+end)
diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua
new file mode 100644
index 0000000000..91966ed3dd
--- /dev/null
+++ b/test/functional/eval/execute_spec.lua
@@ -0,0 +1,197 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
+local eval = helpers.eval
+local clear = helpers.clear
+local source = helpers.source
+local redir_exec = helpers.redir_exec
+local exc_exec = helpers.exc_exec
+local funcs = helpers.funcs
+local Screen = require('test.functional.ui.screen')
+local command = helpers.command
+local feed = helpers.feed
+
+describe('execute()', function()
+ before_each(clear)
+
+ it('captures the same result as :redir', function()
+ eq(redir_exec('messages'), funcs.execute('messages'))
+ end)
+
+ it('captures the concatenated outputs of a List of commands', function()
+ eq("foobar", funcs.execute({'echon "foo"', 'echon "bar"'}))
+ eq("\nfoo\nbar", funcs.execute({'echo "foo"', 'echo "bar"'}))
+ end)
+
+ it('supports nested execute("execute(...)")', function()
+ eq('42', funcs.execute([[echon execute("echon execute('echon 42')")]]))
+ end)
+
+ it('supports nested :redir to a variable', function()
+ source([[
+ function! g:Foo()
+ let a = ''
+ redir => a
+ silent echon "foo"
+ redir END
+ return a
+ endfunction
+ function! g:Bar()
+ let a = ''
+ redir => a
+ silent echon "bar1"
+ call g:Foo()
+ silent echon "bar2"
+ redir END
+ silent echon "bar3"
+ return a
+ endfunction
+ ]])
+ eq('top1bar1foobar2bar3', funcs.execute('echon "top1"|call g:Bar()'))
+ end)
+
+ it('supports nested :redir to a register', function()
+ source([[
+ let @a = ''
+ function! g:Foo()
+ redir @a>>
+ silent echon "foo"
+ redir END
+ return @a
+ endfunction
+ function! g:Bar()
+ redir @a>>
+ silent echon "bar1"
+ call g:Foo()
+ silent echon "bar2"
+ redir END
+ silent echon "bar3"
+ return @a
+ endfunction
+ ]])
+ eq('top1bar1foobar2bar3', funcs.execute('echon "top1"|call g:Bar()'))
+ -- :redir itself doesn't nest, so the redirection ends in g:Foo
+ eq('bar1foo', eval('@a'))
+ end)
+
+ it('captures a transformed string', function()
+ eq('^A', funcs.execute('echon "\\<C-a>"'))
+ end)
+
+ it('returns empty string if the argument list is empty', function()
+ eq('', funcs.execute({}))
+ eq(0, exc_exec('let g:ret = execute(v:_null_list)'))
+ eq('', eval('g:ret'))
+ end)
+
+ it('captures errors', function()
+ local ret
+ ret = exc_exec('call execute(0.0)')
+ eq('Vim(call):E806: using Float as a String', ret)
+ ret = exc_exec('call execute(v:_null_dict)')
+ eq('Vim(call):E731: using Dictionary as a String', ret)
+ ret = exc_exec('call execute(function("tr"))')
+ eq('Vim(call):E729: using Funcref as a String', ret)
+ ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])')
+ eq('Vim:E806: using Float as a String', ret)
+ ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])')
+ eq('Vim:E731: using Dictionary as a String', ret)
+ ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])')
+ eq('Vim:E729: using Funcref as a String', ret)
+ end)
+
+ it('captures output with highlights', function()
+ eq('\nErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red',
+ eval('execute("hi ErrorMsg")'))
+ end)
+
+ it('does not corrupt the command display #5422', function()
+ local screen = Screen.new(70, 5)
+ screen:attach()
+ feed(':echo execute("hi ErrorMsg")<CR>')
+ screen:expect([[
+ ~ |
+ ~ |
+ :echo execute("hi ErrorMsg") |
+ ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red |
+ Press ENTER or type command to continue^ |
+ ]])
+ feed('<CR>')
+ end)
+
+ -- This matches Vim behavior.
+ it('does not capture shell-command output', function()
+ eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"'))
+ end)
+
+ describe('{silent} argument', function()
+ it('captures & displays output for ""', function()
+ local screen = Screen.new(40, 5)
+ screen:attach()
+ command('let g:mes = execute("echon 42", "")')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ 42 |
+ ]])
+ eq('42', eval('g:mes'))
+ end)
+
+ it('captures but does not display output for "silent"', function()
+ local screen = Screen.new(40, 5)
+ screen:attach()
+ command('let g:mes = execute("echon 42")')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ eq('42', eval('g:mes'))
+
+ command('let g:mes = execute("echon 13", "silent")')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ eq('13', eval('g:mes'))
+ end)
+
+ it('suppresses errors for "silent!"', function()
+ eq(0, exc_exec('let g:mes = execute(0.0, "silent!")'))
+ eq('', eval('g:mes'))
+
+ eq(0, exc_exec('let g:mes = execute("echon add(1, 1)", "silent!")'))
+ eq('1', eval('g:mes'))
+
+ eq(0, exc_exec('let g:mes = execute(["echon 42", "echon add(1, 1)"], "silent!")'))
+ eq('421', eval('g:mes'))
+ end)
+
+ it('propagates errors for "" and "silent"', function()
+ local ret
+ ret = exc_exec('call execute(0.0, "")')
+ eq('Vim(call):E806: using Float as a String', ret)
+
+ ret = exc_exec('call execute(v:_null_dict, "silent")')
+ eq('Vim(call):E731: using Dictionary as a String', ret)
+
+ ret = exc_exec('call execute("echo add(1, 1)", "")')
+ eq('Vim(echo):E714: List required', ret)
+
+ ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")')
+ eq('Vim(echo):E714: List required', ret)
+
+ ret = exc_exec('call execute("echo add(1, 1)", "silent")')
+ eq('Vim(echo):E714: List required', ret)
+
+ ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")')
+ eq('Vim(echo):E714: List required', ret)
+ end)
+ end)
+end)
diff --git a/test/functional/eval/glob_spec.lua b/test/functional/eval/glob_spec.lua
index c6bba46424..599b3dcdc3 100644
--- a/test/functional/eval/glob_spec.lua
+++ b/test/functional/eval/glob_spec.lua
@@ -1,5 +1,5 @@
local lfs = require('lfs')
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local clear, execute, eval, eq = helpers.clear, helpers.execute, helpers.eval, helpers.eq
before_each(function()
diff --git a/test/functional/eval/has_spec.lua b/test/functional/eval/has_spec.lua
new file mode 100644
index 0000000000..97b3b0e620
--- /dev/null
+++ b/test/functional/eval/has_spec.lua
@@ -0,0 +1,52 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq = helpers.eq
+local clear = helpers.clear
+local funcs = helpers.funcs
+
+describe('has()', function()
+ before_each(clear)
+
+ it('"nvim-x.y.z"', function()
+ eq(0, funcs.has("nvim-"))
+ eq(0, funcs.has("nvim- "))
+ eq(0, funcs.has("nvim- \t "))
+ eq(0, funcs.has("nvim-0. 1. 1"))
+ eq(0, funcs.has("nvim-0. 1.1"))
+ eq(0, funcs.has("nvim-0.1. 1"))
+ eq(0, funcs.has("nvim-a"))
+ eq(0, funcs.has("nvim-a.b.c"))
+ eq(0, funcs.has("nvim-0.b.c"))
+ eq(0, funcs.has("nvim-0.0.c"))
+ eq(0, funcs.has("nvim-0.b.0"))
+ eq(0, funcs.has("nvim-a.b.0"))
+ eq(0, funcs.has("nvim-.0.0.0"))
+ eq(0, funcs.has("nvim-.0"))
+ eq(0, funcs.has("nvim-0."))
+ eq(0, funcs.has("nvim-0.."))
+ eq(0, funcs.has("nvim-."))
+ eq(0, funcs.has("nvim-.."))
+ eq(0, funcs.has("nvim-..."))
+ eq(0, funcs.has("nvim-42"))
+ eq(0, funcs.has("nvim-9999"))
+ eq(0, funcs.has("nvim-99.001.05"))
+
+ eq(1, funcs.has("nvim"))
+ eq(1, funcs.has("nvim-0"))
+ eq(1, funcs.has("nvim-0.1"))
+ eq(1, funcs.has("nvim-0.0.0"))
+ eq(1, funcs.has("nvim-0.1.1."))
+ eq(1, funcs.has("nvim-0.1.1.abc"))
+ eq(1, funcs.has("nvim-0.1.1.."))
+ eq(1, funcs.has("nvim-0.1.1.. .."))
+ eq(1, funcs.has("nvim-0.1.1.... "))
+ eq(1, funcs.has("nvim-0.0.0"))
+ eq(1, funcs.has("nvim-0.0.1"))
+ eq(1, funcs.has("nvim-0.1.0"))
+ eq(1, funcs.has("nvim-0.1.1"))
+ eq(1, funcs.has("nvim-0.1.5"))
+ eq(1, funcs.has("nvim-0000.001.05"))
+ eq(1, funcs.has("nvim-0.01.005"))
+ eq(1, funcs.has("nvim-00.001.05"))
+ end)
+
+end)
diff --git a/test/functional/eval/json_functions_spec.lua b/test/functional/eval/json_functions_spec.lua
index b32688a9d2..fc0a19bdfa 100644
--- a/test/functional/eval/json_functions_spec.lua
+++ b/test/functional/eval/json_functions_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local funcs = helpers.funcs
local meths = helpers.meths
@@ -10,8 +10,8 @@ local redir_exec = helpers.redir_exec
local NIL = helpers.NIL
describe('json_decode() function', function()
- local restart = function(cmd)
- clear(cmd)
+ local restart = function(...)
+ clear(...)
execute('language C')
execute([[
function Eq(exp, act)
@@ -489,18 +489,6 @@ describe('json_decode() function', function()
'{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}')
end)
- it('converts strings to latin1 when &encoding is latin1', function()
- restart('set encoding=latin1')
- eq('\171', funcs.json_decode('"\\u00AB"'))
- sp_decode_eq({_TYPE='string', _VAL={'\n\171\n'}}, '"\\u0000\\u00AB\\u0000"')
- end)
-
- it('fails to convert string to latin1 if it is impossible', function()
- restart('set encoding=latin1')
- eq('Vim(call):E474: Failed to convert string "ꯍ" from UTF-8',
- exc_exec('call json_decode(\'"\\uABCD"\')'))
- end)
-
it('parses U+00C3 correctly', function()
eq('\195\131', funcs.json_decode('"\195\131"'))
end)
@@ -528,14 +516,6 @@ describe('json_decode() function', function()
eq({key={'val', 'val2'}, key2=1}, funcs.json_decode(str))
end)
- it('always treats input as UTF-8', function()
- -- When &encoding is latin1 string "«" is U+00C2 U+00AB U+00C2: «Â. So if
- -- '"«"' was parsed as latin1 json_decode would return three characters, and
- -- only one U+00AB when this string is parsed as latin1.
- restart('set encoding=latin1')
- eq(('%c'):format(0xAB), funcs.json_decode('"«"'))
- end)
-
it('does not overflow when writing error message about decoding ["", ""]',
function()
eq('\nE474: Attempt to decode a blank string'
@@ -692,6 +672,12 @@ describe('json_encode() function', function()
exc_exec('call json_encode(function("tr"))'))
end)
+ it('fails to dump a partial', function()
+ execute('function T() dict\nendfunction')
+ eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
+ exc_exec('call json_encode(function("T", [1, 2], {}))'))
+ end)
+
it('fails to dump a function reference in a list', function()
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference',
exc_exec('call json_encode([function("tr")])'))
@@ -762,12 +748,6 @@ describe('json_encode() function', function()
exc_exec('call json_encode(["", ""], 1)'))
end)
- it('converts strings from latin1 when &encoding is latin1', function()
- clear('set encoding=latin1')
- eq('"\\u00AB"', funcs.json_encode('\171'))
- eq('"\\u0000\\u00AB\\u0000"', eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\\n\171\\n"]})'))
- end)
-
it('ignores improper values in &isprint', function()
meths.set_option('isprint', '1')
eq(1, eval('"\1" =~# "\\\\p"'))
diff --git a/test/functional/eval/modeline_spec.lua b/test/functional/eval/modeline_spec.lua
new file mode 100644
index 0000000000..0be7210a76
--- /dev/null
+++ b/test/functional/eval/modeline_spec.lua
@@ -0,0 +1,19 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear, execute, write_file = helpers.clear, helpers.execute, helpers.write_file
+local eq, eval = helpers.eq, helpers.eval
+
+describe("modeline", function()
+ local tempfile = helpers.tmpname()
+ before_each(clear)
+
+ after_each(function()
+ os.remove(tempfile)
+ end)
+
+ it('does not crash with a large version number', function()
+ write_file(tempfile, 'vim100000000000000000000000')
+ execute('e! ' .. tempfile)
+
+ eq(2, eval('1+1')) -- Still alive?
+ end)
+end)
diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua
index 9e501353a5..44c01d2226 100644
--- a/test/functional/eval/msgpack_functions_spec.lua
+++ b/test/functional/eval/msgpack_functions_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local funcs = helpers.funcs
local eval, eq = helpers.eval, helpers.eq
@@ -16,18 +16,18 @@ describe('msgpack*() functions', function()
end)
end
- -- Regression test: msgpack_list_write was failing to write buffer with zero
+ -- Regression test: msgpack_list_write was failing to write buffer with zero
-- length.
obj_test('are able to dump and restore {"file": ""}', {{file=''}})
- -- Regression test: msgpack_list_write was failing to write buffer with NL at
+ -- Regression test: msgpack_list_write was failing to write buffer with NL at
-- the end.
obj_test('are able to dump and restore {0, "echo mpack"}', {{0, 'echo mpack'}})
obj_test('are able to dump and restore "Test\\n"', {'Test\n'})
- -- Regression test: msgpack_list_write was failing to write buffer with NL
+ -- Regression test: msgpack_list_write was failing to write buffer with NL
-- inside.
obj_test('are able to dump and restore "Test\\nTest 2"', {'Test\nTest 2'})
- -- Test that big objects (requirement: dump to something that is bigger then
- -- IOSIZE) are also fine. This particular object is obtained by concatenating
+ -- Test that big objects (requirement: dump to something that is bigger then
+ -- IOSIZE) are also fine. This particular object is obtained by concatenating
-- 5 identical shada files.
local big_obj = {
1, 1436711454, 78, {
@@ -460,7 +460,7 @@ describe('msgpackparse() function', function()
eval(cmd)
eval(cmd) -- do it again (try to force segfault)
local api_info = eval(cmd) -- do it again
- eq({'error_types', 'functions', 'types'}, api_info)
+ eq({'error_types', 'functions', 'types', 'version'}, api_info)
end)
it('fails when called with no arguments', function()
@@ -493,6 +493,12 @@ describe('msgpackparse() function', function()
exc_exec('call msgpackparse(function("tr"))'))
end)
+ it('fails to parse a partial', function()
+ execute('function T() dict\nendfunction')
+ eq('Vim(call):E686: Argument of msgpackparse() must be a List',
+ exc_exec('call msgpackparse(function("T", [1, 2], {}))'))
+ end)
+
it('fails to parse a float', function()
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
exc_exec('call msgpackparse(0.0)'))
@@ -566,27 +572,34 @@ describe('msgpackdump() function', function()
it('fails to dump a function reference', function()
execute('let Todump = function("tr")')
- eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
+ eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
+ exc_exec('call msgpackdump([Todump])'))
+ end)
+
+ it('fails to dump a partial', function()
+ execute('function T() dict\nendfunction')
+ execute('let Todump = function("T", [1, 2], {})')
+ eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
exc_exec('call msgpackdump([Todump])'))
end)
it('fails to dump a function reference in a list', function()
execute('let todump = [function("tr")]')
- eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference',
+ eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive list', function()
execute('let todump = [[[]]]')
execute('call add(todump[0][0], todump)')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive dict', function()
execute('let todump = {"d": {"d": {}}}')
execute('call extend(todump.d.d, {"d": todump})')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'',
exc_exec('call msgpackdump([todump])'))
end)
@@ -605,35 +618,35 @@ describe('msgpackdump() function', function()
it('fails to dump a recursive list in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, todump)')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive (key) map in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [todump, 0])')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 1',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive (val) map in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [0, todump])')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive (key) map in a special dict, _VAL reference', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}')
execute('call add(todump._VAL[0][0], todump._VAL)')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive (val) map in a special dict, _VAL reference', function()
execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}')
execute('call add(todump._VAL[0][1], todump._VAL)')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0',
exc_exec('call msgpackdump([todump])'))
end)
@@ -641,7 +654,7 @@ describe('msgpackdump() function', function()
function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, [0, todump._VAL])')
- eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1',
+ eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1',
exc_exec('call msgpackdump([todump])'))
end)
@@ -675,6 +688,12 @@ describe('msgpackdump() function', function()
exc_exec('call msgpackdump(function("tr"))'))
end)
+ it('fails to dump a partial', function()
+ execute('function T() dict\nendfunction')
+ eq('Vim(call):E686: Argument of msgpackdump() must be a List',
+ exc_exec('call msgpackdump(function("T", [1, 2], {}))'))
+ end)
+
it('fails to dump a float', function()
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
exc_exec('call msgpackdump(0.0)'))
diff --git a/test/functional/eval/operators_spec.lua b/test/functional/eval/operators_spec.lua
index bc9a17935c..4d07bc1b05 100644
--- a/test/functional/eval/operators_spec.lua
+++ b/test/functional/eval/operators_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local eval = helpers.eval
local clear = helpers.clear
diff --git a/test/functional/eval/printf_spec.lua b/test/functional/eval/printf_spec.lua
index 6180f4156a..c84290ceef 100644
--- a/test/functional/eval/printf_spec.lua
+++ b/test/functional/eval/printf_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local funcs = helpers.funcs
diff --git a/test/functional/eval/reltime_spec.lua b/test/functional/eval/reltime_spec.lua
index da55a3fac3..0b19d372ec 100644
--- a/test/functional/eval/reltime_spec.lua
+++ b/test/functional/eval/reltime_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
local neq, execute, funcs = helpers.neq, helpers.execute, helpers.funcs
local reltime, reltimestr, reltimefloat = funcs.reltime, funcs.reltimestr, funcs.reltimefloat
diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua
index 7f53522c08..420aea04aa 100644
--- a/test/functional/eval/server_spec.lua
+++ b/test/functional/eval/server_spec.lua
@@ -1,5 +1,5 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local nvim, eq, neq, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.eval
local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths
local os_name = helpers.os_name
@@ -40,8 +40,8 @@ describe('serverstart(), serverstop()', function()
-- v:servername will take the next available server.
local servername = (os_name() == 'windows'
- and [[\\.\pipe\Xtest-functional-server-server-pipe]]
- or 'Xtest-functional-server-server-socket')
+ and [[\\.\pipe\Xtest-functional-server-pipe]]
+ or 'Xtest-functional-server-socket')
funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername'))
end)
@@ -61,9 +61,11 @@ describe('serverlist()', function()
local n = eval('len(serverlist())')
-- Add a few
- local servs = {'should-not-exist', 'another-one-that-shouldnt'}
+ local servs = (os_name() == 'windows'
+ and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
+ or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] })
for _, s in ipairs(servs) do
- eq(s, eval('serverstart("'..s..'")'))
+ eq(s, eval("serverstart('"..s.."')"))
end
local new_servs = eval('serverlist()')
@@ -73,10 +75,9 @@ 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])
- nvim('command', 'call serverstop("'..servs[i]..'")')
+ nvim('command', "call serverstop('"..servs[i].."')")
end
- -- After calling serverstop() on the new servers, they should no longer be
- -- in the list.
+ -- After serverstop() the servers should NOT be in the list.
eq(n, eval('len(serverlist())'))
end)
end)
diff --git a/test/functional/eval/setpos_spec.lua b/test/functional/eval/setpos_spec.lua
new file mode 100644
index 0000000000..2e27cd8ac0
--- /dev/null
+++ b/test/functional/eval/setpos_spec.lua
@@ -0,0 +1,64 @@
+local helpers = require('test.functional.helpers')(after_each)
+local setpos = helpers.funcs.setpos
+local getpos = helpers.funcs.getpos
+local insert = helpers.insert
+local clear = helpers.clear
+local execute = helpers.execute
+local eval = helpers.eval
+local eq = helpers.eq
+local exc_exec = helpers.exc_exec
+
+
+describe('setpos() function', function()
+ before_each(function()
+ clear()
+ insert([[
+ First line of text
+ Second line of text
+ Third line of text]])
+ execute('new')
+ insert([[
+ Line of text 1
+ Line of text 2
+ Line of text 3]])
+ end)
+ it('can set the current cursor position', function()
+ setpos(".", {0, 2, 1, 0})
+ eq(getpos("."), {0, 2, 1, 0})
+ setpos(".", {2, 1, 1, 0})
+ eq(getpos("."), {0, 1, 1, 0})
+ -- Ensure get an error attempting to set position to another buffer
+ local ret = exc_exec('call setpos(".", [1, 1, 1, 0])')
+ eq('Vim(call):E474: Invalid argument', ret)
+ end)
+ it('can set lowercase marks in the current buffer', function()
+ setpos("'d", {0, 2, 1, 0})
+ eq(getpos("'d"), {0, 2, 1, 0})
+ execute('undo', 'call setpos("\'d", [2, 3, 1, 0])')
+ eq(getpos("'d"), {0, 3, 1, 0})
+ end)
+ it('can set lowercase marks in other buffers', function()
+ local retval = setpos("'d", {1, 2, 1, 0})
+ eq(0, retval)
+ setpos("'d", {1, 2, 1, 0})
+ eq(getpos("'d"), {0, 0, 0, 0})
+ execute('wincmd w')
+ eq(eval('bufnr("%")'), 1)
+ eq(getpos("'d"), {0, 2, 1, 0})
+ end)
+ it("fails when setting a mark in a buffer that doesn't exist", function()
+ local retval = setpos("'d", {3, 2, 1, 0})
+ eq(-1, retval)
+ eq(getpos("'d"), {0, 0, 0, 0})
+ retval = setpos("'D", {3, 2, 1, 0})
+ eq(-1, retval)
+ eq(getpos("'D"), {0, 0, 0, 0})
+ end)
+ it('can set uppercase marks', function()
+ setpos("'D", {2, 2, 3, 0})
+ eq(getpos("'D"), {2, 2, 3, 0})
+ -- Can set a mark in another buffer
+ setpos("'D", {1, 2, 2, 0})
+ eq(getpos("'D"), {1, 2, 2, 0})
+ end)
+end)
diff --git a/test/functional/eval/special_vars_spec.lua b/test/functional/eval/special_vars_spec.lua
index 2526483830..4c5d63ce23 100644
--- a/test/functional/eval/special_vars_spec.lua
+++ b/test/functional/eval/special_vars_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local exc_exec = helpers.exc_exec
local execute = helpers.execute
local funcs = helpers.funcs
diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua
index abda2c59cb..f6279e85e8 100644
--- a/test/functional/eval/string_spec.lua
+++ b/test/functional/eval/string_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local command = helpers.command
@@ -9,6 +9,8 @@ local redir_exec = helpers.redir_exec
local funcs = helpers.funcs
local write_file = helpers.write_file
local NIL = helpers.NIL
+local source = helpers.source
+local dedent = helpers.dedent
describe('string() function', function()
before_each(clear)
@@ -110,10 +112,10 @@ describe('string() function', function()
function Test1()
endfunction
- function s:Test2()
+ function s:Test2() dict
endfunction
- function g:Test3()
+ function g:Test3() dict
endfunction
let g:Test2_f = function('s:Test2')
@@ -137,6 +139,85 @@ describe('string() function', function()
it('dumps references to script functions', function()
eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)'))
end)
+
+ it('dumps partials with self referencing a partial', function()
+ source([[
+ function TestDict() dict
+ endfunction
+ let d = {}
+ let TestDictRef = function('TestDict', d)
+ let d.tdr = TestDictRef
+ ]])
+ eq("\nE724: unable to correctly dump variable with self-referencing container\nfunction('TestDict', {'tdr': function('TestDict', {E724@1})})",
+ redir_exec('echo string(d.tdr)'))
+ end)
+
+ it('dumps automatically created partials', function()
+ eq('function(\'<SNR>1_Test2\', {\'f\': function(\'<SNR>1_Test2\')})',
+ eval('string({"f": Test2_f}.f)'))
+ eq('function(\'<SNR>1_Test2\', [1], {\'f\': function(\'<SNR>1_Test2\', [1])})',
+ eval('string({"f": function(Test2_f, [1])}.f)'))
+ end)
+
+ it('dumps manually created partials', function()
+ eq('function(\'Test3\', [1, 2], {})',
+ eval('string(function("Test3", [1, 2], {}))'))
+ eq('function(\'Test3\', {})',
+ eval('string(function("Test3", {}))'))
+ eq('function(\'Test3\', [1, 2])',
+ eval('string(function("Test3", [1, 2]))'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in self',
+ function()
+ meths.set_var('d', {v=true})
+ eq(dedent([[
+
+ E724: unable to correctly dump variable with self-referencing container
+ {'p': function('<SNR>1_Test2', {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]),
+ redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
+ end)
+
+ it('does not show errors when dumping partials referencing the same dictionary',
+ function()
+ command('let d = {}')
+ -- Regression for “eval/typval_encode: Dump empty dictionary before
+ -- checking for refcycle”, results in error.
+ eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('string([function("tr", d), function("tr", d)])'))
+ -- Regression for “eval: Work with reference cycles in partials (self)
+ -- properly”, results in crash.
+ eval('extend(d, {"a": 1})')
+ eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('string([function("tr", d), function("tr", d)])'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in arguments',
+ function()
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ -- Regression: the below line used to crash (add returns original list and
+ -- there was error in dumping partials). Tested explicitly in
+ -- test/unit/api/private_helpers_spec.lua.
+ eval('add(l, function("Test1", l))')
+ eq(dedent([=[
+
+ E724: unable to correctly dump variable with self-referencing container
+ function('Test1', [[{E724@2}, function('Test1', [{E724@2}])], function('Test1', [[{E724@4}, function('Test1', [{E724@4}])]])])]=]),
+ redir_exec('echo string(function("Test1", l))'))
+ end)
+
+ it('does not crash or halt when dumping partials with reference cycles in self and arguments',
+ function()
+ meths.set_var('d', {v=true})
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ eval('add(l, function("Test1", l))')
+ eval('add(l, function("Test1", d))')
+ eq(dedent([=[
+
+ E724: unable to correctly dump variable with self-referencing container
+ {'p': function('<SNR>1_Test2', [[{E724@3}, function('Test1', [{E724@3}]), function('Test1', {E724@0})], function('Test1', [[{E724@5}, function('Test1', [{E724@5}]), function('Test1', {E724@0})]]), function('Test1', {E724@0})], {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]),
+ redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
+ end)
end)
describe('used to represent lists', function()
@@ -174,6 +255,13 @@ describe('string() function', function()
eq('{}', eval('string({})'))
end)
+ it('dumps list with two same empty dictionaries, also in partials', function()
+ command('let d = {}')
+ eq('[{}, {}]', eval('string([d, d])'))
+ eq('[function(\'tr\', {}), {}]', eval('string([function("tr", d), d])'))
+ eq('[{}, function(\'tr\', {})]', eval('string([d, function("tr", d)])'))
+ end)
+
it('dumps non-empty dictionary', function()
eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1}))
end)
diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua
new file mode 100644
index 0000000000..ee75b593ff
--- /dev/null
+++ b/test/functional/eval/system_spec.lua
@@ -0,0 +1,451 @@
+local helpers = require('test.functional.helpers')(after_each)
+local eq, call, clear, eval, execute, feed, nvim =
+ helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.execute,
+ helpers.feed, helpers.nvim
+
+local Screen = require('test.functional.ui.screen')
+
+local function create_file_with_nuls(name)
+ return function()
+ feed('ipart1<C-V>000part2<C-V>000part3<ESC>:w '..name..'<CR>')
+ eval('1') -- wait for the file to be created
+ end
+end
+
+local function delete_file(name)
+ return function()
+ eval("delete('"..name.."')")
+ end
+end
+
+-- Some tests require the xclip program and a x server.
+local xclip = nil
+do
+ if os.getenv('DISPLAY') then
+ xclip = (os.execute('command -v xclip > /dev/null 2>&1') == 0)
+ end
+end
+
+describe('system()', function()
+ before_each(clear)
+
+ describe('command passed as a List', function()
+ local function printargs_path()
+ return helpers.nvim_dir..'/printargs-test'
+ .. (helpers.os_name() == 'windows' and '.exe' or '')
+ end
+
+ it('sets v:shell_error if cmd[0] is not executable', function()
+ call('system', { 'this-should-not-exist' })
+ eq(-1, eval('v:shell_error'))
+ end)
+
+ it('parameter validation does NOT modify v:shell_error', function()
+ -- 1. Call system() with invalid parameters.
+ -- 2. Assert that v:shell_error was NOT set.
+ execute('call system({})')
+ eq('E475: Invalid argument: expected String or List', eval('v:errmsg'))
+ eq(0, eval('v:shell_error'))
+ execute('call system([])')
+ eq('E474: Invalid argument', eval('v:errmsg'))
+ eq(0, eval('v:shell_error'))
+
+ -- Provoke a non-zero v:shell_error.
+ call('system', { 'this-should-not-exist' })
+ local old_val = eval('v:shell_error')
+ eq(-1, old_val)
+
+ -- 1. Call system() with invalid parameters.
+ -- 2. Assert that v:shell_error was NOT modified.
+ execute('call system({})')
+ eq(old_val, eval('v:shell_error'))
+ execute('call system([])')
+ eq(old_val, eval('v:shell_error'))
+ end)
+
+ it('quotes arguments correctly #5280', function()
+ local out = call('system',
+ { printargs_path(), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] })
+
+ eq(0, eval('v:shell_error'))
+ eq([[arg1=1;arg2=2 "3;arg3=4 ' 5;arg4=6 ' 7';]], out)
+
+ out = call('system', { printargs_path(), [['1]], [[2 "3]] })
+ eq(0, eval('v:shell_error'))
+ eq([[arg1='1;arg2=2 "3;]], out)
+
+ out = call('system', { printargs_path(), "A\nB" })
+ eq(0, eval('v:shell_error'))
+ eq("arg1=A\nB;", out)
+ end)
+
+ it('calls executable in $PATH', function()
+ if 0 == eval("executable('python')") then pending("missing `python`") end
+ eq("foo\n", eval([[system(['python', '-c', 'print("foo")'])]]))
+ eq(0, eval('v:shell_error'))
+ end)
+
+ it('does NOT run in shell', function()
+ if helpers.os_name() ~= 'windows' then
+ eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])"))
+ end
+ end)
+ end)
+
+ if helpers.pending_win32(pending) then return end
+
+ it('sets v:shell_error', function()
+ eval([[system("sh -c 'exit'")]])
+ eq(0, eval('v:shell_error'))
+ eval([[system("sh -c 'exit 1'")]])
+ eq(1, eval('v:shell_error'))
+ eval([[system("sh -c 'exit 5'")]])
+ eq(5, eval('v:shell_error'))
+ eval([[system('this-should-not-exist')]])
+ eq(127, eval('v:shell_error'))
+ end)
+
+ describe('executes shell function if passed a string', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new()
+ screen:attach()
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('`echo` and waits for its return', function()
+ feed(':call system("echo")<cr>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ :call system("echo") |
+ ]])
+ end)
+
+ it('`yes` and is interrupted with CTRL-C', function()
+ feed(':call system("yes")<cr>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ :call system("yes") |
+ ]])
+ feed('<c-c>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ Type :quit<Enter> to exit Nvim |
+ ]])
+ end)
+ end)
+
+ describe('passing no input', function()
+ it('returns the program output', function()
+ eq("echoed", eval('system("echo -n echoed")'))
+ end)
+ it('to backgrounded command does not crash', function()
+ -- This is indeterminate, just exercise the codepath. May get E5677.
+ execute('call system("echo -n echoed &")')
+ local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
+ if v_errnum then
+ eq("E5677:", v_errnum)
+ end
+ eq(2, eval("1+1")) -- Still alive?
+ end)
+ end)
+
+ describe('passing input', function()
+ it('returns the program output', function()
+ eq("input", eval('system("cat -", "input")'))
+ end)
+ it('to backgrounded command does not crash', function()
+ -- This is indeterminate, just exercise the codepath. May get E5677.
+ execute('call system("cat - &")')
+ local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
+ if v_errnum then
+ eq("E5677:", v_errnum)
+ end
+ eq(2, eval("1+1")) -- Still alive?
+ end)
+ end)
+
+ describe('passing a lot of input', function()
+ it('returns the program output', function()
+ local input = {}
+ -- write more than 1mb of data, which should be enough to overcome
+ -- the os buffer limit and force multiple event loop iterations to write
+ -- everything
+ for _ = 1, 0xffff do
+ input[#input + 1] = '01234567890ABCDEFabcdef'
+ end
+ input = table.concat(input, '\n')
+ nvim('set_var', 'input', input)
+ eq(input, eval('system("cat -", g:input)'))
+ end)
+ end)
+
+ describe('input passed as Number', function()
+ it('stringifies the input', function()
+ eq('1', eval('system("cat", 1)'))
+ end)
+ end)
+
+ describe('with output containing NULs', function()
+ local fname = 'Xtest'
+
+ before_each(create_file_with_nuls(fname))
+ after_each(delete_file(fname))
+
+ it('replaces NULs by SOH characters', function()
+ eq('part1\001part2\001part3\n', eval('system("cat '..fname..'")'))
+ end)
+ end)
+
+ describe('input passed as List', function()
+ it('joins List items with linefeed characters', function()
+ eq('line1\nline2\nline3',
+ eval("system('cat -', ['line1', 'line2', 'line3'])"))
+ end)
+
+ -- Notice that NULs are converted to SOH when the data is read back. This
+ -- is inconsistent and is a good reason for the existence of the
+ -- `systemlist()` function, where input and output map to the same
+ -- characters(see the following tests with `systemlist()` below)
+ describe('with linefeed characters inside List items', function()
+ it('converts linefeed characters to NULs', function()
+ eq('l1\001p2\nline2\001a\001b\nl3',
+ eval([[system('cat -', ["l1\np2", "line2\na\nb", 'l3'])]]))
+ end)
+ end)
+
+ describe('with leading/trailing whitespace characters on items', function()
+ it('preserves whitespace, replacing linefeeds by NULs', function()
+ eq('line \nline2\001\n\001line3',
+ eval([[system('cat -', ['line ', "line2\n", "\nline3"])]]))
+ end)
+ end)
+ end)
+
+ describe("with a program that doesn't close stdout", function()
+ if not xclip then
+ pending('missing `xclip`', function() end)
+ else
+ it('will exit properly after passing input', function()
+ eq('', eval([[system('xclip -i -loops 1 -selection clipboard', 'clip-data')]]))
+ eq('clip-data', eval([[system('xclip -o -selection clipboard')]]))
+ end)
+ end
+ end)
+end)
+
+if helpers.pending_win32(pending) then return end
+
+describe('systemlist()', function()
+ -- Similar to `system()`, but returns List instead of String.
+ before_each(clear)
+
+ it('sets the v:shell_error variable', function()
+ eval([[systemlist("sh -c 'exit'")]])
+ eq(0, eval('v:shell_error'))
+ eval([[systemlist("sh -c 'exit 1'")]])
+ eq(1, eval('v:shell_error'))
+ eval([[systemlist("sh -c 'exit 5'")]])
+ eq(5, eval('v:shell_error'))
+ eval([[systemlist('this-should-not-exist')]])
+ eq(127, eval('v:shell_error'))
+ end)
+
+ describe('exectues shell function', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new()
+ screen:attach()
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('`echo` and waits for its return', function()
+ feed(':call systemlist("echo")<cr>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ :call systemlist("echo") |
+ ]])
+ end)
+
+ it('`yes` and is interrupted with CTRL-C', function()
+ feed(':call systemlist("yes | xargs")<cr>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ :call systemlist("yes | xargs") |
+ ]])
+ feed('<c-c>')
+ screen:expect([[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ Type :quit<Enter> to exit Nvim |
+ ]])
+ end)
+ end)
+
+ describe('passing string with linefeed characters as input', function()
+ it('splits the output on linefeed characters', function()
+ eq({'abc', 'def', 'ghi'}, eval([[systemlist("cat -", "abc\ndef\nghi")]]))
+ end)
+ end)
+
+ describe('passing a lot of input', function()
+ it('returns the program output', function()
+ local input = {}
+ for _ = 1, 0xffff do
+ input[#input + 1] = '01234567890ABCDEFabcdef'
+ end
+ nvim('set_var', 'input', input)
+ eq(input, eval('systemlist("cat -", g:input)'))
+ end)
+ end)
+
+ describe('with output containing NULs', function()
+ local fname = 'Xtest'
+
+ before_each(create_file_with_nuls(fname))
+ after_each(delete_file(fname))
+
+ it('replaces NULs by newline characters', function()
+ eq({'part1\npart2\npart3'}, eval('systemlist("cat '..fname..'")'))
+ end)
+ end)
+
+ describe('input passed as List', function()
+ it('joins list items with linefeed characters', function()
+ eq({'line1', 'line2', 'line3'},
+ eval("systemlist('cat -', ['line1', 'line2', 'line3'])"))
+ end)
+
+ -- Unlike `system()` which uses SOH to represent NULs, with `systemlist()`
+ -- input and ouput are the same.
+ describe('with linefeed characters inside list items', function()
+ it('converts linefeed characters to NULs', function()
+ eq({'l1\np2', 'line2\na\nb', 'l3'},
+ eval([[systemlist('cat -', ["l1\np2", "line2\na\nb", 'l3'])]]))
+ end)
+ end)
+
+ describe('with leading/trailing whitespace characters on items', function()
+ it('preserves whitespace, replacing linefeeds by NULs', function()
+ eq({'line ', 'line2\n', '\nline3'},
+ eval([[systemlist('cat -', ['line ', "line2\n", "\nline3"])]]))
+ end)
+ end)
+ end)
+
+ describe('handles empty lines', function()
+ it('in the middle', function()
+ eq({'line one','','line two'}, eval("systemlist('cat',['line one','','line two'])"))
+ end)
+
+ it('in the beginning', function()
+ eq({'','line one','line two'}, eval("systemlist('cat',['','line one','line two'])"))
+ end)
+ end)
+
+ describe('when keepempty option is', function()
+ it('0, ignores trailing newline', function()
+ eq({'aa','bb'}, eval("systemlist('cat',['aa','bb'],0)"))
+ eq({'aa','bb'}, eval("systemlist('cat',['aa','bb',''],0)"))
+ end)
+
+ it('1, preserves trailing newline', function()
+ eq({'aa','bb'}, eval("systemlist('cat',['aa','bb'],1)"))
+ eq({'aa','bb',''}, eval("systemlist('cat',['aa','bb',''],2)"))
+ end)
+ end)
+
+ describe("with a program that doesn't close stdout", function()
+ if not xclip then
+ pending('missing `xclip`', function() end)
+ else
+ it('will exit properly after passing input', function()
+ eq({}, eval(
+ "systemlist('xclip -i -loops 1 -selection clipboard', ['clip', 'data'])"))
+ eq({'clip', 'data'}, eval(
+ "systemlist('xclip -o -selection clipboard')"))
+ end)
+ end
+ end)
+end)
diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua
new file mode 100644
index 0000000000..4353619ff0
--- /dev/null
+++ b/test/functional/eval/timer_spec.lua
@@ -0,0 +1,217 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local ok, feed, eq, eval = helpers.ok, helpers.feed, helpers.eq, helpers.eval
+local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run
+local clear, execute, funcs = helpers.clear, helpers.execute, helpers.funcs
+local curbufmeths = helpers.curbufmeths
+
+describe('timers', function()
+ before_each(function()
+ clear()
+ source([[
+ let g:val = 0
+ func MyHandler(timer)
+ let g:val += 1
+ endfunc
+ ]])
+ end)
+
+ it('works one-shot', function()
+ execute("call timer_start(50, 'MyHandler')")
+ eq(0,eval("g:val"))
+ run(nil, nil, nil, 200)
+ eq(1,eval("g:val"))
+ end)
+
+ it('works one-shot when repeat=0', function()
+ execute("call timer_start(50, 'MyHandler', {'repeat': 0})")
+ eq(0,eval("g:val"))
+ run(nil, nil, nil, 200)
+ eq(1,eval("g:val"))
+ end)
+
+
+ it('works with repeat two', function()
+ execute("call timer_start(50, 'MyHandler', {'repeat': 2})")
+ eq(0,eval("g:val"))
+ run(nil, nil, nil, 300)
+ eq(2,eval("g:val"))
+ end)
+
+ it('are triggered during sleep', function()
+ execute("call timer_start(50, 'MyHandler', {'repeat': 2})")
+ nvim_async("command", "sleep 10")
+ eq(0,eval("g:val"))
+ run(nil, nil, nil, 300)
+ eq(2,eval("g:val"))
+ end)
+
+ it('works with zero timeout', function()
+ -- timer_start does still not invoke the callback immediately
+ eq(0,eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]"))
+ run(nil, nil, nil, 300)
+ eq(1000,eval("g:val"))
+ end)
+
+ it('can be started during sleep', function()
+ nvim_async("command", "sleep 10")
+ -- this also tests that remote requests works during sleep
+ eval("timer_start(50, 'MyHandler', {'repeat': 2})")
+ eq(0,eval("g:val"))
+ run(nil, nil, nil, 300)
+ eq(2,eval("g:val"))
+ end)
+
+ it('are paused when event processing is disabled', function()
+ execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
+ run(nil, nil, nil, 100)
+ local count = eval("g:val")
+ -- shows two line error message and thus invokes the return prompt.
+ -- if we start to allow event processing here, we need to change this test.
+ execute("throw 'fatal error'")
+ run(nil, nil, nil, 300)
+ feed("<cr>")
+ local diff = eval("g:val") - count
+ ok(0 <= diff and diff <= 4)
+ end)
+
+ it('are triggered in blocking getchar() call', function()
+ execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
+ nvim_async("command", "let g:c = getchar()")
+ run(nil, nil, nil, 300)
+ feed("c")
+ local count = eval("g:val")
+ ok(count >= 4)
+ eq(99, eval("g:c"))
+ end)
+
+ it('can invoke redraw in blocking getchar() call', function()
+ local screen = Screen.new(40, 6)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ })
+
+ curbufmeths.set_lines(0, -1, true, {"ITEM 1", "ITEM 2"})
+ source([[
+ func! AddItem(timer)
+ call nvim_buf_set_lines(0, 2, 2, v:true, ['ITEM 3'])
+ redraw
+ endfunc
+ call timer_start(200, 'AddItem')
+ ]])
+ nvim_async("command", "let g:c2 = getchar()")
+
+ screen:expect([[
+ ITEM 1 |
+ ITEM 2 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ^ |
+ ]])
+
+ screen:sleep(200)
+ screen:expect([[
+ ITEM 1 |
+ ITEM 2 |
+ ITEM 3 |
+ {1:~ }|
+ {1:~ }|
+ ^ |
+ ]])
+
+ feed("3")
+ eq(51, eval("g:c2"))
+ screen:expect([[
+ ^ITEM 1 |
+ ITEM 2 |
+ ITEM 3 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('can be stopped', function()
+ local t = eval("timer_start(50, 'MyHandler', {'repeat': -1})")
+ eq(0,eval("g:val"))
+ run(nil, nil, nil, 300)
+ funcs.timer_stop(t)
+ local count = eval("g:val")
+ run(nil, nil, nil, 300)
+ local count2 = eval("g:val")
+ ok(4 <= count and count <= 7)
+ -- when count is eval:ed after timer_stop this should be non-racy
+ eq(count, count2)
+ end)
+
+ it('can be stopped from the handler', function()
+ source([[
+ func! MyHandler(timer)
+ let g:val += 1
+ if g:val == 3
+ call timer_stop(a:timer)
+ " check double stop is ignored
+ call timer_stop(a:timer)
+ endif
+ endfunc
+ ]])
+ execute("call timer_start(50, 'MyHandler', {'repeat': -1})")
+ eq(0,eval("g:val"))
+ run(nil, nil, nil, 300)
+ eq(3,eval("g:val"))
+ end)
+
+ it('can have two timers', function()
+ source([[
+ let g:val2 = 0
+ func! MyHandler2(timer)
+ let g:val2 += 1
+ endfunc
+ ]])
+ execute("call timer_start(50, 'MyHandler', {'repeat': 3})")
+ execute("call timer_start(100, 'MyHandler2', {'repeat': 2})")
+ run(nil, nil, nil, 300)
+ eq(3,eval("g:val"))
+ eq(2,eval("g:val2"))
+ end)
+
+ it('do not crash when processing events in the handler', function()
+ source([[
+ let g:val = 0
+ func! MyHandler(timer)
+ call timer_stop(a:timer)
+ sleep 100m
+ let g:val += 1
+ endfunc
+ ]])
+ execute("call timer_start(5, 'MyHandler', {'repeat': 1})")
+ run(nil, nil, nil, 300)
+ eq(1,eval("g:val"))
+ end)
+
+
+ it("doesn't mess up the cmdline", function()
+ local screen = Screen.new(40, 6)
+ screen:attach()
+ screen:set_default_attr_ids( {[0] = {bold=true, foreground=255}} )
+ source([[
+ func! MyHandler(timer)
+ echo "evil"
+ endfunc
+ ]])
+ execute("call timer_start(100, 'MyHandler', {'repeat': 1})")
+ feed(":good")
+ screen:sleep(200)
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :good^ |
+ ]])
+ end)
+
+end)
diff --git a/test/functional/eval/vvar_event_spec.lua b/test/functional/eval/vvar_event_spec.lua
index bbac86524f..eec8aa917a 100644
--- a/test/functional/eval/vvar_event_spec.lua
+++ b/test/functional/eval/vvar_event_spec.lua
@@ -1,4 +1,4 @@
-local helpers = require('test.functional.helpers')
+local helpers = require('test.functional.helpers')(after_each)
local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq
local command = helpers.command
describe('v:event', function()