aboutsummaryrefslogtreecommitdiff
path: root/test/functional/eval
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/eval')
-rw-r--r--test/functional/eval/buf_functions_spec.lua302
-rw-r--r--test/functional/eval/container_functions_spec.lua24
-rw-r--r--test/functional/eval/hostname_spec.lua17
-rw-r--r--test/functional/eval/input_spec.lua38
-rw-r--r--test/functional/eval/match_functions_spec.lua61
-rw-r--r--test/functional/eval/minmax_functions_spec.lua51
-rw-r--r--test/functional/eval/null_spec.lua138
-rw-r--r--test/functional/eval/sort_spec.lua41
-rw-r--r--test/functional/eval/string_spec.lua10
-rw-r--r--test/functional/eval/timer_spec.lua2
-rw-r--r--test/functional/eval/writefile_spec.lua9
11 files changed, 683 insertions, 10 deletions
diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua
new file mode 100644
index 0000000000..db50874c53
--- /dev/null
+++ b/test/functional/eval/buf_functions_spec.lua
@@ -0,0 +1,302 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local lfs = require('lfs')
+
+local eq = helpers.eq
+local clear = helpers.clear
+local funcs = helpers.funcs
+local meths = helpers.meths
+local command = helpers.command
+local exc_exec = helpers.exc_exec
+local bufmeths = helpers.bufmeths
+local winmeths = helpers.winmeths
+local curbufmeths = helpers.curbufmeths
+local curwinmeths = helpers.curwinmeths
+local curtabmeths = helpers.curtabmeths
+local get_pathsep = helpers.get_pathsep
+
+local fname = 'Xtest-functional-eval-buf_functions'
+local fname2 = fname .. '.2'
+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
+ 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', 'v:null'}) do
+ eq('Vim(call):E5300: Expected a Number or a String',
+ exc_exec('call ' .. func:format(var)))
+ end
+ 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")')))
+ end)
+ end)
+end
+
+describe('bufname() function', function()
+ it('returns empty string when buffer was not found', function()
+ command('file ' .. fname)
+ eq('', funcs.bufname(2))
+ eq('', funcs.bufname('non-existent-buffer'))
+ eq('', funcs.bufname('#'))
+ command('edit ' .. fname2)
+ eq(2, funcs.bufnr('%'))
+ eq('', funcs.bufname('X'))
+ end)
+ before_each(function()
+ lfs.mkdir(dirname)
+ end)
+ after_each(function()
+ lfs.rmdir(dirname)
+ end)
+ it('returns expected buffer name', function()
+ eq('', funcs.bufname('%')) -- Buffer has no name yet
+ command('file ' .. fname)
+ local wd = lfs.currentdir()
+ local sep = get_pathsep()
+ local curdirname = funcs.fnamemodify(wd, ':t')
+ for _, arg in ipairs({'%', 1, 'X', wd}) do
+ eq(fname, funcs.bufname(arg))
+ meths.set_current_dir('..')
+ eq(curdirname .. sep .. fname, funcs.bufname(arg))
+ meths.set_current_dir(curdirname)
+ meths.set_current_dir(dirname)
+ eq(wd .. sep .. fname, funcs.bufname(arg))
+ meths.set_current_dir('..')
+ eq(fname, funcs.bufname(arg))
+ command('enew')
+ end
+ eq('', funcs.bufname('%'))
+ eq('', funcs.bufname('$'))
+ eq(2, funcs.bufnr('%'))
+ end)
+end)
+
+describe('bufnr() function', function()
+ it('returns -1 when buffer was not found', function()
+ command('file ' .. fname)
+ eq(-1, funcs.bufnr(2))
+ eq(-1, funcs.bufnr('non-existent-buffer'))
+ eq(-1, funcs.bufnr('#'))
+ command('edit ' .. fname2)
+ eq(2, funcs.bufnr('%'))
+ eq(-1, funcs.bufnr('X'))
+ end)
+ it('returns expected buffer number', function()
+ eq(1, funcs.bufnr('%'))
+ command('file ' .. fname)
+ local wd = lfs.currentdir()
+ local curdirname = funcs.fnamemodify(wd, ':t')
+ eq(1, funcs.bufnr(fname))
+ eq(1, funcs.bufnr(wd))
+ eq(1, funcs.bufnr(curdirname))
+ eq(1, funcs.bufnr('X'))
+ end)
+ it('returns number of last buffer with "$"', function()
+ eq(1, funcs.bufnr('$'))
+ command('new')
+ eq(2, funcs.bufnr('$'))
+ command('new')
+ eq(3, funcs.bufnr('$'))
+ command('only')
+ eq(3, funcs.bufnr('$'))
+ eq(3, funcs.bufnr('%'))
+ command('buffer 1')
+ eq(3, funcs.bufnr('$'))
+ eq(1, funcs.bufnr('%'))
+ command('bwipeout 2')
+ eq(3, funcs.bufnr('$'))
+ eq(1, funcs.bufnr('%'))
+ command('bwipeout 3')
+ eq(1, funcs.bufnr('$'))
+ eq(1, funcs.bufnr('%'))
+ command('new')
+ eq(4, funcs.bufnr('$'))
+ end)
+end)
+
+describe('bufwinnr() function', function()
+ it('returns -1 when buffer was not found', function()
+ command('file ' .. fname)
+ 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
+ eq(2, funcs.bufnr('%'))
+ eq(-1, funcs.bufwinnr('X'))
+ end)
+ before_each(function()
+ lfs.mkdir(dirname)
+ end)
+ after_each(function()
+ lfs.rmdir(dirname)
+ end)
+ it('returns expected window number', function()
+ eq(1, funcs.bufwinnr('%'))
+ command('file ' .. fname)
+ command('vsplit')
+ command('split ' .. fname2)
+ eq(2, funcs.bufwinnr(fname))
+ eq(1, funcs.bufwinnr(fname2))
+ eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1)))
+ meths.set_current_dir(dirname)
+ eq(2, funcs.bufwinnr(fname))
+ eq(1, funcs.bufwinnr(fname2))
+ eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1)))
+ eq(1, funcs.bufwinnr('%'))
+ eq(2, funcs.bufwinnr(1))
+ eq(1, funcs.bufwinnr(2))
+ eq(-1, funcs.bufwinnr(3))
+ eq(1, funcs.bufwinnr('$'))
+ end)
+end)
+
+describe('getbufline() function', function()
+ it('returns empty list when buffer was not found', function()
+ command('file ' .. fname)
+ eq({}, funcs.getbufline(2, 1))
+ eq({}, funcs.getbufline('non-existent-buffer', 1))
+ eq({}, funcs.getbufline('#', 1))
+ command('edit ' .. fname2)
+ eq(2, funcs.bufnr('%'))
+ eq({}, funcs.getbufline('X', 1))
+ end)
+ it('returns empty list when range is invalid', function()
+ eq({}, funcs.getbufline(1, 0))
+ 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))
+ eq({}, funcs.getbufline(1, -1, 9999))
+ end)
+ it('returns expected lines', function()
+ meths.set_option('hidden', true)
+ command('file ' .. fname)
+ 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))
+ end)
+end)
+
+describe('getbufvar() function', function()
+ it('returns empty list when buffer was not found', function()
+ command('file ' .. fname)
+ eq('', funcs.getbufvar(2, '&autoindent'))
+ eq('', funcs.getbufvar('non-existent-buffer', '&autoindent'))
+ eq('', funcs.getbufvar('#', '&autoindent'))
+ command('edit ' .. fname2)
+ eq(2, funcs.bufnr('%'))
+ eq('', funcs.getbufvar('X', '&autoindent'))
+ end)
+ it('returns empty list when variable/option/etc was not found', function()
+ command('file ' .. fname)
+ eq('', funcs.getbufvar(1, '&autondent'))
+ eq('', funcs.getbufvar(1, 'changedtic'))
+ end)
+ it('returns expected option value', function()
+ eq(0, funcs.getbufvar(1, '&autoindent'))
+ eq(0, funcs.getbufvar(1, '&l:autoindent'))
+ eq(0, funcs.getbufvar(1, '&g:autoindent'))
+ -- Also works with global-only options
+ eq(0, funcs.getbufvar(1, '&hidden'))
+ eq(0, funcs.getbufvar(1, '&l:hidden'))
+ eq(0, funcs.getbufvar(1, '&g:hidden'))
+ -- Also works with window-local options
+ eq(0, funcs.getbufvar(1, '&number'))
+ eq(0, funcs.getbufvar(1, '&l:number'))
+ eq(0, funcs.getbufvar(1, '&g:number'))
+ command('new')
+ -- But with window-local options it probably does not what you expect
+ curwinmeths.set_option('number', true)
+ -- (note that current window’s buffer is 2, but getbufvar() receives 1)
+ eq(2, bufmeths.get_number(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.
+ eq(0, funcs.getbufvar(1, '&g:number'))
+ end)
+ it('returns expected variable value', function()
+ eq(2, funcs.getbufvar(1, 'changedtick'))
+ 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, ''))
+ command('new')
+ eq(3, funcs.getbufvar(1, 'changedtick'))
+ eq(true, funcs.getbufvar(1, 'test'))
+ 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)'))
+ command('edit ' .. fname2)
+ eq(2, funcs.bufnr('%'))
+ 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()
+ eq(false, curwinmeths.get_option('number'))
+ command('split')
+ command('new')
+ eq(2, bufmeths.get_number(curwinmeths.get_buf()))
+ funcs.setbufvar(1, '&number', true)
+ local windows = curtabmeths.list_wins()
+ eq(false, winmeths.get_option(windows[1], 'number'))
+ eq(true, winmeths.get_option(windows[2], 'number'))
+ eq(false, winmeths.get_option(windows[3], 'number'))
+ eq(false, winmeths.get_option(meths.get_current_win(), 'number'))
+
+ eq(false, meths.get_option('hidden'))
+ funcs.setbufvar(1, '&hidden', true)
+ eq(true, meths.get_option('hidden'))
+
+ eq(false, bufmeths.get_option(buf1, 'autoindent'))
+ funcs.setbufvar(1, '&autoindent', true)
+ eq(true, bufmeths.get_option(buf1, 'autoindent'))
+ 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()
+ command('split')
+ command('new')
+ 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(true, bufmeths.get_var(buf1, 'number'))
+ funcs.setbufvar(1, 'changedtick', true)
+ -- eq(true, bufmeths.get_var(buf1, 'changedtick'))
+ eq(2, funcs.getbufvar(1, 'changedtick'))
+ end)
+end)
diff --git a/test/functional/eval/container_functions_spec.lua b/test/functional/eval/container_functions_spec.lua
new file mode 100644
index 0000000000..04a3248c49
--- /dev/null
+++ b/test/functional/eval/container_functions_spec.lua
@@ -0,0 +1,24 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local eq = helpers.eq
+local eval = helpers.eval
+local meths = helpers.meths
+local clear = helpers.clear
+
+before_each(clear)
+
+describe('extend()', function()
+ it('suceeds 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, 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'))
+ end)
+end)
diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/eval/hostname_spec.lua
new file mode 100644
index 0000000000..f1867846c4
--- /dev/null
+++ b/test/functional/eval/hostname_spec.lua
@@ -0,0 +1,17 @@
+local helpers = require('test.functional.helpers')(after_each)
+local ok = helpers.ok
+local call = helpers.call
+local clear = helpers.clear
+
+describe('hostname()', function()
+ before_each(clear)
+
+ it('returns hostname string', function()
+ local actual = call('hostname')
+ ok(string.len(actual) > 1)
+ if call('executable', 'hostname') == 1 then
+ local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '')
+ helpers.eq(expected, actual)
+ end
+ end)
+end)
diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua
new file mode 100644
index 0000000000..393fc10175
--- /dev/null
+++ b/test/functional/eval/input_spec.lua
@@ -0,0 +1,38 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local feed = helpers.feed
+local clear = helpers.clear
+local command = helpers.command
+
+local screen
+
+before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach()
+end)
+
+describe('input()', function()
+ it('works correctly with multiline prompts', function()
+ feed([[:call input("Test\nFoo")<CR>]])
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ Test |
+ Foo^ |
+ ]], {{bold=true, foreground=Screen.colors.Blue}})
+ end)
+ it('works correctly with multiline prompts and :echohl', function()
+ command('hi Test ctermfg=Red guifg=Red term=bold')
+ feed([[:echohl Test | call input("Test\nFoo")<CR>]])
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:Test} |
+ {2:Foo}^ |
+ ]], {{bold=true, foreground=Screen.colors.Blue}, {foreground=Screen.colors.Red}})
+ end)
+end)
diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua
new file mode 100644
index 0000000000..3150d89f62
--- /dev/null
+++ b/test/functional/eval/match_functions_spec.lua
@@ -0,0 +1,61 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local eq = helpers.eq
+local clear = helpers.clear
+local funcs = helpers.funcs
+local command = helpers.command
+
+before_each(clear)
+
+describe('setmatches()', 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())
+ end)
+
+ it('fails with -1 if highlight group is not defined', function()
+ eq(-1, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}}))
+ eq({}, funcs.getmatches())
+ eq(-1, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}}))
+ eq({}, funcs.getmatches())
+ end)
+end)
+
+describe('matchadd()', 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())
+ end)
+end)
diff --git a/test/functional/eval/minmax_functions_spec.lua b/test/functional/eval/minmax_functions_spec.lua
new file mode 100644
index 0000000000..c6eb754f91
--- /dev/null
+++ b/test/functional/eval/minmax_functions_spec.lua
@@ -0,0 +1,51 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local eq = helpers.eq
+local eval = helpers.eval
+local clear = helpers.clear
+local funcs = helpers.funcs
+local redir_exec = helpers.redir_exec
+
+before_each(clear)
+for _, func in ipairs({'min', 'max'}) do
+ describe(func .. '()', function()
+ it('gives a single error message when multiple values failed conversions',
+ function()
+ eq('\nE745: Using a List as a Number\n0',
+ redir_exec('echo ' .. func .. '([-5, [], [], [], 5])'))
+ eq('\nE745: Using a List as a Number\n0',
+ redir_exec('echo ' .. func .. '({1:-5, 2:[], 3:[], 4:[], 5:5})'))
+ for errmsg, errinput in pairs({
+ ['E745: Using a List as a Number'] = '[]',
+ ['E805: Using a Float as a Number'] = '0.0',
+ ['E703: Using a Funcref as a Number'] = 'function("tr")',
+ ['E728: Using a Dictionary as a Number'] = '{}',
+ }) do
+ eq('\n' .. errmsg .. '\n0',
+ redir_exec('echo ' .. func .. '([' .. errinput .. '])'))
+ eq('\n' .. errmsg .. '\n0',
+ redir_exec('echo ' .. func .. '({1:' .. errinput .. '})'))
+ end
+ end)
+ it('works with arrays/dictionaries with zero items', function()
+ eq(0, funcs[func]({}))
+ 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}))
+ 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(('\nE712: Argument of %s() must be a List or Dictionary\n0'):format(
+ func),
+ redir_exec('echo ' .. func .. '(' .. errinput .. ')'))
+ end
+ end)
+ end)
+end
diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua
new file mode 100644
index 0000000000..6fd30caec9
--- /dev/null
+++ b/test/functional/eval/null_spec.lua
@@ -0,0 +1,138 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local curbufmeths = helpers.curbufmeths
+local redir_exec = helpers.redir_exec
+local exc_exec = helpers.exc_exec
+local command = helpers.command
+local clear = helpers.clear
+local meths = helpers.meths
+local funcs = helpers.funcs
+local eq = helpers.eq
+
+describe('NULL', function()
+ before_each(function()
+ clear()
+ command('let L = v:_null_list')
+ command('let D = v:_null_dict')
+ command('let S = $XXX_NONEXISTENT_VAR_XXX')
+ end)
+ local tmpfname = 'Xtest-functional-viml-null'
+ after_each(function()
+ os.remove(tmpfname)
+ end)
+ local null_test = function(name, cmd, err)
+ it(name, function()
+ eq(err, exc_exec(cmd))
+ end)
+ 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))
+ if val == nil then
+ eq(0, funcs.exists('g:_var'))
+ else
+ eq(val, meths.get_var('_var'))
+ end
+ if after ~= nil then
+ after()
+ end
+ end)
+ end
+ describe('list', function()
+ -- Incorrect behaviour
+
+ -- FIXME map() should not return 0 without error
+ null_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0)
+ -- FIXME map() should not return 0 without error
+ null_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0)
+ -- FIXME map() should at least return L
+ null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0)
+ -- FIXME filter() should at least return L
+ null_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0)
+ -- FIXME add() should not return 1 at all
+ null_expr_test('does not crash add()', 'add(L, 0)', 0, 1)
+ 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})
+ -- FIXME should be accepted by inputlist()
+ null_expr_test('is accepted as an empty list by inputlist()',
+ '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0})
+ -- FIXME should be accepted by writefile(), return {0, {}}
+ null_expr_test('is accepted as an empty list by writefile()',
+ ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname),
+ 'E484: Can\'t open file ' .. tmpfname, {0, {}})
+ -- FIXME should give error message
+ null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0)
+ -- FIXME should return 0
+ null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1)
+ -- FIXME should return 0
+ null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1)
+ -- FIXME should return 0
+ null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1)
+ -- FIXME should return empty list or error out
+ null_expr_test('is accepted by sort()', 'sort(L)', 0, 0)
+ -- FIXME Should return 1
+ null_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0)
+ -- FIXME should not error out
+ null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected')
+ -- FIXME should not error out
+ null_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected')
+ null_test('is accepted by :for', 'for x in L|throw x|endfor', 0)
+
+ -- Subjectable behaviour
+
+ -- FIXME Should return 1
+ null_expr_test('is equal to empty list', 'L == []', 0, 0)
+ -- FIXME Should return 1
+ null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0)
+ -- FIXME Should return 1
+ null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0)
+
+ -- Crashes
+
+ -- null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0)
+ -- null_expr_test('does not crash setline', 'setline(1, L)', 0, 0)
+ -- null_expr_test('does not crash system()', 'system("cat", L)', 0, '')
+ -- null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {})
+
+ -- Correct behaviour
+ null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function()
+ 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\nE15: Invalid expression: L[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)
+ null_expr_test('does not crash line()', 'line(L)', 0, 0)
+ null_expr_test('does not crash count()', 'count(L, 1)', 0, 0)
+ null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1)
+ null_expr_test('is empty', 'empty(L)', 0, 1)
+ null_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10)
+ null_expr_test('has zero length', 'len(L)', 0, 0)
+ null_expr_test('is accepted as an empty list by max()', 'max(L)', 0, 0)
+ null_expr_test('is accepted as an empty list by min()', 'min(L)', 0, 0)
+ null_expr_test('is stringified correctly', 'string(L)', 0, '[]')
+ null_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]')
+ 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('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)
+ end)
+ describe('dict', function()
+ it('does not crash when indexing NULL dict', function()
+ eq('\nE716: Key not present in Dictionary: test\nE15: Invalid expression: v:_null_dict.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})
+ end)
+end)
diff --git a/test/functional/eval/sort_spec.lua b/test/functional/eval/sort_spec.lua
new file mode 100644
index 0000000000..4e5a0afba4
--- /dev/null
+++ b/test/functional/eval/sort_spec.lua
@@ -0,0 +1,41 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local eq = helpers.eq
+local NIL = helpers.NIL
+local eval = helpers.eval
+local clear = helpers.clear
+local meths = helpers.meths
+local funcs = helpers.funcs
+local command = helpers.command
+local exc_exec = helpers.exc_exec
+
+before_each(clear)
+
+describe('sort()', function()
+ it('errors out when sorting special values', function()
+ eq('Vim(call):E907: Using a special 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})
+ command('call insert(g:list, function("tr"))')
+ 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({
+ ['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)'))
+ end)
+end)
diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua
index f6279e85e8..adc1af9b8e 100644
--- a/test/functional/eval/string_spec.lua
+++ b/test/functional/eval/string_spec.lua
@@ -7,7 +7,6 @@ local eval = helpers.eval
local exc_exec = helpers.exc_exec
local redir_exec = helpers.redir_exec
local funcs = helpers.funcs
-local write_file = helpers.write_file
local NIL = helpers.NIL
local source = helpers.source
local dedent = helpers.dedent
@@ -105,10 +104,8 @@ describe('string() function', function()
end)
describe('used to represent funcrefs', function()
- local fname = 'Xtest-functional-eval-string_spec-fref-script.vim'
-
before_each(function()
- write_file(fname, [[
+ source([[
function Test1()
endfunction
@@ -120,11 +117,6 @@ describe('string() function', function()
let g:Test2_f = function('s:Test2')
]])
- command('source ' .. fname)
- end)
-
- after_each(function()
- os.remove(fname)
end)
it('dumps references to built-in functions', function()
diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua
index 4353619ff0..b3c4cd07eb 100644
--- a/test/functional/eval/timer_spec.lua
+++ b/test/functional/eval/timer_spec.lua
@@ -49,7 +49,7 @@ describe('timers', function()
it('works with zero timeout', function()
-- timer_start does still not invoke the callback immediately
eq(0,eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]"))
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, 400)
eq(1000,eval("g:val"))
end)
diff --git a/test/functional/eval/writefile_spec.lua b/test/functional/eval/writefile_spec.lua
index 3052c616e0..2f84114b9b 100644
--- a/test/functional/eval/writefile_spec.lua
+++ b/test/functional/eval/writefile_spec.lua
@@ -80,6 +80,13 @@ describe('writefile()', function()
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('\0a\0b\0', read_file(fname))
+ 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('\0a\0b\0', read_file(fname))
@@ -115,6 +122,8 @@ describe('writefile()', function()
eq('\nE729: using Funcref as a String',
redir_exec(('call writefile(%s)'):format(args:format('function("tr")'))))
end
+ eq('\nE5060: Unknown flag: «»',
+ redir_exec(('call writefile([], "%s", "bs«»")'):format(fname)))
eq('TEST', read_file(fname))
end)