diff options
Diffstat (limited to 'test/functional/api')
-rw-r--r-- | test/functional/api/buffer_spec.lua | 157 | ||||
-rw-r--r-- | test/functional/api/menu_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/api/rpc_fixture.lua | 38 | ||||
-rw-r--r-- | test/functional/api/server_notifications_spec.lua | 32 | ||||
-rw-r--r-- | test/functional/api/server_requests_spec.lua | 74 | ||||
-rw-r--r-- | test/functional/api/tabpage_spec.lua | 56 | ||||
-rw-r--r-- | test/functional/api/version_spec.lua | 71 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 279 | ||||
-rw-r--r-- | test/functional/api/window_spec.lua | 106 |
9 files changed, 612 insertions, 203 deletions
diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 0eefa25a13..3d3a2bb046 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1,107 +1,113 @@ --- Sanity checks for buffer_* API calls via msgpack-rpc -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear, nvim, buffer = helpers.clear, helpers.nvim, helpers.buffer local curbuf, curwin, eq = helpers.curbuf, helpers.curwin, helpers.eq local curbufmeths, ok = helpers.curbufmeths, helpers.ok -local funcs = helpers.funcs +local funcs, request = helpers.funcs, helpers.request +local NIL = helpers.NIL -describe('buffer_* functions', function() +describe('api/buf', function() before_each(clear) + -- access deprecated functions + local function curbuf_depr(method, ...) + return request('buffer_'..method, 0, ...) + end + + describe('line_count, insert and del_line', function() it('works', function() - eq(1, curbuf('line_count')) - curbuf('insert', -1, {'line'}) - eq(2, curbuf('line_count')) - curbuf('insert', -1, {'line'}) - eq(3, curbuf('line_count')) - curbuf('del_line', -1) - eq(2, curbuf('line_count')) - curbuf('del_line', -1) - curbuf('del_line', -1) + eq(1, curbuf_depr('line_count')) + curbuf_depr('insert', -1, {'line'}) + eq(2, curbuf_depr('line_count')) + curbuf_depr('insert', -1, {'line'}) + eq(3, curbuf_depr('line_count')) + curbuf_depr('del_line', -1) + eq(2, curbuf_depr('line_count')) + curbuf_depr('del_line', -1) + curbuf_depr('del_line', -1) -- There's always at least one line - eq(1, curbuf('line_count')) + eq(1, curbuf_depr('line_count')) end) end) describe('{get,set,del}_line', function() it('works', function() - eq('', curbuf('get_line', 0)) - curbuf('set_line', 0, 'line1') - eq('line1', curbuf('get_line', 0)) - curbuf('set_line', 0, 'line2') - eq('line2', curbuf('get_line', 0)) - curbuf('del_line', 0) - eq('', curbuf('get_line', 0)) + eq('', curbuf_depr('get_line', 0)) + curbuf_depr('set_line', 0, 'line1') + eq('line1', curbuf_depr('get_line', 0)) + curbuf_depr('set_line', 0, 'line2') + eq('line2', curbuf_depr('get_line', 0)) + curbuf_depr('del_line', 0) + eq('', curbuf_depr('get_line', 0)) end) it('get_line: out-of-bounds is an error', function() - curbuf('set_line', 0, 'line1.a') - eq(1, curbuf('line_count')) -- sanity - eq(false, pcall(curbuf, 'get_line', 1)) - eq(false, pcall(curbuf, 'get_line', -2)) + curbuf_depr('set_line', 0, 'line1.a') + eq(1, curbuf_depr('line_count')) -- sanity + eq(false, pcall(curbuf_depr, 'get_line', 1)) + eq(false, pcall(curbuf_depr, 'get_line', -2)) end) it('set_line, del_line: out-of-bounds is an error', function() - curbuf('set_line', 0, 'line1.a') - eq(false, pcall(curbuf, 'set_line', 1, 'line1.b')) - eq(false, pcall(curbuf, 'set_line', -2, 'line1.b')) - eq(false, pcall(curbuf, 'del_line', 2)) - eq(false, pcall(curbuf, 'del_line', -3)) + curbuf_depr('set_line', 0, 'line1.a') + eq(false, pcall(curbuf_depr, 'set_line', 1, 'line1.b')) + eq(false, pcall(curbuf_depr, 'set_line', -2, 'line1.b')) + eq(false, pcall(curbuf_depr, 'del_line', 2)) + eq(false, pcall(curbuf_depr, 'del_line', -3)) end) it('can handle NULs', function() - curbuf('set_line', 0, 'ab\0cd') - eq('ab\0cd', curbuf('get_line', 0)) + curbuf_depr('set_line', 0, 'ab\0cd') + eq('ab\0cd', curbuf_depr('get_line', 0)) end) end) describe('{get,set}_line_slice', function() it('get_line_slice: out-of-bounds returns empty array', function() - curbuf('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) - eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 2, true, true)) --sanity - - eq({}, curbuf('get_line_slice', 2, 3, false, true)) - eq({}, curbuf('get_line_slice', 3, 9, true, true)) - eq({}, curbuf('get_line_slice', 3, -1, true, true)) - eq({}, curbuf('get_line_slice', -3, -4, false, true)) - eq({}, curbuf('get_line_slice', -4, -5, true, true)) + curbuf_depr('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, curbuf_depr('get_line_slice', 0, 2, true, true)) --sanity + + eq({}, curbuf_depr('get_line_slice', 2, 3, false, true)) + eq({}, curbuf_depr('get_line_slice', 3, 9, true, true)) + eq({}, curbuf_depr('get_line_slice', 3, -1, true, true)) + eq({}, curbuf_depr('get_line_slice', -3, -4, false, true)) + eq({}, curbuf_depr('get_line_slice', -4, -5, true, true)) end) it('set_line_slice: out-of-bounds extends past end', function() - curbuf('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) - eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 2, true, true)) --sanity - - eq({'c'}, curbuf('get_line_slice', -1, 4, true, true)) - eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, 5, true, true)) - curbuf('set_line_slice', 4, 5, true, true, {'d'}) - eq({'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) - curbuf('set_line_slice', -4, -5, true, true, {'e'}) - eq({'e', 'a', 'b', 'c', 'd'}, curbuf('get_line_slice', 0, 5, true, true)) + curbuf_depr('set_line_slice', 0, 0, true, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, curbuf_depr('get_line_slice', 0, 2, true, true)) --sanity + + eq({'c'}, curbuf_depr('get_line_slice', -1, 4, true, true)) + eq({'a', 'b', 'c'}, curbuf_depr('get_line_slice', 0, 5, true, true)) + curbuf_depr('set_line_slice', 4, 5, true, true, {'d'}) + eq({'a', 'b', 'c', 'd'}, curbuf_depr('get_line_slice', 0, 5, true, true)) + curbuf_depr('set_line_slice', -4, -5, true, true, {'e'}) + eq({'e', 'a', 'b', 'c', 'd'}, curbuf_depr('get_line_slice', 0, 5, true, true)) end) it('works', function() - eq({''}, curbuf('get_line_slice', 0, -1, true, true)) + eq({''}, curbuf_depr('get_line_slice', 0, -1, true, true)) -- Replace buffer - curbuf('set_line_slice', 0, -1, true, true, {'a', 'b', 'c'}) - eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, -1, true, true)) - eq({'b', 'c'}, curbuf('get_line_slice', 1, -1, true, true)) - eq({'b'}, curbuf('get_line_slice', 1, 2, true, false)) - eq({}, curbuf('get_line_slice', 1, 1, true, false)) - eq({'a', 'b'}, curbuf('get_line_slice', 0, -1, true, false)) - eq({'b'}, curbuf('get_line_slice', 1, -1, true, false)) - eq({'b', 'c'}, curbuf('get_line_slice', -2, -1, true, true)) - curbuf('set_line_slice', 1, 2, true, false, {'a', 'b', 'c'}) - eq({'a', 'a', 'b', 'c', 'c'}, curbuf('get_line_slice', 0, -1, true, true)) - curbuf('set_line_slice', -1, -1, true, true, {'a', 'b', 'c'}) + curbuf_depr('set_line_slice', 0, -1, true, true, {'a', 'b', 'c'}) + eq({'a', 'b', 'c'}, curbuf_depr('get_line_slice', 0, -1, true, true)) + eq({'b', 'c'}, curbuf_depr('get_line_slice', 1, -1, true, true)) + eq({'b'}, curbuf_depr('get_line_slice', 1, 2, true, false)) + eq({}, curbuf_depr('get_line_slice', 1, 1, true, false)) + eq({'a', 'b'}, curbuf_depr('get_line_slice', 0, -1, true, false)) + eq({'b'}, curbuf_depr('get_line_slice', 1, -1, true, false)) + eq({'b', 'c'}, curbuf_depr('get_line_slice', -2, -1, true, true)) + curbuf_depr('set_line_slice', 1, 2, true, false, {'a', 'b', 'c'}) + eq({'a', 'a', 'b', 'c', 'c'}, curbuf_depr('get_line_slice', 0, -1, true, true)) + curbuf_depr('set_line_slice', -1, -1, true, true, {'a', 'b', 'c'}) eq({'a', 'a', 'b', 'c', 'a', 'b', 'c'}, - curbuf('get_line_slice', 0, -1, true, true)) - curbuf('set_line_slice', 0, -3, true, false, {}) - eq({'a', 'b', 'c'}, curbuf('get_line_slice', 0, -1, true, true)) - curbuf('set_line_slice', 0, -1, true, true, {}) - eq({''}, curbuf('get_line_slice', 0, -1, true, true)) + curbuf_depr('get_line_slice', 0, -1, true, true)) + curbuf_depr('set_line_slice', 0, -3, true, false, {}) + eq({'a', 'b', 'c'}, curbuf_depr('get_line_slice', 0, -1, true, true)) + curbuf_depr('set_line_slice', 0, -1, true, true, {}) + eq({''}, curbuf_depr('get_line_slice', 0, -1, true, true)) end) end) @@ -244,6 +250,21 @@ describe('buffer_* functions', function() curbufmeths.del_var('lua') eq(0, funcs.exists('b:lua')) end) + + it('buffer_set_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, request('buffer_set_var', 0, 'lua', val1)) + eq(val1, request('buffer_set_var', 0, 'lua', val2)) + end) + + it('buffer_del_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, request('buffer_set_var', 0, 'lua', val1)) + eq(val1, request('buffer_set_var', 0, 'lua', val2)) + eq(val2, request('buffer_del_var', 0, 'lua')) + end) end) describe('{get,set}_option', function() @@ -277,7 +298,7 @@ describe('buffer_* functions', function() describe('is_valid', function() it('works', function() nvim('command', 'new') - local b = nvim('get_current_buffer') + local b = nvim('get_current_buf') ok(buffer('is_valid', b)) nvim('command', 'bw!') ok(not buffer('is_valid', b)) @@ -286,7 +307,7 @@ describe('buffer_* functions', function() describe('get_mark', function() it('works', function() - curbuf('insert', -1, {'a', 'bit of', 'text'}) + curbuf('set_lines', -1, -1, true, {'a', 'bit of', 'text'}) curwin('set_cursor', {3, 4}) nvim('command', 'mark V') eq({3, 0}, curbuf('get_mark', 'V')) diff --git a/test/functional/api/menu_spec.lua b/test/functional/api/menu_spec.lua index 5b414fb559..d55b7b118a 100644 --- a/test/functional/api/menu_spec.lua +++ b/test/functional/api/menu_spec.lua @@ -1,4 +1,4 @@ -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear = helpers.clear diff --git a/test/functional/api/rpc_fixture.lua b/test/functional/api/rpc_fixture.lua new file mode 100644 index 0000000000..423864740f --- /dev/null +++ b/test/functional/api/rpc_fixture.lua @@ -0,0 +1,38 @@ +local deps_prefix = './.deps/usr' +if os.getenv('DEPS_PREFIX') then + deps_prefix = os.getenv('DEPS_PREFIX') +end + +package.path = deps_prefix .. '/share/lua/5.1/?.lua;' .. + deps_prefix .. '/share/lua/5.1/?/init.lua;' .. + package.path + +package.cpath = deps_prefix .. '/lib/lua/5.1/?.so;' .. + package.cpath + +local mpack = require('mpack') +local StdioStream = require('nvim.stdio_stream') +local Session = require('nvim.session') + +local stdio_stream = StdioStream.open() +local session = Session.new(stdio_stream) + +local function on_request(method, args) + if method == 'poll' then + return 'ok' + elseif method == 'write_stderr' then + io.stderr:write(args[1]) + return "done!" + elseif method == "exit" then + session:stop() + return mpack.NIL + end +end + +local function on_notification(event, args) + if event == 'ping' and #args == 0 then + session:notify("vim_eval", "rpcnotify(g:channel, 'pong')") + end +end + +session:run(on_request, on_notification) diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 6791fbb4ba..78639d7ed7 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -1,8 +1,8 @@ --- Tests for nvim notifications -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local eq, clear, eval, execute, nvim, next_message = helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.nvim, helpers.next_message +local meths = helpers.meths describe('notify', function() local channel @@ -36,5 +36,33 @@ describe('notify', function() eval('rpcnotify(0, "event1", 13, 14, 15)') eq({'notification', 'event1', {13, 14, 15}}, next_message()) end) + + it('does not crash for deeply nested variable', function() + meths.set_var('l', {}) + local nest_level = 1000 + meths.command(('call map(range(%u), "extend(g:, {\'l\': [g:l]})")'):format(nest_level - 1)) + eval('rpcnotify('..channel..', "event", g:l)') + local msg = next_message() + eq('notification', msg[1]) + eq('event', msg[2]) + local act_ret = msg[3] + local act_nest_level = 0 + while act_ret do + if type(act_ret) == 'table' then + local cur_act_ret = nil + for k, v in pairs(act_ret) do + eq(1, k) + cur_act_ret = v + end + if cur_act_ret then + act_nest_level = act_nest_level + 1 + end + act_ret = cur_act_ret + else + eq(nil, act_ret) + end + end + eq(nest_level, act_nest_level) + end) end) end) diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 1b33275803..76a335a8f4 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -1,11 +1,12 @@ --- Tests for some server->client RPC scenarios. Note that unlike with --- `rpcnotify`, to evaluate `rpcrequest` calls we need the client event loop to --- be running. -local helpers = require('test.functional.helpers') +-- Test server -> client RPC scenarios. Note: unlike `rpcnotify`, to evaluate +-- `rpcrequest` calls we need the client event loop to be running. +local helpers = require('test.functional.helpers')(after_each) local clear, nvim, eval = helpers.clear, helpers.nvim, helpers.eval local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop -local nvim_prog = helpers.nvim_prog - +local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.funcs +local source, next_message = helpers.source, helpers.next_message +local ok = helpers.ok +local meths = helpers.meths describe('server -> client', function() local cid @@ -137,12 +138,18 @@ describe('server -> client', function() end) describe('when the client is a recursive vim instance', function() + if os.getenv("TRAVIS") and helpers.os_name() == "osx" then + -- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86. + pending("[Hangs on Travis macOS. #5002]", function() end) + return + end + before_each(function() - nvim('command', "let vim = rpcstart('"..nvim_prog.."', ['-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--embed'])") + command("let vim = rpcstart('"..nvim_prog.."', ['-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--embed'])") neq(0, eval('vim')) end) - after_each(function() nvim('command', 'call rpcstop(vim)') end) + after_each(function() command('call rpcstop(vim)') end) it('can send/recieve notifications and make requests', function() nvim('command', "call rpcnotify(vim, 'vim_set_current_line', 'SOME TEXT')") @@ -154,11 +161,12 @@ describe('server -> client', function() end) it('can communicate buffers, tabpages, and windows', function() - eq({3}, eval("rpcrequest(vim, 'vim_get_tabpages')")) - eq({1}, eval("rpcrequest(vim, 'vim_get_windows')")) + eq({1}, eval("rpcrequest(vim, 'nvim_list_tabpages')")) + -- Window IDs start at 1000 (LOWEST_WIN_ID in vim.h) + eq({1000}, eval("rpcrequest(vim, 'nvim_list_wins')")) - local buf = eval("rpcrequest(vim, 'vim_get_buffers')")[1] - eq(2, buf) + local buf = eval("rpcrequest(vim, 'nvim_list_bufs')")[1] + eq(1, buf) eval("rpcnotify(vim, 'buffer_set_line', "..buf..", 0, 'SOME TEXT')") nvim('command', "call rpcrequest(vim, 'vim_eval', '0')") -- wait @@ -172,7 +180,47 @@ describe('server -> client', function() it('returns an error if the request failed', function() local status, err = pcall(eval, "rpcrequest(vim, 'does-not-exist')") eq(false, status) - eq(true, string.match(err, ': (.*)') == 'Failed to evaluate expression') + ok(nil ~= string.match(err, 'Failed to evaluate expression')) + end) + end) + + describe('when using jobstart', function() + local jobid + before_each(function() + local channel = nvim('get_api_info')[1] + nvim('set_var', 'channel', channel) + source([[ + function! s:OnEvent(id, data, event) + call rpcnotify(g:channel, a:event, 0, a:data) + endfunction + let g:job_opts = { + \ 'on_stderr': function('s:OnEvent'), + \ 'on_exit': function('s:OnEvent'), + \ 'user': 0, + \ 'rpc': v:true + \ } + ]]) + local lua_prog = arg[-1] + meths.set_var("args", {lua_prog, 'test/functional/api/rpc_fixture.lua'}) + jobid = eval("jobstart(g:args, g:job_opts)") + neq(0, 'jobid') + end) + + after_each(function() + funcs.jobstop(jobid) + end) + + if helpers.pending_win32(pending) then return end + + it('rpc and text stderr can be combined', function() + eq("ok",funcs.rpcrequest(jobid, "poll")) + funcs.rpcnotify(jobid, "ping") + eq({'notification', 'pong', {}}, next_message()) + eq("done!",funcs.rpcrequest(jobid, "write_stderr", "fluff\n")) + eq({'notification', 'stderr', {0, {'fluff', ''}}}, next_message()) + funcs.rpcrequest(jobid, "exit") + eq({'notification', 'exit', {0, 0}}, next_message()) end) end) + end) diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua index c782107714..e10f30085f 100644 --- a/test/functional/api/tabpage_spec.lua +++ b/test/functional/api/tabpage_spec.lua @@ -1,25 +1,26 @@ --- Sanity checks for tabpage_* API calls via msgpack-rpc -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear, nvim, tabpage, curtab, eq, ok = helpers.clear, helpers.nvim, helpers.tabpage, helpers.curtab, helpers.eq, helpers.ok local curtabmeths = helpers.curtabmeths local funcs = helpers.funcs +local request = helpers.request +local NIL = helpers.NIL -describe('tabpage_* functions', function() +describe('api/tabpage', function() before_each(clear) - describe('get_windows and get_window', function() + describe('list_wins and get_win', function() it('works', function() nvim('command', 'tabnew') nvim('command', 'vsplit') - local tab1, tab2 = unpack(nvim('get_tabpages')) - local win1, win2, win3 = unpack(nvim('get_windows')) - eq({win1}, tabpage('get_windows', tab1)) - eq({win2, win3}, tabpage('get_windows', tab2)) - eq(win2, tabpage('get_window', tab2)) - nvim('set_current_window', win3) - eq(win3, tabpage('get_window', tab2)) + local tab1, tab2 = unpack(nvim('list_tabpages')) + local win1, win2, win3 = unpack(nvim('list_wins')) + eq({win1}, tabpage('list_wins', tab1)) + eq({win2, win3}, tabpage('list_wins', tab2)) + eq(win2, tabpage('get_win', tab2)) + nvim('set_current_win', win3) + eq(win3, tabpage('get_win', tab2)) end) end) @@ -32,12 +33,43 @@ describe('tabpage_* functions', function() curtabmeths.del_var('lua') eq(0, funcs.exists('t:lua')) end) + + it('tabpage_set_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, request('tabpage_set_var', 0, 'lua', val1)) + eq(val1, request('tabpage_set_var', 0, 'lua', val2)) + end) + + it('tabpage_del_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, request('tabpage_set_var', 0, 'lua', val1)) + eq(val1, request('tabpage_set_var', 0, 'lua', val2)) + eq(val2, request('tabpage_del_var', 0, 'lua')) + end) + end) + + describe('get_number', function() + it('works', function() + local tabs = nvim('list_tabpages') + eq(1, tabpage('get_number', tabs[1])) + + nvim('command', 'tabnew') + local tab1, tab2 = unpack(nvim('list_tabpages')) + eq(1, tabpage('get_number', tab1)) + eq(2, tabpage('get_number', tab2)) + + nvim('command', '-tabmove') + eq(2, tabpage('get_number', tab1)) + eq(1, tabpage('get_number', tab2)) + end) end) describe('is_valid', function() it('works', function() nvim('command', 'tabnew') - local tab = nvim('get_tabpages')[2] + local tab = nvim('list_tabpages')[2] nvim('set_current_tabpage', tab) ok(tabpage('is_valid', tab)) nvim('command', 'tabclose') diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua new file mode 100644 index 0000000000..3efd00ddbe --- /dev/null +++ b/test/functional/api/version_spec.lua @@ -0,0 +1,71 @@ +local helpers = require('test.functional.helpers')(after_each) +local mpack = require('mpack') +local clear, funcs, eq = helpers.clear, helpers.funcs, helpers.eq + +local function read_mpack_file(fname) + local fd = io.open(fname, 'rb') + local data = fd:read('*a') + fd:close() + local unpack = mpack.Unpacker() + return unpack(data) +end + +-- Remove metadata that is not essential to backwards-compatibility. +local function remove_function_metadata(fspec) + fspec['can_fail'] = nil + fspec['async'] = nil + fspec['method'] = nil + fspec['since'] = nil + fspec['deprecated_since'] = nil + fspec['receives_channel_id'] = nil + for idx, _ in ipairs(fspec['parameters']) do + fspec['parameters'][idx][2] = '' -- Remove parameter name. + end + return fspec +end + +describe("api_info()['version']", function() + before_each(clear) + + it("returns API level", function() + local version = helpers.call('api_info')['version'] + local current = version['api_level'] + local compat = version['api_compatible'] + eq("number", type(current)) + eq("number", type(compat)) + assert(current >= compat) + end) + + it("returns Nvim version", function() + local version = helpers.call('api_info')['version'] + local major = version['major'] + local minor = version['minor'] + local patch = version['patch'] + eq("number", type(major)) + eq("number", type(minor)) + eq("number", type(patch)) + eq(1, funcs.has("nvim-"..major.."."..minor.."."..patch)) + eq(0, funcs.has("nvim-"..major.."."..minor.."."..(patch + 1))) + eq(0, funcs.has("nvim-"..major.."."..(minor + 1).."."..patch)) + eq(0, funcs.has("nvim-"..(major + 1).."."..minor.."."..patch)) + end) + + it("api_compatible level is valid", function() + local api = helpers.call('api_info') + local compat = api['version']['api_compatible'] + local path = 'test/functional/fixtures/api_level_' + ..tostring(compat)..'.mpack' + + -- Verify that the current API function signatures match those of the API + -- level for which we claim compatibility. + local old_api = read_mpack_file(path) + for _, fn_old in ipairs(old_api['functions']) do + for _, fn_new in ipairs(api['functions']) do + if fn_old['name'] == fn_new['name'] then + eq(remove_function_metadata(fn_old), + remove_function_metadata(fn_new)) + end + end + end + end) +end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 20de6d0072..ce6c52e334 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,5 +1,4 @@ --- Sanity checks for vim_* API calls via msgpack-rpc -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq @@ -7,13 +6,14 @@ local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed local os_name = helpers.os_name local meths = helpers.meths local funcs = helpers.funcs +local request = helpers.request -describe('vim_* functions', function() +describe('api', function() before_each(clear) - describe('command', function() + describe('nvim_command', function() it('works', function() - local fname = os.tmpname() + local fname = helpers.tmpname() nvim('command', 'new') nvim('command', 'edit '..fname) nvim('command', 'normal itesting\napi') @@ -28,9 +28,18 @@ describe('vim_* functions', function() f:close() os.remove(fname) end) + + it("VimL error: fails (VimL error), does NOT update v:errmsg", function() + -- Most API methods return generic errors (or no error) if a VimL + -- expression fails; nvim_command returns the VimL error details. + local status, rv = pcall(nvim, "command", "bogus_command") + eq(false, status) -- nvim_command() failed. + eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned. + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + end) end) - describe('eval', function() + describe('nvim_eval', function() it('works', function() nvim('command', 'let g:v1 = "a"') nvim('command', 'let g:v2 = [1, 2, {"v3": 3}]') @@ -41,18 +50,45 @@ describe('vim_* functions', function() eq(1, nvim('eval',"matcharg(1) == ['', '']")) eq({'', ''}, nvim('eval','matcharg(1)')) end) + + it('works under deprecated name', function() + eq(2, request("vim_eval", "1+1")) + end) + + it("VimL error: fails (generic error), does NOT update v:errmsg", function() + local status, rv = pcall(nvim, "eval", "bogus expression") + eq(false, status) -- nvim_eval() failed. + ok(nil ~= string.find(rv, "Failed to evaluate expression")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + end) end) - describe('call_function', function() + describe('nvim_call_function', function() it('works', function() nvim('call_function', 'setqflist', {{{ filename = 'something', lnum = 17}}, 'r'}) eq(17, nvim('call_function', 'getqflist', {})[1].lnum) eq(17, nvim('call_function', 'eval', {17})) eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'})) end) + + it("VimL error: fails (generic error), does NOT update v:errmsg", function() + local status, rv = pcall(nvim, "call_function", "bogus function", {"arg1"}) + eq(false, status) -- nvim_call_function() failed. + ok(nil ~= string.find(rv, "Error calling function")) + eq("", nvim("eval", "v:errmsg")) -- v:errmsg was not updated. + 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>") + local v_errnum = string.match(nvim("eval", "v:errmsg"), "E%d*:") + eq(true, status) -- nvim_input() did not fail. + eq("E117:", v_errnum) -- v:errmsg was updated. + end) end) - describe('strwidth', function() + describe('nvim_strwidth', function() it('works', function() eq(3, nvim('strwidth', 'abc')) -- 6 + (neovim) @@ -65,7 +101,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_current_line', function() + describe('nvim_get_current_line, nvim_set_current_line', function() it('works', function() eq('', nvim('get_current_line')) nvim('set_current_line', 'abc') @@ -73,7 +109,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set,del}_var', function() + describe('nvim_get_var, nvim_set_var, nvim_del_var', function() it('works', function() nvim('set_var', 'lua', {1, 2, {['3'] = 1}}) eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua')) @@ -83,19 +119,19 @@ describe('vim_* functions', function() eq(0, funcs.exists('g:lua')) end) - it('set_var returns the old value', function() + it('vim_set_var returns the old value', function() local val1 = {1, 2, {['3'] = 1}} local val2 = {4, 7} - eq(NIL, nvim('set_var', 'lua', val1)) - eq(val1, nvim('set_var', 'lua', val2)) + eq(NIL, request('vim_set_var', 'lua', val1)) + eq(val1, request('vim_set_var', 'lua', val2)) end) - it('del_var returns the old value', function() + it('vim_del_var returns the old value', function() local val1 = {1, 2, {['3'] = 1}} local val2 = {4, 7} - eq(NIL, meths.set_var('lua', val1)) - eq(val1, meths.set_var('lua', val2)) - eq(val2, meths.del_var('lua')) + eq(NIL, request('vim_set_var', 'lua', val1)) + eq(val1, request('vim_set_var', 'lua', val2)) + eq(val2, request('vim_del_var', 'lua')) end) it('truncates values with NULs in them', function() @@ -104,7 +140,7 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_option', function() + describe('nvim_get_option, nvim_set_option', function() it('works', function() ok(nvim('get_option', 'equalalways')) nvim('set_option', 'equalalways', false) @@ -112,51 +148,51 @@ describe('vim_* functions', function() end) end) - describe('{get,set}_current_buffer and get_buffers', function() + describe('nvim_{get,set}_current_buf, nvim_list_bufs', function() it('works', function() - eq(1, #nvim('get_buffers')) - eq(nvim('get_buffers')[1], nvim('get_current_buffer')) + eq(1, #nvim('list_bufs')) + eq(nvim('list_bufs')[1], nvim('get_current_buf')) nvim('command', 'new') - eq(2, #nvim('get_buffers')) - eq(nvim('get_buffers')[2], nvim('get_current_buffer')) - nvim('set_current_buffer', nvim('get_buffers')[1]) - eq(nvim('get_buffers')[1], nvim('get_current_buffer')) + eq(2, #nvim('list_bufs')) + eq(nvim('list_bufs')[2], nvim('get_current_buf')) + nvim('set_current_buf', nvim('list_bufs')[1]) + eq(nvim('list_bufs')[1], nvim('get_current_buf')) end) end) - describe('{get,set}_current_window and get_windows', function() + describe('nvim_{get,set}_current_win, nvim_list_wins', function() it('works', function() - eq(1, #nvim('get_windows')) - eq(nvim('get_windows')[1], nvim('get_current_window')) + eq(1, #nvim('list_wins')) + eq(nvim('list_wins')[1], nvim('get_current_win')) nvim('command', 'vsplit') nvim('command', 'split') - eq(3, #nvim('get_windows')) - eq(nvim('get_windows')[1], nvim('get_current_window')) - nvim('set_current_window', nvim('get_windows')[2]) - eq(nvim('get_windows')[2], nvim('get_current_window')) + eq(3, #nvim('list_wins')) + eq(nvim('list_wins')[1], nvim('get_current_win')) + nvim('set_current_win', nvim('list_wins')[2]) + eq(nvim('list_wins')[2], nvim('get_current_win')) end) end) - describe('{get,set}_current_tabpage and get_tabpages', function() + describe('nvim_{get,set}_current_tabpage, nvim_list_tabpages', function() it('works', function() - eq(1, #nvim('get_tabpages')) - eq(nvim('get_tabpages')[1], nvim('get_current_tabpage')) + eq(1, #nvim('list_tabpages')) + eq(nvim('list_tabpages')[1], nvim('get_current_tabpage')) nvim('command', 'tabnew') - eq(2, #nvim('get_tabpages')) - eq(2, #nvim('get_windows')) - eq(nvim('get_windows')[2], nvim('get_current_window')) - eq(nvim('get_tabpages')[2], nvim('get_current_tabpage')) - nvim('set_current_window', nvim('get_windows')[1]) + eq(2, #nvim('list_tabpages')) + eq(2, #nvim('list_wins')) + eq(nvim('list_wins')[2], nvim('get_current_win')) + eq(nvim('list_tabpages')[2], nvim('get_current_tabpage')) + nvim('set_current_win', nvim('list_wins')[1]) -- Switching window also switches tabpages if necessary - eq(nvim('get_tabpages')[1], nvim('get_current_tabpage')) - eq(nvim('get_windows')[1], nvim('get_current_window')) - nvim('set_current_tabpage', nvim('get_tabpages')[2]) - eq(nvim('get_tabpages')[2], nvim('get_current_tabpage')) - eq(nvim('get_windows')[2], nvim('get_current_window')) + eq(nvim('list_tabpages')[1], nvim('get_current_tabpage')) + eq(nvim('list_wins')[1], nvim('get_current_win')) + nvim('set_current_tabpage', nvim('list_tabpages')[2]) + eq(nvim('list_tabpages')[2], nvim('get_current_tabpage')) + eq(nvim('list_wins')[2], nvim('get_current_win')) end) end) - describe('replace_termcodes', function() + describe('nvim_replace_termcodes', function() it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function() eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true)) end) @@ -178,7 +214,7 @@ describe('vim_* functions', function() end) end) - describe('feedkeys', function() + describe('nvim_feedkeys', function() it('CSI escaping', function() local function on_setup() -- notice the special char(…) \xe2\80\xa6 @@ -205,7 +241,7 @@ describe('vim_* functions', function() end) end) - describe('err_write', function() + describe('nvim_err_write', function() local screen before_each(function() @@ -213,22 +249,22 @@ describe('vim_* functions', function() screen = Screen.new(40, 8) screen:attach() screen:set_default_attr_ids({ + [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {foreground = Screen.colors.White, background = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.SeaGreen} }) - screen:set_default_attr_ignore( {{bold=true, foreground=Screen.colors.Blue}} ) end) it('can show one line', function() nvim_async('err_write', 'has bork\n') screen:expect([[ ^ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| {1:has bork} | ]]) end) @@ -236,11 +272,11 @@ describe('vim_* functions', function() it('shows return prompt when more than &cmdheight lines', function() nvim_async('err_write', 'something happened\nvery bad\n') screen:expect([[ - ~ | - ~ | - ~ | - ~ | - ~ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| {1:something happened} | {1:very bad} | {2:Press ENTER or type command to continue}^ | @@ -250,9 +286,9 @@ describe('vim_* functions', function() it('shows return prompt after all lines are shown', function() nvim_async('err_write', 'FAILURE\nERROR\nEXCEPTION\nTRACEBACK\n') screen:expect([[ - ~ | - ~ | - ~ | + {0:~ }| + {0:~ }| + {0:~ }| {1:FAILURE} | {1:ERROR} | {1:EXCEPTION} | @@ -267,12 +303,12 @@ describe('vim_* functions', function() nvim_async('err_write', 'fail\n') screen:expect([[ ^ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| {1:very fail} | ]]) helpers.wait() @@ -280,11 +316,11 @@ describe('vim_* functions', function() -- shows up to &cmdheight lines nvim_async('err_write', 'more fail\ntoo fail\n') screen:expect([[ - ~ | - ~ | - ~ | - ~ | - ~ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| {1:more fail} | {1:too fail} | {2:Press ENTER or type command to continue}^ | @@ -293,9 +329,104 @@ describe('vim_* functions', function() end) end) + describe('nvim_call_atomic', function() + it('works', function() + meths.buf_set_lines(0, 0, -1, true, {'first'}) + local req = { + {'nvim_get_current_line', {}}, + {'nvim_set_current_line', {'second'}}, + } + eq({{'first', NIL}, NIL}, meths.call_atomic(req)) + eq({'second'}, meths.buf_get_lines(0, 0, -1, true)) + end) + + it('allows multiple return values', function() + local req = { + {'nvim_set_var', {'avar', true}}, + {'nvim_set_var', {'bvar', 'string'}}, + {'nvim_get_var', {'avar'}}, + {'nvim_get_var', {'bvar'}}, + } + eq({{NIL, NIL, true, 'string'}, NIL}, meths.call_atomic(req)) + end) + + it('is aborted by errors in call', function() + local error_types = meths.get_api_info()[2].error_types + local req = { + {'nvim_set_var', {'one', 1}}, + {'nvim_buf_set_lines', {}}, + {'nvim_set_var', {'two', 2}}, + } + eq({{NIL}, {1, error_types.Exception.id, + 'Wrong number of arguments: expecting 5 but got 0'}}, + meths.call_atomic(req)) + eq(1, meths.get_var('one')) + eq(false, pcall(meths.get_var, 'two')) + + -- still returns all previous successful calls + req = { + {'nvim_set_var', {'avar', 5}}, + {'nvim_set_var', {'bvar', 'string'}}, + {'nvim_get_var', {'avar'}}, + {'nvim_buf_get_lines', {0, 10, 20, true}}, + {'nvim_get_var', {'bvar'}}, + } + eq({{NIL, NIL, 5}, {3, error_types.Validation.id, 'Index out of bounds'}}, + meths.call_atomic(req)) + + req = { + {'i_am_not_a_method', {'xx'}}, + {'nvim_set_var', {'avar', 10}}, + } + eq({{}, {0, error_types.Exception.id, 'Invalid method name'}}, + meths.call_atomic(req)) + eq(5, meths.get_var('avar')) + end) + + it('throws error on malformated arguments', function() + local req = { + {'nvim_set_var', {'avar', 1}}, + {'nvim_set_var'}, + {'nvim_set_var', {'avar', 2}}, + } + local status, err = pcall(meths.call_atomic, req) + eq(false, status) + ok(err:match(' All items in calls array must be arrays of size 2') ~= nil) + -- call before was done, but not after + eq(1, meths.get_var('avar')) + + req = { + {'nvim_set_var', {'bvar', {2,3}}}, + 12, + } + status, err = pcall(meths.call_atomic, req) + eq(false, status) + ok(err:match('All items in calls array must be arrays') ~= nil) + eq({2,3}, meths.get_var('bvar')) + + req = { + {'nvim_set_current_line', 'little line'}, + {'nvim_set_var', {'avar', 3}}, + } + status, err = pcall(meths.call_atomic, req) + eq(false, status) + ok(err:match('args must be Array') ~= nil) + -- call before was done, but not after + eq(1, meths.get_var('avar')) + eq({''}, meths.buf_get_lines(0, 0, -1, true)) + end) + end) + it('can throw exceptions', function() local status, err = pcall(nvim, 'get_option', 'invalid-option') eq(false, status) ok(err:match('Invalid option name') ~= nil) end) + + it("doesn't leak memory on incorrect argument types", function() + local status, err = pcall(nvim, 'set_current_dir',{'not', 'a', 'dir'}) + eq(false, status) + ok(err:match(': Wrong type for argument 1, expecting String') ~= nil) + end) + end) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 92a33b4cdb..465bda6bc9 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -1,5 +1,4 @@ --- Sanity checks for window_* API calls via msgpack-rpc -local helpers = require('test.functional.helpers') +local helpers = require('test.functional.helpers')(after_each) local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, @@ -7,6 +6,8 @@ local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, local wait = helpers.wait local curwinmeths = helpers.curwinmeths local funcs = helpers.funcs +local request = helpers.request +local NIL = helpers.NIL -- check if str is visible at the beginning of some line local function is_visible(str) @@ -27,17 +28,17 @@ local function is_visible(str) return false end -describe('window_* functions', function() +describe('api/win', function() before_each(clear) - describe('get_buffer', function() + describe('get_buf', function() it('works', function() - eq(curbuf(), window('get_buffer', nvim('get_windows')[1])) + eq(curbuf(), window('get_buf', nvim('list_wins')[1])) nvim('command', 'new') - nvim('set_current_window', nvim('get_windows')[2]) - eq(curbuf(), window('get_buffer', nvim('get_windows')[2])) - neq(window('get_buffer', nvim('get_windows')[1]), - window('get_buffer', nvim('get_windows')[2])) + nvim('set_current_win', nvim('list_wins')[2]) + eq(curbuf(), window('get_buf', nvim('list_wins')[2])) + neq(window('get_buf', nvim('list_wins')[1]), + window('get_buf', nvim('list_wins')[2])) end) end) @@ -103,28 +104,28 @@ describe('window_* functions', function() describe('{get,set}_height', function() it('works', function() nvim('command', 'vsplit') - eq(window('get_height', nvim('get_windows')[2]), - window('get_height', nvim('get_windows')[1])) - nvim('set_current_window', nvim('get_windows')[2]) + eq(window('get_height', nvim('list_wins')[2]), + window('get_height', nvim('list_wins')[1])) + nvim('set_current_win', nvim('list_wins')[2]) nvim('command', 'split') - eq(window('get_height', nvim('get_windows')[2]), - math.floor(window('get_height', nvim('get_windows')[1]) / 2)) - window('set_height', nvim('get_windows')[2], 2) - eq(2, window('get_height', nvim('get_windows')[2])) + eq(window('get_height', nvim('list_wins')[2]), + math.floor(window('get_height', nvim('list_wins')[1]) / 2)) + window('set_height', nvim('list_wins')[2], 2) + eq(2, window('get_height', nvim('list_wins')[2])) end) end) describe('{get,set}_width', function() it('works', function() nvim('command', 'split') - eq(window('get_width', nvim('get_windows')[2]), - window('get_width', nvim('get_windows')[1])) - nvim('set_current_window', nvim('get_windows')[2]) + eq(window('get_width', nvim('list_wins')[2]), + window('get_width', nvim('list_wins')[1])) + nvim('set_current_win', nvim('list_wins')[2]) nvim('command', 'vsplit') - eq(window('get_width', nvim('get_windows')[2]), - math.floor(window('get_width', nvim('get_windows')[1]) / 2)) - window('set_width', nvim('get_windows')[2], 2) - eq(2, window('get_width', nvim('get_windows')[2])) + eq(window('get_width', nvim('list_wins')[2]), + math.floor(window('get_width', nvim('list_wins')[1]) / 2)) + window('set_width', nvim('list_wins')[2], 2) + eq(2, window('get_width', nvim('list_wins')[2])) end) end) @@ -137,6 +138,21 @@ describe('window_* functions', function() curwinmeths.del_var('lua') eq(0, funcs.exists('w:lua')) end) + + it('window_set_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, request('window_set_var', 0, 'lua', val1)) + eq(val1, request('window_set_var', 0, 'lua', val2)) + end) + + it('window_del_var returns the old value', function() + local val1 = {1, 2, {['3'] = 1}} + local val2 = {4, 7} + eq(NIL, request('window_set_var', 0, 'lua', val1)) + eq(val1, request('window_set_var', 0, 'lua', val2)) + eq(val2, request('window_del_var', 0, 'lua')) + end) end) describe('{get,set}_option', function() @@ -152,17 +168,17 @@ describe('window_* functions', function() describe('get_position', function() it('works', function() - local height = window('get_height', nvim('get_windows')[1]) - local width = window('get_width', nvim('get_windows')[1]) + local height = window('get_height', nvim('list_wins')[1]) + local width = window('get_width', nvim('list_wins')[1]) nvim('command', 'split') nvim('command', 'vsplit') - eq({0, 0}, window('get_position', nvim('get_windows')[1])) + eq({0, 0}, window('get_position', nvim('list_wins')[1])) local vsplit_pos = math.floor(width / 2) local split_pos = math.floor(height / 2) local win2row, win2col = - unpack(window('get_position', nvim('get_windows')[2])) + unpack(window('get_position', nvim('list_wins')[2])) local win3row, win3col = - unpack(window('get_position', nvim('get_windows')[3])) + unpack(window('get_position', nvim('list_wins')[3])) eq(0, win2row) eq(0, win3col) ok(vsplit_pos - 1 <= win2col and win2col <= vsplit_pos + 1) @@ -175,19 +191,43 @@ describe('window_* functions', function() nvim('command', 'tabnew') nvim('command', 'vsplit') eq(window('get_tabpage', - nvim('get_windows')[1]), nvim('get_tabpages')[1]) + nvim('list_wins')[1]), nvim('list_tabpages')[1]) eq(window('get_tabpage', - nvim('get_windows')[2]), nvim('get_tabpages')[2]) + nvim('list_wins')[2]), nvim('list_tabpages')[2]) eq(window('get_tabpage', - nvim('get_windows')[3]), nvim('get_tabpages')[2]) + nvim('list_wins')[3]), nvim('list_tabpages')[2]) + end) + end) + + describe('get_number', function() + it('works', function() + local wins = nvim('list_wins') + eq(1, window('get_number', wins[1])) + + nvim('command', 'split') + local win1, win2 = unpack(nvim('list_wins')) + eq(1, window('get_number', win1)) + eq(2, window('get_number', win2)) + + nvim('command', 'wincmd J') + eq(2, window('get_number', win1)) + eq(1, window('get_number', win2)) + + nvim('command', 'tabnew') + local win3 = nvim('list_wins')[3] + -- First tab page + eq(2, window('get_number', win1)) + eq(1, window('get_number', win2)) + -- Second tab page + eq(1, window('get_number', win3)) end) end) describe('is_valid', function() it('works', function() nvim('command', 'split') - local win = nvim('get_windows')[2] - nvim('set_current_window', win) + local win = nvim('list_wins')[2] + nvim('set_current_win', win) ok(window('is_valid', win)) nvim('command', 'close') ok(not window('is_valid', win)) |