diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/functional/api/keymap_spec.lua | 246 | ||||
-rw-r--r-- | test/functional/api/server_requests_spec.lua | 73 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 35 | ||||
-rw-r--r-- | test/functional/eval/input_spec.lua | 354 | ||||
-rw-r--r-- | test/functional/eval/map_functions_spec.lua | 120 | ||||
-rw-r--r-- | test/functional/eval/server_spec.lua | 47 | ||||
-rw-r--r-- | test/functional/ex_cmds/encoding_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/fixtures/tty-test.c | 9 | ||||
-rw-r--r-- | test/functional/helpers.lua | 5 | ||||
-rw-r--r-- | test/functional/ui/cursor_spec.lua | 4 | ||||
-rw-r--r-- | test/unit/eval/typval_spec.lua | 49 | ||||
-rw-r--r-- | test/unit/os/env_spec.lua | 2 | ||||
-rw-r--r-- | test/unit/path_spec.lua | 55 |
13 files changed, 973 insertions, 30 deletions
diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua new file mode 100644 index 0000000000..833e0d2f3c --- /dev/null +++ b/test/functional/api/keymap_spec.lua @@ -0,0 +1,246 @@ + +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local command = helpers.command +local curbufmeths = helpers.curbufmeths +local eq = helpers.eq +local funcs = helpers.funcs +local meths = helpers.meths +local source = helpers.source + +local function local_copy(t) + local copy = {} + for k,v in pairs(t) do + copy[k] = v + end + return copy +end + +describe('get_keymap', function() + before_each(clear) + + -- Basic mapping and table to be used to describe results + local foo_bar_string = 'nnoremap foo bar' + local foo_bar_map_table = { + lhs='foo', + silent=0, + rhs='bar', + expr=0, + sid=0, + buffer=0, + nowait=0, + mode='n', + noremap=1, + } + + it('returns empty list when no map', function() + eq({}, meths.get_keymap('n')) + end) + + it('returns list of all applicable mappings', function() + command(foo_bar_string) + -- Only one mapping available + -- Should be the same as the dictionary we supplied earlier + -- and the dictionary you would get from maparg + -- since this is a global map, and not script local + eq({foo_bar_map_table}, meths.get_keymap('n')) + eq({funcs.maparg('foo', 'n', false, true)}, + meths.get_keymap('n') + ) + + -- Add another mapping + command('nnoremap foo_longer bar_longer') + local foolong_bar_map_table = local_copy(foo_bar_map_table) + foolong_bar_map_table['lhs'] = 'foo_longer' + foolong_bar_map_table['rhs'] = 'bar_longer' + + eq({foolong_bar_map_table, foo_bar_map_table}, + meths.get_keymap('n') + ) + + -- Remove a mapping + command('unmap foo_longer') + eq({foo_bar_map_table}, + meths.get_keymap('n') + ) + end) + + it('works for other modes', function() + -- Add two mappings, one in insert and one normal + -- We'll only check the insert mode one + command('nnoremap not_going to_check') + + command('inoremap foo bar') + -- The table will be the same except for the mode + local insert_table = local_copy(foo_bar_map_table) + insert_table['mode'] = 'i' + + eq({insert_table}, meths.get_keymap('i')) + end) + + it('considers scope', function() + -- change the map slightly + command('nnoremap foo_longer bar_longer') + local foolong_bar_map_table = local_copy(foo_bar_map_table) + foolong_bar_map_table['lhs'] = 'foo_longer' + foolong_bar_map_table['rhs'] = 'bar_longer' + + local buffer_table = local_copy(foo_bar_map_table) + buffer_table['buffer'] = 1 + + command('nnoremap <buffer> foo bar') + + -- The buffer mapping should not show up + eq({foolong_bar_map_table}, meths.get_keymap('n')) + eq({buffer_table}, curbufmeths.get_keymap('n')) + end) + + it('considers scope for overlapping maps', function() + command('nnoremap foo bar') + + local buffer_table = local_copy(foo_bar_map_table) + buffer_table['buffer'] = 1 + + command('nnoremap <buffer> foo bar') + + eq({foo_bar_map_table}, meths.get_keymap('n')) + eq({buffer_table}, curbufmeths.get_keymap('n')) + end) + + it('can retrieve mapping for different buffers', function() + local original_buffer = curbufmeths.get_number() + -- Place something in each of the buffers to make sure they stick around + -- and set hidden so we can leave them + command('set hidden') + command('new') + command('normal! ihello 2') + command('new') + command('normal! ihello 3') + + local final_buffer = curbufmeths.get_number() + + command('nnoremap <buffer> foo bar') + -- Final buffer will have buffer mappings + local buffer_table = local_copy(foo_bar_map_table) + buffer_table['buffer'] = final_buffer + eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n')) + eq({buffer_table}, meths.buf_get_keymap(0, 'n')) + + command('buffer ' .. original_buffer) + eq(original_buffer, curbufmeths.get_number()) + -- Original buffer won't have any mappings + eq({}, meths.get_keymap('n')) + eq({}, curbufmeths.get_keymap('n')) + eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n')) + end) + + -- Test toggle switches for basic options + -- @param option The key represented in the `maparg()` result dict + local function global_and_buffer_test(map, + option, + option_token, + global_on_result, + buffer_on_result, + global_off_result, + buffer_off_result, + new_windows) + + local function make_new_windows(number_of_windows) + if new_windows == nil then + return nil + end + + for _=1,number_of_windows do + command('new') + end + end + + local mode = string.sub(map, 1,1) + -- Don't run this for the <buffer> mapping, since it doesn't make sense + if option_token ~= '<buffer>' then + it(string.format( 'returns %d for the key "%s" when %s is used globally with %s (%s)', + global_on_result, option, option_token, map, mode), function() + make_new_windows(new_windows) + command(map .. ' ' .. option_token .. ' foo bar') + local result = meths.get_keymap(mode)[1][option] + eq(global_on_result, result) + end) + end + + it(string.format('returns %d for the key "%s" when %s is used for buffers with %s (%s)', + buffer_on_result, option, option_token, map, mode), function() + make_new_windows(new_windows) + command(map .. ' <buffer> ' .. option_token .. ' foo bar') + local result = curbufmeths.get_keymap(mode)[1][option] + eq(buffer_on_result, result) + end) + + -- Don't run these for the <buffer> mapping, since it doesn't make sense + if option_token ~= '<buffer>' then + it(string.format('returns %d for the key "%s" when %s is not used globally with %s (%s)', + global_off_result, option, option_token, map, mode), function() + make_new_windows(new_windows) + command(map .. ' baz bat') + local result = meths.get_keymap(mode)[1][option] + eq(global_off_result, result) + end) + + it(string.format('returns %d for the key "%s" when %s is not used for buffers with %s (%s)', + buffer_off_result, option, option_token, map, mode), function() + make_new_windows(new_windows) + command(map .. ' <buffer> foo bar') + + local result = curbufmeths.get_keymap(mode)[1][option] + eq(buffer_off_result, result) + end) + end + end + + -- Standard modes and returns the same values in the dictionary as maparg() + local mode_list = {'nnoremap', 'nmap', 'imap', 'inoremap', 'cnoremap'} + for mode in pairs(mode_list) do + global_and_buffer_test(mode_list[mode], 'silent', '<silent>', 1, 1, 0, 0) + global_and_buffer_test(mode_list[mode], 'nowait', '<nowait>', 1, 1, 0, 0) + global_and_buffer_test(mode_list[mode], 'expr', '<expr>', 1, 1, 0, 0) + end + + -- noremap will now be 2 if script was used, which is not the same as maparg() + global_and_buffer_test('nmap', 'noremap', '<script>', 2, 2, 0, 0) + global_and_buffer_test('nnoremap', 'noremap', '<script>', 2, 2, 1, 1) + + -- buffer will return the buffer ID, which is not the same as maparg() + -- Three of these tests won't run + global_and_buffer_test('nnoremap', 'buffer', '<buffer>', nil, 3, nil, nil, 2) + + it('returns script numbers for global maps', function() + source([[ + function! s:maparg_test_function() abort + return 'testing' + endfunction + + nnoremap fizz :call <SID>maparg_test_function()<CR> + ]]) + local sid_result = meths.get_keymap('n')[1]['sid'] + eq(1, sid_result) + eq('testing', meths.call_function('<SNR>' .. sid_result .. '_maparg_test_function', {})) + end) + + it('returns script numbers for buffer maps', function() + source([[ + function! s:maparg_test_function() abort + return 'testing' + endfunction + + nnoremap <buffer> fizz :call <SID>maparg_test_function()<CR> + ]]) + local sid_result = curbufmeths.get_keymap('n')[1]['sid'] + eq(1, sid_result) + eq('testing', meths.call_function('<SNR>' .. sid_result .. '_maparg_test_function', {})) + end) + + it('works with <F12> and others', function() + command('nnoremap <F12> :let g:maparg_test_var = 1<CR>') + eq('<F12>', meths.get_keymap('n')[1]['lhs']) + eq(':let g:maparg_test_var = 1<CR>', meths.get_keymap('n')[1]['rhs']) + end) +end) diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 658077b112..cf15062325 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -9,6 +9,8 @@ local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.fu local source, next_message = helpers.source, helpers.next_message local ok = helpers.ok local meths = helpers.meths +local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv +local set_session = helpers.set_session describe('server -> client', function() local cid @@ -225,4 +227,75 @@ describe('server -> client', function() end) end) + describe('when connecting to another nvim instance', function() + local function connect_test(server, mode, address) + local serverpid = funcs.getpid() + local client = spawn(nvim_argv) + set_session(client, true) + local clientpid = funcs.getpid() + neq(serverpid, clientpid) + local id = funcs.sockconnect(mode, address, {rpc=true}) + ok(id > 0) + + funcs.rpcrequest(id, 'nvim_set_current_line', 'hello') + local client_id = funcs.rpcrequest(id, 'nvim_get_api_info')[1] + + set_session(server, true) + eq(serverpid, funcs.getpid()) + eq('hello', meths.get_current_line()) + + -- method calls work both ways + funcs.rpcrequest(client_id, 'nvim_set_current_line', 'howdy!') + eq(id, funcs.rpcrequest(client_id, 'nvim_get_api_info')[1]) + + set_session(client, true) + eq(clientpid, funcs.getpid()) + eq('howdy!', meths.get_current_line()) + + server:close() + client:close() + end + + it('over a named pipe', function() + local server = spawn(nvim_argv) + set_session(server) + local address = funcs.serverlist()[1] + local first = string.sub(address,1,1) + ok(first == '/' or first == '\\') + connect_test(server, 'pipe', address) + end) + + it('to an ip adress', function() + local server = spawn(nvim_argv) + set_session(server) + local address = funcs.serverstart("127.0.0.1:") + eq('127.0.0.1:', string.sub(address,1,10)) + connect_test(server, 'tcp', address) + end) + + it('to a hostname', function() + local server = spawn(nvim_argv) + set_session(server) + local address = funcs.serverstart("localhost:") + eq('localhost:', string.sub(address,1,10)) + connect_test(server, 'tcp', address) + end) + end) + + describe('when connecting to its own pipe adress', function() + it('it does not deadlock', function() + local address = funcs.serverlist()[1] + local first = string.sub(address,1,1) + ok(first == '/' or first == '\\') + local serverpid = funcs.getpid() + + local id = funcs.sockconnect('pipe', address, {rpc=true}) + + funcs.rpcrequest(id, 'nvim_set_current_line', 'hello') + eq('hello', meths.get_current_line()) + eq(serverpid, funcs.rpcrequest(id, "nvim_eval", "getpid()")) + + eq(id, funcs.rpcrequest(id, 'nvim_get_api_info')[1]) + end) + end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 282ecbfd87..161682b973 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -81,6 +81,36 @@ describe('api', function() end) end) + describe('nvim_execute_lua', function() + it('works', function() + meths.execute_lua('vim.api.nvim_set_var("test", 3)', {}) + eq(3, meths.get_var('test')) + + eq(17, meths.execute_lua('a, b = ...\nreturn a + b', {10,7})) + + eq(NIL, meths.execute_lua('function xx(a,b)\nreturn a..b\nend',{})) + eq("xy", meths.execute_lua('return xx(...)', {'x','y'})) + end) + + it('reports errors', function() + eq({false, 'Error loading lua: [string "<nvim>"]:1: '.. + "'=' expected near '+'"}, + meth_pcall(meths.execute_lua, 'a+*b', {})) + + eq({false, 'Error loading lua: [string "<nvim>"]:1: '.. + "unexpected symbol near '1'"}, + meth_pcall(meths.execute_lua, '1+2', {})) + + eq({false, 'Error loading lua: [string "<nvim>"]:1: '.. + "unexpected symbol"}, + meth_pcall(meths.execute_lua, 'aa=bb\0', {})) + + eq({false, 'Error executing lua: [string "<nvim>"]:1: '.. + "attempt to call global 'bork' (a nil value)"}, + meth_pcall(meths.execute_lua, 'bork()', {})) + end) + end) + describe('nvim_input', function() it("VimL error: does NOT fail, updates v:errmsg", function() local status, _ = pcall(nvim, "input", ":call bogus_fn()<CR>") @@ -338,6 +368,11 @@ describe('api', function() '<LeftMouse>', true, true, true)) end) + it('converts keycodes', function() + eq('\nx\27x\rx<x', helpers.nvim('replace_termcodes', + '<NL>x<Esc>x<CR>x<lt>x', true, true, true)) + end) + it('does not crash when transforming an empty string', function() -- Actually does not test anything, because current code will use NULL for -- an empty string. diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 393fc10175..74ad32bc6c 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -1,9 +1,13 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local eq = helpers.eq local feed = helpers.feed +local meths = helpers.meths local clear = helpers.clear +local source = helpers.source local command = helpers.command +local exc_exec = helpers.exc_exec local screen @@ -11,28 +15,352 @@ before_each(function() clear() screen = Screen.new(25, 5) screen:attach() + source([[ + hi Test ctermfg=Red guifg=Red term=bold + function CustomCompl(...) + return 'TEST' + endfunction + function CustomListCompl(...) + return ['FOO'] + endfunction + ]]) + screen:set_default_attr_ids({ + EOB={bold = true, foreground = Screen.colors.Blue1}, + T={foreground=Screen.colors.Red}, + }) end) describe('input()', function() - it('works correctly with multiline prompts', function() + it('works with multiline prompts', function() feed([[:call input("Test\nFoo")<CR>]]) screen:expect([[ - {1:~ }| - {1:~ }| - {1:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| 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') + it('works with multiline prompts and :echohl', function() 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}}) + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Test} | + {T:Foo}^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo}^ | + ]]) + end) + it('allows unequal numeric arguments when using multiple args', function() + command('echohl Test') + feed([[:call input(1, 2)<CR>]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('<BS>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + end) + it('allows unequal numeric values when using {opts} dictionary', function() + command('echohl Test') + meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) + feed([[:echo input(opts)<CR>]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('<BS>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + feed('<Esc>') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:3} | + ]]) + end) + it('works with redraw', function() + command('echohl Test') + meths.set_var('opts', {prompt='Foo>', default='Bar'}) + feed([[:echo inputdialog(opts)<CR>]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + feed('<BS>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + end) + it('allows omitting everything with dictionary argument', function() + command('echohl Test') + feed([[:call input({})<CR>]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ^ | + ]]) + end) + it('supports completion', function() + feed(':let var = input("", "", "custom,CustomCompl")<CR>') + feed('<Tab><CR>') + eq('TEST', meths.get_var('var')) + + feed(':let var = input({"completion": "customlist,CustomListCompl"})<CR>') + feed('<Tab><CR>') + eq('FOO', meths.get_var('var')) + end) + it('supports cancelreturn', function() + feed(':let var = input({"cancelreturn": "BAR"})<CR>') + feed('<Esc>') + eq('BAR', meths.get_var('var')) + end) + it('supports default string', function() + feed(':let var = input("", "DEF1")<CR>') + feed('<CR>') + eq('DEF1', meths.get_var('var')) + + feed(':let var = input({"default": "DEF2"})<CR>') + feed('<CR>') + eq('DEF2', meths.get_var('var')) + end) + it('errors out on invalid inputs', function() + eq('Vim(call):E730: using List as a String', + exc_exec('call input([])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input("", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input("", "", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input({"prompt": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input({"cancelreturn": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call input({"default": []})')) + eq('Vim(call):E730: using 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) +end) +describe('inputdialog()', function() + it('works with multiline prompts', function() + feed([[:call inputdialog("Test\nFoo")<CR>]]) + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + Test | + Foo^ | + ]]) + end) + it('works with multiline prompts and :echohl', function() + feed([[:echohl Test | call inputdialog("Test\nFoo")<CR>]]) + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Test} | + {T:Foo}^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo}^ | + ]]) + end) + it('allows unequal numeric arguments when using multiple args', function() + command('echohl Test') + feed([[:call inputdialog(1, 2)<CR>]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('<BS>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + end) + it('allows unequal numeric values when using {opts} dictionary', function() + command('echohl Test') + meths.set_var('opts', {prompt=1, default=2, cancelreturn=3}) + feed([[:echo input(opts)<CR>]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}2^ | + ]]) + feed('<BS>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:1}^ | + ]]) + feed('<Esc>') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:3} | + ]]) + end) + it('works with redraw', function() + command('echohl Test') + meths.set_var('opts', {prompt='Foo>', default='Bar'}) + feed([[:echo input(opts)<CR>]]) + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Bar^ | + ]]) + feed('<BS>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + command('redraw!') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {T:Foo>}Ba^ | + ]]) + end) + it('allows omitting everything with dictionary argument', function() + command('echohl Test') + feed(':echo inputdialog({})<CR>') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ^ | + ]]) + end) + it('supports completion', function() + feed(':let var = inputdialog({"completion": "customlist,CustomListCompl"})<CR>') + feed('<Tab><CR>') + eq('FOO', meths.get_var('var')) + end) + it('supports cancelreturn', function() + feed(':let var = inputdialog("", "", "CR1")<CR>') + feed('<Esc>') + eq('CR1', meths.get_var('var')) + + feed(':let var = inputdialog({"cancelreturn": "BAR"})<CR>') + feed('<Esc>') + eq('BAR', meths.get_var('var')) + end) + it('supports default string', function() + feed(':let var = inputdialog("", "DEF1")<CR>') + feed('<CR>') + eq('DEF1', meths.get_var('var')) + + feed(':let var = inputdialog({"default": "DEF2"})<CR>') + feed('<CR>') + eq('DEF2', meths.get_var('var')) + end) + it('errors out on invalid inputs', function() + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog([])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog("", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog("", "", [])')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog({"prompt": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog({"cancelreturn": []})')) + eq('Vim(call):E730: using List as a String', + exc_exec('call inputdialog({"default": []})')) + eq('Vim(call):E730: using 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) end) diff --git a/test/functional/eval/map_functions_spec.lua b/test/functional/eval/map_functions_spec.lua new file mode 100644 index 0000000000..a260522aa3 --- /dev/null +++ b/test/functional/eval/map_functions_spec.lua @@ -0,0 +1,120 @@ + +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local funcs = helpers.funcs +local nvim = helpers.nvim +local source = helpers.source + +describe('maparg()', function() + before_each(clear) + + local foo_bar_map_table = { + lhs='foo', + silent=0, + rhs='bar', + expr=0, + sid=0, + buffer=0, + nowait=0, + mode='n', + noremap=1, + } + + it('returns a dictionary', function() + nvim('command', 'nnoremap foo bar') + eq('bar', funcs.maparg('foo')) + eq(foo_bar_map_table, funcs.maparg('foo', 'n', false, true)) + end) + + it('returns 1 for silent when <silent> is used', function() + nvim('command', 'nnoremap <silent> foo bar') + eq(1, funcs.maparg('foo', 'n', false, true)['silent']) + + nvim('command', 'nnoremap baz bat') + eq(0, funcs.maparg('baz', 'n', false, true)['silent']) + end) + + it('returns an empty string when no map is present', function() + eq('', funcs.maparg('not a mapping')) + end) + + it('returns an empty dictionary when no map is present and dict is requested', function() + eq({}, funcs.maparg('not a mapping', 'n', false, true)) + end) + + it('returns the same value for noremap and <script>', function() + nvim('command', 'inoremap <script> hello world') + nvim('command', 'inoremap this that') + eq( + funcs.maparg('hello', 'i', false, true)['noremap'], + funcs.maparg('this', 'i', false, true)['noremap'] + ) + end) + + it('returns a boolean for buffer', function() + -- Open enough windows to know we aren't on buffer number 1 + nvim('command', 'new') + nvim('command', 'new') + nvim('command', 'new') + nvim('command', 'cnoremap <buffer> this that') + eq(1, funcs.maparg('this', 'c', false, true)['buffer']) + + -- Global will return 0 always + nvim('command', 'nnoremap other another') + eq(0, funcs.maparg('other', 'n', false, true)['buffer']) + end) + + it('returns script numbers', function() + source([[ + function! s:maparg_test_function() abort + return 'testing' + endfunction + + nnoremap fizz :call <SID>maparg_test_function()<CR> + ]]) + eq(1, funcs.maparg('fizz', 'n', false, true)['sid']) + eq('testing', nvim('call_function', '<SNR>1_maparg_test_function', {})) + end) + + it('works with <F12> and others', function() + source([[ + let g:maparg_test_var = 0 + + nnoremap <F12> :let g:maparg_test_var = 1<CR> + ]]) + eq(0, eval('g:maparg_test_var')) + source([[ + call feedkeys("\<F12>") + ]]) + eq(1, eval('g:maparg_test_var')) + + eq(':let g:maparg_test_var = 1<CR>', funcs.maparg('<F12>', 'n', false, true)['rhs']) + end) + + it('works with <expr>', function() + source([[ + let counter = 0 + inoremap <expr> <C-L> ListItem() + inoremap <expr> <C-R> ListReset() + + func ListItem() + let g:counter += 1 + return g:counter . '. ' + endfunc + + func ListReset() + let g:counter = 0 + return '' + endfunc + + call feedkeys("i\<C-L>") + ]]) + eq(1, eval('g:counter')) + + local map_dict = funcs.maparg('<C-L>', 'i', false, true) + eq(1, map_dict['expr']) + eq('i', map_dict['mode']) + end) +end) diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua index 420aea04aa..115114c3c3 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -1,20 +1,27 @@ local helpers = require('test.functional.helpers')(after_each) -local nvim, eq, neq, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.eval +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 function clear_serverlist() + for _, server in pairs(funcs.serverlist()) do + funcs.serverstop(server) + end +end + describe('serverstart(), serverstop()', function() before_each(clear) it('sets $NVIM_LISTEN_ADDRESS on first invocation', function() -- Unset $NVIM_LISTEN_ADDRESS - nvim('command', 'let $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')) - nvim('command', "call serverstop('"..s.."')") + command("call serverstop('"..s.."')") eq('', eval('$NVIM_LISTEN_ADDRESS')) end) @@ -47,10 +54,38 @@ describe('serverstart(), serverstop()', function() end) it('serverstop() ignores invalid input', function() - nvim('command', "call serverstop('')") - nvim('command', "call serverstop('bogus-socket-name')") + command("call serverstop('')") + command("call serverstop('bogus-socket-name')") end) + it('parses endpoints correctly', function() + clear_serverlist() + eq({}, funcs.serverlist()) + + local s = funcs.serverstart('127.0.0.1:0') -- assign random port + assert(string.match(s, '127.0.0.1:%d+')) + eq(s, funcs.serverlist()[1]) + clear_serverlist() + + s = funcs.serverstart('127.0.0.1:') -- assign random port + assert(string.match(s, '127.0.0.1:%d+')) + eq(s, funcs.serverlist()[1]) + clear_serverlist() + + funcs.serverstart('127.0.0.1:12345') + funcs.serverstart('127.0.0.1:12345') -- exists already; ignore + funcs.serverstart('::1:12345') + funcs.serverstart('::1:12345') -- exists already; ignore + local expected = { + '127.0.0.1:12345', + '::1:12345', + } + eq(expected, funcs.serverlist()) + clear_serverlist() + + funcs.serverstart('127.0.0.1:65536') -- invalid port + eq({}, funcs.serverlist()) + end) end) describe('serverlist()', function() @@ -75,7 +110,7 @@ describe('serverlist()', function() -- The new servers should be at the end of the list. for i = 1, #servs do eq(servs[i], new_servs[i + n]) - nvim('command', "call serverstop('"..servs[i].."')") + command("call serverstop('"..servs[i].."')") end -- After serverstop() the servers should NOT be in the list. eq(n, eval('len(serverlist())')) diff --git a/test/functional/ex_cmds/encoding_spec.lua b/test/functional/ex_cmds/encoding_spec.lua index 0769259be4..7f2bd78a47 100644 --- a/test/functional/ex_cmds/encoding_spec.lua +++ b/test/functional/ex_cmds/encoding_spec.lua @@ -15,7 +15,7 @@ describe('&encoding', function() feed_command('set encoding=latin1') -- error message expected feed('<cr>') - neq(nil, string.find(eval('v:errmsg'), '^E474:')) + neq(nil, string.find(eval('v:errmsg'), '^E519:')) eq('utf-8', eval('&encoding')) -- check nvim is still in utf-8 mode eq(3, eval('strwidth("Bär")')) @@ -25,7 +25,7 @@ describe('&encoding', function() clear('--cmd', 'set enc=latin1') -- error message expected feed('<cr>') - neq(nil, string.find(eval('v:errmsg'), '^E474:')) + neq(nil, string.find(eval('v:errmsg'), '^E519:')) eq('utf-8', eval('&encoding')) eq(3, eval('strwidth("Bär")')) end) diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c index 3406b3a202..7ba21d652a 100644 --- a/test/functional/fixtures/tty-test.c +++ b/test/functional/fixtures/tty-test.c @@ -6,6 +6,9 @@ #include <stdlib.h> #include <uv.h> +// -V:STRUCT_CAST:641 +#define STRUCT_CAST(Type, obj) ((Type *)(obj)) + uv_tty_t tty; #ifdef _WIN32 @@ -88,9 +91,9 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) uv_tty_init(&write_loop, &out, 1, 0); uv_write_t req; uv_buf_t b = {.base = buf->base, .len = (size_t)cnt}; - uv_write(&req, (uv_stream_t *)&out, &b, 1, NULL); + uv_write(&req, STRUCT_CAST(uv_stream_t, &out), &b, 1, NULL); uv_run(&write_loop, UV_RUN_DEFAULT); - uv_close((uv_handle_t *)&out, NULL); + uv_close(STRUCT_CAST(uv_handle_t, &out), NULL); uv_run(&write_loop, UV_RUN_DEFAULT); if (uv_loop_close(&write_loop)) { abort(); @@ -149,7 +152,7 @@ int main(int argc, char **argv) uv_tty_init(uv_default_loop(), &tty, fileno(stderr), 1); uv_tty_set_mode(&tty, UV_TTY_MODE_RAW); tty.data = &interrupted; - uv_read_start((uv_stream_t *)&tty, alloc_cb, read_cb); + uv_read_start(STRUCT_CAST(uv_stream_t, &tty), alloc_cb, read_cb); #ifndef WIN32 struct sigaction sa; sigemptyset(&sa.sa_mask); diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index b03840b3fe..62b0ce1200 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -76,8 +76,8 @@ end local session, loop_running, last_error -local function set_session(s) - if session then +local function set_session(s, keep) + if session and not keep then session:close() end session = s @@ -609,6 +609,7 @@ local module = { nvim = nvim, nvim_async = nvim_async, nvim_prog = nvim_prog, + nvim_argv = nvim_argv, nvim_set = nvim_set, nvim_dir = nvim_dir, buffer = buffer, diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index f4eec4bdc7..b47210a777 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -194,8 +194,8 @@ describe('ui/cursor', function() if m.blinkoff then m.blinkoff = 400 end if m.blinkwait then m.blinkwait = 700 end end - if m.hl_id then m.hl_id = 47 end - if m.id_lm then m.id_lm = 48 end + if m.hl_id then m.hl_id = 48 end + if m.id_lm then m.id_lm = 49 end end -- Assert the new expectation. diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 6308ae7367..5d543f914f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1751,6 +1751,55 @@ describe('typval.c', function() eq('2', s) end) end) + describe('get_string_buf_chk()', function() + local function tv_dict_get_string_buf_chk(d, key, len, buf, def, emsg) + buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) + def = def or ffi.gc(lib.xstrdup('DEFAULT'), lib.xfree) + len = len or #key + alloc_log:clear() + local ret = check_emsg(function() return lib.tv_dict_get_string_buf_chk(d, key, len, buf, def) end, + emsg) + local s_ret = (ret ~= nil) and ffi.string(ret) or nil + if not emsg then + alloc_log:check({}) + end + return s_ret, ret, buf, def + end + itp('works with NULL dict', function() + eq('DEFAULT', tv_dict_get_string_buf_chk(nil, 'test')) + end) + itp('works', function() + local lua_d = { + ['']={}, + t=1, + te=int(2), + tes=empty_list, + test='tset', + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + local s, r, b, def + s, r, b, def = tv_dict_get_string_buf_chk(d, 'test') + neq(r, b) + neq(r, def) + eq('tset', s) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', 1, nil, nil, 'E806: using Float as a String') + neq(r, b) + neq(r, def) + eq(nil, s) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'te') + eq(r, b) + neq(r, def) + eq('2', s) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'TEST') + eq(r, def) + neq(r, b) + eq('DEFAULT', s) + end) + end) describe('get_callback()', function() local function tv_dict_get_callback(d, key, key_len, emsg) key_len = key_len or #key diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua index 575787a25e..cefd0315b7 100644 --- a/test/unit/os/env_spec.lua +++ b/test/unit/os/env_spec.lua @@ -13,7 +13,7 @@ require('lfs') local cimp = cimport('./src/nvim/os/os.h') -describe('env function', function() +describe('env.c', function() local function os_setenv(name, value, override) return cimp.os_setenv(to_cstr(name), to_cstr(value), override) end diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index 6b9e2c8695..a9cba7df84 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -18,7 +18,7 @@ local cimp = cimport('./src/nvim/os/os.h', './src/nvim/path.h') local length = 0 local buffer = nil -describe('path function', function() +describe('path.c', function() describe('path_full_dir_name', function() setup(function() lfs.mkdir('unit-test-directory') @@ -293,6 +293,59 @@ describe('path_shorten_fname_if_possible', function() end) end) +describe('path.c path_guess_exepath', function() + local cwd = lfs.currentdir() + + for _,name in ipairs({'./nvim', '.nvim', 'foo/nvim'}) do + itp('"'..name..'" returns name catenated with CWD', function() + local bufsize = 255 + local buf = cstr(bufsize, '') + cimp.path_guess_exepath(name, buf, bufsize) + eq(cwd..'/'..name, ffi.string(buf)) + end) + end + + itp('absolute path returns the name unmodified', function() + local name = '/foo/bar/baz' + local bufsize = 255 + local buf = cstr(bufsize, '') + cimp.path_guess_exepath(name, buf, bufsize) + eq(name, ffi.string(buf)) + end) + + itp('returns the name unmodified if not found in $PATH', function() + local name = '23u0293_not_in_path' + local bufsize = 255 + local buf = cstr(bufsize, '') + cimp.path_guess_exepath(name, buf, bufsize) + eq(name, ffi.string(buf)) + end) + + itp('does not crash if $PATH item exceeds MAXPATHL', function() + local orig_path_env = os.getenv('PATH') + local name = 'cat' -- Some executable in $PATH. + local bufsize = 255 + local buf = cstr(bufsize, '') + local insane_path = orig_path_env..':'..(("x/"):rep(4097)) + + cimp.os_setenv('PATH', insane_path, true) + cimp.path_guess_exepath(name, buf, bufsize) + eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1)) + + -- Restore $PATH. + cimp.os_setenv('PATH', orig_path_env, true) + end) + + itp('returns full path found in $PATH', function() + local name = 'cat' -- Some executable in $PATH. + local bufsize = 255 + local buf = cstr(bufsize, '') + cimp.path_guess_exepath(name, buf, bufsize) + -- Usually "/bin/cat" on unix, "/path/to/nvim/cat" on Windows. + eq('bin/' .. name, ffi.string(buf):sub(-#('bin/' .. name), -1)) + end) +end) + describe('path.c', function() setup(function() lfs.mkdir('unit-test-directory'); |