refactor: format test/*

This commit is contained in:
Justin M. Keyes
2024-01-03 02:09:18 +01:00
parent 59d117ec99
commit 04f2f864e2
363 changed files with 30631 additions and 20833 deletions

View File

@@ -11,24 +11,24 @@ local feed = helpers.feed
describe('eval-API', function()
before_each(clear)
it("work", function()
it('work', function()
command("call nvim_command('let g:test = 1')")
eq(1, eval("nvim_get_var('test')"))
local buf = eval("nvim_get_current_buf()")
command("call nvim_buf_set_lines("..buf..", 0, -1, v:true, ['aa', 'bb'])")
local buf = eval('nvim_get_current_buf()')
command('call nvim_buf_set_lines(' .. buf .. ", 0, -1, v:true, ['aa', 'bb'])")
expect([[
aa
bb]])
command("call nvim_win_set_cursor(0, [1, 1])")
command('call nvim_win_set_cursor(0, [1, 1])')
command("call nvim_input('ax<esc>')")
expect([[
aax
bb]])
end)
it("throw errors for invalid arguments", function()
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)
@@ -36,100 +36,121 @@ describe('eval-API', function()
eq('Vim(call):E119: Not enough arguments for function: nvim_set_option_value', err)
err = exc_exec('call nvim_buf_set_lines(1, 0, -1, [], ["list"])')
eq('Vim(call):E5555: API call: Wrong type for argument 4 when calling nvim_buf_set_lines, expecting Boolean', err)
eq(
'Vim(call):E5555: API call: Wrong type for argument 4 when calling nvim_buf_set_lines, expecting Boolean',
err
)
err = exc_exec('call nvim_buf_set_lines(0, 0, -1, v:true, "string")')
eq('Vim(call):E5555: API call: Wrong type for argument 5 when calling nvim_buf_set_lines, expecting ArrayOf(String)', err)
eq(
'Vim(call):E5555: API call: Wrong type for argument 5 when calling nvim_buf_set_lines, expecting ArrayOf(String)',
err
)
err = exc_exec('call nvim_buf_get_number("0")')
eq('Vim(call):E5555: API call: Wrong type for argument 1 when calling nvim_buf_get_number, expecting Buffer', err)
eq(
'Vim(call):E5555: API call: Wrong type for argument 1 when calling nvim_buf_get_number, expecting Buffer',
err
)
err = exc_exec('call nvim_buf_line_count(17)')
eq('Vim(call):E5555: API call: Invalid buffer id: 17', err)
end)
it('cannot change text or window if textlocked', function()
command("autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])")
matches('Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$',
pcall_err(command, "normal! yy"))
command('autocmd TextYankPost <buffer> ++once call nvim_buf_set_lines(0, 0, -1, v:false, [])')
matches(
'Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$',
pcall_err(command, 'normal! yy')
)
command("autocmd TextYankPost <buffer> ++once call nvim_open_term(0, {})")
matches('Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$',
pcall_err(command, "normal! yy"))
command('autocmd TextYankPost <buffer> ++once call nvim_open_term(0, {})')
matches(
'Vim%(call%):E5555: API call: E565: Not allowed to change text or change window$',
pcall_err(command, 'normal! yy')
)
-- Functions checking textlock should also not be usable from <expr> mappings.
command("inoremap <expr> <f2> nvim_win_close(0, 1)")
eq('Vim(normal):E5555: API call: E565: Not allowed to change text or change window',
pcall_err(command, [[execute "normal i\<f2>"]]))
command('inoremap <expr> <f2> nvim_win_close(0, 1)')
eq(
'Vim(normal):E5555: API call: E565: Not allowed to change text or change window',
pcall_err(command, [[execute "normal i\<f2>"]])
)
-- Text-changing functions gave a "Failed to save undo information" error when called from an
-- <expr> mapping outside do_cmdline() (msg_list == NULL), so use feed() to test this.
command("inoremap <expr> <f2> nvim_buf_set_text(0, 0, 0, 0, 0, ['hi'])")
meths.set_vvar('errmsg', '')
feed("i<f2><esc>")
eq('E5555: API call: E565: Not allowed to change text or change window',
meths.get_vvar('errmsg'))
feed('i<f2><esc>')
eq(
'E5555: API call: E565: Not allowed to change text or change window',
meths.get_vvar('errmsg')
)
-- Some functions checking textlock (usually those that may change the current window or buffer)
-- also ought to not be usable in the cmdwin.
local old_win = meths.get_current_win()
feed("q:")
eq('E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
pcall_err(meths.set_current_win, old_win))
feed('q:')
eq(
'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
pcall_err(meths.set_current_win, old_win)
)
-- But others, like nvim_buf_set_lines(), which just changes text, is OK.
curbufmeths.set_lines(0, -1, 1, {"wow!"})
eq({'wow!'}, curbufmeths.get_lines(0, -1, 1))
curbufmeths.set_lines(0, -1, 1, { 'wow!' })
eq({ 'wow!' }, curbufmeths.get_lines(0, -1, 1))
-- Turning the cmdwin buffer into a terminal buffer would be pretty weird.
eq('E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
pcall_err(meths.open_term, 0, {}))
eq(
'E11: Invalid in command-line window; <CR> executes, CTRL-C quits',
pcall_err(meths.open_term, 0, {})
)
-- But turning a different buffer into a terminal from the cmdwin is OK.
local term_buf = meths.create_buf(false, true)
meths.open_term(term_buf, {})
eq('terminal', meths.get_option_value("buftype", {buf = term_buf}))
eq('terminal', meths.get_option_value('buftype', { buf = term_buf }))
end)
it("use buffer numbers and windows ids as handles", function()
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()")
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)
command("new") -- creates new buffer and new window
command('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()")
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)"))
eq(bnr2, eval('nvim_buf_get_number(0)'))
command("bn") -- show old buffer in new window
eq(bnr, eval("nvim_get_current_buf()"))
command('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()"))
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)"))
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)'))
command('call nvim_buf_set_lines(0, 1, 2, v:true, ["xx", "\\nyy"])')
eq({'aa\0', 'xx', '\0yy'}, curbufmeths.get_lines(0, -1, 1))
eq({ 'aa\0', 'xx', '\0yy' }, curbufmeths.get_lines(0, -1, 1))
end)
it("that are FUNC_ATTR_NOEVAL cannot be called", function()
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)
@@ -149,25 +170,24 @@ describe('eval-API', function()
end)
it('have metadata accessible with api_info()', function()
local api_keys = eval("sort(keys(api_info()))")
eq({'error_types', 'functions', 'types',
'ui_events', 'ui_options', 'version'}, api_keys)
local api_keys = eval('sort(keys(api_info()))')
eq({ 'error_types', 'functions', 'types', 'ui_events', 'ui_options', 'version' }, api_keys)
end)
it('are highlighted by vim.vim syntax file', function()
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},
[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 },
})
command("set ft=vim")
command("set rtp^=build/runtime/")
command("syntax on")
command('set ft=vim')
command('set rtp^=build/runtime/')
command('syntax on')
insert([[
call bufnr('%')
call nvim_input('typing...')
@@ -183,9 +203,11 @@ describe('eval-API', function()
end)
it('cannot be called from sandbox', function()
eq('Vim(call):E48: Not allowed in sandbox',
pcall_err(command, "sandbox call nvim_input('ievil')"))
eq({''}, meths.buf_get_lines(0, 0, -1, true))
eq(
'Vim(call):E48: Not allowed in sandbox',
pcall_err(command, "sandbox call nvim_input('ievil')")
)
eq({ '' }, meths.buf_get_lines(0, 0, -1, true))
end)
it('converts blobs to API strings', function()

View File

@@ -23,30 +23,47 @@ local dirname = fname .. '.d'
before_each(clear)
for _, func in ipairs({'bufname(%s)', 'bufnr(%s)', 'bufwinnr(%s)',
'getbufline(%s, 1)', 'getbufvar(%s, "changedtick")',
'setbufvar(%s, "f", 0)'}) do
for _, func in ipairs({
'bufname(%s)',
'bufnr(%s)',
'bufwinnr(%s)',
'getbufline(%s, 1)',
'getbufvar(%s, "changedtick")',
'setbufvar(%s, "f", 0)',
}) do
local funcname = func:match('%w+')
describe(funcname .. '() function', function()
it('errors out when receives v:true/v:false/v:null', function()
-- Not compatible with Vim: in Vim it always results in buffer not found
-- without any error messages.
for _, var in ipairs({'v:true', 'v:false'}) do
eq('Vim(call):E5299: Expected a Number or a String, Boolean found',
exc_exec('call ' .. func:format(var)))
for _, var in ipairs({ 'v:true', 'v:false' }) do
eq(
'Vim(call):E5299: Expected a Number or a String, Boolean found',
exc_exec('call ' .. func:format(var))
)
end
eq('Vim(call):E5300: Expected a Number or a String',
exc_exec('call ' .. func:format('v:null')))
eq(
'Vim(call):E5300: Expected a Number or a String',
exc_exec('call ' .. func:format('v:null'))
)
end)
it('errors out when receives invalid argument', function()
eq('Vim(call):E745: Expected a Number or a String, List found',
exc_exec('call ' .. func:format('[]')))
eq('Vim(call):E728: Expected a Number or a String, Dictionary found',
exc_exec('call ' .. func:format('{}')))
eq('Vim(call):E805: Expected a Number or a String, Float found',
exc_exec('call ' .. func:format('0.0')))
eq('Vim(call):E703: Expected a Number or a String, Funcref found',
exc_exec('call ' .. func:format('function("tr")')))
eq(
'Vim(call):E745: Expected a Number or a String, List found',
exc_exec('call ' .. func:format('[]'))
)
eq(
'Vim(call):E728: Expected a Number or a String, Dictionary found',
exc_exec('call ' .. func:format('{}'))
)
eq(
'Vim(call):E805: Expected a Number or a String, Float found',
exc_exec('call ' .. func:format('0.0'))
)
eq(
'Vim(call):E703: Expected a Number or a String, Funcref found',
exc_exec('call ' .. func:format('function("tr")'))
)
end)
end)
end
@@ -68,12 +85,12 @@ describe('bufname() function', function()
rmdir(dirname)
end)
it('returns expected buffer name', function()
eq('', funcs.bufname('%')) -- Buffer has no name yet
eq('', funcs.bufname('%')) -- Buffer has no name yet
command('file ' .. fname)
local wd = luv.cwd()
local sep = get_pathsep()
local curdirname = funcs.fnamemodify(wd, ':t')
for _, arg in ipairs({'%', 1, 'X', wd}) do
for _, arg in ipairs({ '%', 1, 'X', wd }) do
eq(fname, funcs.bufname(arg))
meths.set_current_dir('..')
eq(curdirname .. sep .. fname, funcs.bufname(arg))
@@ -139,7 +156,7 @@ describe('bufwinnr() function', function()
eq(-1, funcs.bufwinnr(2))
eq(-1, funcs.bufwinnr('non-existent-buffer'))
eq(-1, funcs.bufwinnr('#'))
command('split ' .. fname2) -- It would be OK if there was one window
command('split ' .. fname2) -- It would be OK if there was one window
eq(2, funcs.bufnr('%'))
eq(-1, funcs.bufwinnr('X'))
end)
@@ -181,7 +198,7 @@ describe('getbufline() function', function()
end)
it('returns empty list when range is invalid', function()
eq({}, funcs.getbufline(1, 0))
curbufmeths.set_lines(0, 1, false, {'foo', 'bar', 'baz'})
curbufmeths.set_lines(0, 1, false, { 'foo', 'bar', 'baz' })
eq({}, funcs.getbufline(1, 2, 1))
eq({}, funcs.getbufline(1, -10, -20))
eq({}, funcs.getbufline(1, -2, -1))
@@ -190,14 +207,14 @@ describe('getbufline() function', function()
it('returns expected lines', function()
meths.set_option_value('hidden', true, {})
command('file ' .. fname)
curbufmeths.set_lines(0, 1, false, {'foo\0', '\0bar', 'baz'})
curbufmeths.set_lines(0, 1, false, { 'foo\0', '\0bar', 'baz' })
command('edit ' .. fname2)
curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'})
eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, 9999))
eq({'abc\n', '\ndef', 'ghi'}, funcs.getbufline(2, 1, 9999))
eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, '$'))
eq({'baz'}, funcs.getbufline(1, '$', '$'))
eq({'baz'}, funcs.getbufline(1, '$', 9999))
curbufmeths.set_lines(0, 1, false, { 'abc\0', '\0def', 'ghi' })
eq({ 'foo\n', '\nbar', 'baz' }, funcs.getbufline(1, 1, 9999))
eq({ 'abc\n', '\ndef', 'ghi' }, funcs.getbufline(2, 1, 9999))
eq({ 'foo\n', '\nbar', 'baz' }, funcs.getbufline(1, 1, '$'))
eq({ 'baz' }, funcs.getbufline(1, '$', '$'))
eq({ 'baz' }, funcs.getbufline(1, '$', 9999))
end)
end)
@@ -230,9 +247,9 @@ describe('getbufvar() function', function()
eq(0, funcs.getbufvar(1, '&g:number'))
command('new')
-- But with window-local options it probably does not what you expect
command("setl number")
command('setl number')
-- (note that current windows buffer is 2, but getbufvar() receives 1)
eq({id=2}, curwinmeths.get_buf())
eq({ id = 2 }, curwinmeths.get_buf())
eq(1, funcs.getbufvar(1, '&number'))
eq(1, funcs.getbufvar(1, '&l:number'))
-- You can get global value though, if you find this useful.
@@ -240,31 +257,33 @@ describe('getbufvar() function', function()
end)
it('returns expected variable value', function()
eq(2, funcs.getbufvar(1, 'changedtick'))
curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'})
curbufmeths.set_lines(0, 1, false, { 'abc\0', '\0def', 'ghi' })
eq(3, funcs.getbufvar(1, 'changedtick'))
curbufmeths.set_var('test', true)
eq(true, funcs.getbufvar(1, 'test'))
eq({test=true, changedtick=3}, funcs.getbufvar(1, ''))
eq({ test = true, changedtick = 3 }, funcs.getbufvar(1, ''))
command('new')
eq(3, funcs.getbufvar(1, 'changedtick'))
eq(true, funcs.getbufvar(1, 'test'))
eq({test=true, changedtick=3}, funcs.getbufvar(1, ''))
eq({ test = true, changedtick = 3 }, funcs.getbufvar(1, ''))
end)
end)
describe('setbufvar() function', function()
it('throws the error or ignores the input when buffer was not found', function()
command('file ' .. fname)
eq(0,
exc_exec('call setbufvar(2, "&autoindent", 0)'))
eq('Vim(call):E94: No matching buffer for non-existent-buffer',
exc_exec('call setbufvar("non-existent-buffer", "&autoindent", 0)'))
eq(0,
exc_exec('call setbufvar("#", "&autoindent", 0)'))
eq(0, exc_exec('call setbufvar(2, "&autoindent", 0)'))
eq(
'Vim(call):E94: No matching buffer for non-existent-buffer',
exc_exec('call setbufvar("non-existent-buffer", "&autoindent", 0)')
)
eq(0, exc_exec('call setbufvar("#", "&autoindent", 0)'))
command('edit ' .. fname2)
eq(2, funcs.bufnr('%'))
eq('Vim(call):E93: More than one match for X',
exc_exec('call setbufvar("X", "&autoindent", 0)'))
eq(
'Vim(call):E93: More than one match for X',
exc_exec('call setbufvar("X", "&autoindent", 0)')
)
end)
it('may set options, including window-local and global values', function()
local buf1 = meths.get_current_buf()
@@ -274,21 +293,19 @@ describe('setbufvar() function', function()
eq(2, bufmeths.get_number(curwinmeths.get_buf()))
funcs.setbufvar(1, '&number', true)
local windows = curtabmeths.list_wins()
eq(false, meths.get_option_value('number', {win=windows[1].id}))
eq(true, meths.get_option_value('number', {win=windows[2].id}))
eq(false, meths.get_option_value('number', {win=windows[3].id}))
eq(false, meths.get_option_value('number', {win=meths.get_current_win().id}))
eq(false, meths.get_option_value('number', { win = windows[1].id }))
eq(true, meths.get_option_value('number', { win = windows[2].id }))
eq(false, meths.get_option_value('number', { win = windows[3].id }))
eq(false, meths.get_option_value('number', { win = meths.get_current_win().id }))
eq(true, meths.get_option_value('hidden', {}))
funcs.setbufvar(1, '&hidden', 0)
eq(false, meths.get_option_value('hidden', {}))
eq(false, meths.get_option_value('autoindent', {buf=buf1.id}))
eq(false, meths.get_option_value('autoindent', { buf = buf1.id }))
funcs.setbufvar(1, '&autoindent', true)
eq(true, meths.get_option_value('autoindent', {buf=buf1.id}))
eq('Vim(call):E355: Unknown option: xxx',
exc_exec('call setbufvar(1, "&xxx", 0)'))
eq(true, meths.get_option_value('autoindent', { buf = buf1.id }))
eq('Vim(call):E355: Unknown option: xxx', exc_exec('call setbufvar(1, "&xxx", 0)'))
end)
it('may set variables', function()
local buf1 = meths.get_current_buf()
@@ -297,15 +314,15 @@ describe('setbufvar() function', function()
eq(2, curbufmeths.get_number())
funcs.setbufvar(1, 'number', true)
eq(true, bufmeths.get_var(buf1, 'number'))
eq('Vim(call):E461: Illegal variable name: b:',
exc_exec('call setbufvar(1, "", 0)'))
eq('Vim(call):E461: Illegal variable name: b:', exc_exec('call setbufvar(1, "", 0)'))
eq(true, bufmeths.get_var(buf1, 'number'))
eq('Vim:E46: Cannot change read-only variable "b:changedtick"',
pcall_err(funcs.setbufvar, 1, 'changedtick', true))
eq(
'Vim:E46: Cannot change read-only variable "b:changedtick"',
pcall_err(funcs.setbufvar, 1, 'changedtick', true)
)
eq(2, funcs.getbufvar(1, 'changedtick'))
end)
it('throws error when setting a string option to a boolean value vim-patch:9.0.0090', function()
eq('Vim:E928: String required',
pcall_err(funcs.setbufvar, '', '&errorformat', true))
eq('Vim:E928: String required', pcall_err(funcs.setbufvar, '', '&errorformat', true))
end)
end)

View File

@@ -29,7 +29,7 @@ end
describe('b:changedtick', function()
-- Ported tests from Vim-8.0.333
it('increments', function() -- Test_changedtick_increments
it('increments', function() -- Test_changedtick_increments
-- New buffer has an empty line, tick starts at 2
eq(2, changedtick())
funcs.setline(1, 'hello')
@@ -56,35 +56,55 @@ describe('b:changedtick', function()
local ct = changedtick()
local ctn = ct + 100500
eq(0, exc_exec('let d = b:'))
eq('Vim(let):E46: Cannot change read-only variable "b:changedtick"',
pcall_err(command, 'let b:changedtick = ' .. ctn))
eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] = ' .. ctn))
eq('Vim(let):E46: Cannot change read-only variable "b:.changedtick"',
pcall_err(command, 'let b:.changedtick = ' .. ctn))
eq('Vim(let):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'let d.changedtick = ' .. ctn))
eq('Key is read-only: changedtick',
pcall_err(curbufmeths.set_var, 'changedtick', ctn))
eq(
'Vim(let):E46: Cannot change read-only variable "b:changedtick"',
pcall_err(command, 'let b:changedtick = ' .. ctn)
)
eq(
'Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] = ' .. ctn)
)
eq(
'Vim(let):E46: Cannot change read-only variable "b:.changedtick"',
pcall_err(command, 'let b:.changedtick = ' .. ctn)
)
eq(
'Vim(let):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'let d.changedtick = ' .. ctn)
)
eq('Key is read-only: changedtick', pcall_err(curbufmeths.set_var, 'changedtick', ctn))
eq('Vim(unlet):E795: Cannot delete variable b:changedtick',
pcall_err(command, 'unlet b:changedtick'))
eq('Vim(unlet):E46: Cannot change read-only variable "b:.changedtick"',
pcall_err(command, 'unlet b:.changedtick'))
eq('Vim(unlet):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'unlet b:["changedtick"]'))
eq('Vim(unlet):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'unlet d.changedtick'))
eq('Key is read-only: changedtick',
pcall_err(curbufmeths.del_var, 'changedtick'))
eq(
'Vim(unlet):E795: Cannot delete variable b:changedtick',
pcall_err(command, 'unlet b:changedtick')
)
eq(
'Vim(unlet):E46: Cannot change read-only variable "b:.changedtick"',
pcall_err(command, 'unlet b:.changedtick')
)
eq(
'Vim(unlet):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'unlet b:["changedtick"]')
)
eq(
'Vim(unlet):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'unlet d.changedtick')
)
eq('Key is read-only: changedtick', pcall_err(curbufmeths.del_var, 'changedtick'))
eq(ct, changedtick())
eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] += ' .. ctn))
eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] -= ' .. ctn))
eq('Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] .= ' .. ctn))
eq(
'Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] += ' .. ctn)
)
eq(
'Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] -= ' .. ctn)
)
eq(
'Vim(let):E46: Cannot change read-only variable "b:["changedtick"]"',
pcall_err(command, 'let b:["changedtick"] .= ' .. ctn)
)
eq(ct, changedtick())
@@ -99,16 +119,24 @@ describe('b:changedtick', function()
eq(0, exc_exec('let d = b:'))
eq(0, funcs.islocked('b:changedtick'))
eq(0, funcs.islocked('d.changedtick'))
eq('Vim(unlockvar):E940: Cannot lock or unlock variable b:changedtick',
pcall_err(command, 'unlockvar b:changedtick'))
eq('Vim(unlockvar):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'unlockvar d.changedtick'))
eq(
'Vim(unlockvar):E940: Cannot lock or unlock variable b:changedtick',
pcall_err(command, 'unlockvar b:changedtick')
)
eq(
'Vim(unlockvar):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'unlockvar d.changedtick')
)
eq(0, funcs.islocked('b:changedtick'))
eq(0, funcs.islocked('d.changedtick'))
eq('Vim(lockvar):E940: Cannot lock or unlock variable b:changedtick',
pcall_err(command, 'lockvar b:changedtick'))
eq('Vim(lockvar):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'lockvar d.changedtick'))
eq(
'Vim(lockvar):E940: Cannot lock or unlock variable b:changedtick',
pcall_err(command, 'lockvar b:changedtick')
)
eq(
'Vim(lockvar):E46: Cannot change read-only variable "d.changedtick"',
pcall_err(command, 'lockvar d.changedtick')
)
eq(0, funcs.islocked('b:changedtick'))
eq(0, funcs.islocked('d.changedtick'))
end)
@@ -118,18 +146,26 @@ describe('b:changedtick', function()
end)
it('cannot be changed by filter() or map()', function()
eq(2, changedtick())
eq('Vim(call):E795: Cannot delete variable filter() argument',
pcall_err(command, 'call filter(b:, 0)'))
eq('Vim(call):E742: Cannot change value of map() argument',
pcall_err(command, 'call map(b:, 0)'))
eq('Vim(call):E742: Cannot change value of map() argument',
pcall_err(command, 'call map(b:, "v:val")'))
eq(
'Vim(call):E795: Cannot delete variable filter() argument',
pcall_err(command, 'call filter(b:, 0)')
)
eq(
'Vim(call):E742: Cannot change value of map() argument',
pcall_err(command, 'call map(b:, 0)')
)
eq(
'Vim(call):E742: Cannot change value of map() argument',
pcall_err(command, 'call map(b:, "v:val")')
)
eq(2, changedtick())
end)
it('cannot be remove()d', function()
eq(2, changedtick())
eq('Vim(call):E795: Cannot delete variable remove() argument',
pcall_err(command, 'call remove(b:, "changedtick")'))
eq(
'Vim(call):E795: Cannot delete variable remove() argument',
pcall_err(command, 'call remove(b:, "changedtick")')
)
eq(2, changedtick())
end)
it('does not inherit VAR_FIXED when copying dictionary over', function()

View File

@@ -9,16 +9,16 @@ before_each(clear)
describe('extend()', function()
it('succeeds to extend list with itself', function()
meths.set_var('l', {1, {}})
eq({1, {}, 1, {}}, eval('extend(l, l)'))
eq({1, {}, 1, {}}, meths.get_var('l'))
meths.set_var('l', { 1, {} })
eq({ 1, {}, 1, {} }, eval('extend(l, l)'))
eq({ 1, {}, 1, {} }, meths.get_var('l'))
meths.set_var('l', {1, {}})
eq({1, {}, 1, {}}, eval('extend(l, l, 0)'))
eq({1, {}, 1, {}}, meths.get_var('l'))
meths.set_var('l', { 1, {} })
eq({ 1, {}, 1, {} }, eval('extend(l, l, 0)'))
eq({ 1, {}, 1, {} }, meths.get_var('l'))
meths.set_var('l', {1, {}})
eq({1, 1, {}, {}}, eval('extend(l, l, 1)'))
eq({1, 1, {}, {}}, meths.get_var('l'))
meths.set_var('l', { 1, {} })
eq({ 1, 1, {}, {} }, eval('extend(l, l, 1)'))
eq({ 1, 1, {}, {} }, meths.get_var('l'))
end)
end)

View File

@@ -18,13 +18,12 @@ local pcall_err = helpers.pcall_err
describe('context functions', function()
local fname1 = 'Xtest-functional-eval-ctx1'
local fname2 = 'Xtest-functional-eval-ctx2'
local outofbounds =
'Vim:E475: Invalid value for argument index: out of bounds'
local outofbounds = 'Vim:E475: Invalid value for argument index: out of bounds'
before_each(function()
clear()
write_file(fname1, "1\n2\n3")
write_file(fname2, "a\nb\nc")
write_file(fname1, '1\n2\n3')
write_file(fname2, 'a\nb\nc')
end)
after_each(function()
@@ -34,78 +33,105 @@ describe('context functions', function()
describe('ctxpush/ctxpop', function()
it('saves and restores registers properly', function()
local regs = {'1', '2', '3', 'a'}
local vals = {'1', '2', '3', 'hjkl'}
local regs = { '1', '2', '3', 'a' }
local vals = { '1', '2', '3', 'hjkl' }
feed('i1<cr>2<cr>3<c-[>ddddddqahjklq')
eq(vals, map(function(r) return trim(call('getreg', r)) end, regs))
eq(
vals,
map(function(r)
return trim(call('getreg', r))
end, regs)
)
call('ctxpush')
call('ctxpush', {'regs'})
call('ctxpush', { 'regs' })
map(function(r) call('setreg', r, {}) end, regs)
eq({'', '', '', ''},
map(function(r) return trim(call('getreg', r)) end, regs))
map(function(r)
call('setreg', r, {})
end, regs)
eq(
{ '', '', '', '' },
map(function(r)
return trim(call('getreg', r))
end, regs)
)
call('ctxpop')
eq(vals, map(function(r) return trim(call('getreg', r)) end, regs))
eq(
vals,
map(function(r)
return trim(call('getreg', r))
end, regs)
)
map(function(r) call('setreg', r, {}) end, regs)
eq({'', '', '', ''},
map(function(r) return trim(call('getreg', r)) end, regs))
map(function(r)
call('setreg', r, {})
end, regs)
eq(
{ '', '', '', '' },
map(function(r)
return trim(call('getreg', r))
end, regs)
)
call('ctxpop')
eq(vals, map(function(r) return trim(call('getreg', r)) end, regs))
eq(
vals,
map(function(r)
return trim(call('getreg', r))
end, regs)
)
end)
it('saves and restores jumplist properly', function()
command('edit '..fname1)
command('edit ' .. fname1)
feed('G')
feed('gg')
command('edit '..fname2)
command('edit ' .. fname2)
local jumplist = call('getjumplist')
call('ctxpush')
call('ctxpush', {'jumps'})
call('ctxpush', { 'jumps' })
command('clearjumps')
eq({{}, 0}, call('getjumplist'))
eq({ {}, 0 }, call('getjumplist'))
call('ctxpop')
eq(jumplist, call('getjumplist'))
command('clearjumps')
eq({{}, 0}, call('getjumplist'))
eq({ {}, 0 }, call('getjumplist'))
call('ctxpop')
eq(jumplist, call('getjumplist'))
end)
it('saves and restores buffer list properly', function()
command('edit '..fname1)
command('edit '..fname2)
command('edit ' .. fname1)
command('edit ' .. fname2)
command('edit TEST')
local bufs = call('map', call('getbufinfo'), 'v:val.name')
call('ctxpush')
call('ctxpush', {'bufs'})
call('ctxpush', { 'bufs' })
command('%bwipeout')
eq({''}, call('map', call('getbufinfo'), 'v:val.name'))
eq({ '' }, call('map', call('getbufinfo'), 'v:val.name'))
call('ctxpop')
eq({'', unpack(bufs)}, call('map', call('getbufinfo'), 'v:val.name'))
eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name'))
command('%bwipeout')
eq({''}, call('map', call('getbufinfo'), 'v:val.name'))
eq({ '' }, call('map', call('getbufinfo'), 'v:val.name'))
call('ctxpop')
eq({'', unpack(bufs)}, call('map', call('getbufinfo'), 'v:val.name'))
eq({ '', unpack(bufs) }, call('map', call('getbufinfo'), 'v:val.name'))
end)
it('saves and restores global variables properly', function()
nvim('set_var', 'one', 1)
nvim('set_var', 'Two', 2)
nvim('set_var', 'THREE', 3)
eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
call('ctxpush')
call('ctxpush', {'gvars'})
call('ctxpush', { 'gvars' })
nvim('del_var', 'one')
nvim('del_var', 'Two')
@@ -115,7 +141,7 @@ describe('context functions', function()
eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE'))
call('ctxpop')
eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
nvim('del_var', 'one')
nvim('del_var', 'Two')
@@ -125,7 +151,7 @@ describe('context functions', function()
eq('Vim:E121: Undefined variable: g:THREE', pcall_err(eval, 'g:THREE'))
call('ctxpop')
eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
end)
it('saves and restores script functions properly', function()
@@ -164,28 +190,30 @@ describe('context functions', function()
]])
eq('Hello, World!', exec_capture([[call Greet('World')]]))
eq('Hello, World!'..
'\nHello, One!'..
'\nHello, Two!'..
'\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]))
eq(
'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
)
call('SaveSFuncs')
call('DeleteSFuncs')
eq('function Greet, line 1: Vim(call):E117: Unknown function: s:greet',
pcall_err(command, [[call Greet('World')]]))
eq('function GreetAll, line 1: Vim(call):E117: Unknown function: s:greet_all',
pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]]))
eq(
'function Greet, line 1: Vim(call):E117: Unknown function: s:greet',
pcall_err(command, [[call Greet('World')]])
)
eq(
'function GreetAll, line 1: Vim(call):E117: Unknown function: s:greet_all',
pcall_err(command, [[call GreetAll('World', 'One', 'Two', 'Three')]])
)
call('RestoreFuncs')
eq('Hello, World!', exec_capture([[call Greet('World')]]))
eq('Hello, World!'..
'\nHello, One!'..
'\nHello, Two!'..
'\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]))
eq(
'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
)
end)
it('saves and restores functions properly', function()
@@ -203,28 +231,28 @@ describe('context functions', function()
]])
eq('Hello, World!', exec_capture([[call Greet('World')]]))
eq('Hello, World!'..
'\nHello, One!'..
'\nHello, Two!'..
'\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]))
eq(
'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
)
call('ctxpush', {'funcs'})
call('ctxpush', { 'funcs' })
command('delfunction Greet')
command('delfunction GreetAll')
eq('Vim:E117: Unknown function: Greet', pcall_err(call, 'Greet', 'World'))
eq('Vim:E117: Unknown function: GreetAll',
pcall_err(call, 'GreetAll', 'World', 'One', 'Two', 'Three'))
eq(
'Vim:E117: Unknown function: GreetAll',
pcall_err(call, 'GreetAll', 'World', 'One', 'Two', 'Three')
)
call('ctxpop')
eq('Hello, World!', exec_capture([[call Greet('World')]]))
eq('Hello, World!'..
'\nHello, One!'..
'\nHello, Two!'..
'\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]]))
eq(
'Hello, World!' .. '\nHello, One!' .. '\nHello, Two!' .. '\nHello, Three!',
exec_capture([[call GreetAll('World', 'One', 'Two', 'Three')]])
)
end)
it('errors out when context stack is empty', function()
@@ -268,21 +296,21 @@ describe('context functions', function()
it('returns context dictionary at index in context stack', function()
feed('i1<cr>2<cr>3<c-[>ddddddqahjklq')
command('edit! '..fname1)
command('edit! ' .. fname1)
feed('G')
feed('gg')
command('edit '..fname2)
command('edit ' .. fname2)
nvim('set_var', 'one', 1)
nvim('set_var', 'Two', 2)
nvim('set_var', 'THREE', 3)
local with_regs = {
['regs'] = {
{['rt'] = 1, ['rc'] = {'1'}, ['n'] = 49, ['ru'] = true},
{['rt'] = 1, ['rc'] = {'2'}, ['n'] = 50},
{['rt'] = 1, ['rc'] = {'3'}, ['n'] = 51},
{['rc'] = {'hjkl'}, ['n'] = 97},
}
{ ['rt'] = 1, ['rc'] = { '1' }, ['n'] = 49, ['ru'] = true },
{ ['rt'] = 1, ['rc'] = { '2' }, ['n'] = 50 },
{ ['rt'] = 1, ['rc'] = { '3' }, ['n'] = 51 },
{ ['rc'] = { 'hjkl' }, ['n'] = 97 },
},
}
local with_jumps = {
@@ -292,17 +320,17 @@ describe('context functions', function()
'filter(
{ "f": expand("#".v:val.bufnr.":p"), "l": v:val.lnum },
{ k, v -> k != "l" || v != 1 })'), '!empty(v:val.f)')
]]):gsub('\n', ''))
]]):gsub('\n', '')),
}
local with_bufs = {
['bufs'] = eval([[
filter(map(getbufinfo(), '{ "f": v:val.name }'), '!empty(v:val.f)')
]])
]]),
}
local with_gvars = {
['gvars'] = {{'one', 1}, {'Two', 2}, {'THREE', 3}}
['gvars'] = { { 'one', 1 }, { 'Two', 2 }, { 'THREE', 3 } },
}
local with_all = {
@@ -316,25 +344,25 @@ describe('context functions', function()
eq(with_all, parse_context(call('ctxget')))
eq(with_all, parse_context(call('ctxget', 0)))
call('ctxpush', {'gvars'})
call('ctxpush', { 'gvars' })
eq(with_gvars, parse_context(call('ctxget')))
eq(with_gvars, parse_context(call('ctxget', 0)))
eq(with_all, parse_context(call('ctxget', 1)))
call('ctxpush', {'bufs'})
call('ctxpush', { 'bufs' })
eq(with_bufs, parse_context(call('ctxget')))
eq(with_bufs, parse_context(call('ctxget', 0)))
eq(with_gvars, parse_context(call('ctxget', 1)))
eq(with_all, parse_context(call('ctxget', 2)))
call('ctxpush', {'jumps'})
call('ctxpush', { 'jumps' })
eq(with_jumps, parse_context(call('ctxget')))
eq(with_jumps, parse_context(call('ctxget', 0)))
eq(with_bufs, parse_context(call('ctxget', 1)))
eq(with_gvars, parse_context(call('ctxget', 2)))
eq(with_all, parse_context(call('ctxget', 3)))
call('ctxpush', {'regs'})
call('ctxpush', { 'regs' })
eq(with_regs, parse_context(call('ctxget')))
eq(with_regs, parse_context(call('ctxget', 0)))
eq(with_jumps, parse_context(call('ctxget', 1)))
@@ -368,17 +396,19 @@ describe('context functions', function()
describe('ctxset()', function()
it('errors out when index is out of bounds', function()
eq(outofbounds, pcall_err(call, 'ctxset', {dummy = 1}))
eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }))
call('ctxpush')
eq(outofbounds, pcall_err(call, 'ctxset', {dummy = 1}, 1))
eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 1))
call('ctxpop')
eq(outofbounds, pcall_err(call, 'ctxset', {dummy = 1}, 0))
eq(outofbounds, pcall_err(call, 'ctxset', { dummy = 1 }, 0))
end)
it('errors when context dictionary is invalid', function()
call('ctxpush')
eq('Vim:E474: Failed to convert list to msgpack string buffer',
pcall_err(call, 'ctxset', { regs = { {} }, jumps = { {} } }))
eq(
'Vim:E474: Failed to convert list to msgpack string buffer',
pcall_err(call, 'ctxset', { regs = { {} }, jumps = { {} } })
)
end)
it('sets context dictionary at index in context stack', function()
@@ -394,17 +424,17 @@ describe('context functions', function()
call('ctxpush')
local ctx2 = call('ctxget')
eq({'a', 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]'))
eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
call('ctxset', ctx1)
call('ctxset', ctx2, 2)
call('ctxpop')
eq({1, 2 ,3}, eval('[g:one, g:Two, g:THREE]'))
eq({ 1, 2, 3 }, eval('[g:one, g:Two, g:THREE]'))
call('ctxpop')
eq({'a', 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]'))
eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
nvim('set_var', 'one', 1.5)
eq({1.5, 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]'))
eq({ 1.5, 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
call('ctxpop')
eq({'a', 'b' ,'c'}, eval('[g:one, g:Two, g:THREE]'))
eq({ 'a', 'b', 'c' }, eval('[g:one, g:Two, g:THREE]'))
end)
end)
end)

View File

@@ -11,13 +11,13 @@ local setenv = helpers.funcs.setenv
describe('environment variables', function()
it('environ() handles empty env variable', function()
clear({env={EMPTY_VAR=""}})
eq("", environ()['EMPTY_VAR'])
clear({ env = { EMPTY_VAR = '' } })
eq('', environ()['EMPTY_VAR'])
eq(nil, environ()['DOES_NOT_EXIST'])
end)
it('exists() handles empty env variable', function()
clear({env={EMPTY_VAR=""}})
clear({ env = { EMPTY_VAR = '' } })
eq(1, exists('$EMPTY_VAR'))
eq(0, exists('$DOES_NOT_EXIST'))
end)
@@ -46,23 +46,32 @@ describe('empty $HOME', function()
end
local function write_and_test_tilde()
system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless',
'-c', 'write test_empty_home', '+q'})
system({
nvim_prog,
'-u',
'NONE',
'-i',
'NONE',
'--headless',
'-c',
'write test_empty_home',
'+q',
})
eq(false, tilde_in_cwd())
end
it("'~' folder not created in cwd if $HOME and related env not defined", function()
command("unlet $HOME")
command('unlet $HOME')
write_and_test_tilde()
command("let $HOMEDRIVE='C:'")
command("let $USERPROFILE='C:\\'")
write_and_test_tilde()
command("unlet $HOMEDRIVE")
command('unlet $HOMEDRIVE')
write_and_test_tilde()
command("unlet $USERPROFILE")
command('unlet $USERPROFILE')
write_and_test_tilde()
command("let $HOME='%USERPROFILE%'")

View File

@@ -24,10 +24,10 @@ describe('setqflist()', function()
end)
it('sets w:quickfix_title', function()
setqflist({''}, 'r', 'foo')
setqflist({ '' }, 'r', 'foo')
command('copen')
eq('foo', get_cur_win_var('quickfix_title'))
setqflist({}, 'r', {['title'] = 'qf_title'})
setqflist({}, 'r', { ['title'] = 'qf_title' })
eq('qf_title', get_cur_win_var('quickfix_title'))
end)
@@ -38,7 +38,10 @@ describe('setqflist()', function()
end)
it('requires a dict for {what}', function()
eq('Vim(call):E715: Dictionary required', exc_exec('call setqflist([], "r", function("function"))'))
eq(
'Vim(call):E715: Dictionary required',
exc_exec('call setqflist([], "r", function("function"))')
)
end)
end)
@@ -73,11 +76,11 @@ describe('setloclist()', function()
helpers.insert([[
hello world]])
command("vsplit")
command("autocmd WinLeave * :call nvim_win_close(0, v:true)")
command('vsplit')
command('autocmd WinLeave * :call nvim_win_close(0, v:true)')
command("call setloclist(0, [])")
command("lopen")
command('call setloclist(0, [])')
command('lopen')
helpers.assert_alive()
end)

View File

@@ -32,7 +32,7 @@ local feed = helpers.feed
local expect_exit = helpers.expect_exit
describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
local max_func_args = 20 -- from eval.h
local max_func_args = 20 -- from eval.h
local range = helpers.funcs.range
before_each(clear)
@@ -41,7 +41,7 @@ describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
local printf = helpers.funcs.printf
local rep = helpers.funcs['repeat']
local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'
eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args))))
eq(expected, printf(rep('%d,', max_func_args - 1), unpack(range(2, max_func_args))))
local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
eq('Vim(call):E740: Too many arguments for function printf', ret)
end)
@@ -55,15 +55,15 @@ describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
end)
end)
describe("backtick expansion", function()
describe('backtick expansion', function()
setup(function()
clear()
mkdir("test-backticks")
write_file("test-backticks/file1", "test file 1")
write_file("test-backticks/file2", "test file 2")
write_file("test-backticks/file3", "test file 3")
mkdir("test-backticks/subdir")
write_file("test-backticks/subdir/file4", "test file 4")
mkdir('test-backticks')
write_file('test-backticks/file1', 'test file 1')
write_file('test-backticks/file2', 'test file 2')
write_file('test-backticks/file3', 'test file 3')
mkdir('test-backticks/subdir')
write_file('test-backticks/subdir/file4', 'test file 4')
-- Long path might cause "Press ENTER" prompt; use :silent to avoid it.
command('silent cd test-backticks')
end)
@@ -74,30 +74,30 @@ describe("backtick expansion", function()
it("with default 'shell'", function()
if helpers.is_os('win') then
command(":silent args `dir /b *2`")
command(':silent args `dir /b *2`')
else
command(":silent args `echo ***2`")
command(':silent args `echo ***2`')
end
eq({ "file2", }, eval("argv()"))
eq({ 'file2' }, eval('argv()'))
if helpers.is_os('win') then
command(":silent args `dir /s/b *4`")
eq({ "subdir\\file4", }, eval("map(argv(), 'fnamemodify(v:val, \":.\")')"))
command(':silent args `dir /s/b *4`')
eq({ 'subdir\\file4' }, eval('map(argv(), \'fnamemodify(v:val, ":.")\')'))
else
command(":silent args `echo */*4`")
eq({ "subdir/file4", }, eval("argv()"))
command(':silent args `echo */*4`')
eq({ 'subdir/file4' }, eval('argv()'))
end
end)
it("with shell=fish", function()
it('with shell=fish', function()
if eval("executable('fish')") == 0 then
pending('missing "fish" command')
return
end
command("set shell=fish")
command(":silent args `echo ***2`")
eq({ "file2", }, eval("argv()"))
command(":silent args `echo */*4`")
eq({ "subdir/file4", }, eval("argv()"))
command('set shell=fish')
command(':silent args `echo ***2`')
eq({ 'file2' }, eval('argv()'))
command(':silent args `echo */*4`')
eq({ 'subdir/file4' }, eval('argv()'))
end)
end)
@@ -106,7 +106,9 @@ describe('List support code', function()
local min_dur = 8
local len = 131072
if not pending('does not actually allows interrupting with just got_int', function() end) then return end
if not pending('does not actually allows interrupting with just got_int', function() end) then
return
end
-- The following tests are confirmed to work with os_breakcheck() just before
-- `if (got_int) {break;}` in tv_list_copy and list_join_inner() and not to
-- work without.
@@ -153,7 +155,7 @@ describe('List support code', function()
end)
end)
describe("uncaught exception", function()
describe('uncaught exception', function()
before_each(clear)
it('is not forgotten #13490', function()
@@ -164,11 +166,14 @@ describe("uncaught exception", function()
-- from processing the others.
-- Only the first thrown exception should be rethrown from the :try below, though.
for i = 1, 3 do
write_file('throw' .. i .. '.vim', ([[
write_file(
'throw' .. i .. '.vim',
([[
let result ..= '%d'
throw 'throw%d'
let result ..= 'X'
]]):format(i, i))
]]):format(i, i)
)
end
finally(function()
for i = 1, 3 do
@@ -184,9 +189,9 @@ describe("uncaught exception", function()
it('multiline exception remains multiline #25350', function()
local screen = Screen.new(80, 11)
screen:set_default_attr_ids({
[1] = {bold = true, reverse = true}; -- MsgSeparator
[2] = {foreground = Screen.colors.White, background = Screen.colors.Red}; -- ErrorMsg
[3] = {bold = true, foreground = Screen.colors.SeaGreen}; -- MoreMsg
[1] = { bold = true, reverse = true }, -- MsgSeparator
[2] = { foreground = Screen.colors.White, background = Screen.colors.Red }, -- ErrorMsg
[3] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg
})
screen:attach()
exec_lua([[
@@ -195,7 +200,8 @@ describe("uncaught exception", function()
end
]])
feed(':try\rlua _G.Oops()\rendtry\r')
screen:expect{grid=[[
screen:expect {
grid = [[
{1: }|
:try |
: lua _G.Oops() |
@@ -207,7 +213,8 @@ describe("uncaught exception", function()
{2: [string "<nvim>"]:2: in function 'Oops'} |
{2: [string ":lua"]:1: in main chunk} |
{3:Press ENTER or type command to continue}^ |
]]}
]],
}
end)
end)
@@ -217,16 +224,23 @@ describe('listing functions using :function', function()
it('works for lambda functions with <lambda> #20466', function()
command('let A = {-> 1}')
local num = exec_capture('echo A'):match("function%('<lambda>(%d+)'%)")
eq(([[
eq(
([[
function <lambda>%s(...)
1 return 1
endfunction]]):format(num), exec_capture(('function <lambda>%s'):format(num)))
endfunction]]):format(num),
exec_capture(('function <lambda>%s'):format(num))
)
end)
it('does not crash if another function is deleted while listing', function()
local screen = Screen.new(80, 24)
screen:attach()
matches('Vim%(function%):E454: Function list was modified$', pcall_err(exec_lua, [=[
matches(
'Vim%(function%):E454: Function list was modified$',
pcall_err(
exec_lua,
[=[
vim.cmd([[
func Func1()
endfunc
@@ -247,14 +261,20 @@ describe('listing functions using :function', function()
vim.cmd('function')
vim.ui_detach(ns)
]=]))
]=]
)
)
assert_alive()
end)
it('does not crash if the same function is deleted while listing', function()
local screen = Screen.new(80, 24)
screen:attach()
matches('Vim%(function%):E454: Function list was modified$', pcall_err(exec_lua, [=[
matches(
'Vim%(function%):E454: Function list was modified$',
pcall_err(
exec_lua,
[=[
vim.cmd([[
func Func1()
endfunc
@@ -275,7 +295,9 @@ describe('listing functions using :function', function()
vim.cmd('function')
vim.ui_detach(ns)
]=]))
]=]
)
)
assert_alive()
end)
end)
@@ -283,7 +305,9 @@ end)
it('no double-free in garbage collection #16287', function()
clear()
-- Don't use exec() here as using a named script reproduces the issue better.
write_file('Xgarbagecollect.vim', [[
write_file(
'Xgarbagecollect.vim',
[[
func Foo() abort
let s:args = [a:000]
let foo0 = ""
@@ -306,7 +330,8 @@ it('no double-free in garbage collection #16287', function()
set updatetime=1
call Foo()
call Foo()
]])
]]
)
finally(function()
os.remove('Xgarbagecollect.vim')
end)

View File

@@ -1,7 +1,6 @@
local helpers = require('test.functional.helpers')(after_each)
local eq, clear, call, write_file, command =
helpers.eq, helpers.clear, helpers.call, helpers.write_file,
helpers.command
helpers.eq, helpers.clear, helpers.call, helpers.write_file, helpers.command
local exc_exec = helpers.exc_exec
local eval = helpers.eval
local is_os = helpers.is_os
@@ -21,9 +20,15 @@ describe('executable()', function()
if is_os('win') then
it('exepath respects shellslash', function()
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
eq([[test\functional\fixtures\bin\null.CMD]], call('fnamemodify', call('exepath', 'null'), ':.'))
eq(
[[test\functional\fixtures\bin\null.CMD]],
call('fnamemodify', call('exepath', 'null'), ':.')
)
command('set shellslash')
eq('test/functional/fixtures/bin/null.CMD', call('fnamemodify', call('exepath', 'null'), ':.'))
eq(
'test/functional/fixtures/bin/null.CMD',
call('fnamemodify', call('exepath', 'null'), ':.')
)
end)
it('stdpath respects shellslash', function()
@@ -34,14 +39,18 @@ describe('executable()', function()
end
it('fails for invalid values', function()
for _, input in ipairs({'v:null', 'v:true', 'v:false', '{}', '[]'}) do
eq('Vim(call):E1174: String required for argument 1',
exc_exec('call executable('..input..')'))
for _, input in ipairs({ 'v:null', 'v:true', 'v:false', '{}', '[]' }) do
eq(
'Vim(call):E1174: String required for argument 1',
exc_exec('call executable(' .. input .. ')')
)
end
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do
eq('Vim(call):E1174: String required for argument 1',
exc_exec('call executable('..input..')'))
for _, input in ipairs({ 'v:null', 'v:true', 'v:false' }) do
eq(
'Vim(call):E1174: String required for argument 1',
exc_exec('call executable(' .. input .. ')')
)
end
end)
@@ -59,8 +68,7 @@ describe('executable()', function()
-- Windows: siblings are in Nvim's "pseudo-$PATH".
local expected = is_os('win') and 1 or 0
if is_os('win') then
eq('arg1=lemon;arg2=sky;arg3=tree;',
call('system', sibling_exe..' lemon sky tree'))
eq('arg1=lemon;arg2=sky;arg3=tree;', call('system', sibling_exe .. ' lemon sky tree'))
end
eq(expected, call('executable', sibling_exe))
end)
@@ -70,9 +78,9 @@ describe('executable()', function()
clear()
write_file('Xtest_not_executable', 'non-executable file')
write_file('Xtest_executable', 'executable file (exec-bit set)')
if not is_os('win') then -- N/A for Windows.
call('system', {'chmod', '-x', 'Xtest_not_executable'})
call('system', {'chmod', '+x', 'Xtest_executable'})
if not is_os('win') then -- N/A for Windows.
call('system', { 'chmod', '-x', 'Xtest_not_executable' })
call('system', { 'chmod', '+x', 'Xtest_executable' })
end
end)
@@ -103,144 +111,142 @@ describe('executable() (Windows)', function()
return
end
local exts = {'bat', 'exe', 'com', 'cmd'}
local exts = { 'bat', 'exe', 'com', 'cmd' }
setup(function()
for _, ext in ipairs(exts) do
write_file('test_executable_'..ext..'.'..ext, '')
write_file('test_executable_' .. ext .. '.' .. ext, '')
end
write_file('test_executable_zzz.zzz', '')
end)
teardown(function()
for _, ext in ipairs(exts) do
os.remove('test_executable_'..ext..'.'..ext)
os.remove('test_executable_' .. ext .. '.' .. ext)
end
os.remove('test_executable_zzz.zzz')
end)
it('tries default extensions on a filename if $PATHEXT is empty', function()
-- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
clear({env={PATHEXT=''}})
for _,ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_'..ext))
clear({ env = { PATHEXT = '' } })
for _, ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_' .. ext))
end
eq(0, call('executable', 'test_executable_zzz'))
end)
it('tries default extensions on a filepath if $PATHEXT is empty', function()
-- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
clear({env={PATHEXT=''}})
for _,ext in ipairs(exts) do
eq(1, call('executable', '.\\test_executable_'..ext))
clear({ env = { PATHEXT = '' } })
for _, ext in ipairs(exts) do
eq(1, call('executable', '.\\test_executable_' .. ext))
end
eq(0, call('executable', '.\\test_executable_zzz'))
end)
it('system([…]), jobstart([…]) use $PATHEXT #9569', function()
-- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
clear({env={PATHEXT=''}})
clear({ env = { PATHEXT = '' } })
-- Invoking `cmdscript` should find/execute `cmdscript.cmd`.
eq('much success\n', call('system', {'test/functional/fixtures/cmdscript'}))
assert(0 < call('jobstart', {'test/functional/fixtures/cmdscript'}))
eq('much success\n', call('system', { 'test/functional/fixtures/cmdscript' }))
assert(0 < call('jobstart', { 'test/functional/fixtures/cmdscript' }))
end)
it('full path with extension', function()
-- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
clear({env={PATHEXT=''}})
clear({ env = { PATHEXT = '' } })
-- Some executable we can expect in the test env.
local exe = 'printargs-test'
local exedir = eval("fnamemodify(v:progpath, ':h')")
local exepath = exedir..'/'..exe..'.exe'
local exepath = exedir .. '/' .. exe .. '.exe'
eq(1, call('executable', exepath))
eq('arg1=lemon;arg2=sky;arg3=tree;',
call('system', exepath..' lemon sky tree'))
eq('arg1=lemon;arg2=sky;arg3=tree;', call('system', exepath .. ' lemon sky tree'))
end)
it('full path without extension', function()
-- Empty $PATHEXT defaults to ".com;.exe;.bat;.cmd".
clear({env={PATHEXT=''}})
clear({ env = { PATHEXT = '' } })
-- Some executable we can expect in the test env.
local exe = 'printargs-test'
local exedir = eval("fnamemodify(v:progpath, ':h')")
local exepath = exedir..'/'..exe
eq('arg1=lemon;arg2=sky;arg3=tree;',
call('system', exepath..' lemon sky tree'))
eq(1, call('executable', exepath))
local exepath = exedir .. '/' .. exe
eq('arg1=lemon;arg2=sky;arg3=tree;', call('system', exepath .. ' lemon sky tree'))
eq(1, call('executable', exepath))
end)
it('respects $PATHEXT when trying extensions on a filename', function()
clear({env={PATHEXT='.zzz'}})
for _,ext in ipairs(exts) do
eq(0, call('executable', 'test_executable_'..ext))
clear({ env = { PATHEXT = '.zzz' } })
for _, ext in ipairs(exts) do
eq(0, call('executable', 'test_executable_' .. ext))
end
eq(1, call('executable', 'test_executable_zzz'))
end)
it('respects $PATHEXT when trying extensions on a filepath', function()
clear({env={PATHEXT='.zzz'}})
for _,ext in ipairs(exts) do
eq(0, call('executable', '.\\test_executable_'..ext))
clear({ env = { PATHEXT = '.zzz' } })
for _, ext in ipairs(exts) do
eq(0, call('executable', '.\\test_executable_' .. ext))
end
eq(1, call('executable', '.\\test_executable_zzz'))
end)
it("with weird $PATHEXT", function()
clear({env={PATHEXT=';'}})
it('with weird $PATHEXT', function()
clear({ env = { PATHEXT = ';' } })
eq(0, call('executable', '.\\test_executable_zzz'))
clear({env={PATHEXT=';;;.zzz;;'}})
clear({ env = { PATHEXT = ';;;.zzz;;' } })
eq(1, call('executable', '.\\test_executable_zzz'))
end)
it("unqualified filename, Unix-style 'shell'", function()
clear({env={PATHEXT=''}})
clear({ env = { PATHEXT = '' } })
command('set shell=sh')
for _,ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_'..ext..'.'..ext))
for _, ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_' .. ext .. '.' .. ext))
end
eq(1, call('executable', 'test_executable_zzz.zzz'))
end)
it("relative path, Unix-style 'shell' (backslashes)", function()
clear({env={PATHEXT=''}})
clear({ env = { PATHEXT = '' } })
command('set shell=bash.exe')
for _,ext in ipairs(exts) do
eq(1, call('executable', '.\\test_executable_'..ext..'.'..ext))
eq(1, call('executable', './test_executable_'..ext..'.'..ext))
for _, ext in ipairs(exts) do
eq(1, call('executable', '.\\test_executable_' .. ext .. '.' .. ext))
eq(1, call('executable', './test_executable_' .. ext .. '.' .. ext))
end
eq(1, call('executable', '.\\test_executable_zzz.zzz'))
eq(1, call('executable', './test_executable_zzz.zzz'))
end)
it('unqualified filename, $PATHEXT contains dot', function()
clear({env={PATHEXT='.;.zzz'}})
for _,ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_'..ext..'.'..ext))
clear({ env = { PATHEXT = '.;.zzz' } })
for _, ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_' .. ext .. '.' .. ext))
end
eq(1, call('executable', 'test_executable_zzz.zzz'))
clear({env={PATHEXT='.zzz;.'}})
for _,ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_'..ext..'.'..ext))
clear({ env = { PATHEXT = '.zzz;.' } })
for _, ext in ipairs(exts) do
eq(1, call('executable', 'test_executable_' .. ext .. '.' .. ext))
end
eq(1, call('executable', 'test_executable_zzz.zzz'))
end)
it('relative path, $PATHEXT contains dot (backslashes)', function()
clear({env={PATHEXT='.;.zzz'}})
for _,ext in ipairs(exts) do
eq(1, call('executable', '.\\test_executable_'..ext..'.'..ext))
eq(1, call('executable', './test_executable_'..ext..'.'..ext))
clear({ env = { PATHEXT = '.;.zzz' } })
for _, ext in ipairs(exts) do
eq(1, call('executable', '.\\test_executable_' .. ext .. '.' .. ext))
eq(1, call('executable', './test_executable_' .. ext .. '.' .. ext))
end
eq(1, call('executable', '.\\test_executable_zzz.zzz'))
eq(1, call('executable', './test_executable_zzz.zzz'))
end)
it('ignores case of extension', function()
clear({env={PATHEXT='.ZZZ'}})
clear({ env = { PATHEXT = '.ZZZ' } })
eq(1, call('executable', 'test_executable_zzz.zzz'))
end)
it('relative path does not search $PATH', function()
clear({env={PATHEXT=''}})
clear({ env = { PATHEXT = '' } })
eq(0, call('executable', './System32/notepad.exe'))
eq(0, call('executable', '.\\System32\\notepad.exe'))
eq(0, call('executable', '../notepad.exe'))

View File

@@ -26,8 +26,8 @@ describe('execute()', function()
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"'}))
eq('foobar', funcs.execute({ 'echon "foo"', 'echon "bar"' }))
eq('\nfoo\nbar', funcs.execute({ 'echo "foo"', 'echo "bar"' }))
end)
it('supports nested execute("execute(...)")', function()
@@ -104,26 +104,31 @@ describe('execute()', function()
end)
it('captures output with highlights', function()
eq('\nErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red',
eval('execute("hi ErrorMsg")'))
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, 7)
screen:attach()
feed(':echo execute("hi ErrorMsg")<CR>')
screen:expect([[
screen:expect(
[[
|
{1:~ }|*2
{2: }|
|
ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red |
{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},
})
]],
{
[1] = { bold = true, foreground = Screen.colors.Blue1 },
[2] = { bold = true, reverse = true },
[3] = { bold = true, foreground = Screen.colors.SeaGreen4 },
}
)
feed('<CR>')
end)
@@ -250,7 +255,7 @@ describe('execute()', function()
-- with how nvim currently displays the output.
it('captures shell-command output', function()
local win_lf = is_os('win') and '\13' or ''
eq('\n:!echo foo\r\n\nfoo'..win_lf..'\n', funcs.execute('!echo foo'))
eq('\n:!echo foo\r\n\nfoo' .. win_lf .. '\n', funcs.execute('!echo foo'))
end)
describe('{silent} argument', function()
@@ -268,10 +273,14 @@ describe('execute()', function()
it('gives E493 instead of prompting on backwards range for ""', function()
command('split')
eq('Vim(windo):E493: Backwards range given: 2,1windo echo',
pcall_err(funcs.execute, '2,1windo echo', ''))
eq('Vim(windo):E493: Backwards range given: 2,1windo echo',
pcall_err(funcs.execute, {'2,1windo echo'}, ''))
eq(
'Vim(windo):E493: Backwards range given: 2,1windo echo',
pcall_err(funcs.execute, '2,1windo echo', '')
)
eq(
'Vim(windo):E493: Backwards range given: 2,1windo echo',
pcall_err(funcs.execute, { '2,1windo echo' }, '')
)
end)
it('captures but does not display output for "silent"', function()
@@ -286,11 +295,14 @@ describe('execute()', function()
eq('42', eval('g:mes'))
command('let g:mes = execute("echon 13", "silent")')
screen:expect{grid=[[
screen:expect {
grid = [[
^ |
~ |*3
|
]], unchanged=true}
]],
unchanged = true,
}
eq('13', eval('g:mes'))
end)

View File

@@ -1,6 +1,5 @@
local helpers = require('test.functional.helpers')(after_each)
local eq, clear, call =
helpers.eq, helpers.clear, helpers.call
local eq, clear, call = helpers.eq, helpers.clear, helpers.call
local command = helpers.command
local exc_exec = helpers.exc_exec
local matches = helpers.matches
@@ -14,20 +13,26 @@ local find_dummies = function(ext_pat)
matches('null' .. ext_pat, call('exepath', 'null'))
matches('true' .. ext_pat, call('exepath', 'true'))
matches('false' .. ext_pat, call('exepath', 'false'))
command("let $PATH = '"..tmp_path.."'")
command("let $PATH = '" .. tmp_path .. "'")
end
describe('exepath()', function()
before_each(clear)
it('fails for invalid values', function()
for _, input in ipairs({'v:null', 'v:true', 'v:false', '{}', '[]'}) do
eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')'))
for _, input in ipairs({ 'v:null', 'v:true', 'v:false', '{}', '[]' }) do
eq(
'Vim(call):E1174: String required for argument 1',
exc_exec('call exepath(' .. input .. ')')
)
end
eq('Vim(call):E1175: Non-empty string required for argument 1', exc_exec('call exepath("")'))
command('let $PATH = fnamemodify("./test/functional/fixtures/bin", ":p")')
for _, input in ipairs({'v:null', 'v:true', 'v:false'}) do
eq('Vim(call):E1174: String required for argument 1', exc_exec('call exepath('..input..')'))
for _, input in ipairs({ 'v:null', 'v:true', 'v:false' }) do
eq(
'Vim(call):E1174: String required for argument 1',
exc_exec('call exepath(' .. input .. ')')
)
end
end)
@@ -40,24 +45,30 @@ describe('exepath()', function()
it('append extension if omitted', function()
local filename = 'cmd'
local pathext = '.exe'
clear({env={PATHEXT=pathext}})
eq(call('exepath', filename..pathext), call('exepath', filename))
clear({ env = { PATHEXT = pathext } })
eq(call('exepath', filename .. pathext), call('exepath', filename))
end)
it('returns file WITH extension if files both with and without extension exist in $PATH', function()
local ext_pat = '%.CMD$'
find_dummies(ext_pat)
set_shell_powershell()
find_dummies(ext_pat)
end)
it(
'returns file WITH extension if files both with and without extension exist in $PATH',
function()
local ext_pat = '%.CMD$'
find_dummies(ext_pat)
set_shell_powershell()
find_dummies(ext_pat)
end
)
else
it('returns 1 for commands in $PATH (not Windows)', function()
local exe = 'ls'
matches(exe .. '$', call('exepath', exe))
end)
it('returns file WITHOUT extension if files both with and without extension exist in $PATH', function()
find_dummies('$')
end)
it(
'returns file WITHOUT extension if files both with and without extension exist in $PATH',
function()
find_dummies('$')
end
)
end
end)

View File

@@ -31,7 +31,7 @@ describe('fnamemodify()', function()
eq(root, fnamemodify([[\]], ':p:h'))
eq(root, fnamemodify([[\]], ':p'))
command('set shellslash')
root = string.sub(root, 1, -2)..'/'
root = string.sub(root, 1, -2) .. '/'
eq(root, fnamemodify([[\]], ':p:h'))
eq(root, fnamemodify([[\]], ':p'))
eq(root, fnamemodify([[/]], ':p:h'))
@@ -44,7 +44,7 @@ describe('fnamemodify()', function()
end)
it('handles examples from ":help filename-modifiers"', function()
local filename = "src/version.c"
local filename = 'src/version.c'
local cwd = getcwd()
eq_slashconvert(cwd .. '/src/version.c', fnamemodify(filename, ':p'))
@@ -70,7 +70,7 @@ describe('fnamemodify()', function()
end)
it('handles advanced examples from ":help filename-modifiers"', function()
local filename = "src/version.c.gz"
local filename = 'src/version.c.gz'
eq('gz', fnamemodify(filename, ':e'))
eq('c.gz', fnamemodify(filename, ':e:e'))

View File

@@ -8,7 +8,7 @@ local expect = helpers.expect
describe('getline()', function()
before_each(function()
clear()
call('setline', 1, {'a', 'b', 'c'})
call('setline', 1, { 'a', 'b', 'c' })
expect([[
a
b
@@ -33,7 +33,7 @@ describe('getline()', function()
end)
it('returns value of valid range', function()
eq({'a', 'b'}, call('getline', 1, 2))
eq({'a', 'b', 'c'}, call('getline', 1, 4))
eq({ 'a', 'b' }, call('getline', 1, 2))
eq({ 'a', 'b', 'c' }, call('getline', 1, 4))
end)
end)

View File

@@ -17,9 +17,9 @@ end)
describe('glob()', function()
it("glob('.*') returns . and .. ", function()
eq({'.', '..'}, eval("glob('.*', 0, 1)"))
eq({ '.', '..' }, eval("glob('.*', 0, 1)"))
-- Do it again to verify scandir_next_with_dots() internal state.
eq({'.', '..'}, eval("glob('.*', 0, 1)"))
eq({ '.', '..' }, eval("glob('.*', 0, 1)"))
end)
it("glob('*') returns an empty list ", function()
eq({}, eval("glob('*', 0, 1)"))

View File

@@ -11,60 +11,59 @@ 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(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"))
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)
it('"unnamedplus"', function()
if (not is_os('win')) and funcs.has("clipboard") == 1 then
eq(1, funcs.has("unnamedplus"))
if (not is_os('win')) and funcs.has('clipboard') == 1 then
eq(1, funcs.has('unnamedplus'))
else
eq(0, funcs.has("unnamedplus"))
eq(0, funcs.has('unnamedplus'))
end
end)
it('"wsl"', function()
local luv = require('luv')
local is_wsl =
luv.os_uname()['release']:lower():match('microsoft') and true or false
local is_wsl = luv.os_uname()['release']:lower():match('microsoft') and true or false
if is_wsl then
eq(1, funcs.has('wsl'))
else
@@ -74,12 +73,12 @@ describe('has()', function()
it('"gui_running"', function()
eq(0, funcs.has('gui_running'))
local tui = Screen.new(50,15)
local tui = Screen.new(50, 15)
local gui_session = connect(funcs.serverstart())
local gui = Screen.new(50,15)
local gui = Screen.new(50, 15)
eq(0, funcs.has('gui_running'))
tui:attach({ext_linegrid=true, rgb=true, stdin_tty=true, stdout_tty=true})
gui:attach({ext_multigrid=true, rgb=true}, gui_session)
tui:attach({ ext_linegrid = true, rgb = true, stdin_tty = true, stdout_tty = true })
gui:attach({ ext_multigrid = true, rgb = true }, gui_session)
eq(1, funcs.has('gui_running'))
tui:detach()
eq(1, funcs.has('gui_running'))
@@ -88,7 +87,7 @@ describe('has()', function()
end)
it('does not change v:shell_error', function()
funcs.system({nvim_prog, '-es', '+73cquit'})
funcs.system({ nvim_prog, '-es', '+73cquit' })
funcs.has('python3') -- use a call whose implementation shells out
eq(73, funcs.eval('v:shell_error'))
end)

View File

@@ -13,8 +13,10 @@ describe('hostname()', function()
ok(string.len(actual) > 0)
if call('executable', 'hostname') == 1 then
local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '')
eq((is_os('win') and expected:upper() or expected),
(is_os('win') and actual:upper() or actual))
eq(
(is_os('win') and expected:upper() or expected),
(is_os('win') and actual:upper() or actual)
)
end
end)
end)

View File

@@ -55,14 +55,14 @@ before_each(function()
endfunction
]])
screen:set_default_attr_ids({
EOB={bold = true, foreground = Screen.colors.Blue1},
T={foreground=Screen.colors.Red},
RBP1={background=Screen.colors.Red},
RBP2={background=Screen.colors.Yellow},
RBP3={background=Screen.colors.Green},
RBP4={background=Screen.colors.Blue},
SEP={bold = true, reverse = true},
CONFIRM={bold = true, foreground = Screen.colors.SeaGreen4},
EOB = { bold = true, foreground = Screen.colors.Blue1 },
T = { foreground = Screen.colors.Red },
RBP1 = { background = Screen.colors.Red },
RBP2 = { background = Screen.colors.Yellow },
RBP3 = { background = Screen.colors.Green },
RBP4 = { background = Screen.colors.Blue },
SEP = { bold = true, reverse = true },
CONFIRM = { bold = true, foreground = Screen.colors.SeaGreen4 },
})
end)
@@ -110,7 +110,7 @@ describe('input()', function()
end)
it('allows unequal numeric values when using {opts} dictionary', function()
command('echohl Test')
meths.set_var('opts', {prompt=1, default=2, cancelreturn=3})
meths.set_var('opts', { prompt = 1, default = 2, cancelreturn = 3 })
feed([[:echo input(opts)<CR>]])
screen:expect([[
|
@@ -132,7 +132,7 @@ describe('input()', function()
end)
it('works with redraw', function()
command('echohl Test')
meths.set_var('opts', {prompt='Foo>', default='Bar'})
meths.set_var('opts', { prompt = 'Foo>', default = 'Bar' })
feed([[:echo inputdialog(opts)<CR>]])
screen:expect([[
|
@@ -140,11 +140,14 @@ describe('input()', function()
{T:Foo>}Bar^ |
]])
command('mode')
screen:expect{grid=[[
screen:expect {
grid = [[
|
{EOB:~ }|*3
{T:Foo>}Bar^ |
]], reset=true}
]],
reset = true,
}
feed('<BS>')
screen:expect([[
|
@@ -152,11 +155,14 @@ describe('input()', function()
{T:Foo>}Ba^ |
]])
command('mode')
screen:expect{grid=[[
screen:expect {
grid = [[
|
{EOB:~ }|*3
{T:Foo>}Ba^ |
]], reset=true}
]],
reset = true,
}
end)
it('allows omitting everything with dictionary argument', function()
command('echohl Test')
@@ -200,22 +206,17 @@ describe('input()', function()
eq('DEF2', meths.get_var('var'))
end)
it('errors out on invalid inputs', function()
eq('Vim(call):E730: Using a List as a String',
exc_exec('call input([])'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call input("", [])'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call input("", "", [])'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call input({"prompt": []})'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call input({"default": []})'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call input({"completion": []})'))
eq('Vim(call):E5050: {opts} must be the only argument',
exc_exec('call input({}, "default")'))
eq('Vim(call):E118: Too many arguments for function: input',
exc_exec('call input("prompt> ", "default", "file", "extra")'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call input([])'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", [])'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call input("", "", [])'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"prompt": []})'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"default": []})'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call input({"completion": []})'))
eq('Vim(call):E5050: {opts} must be the only argument', exc_exec('call input({}, "default")'))
eq(
'Vim(call):E118: Too many arguments for function: input',
exc_exec('call input("prompt> ", "default", "file", "extra")')
)
end)
it('supports highlighting', function()
command('nnoremap <expr> X input({"highlight": "RainBowParens"})[-1]')
@@ -291,7 +292,7 @@ describe('inputdialog()', function()
end)
it('allows unequal numeric values when using {opts} dictionary', function()
command('echohl Test')
meths.set_var('opts', {prompt=1, default=2, cancelreturn=3})
meths.set_var('opts', { prompt = 1, default = 2, cancelreturn = 3 })
feed([[:echo input(opts)<CR>]])
screen:expect([[
|
@@ -313,7 +314,7 @@ describe('inputdialog()', function()
end)
it('works with redraw', function()
command('echohl Test')
meths.set_var('opts', {prompt='Foo>', default='Bar'})
meths.set_var('opts', { prompt = 'Foo>', default = 'Bar' })
feed([[:echo input(opts)<CR>]])
screen:expect([[
|
@@ -321,11 +322,14 @@ describe('inputdialog()', function()
{T:Foo>}Bar^ |
]])
command('mode')
screen:expect{grid=[[
screen:expect {
grid = [[
|
{EOB:~ }|*3
{T:Foo>}Bar^ |
]], reset=true}
]],
reset = true,
}
feed('<BS>')
screen:expect([[
|
@@ -333,11 +337,14 @@ describe('inputdialog()', function()
{T:Foo>}Ba^ |
]])
command('mode')
screen:expect{grid=[[
screen:expect {
grid = [[
|
{EOB:~ }|*3
{T:Foo>}Ba^ |
]], reset=true}
]],
reset = true,
}
end)
it('allows omitting everything with dictionary argument', function()
command('echohl Test')
@@ -372,22 +379,20 @@ describe('inputdialog()', function()
eq('DEF2', meths.get_var('var'))
end)
it('errors out on invalid inputs', function()
eq('Vim(call):E730: Using a List as a String',
exc_exec('call inputdialog([])'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call inputdialog("", [])'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call inputdialog("", "", [])'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call inputdialog({"prompt": []})'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call inputdialog({"default": []})'))
eq('Vim(call):E730: Using a List as a String',
exc_exec('call inputdialog({"completion": []})'))
eq('Vim(call):E5050: {opts} must be the only argument',
exc_exec('call inputdialog({}, "default")'))
eq('Vim(call):E118: Too many arguments for function: inputdialog',
exc_exec('call inputdialog("prompt> ", "default", "file", "extra")'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog([])'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", [])'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog("", "", [])'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"prompt": []})'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"default": []})'))
eq('Vim(call):E730: Using a List as a String', exc_exec('call inputdialog({"completion": []})'))
eq(
'Vim(call):E5050: {opts} must be the only argument',
exc_exec('call inputdialog({}, "default")')
)
eq(
'Vim(call):E118: Too many arguments for function: inputdialog',
exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')
)
end)
it('supports highlighting', function()
command('nnoremap <expr> X inputdialog({"highlight": "RainBowParens"})[-1]')
@@ -404,84 +409,89 @@ end)
describe('confirm()', function()
-- oldtest: Test_confirm()
it('works', function()
meths.set_option_value('more', false, {}) -- Avoid hit-enter prompt
meths.set_option_value('more', false, {}) -- Avoid hit-enter prompt
meths.set_option_value('laststatus', 2, {})
-- screen:expect() calls are needed to avoid feeding input too early
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
async_meths.command([[let a = confirm('Press O to proceed')]])
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('o')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(1, meths.get_var('a'))
async_meths.command([[let a = 'Are you sure?'->confirm("&Yes\n&No")]])
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('y')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(1, meths.get_var('a'))
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('n')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(2, meths.get_var('a'))
-- Not possible to match Vim's CTRL-C test here as CTRL-C always sets got_int in Nvim.
-- confirm() should return 0 when pressing ESC.
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('<Esc>')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(0, meths.get_var('a'))
-- Default choice is returned when pressing <CR>.
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No")]])
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('<CR>')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(1, meths.get_var('a'))
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 2)]])
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('<CR>')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(2, meths.get_var('a'))
async_meths.command([[let a = confirm('Are you sure?', "&Yes\n&No", 0)]])
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('<CR>')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(0, meths.get_var('a'))
-- Test with the {type} 4th argument
for _, type in ipairs({'Error', 'Question', 'Info', 'Warning', 'Generic'}) do
for _, type in ipairs({ 'Error', 'Question', 'Info', 'Warning', 'Generic' }) do
async_meths.command(([[let a = confirm('Are you sure?', "&Yes\n&No", 1, '%s')]]):format(type))
screen:expect({any = '{CONFIRM:.+: }'})
screen:expect({ any = '{CONFIRM:.+: }' })
feed('y')
screen:expect({any = '%[No Name%]'})
screen:expect({ any = '%[No Name%]' })
eq(1, meths.get_var('a'))
end
eq('Vim(call):E730: Using a List as a String',
pcall_err(command, 'call confirm([])'))
eq('Vim(call):E730: Using a List as a String',
pcall_err(command, 'call confirm("Are you sure?", [])'))
eq('Vim(call):E745: Using a List as a Number',
pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", [])'))
eq('Vim(call):E730: Using a List as a String',
pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", 0, [])'))
eq('Vim(call):E730: Using a List as a String', pcall_err(command, 'call confirm([])'))
eq(
'Vim(call):E730: Using a List as a String',
pcall_err(command, 'call confirm("Are you sure?", [])')
)
eq(
'Vim(call):E745: Using a List as a Number',
pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", [])')
)
eq(
'Vim(call):E730: Using a List as a String',
pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", 0, [])')
)
end)
it("shows dialog even if :silent #8788", function()
it('shows dialog even if :silent #8788', function()
command("autocmd BufNewFile * call confirm('test')")
local function check_and_clear(edit_line)
screen:expect([[
|
{SEP: }|
]]..edit_line..[[
]] .. edit_line .. [[
{CONFIRM:test} |
{CONFIRM:[O]k: }^ |
]])

View File

@@ -63,19 +63,25 @@ describe('json_decode() function', function()
end
it('accepts readfile()-style list', function()
eq({Test=1}, funcs.json_decode({
'{',
'\t"Test": 1',
'}',
}))
eq(
{ Test = 1 },
funcs.json_decode({
'{',
'\t"Test": 1',
'}',
})
)
end)
it('accepts strings with newlines', function()
eq({Test=1}, funcs.json_decode([[
eq(
{ Test = 1 },
funcs.json_decode([[
{
"Test": 1
}
]]))
]])
)
end)
it('parses null, true, false', function()
@@ -85,34 +91,21 @@ describe('json_decode() function', function()
end)
it('fails to parse incomplete null, true, false', function()
eq('Vim(call):E474: Expected null: n',
exc_exec('call json_decode("n")'))
eq('Vim(call):E474: Expected null: nu',
exc_exec('call json_decode("nu")'))
eq('Vim(call):E474: Expected null: nul',
exc_exec('call json_decode("nul")'))
eq('Vim(call):E474: Expected null: nul\n\t',
exc_exec('call json_decode("nul\\n\\t")'))
eq('Vim(call):E474: Expected null: n', exc_exec('call json_decode("n")'))
eq('Vim(call):E474: Expected null: nu', exc_exec('call json_decode("nu")'))
eq('Vim(call):E474: Expected null: nul', exc_exec('call json_decode("nul")'))
eq('Vim(call):E474: Expected null: nul\n\t', exc_exec('call json_decode("nul\\n\\t")'))
eq('Vim(call):E474: Expected true: t',
exc_exec('call json_decode("t")'))
eq('Vim(call):E474: Expected true: tr',
exc_exec('call json_decode("tr")'))
eq('Vim(call):E474: Expected true: tru',
exc_exec('call json_decode("tru")'))
eq('Vim(call):E474: Expected true: tru\t\n',
exc_exec('call json_decode("tru\\t\\n")'))
eq('Vim(call):E474: Expected true: t', exc_exec('call json_decode("t")'))
eq('Vim(call):E474: Expected true: tr', exc_exec('call json_decode("tr")'))
eq('Vim(call):E474: Expected true: tru', exc_exec('call json_decode("tru")'))
eq('Vim(call):E474: Expected true: tru\t\n', exc_exec('call json_decode("tru\\t\\n")'))
eq('Vim(call):E474: Expected false: f',
exc_exec('call json_decode("f")'))
eq('Vim(call):E474: Expected false: fa',
exc_exec('call json_decode("fa")'))
eq('Vim(call):E474: Expected false: fal',
exc_exec('call json_decode("fal")'))
eq('Vim(call):E474: Expected false: fal <',
exc_exec('call json_decode(" fal <")'))
eq('Vim(call):E474: Expected false: fals',
exc_exec('call json_decode("fals")'))
eq('Vim(call):E474: Expected false: f', exc_exec('call json_decode("f")'))
eq('Vim(call):E474: Expected false: fa', exc_exec('call json_decode("fa")'))
eq('Vim(call):E474: Expected false: fal', exc_exec('call json_decode("fal")'))
eq('Vim(call):E474: Expected false: fal <', exc_exec('call json_decode(" fal <")'))
eq('Vim(call):E474: Expected false: fals', exc_exec('call json_decode("fals")'))
end)
it('parses integer numbers', function()
@@ -125,46 +118,41 @@ describe('json_decode() function', function()
end)
it('fails to parse +numbers and .number', function()
eq('Vim(call):E474: Unidentified byte: +1000',
exc_exec('call json_decode("+1000")'))
eq('Vim(call):E474: Unidentified byte: .1000',
exc_exec('call json_decode(".1000")'))
eq('Vim(call):E474: Unidentified byte: +1000', exc_exec('call json_decode("+1000")'))
eq('Vim(call):E474: Unidentified byte: .1000', exc_exec('call json_decode(".1000")'))
end)
it('fails to parse numbers with leading zeroes', function()
eq('Vim(call):E474: Leading zeroes are not allowed: 00.1',
exc_exec('call json_decode("00.1")'))
eq('Vim(call):E474: Leading zeroes are not allowed: 01',
exc_exec('call json_decode("01")'))
eq('Vim(call):E474: Leading zeroes are not allowed: -01',
exc_exec('call json_decode("-01")'))
eq('Vim(call):E474: Leading zeroes are not allowed: -001.0',
exc_exec('call json_decode("-001.0")'))
eq('Vim(call):E474: Leading zeroes are not allowed: 00.1', exc_exec('call json_decode("00.1")'))
eq('Vim(call):E474: Leading zeroes are not allowed: 01', exc_exec('call json_decode("01")'))
eq('Vim(call):E474: Leading zeroes are not allowed: -01', exc_exec('call json_decode("-01")'))
eq(
'Vim(call):E474: Leading zeroes are not allowed: -001.0',
exc_exec('call json_decode("-001.0")')
)
end)
it('fails to parse incomplete numbers', function()
eq('Vim(call):E474: Missing number after minus sign: -.1',
exc_exec('call json_decode("-.1")'))
eq('Vim(call):E474: Missing number after minus sign: -',
exc_exec('call json_decode("-")'))
eq('Vim(call):E474: Missing number after decimal dot: -1.',
exc_exec('call json_decode("-1.")'))
eq('Vim(call):E474: Missing number after decimal dot: 0.',
exc_exec('call json_decode("0.")'))
eq('Vim(call):E474: Missing exponent: 0.0e',
exc_exec('call json_decode("0.0e")'))
eq('Vim(call):E474: Missing exponent: 0.0e+',
exc_exec('call json_decode("0.0e+")'))
eq('Vim(call):E474: Missing exponent: 0.0e-',
exc_exec('call json_decode("0.0e-")'))
eq('Vim(call):E474: Missing exponent: 0.0e-',
exc_exec('call json_decode("0.0e-")'))
eq('Vim(call):E474: Missing number after decimal dot: 1.e5',
exc_exec('call json_decode("1.e5")'))
eq('Vim(call):E474: Missing number after decimal dot: 1.e+5',
exc_exec('call json_decode("1.e+5")'))
eq('Vim(call):E474: Missing number after decimal dot: 1.e+',
exc_exec('call json_decode("1.e+")'))
eq('Vim(call):E474: Missing number after minus sign: -.1', exc_exec('call json_decode("-.1")'))
eq('Vim(call):E474: Missing number after minus sign: -', exc_exec('call json_decode("-")'))
eq('Vim(call):E474: Missing number after decimal dot: -1.', exc_exec('call json_decode("-1.")'))
eq('Vim(call):E474: Missing number after decimal dot: 0.', exc_exec('call json_decode("0.")'))
eq('Vim(call):E474: Missing exponent: 0.0e', exc_exec('call json_decode("0.0e")'))
eq('Vim(call):E474: Missing exponent: 0.0e+', exc_exec('call json_decode("0.0e+")'))
eq('Vim(call):E474: Missing exponent: 0.0e-', exc_exec('call json_decode("0.0e-")'))
eq('Vim(call):E474: Missing exponent: 0.0e-', exc_exec('call json_decode("0.0e-")'))
eq(
'Vim(call):E474: Missing number after decimal dot: 1.e5',
exc_exec('call json_decode("1.e5")')
)
eq(
'Vim(call):E474: Missing number after decimal dot: 1.e+5',
exc_exec('call json_decode("1.e+5")')
)
eq(
'Vim(call):E474: Missing number after decimal dot: 1.e+',
exc_exec('call json_decode("1.e+")')
)
end)
it('parses floating-point numbers', function()
@@ -202,25 +190,23 @@ describe('json_decode() function', function()
end)
it('fails to parse numbers with spaces inside', function()
eq('Vim(call):E474: Missing number after minus sign: - 1000',
exc_exec('call json_decode("- 1000")'))
eq('Vim(call):E474: Missing number after decimal dot: 0. ',
exc_exec('call json_decode("0. ")'))
eq('Vim(call):E474: Missing number after decimal dot: 0. 0',
exc_exec('call json_decode("0. 0")'))
eq('Vim(call):E474: Missing exponent: 0.0e 1',
exc_exec('call json_decode("0.0e 1")'))
eq('Vim(call):E474: Missing exponent: 0.0e+ 1',
exc_exec('call json_decode("0.0e+ 1")'))
eq('Vim(call):E474: Missing exponent: 0.0e- 1',
exc_exec('call json_decode("0.0e- 1")'))
eq(
'Vim(call):E474: Missing number after minus sign: - 1000',
exc_exec('call json_decode("- 1000")')
)
eq('Vim(call):E474: Missing number after decimal dot: 0. ', exc_exec('call json_decode("0. ")'))
eq(
'Vim(call):E474: Missing number after decimal dot: 0. 0',
exc_exec('call json_decode("0. 0")')
)
eq('Vim(call):E474: Missing exponent: 0.0e 1', exc_exec('call json_decode("0.0e 1")'))
eq('Vim(call):E474: Missing exponent: 0.0e+ 1', exc_exec('call json_decode("0.0e+ 1")'))
eq('Vim(call):E474: Missing exponent: 0.0e- 1', exc_exec('call json_decode("0.0e- 1")'))
end)
it('fails to parse "," and ":"', function()
eq('Vim(call):E474: Comma not inside container: , ',
exc_exec('call json_decode(" , ")'))
eq('Vim(call):E474: Colon not inside container: : ',
exc_exec('call json_decode(" : ")'))
eq('Vim(call):E474: Comma not inside container: , ', exc_exec('call json_decode(" , ")'))
eq('Vim(call):E474: Colon not inside container: : ', exc_exec('call json_decode(" : ")'))
end)
it('parses empty containers', function()
@@ -229,123 +215,151 @@ describe('json_decode() function', function()
end)
it('fails to parse "[" and "{"', function()
eq('Vim(call):E474: Unexpected end of input: {',
exc_exec('call json_decode("{")'))
eq('Vim(call):E474: Unexpected end of input: [',
exc_exec('call json_decode("[")'))
eq('Vim(call):E474: Unexpected end of input: {', exc_exec('call json_decode("{")'))
eq('Vim(call):E474: Unexpected end of input: [', exc_exec('call json_decode("[")'))
end)
it('fails to parse "}" and "]"', function()
eq('Vim(call):E474: No container to close: ]',
exc_exec('call json_decode("]")'))
eq('Vim(call):E474: No container to close: }',
exc_exec('call json_decode("}")'))
eq('Vim(call):E474: No container to close: ]', exc_exec('call json_decode("]")'))
eq('Vim(call):E474: No container to close: }', exc_exec('call json_decode("}")'))
end)
it('fails to parse containers which are closed by different brackets',
function()
eq('Vim(call):E474: Closing dictionary with square bracket: ]',
exc_exec('call json_decode("{]")'))
eq('Vim(call):E474: Closing list with curly bracket: }',
exc_exec('call json_decode("[}")'))
it('fails to parse containers which are closed by different brackets', function()
eq(
'Vim(call):E474: Closing dictionary with square bracket: ]',
exc_exec('call json_decode("{]")')
)
eq('Vim(call):E474: Closing list with curly bracket: }', exc_exec('call json_decode("[}")'))
end)
it('fails to parse concat inside container', function()
eq('Vim(call):E474: Expected comma before list item: []]',
exc_exec('call json_decode("[[][]]")'))
eq('Vim(call):E474: Expected comma before list item: {}]',
exc_exec('call json_decode("[{}{}]")'))
eq('Vim(call):E474: Expected comma before list item: ]',
exc_exec('call json_decode("[1 2]")'))
eq('Vim(call):E474: Expected comma before dictionary key: ": 4}',
exc_exec('call json_decode("{\\"1\\": 2 \\"3\\": 4}")'))
eq('Vim(call):E474: Expected colon before dictionary value: , "3" 4}',
exc_exec('call json_decode("{\\"1\\" 2, \\"3\\" 4}")'))
eq(
'Vim(call):E474: Expected comma before list item: []]',
exc_exec('call json_decode("[[][]]")')
)
eq(
'Vim(call):E474: Expected comma before list item: {}]',
exc_exec('call json_decode("[{}{}]")')
)
eq('Vim(call):E474: Expected comma before list item: ]', exc_exec('call json_decode("[1 2]")'))
eq(
'Vim(call):E474: Expected comma before dictionary key: ": 4}',
exc_exec('call json_decode("{\\"1\\": 2 \\"3\\": 4}")')
)
eq(
'Vim(call):E474: Expected colon before dictionary value: , "3" 4}',
exc_exec('call json_decode("{\\"1\\" 2, \\"3\\" 4}")')
)
end)
it('fails to parse containers with leading comma or colon', function()
eq('Vim(call):E474: Leading comma: ,}',
exc_exec('call json_decode("{,}")'))
eq('Vim(call):E474: Leading comma: ,]',
exc_exec('call json_decode("[,]")'))
eq('Vim(call):E474: Using colon not in dictionary: :]',
exc_exec('call json_decode("[:]")'))
eq('Vim(call):E474: Unexpected colon: :}',
exc_exec('call json_decode("{:}")'))
eq('Vim(call):E474: Leading comma: ,}', exc_exec('call json_decode("{,}")'))
eq('Vim(call):E474: Leading comma: ,]', exc_exec('call json_decode("[,]")'))
eq('Vim(call):E474: Using colon not in dictionary: :]', exc_exec('call json_decode("[:]")'))
eq('Vim(call):E474: Unexpected colon: :}', exc_exec('call json_decode("{:}")'))
end)
it('fails to parse containers with trailing comma', function()
eq('Vim(call):E474: Trailing comma: ]',
exc_exec('call json_decode("[1,]")'))
eq('Vim(call):E474: Trailing comma: }',
exc_exec('call json_decode("{\\"1\\": 2,}")'))
eq('Vim(call):E474: Trailing comma: ]', exc_exec('call json_decode("[1,]")'))
eq('Vim(call):E474: Trailing comma: }', exc_exec('call json_decode("{\\"1\\": 2,}")'))
end)
it('fails to parse dictionaries with missing value', function()
eq('Vim(call):E474: Expected value after colon: }',
exc_exec('call json_decode("{\\"1\\":}")'))
eq('Vim(call):E474: Expected value: }',
exc_exec('call json_decode("{\\"1\\"}")'))
eq('Vim(call):E474: Expected value after colon: }', exc_exec('call json_decode("{\\"1\\":}")'))
eq('Vim(call):E474: Expected value: }', exc_exec('call json_decode("{\\"1\\"}")'))
end)
it('fails to parse containers with two commas or colons', function()
eq('Vim(call):E474: Duplicate comma: , "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1,, \\"2\\": 2}")'))
eq('Vim(call):E474: Duplicate comma: , "2", 2]',
exc_exec('call json_decode("[\\"1\\", 1,, \\"2\\", 2]")'))
eq('Vim(call):E474: Duplicate colon: : 2}',
exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":: 2}")'))
eq('Vim(call):E474: Comma after colon: , 2}',
exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":, 2}")'))
eq('Vim(call):E474: Unexpected colon: : "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1,: \\"2\\": 2}")'))
eq('Vim(call):E474: Unexpected colon: :, "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1:, \\"2\\": 2}")'))
eq(
'Vim(call):E474: Duplicate comma: , "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1,, \\"2\\": 2}")')
)
eq(
'Vim(call):E474: Duplicate comma: , "2", 2]',
exc_exec('call json_decode("[\\"1\\", 1,, \\"2\\", 2]")')
)
eq(
'Vim(call):E474: Duplicate colon: : 2}',
exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":: 2}")')
)
eq(
'Vim(call):E474: Comma after colon: , 2}',
exc_exec('call json_decode("{\\"1\\": 1, \\"2\\":, 2}")')
)
eq(
'Vim(call):E474: Unexpected colon: : "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1,: \\"2\\": 2}")')
)
eq(
'Vim(call):E474: Unexpected colon: :, "2": 2}',
exc_exec('call json_decode("{\\"1\\": 1:, \\"2\\": 2}")')
)
end)
it('fails to parse concat of two values', function()
eq('Vim(call):E474: Trailing characters: []',
exc_exec('call json_decode("{}[]")'))
eq('Vim(call):E474: Trailing characters: []', exc_exec('call json_decode("{}[]")'))
end)
it('parses containers', function()
eq({1}, funcs.json_decode('[1]'))
eq({NIL, 1}, funcs.json_decode('[null, 1]'))
eq({['1']=2}, funcs.json_decode('{"1": 2}'))
eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}},
funcs.json_decode('{"1": 2, "3": [{"4": {"5": [[], 1]}}]}'))
eq({ 1 }, funcs.json_decode('[1]'))
eq({ NIL, 1 }, funcs.json_decode('[null, 1]'))
eq({ ['1'] = 2 }, funcs.json_decode('{"1": 2}'))
eq(
{ ['1'] = 2, ['3'] = { { ['4'] = { ['5'] = { {}, 1 } } } } },
funcs.json_decode('{"1": 2, "3": [{"4": {"5": [[], 1]}}]}')
)
end)
it('fails to parse incomplete strings', function()
eq('Vim(call):E474: Expected string end: \t"',
exc_exec('call json_decode("\\t\\"")'))
eq('Vim(call):E474: Expected string end: \t"abc',
exc_exec('call json_decode("\\t\\"abc")'))
eq('Vim(call):E474: Unfinished escape sequence: \t"abc\\',
exc_exec('call json_decode("\\t\\"abc\\\\")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u',
exc_exec('call json_decode("\\t\\"abc\\\\u")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u0',
exc_exec('call json_decode("\\t\\"abc\\\\u0")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u00',
exc_exec('call json_decode("\\t\\"abc\\\\u00")'))
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000',
exc_exec('call json_decode("\\t\\"abc\\\\u000")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u" ',
exc_exec('call json_decode("\\t\\"abc\\\\u\\" ")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u0" ',
exc_exec('call json_decode("\\t\\"abc\\\\u0\\" ")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u00" ',
exc_exec('call json_decode("\\t\\"abc\\\\u00\\" ")'))
eq('Vim(call):E474: Expected four hex digits after \\u: \\u000" ',
exc_exec('call json_decode("\\t\\"abc\\\\u000\\" ")'))
eq('Vim(call):E474: Expected string end: \t"abc\\u0000',
exc_exec('call json_decode("\\t\\"abc\\\\u0000")'))
eq('Vim(call):E474: Expected string end: \t"', exc_exec('call json_decode("\\t\\"")'))
eq('Vim(call):E474: Expected string end: \t"abc', exc_exec('call json_decode("\\t\\"abc")'))
eq(
'Vim(call):E474: Unfinished escape sequence: \t"abc\\',
exc_exec('call json_decode("\\t\\"abc\\\\")')
)
eq(
'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u',
exc_exec('call json_decode("\\t\\"abc\\\\u")')
)
eq(
'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u0',
exc_exec('call json_decode("\\t\\"abc\\\\u0")')
)
eq(
'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u00',
exc_exec('call json_decode("\\t\\"abc\\\\u00")')
)
eq(
'Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000',
exc_exec('call json_decode("\\t\\"abc\\\\u000")')
)
eq(
'Vim(call):E474: Expected four hex digits after \\u: \\u" ',
exc_exec('call json_decode("\\t\\"abc\\\\u\\" ")')
)
eq(
'Vim(call):E474: Expected four hex digits after \\u: \\u0" ',
exc_exec('call json_decode("\\t\\"abc\\\\u0\\" ")')
)
eq(
'Vim(call):E474: Expected four hex digits after \\u: \\u00" ',
exc_exec('call json_decode("\\t\\"abc\\\\u00\\" ")')
)
eq(
'Vim(call):E474: Expected four hex digits after \\u: \\u000" ',
exc_exec('call json_decode("\\t\\"abc\\\\u000\\" ")')
)
eq(
'Vim(call):E474: Expected string end: \t"abc\\u0000',
exc_exec('call json_decode("\\t\\"abc\\\\u0000")')
)
end)
it('fails to parse unknown escape sequences', function()
eq('Vim(call):E474: Unknown escape sequence: \\a"',
exc_exec('call json_decode("\\t\\"\\\\a\\"")'))
eq(
'Vim(call):E474: Unknown escape sequence: \\a"',
exc_exec('call json_decode("\\t\\"\\\\a\\"")')
)
end)
it('parses strings properly', function()
@@ -354,64 +368,105 @@ describe('json_decode() function', function()
eq('\\/"\t\b\n\r\f', funcs.json_decode([["\\\/\"\t\b\n\r\f"]]))
eq('/a', funcs.json_decode([["\/a"]]))
-- Unicode characters: 2-byte, 3-byte, 4-byte
eq({
'«',
'',
'\240\144\128\128',
}, funcs.json_decode({
'[',
'"«",',
'"ફ",',
'"\240\144\128\128"',
']',
}))
eq(
{
'«',
'',
'\240\144\128\128',
},
funcs.json_decode({
'[',
'"«",',
'"ફ",',
'"\240\144\128\128"',
']',
})
)
end)
it('fails on strings with invalid bytes', function()
eq('Vim(call):E474: Only UTF-8 strings allowed: \255"',
exc_exec('call json_decode("\\t\\"\\xFF\\"")'))
eq('Vim(call):E474: ASCII control characters cannot be present inside string: ',
exc_exec('call json_decode(["\\"\\n\\""])'))
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \255"',
exc_exec('call json_decode("\\t\\"\\xFF\\"")')
)
eq(
'Vim(call):E474: ASCII control characters cannot be present inside string: ',
exc_exec('call json_decode(["\\"\\n\\""])')
)
-- 0xC2 starts 2-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \194"',
exc_exec('call json_decode("\\t\\"\\xC2\\"")'))
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \194"',
exc_exec('call json_decode("\\t\\"\\xC2\\"")')
)
-- 0xE0 0xAA starts 3-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \224"',
exc_exec('call json_decode("\\t\\"\\xE0\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \224\170"',
exc_exec('call json_decode("\\t\\"\\xE0\\xAA\\"")'))
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \224"',
exc_exec('call json_decode("\\t\\"\\xE0\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \224\170"',
exc_exec('call json_decode("\\t\\"\\xE0\\xAA\\"")')
)
-- 0xF0 0x90 0x80 starts 4-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \240"',
exc_exec('call json_decode("\\t\\"\\xF0\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144"',
exc_exec('call json_decode("\\t\\"\\xF0\\x90\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \240\144\128"',
exc_exec('call json_decode("\\t\\"\\xF0\\x90\\x80\\"")'))
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \240"',
exc_exec('call json_decode("\\t\\"\\xF0\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \240\144"',
exc_exec('call json_decode("\\t\\"\\xF0\\x90\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \240\144\128"',
exc_exec('call json_decode("\\t\\"\\xF0\\x90\\x80\\"")')
)
-- 0xF9 0x80 0x80 0x80 starts 5-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \249"',
exc_exec('call json_decode("\\t\\"\\xF9\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \249\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\"")'))
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \249"',
exc_exec('call json_decode("\\t\\"\\xF9\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \249\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \249\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \249\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\"")')
)
-- 0xFC 0x90 0x80 0x80 0x80 starts 6-byte unicode character
eq('Vim(call):E474: Only UTF-8 strings allowed: \252"',
exc_exec('call json_decode("\\t\\"\\xFC\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\"")'))
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \252"',
exc_exec('call json_decode("\\t\\"\\xFC\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \252\144"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \252\144\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 strings allowed: \252\144\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\"")')
)
-- Specification does not allow unquoted characters above 0x10FFFF
eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \249\128\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\x80\\"")'))
eq('Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\x80\\"")'))
eq(
'Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \249\128\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xF9\\x80\\x80\\x80\\x80\\"")')
)
eq(
'Vim(call):E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: \252\144\128\128\128\128"',
exc_exec('call json_decode("\\t\\"\\xFC\\x90\\x80\\x80\\x80\\x80\\"")')
)
-- '"\249\128\128\128\128"',
-- '"\252\144\128\128\128\128"',
end)
@@ -445,47 +500,105 @@ describe('json_decode() function', function()
end
it('parses strings with NUL properly', function()
sp_decode_eq({_TYPE='string', _VAL={'\n'}}, '"\\u0000"')
sp_decode_eq({_TYPE='string', _VAL={'\n', '\n'}}, '"\\u0000\\n\\u0000"')
sp_decode_eq({_TYPE='string', _VAL={'\n«\n'}}, '"\\u0000\\u00AB\\u0000"')
sp_decode_eq({ _TYPE = 'string', _VAL = { '\n' } }, '"\\u0000"')
sp_decode_eq({ _TYPE = 'string', _VAL = { '\n', '\n' } }, '"\\u0000\\n\\u0000"')
sp_decode_eq({ _TYPE = 'string', _VAL = { '\n«\n' } }, '"\\u0000\\u00AB\\u0000"')
end)
it('parses dictionaries with duplicate keys to special maps', function()
sp_decode_eq({_TYPE='map', _VAL={{'a', 1}, {'a', 2}}},
'{"a": 1, "a": 2}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'a', 2}}},
'{"b": 3, "a": 1, "a": 2}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}}},
'{"b": 3, "a": 1, "c": 4, "a": 2}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}},
'{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}')
sp_decode_eq({{_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}},
'[{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}]')
sp_decode_eq({{d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}},
'[{"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq({1, {d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}},
'[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq({1, {a={}, d={_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'a', 2}, {'c', 4}}}}},
'[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq({_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}},
'{"": 3, "a": 1, "c": 4, "d": 2, "": 4}')
sp_decode_eq({{_TYPE='map', _VAL={{'', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {'', 4}}}},
'[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]')
sp_decode_eq({ _TYPE = 'map', _VAL = { { 'a', 1 }, { 'a', 2 } } }, '{"a": 1, "a": 2}')
sp_decode_eq(
{ _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'a', 2 } } },
'{"b": 3, "a": 1, "a": 2}'
)
sp_decode_eq(
{ _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 } } },
'{"b": 3, "a": 1, "c": 4, "a": 2}'
)
sp_decode_eq(
{ _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } } },
'{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}'
)
sp_decode_eq(
{ { _TYPE = 'map', _VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } } } },
'[{"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}]'
)
sp_decode_eq({
{
d = {
_TYPE = 'map',
_VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } },
},
},
}, '[{"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq({
1,
{
d = {
_TYPE = 'map',
_VAL = { { 'b', 3 }, { 'a', 1 }, { 'c', 4 }, { 'a', 2 }, { 'c', 4 } },
},
},
}, '[1, {"d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq({
1,
{
a = {},
d = {
_TYPE = 'map',
_VAL = {
{ 'b', 3 },
{ 'a', 1 },
{ 'c', 4 },
{ 'a', 2 },
{
'c',
4,
},
},
},
},
}, '[1, {"a": [], "d": {"b": 3, "a": 1, "c": 4, "a": 2, "c": 4}}]')
sp_decode_eq(
{ _TYPE = 'map', _VAL = { { '', 3 }, { 'a', 1 }, { 'c', 4 }, { 'd', 2 }, { '', 4 } } },
'{"": 3, "a": 1, "c": 4, "d": 2, "": 4}'
)
sp_decode_eq(
{ { _TYPE = 'map', _VAL = { { '', 3 }, { 'a', 1 }, { 'c', 4 }, { 'd', 2 }, { '', 4 } } } },
'[{"": 3, "a": 1, "c": 4, "d": 2, "": 4}]'
)
end)
it('parses dictionaries with empty keys', function()
eq({[""] = 4}, funcs.json_decode('{"": 4}'))
eq({b = 3, a = 1, c = 4, d = 2, [""] = 4},
funcs.json_decode('{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}'))
eq({ [''] = 4 }, funcs.json_decode('{"": 4}'))
eq(
{ b = 3, a = 1, c = 4, d = 2, [''] = 4 },
funcs.json_decode('{"b": 3, "a": 1, "c": 4, "d": 2, "": 4}')
)
end)
it('parses dictionaries with keys with NUL bytes to special maps', function()
sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b'}}, 4}}},
'{"a\\u0000\\nb": 4}')
sp_decode_eq({_TYPE='map', _VAL={{{_TYPE='string', _VAL={'a\n', 'b', ''}}, 4}}},
'{"a\\u0000\\nb\\n": 4}')
sp_decode_eq({_TYPE='map', _VAL={{'b', 3}, {'a', 1}, {'c', 4}, {'d', 2}, {{_TYPE='string', _VAL={'\n'}}, 4}}},
'{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}')
sp_decode_eq(
{ _TYPE = 'map', _VAL = { { { _TYPE = 'string', _VAL = { 'a\n', 'b' } }, 4 } } },
'{"a\\u0000\\nb": 4}'
)
sp_decode_eq(
{ _TYPE = 'map', _VAL = { { { _TYPE = 'string', _VAL = { 'a\n', 'b', '' } }, 4 } } },
'{"a\\u0000\\nb\\n": 4}'
)
sp_decode_eq({
_TYPE = 'map',
_VAL = {
{ 'b', 3 },
{ 'a', 1 },
{ 'c', 4 },
{ 'd', 2 },
{
{ _TYPE = 'string', _VAL = { '\n' } },
4,
},
},
}, '{"b": 3, "a": 1, "c": 4, "d": 2, "\\u0000": 4}')
end)
it('parses U+00C3 correctly', function()
@@ -493,32 +606,30 @@ describe('json_decode() function', function()
end)
it('fails to parse empty string', function()
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode("")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode([])'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode([""])'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode(" ")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode("\\t")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode("\\n")'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode(" \\t\\n \\n\\t\\t \\n\\t\\n \\n \\t\\n\\t ")'))
eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode("")'))
eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode([])'))
eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode([""])'))
eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode(" ")'))
eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode("\\t")'))
eq('Vim(call):E474: Attempt to decode a blank string', exc_exec('call json_decode("\\n")'))
eq(
'Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode(" \\t\\n \\n\\t\\t \\n\\t\\n \\n \\t\\n\\t ")')
)
end)
it('accepts all spaces in every position where space may be put', function()
local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t'
local s =
' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t'
local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s)
eq({key={'val', 'val2'}, key2=1}, funcs.json_decode(str))
eq({ key = { 'val', 'val2' }, key2 = 1 }, funcs.json_decode(str))
end)
it('does not overflow when writing error message about decoding ["", ""]',
function()
eq('Vim(call):E474: Attempt to decode a blank string',
pcall_err(command, 'call json_decode(["", ""])'))
it('does not overflow when writing error message about decoding ["", ""]', function()
eq(
'Vim(call):E474: Attempt to decode a blank string',
pcall_err(command, 'call json_decode(["", ""])')
)
end)
end)
@@ -558,30 +669,35 @@ describe('json_encode() function', function()
end)
it('fails to dump NaN and infinite values', function()
eq('Vim(call):E474: Unable to represent NaN value in JSON',
exc_exec('call json_encode(str2float("nan"))'))
eq('Vim(call):E474: Unable to represent infinity in JSON',
exc_exec('call json_encode(str2float("inf"))'))
eq('Vim(call):E474: Unable to represent infinity in JSON',
exc_exec('call json_encode(-str2float("inf"))'))
eq(
'Vim(call):E474: Unable to represent NaN value in JSON',
exc_exec('call json_encode(str2float("nan"))')
)
eq(
'Vim(call):E474: Unable to represent infinity in JSON',
exc_exec('call json_encode(str2float("inf"))')
)
eq(
'Vim(call):E474: Unable to represent infinity in JSON',
exc_exec('call json_encode(-str2float("inf"))')
)
end)
it('dumps lists', function()
eq('[]', funcs.json_encode({}))
eq('[[]]', funcs.json_encode({{}}))
eq('[[], []]', funcs.json_encode({{}, {}}))
eq('[[]]', funcs.json_encode({ {} }))
eq('[[], []]', funcs.json_encode({ {}, {} }))
end)
it('dumps dictionaries', function()
eq('{}', eval('json_encode({})'))
eq('{"d": []}', funcs.json_encode({d={}}))
eq('{"d": [], "e": []}', funcs.json_encode({d={}, e={}}))
eq('{"d": []}', funcs.json_encode({ d = {} }))
eq('{"d": [], "e": []}', funcs.json_encode({ d = {}, e = {} }))
-- Empty keys are allowed per JSON spec (and Vim dicts, and msgpack).
eq('{"": []}', funcs.json_encode({['']={}}))
eq('{"": []}', funcs.json_encode({ [''] = {} }))
end)
it('cannot dump generic mapping with generic mapping keys and values',
function()
it('cannot dump generic mapping with generic mapping keys and values', function()
command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
command('let todumpv1 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
command('let todumpv2 = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
@@ -674,33 +790,43 @@ describe('json_encode() function', function()
end)
it('fails to dump a function reference', function()
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
exc_exec('call json_encode(function("tr"))'))
eq(
'Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
exc_exec('call json_encode(function("tr"))')
)
end)
it('fails to dump a partial', function()
command('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], {}))'))
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")])'))
eq(
'Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference',
exc_exec('call json_encode([function("tr")])')
)
end)
it('fails to dump a recursive list', function()
command('let todump = [[[]]]')
command('call add(todump[0][0], todump)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
eq(
'Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')
)
end)
it('fails to dump a recursive dict', function()
command('let todump = {"d": {"d": {}}}')
command('call extend(todump.d.d, {"d": todump})')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])'))
eq(
'Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])')
)
end)
it('can dump dict with two same dicts inside', function()
@@ -718,40 +844,51 @@ describe('json_encode() function', function()
it('fails to dump a recursive list in a special dict', function()
command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
command('call add(todump._VAL, todump)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
eq(
'Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')
)
end)
it('fails to dump a recursive (val) map in a special dict', function()
command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
command('call add(todump._VAL, ["", todump])')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])'))
eq(
'Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode([todump])')
)
end)
it('fails to dump a recursive (val) map in a special dict, _VAL reference', function()
command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [["", []]]}')
command('call add(todump._VAL[0][1], todump._VAL)')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
eq(
'Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')
)
end)
it('fails to dump a recursive (val) special list in a special dict',
function()
it('fails to dump a recursive (val) special list in a special dict', function()
command('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
command('call add(todump._VAL, ["", todump._VAL])')
eq('Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)'))
eq(
'Vim(call):E724: unable to correctly dump variable with self-referencing container',
exc_exec('call json_encode(todump)')
)
end)
it('fails when called with no arguments', function()
eq('Vim(call):E119: Not enough arguments for function: json_encode',
exc_exec('call json_encode()'))
eq(
'Vim(call):E119: Not enough arguments for function: json_encode',
exc_exec('call json_encode()')
)
end)
it('fails when called with two arguments', function()
eq('Vim(call):E118: Too many arguments for function: json_encode',
exc_exec('call json_encode(["", ""], 1)'))
eq(
'Vim(call):E118: Too many arguments for function: json_encode',
exc_exec('call json_encode(["", ""], 1)')
)
end)
it('ignores improper values in &isprint', function()
@@ -761,15 +898,23 @@ describe('json_encode() function', function()
end)
it('fails when using surrogate character in a UTF-8 string', function()
eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\160\128',
exc_exec('call json_encode("\237\160\128")'))
eq('Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\175\191',
exc_exec('call json_encode("\237\175\191")'))
eq(
'Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\160\128',
exc_exec('call json_encode("\237\160\128")')
)
eq(
'Vim(call):E474: UTF-8 string contains code point which belongs to a surrogate pair: \237\175\191',
exc_exec('call json_encode("\237\175\191")')
)
end)
it('dumps control characters as expected', function()
eq([["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013"]],
eval('json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\n\1\2\3\4\5\6\7\8\9", "\11\12\13\14\15\16\17\18\19"]})'))
eq(
[["\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000B\f\r\u000E\u000F\u0010\u0011\u0012\u0013"]],
eval(
'json_encode({"_TYPE": v:msgpack_types.string, "_VAL": ["\n\1\2\3\4\5\6\7\8\9", "\11\12\13\14\15\16\17\18\19"]})'
)
)
end)
it('can dump NULL string', function()
@@ -789,9 +934,13 @@ describe('json_encode() function', function()
end)
it('fails to parse NULL strings and lists', function()
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode($XXX_UNEXISTENT_VAR_XXX)'))
eq('Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode(v:_null_list)'))
eq(
'Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode($XXX_UNEXISTENT_VAR_XXX)')
)
eq(
'Vim(call):E474: Attempt to decode a blank string',
exc_exec('call json_decode(v:_null_list)')
)
end)
end)

View File

@@ -7,7 +7,7 @@ describe('vimscript', function()
it('parses `<SID>` with turkish locale', function()
if exc_exec('lang ctype tr_TR.UTF-8') ~= 0 then
pending("Locale tr_TR.UTF-8 not supported")
pending('Locale tr_TR.UTF-8 not supported')
return
end
source([[
@@ -22,10 +22,10 @@ describe('vimscript', function()
it('str2float is not affected by locale', function()
if exc_exec('lang ctype sv_SE.UTF-8') ~= 0 then
pending("Locale sv_SE.UTF-8 not supported")
pending('Locale sv_SE.UTF-8 not supported')
return
end
clear{env={LANG="", LC_NUMERIC="sv_SE.UTF-8"}}
clear { env = { LANG = '', LC_NUMERIC = 'sv_SE.UTF-8' } }
eq(2.2, eval('str2float("2.2")'))
end)
end)

View File

@@ -15,21 +15,24 @@ before_each(clear)
describe(':let', function()
it('correctly lists variables with curly-braces', function()
meths.set_var('v', {0})
meths.set_var('v', { 0 })
eq('v [0]', exec_capture('let {"v"}'))
end)
it('correctly lists variables with subscript', function()
meths.set_var('v', {0})
meths.set_var('v', { 0 })
eq('v[0] #0', exec_capture('let v[0]'))
eq('g:["v"][0] #0', exec_capture('let g:["v"][0]'))
eq('{"g:"}["v"][0] #0', exec_capture('let {"g:"}["v"][0]'))
end)
it(":unlet self-referencing node in a List graph #6070", function()
it(':unlet self-referencing node in a List graph #6070', function()
-- :unlet-ing a self-referencing List must not allow GC on indirectly
-- referenced in-scope Lists. Before #6070 this caused use-after-free.
expect_exit(1000, source, [=[
expect_exit(
1000,
source,
[=[
let [l1, l2] = [[], []]
echo 'l1:' . id(l1)
echo 'l2:' . id(l2)
@@ -45,10 +48,11 @@ describe(':let', function()
unlet l4
call garbagecollect(1)
call feedkeys(":\e:echo l1 l3\n:echo 42\n:cq\n", "t")
]=])
]=]
)
end)
it("multibyte env var #8398 #9267", function()
it('multibyte env var #8398 #9267', function()
command("let $NVIM_TEST_LET = 'AìaB'")
eq('AìaB', eval('$NVIM_TEST_LET'))
command("let $NVIM_TEST_LET = 'AaあB'")
@@ -56,12 +60,14 @@ describe(':let', function()
local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]]
command("let $NVIM_TEST_LET = '"..mbyte.."'")
command("let $NVIM_TEST_LET = '" .. mbyte .. "'")
eq(mbyte, eval('$NVIM_TEST_LET'))
end)
it("multibyte env var to child process #8398 #9267", function()
local cmd_get_child_env = ("let g:env_from_child = system(['%s', 'NVIM_TEST_LET'])"):format(testprg('printenv-test'))
it('multibyte env var to child process #8398 #9267', function()
local cmd_get_child_env = ("let g:env_from_child = system(['%s', 'NVIM_TEST_LET'])"):format(
testprg('printenv-test')
)
command("let $NVIM_TEST_LET = 'AìaB'")
command(cmd_get_child_env)
eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
@@ -73,12 +79,12 @@ describe(':let', function()
local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]]
command("let $NVIM_TEST_LET = '"..mbyte.."'")
command("let $NVIM_TEST_LET = '" .. mbyte .. "'")
command(cmd_get_child_env)
eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
end)
it("release of list assigned to l: variable does not trigger assertion #12387, #12430", function()
it('release of list assigned to l: variable does not trigger assertion #12387, #12430', function()
source([[
func! s:f()
let l:x = [1]

View File

@@ -19,22 +19,22 @@ describe('maparg()', function()
before_each(clear)
local foo_bar_map_table = {
lhs='foo',
lhsraw='foo',
script=0,
silent=0,
rhs='bar',
expr=0,
sid=0,
scriptversion=1,
buffer=0,
nowait=0,
mode='n',
mode_bits=0x01,
abbr=0,
noremap=1,
lnum=0,
}
lhs = 'foo',
lhsraw = 'foo',
script = 0,
silent = 0,
rhs = 'bar',
expr = 0,
sid = 0,
scriptversion = 1,
buffer = 0,
nowait = 0,
mode = 'n',
mode_bits = 0x01,
abbr = 0,
noremap = 1,
lnum = 0,
}
it('returns a dictionary', function()
nvim('command', 'nnoremap foo bar')
@@ -64,7 +64,7 @@ describe('maparg()', function()
eq(
funcs.maparg('hello', 'i', false, true)['noremap'],
funcs.maparg('this', 'i', false, true)['noremap']
)
)
end)
it('returns a boolean for buffer', function()
@@ -135,7 +135,7 @@ describe('maparg()', function()
it('works with combining characters', function()
-- Using addacutes to make combining character better visible
local function ac(s)
local acute = '\204\129' -- U+0301 COMBINING ACUTE ACCENT
local acute = '\204\129' -- U+0301 COMBINING ACUTE ACCENT
local ret = s:gsub('`', acute)
return ret
end
@@ -145,8 +145,8 @@ describe('maparg()', function()
nnoremap e` f`
]]))
eq(ac('b`'), funcs.maparg(ac('a')))
eq(ac(''), funcs.maparg(ac('c')))
eq(ac('d'), funcs.maparg(ac('c`')))
eq(ac(''), funcs.maparg(ac('c')))
eq(ac('d'), funcs.maparg(ac('c`')))
eq(ac('f`'), funcs.maparg(ac('e`')))
local function acmap(lhs, rhs)
@@ -170,9 +170,9 @@ describe('maparg()', function()
}
end
eq({}, funcs.maparg(ac('c'), 'n', 0, 1))
eq(acmap('a', 'b`'), funcs.maparg(ac('a'), 'n', 0, 1))
eq(acmap('c`', 'd'), funcs.maparg(ac('c`'), 'n', 0, 1))
eq({}, funcs.maparg(ac('c'), 'n', 0, 1))
eq(acmap('a', 'b`'), funcs.maparg(ac('a'), 'n', 0, 1))
eq(acmap('c`', 'd'), funcs.maparg(ac('c`'), 'n', 0, 1))
eq(acmap('e`', 'f`'), funcs.maparg(ac('e`'), 'n', 0, 1))
end)
end)
@@ -182,30 +182,30 @@ describe('mapset()', function()
it('can restore mapping with backslash in lhs', function()
meths.set_keymap('n', '\\ab', 'a', {})
eq('\nn \\ab a', exec_capture("nmap \\ab"))
eq('\nn \\ab a', exec_capture('nmap \\ab'))
local mapargs = funcs.maparg('\\ab', 'n', false, true)
meths.set_keymap('n', '\\ab', 'b', {})
eq('\nn \\ab b', exec_capture("nmap \\ab"))
eq('\nn \\ab b', exec_capture('nmap \\ab'))
funcs.mapset('n', false, mapargs)
eq('\nn \\ab a', exec_capture("nmap \\ab"))
eq('\nn \\ab a', exec_capture('nmap \\ab'))
end)
it('can restore mapping description from the dict returned by maparg()', function()
meths.set_keymap('n', 'lhs', 'rhs', {desc = 'map description'})
eq('\nn lhs rhs\n map description', exec_capture("nmap lhs"))
meths.set_keymap('n', 'lhs', 'rhs', { desc = 'map description' })
eq('\nn lhs rhs\n map description', exec_capture('nmap lhs'))
local mapargs = funcs.maparg('lhs', 'n', false, true)
meths.set_keymap('n', 'lhs', 'rhs', {desc = 'MAP DESCRIPTION'})
eq('\nn lhs rhs\n MAP DESCRIPTION', exec_capture("nmap lhs"))
meths.set_keymap('n', 'lhs', 'rhs', { desc = 'MAP DESCRIPTION' })
eq('\nn lhs rhs\n MAP DESCRIPTION', exec_capture('nmap lhs'))
funcs.mapset('n', false, mapargs)
eq('\nn lhs rhs\n map description', exec_capture("nmap lhs"))
eq('\nn lhs rhs\n map description', exec_capture('nmap lhs'))
end)
it('can restore "replace_keycodes" from the dict returned by maparg()', function()
meths.set_keymap('i', 'foo', [['<l' .. 't>']], {expr = true, replace_keycodes = true})
meths.set_keymap('i', 'foo', [['<l' .. 't>']], { expr = true, replace_keycodes = true })
feed('Afoo')
expect('<')
local mapargs = funcs.maparg('foo', 'i', false, true)
meths.set_keymap('i', 'foo', [['<l' .. 't>']], {expr = true})
meths.set_keymap('i', 'foo', [['<l' .. 't>']], { expr = true })
feed('foo')
expect('<<lt>')
funcs.mapset('i', false, mapargs)
@@ -230,11 +230,14 @@ describe('mapset()', function()
end)
it('can restore Lua callback from the dict returned by maparg()', function()
eq(0, exec_lua([[
eq(
0,
exec_lua([[
GlobalCount = 0
vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end })
return GlobalCount
]]))
]])
)
feed('asdf')
eq(1, exec_lua([[return GlobalCount]]))
@@ -262,9 +265,13 @@ describe('mapset()', function()
end)
it('does not leak memory if lhs is missing', function()
eq('Vim:E460: Entries missing in mapset() dict argument',
pcall_err(exec_lua, [[vim.fn.mapset('n', false, {rhs = 'foo'})]]))
eq('Vim:E460: Entries missing in mapset() dict argument',
pcall_err(exec_lua, [[vim.fn.mapset('n', false, {callback = function() end})]]))
eq(
'Vim:E460: Entries missing in mapset() dict argument',
pcall_err(exec_lua, [[vim.fn.mapset('n', false, {rhs = 'foo'})]])
)
eq(
'Vim:E460: Entries missing in mapset() dict argument',
pcall_err(exec_lua, [[vim.fn.mapset('n', false, {callback = function() end})]])
)
end)
end)

View File

@@ -10,146 +10,191 @@ local exc_exec = helpers.exc_exec
before_each(clear)
describe('setmatches()', function()
it('correctly handles case when both group and pattern entries are numbers',
function()
it('correctly handles case when both group and pattern entries are numbers', function()
command('hi def link 1 PreProc')
eq(0, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}}))
eq({{
group='1',
pattern='2',
id=3,
priority=4,
}}, funcs.getmatches())
eq(0, funcs.setmatches({{group=1, pattern=2, id=3, priority=4, conceal=5}}))
eq({{
group='1',
pattern='2',
id=3,
priority=4,
conceal='5',
}}, funcs.getmatches())
eq(0, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}}))
eq({{
group='1',
pos1={2},
pos2={6},
id=3,
priority=4,
conceal='5',
}}, funcs.getmatches())
eq(0, funcs.setmatches({ { group = 1, pattern = 2, id = 3, priority = 4 } }))
eq({
{
group = '1',
pattern = '2',
id = 3,
priority = 4,
},
}, funcs.getmatches())
eq(0, funcs.setmatches({ { group = 1, pattern = 2, id = 3, priority = 4, conceal = 5 } }))
eq({
{
group = '1',
pattern = '2',
id = 3,
priority = 4,
conceal = '5',
},
}, funcs.getmatches())
eq(
0,
funcs.setmatches({
{ group = 1, pos1 = { 2 }, pos2 = { 6 }, id = 3, priority = 4, conceal = 5 },
})
)
eq({
{
group = '1',
pos1 = { 2 },
pos2 = { 6 },
id = 3,
priority = 4,
conceal = '5',
},
}, funcs.getmatches())
end)
it('does not fail if highlight group is not defined', function()
eq(0, funcs.setmatches{{group=1, pattern=2, id=3, priority=4}})
eq({{group='1', pattern='2', id=3, priority=4}},
funcs.getmatches())
eq(0, funcs.setmatches{{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})
eq({{group='1', pos1={2}, pos2={6}, id=3, priority=4, conceal='5'}},
funcs.getmatches())
eq(0, funcs.setmatches { { group = 1, pattern = 2, id = 3, priority = 4 } })
eq({ { group = '1', pattern = '2', id = 3, priority = 4 } }, funcs.getmatches())
eq(
0,
funcs.setmatches {
{ group = 1, pos1 = { 2 }, pos2 = { 6 }, id = 3, priority = 4, conceal = 5 },
}
)
eq(
{ { group = '1', pos1 = { 2 }, pos2 = { 6 }, id = 3, priority = 4, conceal = '5' } },
funcs.getmatches()
)
end)
end)
describe('matchadd()', function()
it('correctly works when first two arguments and conceal are numbers at once',
function()
it('correctly works when first two arguments and conceal are numbers at once', function()
command('hi def link 1 PreProc')
eq(4, funcs.matchadd(1, 2, 3, 4, {conceal=5}))
eq({{
group='1',
pattern='2',
priority=3,
id=4,
conceal='5',
}}, funcs.getmatches())
eq(4, funcs.matchadd(1, 2, 3, 4, { conceal = 5 }))
eq({
{
group = '1',
pattern = '2',
priority = 3,
id = 4,
conceal = '5',
},
}, funcs.getmatches())
end)
end)
describe('matchaddpos()', function()
it('errors out on invalid input', function()
command('hi clear PreProc')
eq('Vim(let):E5030: Empty list at position 0',
exc_exec('let val = matchaddpos("PreProc", [[]])'))
eq('Vim(let):E5030: Empty list at position 1',
exc_exec('let val = matchaddpos("PreProc", [1, v:_null_list])'))
eq('Vim(let):E5031: List or number required at position 1',
exc_exec('let val = matchaddpos("PreProc", [1, v:_null_dict])'))
eq(
'Vim(let):E5030: Empty list at position 0',
exc_exec('let val = matchaddpos("PreProc", [[]])')
)
eq(
'Vim(let):E5030: Empty list at position 1',
exc_exec('let val = matchaddpos("PreProc", [1, v:_null_list])')
)
eq(
'Vim(let):E5031: List or number required at position 1',
exc_exec('let val = matchaddpos("PreProc", [1, v:_null_dict])')
)
end)
it('works with 0 lnum', function()
command('hi clear PreProc')
eq(4, funcs.matchaddpos('PreProc', {1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
eq(4, funcs.matchaddpos('PreProc', { 1 }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{0}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
eq(4, funcs.matchaddpos('PreProc', { { 0 }, 1 }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {0, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
eq(4, funcs.matchaddpos('PreProc', { 0, 1 }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
end)
it('works with negative numbers', function()
command('hi clear PreProc')
eq(4, funcs.matchaddpos('PreProc', {-10, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
eq(4, funcs.matchaddpos('PreProc', { -10, 1 }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{-10}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
eq(4, funcs.matchaddpos('PreProc', { { -10 }, 1 }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{2, -1}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
eq(4, funcs.matchaddpos('PreProc', { { 2, -1 }, 1 }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
funcs.matchdelete(4)
eq(4, funcs.matchaddpos('PreProc', {{2, 0, -1}, 1}, 3, 4))
eq({{
group='PreProc',
pos1 = {1},
priority=3,
id=4,
}}, funcs.getmatches())
eq(4, funcs.matchaddpos('PreProc', { { 2, 0, -1 }, 1 }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
end)
it('works with zero length', function()
local screen = Screen.new(40, 5)
screen:attach()
funcs.setline(1, 'abcdef')
command('hi PreProc guifg=Red')
eq(4, funcs.matchaddpos('PreProc', {{1, 2, 0}}, 3, 4))
eq({{
group='PreProc',
pos1 = {1, 2, 0},
priority=3,
id=4,
}}, funcs.getmatches())
screen:expect([[
eq(4, funcs.matchaddpos('PreProc', { { 1, 2, 0 } }, 3, 4))
eq({
{
group = 'PreProc',
pos1 = { 1, 2, 0 },
priority = 3,
id = 4,
},
}, funcs.getmatches())
screen:expect(
[[
^a{1:b}cdef |
{2:~ }|*3
|
]], {[1] = {foreground = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.Blue1}})
]],
{
[1] = { foreground = Screen.colors.Red },
[2] = { bold = true, foreground = Screen.colors.Blue1 },
}
)
end)
end)

View File

@@ -8,14 +8,17 @@ local funcs = helpers.funcs
local pcall_err = helpers.pcall_err
before_each(clear)
for _, func in ipairs({'min', 'max'}) do
for _, func in ipairs({ 'min', 'max' }) do
describe(func .. '()', function()
it('gives a single error message when multiple values failed conversions',
function()
eq('Vim(echo):E745: Using a List as a Number',
pcall_err(command, 'echo ' .. func .. '([-5, [], [], [], 5])'))
eq('Vim(echo):E745: Using a List as a Number',
pcall_err(command, 'echo ' .. func .. '({1:-5, 2:[], 3:[], 4:[], 5:5})'))
it('gives a single error message when multiple values failed conversions', function()
eq(
'Vim(echo):E745: Using a List as a Number',
pcall_err(command, 'echo ' .. func .. '([-5, [], [], [], 5])')
)
eq(
'Vim(echo):E745: Using a List as a Number',
pcall_err(command, 'echo ' .. func .. '({1:-5, 2:[], 3:[], 4:[], 5:5})')
)
for errmsg, errinput in pairs({
['Vim(echo):E745: Using a List as a Number'] = '[]',
['Vim(echo):E805: Using a Float as a Number'] = '0.0',
@@ -31,18 +34,26 @@ for _, func in ipairs({'min', 'max'}) do
eq(0, eval(func .. '({})'))
end)
it('works with arrays/dictionaries with one item', function()
eq(5, funcs[func]({5}))
eq(5, funcs[func]({test=5}))
eq(5, funcs[func]({ 5 }))
eq(5, funcs[func]({ test = 5 }))
end)
it('works with NULL arrays/dictionaries', function()
eq(0, eval(func .. '(v:_null_list)'))
eq(0, eval(func .. '(v:_null_dict)'))
end)
it('errors out for invalid types', function()
for _, errinput in ipairs({'1', 'v:true', 'v:false', 'v:null',
'function("tr")', '""'}) do
eq(('Vim(echo):E712: Argument of %s() must be a List or Dictionary'):format(func),
pcall_err(command, 'echo ' .. func .. '(' .. errinput .. ')'))
for _, errinput in ipairs({
'1',
'v:true',
'v:false',
'v:null',
'function("tr")',
'""',
}) do
eq(
('Vim(echo):E712: Argument of %s() must be a List or Dictionary'):format(func),
pcall_err(command, 'echo ' .. func .. '(' .. errinput .. ')')
)
end
end)
end)

View File

@@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
local assert_alive = helpers.assert_alive
local clear, command, write_file = helpers.clear, helpers.command, helpers.write_file
describe("modeline", function()
describe('modeline', function()
local tempfile = helpers.tmpname()
before_each(clear)

View File

@@ -40,8 +40,7 @@ describe('NULL', function()
end
local null_expr_test = function(name, expr, err, val, after)
it(name, function()
eq((err == 0) and ('') or ('\n' .. err),
redir_exec('let g:_var = ' .. expr))
eq((err == 0) and '' or ('\n' .. err), redir_exec('let g:_var = ' .. expr))
if val == nil then
eq(0, funcs.exists('g:_var'))
else
@@ -58,25 +57,31 @@ describe('NULL', function()
null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 1)
-- Correct behaviour
null_test('can be :unlet item with error message for empty list', ':unlet L[0]',
'Vim(unlet):E684: List index out of range: 0')
null_expr_test('can be indexed with error message for empty list', 'L[0]',
'E684: List index out of range: 0', nil)
null_test(
'can be :unlet item with error message for empty list',
':unlet L[0]',
'Vim(unlet):E684: List index out of range: 0'
)
null_expr_test(
'can be indexed with error message for empty list',
'L[0]',
'E684: List index out of range: 0',
nil
)
null_expr_test('can be splice-indexed', 'L[:]', 0, {})
null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0)
null_test('is accepted by :for', 'for x in L|throw x|endfor', 0)
null_expr_test('does not crash append()', 'append(0, L)', 0, 0, function()
eq({''}, curbufmeths.get_lines(0, -1, false))
eq({ '' }, curbufmeths.get_lines(0, -1, false))
end)
null_expr_test('does not crash setline()', 'setline(1, L)', 0, 0, function()
eq({''}, curbufmeths.get_lines(0, -1, false))
eq({ '' }, curbufmeths.get_lines(0, -1, false))
end)
null_expr_test('is identical to itself', 'L is L', 0, 1)
null_expr_test('can be sliced', 'L[:]', 0, {})
null_expr_test('can be copied', 'copy(L)', 0, {})
null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {})
null_expr_test('does not crash when indexed', 'L[1]',
'E684: List index out of range: 1', nil)
null_expr_test('does not crash when indexed', 'L[1]', 'E684: List index out of range: 1', nil)
null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0)
null_expr_test('does not crash col()', 'col(L)', 0, 0)
null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0)
@@ -96,44 +101,92 @@ describe('NULL', function()
null_test('does not crash lockvar', 'lockvar! L', 0)
null_expr_test('can be added to itself', '(L + L)', 0, {})
null_expr_test('can be added to itself', '(L + L) is L', 0, 1)
null_expr_test('can be added to non-empty list', '([1] + L)', 0, {1})
null_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1})
null_expr_test('can be added to non-empty list', '([1] + L)', 0, { 1 })
null_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, { 1 })
null_expr_test('is equal to itself', 'L == L', 0, 1)
null_expr_test('is not not equal to itself', 'L != L', 0, 0)
null_expr_test('counts correctly', 'count([L], L)', 0, 1)
null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 1)
null_expr_test('makes filter() return v:_null_list', 'filter(L, "1") is# L', 0, 1)
null_test('is treated by :let as empty list', ':let [l] = L', 'Vim(let):E688: More targets than List items')
null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]',
'Type number and <Enter> or click with the mouse (q or empty cancels): ', {0, 0})
null_expr_test('is accepted as an empty list by writefile()',
('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname),
0, {0, {}})
null_expr_test('makes add() error out', 'add(L, 0)',
'E742: Cannot change value of add() argument', 1)
null_expr_test('makes insert() error out', 'insert(L, 1)',
'E742: Cannot change value of insert() argument', 0)
null_expr_test('does not crash remove()', 'remove(L, 0)',
'E742: Cannot change value of remove() argument', 0)
null_expr_test('makes reverse() error out', 'reverse(L)',
'E742: Cannot change value of reverse() argument', 0)
null_expr_test('makes sort() error out', 'sort(L)',
'E742: Cannot change value of sort() argument', 0)
null_expr_test('makes uniq() error out', 'uniq(L)',
'E742: Cannot change value of uniq() argument', 0)
null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0)
null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1})
null_test(
'is treated by :let as empty list',
':let [l] = L',
'Vim(let):E688: More targets than List items'
)
null_expr_test(
'is accepted as an empty list by inputlist()',
'[feedkeys("\\n"), inputlist(L)]',
'Type number and <Enter> or click with the mouse (q or empty cancels): ',
{ 0, 0 }
)
null_expr_test(
'is accepted as an empty list by writefile()',
('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname),
0,
{ 0, {} }
)
null_expr_test(
'makes add() error out',
'add(L, 0)',
'E742: Cannot change value of add() argument',
1
)
null_expr_test(
'makes insert() error out',
'insert(L, 1)',
'E742: Cannot change value of insert() argument',
0
)
null_expr_test(
'does not crash remove()',
'remove(L, 0)',
'E742: Cannot change value of remove() argument',
0
)
null_expr_test(
'makes reverse() error out',
'reverse(L)',
'E742: Cannot change value of reverse() argument',
0
)
null_expr_test(
'makes sort() error out',
'sort(L)',
'E742: Cannot change value of sort() argument',
0
)
null_expr_test(
'makes uniq() error out',
'uniq(L)',
'E742: Cannot change value of uniq() argument',
0
)
null_expr_test(
'does not crash extend()',
'extend(L, [1])',
'E742: Cannot change value of extend() argument',
0
)
null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, { 1 })
null_expr_test('makes join() return empty string', 'join(L, "")', 0, '')
null_expr_test('makes msgpackdump() return empty list', 'msgpackdump(L)', 0, {})
null_expr_test('does not crash system()', 'system("cat", L)', 0, '')
null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0)
null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {})
null_test('does not make Neovim crash when v:oldfiles gets assigned to that', ':let v:oldfiles = L|oldfiles', 0)
null_expr_test('does not make complete() crash or error out',
'execute(":normal i\\<C-r>=complete(1, L)[-1]\\n")',
0, '', function()
eq({''}, curbufmeths.get_lines(0, -1, false))
end)
null_test(
'does not make Neovim crash when v:oldfiles gets assigned to that',
':let v:oldfiles = L|oldfiles',
0
)
null_expr_test(
'does not make complete() crash or error out',
'execute(":normal i\\<C-r>=complete(1, L)[-1]\\n")',
0,
'',
function()
eq({ '' }, curbufmeths.get_lines(0, -1, false))
end
)
null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, 0)
null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, 0)
null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, 0)
@@ -143,11 +196,15 @@ describe('NULL', function()
end)
describe('dict', function()
it('does not crash when indexing NULL dict', function()
eq('\nE716: Key not present in Dictionary: "test"',
redir_exec('echo v:_null_dict.test'))
eq('\nE716: Key not present in Dictionary: "test"', redir_exec('echo v:_null_dict.test'))
end)
null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0)
null_expr_test('makes extend do nothing', 'extend({1: 2}, D)', 0, {['1']=2})
null_expr_test(
'makes extend error out',
'extend(D, {})',
'E742: Cannot change value of extend() argument',
0
)
null_expr_test('makes extend do nothing', 'extend({1: 2}, D)', 0, { ['1'] = 2 })
null_expr_test('does not crash map()', 'map(D, "v:val")', 0, {})
null_expr_test('does not crash filter()', 'filter(D, "1")', 0, {})
null_expr_test('makes map() return v:_null_dict', 'map(D, "v:val") is# D', 0, 1)
@@ -158,7 +215,12 @@ describe('NULL', function()
null_test('does not crash :execute', 'execute S', 0)
null_expr_test('does not crash execute()', 'execute(S)', 0, '')
null_expr_test('does not crash executable()', 'executable(S)', 0, 0)
null_expr_test('makes timer_start() error out', 'timer_start(0, S)', 'E921: Invalid callback argument', -1)
null_expr_test(
'makes timer_start() error out',
'timer_start(0, S)',
'E921: Invalid callback argument',
-1
)
null_expr_test('does not crash filereadable()', 'filereadable(S)', 0, 0)
null_expr_test('does not crash filewritable()', 'filewritable(S)', 0, 0)
null_expr_test('does not crash fnamemodify()', 'fnamemodify(S, S)', 0, '')
@@ -169,7 +231,7 @@ describe('NULL', function()
null_expr_test('does not crash glob()', 'glob(S)', 0, '')
null_expr_test('does not crash globpath()', 'globpath(S, S)', 0, '')
null_expr_test('does not crash mkdir()', 'mkdir(S)', 0, 0)
null_expr_test('does not crash sort()', 'sort(["b", S, "a"])', 0, {'', 'a', 'b'})
null_expr_test('does not crash sort()', 'sort(["b", S, "a"])', 0, { '', 'a', 'b' })
null_expr_test('does not crash split()', 'split(S)', 0, {})
null_test('can be used to set an option', 'let &grepprg = S', 0)

View File

@@ -7,22 +7,22 @@ describe('Division operator', function()
before_each(clear)
it('returns infinity on {positive}/0.0', function()
eq('str2float(\'inf\')', eval('string(1.0/0.0)'))
eq('str2float(\'inf\')', eval('string(1.0e-100/0.0)'))
eq('str2float(\'inf\')', eval('string(1.0e+100/0.0)'))
eq('str2float(\'inf\')', eval('string((1.0/0.0)/0.0)'))
eq("str2float('inf')", eval('string(1.0/0.0)'))
eq("str2float('inf')", eval('string(1.0e-100/0.0)'))
eq("str2float('inf')", eval('string(1.0e+100/0.0)'))
eq("str2float('inf')", eval('string((1.0/0.0)/0.0)'))
end)
it('returns -infinity on {negative}/0.0', function()
eq('-str2float(\'inf\')', eval('string((-1.0)/0.0)'))
eq('-str2float(\'inf\')', eval('string((-1.0e-100)/0.0)'))
eq('-str2float(\'inf\')', eval('string((-1.0e+100)/0.0)'))
eq('-str2float(\'inf\')', eval('string((-1.0/0.0)/0.0)'))
eq("-str2float('inf')", eval('string((-1.0)/0.0)'))
eq("-str2float('inf')", eval('string((-1.0e-100)/0.0)'))
eq("-str2float('inf')", eval('string((-1.0e+100)/0.0)'))
eq("-str2float('inf')", eval('string((-1.0/0.0)/0.0)'))
end)
it('returns NaN on 0.0/0.0', function()
eq('str2float(\'nan\')', eval('string(0.0/0.0)'))
eq('str2float(\'nan\')', eval('string(-(0.0/0.0))'))
eq('str2float(\'nan\')', eval('string((-0.0)/0.0)'))
eq("str2float('nan')", eval('string(0.0/0.0)'))
eq("str2float('nan')", eval('string(-(0.0/0.0))'))
eq("str2float('nan')", eval('string((-0.0)/0.0)'))
end)
end)

View File

@@ -52,7 +52,7 @@ describe('printf()', function()
-- zero-fill modifier is ignored when used with left-align
-- force-sign and add-blank are ignored
-- use-grouping-characters modifier is ignored always
eq('0b00011 ', funcs.printf('% \'+#0-10.5b', 3))
eq('0b00011 ', funcs.printf("% '+#0-10.5b", 3))
end)
it('errors out when %b modifier is used for a list', function()
eq('Vim(call):E745: Using a List as a Number', exc_exec('call printf("%b", [])'))

View File

@@ -1,6 +1,6 @@
local helpers = require('test.functional.helpers')(after_each)
local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
local neq, command, funcs = helpers.neq, helpers.command, helpers.funcs
local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok
local neq, command, funcs = helpers.neq, helpers.command, helpers.funcs
local reltime, reltimestr, reltimefloat = funcs.reltime, funcs.reltimestr, funcs.reltimefloat
describe('reltimestr(), reltimefloat()', function()
@@ -15,7 +15,7 @@ describe('reltimestr(), reltimefloat()', function()
neq('0.0', reltimestr(elapsed))
ok(reltimefloat(elapsed) > 0.0)
-- original vim test for < 0.1, but easily fails on travis
ok(nil ~= string.match(reltimestr(elapsed), "0%."))
ok(nil ~= string.match(reltimestr(elapsed), '0%.'))
ok(reltimefloat(elapsed) < 1.0)
local same = reltime(now, now)
@@ -29,7 +29,7 @@ describe('reltimestr(), reltimefloat()', function()
neq('0.0', reltimestr(differs))
ok(reltimefloat(differs) > 0.0)
-- original vim test for < 0.1, but easily fails on travis
ok(nil ~= string.match(reltimestr(differs), "0%."))
ok(nil ~= string.match(reltimestr(differs), '0%.'))
ok(reltimefloat(differs) < 1.0)
end)

View File

@@ -8,66 +8,72 @@ before_each(clear)
describe('screenpos() function', function()
it('works in floating window with border', function()
local opts = {
relative='editor',
height=8,
width=12,
row=6,
col=8,
anchor='NW',
style='minimal',
border='none',
focusable=1
relative = 'editor',
height = 8,
width = 12,
row = 6,
col = 8,
anchor = 'NW',
style = 'minimal',
border = 'none',
focusable = 1,
}
local float = meths.open_win(meths.create_buf(false, true), false, opts)
command('redraw')
eq({row = 7, col = 9, endcol = 9, curscol = 9}, funcs.screenpos(float, 1, 1))
eq({ row = 7, col = 9, endcol = 9, curscol = 9 }, funcs.screenpos(float, 1, 1))
-- only left border
opts.border = {'', '', '', '', '', '', '', '|'}
opts.border = { '', '', '', '', '', '', '', '|' }
meths.win_set_config(float, opts)
command('redraw')
eq({row = 7, col = 10, endcol = 10, curscol = 10}, funcs.screenpos(float, 1, 1))
eq({ row = 7, col = 10, endcol = 10, curscol = 10 }, funcs.screenpos(float, 1, 1))
-- only top border
opts.border = {'', '_', '', '', '', '', '', ''}
opts.border = { '', '_', '', '', '', '', '', '' }
meths.win_set_config(float, opts)
command('redraw')
eq({row = 8, col = 9, endcol = 9, curscol = 9}, funcs.screenpos(float, 1, 1))
eq({ row = 8, col = 9, endcol = 9, curscol = 9 }, funcs.screenpos(float, 1, 1))
-- both left and top border
opts.border = 'single'
meths.win_set_config(float, opts)
command('redraw')
eq({row = 8, col = 10, endcol = 10, curscol = 10}, funcs.screenpos(float, 1, 1))
eq({ row = 8, col = 10, endcol = 10, curscol = 10 }, funcs.screenpos(float, 1, 1))
end)
it('works for folded line with virt_lines attached to line above', function()
meths.buf_set_lines(0, 0, -1, true, {'aaa', 'bbb', 'ccc', 'ddd'})
meths.buf_set_lines(0, 0, -1, true, { 'aaa', 'bbb', 'ccc', 'ddd' })
local ns = meths.create_namespace('')
meths.buf_set_extmark(0, ns, 0, 0, { virt_lines = {{{'abb'}}, {{'acc'}}, {{'add'}}} })
meths.buf_set_extmark(
0,
ns,
0,
0,
{ virt_lines = { { { 'abb' } }, { { 'acc' } }, { { 'add' } } } }
)
command('2,3fold')
eq({row = 5, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1))
eq({row = 5, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1))
eq({row = 6, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1))
eq({ row = 5, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 2, 1))
eq({ row = 5, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 3, 1))
eq({ row = 6, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 4, 1))
feed('<C-E>')
eq({row = 4, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1))
eq({row = 4, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1))
eq({row = 5, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1))
eq({ row = 4, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 2, 1))
eq({ row = 4, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 3, 1))
eq({ row = 5, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 4, 1))
feed('<C-E>')
eq({row = 3, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1))
eq({row = 3, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1))
eq({row = 4, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1))
eq({ row = 3, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 2, 1))
eq({ row = 3, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 3, 1))
eq({ row = 4, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 4, 1))
feed('<C-E>')
eq({row = 2, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1))
eq({row = 2, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1))
eq({row = 3, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1))
eq({ row = 2, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 2, 1))
eq({ row = 2, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 3, 1))
eq({ row = 3, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 4, 1))
feed('<C-E>')
eq({row = 1, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 2, 1))
eq({row = 1, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 3, 1))
eq({row = 2, col = 1, endcol = 1, curscol = 1}, funcs.screenpos(0, 4, 1))
eq({ row = 1, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 2, 1))
eq({ row = 1, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 3, 1))
eq({ row = 2, col = 1, endcol = 1, curscol = 1 }, funcs.screenpos(0, 4, 1))
end)
end)

View File

@@ -24,43 +24,41 @@ describe('server', function()
it('serverstart() stores sockets in $XDG_RUNTIME_DIR', function()
local dir = 'Xtest_xdg_run'
mkdir(dir)
clear({ env={ XDG_RUNTIME_DIR=dir } })
clear({ env = { XDG_RUNTIME_DIR = dir } })
matches(dir, funcs.stdpath('run'))
if not is_os('win') then
matches(dir, funcs.serverstart())
end
end)
it('serverstart(), serverstop() does not set $NVIM', function()
clear()
local s = eval('serverstart()')
assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
assert(s ~= nil and s:len() > 0, 'serverstart() returned empty')
eq('', eval('$NVIM'))
eq('', eval('$NVIM_LISTEN_ADDRESS'))
eq(1, eval("serverstop('"..s.."')"))
eq(1, eval("serverstop('" .. s .. "')"))
eq('', eval('$NVIM_LISTEN_ADDRESS'))
end)
it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function()
clear({env={NVIM_LISTEN_ADDRESS='.'}})
clear({ env = { NVIM_LISTEN_ADDRESS = '.' } })
-- Cleared on startup.
eq('', eval('$NVIM_LISTEN_ADDRESS'))
local servers = funcs.serverlist()
eq(1, #servers)
ok(string.len(servers[1]) > 4) -- "~/.local/state/nvim…/…" or "\\.\pipe\…"
ok(string.len(servers[1]) > 4) -- "~/.local/state/nvim…/…" or "\\.\pipe\…"
end)
it('sets v:servername at startup or if all servers were stopped', function()
clear()
local initial_server = meths.get_vvar('servername')
assert(initial_server ~= nil and initial_server:len() > 0,
'v:servername was not initialized')
assert(initial_server ~= nil and initial_server:len() > 0, 'v:servername was not initialized')
-- v:servername is readonly so we cannot unset it--but we can test that it
-- does not get set again thereafter.
local s = funcs.serverstart()
assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
assert(s ~= nil and s:len() > 0, 'serverstart() returned empty')
neq(initial_server, s)
-- serverstop() does _not_ modify v:servername...
@@ -72,8 +70,10 @@ describe('server', function()
eq('', meths.get_vvar('servername'))
-- v:servername and $NVIM take the next available server.
local servername = (is_os('win') and [[\\.\pipe\Xtest-functional-server-pipe]]
or './Xtest-functional-server-socket')
local servername = (
is_os('win') and [[\\.\pipe\Xtest-functional-server-pipe]]
or './Xtest-functional-server-socket'
)
funcs.serverstart(servername)
eq(servername, meths.get_vvar('servername'))
-- Not set in the current process, only in children.
@@ -81,31 +81,31 @@ describe('server', function()
end)
it('serverstop() returns false for invalid input', function()
clear{env={
NVIM_LOG_FILE=testlog,
NVIM_LISTEN_ADDRESS='.',
}}
clear { env = {
NVIM_LOG_FILE = testlog,
NVIM_LISTEN_ADDRESS = '.',
} }
eq(0, eval("serverstop('')"))
eq(0, eval("serverstop('bogus-socket-name')"))
assert_log('Not listening on bogus%-socket%-name', testlog, 10)
end)
it('parses endpoints', function()
clear{env={
NVIM_LOG_FILE=testlog,
NVIM_LISTEN_ADDRESS='.',
}}
clear { env = {
NVIM_LOG_FILE = testlog,
NVIM_LISTEN_ADDRESS = '.',
} }
clear_serverlist()
eq({}, funcs.serverlist())
local s = funcs.serverstart('127.0.0.1:0') -- assign random port
local s = funcs.serverstart('127.0.0.1:0') -- assign random port
if #s > 0 then
assert(string.match(s, '127.0.0.1:%d+'))
eq(s, funcs.serverlist()[1])
clear_serverlist()
end
s = funcs.serverstart('127.0.0.1:') -- assign random port
s = funcs.serverstart('127.0.0.1:') -- assign random port
if #s > 0 then
assert(string.match(s, '127.0.0.1:%d+'))
eq(s, funcs.serverlist()[1])
@@ -117,7 +117,7 @@ describe('server', function()
local status, _ = pcall(funcs.serverstart, v4)
if status then
table.insert(expected, v4)
pcall(funcs.serverstart, v4) -- exists already; ignore
pcall(funcs.serverstart, v4) -- exists already; ignore
assert_log('Failed to start server: address already in use: 127%.0%.0%.1', testlog, 10)
end
@@ -125,7 +125,7 @@ describe('server', function()
status, _ = pcall(funcs.serverstart, v6)
if status then
table.insert(expected, v6)
pcall(funcs.serverstart, v6) -- exists already; ignore
pcall(funcs.serverstart, v6) -- exists already; ignore
assert_log('Failed to start server: address already in use: ::1', testlog, 10)
end
eq(expected, funcs.serverlist())
@@ -135,8 +135,10 @@ describe('server', function()
matches([[.*[/\\]xtest1%.2%.3%.4[^/\\]*]], funcs.serverstart('xtest1.2.3.4'))
clear_serverlist()
eq('Vim:Failed to start server: invalid argument',
pcall_err(funcs.serverstart, '127.0.0.1:65536')) -- invalid port
eq(
'Vim:Failed to start server: invalid argument',
pcall_err(funcs.serverstart, '127.0.0.1:65536')
) -- invalid port
eq({}, funcs.serverlist())
end)
@@ -146,11 +148,12 @@ describe('server', function()
local n = eval('len(serverlist())')
-- Add some servers.
local servs = (is_os('win')
and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] }
or { [[./Xtest-pipe0934]], [[./Xtest-pipe4324]] })
local servs = (
is_os('win') 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()')
@@ -160,7 +163,7 @@ describe('server', function()
-- The new servers should be at the end of the list.
for i = 1, #servs do
eq(servs[i], new_servs[i + n])
eq(1, eval("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())'))
@@ -180,14 +183,12 @@ describe('startup --listen', function()
end)
it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
local addr = (is_os('win') and [[\\.\pipe\Xtest-listen-pipe]]
or './Xtest-listen-pipe')
clear({ env={ NVIM_LISTEN_ADDRESS='./Xtest-env-pipe' },
args={ '--listen', addr } })
local addr = (is_os('win') 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'))
-- Address without slashes is a "name" which is appended to a generated path. #8519
clear({ args={ '--listen', 'test-name' } })
clear({ args = { '--listen', 'test-name' } })
matches([[.*[/\\]test%-name[^/\\]*]], meths.get_vvar('servername'))
end)
end)

View File

@@ -8,7 +8,6 @@ local eval = helpers.eval
local eq = helpers.eq
local exc_exec = helpers.exc_exec
describe('setpos() function', function()
before_each(function()
clear()
@@ -23,42 +22,42 @@ describe('setpos() function', function()
Line of text 3]])
end)
it('can set the current cursor position', function()
setpos(".", {0, 2, 1, 0})
eq({0, 2, 1, 0}, getpos("."))
setpos(".", {2, 1, 1, 0})
eq({0, 1, 1, 0}, getpos("."))
setpos('.', { 0, 2, 1, 0 })
eq({ 0, 2, 1, 0 }, getpos('.'))
setpos('.', { 2, 1, 1, 0 })
eq({ 0, 1, 1, 0 }, getpos('.'))
local ret = exc_exec('call setpos(".", [1, 1, 1, 0])')
eq(0, ret)
end)
it('can set lowercase marks in the current buffer', function()
setpos("'d", {0, 2, 1, 0})
eq({0, 2, 1, 0}, getpos("'d"))
setpos("'d", { 0, 2, 1, 0 })
eq({ 0, 2, 1, 0 }, getpos("'d"))
command('undo')
command('call setpos("\'d", [2, 3, 1, 0])')
eq({0, 3, 1, 0}, getpos("'d"))
eq({ 0, 3, 1, 0 }, getpos("'d"))
end)
it('can set lowercase marks in other buffers', function()
local retval = setpos("'d", {1, 2, 1, 0})
local retval = setpos("'d", { 1, 2, 1, 0 })
eq(0, retval)
setpos("'d", {1, 2, 1, 0})
eq({0, 0, 0, 0}, getpos("'d"))
setpos("'d", { 1, 2, 1, 0 })
eq({ 0, 0, 0, 0 }, getpos("'d"))
command('wincmd w')
eq(1, eval('bufnr("%")'))
eq({0, 2, 1, 0}, getpos("'d"))
eq({ 0, 2, 1, 0 }, getpos("'d"))
end)
it("fails when setting a mark in a buffer that doesn't exist", function()
local retval = setpos("'d", {3, 2, 1, 0})
local retval = setpos("'d", { 3, 2, 1, 0 })
eq(-1, retval)
eq({0, 0, 0, 0}, getpos("'d"))
retval = setpos("'D", {3, 2, 1, 0})
eq({ 0, 0, 0, 0 }, getpos("'d"))
retval = setpos("'D", { 3, 2, 1, 0 })
eq(-1, retval)
eq({0, 0, 0, 0}, getpos("'D"))
eq({ 0, 0, 0, 0 }, getpos("'D"))
end)
it('can set uppercase marks', function()
setpos("'D", {2, 2, 3, 0})
eq({2, 2, 3, 0}, getpos("'D"))
setpos("'D", { 2, 2, 3, 0 })
eq({ 2, 2, 3, 0 }, getpos("'D"))
-- Can set a mark in another buffer
setpos("'D", {1, 2, 2, 0})
eq({1, 2, 2, 0}, getpos("'D"))
setpos("'D", { 1, 2, 2, 0 })
eq({ 1, 2, 2, 0 }, getpos("'D"))
end)
end)

View File

@@ -14,31 +14,41 @@ before_each(clear)
describe('sort()', function()
it('errors out when sorting special values', function()
eq('Vim(call):E362: Using a boolean value as a Float',
exc_exec('call sort([v:true, v:false], "f")'))
eq(
'Vim(call):E362: Using a boolean value as a Float',
exc_exec('call sort([v:true, v:false], "f")')
)
end)
it('sorts “wrong” values between -0.0001 and 0.0001, preserving order',
function()
meths.set_var('list', {true, false, NIL, {}, {a=42}, 'check',
0.0001, -0.0001})
it('sorts “wrong” values between -0.0001 and 0.0001, preserving order', function()
meths.set_var('list', {
true,
false,
NIL,
{},
{ a = 42 },
'check',
0.0001,
-0.0001,
})
command('call insert(g:list, function("tr"))')
local error_lines = funcs.split(
funcs.execute('silent! call sort(g:list, "f")'), '\n')
local error_lines = funcs.split(funcs.execute('silent! call sort(g:list, "f")'), '\n')
local errors = {}
for _, err in ipairs(error_lines) do
errors[err] = true
end
eq({
['E362: Using a boolean value as a Float']=true,
['E891: Using a Funcref as a Float']=true,
['E892: Using a String as a Float']=true,
['E893: Using a List as a Float']=true,
['E894: Using a Dictionary as a Float']=true,
['E907: Using a special value as a Float']=true,
['E362: Using a boolean value as a Float'] = true,
['E891: Using a Funcref as a Float'] = true,
['E892: Using a String as a Float'] = true,
['E893: Using a List as a Float'] = true,
['E894: Using a Dictionary as a Float'] = true,
['E907: Using a special value as a Float'] = true,
}, errors)
eq('[-1.0e-4, function(\'tr\'), v:true, v:false, v:null, [], {\'a\': 42}, \'check\', 1.0e-4]',
eval('string(g:list)'))
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()
@@ -50,7 +60,9 @@ describe('sort()', function()
return (a:a > a:b) - (a:a < a:b)
endfunction
]])
eq('Vim(let):E745: Using a List as a Number',
pcall_err(command, 'let sl = sort([1, 0, [], 3, 2], "Cmp")'))
eq(
'Vim(let):E745: Using a List as a Number',
pcall_err(command, 'let sl = sort([1, 0, [], 3, 2], "Cmp")')
)
end)
end)

View File

@@ -98,8 +98,8 @@ describe('Special values', function()
eq(0, eval('0 + v:false'))
eq(-1, eval('0 - v:true'))
eq( 0, eval('0 - v:null'))
eq( 0, eval('0 - v:false'))
eq(0, eval('0 - v:null'))
eq(0, eval('0 - v:false'))
eq(1, eval('1 * v:true'))
eq(0, eval('1 * v:null'))
@@ -125,9 +125,9 @@ describe('Special values', function()
end)
it('work with . (concat) properly', function()
eq("v:true", eval('"" . v:true'))
eq("v:null", eval('"" . v:null'))
eq("v:false", eval('"" . v:false'))
eq('v:true', eval('"" . v:true'))
eq('v:null', eval('"" . v:null'))
eq('v:false', eval('"" . v:false'))
end)
it('work with ?? (falsy operator)', function()

View File

@@ -12,7 +12,7 @@ before_each(clear)
describe('state() function', function()
-- oldtest: Test_state()
it('works', function()
meths.ui_attach(80, 24, {}) -- Allow hit-enter-prompt
meths.ui_attach(80, 24, {}) -- Allow hit-enter-prompt
exec_lua([[
function _G.Get_state_mode()
@@ -42,34 +42,34 @@ describe('state() function', function()
-- Using a timer callback
feed([[:call RunTimer()<CR>]])
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
eq({ 'c', 'n' }, exec_lua('return _G.res'))
-- Halfway a mapping
feed([[:call v:lua.Run_timer()<CR>;]])
meths.get_mode() -- Process pending input and luv timer callback
meths.get_mode() -- Process pending input and luv timer callback
feed(';')
eq({ 'mS', 'n' }, exec_lua('return _G.res'))
-- An operator is pending
feed([[:call RunTimer()<CR>y]])
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
feed('y')
eq({ 'oSc', 'n' }, exec_lua('return _G.res'))
-- A register was specified
feed([[:call RunTimer()<CR>"r]])
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
feed('yy')
eq({ 'oSc', 'n' }, exec_lua('return _G.res'))
-- Insert mode completion
feed([[:call RunTimer()<CR>Got<C-N>]])
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
poke_eventloop() -- Process pending input
poke_eventloop() -- Process time_event
feed('<Esc>')
eq({ 'aSc', 'i' }, exec_lua('return _G.res'))
@@ -79,7 +79,7 @@ describe('state() function', function()
-- messages scrolled
feed([[:call v:lua.Run_timer() | echo "one\ntwo\nthree"<CR>]])
meths.get_mode() -- Process pending input and luv timer callback
meths.get_mode() -- Process pending input and luv timer callback
feed('<CR>')
eq({ 'Ss', 'r' }, exec_lua('return _G.res'))
end)

View File

@@ -15,12 +15,12 @@ describe('string() function', function()
describe('used to represent floating-point values', function()
it('dumps NaN values', function()
eq('str2float(\'nan\')', eval('string(str2float(\'nan\'))'))
eq("str2float('nan')", eval("string(str2float('nan'))"))
end)
it('dumps infinite values', function()
eq('str2float(\'inf\')', eval('string(str2float(\'inf\'))'))
eq('-str2float(\'inf\')', eval('string(str2float(\'-inf\'))'))
eq("str2float('inf')", eval("string(str2float('inf'))"))
eq("-str2float('inf')", eval("string(str2float('-inf'))"))
end)
it('dumps regular values', function()
@@ -38,14 +38,12 @@ describe('string() function', function()
eq('v:null', funcs.string(NIL))
end)
it('dumps values with at most six digits after the decimal point',
function()
it('dumps values with at most six digits after the decimal point', function()
eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020))
eq('1.234568', funcs.string(1.23456789123456789123456789))
end)
it('dumps values with at most seven digits before the decimal point',
function()
it('dumps values with at most seven digits before the decimal point', function()
eq('1234567.891235', funcs.string(1234567.89123456789123456789))
eq('1.234568e7', funcs.string(12345678.9123456789123456789))
end)
@@ -68,29 +66,29 @@ describe('string() function', function()
end)
it('dumps large values', function()
eq('2147483647', funcs.string(2^31-1))
eq('-2147483648', funcs.string(-2^31))
eq('2147483647', funcs.string(2 ^ 31 - 1))
eq('-2147483648', funcs.string(-2 ^ 31))
end)
end)
describe('used to represent strings', function()
it('dumps regular strings', function()
eq('\'test\'', funcs.string('test'))
eq("'test'", funcs.string('test'))
end)
it('dumps empty strings', function()
eq('\'\'', funcs.string(''))
eq("''", funcs.string(''))
end)
it('dumps strings with \' inside', function()
eq('\'\'\'\'\'\'\'\'', funcs.string('\'\'\''))
eq('\'a\'\'b\'\'\'\'\'', funcs.string('a\'b\'\''))
eq('\'\'\'b\'\'\'\'d\'', funcs.string('\'b\'\'d'))
eq('\'a\'\'b\'\'c\'\'d\'', funcs.string('a\'b\'c\'d'))
it("dumps strings with ' inside", function()
eq("''''''''", funcs.string("'''"))
eq("'a''b'''''", funcs.string("a'b''"))
eq("'''b''''d'", funcs.string("'b''d"))
eq("'a''b''c''d'", funcs.string("a'b'c'd"))
end)
it('dumps NULL strings', function()
eq('\'\'', eval('string($XXX_UNEXISTENT_VAR_XXX)'))
eq("''", eval('string($XXX_UNEXISTENT_VAR_XXX)'))
end)
it('dumps NULL lists', function()
@@ -119,16 +117,16 @@ describe('string() function', function()
end)
it('dumps references to built-in functions', function()
eq('function(\'function\')', eval('string(function("function"))'))
eq("function('function')", eval('string(function("function"))'))
end)
it('dumps references to user functions', function()
eq('function(\'Test1\')', eval('string(function("Test1"))'))
eq('function(\'g:Test3\')', eval('string(function("g:Test3"))'))
eq("function('Test1')", eval('string(function("Test1"))'))
eq("function('g:Test3')", eval('string(function("g:Test3"))'))
end)
it('dumps references to script functions', function()
eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)'))
eq("function('<SNR>1_Test2')", eval('string(Test2_f)'))
end)
it('dumps partials with self referencing a partial', function()
@@ -139,67 +137,84 @@ describe('string() function', function()
let TestDictRef = function('TestDict', d)
let d.tdr = TestDictRef
]])
eq("Vim(echo):E724: unable to correctly dump variable with self-referencing container",
pcall_err(command, 'echo string(d.tdr)'))
eq(
'Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, '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)'))
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]))'))
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([[Vim(echo):E724: unable to correctly dump variable with self-referencing container]],
pcall_err(command, 'echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
it('does not crash or halt when dumping partials with reference cycles in self', function()
meths.set_var('d', { v = true })
eq(
[[Vim(echo):E724: unable to correctly dump variable with self-referencing container]],
pcall_err(command, '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()
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)])'))
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)])'))
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()
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([=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=],
pcall_err(command, 'echo string(function("Test1", l))'))
eq(
[=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=],
pcall_err(command, '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([=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=],
pcall_err(command, 'echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, 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(
[=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=],
pcall_err(
command,
'echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'
)
)
end
)
end)
describe('used to represent lists', function()
@@ -208,27 +223,33 @@ describe('string() function', function()
end)
it('dumps nested lists', function()
eq('[[[[[]]]]]', funcs.string({{{{{}}}}}))
eq('[[[[[]]]]]', funcs.string({ { { { {} } } } }))
end)
it('dumps nested non-empty lists', function()
eq('[1, [[3, [[5], 4]], 2]]', funcs.string({1, {{3, {{5}, 4}}, 2}}))
eq('[1, [[3, [[5], 4]], 2]]', funcs.string({ 1, { { 3, { { 5 }, 4 } }, 2 } }))
end)
it('errors when dumping recursive lists', function()
meths.set_var('l', {})
eval('add(l, l)')
eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
exc_exec('echo string(l)'))
eq(
'Vim(echo):E724: unable to correctly dump variable with self-referencing container',
exc_exec('echo string(l)')
)
end)
it('dumps recursive lists despite the error', function()
meths.set_var('l', {})
eval('add(l, l)')
eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string(l)'))
eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string([l])'))
eq(
'Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string(l)')
)
eq(
'Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string([l])')
)
end)
end)
@@ -240,28 +261,34 @@ describe('string() function', function()
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)])'))
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}))
eq("{'t''est': 1}", funcs.string({ ["t'est"] = 1 }))
end)
it('errors when dumping recursive dictionaries', function()
meths.set_var('d', {d=1})
meths.set_var('d', { d = 1 })
eval('extend(d, {"d": d})')
eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
exc_exec('echo string(d)'))
eq(
'Vim(echo):E724: unable to correctly dump variable with self-referencing container',
exc_exec('echo string(d)')
)
end)
it('dumps recursive dictionaries despite the error', function()
meths.set_var('d', {d=1})
meths.set_var('d', { d = 1 })
eval('extend(d, {"d": d})')
eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string(d)'))
eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string({"out": d})'))
eq(
'Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string(d)')
)
eq(
'Vim(echo):E724: unable to correctly dump variable with self-referencing container',
pcall_err(command, 'echo string({"out": d})')
)
end)
end)
end)

View File

@@ -5,8 +5,13 @@ local helpers = require('test.functional.helpers')(after_each)
local assert_alive = helpers.assert_alive
local testprg = helpers.testprg
local eq, call, clear, eval, feed_command, feed, nvim =
helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command,
helpers.feed, helpers.nvim
helpers.eq,
helpers.call,
helpers.clear,
helpers.eval,
helpers.feed_command,
helpers.feed,
helpers.nvim
local command = helpers.command
local insert = helpers.insert
local expect = helpers.expect
@@ -19,14 +24,14 @@ 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
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.."')")
eval("delete('" .. name .. "')")
end
end
@@ -35,8 +40,10 @@ describe('system()', function()
describe('command passed as a List', function()
it('throws error if cmd[0] is not executable', function()
eq("Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable",
pcall_err(call, 'system', { 'this-should-not-exist' }))
eq(
"Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable",
pcall_err(call, 'system', { 'this-should-not-exist' })
)
eq(-1, eval('v:shell_error'))
end)
@@ -51,8 +58,10 @@ describe('system()', function()
eq(0, eval('v:shell_error'))
-- Provoke a non-zero v:shell_error.
eq("Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable",
pcall_err(call, 'system', { 'this-should-not-exist' }))
eq(
"Vim:E475: Invalid value for argument cmd: 'this-should-not-exist' is not executable",
pcall_err(call, 'system', { 'this-should-not-exist' })
)
local old_val = eval('v:shell_error')
eq(-1, old_val)
@@ -65,8 +74,8 @@ describe('system()', function()
end)
it('quotes arguments correctly #5280', function()
local out = call('system',
{ testprg('printargs-test'), [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] })
local out =
call('system', { testprg('printargs-test'), [[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)
@@ -75,22 +84,29 @@ describe('system()', function()
eq(0, eval('v:shell_error'))
eq([[arg1='1;arg2=2 "3;]], out)
out = call('system', { testprg('printargs-test'), "A\nB" })
out = call('system', { testprg('printargs-test'), 'A\nB' })
eq(0, eval('v:shell_error'))
eq("arg1=A\nB;", out)
eq('arg1=A\nB;', out)
end)
it('calls executable in $PATH', function()
if 0 == eval("executable('python3')") then pending("missing `python3`") end
eq("foo\n", eval([[system(['python3', '-c', 'print("foo")'])]]))
if 0 == eval("executable('python3')") then
pending('missing `python3`')
end
eq('foo\n', eval([[system(['python3', '-c', 'print("foo")'])]]))
eq(0, eval('v:shell_error'))
end)
it('does NOT run in shell', function()
if is_os('win') then
eq("%PATH%\n", eval("system(['powershell', '-NoProfile', '-NoLogo', '-ExecutionPolicy', 'RemoteSigned', '-Command', 'Write-Output', '%PATH%'])"))
eq(
'%PATH%\n',
eval(
"system(['powershell', '-NoProfile', '-NoLogo', '-ExecutionPolicy', 'RemoteSigned', '-Command', 'Write-Output', '%PATH%'])"
)
)
else
eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])"))
eq('* $PATH %PATH%\n', eval("system(['echo', '*', '$PATH', '%PATH%'])"))
end
end)
end)
@@ -133,7 +149,12 @@ describe('system()', function()
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 Write-Output ''\^"a b\^"''')]]))
eq(
'"a b"\n',
eval(
[[system('powershell -NoProfile -NoLogo -ExecutionPolicy RemoteSigned -Command Write-Output ''\^"a b\^"''')]]
)
)
end
it('with shell=cmd.exe', function()
@@ -177,7 +198,7 @@ describe('system()', function()
it('powershell w/ UTF-8 text #13713', function()
if not helpers.has_powershell() then
pending("powershell not found", function() end)
pending('powershell not found', function() end)
return
end
helpers.set_shell_powershell()
@@ -205,9 +226,9 @@ describe('system()', function()
screen:try_resize(72, 14)
feed(':4verbose echo system("echo hi")<cr>')
if is_os('win') then
screen:expect{any=[[Executing command: "'fake_shell' 'cmdflag' '"echo hi"'"]]}
screen:expect { any = [[Executing command: "'fake_shell' 'cmdflag' '"echo hi"'"]] }
else
screen:expect{any=[[Executing command: "'fake_shell' 'cmdflag' 'echo hi'"]]}
screen:expect { any = [[Executing command: "'fake_shell' 'cmdflag' 'echo hi'"]] }
end
feed('<cr>')
end)
@@ -234,16 +255,16 @@ describe('system()', function()
end)
it('`yes` interrupted with CTRL-C', function()
feed(':call system("' .. (is_os('win')
and 'for /L %I in (1,0,2) do @echo y'
or 'yes') .. '")<cr>')
feed(
':call system("'
.. (is_os('win') and 'for /L %I in (1,0,2) do @echo y' or 'yes')
.. '")<cr>'
)
screen:expect([[
|
~ |*12
]] .. (is_os('win')
and [[
:call system("for /L %I in (1,0,2) do @echo y") |]]
or [[
]] .. (is_os('win') and [[
:call system("for /L %I in (1,0,2) do @echo y") |]] or [[
:call system("yes") |]]))
feed('foo<c-c>')
screen:expect([[
@@ -255,16 +276,16 @@ describe('system()', function()
it('`yes` interrupted with mapped CTRL-C', function()
command('nnoremap <C-C> i')
feed(':call system("' .. (is_os('win')
and 'for /L %I in (1,0,2) do @echo y'
or 'yes') .. '")<cr>')
feed(
':call system("'
.. (is_os('win') and 'for /L %I in (1,0,2) do @echo y' or 'yes')
.. '")<cr>'
)
screen:expect([[
|
~ |*12
]] .. (is_os('win')
and [[
:call system("for /L %I in (1,0,2) do @echo y") |]]
or [[
]] .. (is_os('win') and [[
:call system("for /L %I in (1,0,2) do @echo y") |]] or [[
:call system("yes") |]]))
feed('foo<c-c>')
screen:expect([[
@@ -278,17 +299,19 @@ describe('system()', function()
describe('passing no input', function()
it('returns the program output', function()
if is_os('win') then
eq("echoed\n", eval('system("echo echoed")'))
eq('echoed\n', eval('system("echo echoed")'))
else
eq("echoed", eval('system("printf echoed")'))
eq('echoed', eval('system("printf echoed")'))
end
end)
it('to backgrounded command does not crash', function()
-- This is indeterminate, just exercise the codepath. May get E5677.
feed_command('call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "printf echoed &")')
local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
feed_command(
'call system(has("win32") ? "start /b /wait cmd /c echo echoed" : "printf echoed &")'
)
local v_errnum = string.match(eval('v:errmsg'), '^E%d*:')
if v_errnum then
eq("E5677:", v_errnum)
eq('E5677:', v_errnum)
end
assert_alive()
end)
@@ -296,19 +319,19 @@ describe('system()', function()
describe('passing input', function()
it('returns the program output', function()
eq("input", eval('system("cat -", "input")'))
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.
feed_command('call system(has("win32") ? "start /b /wait more" : "cat - &", "input")')
local v_errnum = string.match(eval("v:errmsg"), "^E%d*:")
local v_errnum = string.match(eval('v:errmsg'), '^E%d*:')
if v_errnum then
eq("E5677:", v_errnum)
eq('E5677:', v_errnum)
end
assert_alive()
end)
it('works with an empty string', function()
eq("test\n", eval('system("echo test", "")'))
eq('test\n', eval('system("echo test", "")'))
assert_alive()
end)
end)
@@ -332,8 +355,7 @@ describe('system()', function()
it('is treated as a buffer id', function()
command("put ='text in buffer 1'")
eq('\ntext in buffer 1\n', eval('system("cat", 1)'))
eq('Vim(echo):E86: Buffer 42 does not exist',
exc_exec('echo system("cat", 42)'))
eq('Vim(echo):E86: Buffer 42 does not exist', exc_exec('echo system("cat", 42)'))
end)
end)
@@ -344,14 +366,13 @@ 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)
describe('input passed as List', function()
it('joins List items with linefeed characters', function()
eq('line1\nline2\nline3',
eval("system('cat -', ['line1', 'line2', 'line3'])"))
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
@@ -360,15 +381,19 @@ describe('system()', function()
-- 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'])]]))
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"])]]))
eq(
'line \nline2\001\n\001line3',
eval([[system('cat -', ['line ', "line2\n", "\nline3"])]])
)
end)
end)
end)
@@ -376,7 +401,7 @@ describe('system()', function()
it("with a program that doesn't close stdout will exit properly after passing input", function()
local out = eval(string.format("system('%s', 'clip-data')", testprg('streams-test')))
assert(out:sub(0, 5) == 'pid: ', out)
os_kill(out:match("%d+"))
os_kill(out:match('%d+'))
end)
end)
@@ -445,7 +470,7 @@ describe('systemlist()', function()
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")]]))
eq({ 'abc', 'def', 'ghi' }, eval([[systemlist("cat -", "abc\ndef\nghi")]]))
end)
end)
@@ -470,75 +495,77 @@ 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)
describe('input passed as List', function()
it('joins list items with linefeed characters', function()
eq({'line1', 'line2', 'line3'},
eval("systemlist('cat -', ['line1', 'line2', 'line3'])"))
eq({ 'line1', 'line2', 'line3' }, eval("systemlist('cat -', ['line1', 'line2', 'line3'])"))
end)
-- Unlike `system()` which uses SOH to represent NULs, with `systemlist()`
-- input and output 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'])]]))
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"])]]))
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'])"))
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'])"))
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)"))
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)"))
eq({ 'aa', 'bb' }, eval("systemlist('cat',['aa','bb'],1)"))
eq({ 'aa', 'bb', '' }, eval("systemlist('cat',['aa','bb',''],2)"))
end)
end)
it("with a program that doesn't close stdout will exit properly after passing input", function()
local out = eval(string.format("systemlist('%s', 'clip-data')", testprg('streams-test')))
assert(out[1]:sub(0, 5) == 'pid: ', out)
os_kill(out[1]:match("%d+"))
os_kill(out[1]:match('%d+'))
end)
it('powershell w/ UTF-8 text #13713', function()
if not helpers.has_powershell() then
pending("powershell not found", function() end)
pending('powershell not found', function() end)
return
end
helpers.set_shell_powershell()
eq({is_os('win') and '\r' or ''}, eval([[systemlist('Write-Output あ')]]))
eq({ is_os('win') and '\r' or '' }, eval([[systemlist('Write-Output あ')]]))
-- Sanity test w/ default encoding
-- * on Windows, expected to default to Western European enc
-- * on Linux, expected to default to UTF8
command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']])
eq({is_os('win') and '?\r' or ''}, eval([[systemlist('Write-Output あ')]]))
eq({ is_os('win') and '?\r' or '' }, eval([[systemlist('Write-Output あ')]]))
end)
end)
describe('shell :!', function()
@@ -555,13 +582,13 @@ describe('shell :!', function()
2]])
if is_os('win') then
feed(':4verbose %!sort /R<cr>')
screen:expect{
any=[[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]]
screen:expect {
any = [[Executing command: .?& { Get%-Content .* | & sort /R } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]],
}
else
feed(':4verbose %!sort -r<cr>')
screen:expect{
any=[[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]]
screen:expect {
any = [[Executing command: .?& { Get%-Content .* | & sort %-r } 2>&1 | %%{ "$_" } | Out%-File .*; exit $LastExitCode"]],
}
end
feed('<CR>')
@@ -585,20 +612,19 @@ describe('shell :!', function()
2]])
feed(':4verbose %w !sort<cr>')
if is_os('win') then
screen:expect{
any=[[Executing command: .?sort %< .*]]
screen:expect {
any = [[Executing command: .?sort %< .*]],
}
else
screen:expect{
any=[[Executing command: .?%(sort%) %< .*]]
screen:expect {
any = [[Executing command: .?%(sort%) %< .*]],
}
end
feed('<CR>')
helpers.set_shell_powershell(true)
feed(':4verbose %w !sort<cr>')
screen:expect{
any=[[Executing command: .?& { Get%-Content .* | & sort }]]
screen:expect {
any = [[Executing command: .?& { Get%-Content .* | & sort }]],
}
feed('<CR>')
helpers.expect_exit(command, 'qall!')

View File

@@ -22,20 +22,20 @@ describe('timers', function()
it('works one-shot', function()
eq(0, eval("[timer_start(10, 'MyHandler'), g:val][1]"))
run(nil, nil, nil, load_adjust(100))
eq(1,eval("g:val"))
eq(1, eval('g:val'))
end)
it('works one-shot when repeat=0', function()
eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 0}), g:val][1]"))
run(nil, nil, nil, load_adjust(100))
eq(1, eval("g:val"))
eq(1, eval('g:val'))
end)
it('works with repeat two', function()
eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]"))
run(nil, nil, nil, load_adjust(20))
retry(nil, load_adjust(300), function()
eq(2, eval("g:val"))
eq(2, eval('g:val'))
end)
end)
@@ -52,12 +52,12 @@ describe('timers', function()
endfunc
]])
eval("timer_start(10, 'MyHandler', {'repeat': -1})")
nvim_async("command", "sleep 10")
eq(-1, eval("g:val")) -- timer did nothing yet.
nvim_async("command", "let g:val = 0")
nvim_async('command', 'sleep 10')
eq(-1, eval('g:val')) -- timer did nothing yet.
nvim_async('command', 'let g:val = 0')
run(nil, nil, nil, load_adjust(20))
retry(nil, nil, function()
eq(2, eval("g:val"))
eq(2, eval('g:val'))
end)
end)
@@ -65,52 +65,53 @@ describe('timers', function()
-- timer_start does still not invoke the callback immediately
eq(0, eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]"))
retry(nil, nil, function()
eq(1000, eval("g:val"))
eq(1000, eval('g:val'))
end)
end)
it('can be started during sleep', function()
nvim_async("command", "sleep 10")
nvim_async('command', 'sleep 10')
-- this also tests that remote requests works during sleep
eq(0, eval("[timer_start(10, 'MyHandler', {'repeat': 2}), g:val][1]"))
run(nil, nil, nil, load_adjust(20))
retry(nil, load_adjust(300), function() eq(2,eval("g:val")) end)
retry(nil, load_adjust(300), function()
eq(2, eval('g:val'))
end)
end)
it('are paused when event processing is disabled', function()
command("call timer_start(5, 'MyHandler', {'repeat': -1})")
run(nil, nil, nil, load_adjust(10))
local count = eval("g:val")
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.
feed(':throw "fatal error"<CR>')
run(nil, nil, nil, load_adjust(30))
feed("<cr>")
local diff = eval("g:val") - count
assert(0 <= diff and diff <= 4,
'expected (0 <= diff <= 4), got: '..tostring(diff))
feed('<cr>')
local diff = eval('g:val') - count
assert(0 <= diff and diff <= 4, 'expected (0 <= diff <= 4), got: ' .. tostring(diff))
end)
it('are triggered in blocking getchar() call', function()
command("call timer_start(5, 'MyHandler', {'repeat': -1})")
nvim_async("command", "let g:val = 0 | let g:c = getchar()")
nvim_async('command', 'let g:val = 0 | let g:c = getchar()')
retry(nil, nil, function()
local val = eval("g:val")
local val = eval('g:val')
ok(val >= 2, '>= 2', tostring(val))
eq(0, eval("getchar(1)"))
eq(0, eval('getchar(1)'))
end)
feed("c")
eq(99, eval("g:c"))
feed('c')
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},
[1] = { bold = true, foreground = Screen.colors.Blue },
})
curbufmeths.set_lines(0, -1, true, {"ITEM 1", "ITEM 2"})
curbufmeths.set_lines(0, -1, true, { 'ITEM 1', 'ITEM 2' })
source([[
let g:cont = 0
func! AddItem(timer)
@@ -127,8 +128,8 @@ describe('timers', function()
redraw
endfunc
]])
nvim_async("command", "let g:c2 = getchar()")
nvim_async("command", "call timer_start("..load_adjust(100)..", 'AddItem', {'repeat': -1})")
nvim_async('command', 'let g:c2 = getchar()')
nvim_async('command', 'call timer_start(' .. load_adjust(100) .. ", 'AddItem', {'repeat': -1})")
screen:expect([[
^ITEM 1 |
@@ -136,7 +137,7 @@ describe('timers', function()
{1:~ }|*3
|
]])
nvim_async("command", "let g:cont = 1")
nvim_async('command', 'let g:cont = 1')
screen:expect([[
^ITEM 1 |
@@ -146,15 +147,18 @@ describe('timers', function()
|
]])
feed("3")
eq(51, eval("g:c2"))
screen:expect{grid=[[
feed('3')
eq(51, eval('g:c2'))
screen:expect {
grid = [[
^ITEM 1 |
ITEM 2 |
ITEM 3 |
{1:~ }|*2
|
]], unchanged=true}
]],
unchanged = true,
}
end)
it('can be stopped', function()
@@ -162,9 +166,9 @@ describe('timers', function()
eq(0, t_init_val[2])
run(nil, nil, nil, load_adjust(30))
funcs.timer_stop(t_init_val[1])
local count = eval("g:val")
local count = eval('g:val')
run(nil, load_adjust(300), nil, load_adjust(30))
local count2 = eval("g:val")
local count2 = eval('g:val')
-- when count is eval:ed after timer_stop this should be non-racy
eq(count, count2)
end)
@@ -180,10 +184,10 @@ describe('timers', function()
endif
endfunc
]])
eq(0, eval("g:val"))
eq(0, eval('g:val'))
command("call timer_start(10, 'MyHandler', {'repeat': -1})")
retry(nil, nil, function()
eq(3, eval("g:val"))
eq(3, eval('g:val'))
end)
end)
@@ -197,8 +201,8 @@ describe('timers', function()
command("call timer_start(2, 'MyHandler', {'repeat': 3})")
command("call timer_start(4, 'MyHandler2', {'repeat': 2})")
retry(nil, nil, function()
eq(3, eval("g:val"))
eq(2, eval("g:val2"))
eq(3, eval('g:val'))
eq(2, eval('g:val2'))
end)
end)
@@ -214,15 +218,14 @@ describe('timers', function()
command("call timer_start(5, 'MyHandler', {'repeat': 1})")
run(nil, nil, nil, load_adjust(20))
retry(nil, load_adjust(150), function()
eq(1, eval("g:val"))
eq(1, eval('g:val'))
end)
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}} )
screen:set_default_attr_ids({ [0] = { bold = true, foreground = 255 } })
source([[
let g:val = 0
func! MyHandler(timer)
@@ -237,7 +240,7 @@ describe('timers', function()
endfunc
]])
command("call timer_start(100, 'MyHandler', {'repeat': -1})")
feed(":good")
feed(':good')
screen:expect([[
|
{0:~ }|*4
@@ -255,7 +258,7 @@ describe('timers', function()
call execute('echo ''execute() should be disallowed''', '')
endfunction
]]
eq("Vim(call):E48: Not allowed in sandbox", exc_exec("sandbox call timer_start(0, 'Scary')"))
eq('Vim(call):E48: Not allowed in sandbox', exc_exec("sandbox call timer_start(0, 'Scary')"))
end)
it('can be triggered after an empty string <expr> mapping #17257', function()
@@ -263,6 +266,6 @@ describe('timers', function()
screen:attach()
command([=[imap <expr> <F2> [timer_start(0, { _ -> execute("throw 'x'", "") }), ''][-1]]=])
feed('i<F2>')
screen:expect({any='E605: Exception not caught: x'})
screen:expect({ any = 'E605: Exception not caught: x' })
end)
end)

View File

@@ -10,8 +10,10 @@ before_each(clear)
describe('uniq()', function()
it('errors out when processing special values', function()
eq('Vim(call):E362: Using a boolean value as a Float',
exc_exec('call uniq([v:true, v:false], "f")'))
eq(
'Vim(call):E362: Using a boolean value as a Float',
exc_exec('call uniq([v:true, v:false], "f")')
)
end)
it('can yield E882 and stop filtering after that', function()
@@ -23,7 +25,9 @@ describe('uniq()', function()
return (a:a > a:b) - (a:a < a:b)
endfunction
]])
eq('Vim(let):E745: Using a List as a Number',
pcall_err(command, 'let fl = uniq([0, 0, [], 1, 1], "Cmp")'))
eq(
'Vim(let):E745: Using a List as a Number',
pcall_err(command, 'let fl = uniq([0, 0, [], 1, 1], "Cmp")')
)
end)
end)

View File

@@ -12,4 +12,3 @@ describe('v:event', function()
eq(false, pcall(command, 'let v:event.mykey = {}'))
end)
end)

View File

@@ -36,11 +36,12 @@ describe('wait()', function()
end)
it('returns -2 when interrupted', function()
feed_command('call rpcnotify(g:channel, "ready") | '..
'call rpcnotify(g:channel, "wait", wait(-1, 0))')
eq({'notification', 'ready', {}}, next_msg())
feed_command(
'call rpcnotify(g:channel, "ready") | ' .. 'call rpcnotify(g:channel, "wait", wait(-1, 0))'
)
eq({ 'notification', 'ready', {} }, next_msg())
feed('<c-c>')
eq({'notification', 'wait', {-2}}, next_msg())
eq({ 'notification', 'wait', { -2 } }, next_msg())
end)
it('returns -3 on error', function()

View File

@@ -52,125 +52,151 @@ describe('writefile()', function()
end)
it('writes list with an empty string to a file', function()
eq(0, exc_exec(
('call writefile([$XXX_NONEXISTENT_VAR_XXX], "%s", "b")'):format(
fname)))
eq(0, exc_exec(('call writefile([$XXX_NONEXISTENT_VAR_XXX], "%s", "b")'):format(fname)))
eq('', read_file(fname))
eq(0, exc_exec(('call writefile([$XXX_NONEXISTENT_VAR_XXX], "%s")'):format(
fname)))
eq(0, exc_exec(('call writefile([$XXX_NONEXISTENT_VAR_XXX], "%s")'):format(fname)))
eq('\n', read_file(fname))
end)
it('writes list with a null string to a file', function()
eq(0, exc_exec(
('call writefile([v:_null_string], "%s", "b")'):format(
fname)))
eq(0, exc_exec(('call writefile([v:_null_string], "%s", "b")'):format(fname)))
eq('', read_file(fname))
eq(0, exc_exec(('call writefile([v:_null_string], "%s")'):format(
fname)))
eq(0, exc_exec(('call writefile([v:_null_string], "%s")'):format(fname)))
eq('\n', read_file(fname))
end)
it('appends to a file', function()
eq(nil, read_file(fname))
eq(0, funcs.writefile({'abc', 'def', 'ghi'}, fname))
eq(0, funcs.writefile({ 'abc', 'def', 'ghi' }, fname))
eq('abc\ndef\nghi\n', read_file(fname))
eq(0, funcs.writefile({'jkl'}, fname, 'a'))
eq(0, funcs.writefile({ 'jkl' }, fname, 'a'))
eq('abc\ndef\nghi\njkl\n', read_file(fname))
os.remove(fname)
eq(nil, read_file(fname))
eq(0, funcs.writefile({'abc', 'def', 'ghi'}, fname, 'b'))
eq(0, funcs.writefile({ 'abc', 'def', 'ghi' }, fname, 'b'))
eq('abc\ndef\nghi', read_file(fname))
eq(0, funcs.writefile({'jkl'}, fname, 'ab'))
eq(0, funcs.writefile({ 'jkl' }, fname, 'ab'))
eq('abc\ndef\nghijkl', read_file(fname))
end)
it('correctly treats NLs', function()
eq(0, funcs.writefile({'\na\nb\n'}, fname, 'b'))
eq(0, funcs.writefile({ '\na\nb\n' }, fname, 'b'))
eq('\0a\0b\0', read_file(fname))
eq(0, funcs.writefile({'a\n\n\nb'}, fname, 'b'))
eq(0, funcs.writefile({ 'a\n\n\nb' }, fname, 'b'))
eq('a\0\0\0b', read_file(fname))
end)
it('writes with s and S', function()
eq(0, funcs.writefile({'\na\nb\n'}, fname, 'bs'))
eq(0, funcs.writefile({ '\na\nb\n' }, fname, 'bs'))
eq('\0a\0b\0', read_file(fname))
eq(0, funcs.writefile({'a\n\n\nb'}, fname, 'bS'))
eq(0, funcs.writefile({ 'a\n\n\nb' }, fname, 'bS'))
eq('a\0\0\0b', read_file(fname))
end)
it('correctly overwrites file', function()
eq(0, funcs.writefile({'\na\nb\n'}, fname, 'b'))
eq(0, funcs.writefile({ '\na\nb\n' }, fname, 'b'))
eq('\0a\0b\0', read_file(fname))
eq(0, funcs.writefile({'a\n'}, fname, 'b'))
eq(0, funcs.writefile({ 'a\n' }, fname, 'b'))
eq('a\0', read_file(fname))
end)
it('shows correct file name when supplied numbers', function()
meths.set_current_dir(dname)
eq('Vim(call):E482: Can\'t open file 2 for writing: illegal operation on a directory',
pcall_err(command, ('call writefile([42], %s)'):format(ddname_tail)))
eq(
"Vim(call):E482: Can't open file 2 for writing: illegal operation on a directory",
pcall_err(command, ('call writefile([42], %s)'):format(ddname_tail))
)
end)
it('writefile(..., "p") creates missing parent directories', function()
os.remove(dname)
eq(nil, read_file(dfname))
eq(0, funcs.writefile({'abc', 'def', 'ghi'}, dfname, 'p'))
eq(0, funcs.writefile({ 'abc', 'def', 'ghi' }, dfname, 'p'))
eq('abc\ndef\nghi\n', read_file(dfname))
os.remove(dfname)
os.remove(dname)
eq(nil, read_file(dfname))
eq(0, funcs.writefile({'\na\nb\n'}, dfname, 'pb'))
eq(0, funcs.writefile({ '\na\nb\n' }, dfname, 'pb'))
eq('\0a\0b\0', read_file(dfname))
os.remove(dfname)
os.remove(dname)
eq('Vim(call):E32: No file name',
pcall_err(command, ('call writefile([], "%s", "p")'):format(dfname .. '.d/')))
eq(('Vim(call):E482: Can\'t open file ./ for writing: illegal operation on a directory'),
pcall_err(command, 'call writefile([], "./", "p")'))
eq(('Vim(call):E482: Can\'t open file . for writing: illegal operation on a directory'),
pcall_err(command, 'call writefile([], ".", "p")'))
eq(
'Vim(call):E32: No file name',
pcall_err(command, ('call writefile([], "%s", "p")'):format(dfname .. '.d/'))
)
eq(
"Vim(call):E482: Can't open file ./ for writing: illegal operation on a directory",
pcall_err(command, 'call writefile([], "./", "p")')
)
eq(
"Vim(call):E482: Can't open file . for writing: illegal operation on a directory",
pcall_err(command, 'call writefile([], ".", "p")')
)
end)
it('errors out with invalid arguments', function()
write_file(fname, 'TEST')
eq('Vim(call):E119: Not enough arguments for function: writefile',
pcall_err(command, 'call writefile()'))
eq('Vim(call):E119: Not enough arguments for function: writefile',
pcall_err(command, 'call writefile([])'))
eq('Vim(call):E118: Too many arguments for function: writefile',
pcall_err(command, ('call writefile([], "%s", "b", 1)'):format(fname)))
for _, arg in ipairs({'0', '0.0', 'function("tr")', '{}', '"test"'}) do
eq('Vim(call):E475: Invalid argument: writefile() first argument must be a List or a Blob',
pcall_err(command, ('call writefile(%s, "%s", "b")'):format(arg, fname)))
eq(
'Vim(call):E119: Not enough arguments for function: writefile',
pcall_err(command, 'call writefile()')
)
eq(
'Vim(call):E119: Not enough arguments for function: writefile',
pcall_err(command, 'call writefile([])')
)
eq(
'Vim(call):E118: Too many arguments for function: writefile',
pcall_err(command, ('call writefile([], "%s", "b", 1)'):format(fname))
)
for _, arg in ipairs({ '0', '0.0', 'function("tr")', '{}', '"test"' }) do
eq(
'Vim(call):E475: Invalid argument: writefile() first argument must be a List or a Blob',
pcall_err(command, ('call writefile(%s, "%s", "b")'):format(arg, fname))
)
end
for _, args in ipairs({'[], %s, "b"', '[], "' .. fname .. '", %s'}) do
eq('Vim(call):E730: Using a List as a String',
pcall_err(command, ('call writefile(%s)'):format(args:format('[]'))))
eq('Vim(call):E731: Using a Dictionary as a String',
pcall_err(command, ('call writefile(%s)'):format(args:format('{}'))))
eq('Vim(call):E729: Using a Funcref as a String',
pcall_err(command, ('call writefile(%s)'):format(args:format('function("tr")'))))
for _, args in ipairs({ '[], %s, "b"', '[], "' .. fname .. '", %s' }) do
eq(
'Vim(call):E730: Using a List as a String',
pcall_err(command, ('call writefile(%s)'):format(args:format('[]')))
)
eq(
'Vim(call):E731: Using a Dictionary as a String',
pcall_err(command, ('call writefile(%s)'):format(args:format('{}')))
)
eq(
'Vim(call):E729: Using a Funcref as a String',
pcall_err(command, ('call writefile(%s)'):format(args:format('function("tr")')))
)
end
eq('Vim(call):E5060: Unknown flag: «»',
pcall_err(command, ('call writefile([], "%s", "bs«»")'):format(fname)))
eq(
'Vim(call):E5060: Unknown flag: «»',
pcall_err(command, ('call writefile([], "%s", "bs«»")'):format(fname))
)
eq('TEST', read_file(fname))
end)
it('does not write to file if error in list', function()
local args = '["tset"] + repeat([%s], 3), "' .. fname .. '"'
eq('Vim(call):E805: Expected a Number or a String, Float found',
pcall_err(command, ('call writefile(%s)'):format(args:format('0.0'))))
eq(
'Vim(call):E805: Expected a Number or a String, Float found',
pcall_err(command, ('call writefile(%s)'):format(args:format('0.0')))
)
eq(nil, read_file(fname))
write_file(fname, 'TEST')
eq('Vim(call):E745: Expected a Number or a String, List found',
pcall_err(command, ('call writefile(%s)'):format(args:format('[]'))))
eq(
'Vim(call):E745: Expected a Number or a String, List found',
pcall_err(command, ('call writefile(%s)'):format(args:format('[]')))
)
eq('TEST', read_file(fname))
eq('Vim(call):E728: Expected a Number or a String, Dictionary found',
pcall_err(command, ('call writefile(%s)'):format(args:format('{}'))))
eq(
'Vim(call):E728: Expected a Number or a String, Dictionary found',
pcall_err(command, ('call writefile(%s)'):format(args:format('{}')))
)
eq('TEST', read_file(fname))
eq('Vim(call):E703: Expected a Number or a String, Funcref found',
pcall_err(command, ('call writefile(%s)'):format(args:format('function("tr")'))))
eq(
'Vim(call):E703: Expected a Number or a String, Funcref found',
pcall_err(command, ('call writefile(%s)'):format(args:format('function("tr")')))
)
eq('TEST', read_file(fname))
end)
end)