diff options
-rw-r--r-- | .ci/api-python.sh | 32 | ||||
-rw-r--r-- | .travis.yml | 1 | ||||
-rw-r--r-- | test/functional/api/buffer_spec.lua | 118 | ||||
-rw-r--r-- | test/functional/api/server_notifications_spec.lua | 40 | ||||
-rw-r--r-- | test/functional/api/server_requests_spec.lua | 68 | ||||
-rw-r--r-- | test/functional/api/tabpage_spec.lua | 42 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 108 | ||||
-rw-r--r-- | test/functional/api/window_spec.lua | 124 | ||||
-rw-r--r-- | test/functional/helpers.lua | 83 |
9 files changed, 580 insertions, 36 deletions
diff --git a/.ci/api-python.sh b/.ci/api-python.sh deleted file mode 100644 index acdbcc2666..0000000000 --- a/.ci/api-python.sh +++ /dev/null @@ -1,32 +0,0 @@ -. "$CI_SCRIPTS/common.sh" - -set_environment /opt/neovim-deps/64 - -sudo apt-get install expect valgrind - -$MAKE_CMD - -git clone --depth=1 -b master git://github.com/neovim/python-client -cd python-client -sudo pip install . -sudo pip install nose -# We run the tests twice: -# - First by connecting with an nvim instance spawned by "expect" -# - Second by starting nvim in embedded mode through the python client -# This is required until nvim is mature enough to always run in embedded mode -test_cmd="nosetests --verbosity=2 --nologcapture" -nvim_cmd="valgrind -q --track-origins=yes --leak-check=yes --suppressions=$suppressions --log-file=$tmpdir/valgrind-%p.log ../build/bin/nvim -u NONE" -if ! ../scripts/run-api-tests.exp "$test_cmd" "$nvim_cmd"; then - valgrind_check "$tmpdir" - exit 1 -fi - -valgrind_check "$tmpdir" - -export NVIM_SPAWN_ARGV="[\"valgrind\", \"-q\", \"--track-origins=yes\", \"--leak-check=yes\", \"--suppressions=$suppressions\", \"--log-file=$tmpdir/valgrind-%p.log\", \"../build/bin/nvim\", \"-u\", \"NONE\", \"--embed\"]" -if ! nosetests --verbosity=2 --nologcapture; then - valgrind_check "$tmpdir" - exit 1 -fi - -valgrind_check "$tmpdir" diff --git a/.travis.yml b/.travis.yml index 8c3b986aa4..2746cba4ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ env: - CI_TARGET=gcc - CI_TARGET=gcc-32 - CI_TARGET=clint - - CI_TARGET=api-python - CI_TARGET=coverity before_install: # Adds user to a dummy group. diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua new file mode 100644 index 0000000000..169d605b63 --- /dev/null +++ b/test/functional/api/buffer_spec.lua @@ -0,0 +1,118 @@ +-- Sanity checks for buffer_* API calls via msgpack-rpc +local helpers = require('test.functional.helpers') +local clear, nvim, buffer, curbuf, curwin, eq, ok = + helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, helpers.curwin, + helpers.eq, helpers.ok + +describe('buffer_* functions', function() + before_each(clear) + + 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) + -- There's always at least one line + eq(1, curbuf('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)) + end) + end) + + + describe('{get,set}_line_slice', function() + it('works', function() + eq({''}, curbuf('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'}) + 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)) + end) + end) + + describe('{get,set}_var', function() + it('works', function() + curbuf('set_var', 'lua', {1, 2, {['3'] = 1}}) + eq({1, 2, {['3'] = 1}}, curbuf('get_var', 'lua')) + eq({1, 2, {['3'] = 1}}, nvim('eval', 'b:lua')) + end) + end) + + describe('{get,set}_option', function() + it('works', function() + eq(8, curbuf('get_option', 'shiftwidth')) + curbuf('set_option', 'shiftwidth', 4) + eq(4, curbuf('get_option', 'shiftwidth')) + -- global-local option + curbuf('set_option', 'define', 'test') + eq('test', curbuf('get_option', 'define')) + -- Doesn't change the global value + eq([[^\s*#\s*define]], nvim('get_option', 'define')) + end) + end) + + describe('{get,set}_name', function() + it('works', function() + nvim('command', 'new') + eq('', curbuf('get_name')) + local new_name = nvim('eval', 'resolve(tempname())') + curbuf('set_name', new_name) + eq(new_name, curbuf('get_name')) + nvim('command', 'w!') + local f = io.open(new_name) + ok(f ~= nil) + f:close() + os.remove(new_name) + end) + end) + + describe('is_valid', function() + it('works', function() + nvim('command', 'new') + local b = nvim('get_current_buffer') + ok(buffer('is_valid', b)) + nvim('command', 'bw!') + ok(not buffer('is_valid', b)) + end) + end) + + describe('get_mark', function() + it('works', function() + curbuf('insert', -1, {'a', 'bit of', 'text'}) + curwin('set_cursor', {3, 4}) + nvim('command', 'mark V') + eq({3, 0}, curbuf('get_mark', 'V')) + end) + end) +end) diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua new file mode 100644 index 0000000000..6791fbb4ba --- /dev/null +++ b/test/functional/api/server_notifications_spec.lua @@ -0,0 +1,40 @@ +-- Tests for nvim notifications +local helpers = require('test.functional.helpers') +local eq, clear, eval, execute, nvim, next_message = + helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.nvim, + helpers.next_message + +describe('notify', function() + local channel + + before_each(function() + clear() + channel = nvim('get_api_info')[1] + end) + + describe('passing a valid channel id', function() + it('sends the notification/args to the corresponding channel', function() + eval('rpcnotify('..channel..', "test-event", 1, 2, 3)') + eq({'notification', 'test-event', {1, 2, 3}}, next_message()) + execute('au FileType lua call rpcnotify('..channel..', "lua!")') + execute('set filetype=lua') + eq({'notification', 'lua!', {}}, next_message()) + end) + end) + + describe('passing 0 as the channel id', function() + it('sends the notification/args to all subscribed channels', function() + nvim('subscribe', 'event2') + eval('rpcnotify(0, "event1", 1, 2, 3)') + eval('rpcnotify(0, "event2", 4, 5, 6)') + eval('rpcnotify(0, "event2", 7, 8, 9)') + eq({'notification', 'event2', {4, 5, 6}}, next_message()) + eq({'notification', 'event2', {7, 8, 9}}, next_message()) + nvim('unsubscribe', 'event2') + nvim('subscribe', 'event1') + eval('rpcnotify(0, "event2", 10, 11, 12)') + eval('rpcnotify(0, "event1", 13, 14, 15)') + eq({'notification', 'event1', {13, 14, 15}}, next_message()) + end) + end) +end) diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua new file mode 100644 index 0000000000..b6f56a868c --- /dev/null +++ b/test/functional/api/server_requests_spec.lua @@ -0,0 +1,68 @@ +-- 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') +local clear, nvim, eval, eq, run, stop = helpers.clear, helpers.nvim, + helpers.eval, helpers.eq, helpers.run, helpers.stop + + +describe('server -> client', function() + local cid + + before_each(function() + clear() + cid = nvim('get_api_info')[1] + end) + + describe('simple call', function() + it('works', function() + local function on_setup() + eq({4, 5, 6}, eval('rpcrequest('..cid..', "scall", 1, 2, 3)')) + stop() + end + + local function on_request(method, args) + eq('scall', method) + eq({1, 2, 3}, args) + nvim('command', 'let g:result = [4, 5, 6]') + return eval('g:result') + end + run(on_request, nil, on_setup) + end) + end) + + describe('recursive call', function() + it('works', function() + local function on_setup() + nvim('set_var', 'result1', 0) + nvim('set_var', 'result2', 0) + nvim('set_var', 'result3', 0) + nvim('set_var', 'result4', 0) + nvim('command', 'let g:result1 = rpcrequest('..cid..', "rcall", 2)') + eq(4, nvim('get_var', 'result1')) + eq(8, nvim('get_var', 'result2')) + eq(16, nvim('get_var', 'result3')) + eq(32, nvim('get_var', 'result4')) + stop() + end + + local function on_request(method, args) + eq('rcall', method) + local n = unpack(args) * 2 + if n <= 16 then + local cmd + if n == 4 then + cmd = 'let g:result2 = rpcrequest('..cid..', "rcall", '..n..')' + elseif n == 8 then + cmd = 'let g:result3 = rpcrequest('..cid..', "rcall", '..n..')' + elseif n == 16 then + cmd = 'let g:result4 = rpcrequest('..cid..', "rcall", '..n..')' + end + nvim('command', cmd) + end + return n + end + run(on_request, nil, on_setup) + end) + end) +end) diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua new file mode 100644 index 0000000000..9937e0c72e --- /dev/null +++ b/test/functional/api/tabpage_spec.lua @@ -0,0 +1,42 @@ +-- Sanity checks for tabpage_* API calls via msgpack-rpc +local helpers = require('test.functional.helpers') +local clear, nvim, tabpage, curtab, eq, ok = + helpers.clear, helpers.nvim, helpers.tabpage, helpers.curtab, helpers.eq, + helpers.ok + +describe('tabpage_* functions', function() + before_each(clear) + + describe('get_windows and get_window', 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)) + end) + end) + + describe('{get,set}_var', function() + it('works', function() + curtab('set_var', 'lua', {1, 2, {['3'] = 1}}) + eq({1, 2, {['3'] = 1}}, curtab('get_var', 'lua')) + eq({1, 2, {['3'] = 1}}, nvim('eval', 't:lua')) + end) + end) + + describe('is_valid', function() + it('works', function() + nvim('command', 'tabnew') + local tab = nvim('get_tabpages')[2] + nvim('set_current_tabpage', tab) + ok(tabpage('is_valid', tab)) + nvim('command', 'tabclose') + ok(not tabpage('is_valid', tab)) + end) + end) +end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua new file mode 100644 index 0000000000..6a61607d2e --- /dev/null +++ b/test/functional/api/vim_spec.lua @@ -0,0 +1,108 @@ +-- Sanity checks for vim_* API calls via msgpack-rpc +local helpers = require('test.functional.helpers') +local clear, nvim, eq, ok = helpers.clear, helpers.nvim, helpers.eq, helpers.ok + + +describe('vim_* functions', function() + before_each(clear) + + describe('command', function() + it('works', function() + local fname = os.tmpname() + nvim('command', 'new') + nvim('command', 'edit '..fname) + nvim('command', 'normal itesting\napi') + nvim('command', 'w') + local f = io.open(fname) + ok(f ~= nil) + eq('testing\napi\n', f:read('*a')) + f:close() + os.remove(fname) + end) + end) + + describe('eval', function() + it('works', function() + nvim('command', 'let g:v1 = "a"') + nvim('command', 'let g:v2 = [1, 2, {"v3": 3}]') + eq({v1 = 'a', v2 = {1, 2, {v3 = 3}}}, nvim('eval', 'g:')) + end) + end) + + describe('strwidth', function() + it('works', function() + eq(3, nvim('strwidth', 'abc')) + -- 6 + (neovim) + -- 19 * 2 (each japanese character occupies two cells) + eq(44, nvim('strwidth', 'neovimのデザインかなりまともなのになってる。')) + end) + end) + + describe('{get,set}_current_line', function() + it('works', function() + eq('', nvim('get_current_line')) + nvim('set_current_line', 'abc') + eq('abc', nvim('get_current_line')) + end) + end) + + describe('{get,set}_var', function() + it('works', function() + nvim('set_var', 'lua', {1, 2, {['3'] = 1}}) + eq({1, 2, {['3'] = 1}}, nvim('get_var', 'lua')) + eq({1, 2, {['3'] = 1}}, nvim('eval', 'g:lua')) + end) + end) + + describe('{get,set}_option', function() + it('works', function() + ok(nvim('get_option', 'equalalways')) + nvim('set_option', 'equalalways', false) + ok(not nvim('get_option', 'equalalways')) + end) + end) + + describe('{get,set}_current_buffer and get_buffers', function() + it('works', function() + eq(1, #nvim('get_buffers')) + eq(nvim('get_buffers')[1], nvim('get_current_buffer')) + 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')) + end) + end) + + describe('{get,set}_current_window and get_windows', function() + it('works', function() + eq(1, #nvim('get_windows')) + eq(nvim('get_windows')[1], nvim('get_current_window')) + 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')) + end) + end) + + describe('{get,set}_current_tabpage and get_tabpages', function() + it('works', function() + eq(1, #nvim('get_tabpages')) + eq(nvim('get_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]) + -- 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')) + end) + end) +end) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua new file mode 100644 index 0000000000..a3814cce0f --- /dev/null +++ b/test/functional/api/window_spec.lua @@ -0,0 +1,124 @@ +-- Sanity checks for window_* API calls via msgpack-rpc +local helpers = require('test.functional.helpers') +local clear, nvim, buffer, curbuf, curbuf_contents, window, curwin, eq, neq, + ok = helpers.clear, helpers.nvim, helpers.buffer, helpers.curbuf, + helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, + helpers.neq, helpers.ok + +describe('window_* functions', function() + before_each(clear) + + describe('get_buffer', function() + it('works', function() + eq(curbuf(), window('get_buffer', nvim('get_windows')[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])) + end) + end) + + describe('{get,set}_cursor', function() + it('works', function() + eq({1, 0}, curwin('get_cursor')) + nvim('command', 'normal ityping\027o some text') + eq('typing\n some text', curbuf_contents()) + eq({2, 10}, curwin('get_cursor')) + curwin('set_cursor', {2, 6}) + nvim('command', 'normal i dumb') + eq('typing\n some dumb text', curbuf_contents()) + end) + end) + + 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]) + nvim('command', 'split') + eq(window('get_height', nvim('get_windows')[2]), + 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])) + 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]) + nvim('command', 'vsplit') + eq(window('get_width', nvim('get_windows')[2]), + 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])) + end) + end) + + describe('{get,set}_var', function() + it('works', function() + curwin('set_var', 'lua', {1, 2, {['3'] = 1}}) + eq({1, 2, {['3'] = 1}}, curwin('get_var', 'lua')) + eq({1, 2, {['3'] = 1}}, nvim('eval', 'w:lua')) + end) + end) + + describe('{get,set}_option', function() + it('works', function() + curwin('set_option', 'colorcolumn', '4,3') + eq('4,3', curwin('get_option', 'colorcolumn')) + -- global-local option + curwin('set_option', 'statusline', 'window-status') + eq('window-status', curwin('get_option', 'statusline')) + eq('', nvim('get_option', 'statusline')) + end) + end) + + 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]) + nvim('command', 'split') + nvim('command', 'vsplit') + eq({0, 0}, window('get_position', nvim('get_windows')[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])) + local win3row, win3col = + unpack(window('get_position', nvim('get_windows')[3])) + eq(0, win2row) + eq(0, win3col) + ok(vsplit_pos - 1 <= win2col and win2col <= vsplit_pos + 1) + ok(split_pos - 1 <= win3row and win3row <= split_pos + 1) + end) + end) + + describe('get_position', function() + it('works', function() + nvim('command', 'tabnew') + nvim('command', 'vsplit') + eq(window('get_tabpage', + nvim('get_windows')[1]), nvim('get_tabpages')[1]) + eq(window('get_tabpage', + nvim('get_windows')[2]), nvim('get_tabpages')[2]) + eq(window('get_tabpage', + nvim('get_windows')[3]), nvim('get_tabpages')[2]) + end) + end) + + describe('is_valid', function() + it('works', function() + nvim('command', 'split') + local win = nvim('get_windows')[2] + nvim('set_current_window', win) + ok(window('is_valid', win)) + nvim('command', 'close') + ok(not window('is_valid', win)) + end) + end) +end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 6aa78804e7..324af6a232 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -24,13 +24,15 @@ if os.getenv('VALGRIND') then end local session -do + +local function restart() local loop = Loop.new() local msgpack_stream = MsgpackStream.new(loop) local async_session = AsyncSession.new(msgpack_stream) session = Session.new(async_session) loop:spawn(nvim_argv) end +restart() local function request(method, ...) local status, rv = session:request(method, ...) @@ -40,6 +42,18 @@ local function request(method, ...) return rv end +local function next_message() + return session:next_message() +end + +local function run(request_cb, notification_cb, setup_cb) + session:run(request_cb, notification_cb, setup_cb) +end + +local function stop() + session:stop() +end + local function nvim_command(cmd) request('vim_command', cmd) end @@ -141,7 +155,7 @@ local function neq(expected, actual) end local function expect(contents, first, last, buffer_index) - return eq(dedent(contents), buffer_slice(first, last, buffer_idx)) + return eq(dedent(contents), buffer_slice(first, last, buffer_index)) end rawfeed([[:function BeforeEachTest() @@ -182,14 +196,77 @@ rawfeed([[:function BeforeEachTest() endfunction ]]) + +local function ok(expr) + assert.is_true(expr) +end + +local function nvim(method, ...) + return request('vim_'..method, ...) +end + +local function buffer(method, ...) + return request('buffer_'..method, ...) +end + +local function window(method, ...) + return request('window_'..method, ...) +end + +local function tabpage(method, ...) + return request('tabpage_'..method, ...) +end + +local function curbuf(method, ...) + local buf = nvim('get_current_buffer') + if not method then + return buf + end + return buffer(method, buf, ...) +end + +local function curbuf_contents() + return table.concat(curbuf('get_line_slice', 0, -1, true, true), '\n') +end + +local function curwin(method, ...) + local win = nvim('get_current_window') + if not method then + return win + end + return window(method, win, ...) +end + +local function curtab(method, ...) + local tab = nvim('get_current_tabpage') + if not method then + return tab + end + return tabpage(method, tab, ...) +end + return { clear = clear, + restart = restart, rawfeed = rawfeed, insert = insert, feed = feed, execute = execute, eval = eval, + request = request, + next_message = next_message, + run = run, + stop = stop, eq = eq, neq = neq, - expect = expect + expect = expect, + ok = ok, + nvim = nvim, + buffer = buffer, + window = window, + tabpage = tabpage, + curbuf = curbuf, + curwin = curwin, + curtab = curtab, + curbuf_contents = curbuf_contents } |