diff options
Diffstat (limited to 'test/functional/helpers.lua')
-rw-r--r-- | test/functional/helpers.lua | 252 |
1 files changed, 188 insertions, 64 deletions
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 4a170d993b..b8d912114d 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -18,6 +18,7 @@ local ok = global_helpers.ok local map = global_helpers.map local filter = global_helpers.filter local dedent = global_helpers.dedent +local table_flatten = global_helpers.table_flatten local start_dir = lfs.currentdir() -- XXX: NVIM_PROG takes precedence, QuickBuild sets it. @@ -96,8 +97,64 @@ local function request(method, ...) return rv end -local function next_message() - return session:next_message() +local function next_msg(timeout) + return session:next_message(timeout and timeout or 10000) +end + +local function expect_twostreams(msgs1, msgs2) + local pos1, pos2 = 1, 1 + while pos1 <= #msgs1 or pos2 <= #msgs2 do + local msg = next_msg() + 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 + +-- Expects a sequence of next_msg() results. If multiple sequences are +-- 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 + local msg = next_msg(10000) -- Big timeout for ASAN/valgrind. + 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 local function call_and_stop_on_error(...) @@ -245,6 +302,7 @@ local function retry(max, max_ms, fn) if status then return result end + luv.update_time() -- Update cached value of luv.now() (libuv: uv_now()). if (max and tries >= max) or (luv.now() - start_time > timeout) then if type(result) == "string" then result = "\nretry() attempts: "..tostring(tries).."\n"..result @@ -317,9 +375,16 @@ local function feed_command(...) end -- Dedent the given text and write it to the file name. -local function write_file(name, text, dont_dedent) - local file = io.open(name, 'w') - if not dont_dedent then +local function write_file(name, text, no_dedent, append) + local file = io.open(name, (append and 'a' or 'w')) + if type(text) == 'table' then + -- Byte blob + local bytes = text + text = '' + for _, char in ipairs(bytes) do + text = ('%s%c'):format(text, char) + end + elseif not no_dedent then text = dedent(text) end file:write(text) @@ -337,19 +402,30 @@ local function read_file(name) return ret end +local sourced_fnames = {} local function source(code) local fname = tmpname() write_file(fname, code) nvim_command('source '..fname) - os.remove(fname) + -- 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 return fname end local function set_shell_powershell() source([[ - set shell=powershell shellquote=\" shellpipe=\| shellredir=> - set shellcmdflag=\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command - let &shellxquote=' ' + set shell=powershell shellquote=( shellpipe=\| shellredir=> shellxquote= + set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command ]]) end @@ -565,11 +641,25 @@ local function redir_exec(cmd) end local function get_pathsep() - return funcs.fnamemodify('.', ':p'):sub(-1) + return iswin() and '\\' or '/' +end + +local function pathroot() + local pathsep = package.config:sub(1,1) + return iswin() and (nvim_dir:sub(1,2)..pathsep) or '/' +end + +-- 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 local function missing_provider(provider) - if provider == 'ruby' then + if provider == 'ruby' or provider == 'node' then local prog = funcs['provider#' .. provider .. '#Detect']() return prog == '' and (provider .. ' not detected') or false elseif provider == 'python' or provider == 'python3' then @@ -599,80 +689,114 @@ local function alter_slashes(obj) end end +local function hexdump(str) + local len = string.len(str) + local dump = "" + local hex = "" + local asc = "" + + for i = 1, len do + if 1 == i % 8 then + dump = dump .. hex .. asc .. "\n" + hex = string.format("%04x: ", i - 1) + asc = "" + end + + local ord = string.byte(str, i) + hex = hex .. string.format("%02x ", ord) + if ord >= 32 and ord <= 126 then + asc = asc .. string.char(ord) + else + asc = asc .. "." + end + end + + return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc +end + local module = { - prepend_argv = prepend_argv, + NIL = mpack.NIL, + alter_slashes = alter_slashes, + buffer = buffer, + bufmeths = bufmeths, + call = nvim_call, clear = clear, + command = nvim_command, connect = connect, - retry = retry, - spawn = spawn, + curbuf = curbuf, + curbuf_contents = curbuf_contents, + curbufmeths = curbufmeths, + curtab = curtab, + curtabmeths = curtabmeths, + curwin = curwin, + curwinmeths = curwinmeths, dedent = dedent, - source = source, - rawfeed = rawfeed, - insert = insert, - iswin = iswin, - feed = feed, - feed_command = feed_command, - eval = nvim_eval, - call = nvim_call, - command = nvim_command, - request = request, - next_message = next_message, - run = run, - stop = stop, eq = eq, - neq = neq, + eval = nvim_eval, + exc_exec = exc_exec, expect = expect, expect_any = expect_any, - ok = ok, - map = map, + expect_msg_seq = expect_msg_seq, + expect_twostreams = expect_twostreams, + feed = feed, + feed_command = feed_command, filter = filter, + funcs = funcs, + get_pathsep = get_pathsep, + hexdump = hexdump, + insert = insert, + iswin = iswin, + map = map, + merge_args = merge_args, + meth_pcall = meth_pcall, + meths = meths, + missing_provider = missing_provider, + mkdir = lfs.mkdir, + neq = neq, + new_pipename = new_pipename, + next_msg = next_msg, nvim = nvim, + nvim_argv = nvim_argv, nvim_async = nvim_async, + nvim_dir = nvim_dir, nvim_prog = nvim_prog, - nvim_argv = nvim_argv, nvim_set = nvim_set, - nvim_dir = nvim_dir, - buffer = buffer, - window = window, - tabpage = tabpage, - curbuf = curbuf, - curwin = curwin, - curtab = curtab, - curbuf_contents = curbuf_contents, - wait = wait, - sleep = sleep, - set_session = set_session, - write_file = write_file, - read_file = read_file, + ok = ok, os_name = os_name, - rmdir = rmdir, - mkdir = lfs.mkdir, - exc_exec = exc_exec, - redir_exec = redir_exec, - merge_args = merge_args, - funcs = funcs, - meths = meths, - bufmeths = bufmeths, - winmeths = winmeths, - tabmeths = tabmeths, - uimeths = uimeths, - curbufmeths = curbufmeths, - curwinmeths = curwinmeths, - curtabmeths = curtabmeths, + pathroot = pathroot, pending_win32 = pending_win32, - skip_fragile = skip_fragile, + 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, set_shell_powershell = set_shell_powershell, + skip_fragile = skip_fragile, + sleep = sleep, + source = source, + spawn = spawn, + stop = stop, + table_flatten = table_flatten, + tabmeths = tabmeths, + tabpage = tabpage, tmpname = tmpname, - meth_pcall = meth_pcall, - NIL = mpack.NIL, - get_pathsep = get_pathsep, - missing_provider = missing_provider, - alter_slashes = alter_slashes, + uimeths = uimeths, + wait = wait, + window = window, + winmeths = winmeths, + write_file = write_file, } return function(after_each) if after_each then after_each(function() + for _, fname in ipairs(sourced_fnames) do + os.remove(fname) + end check_logs() check_cores('build/bin/nvim') end) |