diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/README.md | 2 | ||||
-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/eval/function_spec.lua | 29 | ||||
-rw-r--r-- | test/functional/eval/map_functions_spec.lua | 120 | ||||
-rw-r--r-- | test/functional/eval/msgpack_functions_spec.lua | 6 | ||||
-rw-r--r-- | test/functional/eval/server_spec.lua | 47 | ||||
-rw-r--r-- | test/functional/ex_cmds/mksession_spec.lua | 49 | ||||
-rw-r--r-- | test/functional/helpers.lua | 31 | ||||
-rw-r--r-- | test/functional/legacy/assert_spec.lua | 10 | ||||
-rw-r--r-- | test/functional/legacy/eval_spec.lua | 9 | ||||
-rw-r--r-- | test/functional/options/defaults_spec.lua | 52 | ||||
-rw-r--r-- | test/functional/options/pastetoggle_spec.lua | 35 | ||||
-rw-r--r-- | test/functional/plugin/shada_spec.lua | 37 | ||||
-rw-r--r-- | test/functional/shada/registers_spec.lua | 36 | ||||
-rw-r--r-- | test/functional/ui/highlight_spec.lua | 170 | ||||
-rw-r--r-- | test/functional/ui/screen.lua | 5 | ||||
-rw-r--r-- | test/functional/viml/function_spec.lua | 232 | ||||
-rw-r--r-- | test/helpers.lua | 7 | ||||
-rw-r--r-- | test/unit/os/shell_spec.lua | 22 |
20 files changed, 1119 insertions, 99 deletions
diff --git a/test/README.md b/test/README.md index 2857cc0ecf..01db5960cd 100644 --- a/test/README.md +++ b/test/README.md @@ -2,6 +2,8 @@ Tests are run by `/cmake/RunTests.cmake` file, using busted. +For some failures, `.nvimlog` (or `$NVIM_LOG_FILE`) may provide insight. + ## Directory structure Directories with tests: `/test/benchmark` for benchmarks, `/test/functional` for 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/eval/function_spec.lua b/test/functional/eval/function_spec.lua new file mode 100644 index 0000000000..776e760aaf --- /dev/null +++ b/test/functional/eval/function_spec.lua @@ -0,0 +1,29 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local exc_exec = helpers.exc_exec + +describe('Up to MAX_FUNC_ARGS arguments are handled by', function() + local max_func_args = 20 -- from eval.h + local range = helpers.funcs.range + + before_each(clear) + + it('printf()', function() + local printf = helpers.funcs.printf + local rep = helpers.funcs['repeat'] + local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' + eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) + local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function printf', ret) + end) + + it('rpcnotify()', function() + local rpcnotify = helpers.funcs.rpcnotify + local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) + eq(1, ret) + ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') + eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) + 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/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index 4a052b4aff..b241635dfe 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -337,7 +337,8 @@ describe('msgpack*() functions', function() eq(1, eval('type(parsed[0]) == type(0) ' .. '|| parsed[0]._TYPE is v:msgpack_types.integer')) if eval('type(parsed[0]) == type(0)') == 1 then - eq(1, eval('0xFFFFFFFFFFFFFFFF == parsed[0]')) + command('call assert_equal(0xFFFFFFFFFFFFFFFF, parsed[0])') + eq({}, eval('v:errors')) else eq({_TYPE={}, _VAL={1, 3, 0x7FFFFFFF, 0x7FFFFFFF}}, eval('parsed[0]')) end @@ -351,7 +352,8 @@ describe('msgpack*() functions', function() eq(1, eval('type(parsed[0]) == type(0) ' .. '|| parsed[0]._TYPE is v:msgpack_types.integer')) if eval('type(parsed[0]) == type(0)') == 1 then - eq(1, eval('-0x8000000000000000 == parsed[0]')) + command('call assert_equal(-0x7fffffffffffffff - 1, parsed[0])') + eq({}, eval('v:errors')) else eq({_TYPE={}, _VAL={-1, 2, 0, 0}}, eval('parsed[0]')) 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/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua new file mode 100644 index 0000000000..5d658f10bb --- /dev/null +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -0,0 +1,49 @@ +local lfs = require('lfs') +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local get_pathsep = helpers.get_pathsep +local eq = helpers.eq +local funcs = helpers.funcs + +local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec' + +describe(':mksession', function() + local session_file = file_prefix .. '.vim' + local tab_dir = file_prefix .. '.d' + + before_each(function() + clear() + lfs.mkdir(tab_dir) + end) + + after_each(function() + os.remove(session_file) + lfs.rmdir(tab_dir) + end) + + it('restores tab-local working directories', function() + local tmpfile_base = file_prefix .. '-tmpfile' + local cwd_dir = funcs.getcwd() + + -- :mksession does not save empty tabs, so create some buffers. + command('edit ' .. tmpfile_base .. '1') + command('tabnew') + command('edit ' .. tmpfile_base .. '2') + command('tcd ' .. tab_dir) + command('tabfirst') + command('mksession ' .. session_file) + + -- Create a new test instance of Nvim. + clear() + + command('source ' .. session_file) + -- First tab should have the original working directory. + command('tabnext 1') + eq(cwd_dir, funcs.getcwd()) + -- Second tab should have the tab-local working directory. + command('tabnext 2') + eq(cwd_dir .. get_pathsep() .. tab_dir, funcs.getcwd()) + end) +end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 4bd9ae4bfb..4a170d993b 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 @@ -174,7 +174,7 @@ local os_name = (function() end)() local function iswin() - return os_name() == 'windows' + return package.config:sub(1,1) == '\\' end -- Executes a VimL function. @@ -492,17 +492,6 @@ local exc_exec = function(cmd) return ret end -local function redir_exec(cmd) - nvim_command(([[ - redir => g:__output - silent! execute "%s" - redir END - ]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0'))) - local ret = nvim_eval('get(g:, "__output", 0)') - nvim_command('unlet! g:__output') - return ret -end - local function create_callindex(func) local table = {} setmetatable(table, { @@ -562,6 +551,19 @@ local curbufmeths = create_callindex(curbuf) local curwinmeths = create_callindex(curwin) local curtabmeths = create_callindex(curtab) +local function redir_exec(cmd) + meths.set_var('__redir_exec_cmd', cmd) + nvim_command([[ + redir => g:__redir_exec_output + silent! execute g:__redir_exec_cmd + redir END + ]]) + local ret = meths.get_var('__redir_exec_output') + meths.del_var('__redir_exec_output') + meths.del_var('__redir_exec_cmd') + return ret +end + local function get_pathsep() return funcs.fnamemodify('.', ':p'):sub(-1) end @@ -627,6 +629,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/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua index d6255d42e7..2f342ec9a3 100644 --- a/test/functional/legacy/assert_spec.lua +++ b/test/functional/legacy/assert_spec.lua @@ -255,6 +255,16 @@ describe('assert function:', function() end) end) + -- assert_report({msg}) + describe('assert_report()', function() + it('should add a message to v:errors', function() + command("call assert_report('something is wrong')") + command("call assert_match('something is wrong', v:errors[0])") + command('call remove(v:errors, 0)') + expected_empty() + end) + end) + -- assert_exception({cmd}, [, {error}]) describe('assert_exception()', function() it('should assert thrown exceptions properly', function() diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua index d7ef508194..c5d38d6d05 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -516,6 +516,15 @@ describe('eval', function() eq({'item'}, eval("y")) end) + it('sets the unnamed register when the "u" option is passed to setreg', function() + command("call setreg('a','a reg', 'cu')") + eq("a reg", eval('@"')) + command("call setreg('b','b reg', 'cu')") + eq("b reg", eval('@"')) + command("call setreg('c','c reg', 'c')") + eq("b reg", eval('@"')) + end) + it('search and expressions', function() command('so test_eval_setup.vim') command([=[call SetReg('/', ['abc/'])]=]) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index dc73679bb4..b83b7b8eee 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -8,6 +8,8 @@ local clear = helpers.clear local eval = helpers.eval local eq = helpers.eq local neq = helpers.neq +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir local function init_session(...) local args = { helpers.nvim_prog, '-i', 'NONE', '--embed', @@ -121,6 +123,56 @@ describe('startup defaults', function() it('v:progpath is set to the absolute path', function() eq(eval("fnamemodify(v:progpath, ':p')"), eval('v:progpath')) end) + + describe('$NVIM_LOG_FILE', function() + -- TODO(jkeyes): use stdpath('data') instead. + local datasubdir = helpers.iswin() and 'nvim-data' or 'nvim' + local xdgdir = 'Xtest-startup-xdg-logpath' + local xdgdatadir = xdgdir..'/'..datasubdir + after_each(function() + os.remove('Xtest-logpath') + rmdir(xdgdir) + end) + + it('is used if expansion succeeds', function() + clear({env={ + NVIM_LOG_FILE='Xtest-logpath', + }}) + eq('Xtest-logpath', eval('$NVIM_LOG_FILE')) + end) + it('defaults to stdpath("data")/log if empty', function() + eq(true, mkdir(xdgdir) and mkdir(xdgdatadir)) + clear({env={ + XDG_DATA_HOME=xdgdir, + NVIM_LOG_FILE='', -- Empty is invalid. + }}) + -- server_start() calls ELOG, which tickles log_path_init(). + pcall(command, 'call serverstart(serverlist()[0])') + + eq(xdgdir..'/'..datasubdir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/')) + end) + it('defaults to stdpath("data")/log if invalid', function() + eq(true, mkdir(xdgdir) and mkdir(xdgdatadir)) + clear({env={ + XDG_DATA_HOME=xdgdir, + NVIM_LOG_FILE='.', -- Any directory is invalid. + }}) + -- server_start() calls ELOG, which tickles log_path_init(). + pcall(command, 'call serverstart(serverlist()[0])') + + eq(xdgdir..'/'..datasubdir..'/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/')) + end) + it('defaults to .nvimlog if stdpath("data") is invalid', function() + clear({env={ + XDG_DATA_HOME='Xtest-missing-xdg-dir', + NVIM_LOG_FILE='.', -- Any directory is invalid. + }}) + -- server_start() calls ELOG, which tickles log_path_init(). + pcall(command, 'call serverstart(serverlist()[0])') + + eq('.nvimlog', eval('$NVIM_LOG_FILE')) + end) + end) end) describe('XDG-based defaults', function() diff --git a/test/functional/options/pastetoggle_spec.lua b/test/functional/options/pastetoggle_spec.lua index ec3c60fe37..a1f86f73ff 100644 --- a/test/functional/options/pastetoggle_spec.lua +++ b/test/functional/options/pastetoggle_spec.lua @@ -6,32 +6,35 @@ local command = helpers.command local eq = helpers.eq local eval = helpers.eval local sleep = helpers.sleep +local expect = helpers.expect describe("'pastetoggle' option", function() before_each(function() clear() command('set nopaste') - command('set pastetoggle=a') end) + it("toggles 'paste'", function() - eq(eval('&paste'), 0) + command('set pastetoggle=a') + eq(0, eval('&paste')) feed('a') -- Need another key so that the vgetorpeek() function returns. feed('j') - eq(eval('&paste'), 1) + eq(1, eval('&paste')) end) - it("multiple key 'pastetoggle' is waited for", function() - eq(eval('&paste'), 0) - local pastetoggle = 'lllll' - command('set pastetoggle=' .. pastetoggle) - command('set timeoutlen=1 ttimeoutlen=10000') - feed(pastetoggle:sub(0, 2)) - -- sleep() for long enough that vgetorpeek() is gotten into, but short - -- enough that ttimeoutlen is not reached. - sleep(200) - feed(pastetoggle:sub(3, -1)) - -- Need another key so that the vgetorpeek() function returns. - feed('j') - eq(eval('&paste'), 1) + + + it('does not wait for timeout', function() + command('set pastetoggle=abc') + command('set ttimeoutlen=9999999') + eq(0, eval('&paste')) + -- n.b. need <esc> to return from vgetorpeek() + feed('abc<esc>') + eq(1, eval('&paste')) + feed('ab') + sleep(10) + feed('c<esc>') + expect('bc') + eq(1, eval('&paste')) end) end) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index b543037ae2..639833071b 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -179,6 +179,7 @@ describe('In autoload/shada.vim', function() ' + n name \'@\'', ' + rc contents ["abc", "def"]', ' + rt type CHARACTERWISE', + ' + ru is_unnamed FALSE', ' + rw block width 10', ' + sb search backward TRUE', ' + sc smartcase value FALSE', @@ -204,6 +205,7 @@ describe('In autoload/shada.vim', function() 'rt': 0, 'rw': 10, 'rc': ['abc', 'def'], + 'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, 'n': 0x40, 'l': 10, 'c': 0, @@ -226,6 +228,8 @@ describe('In autoload/shada.vim', function() .. '0 (CHARACTERWISE), 1 (LINEWISE), 2 (BLOCKWISE)', ' + rt type 10', ' # Expected boolean', + ' + ru is_unnamed 10', + ' # Expected boolean', ' + sc smartcase value NIL', ' # Expected boolean', ' + sm magic value "TRUE"', @@ -240,6 +244,7 @@ describe('In autoload/shada.vim', function() 'sp': {'_TYPE': v:msgpack_types.string, '_VAL': ["abc"]}, 'rt': 10, 'rc': '10', + 'ru': 10, 'n': -0x40, 'l': -10, 'c': 'abc', @@ -636,6 +641,7 @@ describe('In autoload/shada.vim', function() ' # Required key missing: rc', ' + rw block width 0', ' + rt type CHARACTERWISE', + ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { }}] ]]):gsub('\n', '')) sd2strings_eq({ @@ -645,6 +651,7 @@ describe('In autoload/shada.vim', function() ' # Required key missing: rc', ' + rw block width 0', ' + rt type CHARACTERWISE', + ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, }}] ]]):gsub('\n', '')) @@ -655,9 +662,11 @@ describe('In autoload/shada.vim', function() ' + rc contents ["abc", "def"]', ' + rw block width 0', ' + rt type CHARACTERWISE', + ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ["abc", "def"], + 'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 0}, }}] ]]):gsub('\n', '')) sd2strings_eq({ 'Register with timestamp ' .. epoch .. ':', @@ -668,9 +677,11 @@ describe('In autoload/shada.vim', function() ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 0', ' + rt type CHARACTERWISE', + ' + ru is_unnamed TRUE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'ru': {'_TYPE': v:msgpack_types.boolean, '_VAL': 1}, }}] ]]):gsub('\n', '')) sd2strings_eq({ 'Register with timestamp ' .. epoch .. ':', @@ -681,6 +692,7 @@ describe('In autoload/shada.vim', function() ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 0', ' + rt type CHARACTERWISE', + ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], @@ -696,6 +708,7 @@ describe('In autoload/shada.vim', function() ' | - "abcdefghijklmnopqrstuvwxyz"', ' + rw block width 5', ' + rt type LINEWISE', + ' + ru is_unnamed FALSE', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], @@ -712,11 +725,14 @@ describe('In autoload/shada.vim', function() ' # Expected integer', ' + rw block width ""', ' + rt type BLOCKWISE', + ' # Expected boolean', + ' + ru is_unnamed ""', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], 'rw': "", 'rt': 2, + 'ru': "" }}] ]]):gsub('\n', '')) sd2strings_eq({ 'Register with timestamp ' .. epoch .. ':', @@ -729,11 +745,32 @@ describe('In autoload/shada.vim', function() ' # Unexpected enum value: expected one of 0 (CHARACTERWISE), ' .. '1 (LINEWISE), 2 (BLOCKWISE)', ' + rt type 10', + ' # Expected boolean', + ' + ru is_unnamed ["abc", "def"]', }, ([[ [{'type': 5, 'timestamp': 0, 'data': { 'n': 0x20, 'rc': 0, 'rw': -1, 'rt': 10, + 'ru': ['abc', 'def'], + }}] ]]):gsub('\n', '')) + sd2strings_eq({ + 'Register with timestamp ' .. epoch .. ':', + ' % Key Description Value', + ' + n name \' \'', + ' + rc contents @', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' | - "abcdefghijklmnopqrstuvwxyz"', + ' + rw block width 5', + ' + rt type LINEWISE', + ' # Expected boolean', + ' + ru is_unnamed 0', + }, ([[ [{'type': 5, 'timestamp': 0, 'data': { + 'n': 0x20, + 'rc': ['abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz'], + 'rw': 5, + 'rt': 1, + 'ru': 0, }}] ]]):gsub('\n', '')) end) diff --git a/test/functional/shada/registers_spec.lua b/test/functional/shada/registers_spec.lua index fc812f799c..71af14aba8 100644 --- a/test/functional/shada/registers_spec.lua +++ b/test/functional/shada/registers_spec.lua @@ -148,4 +148,40 @@ describe('ShaDa support code', function() eq({{'\171«'}, 'v'}, getreg('e')) end) + it('has a blank unnamed register if it wasn\'t set and register 0 is empty', + function() + setreg('1', {'one'}, 'c') + setreg('2', {'two'}, 'c') + setreg('a', {'a'}, 'c') + nvim_command('qall') + reset() + eq({{}, ''}, getreg('0')) + eq({{'one'}, 'v'}, getreg('1')) + eq({{}, ''}, getreg('"')) + eq({{'a'}, 'v'}, getreg('a')) + end) + + it('defaults the unnamed register to register 0 if it wasn\'t set', + function() + setreg('0', {'zero'}, 'c') + setreg('1', {'one'}, 'c') + setreg('2', {'two'}, 'c') + nvim_command('qall') + reset() + eq({{'zero'}, 'v'}, getreg('0')) + eq({{'one'}, 'v'}, getreg('1')) + eq({{'zero'}, 'v'}, getreg('"')) + end) + + it('remembers which register was the unnamed register when loading', + function() + setreg('0', {'zero'}, 'c') + setreg('1', {'one'}, 'cu') + setreg('2', {'two'}, 'c') + nvim_command('qall') + reset() + eq({{'zero'}, 'v'}, getreg('0')) + eq({{'one'}, 'v'}, getreg('1')) + eq({{'one'}, 'v'}, getreg('"')) + end) end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 4fd00cc320..d1357ea525 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -5,6 +5,7 @@ local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local command = helpers.command local eval, exc_exec = helpers.eval, helpers.exc_exec local feed_command, request, eq = helpers.feed_command, helpers.request, helpers.eq +local curbufmeths = helpers.curbufmeths describe('colorscheme compatibility', function() before_each(function() @@ -670,12 +671,26 @@ describe("'winhighlight' highlight", function() [8] = {background = Screen.colors.DarkMagenta, bold = true, foreground = Screen.colors.Blue1}, [9] = {foreground = Screen.colors.Brown}, [10] = {foreground = Screen.colors.Brown, background = Screen.colors.DarkBlue}, + [11] = {background = Screen.colors.DarkBlue, bold = true, reverse = true}, + [12] = {background = Screen.colors.DarkGreen, reverse = true}, + [13] = {background = Screen.colors.Magenta4, reverse = true}, + [14] = {background = Screen.colors.DarkBlue, reverse = true}, + [15] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [16] = {foreground = Screen.colors.Blue1}, + [17] = {background = Screen.colors.LightRed}, + [18] = {background = Screen.colors.Gray90}, + [19] = {foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray}, + [20] = {background = Screen.colors.LightGrey, underline = true}, + [21] = {bold = true}, + [22] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [23] = {background = Screen.colors.LightMagenta}, + [24] = {background = Screen.colors.WebGray}, }) command("hi Background1 guibg=DarkBlue") command("hi Background2 guibg=DarkGreen") end) - it('works', function() + it('works for background color', function() insert("aa") command("split") command("set winhl=Normal:Background1") @@ -683,7 +698,7 @@ describe("'winhighlight' highlight", function() {1:a^a }| {2:~ }| {2:~ }| - {3:[No Name] [+] }| + {11:[No Name] [+] }| aa | {0:~ }| {4:[No Name] [+] }| @@ -695,7 +710,7 @@ describe("'winhighlight' highlight", function() {1:^ }| {2:~ }| {2:~ }| - {3:[No Name] }| + {11:[No Name] }| aa | {0:~ }| {4:[No Name] [+] }| @@ -740,7 +755,7 @@ describe("'winhighlight' highlight", function() {1:a^a }| {2:~ }| {2:~ }| - {3:[No Name] [+] }| + {11:[No Name] [+] }| aa | {0:~ }| {4:[No Name] [+] }| @@ -764,16 +779,15 @@ describe("'winhighlight' highlight", function() {1:^aa }| {2:~ }| {2:~ }| - {3:[No Name] [+] }| + {11:[No Name] [+] }| aa | {0:~ }| {4:[No Name] [+] }| <f 1 --100%-- col 1 | ]]) - end) - it('for inactive window works', function() + it('for inactive window background works', function() command("set winhl=Normal:Background1,NormalNC:Background2") -- tests global value is copied across split command("split") @@ -781,10 +795,10 @@ describe("'winhighlight' highlight", function() {1:^ }| {2:~ }| {2:~ }| - {3:[No Name] }| + {11:[No Name] }| {5: }| {6:~ }| - {4:[No Name] }| + {12:[No Name] }| | ]]) @@ -793,10 +807,10 @@ describe("'winhighlight' highlight", function() {5: }| {6:~ }| {6:~ }| - {4:[No Name] }| + {12:[No Name] }| {1:^ }| {2:~ }| - {3:[No Name] }| + {11:[No Name] }| | ]]) @@ -805,13 +819,12 @@ describe("'winhighlight' highlight", function() {1:^ }| {2:~ }| {2:~ }| - {3:[No Name] }| + {11:[No Name] }| {5: }| {6:~ }| - {4:[No Name] }| + {12:[No Name] }| | ]]) - end) it('works with NormalNC', function() @@ -825,7 +838,7 @@ describe("'winhighlight' highlight", function() {3:[No Name] }| {7: }| {8:~ }| - {4:[No Name] }| + {13:[No Name] }| | ]]) @@ -834,7 +847,7 @@ describe("'winhighlight' highlight", function() {7: }| {8:~ }| {8:~ }| - {4:[No Name] }| + {13:[No Name] }| ^ | {0:~ }| {3:[No Name] }| @@ -848,10 +861,10 @@ describe("'winhighlight' highlight", function() {7: }| {8:~ }| {8:~ }| - {4:[No Name] }| + {13:[No Name] }| {1:^ }| {2:~ }| - {3:[No Name] }| + {11:[No Name] }| | ]]) @@ -863,7 +876,7 @@ describe("'winhighlight' highlight", function() {3:[No Name] }| {1: }| {2:~ }| - {4:[No Name] }| + {14:[No Name] }| | ]]) @@ -873,10 +886,10 @@ describe("'winhighlight' highlight", function() {7: }| {8:~ }| {8:~ }| - {4:[No Name] }| + {13:[No Name] }| {1:^ }| {2:~ }| - {3:[No Name] }| + {11:[No Name] }| | ]]) @@ -888,13 +901,12 @@ describe("'winhighlight' highlight", function() {3:[No Name] }| {5: }| {6:~ }| - {4:[No Name] }| + {12:[No Name] }| | ]]) - end) - it('applies also to non-text', function() + it('background applies also to non-text', function() insert('Lorem ipsum dolor sit amet ') command('set shiftwidth=2') feed('>>') @@ -940,6 +952,114 @@ describe("'winhighlight' highlight", function() | ]]) end) -end) + it('can override NonText, Conceal and EndOfBuffer', function() + curbufmeths.set_lines(0,-1,true, {"raa\000"}) + command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})') + command('set cole=2 cocu=nvic') + command('split') + command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})') + command('set winhl=SpecialKey:ErrorMsg,EndOfBuffer:Background1,' + ..'Conceal:Background2') + + screen:expect([[ + ^r{5:#}a{15:^@} | + {1:~ }| + {1:~ }| + {3:[No Name] [+] }| + r{19:#}a{16:^@} | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end) + + it('can override LineNr, CursorColumn and ColorColumn', function() + insert('very text\nmore text') + command('set number') + command('set colorcolumn=2') + command('set cursorcolumn') + + command('split') + command('set winhl=LineNr:Background1,CursorColumn:Background2,' + ..'ColorColumn:ErrorMsg') + screen:expect([[ + {1: 1 }v{15:e}ry tex{5:t} | + {1: 2 }m{15:o}re tex^t | + {0:~ }| + {3:[No Name] [+] }| + {9: 1 }v{17:e}ry tex{18:t} | + {9: 2 }m{17:o}re text | + {4:[No Name] [+] }| + | + ]]) + end) + + it('can override Tabline', function() + command('tabnew') + command('set winhl=TabLine:Background1,TabLineSel:ErrorMsg') + + screen:expect([[ + {20: No Name] }{15: No Name]}{20:X}| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + command("tabnext") + screen:expect([[ + {21: No Name] }{1: No Name]}{20:X}| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end) + + it('can override popupmenu', function() + insert('word wording wordy') + command('split') + command('set winhl=Pmenu:Background1,PmenuSel:Background2,' + ..'PmenuSbar:ErrorMsg,PmenuThumb:Normal') + screen:expect([[ + word wording word^y | + {0:~ }| + {0:~ }| + {3:[No Name] [+] }| + word wording wordy | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + feed('oword<c-x><c-p>') + screen:expect([[ + word wording wordy | + wordy^ | + {1:word }{0: }| + {1:wording }{3: }| + {5:wordy }rdy | + wordy | + {4:[No Name] [+] }| + {21:-- }{22:match 1 of 3} | + ]]) + + feed('<esc>u<c-w><c-w>oword<c-x><c-p>') + screen:expect([[ + word wording wordy | + wordy | + {23:word }{0: }| + {23:wording }{4: }| + {24:wordy }rdy | + wordy^ | + {3:[No Name] [+] }| + {21:-- }{22:match 1 of 3} | + ]]) + end) +end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 7d9cd6c026..5408e1e195 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -198,8 +198,9 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any) condition = expected expected = nil else - -- Remove the last line and dedent. - expected = dedent(expected:gsub('\n[ ]+$', '')) + -- Remove the last line and dedent. Note that gsub returns more then one + -- value. + expected = dedent(expected:gsub('\n[ ]+$', ''), 0) for row in expected:gmatch('[^\n]+') do row = row:sub(1, #row - 1) -- Last char must be the screen delimiter. table.insert(expected_rows, row) diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua index 776e760aaf..0cf92f7d40 100644 --- a/test/functional/viml/function_spec.lua +++ b/test/functional/viml/function_spec.lua @@ -1,29 +1,221 @@ local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear local eq = helpers.eq -local exc_exec = helpers.exc_exec +local clear = helpers.clear +local funcs = helpers.funcs +local dedent = helpers.dedent +local redir_exec = helpers.redir_exec -describe('Up to MAX_FUNC_ARGS arguments are handled by', function() - local max_func_args = 20 -- from eval.h - local range = helpers.funcs.range +before_each(clear) - before_each(clear) +local function check_nofunc(fname) + eq(0, funcs.exists('*' .. fname)) +end - it('printf()', function() - local printf = helpers.funcs.printf - local rep = helpers.funcs['repeat'] - local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' - eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args)))) - local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') - eq('Vim(call):E740: Too many arguments for function printf', ret) - end) +local function check_func(fname, body, indent) + if type(body) == 'number' then + body = ('return %i'):format(body) + end + eq(dedent(([[ - it('rpcnotify()', function() - local rpcnotify = helpers.funcs.rpcnotify - local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) - eq(1, ret) - ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') - eq('Vim(call):E740: Too many arguments for function rpcnotify', ret) + function %s()%s + endfunction]] + ), 3):format( + fname, + body and ('\n1' .. (' '):rep(2 + (indent or 8)) .. body) or ''), + redir_exec('function ' .. fname)) +end + +describe(':endfunction', function() + it('accepts bang', function() + eq('', redir_exec([[ + function F() + endfunction! + ]])) + check_func('F') + eq('', redir_exec([[ + function! F() + return 1 + endfunction! + ]])) + check_func('F', 1) + end) + it('accepts comments', function() + eq('', redir_exec([[ + function F1() + endfunction " Comment + ]])) + check_func('F1') + eq('', redir_exec([[ + function F2() + endfunction " }}} + ]])) + check_func('F2') + eq('', redir_exec([[ + function F3() + endfunction " F3 + ]])) + check_func('F3') + eq('', redir_exec([[ + function F4() + endfunction! " F4 + ]])) + check_func('F4') + eq('', redir_exec([[ + function! F4() + return 2 + endfunction! " F4 + ]])) + check_func('F4', 2) + end) + it('accepts function name', function() + eq('', redir_exec([[ + function F0() + endfunction F0 + ]])) + check_func('F0') + eq('', redir_exec([[ + function F1() + endfunction! F1 + ]])) + check_func('F1') + eq('', redir_exec([[ + function! F2() + endfunction! F2 + ]])) + check_func('F2') + eq('', redir_exec([[ + function! F2() + return 3 + endfunction! F2 + ]])) + check_func('F2', 3) + end) + it('accepts weird characters', function() + eq('', redir_exec([[ + function F1() + endfunction: }}} + ]])) + check_func('F1') + -- From accurev + eq('', redir_exec([[ + function F2() + endfunction :}}} + ]])) + check_func('F2') + -- From cream-vimabbrev + eq('', redir_exec([[ + function F3() + endfunction 1}}} + ]])) + check_func('F3') + -- From pyunit + eq('', redir_exec([[ + function F4() + endfunction # }}} + ]])) + check_func('F4') + -- From vim-lldb + eq('', redir_exec([[ + function F5() + endfunction() + ]])) + check_func('F5') + -- From vim-mail + eq('', redir_exec([[ + function F6() + endfunction; + ]])) + check_func('F6') + end) + it('accepts commented bar', function() + eq('', redir_exec([[ + function F1() + endfunction " F1 | echo 42 + ]])) + check_func('F1') + eq('', redir_exec([[ + function! F1() + return 42 + endfunction! " F1 | echo 42 + ]])) + check_func('F1', 42) + end) + it('errors out on an uncommented bar', function() + eq('\nE488: Trailing characters: | echo 42', redir_exec([[ + function F1() + endfunction | echo 42 + ]])) + check_nofunc('F1') + end) + it('allows running multiple commands', function() + eq('\n2', redir_exec([[ + function F1() + echo 2 + endfunction + call F1() + ]])) + check_func('F1', 'echo 2') + eq('\n2\n3\n4', redir_exec([[ + function F2() + echo 2 + endfunction F2 + function F3() + echo 3 + endfunction " F3 + function! F4() + echo 4 + endfunction! + call F2() + call F3() + call F4() + ]])) + check_func('F2', 'echo 2') + check_func('F3', 'echo 3') + check_func('F4', 'echo 4') + end) + it('allows running multiple commands with only one character in between', + function() + eq('\n3', redir_exec(dedent([[ + function! F1() + echo 3 + endfunction! + call F1()]]))) + check_func('F1', 'echo 3', 2) + eq('\n4', redir_exec(dedent([[ + function F5() + echo 4 + endfunction + call F5()]]))) + check_func('F5', 'echo 4', 2) + eq('\n5', redir_exec(dedent([[ + function F6() + echo 5 + endfunction " TEST + call F6()]]))) + check_func('F6', 'echo 5', 2) + eq('\n6', redir_exec(dedent([[ + function F7() + echo 6 + endfunction F7 + call F7()]]))) + check_func('F7', 'echo 6', 2) + eq('\n2\n3\n4', redir_exec(dedent([[ + function F2() + echo 2 + endfunction F2 + function F3() + echo 3 + endfunction " F3 + function! F4() + echo 4 + endfunction! + call F2() + call F3() + call F4()]]))) + check_func('F2', 'echo 2', 2) + check_func('F3', 'echo 3', 2) + check_func('F4', 'echo 4', 2) end) end) +-- vim: foldmarker=▶,▲ diff --git a/test/helpers.lua b/test/helpers.lua index 7a0e4b8c3c..260f10002e 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -284,7 +284,7 @@ local function concat_tables(...) return ret end -local function dedent(str) +local function dedent(str, leave_indent) -- find minimum common indent across lines local indent = nil for line in str:gmatch('[^\n]+') do @@ -297,12 +297,13 @@ local function dedent(str) -- no minimum common indent return str end + local left_indent = (' '):rep(leave_indent or 0) -- create a pattern for the indent indent = indent:gsub('%s', '[ \t]') -- strip it from the first line - str = str:gsub('^'..indent, '') + str = str:gsub('^'..indent, left_indent) -- strip it from the remaining lines - str = str:gsub('[\n]'..indent, '\n') + str = str:gsub('[\n]'..indent, '\n' .. left_indent) return str end diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index e883301cfb..37274502de 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -15,7 +15,7 @@ local NULL = ffi.cast('void *', 0) describe('shell functions', function() before_each(function() -- os_system() can't work when the p_sh and p_shcf variables are unset - cimported.p_sh = to_cstr('/bin/bash') + cimported.p_sh = to_cstr('/bin/sh') cimported.p_shcf = to_cstr('-c') cimported.p_sxq = to_cstr('') cimported.p_sxe = to_cstr('') @@ -53,14 +53,14 @@ describe('shell functions', function() describe('os_system', function() itp('can echo some output (shell builtin)', function() - local cmd, text = 'echo -n', 'some text' + local cmd, text = 'printf "%s "', 'some text ' local status, output = os_system(cmd .. ' ' .. text) eq(text, output) eq(0, status) end) itp('can deal with empty output', function() - local cmd = 'echo -n' + local cmd = 'printf ""' local status, output = os_system(cmd) eq('', output) eq(0, status) @@ -81,19 +81,19 @@ describe('shell functions', function() describe('shell_build_argv', function() itp('works with NULL arguments', function() - eq({'/bin/bash'}, shell_build_argv(nil, nil)) + eq({'/bin/sh'}, shell_build_argv(nil, nil)) end) itp('works with cmd', function() - eq({'/bin/bash', '-c', 'abc def'}, shell_build_argv('abc def', nil)) + eq({'/bin/sh', '-c', 'abc def'}, shell_build_argv('abc def', nil)) end) itp('works with extra_args', function() - eq({'/bin/bash', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl')) + eq({'/bin/sh', 'ghi jkl'}, shell_build_argv(nil, 'ghi jkl')) end) itp('works with cmd and extra_args', function() - eq({'/bin/bash', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl')) + eq({'/bin/sh', 'ghi jkl', '-c', 'abc def'}, shell_build_argv('abc def', 'ghi jkl')) end) itp('splits and unquotes &shell and &shellcmdflag', function() @@ -112,7 +112,7 @@ describe('shell functions', function() local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo &|<>()@^'), nil)) - eq(ffi.string(argv[0]), '/bin/bash') + eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[2]), '(echo ^&^|^<^>^(^)^@^^)') eq(nil, argv[3]) @@ -124,7 +124,7 @@ describe('shell functions', function() local argv = ffi.cast('char**', cimported.shell_build_argv( to_cstr('echo -n some text'), nil)) - eq(ffi.string(argv[0]), '/bin/bash') + eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[2]), '"(echo -n some text)"') eq(nil, argv[3]) @@ -136,7 +136,7 @@ describe('shell functions', function() local argv = ffi.cast('char**', cimported.shell_build_argv( to_cstr('echo -n some text'), nil)) - eq(ffi.string(argv[0]), '/bin/bash') + eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[2]), '"echo -n some text"') eq(nil, argv[3]) @@ -145,7 +145,7 @@ describe('shell functions', function() itp('with empty shellxquote/shellxescape', function() local argv = ffi.cast('char**', cimported.shell_build_argv( to_cstr('echo -n some text'), nil)) - eq(ffi.string(argv[0]), '/bin/bash') + eq(ffi.string(argv[0]), '/bin/sh') eq(ffi.string(argv[1]), '-c') eq(ffi.string(argv[2]), 'echo -n some text') eq(nil, argv[3]) |