2014-11-06 19:54:49 -07:00
|
|
|
require('coxpcall')
|
2016-12-14 08:56:00 -07:00
|
|
|
local luv = require('luv')
|
2015-11-17 15:31:22 -07:00
|
|
|
local lfs = require('lfs')
|
2016-04-23 16:53:11 -07:00
|
|
|
local global_helpers = require('test.helpers')
|
|
|
|
|
2016-10-22 09:15:46 -07:00
|
|
|
-- nvim client: Found in .deps/usr/share/lua/<version>/nvim/ if "bundled".
|
|
|
|
local Session = require('nvim.session')
|
|
|
|
local TcpStream = require('nvim.tcp_stream')
|
|
|
|
local SocketStream = require('nvim.socket_stream')
|
|
|
|
local ChildProcessStream = require('nvim.child_process_stream')
|
2017-04-08 15:55:19 -07:00
|
|
|
local Paths = require('test.config.paths')
|
2016-10-22 09:15:46 -07:00
|
|
|
|
2016-11-04 08:20:58 -07:00
|
|
|
local check_cores = global_helpers.check_cores
|
2016-04-23 16:53:11 -07:00
|
|
|
local check_logs = global_helpers.check_logs
|
2018-04-27 01:07:26 -07:00
|
|
|
local dedent = global_helpers.dedent
|
2016-04-23 16:53:11 -07:00
|
|
|
local eq = global_helpers.eq
|
2018-04-11 13:07:00 -07:00
|
|
|
local expect_err = global_helpers.expect_err
|
2018-04-27 01:07:26 -07:00
|
|
|
local filter = global_helpers.filter
|
put fixup, esp. ". register close #5709 #5781
Note some bugs were judged to have too ugly a fix to solve, tests to
demonstrate these problems, and the explanation behind not fixing them
are below.
describe('register . problems', function()
before_each(reset)
-- The difficulty here is: The basic requirement is that the text
-- inserted is treated as if it were typed in insert mode. This is why
-- the paste method is to enter insert mode and enter the ". register
-- into readbuf1.
-- We can't add a count into the readbuf here because the insert mode
-- count is implemented with readbuf2 which is checked for characters
-- after readbuf1.
-- Hence, the ".gp command (which adds extra characters into readbuf1
-- to emulate leaving the cursor after the text by moving the cursor
-- after inserting the text) would insert the motion characters into
-- the buffer instead of using them to move after the insert has been
-- done.
-- I could probably get this working properly with a special flag put
-- into start_redo_ins() and set in do_put(), but I think this adds
-- much more complexity than fixing this bug justifies.
pending('should not change the ". register with ".2p', function()
local orig_register = funcs.getreg('.')
feed('2".p')
eq(orig_register, funcs.getreg('.'))
end)
describe("cursor positioning after undo and redo with '.'", function()
before_each(reset)
local function make_cursor_test(macro_string)
return function()
feed(macro_string)
local afterpos = funcs.getcurpos()
local orig_string = curbuf_contents()
feed('u.')
eq(afterpos, funcs.getcurpos())
expect(orig_string)
end
end
-- The difficulty here is: setting the cursor after the end of the
-- pasted text is done by adding a motion command to the
-- stuffbuffer after the insert.
-- Modifying 'redobuff' is done in the code that handles inserting
-- text and moving around.
-- I could add a special case in ins_esc() that checks for a flag
-- set in do_put() to add the motion character to the redo buffer,
-- but I think that is starting to get way too convoluted for the
-- benefit.
pending('should be the same after ".gp and ".gpu.',
make_cursor_test('".gp'))
-- The difficulty here is: putting forwards is implemented by using
-- 'a' instead of 'i' to start insert.
-- Undoing with 'u' an insert that began with 'a' leaves the cursor
-- where the first character was inserted, not where the cursor was
-- when the 'a' was pressed.
-- We account for this the first time by saving the cursor position
-- in do_put(), but this isn't stored in redobuff for a second time
-- around.
-- We can't change how such a fundamental action as undo after
-- inserting with 'a' behaves, we could add in a special case
-- whereby we set a flag in do_put() and read it when entering
-- insert mode but this seems like way too much to fix such a minor
-- bug.
pending('should be the same after ".pu. and ".pu.u.',
make_cursor_test('".pu.'))
end)
end)
2016-12-12 07:04:44 -07:00
|
|
|
local map = global_helpers.map
|
2018-04-10 16:43:15 -07:00
|
|
|
local matches = global_helpers.matches
|
2018-04-27 01:07:26 -07:00
|
|
|
local neq = global_helpers.neq
|
|
|
|
local ok = global_helpers.ok
|
|
|
|
local read_file = global_helpers.read_file
|
|
|
|
local sleep = global_helpers.sleep
|
2018-02-16 11:42:05 -07:00
|
|
|
local table_flatten = global_helpers.table_flatten
|
2018-04-27 01:07:26 -07:00
|
|
|
local write_file = global_helpers.write_file
|
2014-10-08 08:56:28 -07:00
|
|
|
|
2017-01-03 22:35:21 -07:00
|
|
|
local start_dir = lfs.currentdir()
|
2017-02-11 17:02:54 -07:00
|
|
|
-- XXX: NVIM_PROG takes precedence, QuickBuild sets it.
|
2017-04-08 15:55:19 -07:00
|
|
|
local nvim_prog = (
|
|
|
|
os.getenv('NVIM_PROG')
|
|
|
|
or os.getenv('NVIM_PRG')
|
|
|
|
or Paths.test_build_dir .. '/bin/nvim'
|
|
|
|
)
|
2017-02-09 19:39:00 -07:00
|
|
|
-- Default settings for the test session.
|
|
|
|
local nvim_set = 'set shortmess+=I background=light noswapfile noautoindent'
|
|
|
|
..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.'
|
2017-03-30 10:00:34 -07:00
|
|
|
..' belloff= noshowcmd noruler nomore'
|
2015-01-22 04:53:04 -07:00
|
|
|
local nvim_argv = {nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
|
2017-02-09 19:39:00 -07:00
|
|
|
'--cmd', nvim_set, '--embed'}
|
2017-04-09 16:38:20 -07:00
|
|
|
-- Directory containing nvim.
|
2015-02-28 07:39:04 -07:00
|
|
|
local nvim_dir = nvim_prog:gsub("[/\\][^/\\]+$", "")
|
|
|
|
if nvim_dir == nvim_prog then
|
2017-01-22 06:13:10 -07:00
|
|
|
nvim_dir = "."
|
2015-02-28 07:39:04 -07:00
|
|
|
end
|
|
|
|
|
2017-04-09 16:38:20 -07:00
|
|
|
local mpack = require('mpack')
|
|
|
|
local tmpname = global_helpers.tmpname
|
|
|
|
local uname = global_helpers.uname
|
2014-11-22 08:29:03 -07:00
|
|
|
local prepend_argv
|
2014-10-08 08:56:28 -07:00
|
|
|
|
|
|
|
if os.getenv('VALGRIND') then
|
|
|
|
local log_file = os.getenv('VALGRIND_LOG') or 'valgrind-%p.log'
|
2014-11-22 08:29:03 -07:00
|
|
|
prepend_argv = {'valgrind', '-q', '--tool=memcheck',
|
|
|
|
'--leak-check=yes', '--track-origins=yes',
|
|
|
|
'--show-possibly-lost=no',
|
2016-06-07 21:24:23 -07:00
|
|
|
'--suppressions=src/.valgrind.supp',
|
2014-11-22 08:29:03 -07:00
|
|
|
'--log-file='..log_file}
|
|
|
|
if os.getenv('GDB') then
|
|
|
|
table.insert(prepend_argv, '--vgdb=yes')
|
|
|
|
table.insert(prepend_argv, '--vgdb-error=0')
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
2014-11-22 08:29:03 -07:00
|
|
|
elseif os.getenv('GDB') then
|
|
|
|
local gdbserver_port = '7777'
|
|
|
|
if os.getenv('GDBSERVER_PORT') then
|
|
|
|
gdbserver_port = os.getenv('GDBSERVER_PORT')
|
|
|
|
end
|
|
|
|
prepend_argv = {'gdbserver', 'localhost:'..gdbserver_port}
|
|
|
|
end
|
|
|
|
|
|
|
|
if prepend_argv then
|
2015-04-26 05:31:39 -07:00
|
|
|
local new_nvim_argv = {}
|
2014-11-22 08:29:03 -07:00
|
|
|
local len = #prepend_argv
|
2015-04-26 05:31:39 -07:00
|
|
|
for i = 1, len do
|
|
|
|
new_nvim_argv[i] = prepend_argv[i]
|
|
|
|
end
|
2014-10-08 08:56:28 -07:00
|
|
|
for i = 1, #nvim_argv do
|
2015-04-26 05:31:39 -07:00
|
|
|
new_nvim_argv[i + len] = nvim_argv[i]
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
2015-04-26 05:31:39 -07:00
|
|
|
nvim_argv = new_nvim_argv
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
|
2015-11-17 15:31:22 -07:00
|
|
|
local session, loop_running, last_error
|
2014-10-08 08:56:28 -07:00
|
|
|
|
2017-04-26 04:10:21 -07:00
|
|
|
local function set_session(s, keep)
|
|
|
|
if session and not keep then
|
2016-04-13 05:21:32 -07:00
|
|
|
session:close()
|
2016-04-10 19:46:11 -07:00
|
|
|
end
|
2015-04-11 21:48:16 -07:00
|
|
|
session = s
|
|
|
|
end
|
|
|
|
|
2014-10-08 08:56:28 -07:00
|
|
|
local function request(method, ...)
|
|
|
|
local status, rv = session:request(method, ...)
|
|
|
|
if not status then
|
2014-10-27 14:34:42 -07:00
|
|
|
if loop_running then
|
|
|
|
last_error = rv[2]
|
|
|
|
session:stop()
|
|
|
|
else
|
|
|
|
error(rv[2])
|
|
|
|
end
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
return rv
|
|
|
|
end
|
|
|
|
|
2018-03-08 16:24:38 -07:00
|
|
|
local function next_msg(timeout)
|
2018-03-08 16:29:20 -07:00
|
|
|
return session:next_message(timeout and timeout or 10000)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2017-06-10 06:25:23 -07:00
|
|
|
local function expect_twostreams(msgs1, msgs2)
|
|
|
|
local pos1, pos2 = 1, 1
|
|
|
|
while pos1 <= #msgs1 or pos2 <= #msgs2 do
|
2018-03-08 16:24:38 -07:00
|
|
|
local msg = next_msg()
|
2017-06-10 06:25:23 -07:00
|
|
|
if pos1 <= #msgs1 and pcall(eq, msgs1[pos1], msg) then
|
|
|
|
pos1 = pos1 + 1
|
|
|
|
elseif pos2 <= #msgs2 then
|
|
|
|
eq(msgs2[pos2], msg)
|
|
|
|
pos2 = pos2 + 1
|
|
|
|
else
|
|
|
|
-- already failed, but show the right error message
|
|
|
|
eq(msgs1[pos1], msg)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-08 16:24:38 -07:00
|
|
|
-- Expects a sequence of next_msg() results. If multiple sequences are
|
2018-02-16 11:42:05 -07:00
|
|
|
-- passed they are tried until one succeeds, in order of shortest to longest.
|
|
|
|
local function expect_msg_seq(...)
|
|
|
|
if select('#', ...) < 1 then
|
|
|
|
error('need at least 1 argument')
|
|
|
|
end
|
|
|
|
local seqs = {...}
|
|
|
|
table.sort(seqs, function(a, b) -- Sort ascending, by (shallow) length.
|
|
|
|
return #a < #b
|
|
|
|
end)
|
|
|
|
|
|
|
|
local actual_seq = {}
|
|
|
|
local final_error = ''
|
|
|
|
local function cat_err(err1, err2)
|
|
|
|
if err1 == nil then
|
|
|
|
return err2
|
|
|
|
end
|
|
|
|
return string.format('%s\n%s\n%s', err1, string.rep('=', 78), err2)
|
|
|
|
end
|
|
|
|
for anum = 1, #seqs do
|
|
|
|
local expected_seq = seqs[anum]
|
|
|
|
-- Collect enough messages to compare the next expected sequence.
|
|
|
|
while #actual_seq < #expected_seq do
|
2018-03-08 16:24:38 -07:00
|
|
|
local msg = next_msg(10000) -- Big timeout for ASAN/valgrind.
|
2018-02-16 11:42:05 -07:00
|
|
|
if msg == nil then
|
|
|
|
error(cat_err(final_error,
|
|
|
|
string.format('got %d messages, expected %d',
|
|
|
|
#actual_seq, #expected_seq)))
|
|
|
|
end
|
|
|
|
table.insert(actual_seq, msg)
|
|
|
|
end
|
|
|
|
local status, result = pcall(eq, expected_seq, actual_seq)
|
|
|
|
if status then
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
final_error = cat_err(final_error, result)
|
|
|
|
end
|
|
|
|
error(final_error)
|
|
|
|
end
|
|
|
|
|
2014-11-06 19:54:49 -07:00
|
|
|
local function call_and_stop_on_error(...)
|
2015-11-17 15:31:22 -07:00
|
|
|
local status, result = copcall(...) -- luacheck: ignore
|
2014-11-06 19:54:49 -07:00
|
|
|
if not status then
|
|
|
|
session:stop()
|
|
|
|
last_error = result
|
|
|
|
return ''
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
2014-12-08 18:31:45 -07:00
|
|
|
local function run(request_cb, notification_cb, setup_cb, timeout)
|
|
|
|
local on_request, on_notification, on_setup
|
2014-11-06 19:54:49 -07:00
|
|
|
|
2014-12-08 18:31:45 -07:00
|
|
|
if request_cb then
|
|
|
|
function on_request(method, args)
|
|
|
|
return call_and_stop_on_error(request_cb, method, args)
|
|
|
|
end
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2014-12-08 18:31:45 -07:00
|
|
|
if notification_cb then
|
|
|
|
function on_notification(method, args)
|
|
|
|
call_and_stop_on_error(notification_cb, method, args)
|
|
|
|
end
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2014-12-08 18:31:45 -07:00
|
|
|
if setup_cb then
|
|
|
|
function on_setup()
|
|
|
|
call_and_stop_on_error(setup_cb)
|
|
|
|
end
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2014-10-27 14:34:42 -07:00
|
|
|
loop_running = true
|
2014-12-08 18:31:45 -07:00
|
|
|
session:run(on_request, on_notification, on_setup, timeout)
|
2014-10-27 14:34:42 -07:00
|
|
|
loop_running = false
|
|
|
|
if last_error then
|
|
|
|
local err = last_error
|
|
|
|
last_error = nil
|
|
|
|
error(err)
|
|
|
|
end
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
local function stop()
|
|
|
|
session:stop()
|
|
|
|
end
|
|
|
|
|
2016-10-22 15:02:22 -07:00
|
|
|
-- Executes an ex-command. VimL errors manifest as client (lua) errors, but
|
|
|
|
-- v:errmsg will not be updated.
|
2014-10-08 08:56:28 -07:00
|
|
|
local function nvim_command(cmd)
|
2016-06-28 12:45:19 -07:00
|
|
|
request('nvim_command', cmd)
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
|
2016-10-22 09:15:46 -07:00
|
|
|
-- Evaluates a VimL expression.
|
|
|
|
-- Fails on VimL error, but does not update v:errmsg.
|
2014-10-08 08:56:28 -07:00
|
|
|
local function nvim_eval(expr)
|
2016-06-28 12:45:19 -07:00
|
|
|
return request('nvim_eval', expr)
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
|
2016-03-06 15:26:23 -07:00
|
|
|
local os_name = (function()
|
|
|
|
local name = nil
|
|
|
|
return (function()
|
|
|
|
if not name then
|
|
|
|
if nvim_eval('has("win32")') == 1 then
|
|
|
|
name = 'windows'
|
|
|
|
elseif nvim_eval('has("macunix")') == 1 then
|
|
|
|
name = 'osx'
|
|
|
|
else
|
|
|
|
name = 'unix'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return name
|
|
|
|
end)
|
|
|
|
end)()
|
|
|
|
|
2017-01-14 01:47:33 -07:00
|
|
|
local function iswin()
|
2017-05-31 09:16:03 -07:00
|
|
|
return package.config:sub(1,1) == '\\'
|
2017-01-14 01:47:33 -07:00
|
|
|
end
|
|
|
|
|
2016-10-22 09:15:46 -07:00
|
|
|
-- Executes a VimL function.
|
|
|
|
-- Fails on VimL error, but does not update v:errmsg.
|
2015-08-23 05:16:46 -07:00
|
|
|
local function nvim_call(name, ...)
|
2016-06-28 12:45:19 -07:00
|
|
|
return request('nvim_call_function', name, {...})
|
2015-08-23 05:16:46 -07:00
|
|
|
end
|
|
|
|
|
2016-10-22 09:15:46 -07:00
|
|
|
-- Sends user input to Nvim.
|
|
|
|
-- Does not fail on VimL error, but v:errmsg will be updated.
|
2014-11-21 09:06:03 -07:00
|
|
|
local function nvim_feed(input)
|
|
|
|
while #input > 0 do
|
2016-06-28 12:45:19 -07:00
|
|
|
local written = request('nvim_input', input)
|
2014-11-21 09:06:03 -07:00
|
|
|
input = input:sub(written + 1)
|
2014-10-08 08:56:28 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-09-29 05:43:52 -07:00
|
|
|
local function feed(...)
|
|
|
|
for _, v in ipairs({...}) do
|
2014-12-08 18:31:45 -07:00
|
|
|
nvim_feed(dedent(v))
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-11-06 19:54:49 -07:00
|
|
|
local function rawfeed(...)
|
2014-09-29 05:43:52 -07:00
|
|
|
for _, v in ipairs({...}) do
|
2014-11-21 09:06:03 -07:00
|
|
|
nvim_feed(dedent(v))
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-15 07:25:10 -07:00
|
|
|
local function merge_args(...)
|
|
|
|
local i = 1
|
|
|
|
local argv = {}
|
|
|
|
for anum = 1,select('#', ...) do
|
|
|
|
local args = select(anum, ...)
|
|
|
|
if args then
|
|
|
|
for _, arg in ipairs(args) do
|
|
|
|
argv[i] = arg
|
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return argv
|
|
|
|
end
|
|
|
|
|
2016-06-26 08:16:54 -07:00
|
|
|
local function spawn(argv, merge, env)
|
|
|
|
local child_stream = ChildProcessStream.spawn(
|
|
|
|
merge and merge_args(prepend_argv, argv) or argv,
|
|
|
|
env)
|
2016-04-13 05:21:32 -07:00
|
|
|
return Session.new(child_stream)
|
2015-03-17 04:45:13 -07:00
|
|
|
end
|
|
|
|
|
2016-06-26 12:35:14 -07:00
|
|
|
-- Creates a new Session connected by domain socket (named pipe) or TCP.
|
|
|
|
local function connect(file_or_address)
|
|
|
|
local addr, port = string.match(file_or_address, "(.*):(%d+)")
|
|
|
|
local stream = (addr and port) and TcpStream.open(addr, port) or
|
|
|
|
SocketStream.open(file_or_address)
|
|
|
|
return Session.new(stream)
|
|
|
|
end
|
|
|
|
|
2016-12-14 08:56:00 -07:00
|
|
|
-- Calls fn() until it succeeds, up to `max` times or until `max_ms`
|
|
|
|
-- milliseconds have passed.
|
|
|
|
local function retry(max, max_ms, fn)
|
|
|
|
local tries = 1
|
|
|
|
local timeout = (max_ms and max_ms > 0) and max_ms or 10000
|
|
|
|
local start_time = luv.now()
|
|
|
|
while true do
|
|
|
|
local status, result = pcall(fn)
|
|
|
|
if status then
|
|
|
|
return result
|
2016-12-07 05:01:03 -07:00
|
|
|
end
|
2017-12-04 14:18:11 -07:00
|
|
|
luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()).
|
2016-12-14 08:56:00 -07:00
|
|
|
if (max and tries >= max) or (luv.now() - start_time > timeout) then
|
2018-04-18 00:47:51 -07:00
|
|
|
error("\nretry() attempts: "..tostring(tries).."\n"..tostring(result))
|
2016-12-14 08:56:00 -07:00
|
|
|
end
|
|
|
|
tries = tries + 1
|
2018-04-20 14:56:50 -07:00
|
|
|
luv.sleep(20) -- Avoid hot loop...
|
2016-12-07 05:01:03 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-05-29 22:21:45 -07:00
|
|
|
-- Starts a new global Nvim session.
|
|
|
|
-- Parameters are interpreted as startup args, OR a map with these keys:
|
|
|
|
-- args: Merged with the default `nvim_argv` set.
|
|
|
|
-- env : Defines the environment of the new session.
|
|
|
|
--
|
|
|
|
-- Example:
|
|
|
|
-- clear('-e')
|
|
|
|
-- clear({args={'-e'}, env={TERM=term}})
|
2016-06-23 02:15:08 -07:00
|
|
|
local function clear(...)
|
2015-08-29 08:10:06 -07:00
|
|
|
local args = {unpack(nvim_argv)}
|
2016-06-26 08:16:54 -07:00
|
|
|
local new_args
|
|
|
|
local env = nil
|
|
|
|
local opts = select(1, ...)
|
|
|
|
if type(opts) == 'table' then
|
|
|
|
if opts.env then
|
|
|
|
local env_tbl = {}
|
|
|
|
for k, v in pairs(opts.env) do
|
|
|
|
assert(type(k) == 'string')
|
|
|
|
assert(type(v) == 'string')
|
|
|
|
env_tbl[k] = v
|
|
|
|
end
|
|
|
|
for _, k in ipairs({
|
|
|
|
'HOME',
|
|
|
|
'ASAN_OPTIONS',
|
|
|
|
'LD_LIBRARY_PATH', 'PATH',
|
|
|
|
'NVIM_LOG_FILE',
|
2016-08-15 20:25:02 -07:00
|
|
|
'NVIM_RPLUGIN_MANIFEST',
|
2016-06-26 08:16:54 -07:00
|
|
|
}) do
|
2017-02-02 20:18:16 -07:00
|
|
|
if not env_tbl[k] then
|
|
|
|
env_tbl[k] = os.getenv(k)
|
|
|
|
end
|
2016-06-26 08:16:54 -07:00
|
|
|
end
|
|
|
|
env = {}
|
|
|
|
for k, v in pairs(env_tbl) do
|
|
|
|
env[#env + 1] = k .. '=' .. v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
new_args = opts.args or {}
|
|
|
|
else
|
|
|
|
new_args = {...}
|
|
|
|
end
|
|
|
|
for _, arg in ipairs(new_args) do
|
2016-06-23 02:15:08 -07:00
|
|
|
table.insert(args, arg)
|
2015-08-29 08:10:06 -07:00
|
|
|
end
|
2016-06-26 08:16:54 -07:00
|
|
|
set_session(spawn(args, nil, env))
|
2014-11-06 19:54:49 -07:00
|
|
|
end
|
|
|
|
|
2014-09-29 05:43:52 -07:00
|
|
|
local function insert(...)
|
2014-11-21 09:06:03 -07:00
|
|
|
nvim_feed('i')
|
2014-12-08 18:31:45 -07:00
|
|
|
for _, v in ipairs({...}) do
|
|
|
|
local escaped = v:gsub('<', '<lt>')
|
|
|
|
rawfeed(escaped)
|
|
|
|
end
|
|
|
|
nvim_feed('<ESC>')
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
|
2016-10-22 09:15:46 -07:00
|
|
|
-- Executes an ex-command by user input. Because nvim_input() is used, VimL
|
|
|
|
-- errors will not manifest as client (lua) errors. Use command() for that.
|
2017-04-08 12:00:50 -07:00
|
|
|
local function feed_command(...)
|
2014-09-29 05:43:52 -07:00
|
|
|
for _, v in ipairs({...}) do
|
|
|
|
if v:sub(1, 1) ~= '/' then
|
|
|
|
-- not a search command, prefix with colon
|
2014-11-21 09:06:03 -07:00
|
|
|
nvim_feed(':')
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
2014-12-08 18:31:45 -07:00
|
|
|
nvim_feed(v:gsub('<', '<lt>'))
|
|
|
|
nvim_feed('<CR>')
|
2014-09-29 05:43:52 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-07-04 05:15:23 -07:00
|
|
|
local sourced_fnames = {}
|
2015-12-29 12:18:16 -07:00
|
|
|
local function source(code)
|
|
|
|
local fname = tmpname()
|
|
|
|
write_file(fname, code)
|
|
|
|
nvim_command('source '..fname)
|
2017-07-04 05:15:23 -07:00
|
|
|
-- DO NOT REMOVE FILE HERE.
|
|
|
|
-- do_source() has a habit of checking whether files are “same” by using inode
|
|
|
|
-- and device IDs. If you run two source() calls in quick succession there is
|
|
|
|
-- a good chance that underlying filesystem will reuse the inode, making files
|
|
|
|
-- appear as “symlinks” to do_source when it checks FileIDs. With current
|
|
|
|
-- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse
|
|
|
|
-- inodes, Mac OS machines (again, both QB and travis) do not.
|
|
|
|
--
|
|
|
|
-- Files appearing as “symlinks” mean that both the first and the second
|
|
|
|
-- source() calls will use same SID, which may fail some tests which check for
|
|
|
|
-- exact numbers after `<SNR>` in e.g. function names.
|
|
|
|
sourced_fnames[#sourced_fnames + 1] = fname
|
2015-12-29 12:18:16 -07:00
|
|
|
return fname
|
2014-11-11 02:12:19 -07:00
|
|
|
end
|
|
|
|
|
2017-01-11 21:08:19 -07:00
|
|
|
local function set_shell_powershell()
|
|
|
|
source([[
|
2018-01-07 08:20:37 -07:00
|
|
|
set shell=powershell shellquote=( shellpipe=\| shellredir=> shellxquote=
|
2018-03-08 18:50:32 -07:00
|
|
|
let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command Remove-Item -Force alias:sleep;'
|
2017-01-11 21:08:19 -07:00
|
|
|
]])
|
|
|
|
end
|
|
|
|
|
2014-10-08 09:56:01 -07:00
|
|
|
local function nvim(method, ...)
|
2016-06-28 12:45:19 -07:00
|
|
|
return request('nvim_'..method, ...)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2016-06-08 02:26:06 -07:00
|
|
|
local function ui(method, ...)
|
|
|
|
return request('nvim_ui_'..method, ...)
|
|
|
|
end
|
|
|
|
|
2015-07-27 04:39:38 -07:00
|
|
|
local function nvim_async(method, ...)
|
2016-06-28 12:45:19 -07:00
|
|
|
session:notify('nvim_'..method, ...)
|
2015-07-27 04:39:38 -07:00
|
|
|
end
|
|
|
|
|
2014-10-08 09:56:01 -07:00
|
|
|
local function buffer(method, ...)
|
2016-06-28 12:45:19 -07:00
|
|
|
return request('nvim_buf_'..method, ...)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
local function window(method, ...)
|
2016-06-28 12:45:19 -07:00
|
|
|
return request('nvim_win_'..method, ...)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
local function tabpage(method, ...)
|
2016-06-28 12:45:19 -07:00
|
|
|
return request('nvim_tabpage_'..method, ...)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
local function curbuf(method, ...)
|
|
|
|
if not method then
|
2016-09-16 21:30:36 -07:00
|
|
|
return nvim('get_current_buf')
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
2016-06-28 12:45:19 -07:00
|
|
|
return buffer(method, 0, ...)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2015-02-12 09:29:48 -07:00
|
|
|
local function wait()
|
2017-03-13 07:02:37 -07:00
|
|
|
-- Execute 'nvim_eval' (a deferred function) to block
|
2016-06-21 23:18:21 -07:00
|
|
|
-- until all pending input is processed.
|
2017-03-13 07:02:37 -07:00
|
|
|
session:request('nvim_eval', '1')
|
2015-02-12 09:29:48 -07:00
|
|
|
end
|
|
|
|
|
2014-10-08 09:56:01 -07:00
|
|
|
local function curbuf_contents()
|
2016-06-21 23:18:21 -07:00
|
|
|
wait() -- Before inspecting the buffer, process all input.
|
2016-03-12 10:49:18 -07:00
|
|
|
return table.concat(curbuf('get_lines', 0, -1, true), '\n')
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
local function curwin(method, ...)
|
|
|
|
if not method then
|
2016-09-16 21:30:36 -07:00
|
|
|
return nvim('get_current_win')
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
2016-06-28 12:45:19 -07:00
|
|
|
return window(method, 0, ...)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
local function curtab(method, ...)
|
|
|
|
if not method then
|
2016-06-28 12:45:19 -07:00
|
|
|
return nvim('get_current_tabpage')
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
2016-06-28 12:45:19 -07:00
|
|
|
return tabpage(method, 0, ...)
|
2014-10-08 09:56:01 -07:00
|
|
|
end
|
|
|
|
|
2014-11-21 09:06:03 -07:00
|
|
|
local function expect(contents)
|
|
|
|
return eq(dedent(contents), curbuf_contents())
|
|
|
|
end
|
|
|
|
|
2017-02-28 01:34:02 -07:00
|
|
|
local function expect_any(contents)
|
|
|
|
contents = dedent(contents)
|
|
|
|
return ok(nil ~= string.find(curbuf_contents(), contents, 1, true))
|
|
|
|
end
|
|
|
|
|
2016-09-18 14:43:32 -07:00
|
|
|
local function do_rmdir(path)
|
2015-07-20 07:51:53 -07:00
|
|
|
if lfs.attributes(path, 'mode') ~= 'directory' then
|
2017-04-10 13:57:49 -07:00
|
|
|
return -- Don't complain.
|
2015-07-20 07:51:53 -07:00
|
|
|
end
|
|
|
|
for file in lfs.dir(path) do
|
2016-03-06 15:53:55 -07:00
|
|
|
if file ~= '.' and file ~= '..' then
|
2016-06-04 23:51:50 -07:00
|
|
|
local abspath = path..'/'..file
|
|
|
|
if lfs.attributes(abspath, 'mode') == 'directory' then
|
2017-04-10 13:57:49 -07:00
|
|
|
do_rmdir(abspath) -- recurse
|
2016-06-04 23:51:50 -07:00
|
|
|
else
|
|
|
|
local ret, err = os.remove(abspath)
|
|
|
|
if not ret then
|
2017-04-10 13:57:49 -07:00
|
|
|
if not session then
|
2017-04-10 10:12:56 -07:00
|
|
|
error('os.remove: '..err)
|
2017-04-10 13:57:49 -07:00
|
|
|
else
|
|
|
|
-- Try Nvim delete(): it handles `readonly` attribute on Windows,
|
|
|
|
-- and avoids Lua cross-version/platform incompatibilities.
|
|
|
|
if -1 == nvim_call('delete', abspath) then
|
|
|
|
local hint = (os_name() == 'windows'
|
|
|
|
and ' (hint: try :%bwipeout! before rmdir())' or '')
|
|
|
|
error('delete() failed'..hint..': '..abspath)
|
|
|
|
end
|
2017-04-10 10:12:56 -07:00
|
|
|
end
|
2016-06-04 23:51:50 -07:00
|
|
|
end
|
2016-03-06 15:53:55 -07:00
|
|
|
end
|
2015-07-20 07:51:53 -07:00
|
|
|
end
|
|
|
|
end
|
2016-09-19 18:02:48 -07:00
|
|
|
local ret, err = lfs.rmdir(path)
|
2015-07-20 07:51:53 -07:00
|
|
|
if not ret then
|
2016-09-19 18:02:48 -07:00
|
|
|
error('lfs.rmdir('..path..'): '..err)
|
2015-07-20 07:51:53 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-09-18 14:43:32 -07:00
|
|
|
local function rmdir(path)
|
2016-09-19 18:02:48 -07:00
|
|
|
local ret, _ = pcall(do_rmdir, path)
|
2017-01-03 22:35:21 -07:00
|
|
|
if not ret and os_name() == "windows" then
|
|
|
|
-- Maybe "Permission denied"; try again after changing the nvim
|
|
|
|
-- process to the top-level directory.
|
|
|
|
nvim_command([[exe 'cd '.fnameescape(']]..start_dir.."')")
|
|
|
|
ret, _ = pcall(do_rmdir, path)
|
|
|
|
end
|
2016-09-18 14:43:32 -07:00
|
|
|
-- During teardown, the nvim process may not exit quickly enough, then rmdir()
|
|
|
|
-- will fail (on Windows).
|
|
|
|
if not ret then -- Try again.
|
|
|
|
sleep(1000)
|
|
|
|
do_rmdir(path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-09-18 15:53:58 -07:00
|
|
|
local exc_exec = function(cmd)
|
|
|
|
nvim_command(([[
|
|
|
|
try
|
|
|
|
execute "%s"
|
|
|
|
catch
|
|
|
|
let g:__exception = v:exception
|
|
|
|
endtry
|
|
|
|
]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0')))
|
|
|
|
local ret = nvim_eval('get(g:, "__exception", 0)')
|
|
|
|
nvim_command('unlet! g:__exception')
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2015-09-26 16:49:48 -07:00
|
|
|
local function create_callindex(func)
|
2015-11-17 15:31:22 -07:00
|
|
|
local table = {}
|
|
|
|
setmetatable(table, {
|
2015-09-26 16:49:48 -07:00
|
|
|
__index = function(tbl, arg1)
|
2015-11-17 15:31:22 -07:00
|
|
|
local ret = function(...) return func(arg1, ...) end
|
2015-09-26 16:49:48 -07:00
|
|
|
tbl[arg1] = ret
|
|
|
|
return ret
|
|
|
|
end,
|
|
|
|
})
|
2015-11-17 15:31:22 -07:00
|
|
|
return table
|
2015-09-26 16:49:48 -07:00
|
|
|
end
|
|
|
|
|
2016-08-15 16:42:12 -07:00
|
|
|
-- Helper to skip tests. Returns true in Windows systems.
|
2017-01-03 20:10:38 -07:00
|
|
|
-- pending_fn is pending() from busted
|
|
|
|
local function pending_win32(pending_fn)
|
2015-12-29 12:18:16 -07:00
|
|
|
if uname() == 'Windows' then
|
2017-01-03 20:10:38 -07:00
|
|
|
if pending_fn ~= nil then
|
|
|
|
pending_fn('FIXME: Windows', function() end)
|
2016-08-15 16:42:12 -07:00
|
|
|
end
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-01-03 20:10:38 -07:00
|
|
|
-- Calls pending() and returns `true` if the system is too slow to
|
|
|
|
-- run fragile or expensive tests. Else returns `false`.
|
|
|
|
local function skip_fragile(pending_fn, cond)
|
|
|
|
if pending_fn == nil or type(pending_fn) ~= type(function()end) then
|
|
|
|
error("invalid pending_fn")
|
|
|
|
end
|
|
|
|
if cond then
|
|
|
|
pending_fn("skipped (test is fragile on this system)", function() end)
|
|
|
|
return true
|
|
|
|
elseif os.getenv("TEST_SKIP_FRAGILE") then
|
|
|
|
pending_fn("skipped (TEST_SKIP_FRAGILE)", function() end)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2017-02-22 15:34:25 -07:00
|
|
|
local function meth_pcall(...)
|
|
|
|
local ret = {pcall(...)}
|
|
|
|
if type(ret[2]) == 'string' then
|
|
|
|
ret[2] = ret[2]:gsub('^[^:]+:%d+: ', '')
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2015-09-26 16:49:48 -07:00
|
|
|
local funcs = create_callindex(nvim_call)
|
|
|
|
local meths = create_callindex(nvim)
|
2016-06-08 02:26:06 -07:00
|
|
|
local uimeths = create_callindex(ui)
|
2015-09-26 16:49:48 -07:00
|
|
|
local bufmeths = create_callindex(buffer)
|
|
|
|
local winmeths = create_callindex(window)
|
|
|
|
local tabmeths = create_callindex(tabpage)
|
|
|
|
local curbufmeths = create_callindex(curbuf)
|
|
|
|
local curwinmeths = create_callindex(curwin)
|
|
|
|
local curtabmeths = create_callindex(curtab)
|
|
|
|
|
2017-06-20 07:27:06 -07:00
|
|
|
local function redir_exec(cmd)
|
|
|
|
meths.set_var('__redir_exec_cmd', cmd)
|
|
|
|
nvim_command([[
|
|
|
|
redir => g:__redir_exec_output
|
|
|
|
silent! execute g:__redir_exec_cmd
|
|
|
|
redir END
|
|
|
|
]])
|
|
|
|
local ret = meths.get_var('__redir_exec_output')
|
|
|
|
meths.del_var('__redir_exec_output')
|
|
|
|
meths.del_var('__redir_exec_cmd')
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2016-11-05 11:34:22 -07:00
|
|
|
local function get_pathsep()
|
2017-11-11 01:14:44 -07:00
|
|
|
return iswin() and '\\' or '/'
|
2016-11-05 11:34:22 -07:00
|
|
|
end
|
|
|
|
|
2018-01-24 03:01:14 -07:00
|
|
|
local function pathroot()
|
|
|
|
local pathsep = package.config:sub(1,1)
|
|
|
|
return iswin() and (nvim_dir:sub(1,2)..pathsep) or '/'
|
|
|
|
end
|
|
|
|
|
2017-11-26 15:15:17 -07:00
|
|
|
-- Returns a valid, platform-independent $NVIM_LISTEN_ADDRESS.
|
|
|
|
-- Useful for communicating with child instances.
|
|
|
|
local function new_pipename()
|
|
|
|
-- HACK: Start a server temporarily, get the name, then stop it.
|
|
|
|
local pipename = nvim_eval('serverstart()')
|
|
|
|
funcs.serverstop(pipename)
|
|
|
|
return pipename
|
|
|
|
end
|
|
|
|
|
2017-05-12 10:47:33 -07:00
|
|
|
local function missing_provider(provider)
|
2017-11-28 23:50:11 -07:00
|
|
|
if provider == 'ruby' or provider == 'node' then
|
2017-05-12 10:03:05 -07:00
|
|
|
local prog = funcs['provider#' .. provider .. '#Detect']()
|
2017-05-12 10:47:33 -07:00
|
|
|
return prog == '' and (provider .. ' not detected') or false
|
2017-05-12 10:03:05 -07:00
|
|
|
elseif provider == 'python' or provider == 'python3' then
|
|
|
|
local py_major_version = (provider == 'python3' and 3 or 2)
|
|
|
|
local errors = funcs['provider#pythonx#Detect'](py_major_version)[2]
|
2017-05-12 10:47:33 -07:00
|
|
|
return errors ~= '' and errors or false
|
2017-05-12 10:03:05 -07:00
|
|
|
else
|
|
|
|
assert(false, 'Unknown provider: ' .. provider)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-05-22 14:46:57 -07:00
|
|
|
local function alter_slashes(obj)
|
|
|
|
if not iswin() then
|
|
|
|
return obj
|
|
|
|
end
|
|
|
|
if type(obj) == 'string' then
|
|
|
|
local ret = obj:gsub('/', '\\')
|
|
|
|
return ret
|
|
|
|
elseif type(obj) == 'table' then
|
|
|
|
local ret = {}
|
|
|
|
for k, v in pairs(obj) do
|
|
|
|
ret[k] = alter_slashes(v)
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
else
|
|
|
|
assert(false, 'Could only alter slashes for tables of strings and strings')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-01-28 14:52:48 -07:00
|
|
|
local module = {
|
2018-02-16 11:42:05 -07:00
|
|
|
NIL = mpack.NIL,
|
|
|
|
alter_slashes = alter_slashes,
|
|
|
|
buffer = buffer,
|
|
|
|
bufmeths = bufmeths,
|
|
|
|
call = nvim_call,
|
2017-01-22 06:13:10 -07:00
|
|
|
clear = clear,
|
2018-02-16 11:42:05 -07:00
|
|
|
command = nvim_command,
|
2017-01-22 06:13:10 -07:00
|
|
|
connect = connect,
|
2018-02-16 11:42:05 -07:00
|
|
|
curbuf = curbuf,
|
|
|
|
curbuf_contents = curbuf_contents,
|
|
|
|
curbufmeths = curbufmeths,
|
|
|
|
curtab = curtab,
|
|
|
|
curtabmeths = curtabmeths,
|
|
|
|
curwin = curwin,
|
|
|
|
curwinmeths = curwinmeths,
|
2017-01-22 06:13:10 -07:00
|
|
|
dedent = dedent,
|
|
|
|
eq = eq,
|
2018-02-16 11:42:05 -07:00
|
|
|
eval = nvim_eval,
|
|
|
|
exc_exec = exc_exec,
|
2017-01-22 06:13:10 -07:00
|
|
|
expect = expect,
|
2017-02-28 01:34:02 -07:00
|
|
|
expect_any = expect_any,
|
2018-04-11 13:07:00 -07:00
|
|
|
expect_err = expect_err,
|
2018-02-16 11:42:05 -07:00
|
|
|
expect_msg_seq = expect_msg_seq,
|
|
|
|
expect_twostreams = expect_twostreams,
|
|
|
|
feed = feed,
|
|
|
|
feed_command = feed_command,
|
2017-01-22 06:13:10 -07:00
|
|
|
filter = filter,
|
2018-02-16 11:42:05 -07:00
|
|
|
funcs = funcs,
|
|
|
|
get_pathsep = get_pathsep,
|
|
|
|
insert = insert,
|
|
|
|
iswin = iswin,
|
|
|
|
map = map,
|
2018-04-10 16:43:15 -07:00
|
|
|
matches = matches,
|
2018-02-16 11:42:05 -07:00
|
|
|
merge_args = merge_args,
|
|
|
|
meth_pcall = meth_pcall,
|
|
|
|
meths = meths,
|
|
|
|
missing_provider = missing_provider,
|
|
|
|
mkdir = lfs.mkdir,
|
|
|
|
neq = neq,
|
|
|
|
new_pipename = new_pipename,
|
2018-03-08 16:24:38 -07:00
|
|
|
next_msg = next_msg,
|
2017-01-22 06:13:10 -07:00
|
|
|
nvim = nvim,
|
2018-02-16 11:42:05 -07:00
|
|
|
nvim_argv = nvim_argv,
|
2017-01-22 06:13:10 -07:00
|
|
|
nvim_async = nvim_async,
|
2018-02-16 11:42:05 -07:00
|
|
|
nvim_dir = nvim_dir,
|
2017-01-22 06:13:10 -07:00
|
|
|
nvim_prog = nvim_prog,
|
2017-02-09 19:39:00 -07:00
|
|
|
nvim_set = nvim_set,
|
2018-02-16 11:42:05 -07:00
|
|
|
ok = ok,
|
2017-01-22 06:13:10 -07:00
|
|
|
os_name = os_name,
|
2018-02-16 11:42:05 -07:00
|
|
|
pathroot = pathroot,
|
2017-01-22 06:13:10 -07:00
|
|
|
pending_win32 = pending_win32,
|
2018-02-16 11:42:05 -07:00
|
|
|
prepend_argv = prepend_argv,
|
|
|
|
rawfeed = rawfeed,
|
|
|
|
read_file = read_file,
|
|
|
|
redir_exec = redir_exec,
|
|
|
|
request = request,
|
|
|
|
retry = retry,
|
|
|
|
rmdir = rmdir,
|
|
|
|
run = run,
|
|
|
|
set_session = set_session,
|
2017-01-22 06:13:10 -07:00
|
|
|
set_shell_powershell = set_shell_powershell,
|
2018-02-16 11:42:05 -07:00
|
|
|
skip_fragile = skip_fragile,
|
|
|
|
sleep = sleep,
|
|
|
|
source = source,
|
|
|
|
spawn = spawn,
|
|
|
|
stop = stop,
|
|
|
|
table_flatten = table_flatten,
|
|
|
|
tabmeths = tabmeths,
|
|
|
|
tabpage = tabpage,
|
2017-01-22 06:13:10 -07:00
|
|
|
tmpname = tmpname,
|
2018-02-16 11:42:05 -07:00
|
|
|
uimeths = uimeths,
|
|
|
|
wait = wait,
|
|
|
|
window = window,
|
|
|
|
winmeths = winmeths,
|
|
|
|
write_file = write_file,
|
2017-01-22 06:13:10 -07:00
|
|
|
}
|
|
|
|
|
2016-04-23 16:53:11 -07:00
|
|
|
return function(after_each)
|
|
|
|
if after_each then
|
2016-11-04 08:20:58 -07:00
|
|
|
after_each(function()
|
2017-07-04 05:15:23 -07:00
|
|
|
for _, fname in ipairs(sourced_fnames) do
|
|
|
|
os.remove(fname)
|
|
|
|
end
|
2016-11-04 08:20:58 -07:00
|
|
|
check_logs()
|
|
|
|
check_cores('build/bin/nvim')
|
|
|
|
end)
|
2016-04-23 16:53:11 -07:00
|
|
|
end
|
2017-01-28 14:52:48 -07:00
|
|
|
return module
|
2016-04-23 16:53:11 -07:00
|
|
|
end
|