aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/api-python.sh32
-rw-r--r--.travis.yml1
-rw-r--r--CMakeLists.txt10
-rw-r--r--cmake/RunTests.cmake4
-rw-r--r--scripts/run-functional-tests.py93
-rw-r--r--test/functional/api/buffer_spec.lua118
-rw-r--r--test/functional/api/server_notifications_spec.lua40
-rw-r--r--test/functional/api/server_requests_spec.lua68
-rw-r--r--test/functional/api/tabpage_spec.lua42
-rw-r--r--test/functional/api/vim_spec.lua108
-rw-r--r--test/functional/api/window_spec.lua124
-rw-r--r--test/functional/helpers.lua174
-rw-r--r--third-party/CMakeLists.txt9
13 files changed, 683 insertions, 140 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/CMakeLists.txt b/CMakeLists.txt
index 87dd600606..9e79362940 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -228,17 +228,9 @@ if(BUSTED_PRG)
-P ${CMAKE_MODULE_PATH}/RunTests.cmake
DEPENDS nvim-test unittest-headers)
- # For the functional tests we need the full path to the real busted script,
- # which will be included by run-functional-tests.py.
- get_filename_component(LUA_PRG_DIR ${LUA_PRG} PATH)
- get_filename_component(LUA_PREFIX_DIR ${LUA_PRG_DIR} PATH)
- file(GLOB_RECURSE BUSTED_REAL_PRG
- ${LUA_PREFIX_DIR}/lib/luarocks/rocks/busted/*busted)
-
add_custom_target(test
COMMAND ${CMAKE_COMMAND}
- -DBUSTED_PRG=${PROJECT_SOURCE_DIR}/scripts/run-functional-tests.py
- -DBUSTED_REAL_PRG=${BUSTED_REAL_PRG}
+ -DBUSTED_PRG=${BUSTED_PRG}
-DWORKING_DIR=${CMAKE_CURRENT_SOURCE_DIR}
-DBUSTED_OUTPUT_TYPE=${BUSTED_OUTPUT_TYPE}
-DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake
index b89957bb28..e2c548d7e6 100644
--- a/cmake/RunTests.cmake
+++ b/cmake/RunTests.cmake
@@ -6,8 +6,8 @@ endif()
if(TEST_TYPE STREQUAL "functional")
execute_process(
- COMMAND python ${BUSTED_PRG} ${BUSTED_REAL_PRG} -v -o
- ${BUSTED_OUTPUT_TYPE} --lpath=${BUILD_DIR}/?.lua ${TEST_DIR}/functional
+ COMMAND ${BUSTED_PRG} -v -o ${BUSTED_OUTPUT_TYPE}
+ --lpath=${BUILD_DIR}/?.lua ${TEST_DIR}/functional
WORKING_DIRECTORY ${WORKING_DIR}
RESULT_VARIABLE res)
else()
diff --git a/scripts/run-functional-tests.py b/scripts/run-functional-tests.py
deleted file mode 100644
index 3e931b248c..0000000000
--- a/scripts/run-functional-tests.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Run functional tests using lua, busted and the python client
-
-import os
-import sys
-import textwrap
-
-from lupa import LuaRuntime, as_attrgetter
-from neovim import Nvim, spawn_session
-
-
-# Extract arguments
-busted_script = sys.argv[1]
-busted_argv = sys.argv[2:]
-
-# Setup a lua state for running busted
-lua = LuaRuntime(unpack_returned_tuples=True)
-lua_globals = lua.globals()
-
-# helper to transform iterables into lua tables
-list_to_table = lua.eval('''
-function(l)
- local t = {}
- for i, item in python.enumerate(l) do t[i + 1] = item end
- return t
-end
-''')
-
-dict_to_table = lua.eval('''
-function(d)
- local t = {}
- for k, v in python.iterex(d.items()) do t[k] = v end
- return t
-end
-''')
-
-def to_table(obj):
- if type(obj) in [tuple, list]:
- return list_to_table(list(to_table(e) for e in obj))
- if type(obj) is dict:
- return dict_to_table(as_attrgetter(
- dict((k, to_table(v)) for k, v in obj.items())))
- return obj
-
-nvim_prog = os.environ.get('NVIM_PROG', 'build/bin/nvim')
-nvim_argv = [nvim_prog, '-u', 'NONE', '--embed']
-
-if 'VALGRIND' in os.environ:
- log_file = os.environ.get('VALGRIND_LOG', 'valgrind-%p.log')
- valgrind_argv = ['valgrind', '-q', '--tool=memcheck', '--leak-check=yes',
- '--track-origins=yes', '--suppressions=.valgrind.supp',
- '--log-file={0}'.format(log_file)]
- if 'VALGRIND_GDB' in os.environ:
- valgrind_argv += ['--vgdb=yes', '--vgdb-error=0']
- nvim_argv = valgrind_argv + nvim_argv
-
-session = spawn_session(nvim_argv)
-nvim = Nvim.from_session(session)
-
-def nvim_command(cmd):
- nvim.command(cmd)
-
-def nvim_eval(expr):
- return to_table(nvim.eval(expr))
-
-def nvim_feed(input, mode=''):
- nvim.feedkeys(input)
-
-def buffer_slice(start=None, stop=None, buffer_idx=None):
- rv = '\n'.join(nvim.buffers[buffer_idx or 0][start:stop])
- return rv
-
-def nvim_replace_termcodes(input, *opts):
- return nvim.replace_termcodes(input, *opts)
-
-expose = [
- nvim_command,
- nvim_eval,
- nvim_feed,
- nvim_replace_termcodes,
- buffer_slice,
- textwrap.dedent,
-]
-
-for fn in expose:
- lua_globals[fn.__name__] = fn
-
-# Set 'arg' global to let busted parse arguments
-lua_globals['arg'] = list_to_table(busted_argv)
-
-# Read the busted script and execute in the lua state
-with open(busted_script) as f:
- busted_setup = f.read()
-lua.execute(busted_setup)
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 671e34e592..324af6a232 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -1,3 +1,110 @@
+local Loop = require('nvim.loop')
+local MsgpackStream = require('nvim.msgpack_stream')
+local AsyncSession = require('nvim.async_session')
+local Session = require('nvim.session')
+
+local nvim_prog = os.getenv('NVIM_PROG') or 'build/bin/nvim'
+local nvim_argv = {nvim_prog, '-u', 'NONE', '-N', '--embed'}
+
+if os.getenv('VALGRIND') then
+ local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log'
+ local valgrind_argv = {'valgrind', '-q', '--tool=memcheck',
+ '--leak-check=yes', '--track-origins=yes',
+ '--suppressions=.valgrind.supp',
+ '--log-file='..log_file}
+ if os.getenv('VALGRIND_GDB') then
+ table.insert(valgrind_argv, '--vgdb=yes')
+ table.insert(valgrind_argv, '--vgdb-error=0')
+ end
+ local len = #valgrind_argv
+ for i = 1, #nvim_argv do
+ valgrind_argv[i + len] = nvim_argv[i]
+ end
+ nvim_argv = valgrind_argv
+end
+
+local session
+
+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, ...)
+ if not status then
+ error(rv[2])
+ end
+ 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
+
+local function nvim_eval(expr)
+ return request('vim_eval', expr)
+end
+
+local function nvim_feed(input, mode)
+ mode = mode or ''
+ request('vim_feedkeys', input, mode)
+end
+
+local function buffer_slice(start, stop, buffer_idx)
+ local include_end = false
+ if not stop then
+ stop = -1
+ include_end = true
+ end
+ local buffer = request('vim_get_buffers')[buffer_idx or 1]
+ local slice = request('buffer_get_line_slice', buffer, start or 0, stop,
+ true, include_end)
+ return table.concat(slice, '\n')
+end
+
+local function nvim_replace_termcodes(input)
+ return request('vim_replace_termcodes', input, false, true, true )
+end
+
+local function dedent(str)
+ -- find minimum common indent across lines
+ local indent = nil
+ for line in str:gmatch('[^\n]+') do
+ local line_indent = line:match('^%s+') or ''
+ if indent == nil or #line_indent < #indent then
+ indent = line_indent
+ end
+ end
+ if #indent == 0 then
+ -- no minimum common indent
+ return str
+ end
+ -- create a pattern for the indent
+ indent = indent:gsub('%s', '%%s')
+ -- strip it from the first line
+ str = str:gsub('^'..indent, '')
+ -- strip it from the remaining lines
+ str = str:gsub('[\n]'..indent, '\n')
+ return str
+end
+
local function clear()
nvim_command('call BeforeEachTest()')
end
@@ -48,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()
@@ -89,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
}
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index 793be3172d..0e277c8eef 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -189,7 +189,14 @@ if(USE_BUNDLED_LUAROCKS)
add_custom_target(lpeg
DEPENDS ${DEPS_LIB_DIR}/luarocks/rocks/lpeg)
- list(APPEND THIRD_PARTY_DEPS busted lua-messagepack lpeg)
+ add_custom_command(OUTPUT ${DEPS_LIB_DIR}/luarocks/rocks/nvim-client
+ COMMAND ${DEPS_BIN_DIR}/luarocks
+ ARGS build https://raw.githubusercontent.com/neovim/lua-client/master/nvim-client-0.0.1-1.rockspec CC=${DEPS_C_COMPILER} LD=${DEPS_C_COMPILER} LIBUV_DIR=${DEPS_INSTALL_DIR}
+ DEPENDS lpeg libuv)
+ add_custom_target(nvim-client
+ DEPENDS ${DEPS_LIB_DIR}/luarocks/rocks/nvim-client)
+
+ list(APPEND THIRD_PARTY_DEPS busted lua-messagepack lpeg nvim-client)
endif()
add_custom_target(third-party ALL