diff options
Diffstat (limited to 'test/functional/eval')
-rw-r--r-- | test/functional/eval/api_functions_spec.lua | 3 | ||||
-rw-r--r-- | test/functional/eval/backtick_expansion_spec.lua | 16 | ||||
-rw-r--r-- | test/functional/eval/buf_functions_spec.lua | 5 | ||||
-rw-r--r-- | test/functional/eval/execute_spec.lua | 25 | ||||
-rw-r--r-- | test/functional/eval/fnamemodify_spec.lua | 39 | ||||
-rw-r--r-- | test/functional/eval/getline_spec.lua | 39 | ||||
-rw-r--r-- | test/functional/eval/has_spec.lua | 6 | ||||
-rw-r--r-- | test/functional/eval/hostname_spec.lua | 5 | ||||
-rw-r--r-- | test/functional/eval/input_spec.lua | 25 | ||||
-rw-r--r-- | test/functional/eval/interrupt_spec.lua | 61 | ||||
-rw-r--r-- | test/functional/eval/match_functions_spec.lua | 94 | ||||
-rw-r--r-- | test/functional/eval/msgpack_functions_spec.lua | 5 | ||||
-rw-r--r-- | test/functional/eval/null_spec.lua | 101 | ||||
-rw-r--r-- | test/functional/eval/server_spec.lua | 76 | ||||
-rw-r--r-- | test/functional/eval/system_spec.lua | 77 |
15 files changed, 456 insertions, 121 deletions
diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index fea4a87a26..6f440c7d82 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -106,7 +106,8 @@ describe('api functions', function() it('have metadata accessible with api_info()', function() local api_keys = eval("sort(keys(api_info()))") - eq({'error_types', 'functions', 'types', 'ui_events', 'version'}, api_keys) + eq({'error_types', 'functions', 'types', + 'ui_events', 'ui_options', 'version'}, api_keys) end) it('are highlighted by vim.vim syntax file', function() diff --git a/test/functional/eval/backtick_expansion_spec.lua b/test/functional/eval/backtick_expansion_spec.lua index 81e8e295fa..b1b44cfa8b 100644 --- a/test/functional/eval/backtick_expansion_spec.lua +++ b/test/functional/eval/backtick_expansion_spec.lua @@ -21,11 +21,19 @@ describe("backtick expansion", function() end) it("with default 'shell'", function() - if helpers.pending_win32(pending) then return end -- Need win32 shell fixes - command(":silent args `echo ***2`") + if helpers.iswin() then + command(":silent args `dir /b *2`") + else + command(":silent args `echo ***2`") + end eq({ "file2", }, eval("argv()")) - command(":silent args `echo */*4`") - eq({ "subdir/file4", }, eval("argv()")) + if helpers.iswin() then + 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()")) + end end) it("with shell=fish", function() diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua index db50874c53..7de58766b9 100644 --- a/test/functional/eval/buf_functions_spec.lua +++ b/test/functional/eval/buf_functions_spec.lua @@ -14,6 +14,7 @@ local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths local curtabmeths = helpers.curtabmeths local get_pathsep = helpers.get_pathsep +local rmdir = helpers.rmdir local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' @@ -61,7 +62,7 @@ describe('bufname() function', function() lfs.mkdir(dirname) end) after_each(function() - lfs.rmdir(dirname) + rmdir(dirname) end) it('returns expected buffer name', function() eq('', funcs.bufname('%')) -- Buffer has no name yet @@ -143,7 +144,7 @@ describe('bufwinnr() function', function() lfs.mkdir(dirname) end) after_each(function() - lfs.rmdir(dirname) + rmdir(dirname) end) it('returns expected window number', function() eq(1, funcs.bufwinnr('%')) diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua index 91966ed3dd..925e311c7d 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/eval/execute_spec.lua @@ -9,6 +9,7 @@ local funcs = helpers.funcs local Screen = require('test.functional.ui.screen') local command = helpers.command local feed = helpers.feed +local iswin = helpers.iswin describe('execute()', function() before_each(clear) @@ -105,22 +106,30 @@ describe('execute()', function() end) it('does not corrupt the command display #5422', function() - local screen = Screen.new(70, 5) + local screen = Screen.new(70, 7) screen:attach() feed(':echo execute("hi ErrorMsg")<CR>') screen:expect([[ - ~ | - ~ | + | + {1:~ }| + {1:~ }| + {2: }| :echo execute("hi ErrorMsg") | ErrorMsg xxx ctermfg=15 ctermbg=1 guifg=White guibg=Red | - Press ENTER or type command to continue^ | - ]]) + {3:Press ENTER or type command to continue}^ | + ]], { + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {bold = true, reverse = true}, + [3] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) feed('<CR>') end) - -- This matches Vim behavior. - it('does not capture shell-command output', function() - eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"')) + -- This deviates from vim behavior, but is consistent + -- with how nvim currently displays the output. + it('does capture shell-command output', function() + local win_lf = iswin() and '\13' or '' + eq('\n:!echo foo\r\n\nfoo'..win_lf..'\n', funcs.execute('!echo foo')) end) describe('{silent} argument', function() diff --git a/test/functional/eval/fnamemodify_spec.lua b/test/functional/eval/fnamemodify_spec.lua new file mode 100644 index 0000000000..fe6b50a544 --- /dev/null +++ b/test/functional/eval/fnamemodify_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local eq = helpers.eq +local iswin = helpers.iswin +local fnamemodify = helpers.funcs.fnamemodify +local command = helpers.command +local write_file = helpers.write_file + +describe('fnamemodify()', function() + setup(function() + write_file('Xtest-fnamemodify.txt', [[foobar]]) + end) + + before_each(clear) + + teardown(function() + os.remove('Xtest-fnamemodify.txt') + end) + + it('works', function() + local root = helpers.pathroot() + eq(root, fnamemodify([[/]], ':p:h')) + eq(root, fnamemodify([[/]], ':p')) + if iswin() then + eq(root, fnamemodify([[\]], ':p:h')) + eq(root, fnamemodify([[\]], ':p')) + command('set shellslash') + root = string.sub(root, 1, -2)..'/' + eq(root, fnamemodify([[\]], ':p:h')) + eq(root, fnamemodify([[\]], ':p')) + eq(root, fnamemodify([[/]], ':p:h')) + eq(root, fnamemodify([[/]], ':p')) + end + end) + + it(':8 works', function() + eq('Xtest-fnamemodify.txt', fnamemodify([[Xtest-fnamemodify.txt]], ':8')) + end) +end) diff --git a/test/functional/eval/getline_spec.lua b/test/functional/eval/getline_spec.lua new file mode 100644 index 0000000000..3c56bde094 --- /dev/null +++ b/test/functional/eval/getline_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) + +local call = helpers.call +local clear = helpers.clear +local eq = helpers.eq +local expect = helpers.expect + +describe('getline()', function() + before_each(function() + clear() + call('setline', 1, {'a', 'b', 'c'}) + expect([[ + a + b + c]]) + end) + + it('returns empty string for invalid line', function() + eq('', call('getline', -1)) + eq('', call('getline', 0)) + eq('', call('getline', 4)) + end) + + it('returns empty list for invalid range', function() + eq({}, call('getline', 2, 1)) + eq({}, call('getline', -1, 1)) + eq({}, call('getline', 4, 4)) + end) + + it('returns value of valid line', function() + eq('b', call('getline', 2)) + eq('a', call('getline', '.')) + end) + + it('returns value of valid range', function() + eq({'a', 'b'}, call('getline', 1, 2)) + eq({'a', 'b', 'c'}, call('getline', 1, 4)) + end) +end) diff --git a/test/functional/eval/has_spec.lua b/test/functional/eval/has_spec.lua index 78c4e08fde..a3af2d1a20 100644 --- a/test/functional/eval/has_spec.lua +++ b/test/functional/eval/has_spec.lua @@ -57,4 +57,10 @@ describe('has()', function() eq(0, funcs.has("unnamedplus")) end end) + + it('"wsl"', function() + if 1 == funcs.has('win32') or 1 == funcs.has('mac') then + eq(0, funcs.has('wsl')) + end + end) end) diff --git a/test/functional/eval/hostname_spec.lua b/test/functional/eval/hostname_spec.lua index 6d5b64b929..6112cf64e3 100644 --- a/test/functional/eval/hostname_spec.lua +++ b/test/functional/eval/hostname_spec.lua @@ -1,7 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) +local eq = helpers.eq local ok = helpers.ok local call = helpers.call local clear = helpers.clear +local iswin = helpers.iswin describe('hostname()', function() before_each(clear) @@ -11,7 +13,8 @@ describe('hostname()', function() ok(string.len(actual) > 0) if call('executable', 'hostname') == 1 then local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '') - helpers.eq(expected, actual) + eq((iswin() and expected:upper() or expected), + (iswin() and actual:upper() or actual)) end end) end) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 1e6b107c60..777f49462d 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -58,6 +58,7 @@ before_each(function() RBP2={background=Screen.colors.Yellow}, RBP3={background=Screen.colors.Green}, RBP4={background=Screen.colors.Blue}, + SEP={bold = true, reverse = true}, }) end) @@ -65,9 +66,9 @@ describe('input()', function() it('works with multiline prompts', function() feed([[:call input("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Test | Foo^ | ]]) @@ -75,9 +76,9 @@ describe('input()', function() it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call input("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| {T:Test} | {T:Foo}^ | ]]) @@ -242,17 +243,17 @@ describe('input()', function() it('is not hidden by :silent', function() feed([[:silent call input('Foo: ')<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Foo: ^ | | ]]) feed('Bar') screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Foo: Bar^ | | ]]) @@ -263,9 +264,9 @@ describe('inputdialog()', function() it('works with multiline prompts', function() feed([[:call inputdialog("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| Test | Foo^ | ]]) @@ -273,9 +274,9 @@ describe('inputdialog()', function() it('works with multiline prompts and :echohl', function() feed([[:echohl Test | call inputdialog("Test\nFoo")<CR>]]) screen:expect([[ + | {EOB:~ }| - {EOB:~ }| - {EOB:~ }| + {SEP: }| {T:Test} | {T:Foo}^ | ]]) diff --git a/test/functional/eval/interrupt_spec.lua b/test/functional/eval/interrupt_spec.lua new file mode 100644 index 0000000000..7f4ca95317 --- /dev/null +++ b/test/functional/eval/interrupt_spec.lua @@ -0,0 +1,61 @@ +local helpers = require('test.functional.helpers')(after_each) + +local command = helpers.command +local meths = helpers.meths +local clear = helpers.clear +local sleep = helpers.sleep +local wait = helpers.wait +local feed = helpers.feed +local eq = helpers.eq + +local dur +local min_dur = 8 +local len = 131072 + +describe('List support code', function() + 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. + setup(function() + clear() + dur = 0 + while true do + command(([[ + let rt = reltime() + let bl = range(%u) + let dur = reltimestr(reltime(rt)) + ]]):format(len)) + dur = tonumber(meths.get_var('dur')) + if dur >= min_dur then + -- print(('Using len %u, dur %g'):format(len, dur)) + break + else + len = len * 2 + end + end + end) + it('allows interrupting copy', function() + feed(':let t_rt = reltime()<CR>:let t_bl = copy(bl)<CR>') + sleep(min_dur / 16 * 1000) + feed('<C-c>') + wait() + command('let t_dur = reltimestr(reltime(t_rt))') + local t_dur = tonumber(meths.get_var('t_dur')) + if t_dur >= dur / 8 then + eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) + end + end) + it('allows interrupting join', function() + feed(':let t_rt = reltime()<CR>:let t_j = join(bl)<CR>') + sleep(min_dur / 16 * 1000) + feed('<C-c>') + wait() + command('let t_dur = reltimestr(reltime(t_rt))') + local t_dur = tonumber(meths.get_var('t_dur')) + print(('t_dur: %g'):format(t_dur)) + if t_dur >= dur / 8 then + eq(nil, ('Took too long to cancel: %g >= %g'):format(t_dur, dur / 8)) + end + end) +end) diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index 3150d89f62..7989b22b5e 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -1,9 +1,11 @@ local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local eq = helpers.eq local clear = helpers.clear local funcs = helpers.funcs local command = helpers.command +local exc_exec = helpers.exc_exec before_each(clear) @@ -59,3 +61,95 @@ describe('matchadd()', function() }}, 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])')) + 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()) + funcs.matchdelete(4) + 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()) + 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()) + funcs.matchdelete(4) + 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()) + 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()) + 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([[ + ^a{1:b}cdef | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]], {[1] = {foreground = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.Blue1}}) + end) +end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index b241635dfe..a8a413f68b 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -463,7 +463,8 @@ describe('msgpackparse() function', function() eval(cmd) eval(cmd) -- do it again (try to force segfault) local api_info = eval(cmd) -- do it again - eq({'error_types', 'functions', 'types', 'ui_events', 'version'}, api_info) + eq({'error_types', 'functions', 'types', + 'ui_events', 'ui_options', 'version'}, api_info) end) it('fails when called with no arguments', function() @@ -628,7 +629,7 @@ describe('msgpackdump() function', function() it('fails to dump a recursive (key) map in a special dict', function() command('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}') command('call add(todump._VAL, [todump, 0])') - eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 1', + eq('Vim(call):E5005: Unable to dump msgpackdump() argument, index 0: container references itself in index 0', exc_exec('call msgpackdump([todump])')) end) diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 6fd30caec9..afe999e1fa 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -41,43 +41,9 @@ describe('NULL', function() 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) + -- FIXME Should error out with different message + null_test('makes :unlet act as if it is not a list', ':unlet L[0]', + 'Vim(unlet):E689: Can only index a List or Dictionary') -- Subjectable behaviour @@ -85,20 +51,19 @@ describe('NULL', function() 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('can be indexed with error message for empty list', 'L[0]', + 'E684: list index out of range: 0\nE15: Invalid expression: L[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(1, L)', 0, 0, function() 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)) + 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, {}) @@ -111,6 +76,8 @@ describe('NULL', function() 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('does not crash map()', 'map(L, "v:val")', 0, {}) + null_expr_test('does not crash filter()', 'filter(L, "1")', 0, {}) 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) @@ -126,6 +93,44 @@ describe('NULL', function() 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 mouse (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")', + '', '\n', 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) + null_test('is accepted by :cexpr', 'cexpr L', 0) + null_test('is accepted by :lexpr', 'lexpr L', 0) end) describe('dict', function() it('does not crash when indexing NULL dict', function() @@ -134,5 +139,9 @@ describe('NULL', function() 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('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) + null_expr_test('makes filter() return v:_null_dict', 'filter(D, "1") is# D', 0, 1) end) end) diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua index 393616838e..4e4aed864b 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -1,31 +1,40 @@ - local helpers = require('test.functional.helpers')(after_each) local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval local command = helpers.command local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths -local os_name = helpers.os_name +local iswin = helpers.iswin +local ok = helpers.ok +local matches = helpers.matches local function clear_serverlist() - for _, server in pairs(funcs.serverlist()) do - funcs.serverstop(server) - end + for _, server in pairs(funcs.serverlist()) do + funcs.serverstop(server) + end end -describe('serverstart(), serverstop()', function() +describe('server', function() before_each(clear) - it('sets $NVIM_LISTEN_ADDRESS on first invocation', function() + it('serverstart() sets $NVIM_LISTEN_ADDRESS on first invocation', function() -- Unset $NVIM_LISTEN_ADDRESS command('let $NVIM_LISTEN_ADDRESS = ""') local s = eval('serverstart()') assert(s ~= nil and s:len() > 0, "serverstart() returned empty") eq(s, eval('$NVIM_LISTEN_ADDRESS')) - command("call serverstop('"..s.."')") + eq(1, eval("serverstop('"..s.."')")) eq('', eval('$NVIM_LISTEN_ADDRESS')) end) - it('sets v:servername _only_ on nvim startup unless all servers are stopped', + it('sets new v:servername if $NVIM_LISTEN_ADDRESS is invalid', function() + clear({env={NVIM_LISTEN_ADDRESS='.'}}) + eq('.', eval('$NVIM_LISTEN_ADDRESS')) + local servers = funcs.serverlist() + eq(1, #servers) + ok(string.len(servers[1]) > 4) -- Like /tmp/nvim…/… or \\.\pipe\… + end) + + it('sets v:servername at startup or if all servers were stopped', function() local initial_server = meths.get_vvar('servername') assert(initial_server ~= nil and initial_server:len() > 0, @@ -38,24 +47,23 @@ describe('serverstart(), serverstop()', function() neq(initial_server, s) -- serverstop() does _not_ modify v:servername... - funcs.serverstop(s) + eq(1, funcs.serverstop(s)) eq(initial_server, meths.get_vvar('servername')) -- ...unless we stop _all_ servers. - funcs.serverstop(funcs.serverlist()[1]) + eq(1, funcs.serverstop(funcs.serverlist()[1])) eq('', meths.get_vvar('servername')) -- v:servername will take the next available server. - local servername = (os_name() == 'windows' - and [[\\.\pipe\Xtest-functional-server-pipe]] - or 'Xtest-functional-server-socket') + local servername = (iswin() and [[\\.\pipe\Xtest-functional-server-pipe]] + or 'Xtest-functional-server-socket') funcs.serverstart(servername) eq(servername, meths.get_vvar('servername')) end) - it('serverstop() ignores invalid input', function() - command("call serverstop('')") - command("call serverstop('bogus-socket-name')") + it('serverstop() returns false for invalid input', function() + eq(0, eval("serverstop('')")) + eq(0, eval("serverstop('bogus-socket-name')")) end) it('parses endpoints correctly', function() @@ -96,17 +104,13 @@ describe('serverstart(), serverstop()', function() funcs.serverstart('127.0.0.1:65536') -- invalid port eq({}, funcs.serverlist()) end) -end) - -describe('serverlist()', function() - before_each(clear) - it('returns the list of servers', function() + it('serverlist() returns the list of servers', function() -- There should already be at least one server. local n = eval('len(serverlist())') - -- Add a few - local servs = (os_name() == 'windows' + -- Add some servers. + local servs = (iswin() and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] } or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] }) for _, s in ipairs(servs) do @@ -120,9 +124,31 @@ describe('serverlist()', function() -- The new servers should be at the end of the list. for i = 1, #servs do eq(servs[i], new_servs[i + n]) - command("call serverstop('"..servs[i].."')") + eq(1, eval("serverstop('"..servs[i].."')")) end -- After serverstop() the servers should NOT be in the list. eq(n, eval('len(serverlist())')) end) end) + +describe('startup --listen', function() + it('validates', function() + clear() + + local cmd = { unpack(helpers.nvim_argv) } + table.insert(cmd, '--listen') + matches('nvim.*: Argument missing after: "%-%-listen"', funcs.system(cmd)) + + cmd = { unpack(helpers.nvim_argv) } + table.insert(cmd, '--listen2') + matches('nvim.*: Garbage after option argument: "%-%-listen2"', funcs.system(cmd)) + end) + + it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function() + local addr = (iswin() and [[\\.\pipe\Xtest-listen-pipe]] + or 'Xtest-listen-pipe') + clear({ env={ NVIM_LISTEN_ADDRESS='Xtest-env-pipe' }, + args={ '--listen', addr } }) + eq(addr, meths.get_vvar('servername')) + end) +end) diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 7e213e2156..201426c40b 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -5,6 +5,7 @@ local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, helpers.feed, helpers.nvim local command = helpers.command +local exc_exec = helpers.exc_exec local iswin = helpers.iswin local Screen = require('test.functional.ui.screen') @@ -89,7 +90,9 @@ describe('system()', function() end) it('does NOT run in shell', function() - if not iswin() then + if iswin() then + eq("%PATH%\n", eval("system(['powershell', '-NoProfile', '-NoLogo', '-ExecutionPolicy', 'RemoteSigned', '-Command', 'echo', '%PATH%'])")) + else eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])")) end end) @@ -117,33 +120,47 @@ describe('system()', function() end end) - describe('executes shell function if passed a string', function() + describe('executes shell function', function() local screen before_each(function() - clear() - screen = Screen.new() - screen:attach() + clear() + screen = Screen.new() + screen:attach() end) after_each(function() - screen:detach() + screen:detach() end) if iswin() then + local function test_more() + eq('root = true', eval([[get(split(system('"more" ".editorconfig"'), "\n"), 0, '')]])) + end + local function test_shell_unquoting() + eval([[system('"ping" "-n" "1" "127.0.0.1"')]]) + eq(0, eval('v:shell_error')) + eq('"a b"\n', eval([[system('cmd /s/c "cmd /s/c "cmd /s/c "echo "a b""""')]])) + eq('"a b"\n', eval([[system('powershell -NoProfile -NoLogo -ExecutionPolicy RemoteSigned -Command echo ''\^"a b\^"''')]])) + end + it('with shell=cmd.exe', function() command('set shell=cmd.exe') eq('""\n', eval([[system('echo ""')]])) eq('"a b"\n', eval([[system('echo "a b"')]])) eq('a \nb\n', eval([[system('echo a & echo b')]])) eq('a \n', eval([[system('echo a 2>&1')]])) + test_more() eval([[system('cd "C:\Program Files"')]]) eq(0, eval('v:shell_error')) + test_shell_unquoting() end) it('with shell=cmd', function() command('set shell=cmd') eq('"a b"\n', eval([[system('echo "a b"')]])) + test_more() + test_shell_unquoting() end) it('with shell=$COMSPEC', function() @@ -151,6 +168,8 @@ describe('system()', function() if comspecshell == 'cmd.exe' then command('set shell=$COMSPEC') eq('"a b"\n', eval([[system('echo "a b"')]])) + test_more() + test_shell_unquoting() else pending('$COMSPEC is not cmd.exe: ' .. comspecshell) end @@ -184,8 +203,10 @@ describe('system()', function() ]]) end) - it('`yes` and is interrupted with CTRL-C', function() - feed(':call system("yes")<cr>') + it('`yes` interrupted with CTRL-C', function() + feed(':call system("' .. (iswin() + and 'for /L %I in (1,0,2) do @echo y' + or 'yes') .. '")<cr>') screen:expect([[ | ~ | @@ -200,8 +221,11 @@ describe('system()', function() ~ | ~ | ~ | - :call system("yes") | - ]]) +]] .. (iswin() + and [[ + :call system("for /L %I in (1,0,2) do @echo y") |]] + or [[ + :call system("yes") |]])) feed('<c-c>') screen:expect([[ ^ | @@ -231,6 +255,8 @@ describe('system()', function() end end) it('to backgrounded command does not crash', function() + -- cmd.exe doesn't background a command with & + if iswin() then return end -- This is indeterminate, just exercise the codepath. May get E5677. feed_command('call system("echo -n echoed &")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") @@ -246,14 +272,20 @@ describe('system()', function() eq("input", eval('system("cat -", "input")')) end) it('to backgrounded command does not crash', function() + -- cmd.exe doesn't background a command with & + if iswin() then return end -- This is indeterminate, just exercise the codepath. May get E5677. - feed_command('call system("cat - &")') + feed_command('call system("cat - &", "input")') local v_errnum = string.match(eval("v:errmsg"), "^E%d*:") if v_errnum then eq("E5677:", v_errnum) end eq(2, eval("1+1")) -- Still alive? end) + it('works with an empty string', function() + eq("test\n", eval('system("echo test", "")')) + eq(2, eval("1+1")) -- Still alive? + end) end) describe('passing a lot of input', function() @@ -271,9 +303,12 @@ describe('system()', function() end) end) - describe('input passed as Number', function() - it('stringifies the input', function() - eq('1', eval('system("cat", 1)')) + describe('Number input', 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)')) end) end) @@ -284,7 +319,7 @@ describe('system()', function() after_each(delete_file(fname)) it('replaces NULs by SOH characters', function() - eq('part1\001part2\001part3\n', eval('system("cat '..fname..'")')) + eq('part1\001part2\001part3\n', eval([[system('"cat" "]]..fname..[["')]])) end) end) @@ -351,7 +386,7 @@ describe('systemlist()', function() end end) - describe('exectues shell function', function() + describe('executes shell function', function() local screen before_each(function() @@ -384,7 +419,7 @@ describe('systemlist()', function() ]]) end) - it('`yes` and is interrupted with CTRL-C', function() + it('`yes` interrupted with CTRL-C', function() feed(':call systemlist("yes | xargs")<cr>') screen:expect([[ | @@ -442,12 +477,14 @@ describe('systemlist()', function() describe('with output containing NULs', function() local fname = 'Xtest' - before_each(create_file_with_nuls(fname)) + before_each(function() + command('set ff=unix') + create_file_with_nuls(fname)() + end) after_each(delete_file(fname)) it('replaces NULs by newline characters', function() - if helpers.pending_win32(pending) then return end - eq({'part1\npart2\npart3'}, eval('systemlist("cat '..fname..'")')) + eq({'part1\npart2\npart3'}, eval([[systemlist('"cat" "]]..fname..[["')]])) end) end) |