diff options
Diffstat (limited to 'test/functional/core')
-rw-r--r-- | test/functional/core/channels_spec.lua | 250 | ||||
-rw-r--r-- | test/functional/core/exit_spec.lua | 19 | ||||
-rw-r--r-- | test/functional/core/fileio_spec.lua | 258 | ||||
-rw-r--r-- | test/functional/core/job_spec.lua | 778 | ||||
-rw-r--r-- | test/functional/core/log_spec.lua | 16 | ||||
-rw-r--r-- | test/functional/core/main_spec.lua | 167 | ||||
-rw-r--r-- | test/functional/core/path_spec.lua | 50 | ||||
-rw-r--r-- | test/functional/core/remote_spec.lua | 39 | ||||
-rw-r--r-- | test/functional/core/spellfile_spec.lua | 55 | ||||
-rw-r--r-- | test/functional/core/startup_spec.lua | 907 |
10 files changed, 1578 insertions, 961 deletions
diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 5771ddcb94..56a2f5a571 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -1,10 +1,10 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, eq, eval, next_msg, ok, source = helpers.clear, helpers.eq, - helpers.eval, helpers.next_msg, helpers.ok, helpers.source -local command, funcs, meths = helpers.command, helpers.funcs, helpers.meths -local sleep = helpers.sleep +local clear, eq, eval, next_msg, ok, source = + helpers.clear, helpers.eq, helpers.eval, helpers.next_msg, helpers.ok, helpers.source +local command, fn, api = helpers.command, helpers.fn, helpers.api +local sleep = vim.uv.sleep local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv -local set_session = helpers.set_session +local get_session, set_session = helpers.get_session, helpers.set_session local nvim_prog = helpers.nvim_prog local is_os = helpers.is_os local retry = helpers.retry @@ -33,31 +33,67 @@ describe('channels', function() pending('can connect to socket', function() local server = spawn(nvim_argv, nil, nil, true) set_session(server) - local address = funcs.serverlist()[1] + local address = fn.serverlist()[1] local client = spawn(nvim_argv, nil, nil, true) set_session(client) source(init) - meths.set_var('address', address) + api.nvim_set_var('address', address) command("let g:id = sockconnect('pipe', address, {'on_data':'OnEvent'})") - local id = eval("g:id") + local id = eval('g:id') ok(id > 0) command("call chansend(g:id, msgpackdump([[2,'nvim_set_var',['code',23]]]))") set_session(server) retry(nil, 1000, function() - eq(23, meths.get_var('code')) + eq(23, api.nvim_get_var('code')) end) set_session(client) command("call chansend(g:id, msgpackdump([[0,0,'nvim_eval',['2+3']]]))") - - local res = eval("msgpackdump([[1,0,v:null,5]])") - eq({"\148\001\n\192\005"}, res) - eq({'notification', 'data', {id, res}}, next_msg()) + local res = eval('msgpackdump([[1,0,v:null,5]])') + eq({ '\148\001\n\192\005' }, res) + eq({ 'notification', 'data', { id, res } }, next_msg()) command("call chansend(g:id, msgpackdump([[2,'nvim_command',['quit']]]))") - eq({'notification', 'data', {id, {''}}}, next_msg()) + eq({ 'notification', 'data', { id, { '' } } }, next_msg()) + end) + + it('dont crash due to garbage in rpc #23781', function() + local client = get_session() + local server = spawn(nvim_argv, nil, nil, true) + set_session(server) + local address = fn.serverlist()[1] + set_session(client) + + api.nvim_set_var('address', address) + command("let g:id = sockconnect('pipe', address, {'on_data':'OnEvent'})") + local id = eval('g:id') + ok(id > 0) + + command("call chansend(g:id, 'F')") + eq({ 'notification', 'data', { id, { '' } } }, next_msg()) + set_session(server) + assert_alive() + + set_session(client) + command('call chanclose(g:id)') + command("let g:id = sockconnect('pipe', address, {'on_data':'OnEvent'})") + id = eval('g:id') + ok(id > 0) + + command("call chansend(g:id, msgpackdump([[2, 'redraw', 'F']], 'B')[:-4])") + set_session(server) + assert_alive() + set_session(client) + command("call chansend(g:id, 'F')") + eq({ 'notification', 'data', { id, { '' } } }, next_msg()) + + set_session(server) + assert_alive() + set_session(client) + command('call chanclose(g:id)') + server:close() end) it('can use stdio channel', function() @@ -68,8 +104,10 @@ describe('channels', function() \ 'on_exit': function('OnEvent'), \ } ]]) - meths.set_var("nvim_prog", nvim_prog) - meths.set_var("code", [[ + api.nvim_set_var('nvim_prog', nvim_prog) + api.nvim_set_var( + 'code', + [[ function! OnEvent(id, data, event) dict let text = string([a:id, a:data, a:event]) call chansend(g:x, text) @@ -81,25 +119,31 @@ describe('channels', function() endfunction let g:x = stdioopen({'on_stdin':'OnEvent'}) call chansend(x, "hello") - ]]) - command("let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)") - local id = eval("g:id") + ]] + ) + command( + "let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)" + ) + local id = eval('g:id') ok(id > 0) - eq({ "notification", "stdout", {id, { "hello" } } }, next_msg()) + eq({ 'notification', 'stdout', { id, { 'hello' } } }, next_msg()) command("call chansend(id, 'howdy')") - eq({"notification", "stdout", {id, {"[1, ['howdy'], 'stdin']"}}}, next_msg()) + eq({ 'notification', 'stdout', { id, { "[1, ['howdy'], 'stdin']" } } }, next_msg()) - command("call chansend(id, 0z686f6c61)") - eq({"notification", "stdout", {id, {"[1, ['hola'], 'stdin']"}}}, next_msg()) + command('call chansend(id, 0z686f6c61)') + eq({ 'notification', 'stdout', { id, { "[1, ['hola'], 'stdin']" } } }, next_msg()) command("call chanclose(id, 'stdin')") - expect_twostreams({{"notification", "stdout", {id, {"[1, [''], 'stdin']"}}}, - {'notification', 'stdout', {id, {''}}}}, - {{"notification", "stderr", {id, {"*dies*"}}}, - {'notification', 'stderr', {id, {''}}}}) - eq({"notification", "exit", {3,0}}, next_msg()) + expect_twostreams({ + { 'notification', 'stdout', { id, { "[1, [''], 'stdin']" } } }, + { 'notification', 'stdout', { id, { '' } } }, + }, { + { 'notification', 'stderr', { id, { '*dies*' } } }, + { 'notification', 'stderr', { id, { '' } } }, + }) + eq({ 'notification', 'exit', { 3, 0 } }, next_msg()) end) it('can use stdio channel and on_print callback', function() @@ -110,8 +154,10 @@ describe('channels', function() \ 'on_exit': function('OnEvent'), \ } ]]) - meths.set_var("nvim_prog", nvim_prog) - meths.set_var("code", [[ + api.nvim_set_var('nvim_prog', nvim_prog) + api.nvim_set_var( + 'code', + [[ function! OnStdin(id, data, event) dict echo string([a:id, a:data, a:event]) if a:data == [''] @@ -123,24 +169,27 @@ describe('channels', function() endfunction let g:x = stdioopen({'on_stdin': funcref('OnStdin'), 'on_print':'OnPrint'}) call chansend(x, "hello") - ]]) - command("let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)") - local id = eval("g:id") + ]] + ) + command( + "let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)" + ) + local id = eval('g:id') ok(id > 0) - eq({ "notification", "stdout", {id, { "hello" } } }, next_msg()) + eq({ 'notification', 'stdout', { id, { 'hello' } } }, next_msg()) command("call chansend(id, 'howdy')") - eq({"notification", "stdout", {id, {"OnPrint:[1, ['howdy'], 'stdin']"}}}, next_msg()) + eq({ 'notification', 'stdout', { id, { "OnPrint:[1, ['howdy'], 'stdin']" } } }, next_msg()) end) local function expect_twoline(id, stream, line1, line2, nobr) local msg = next_msg() - local joined = nobr and {line1..line2} or {line1, line2} - if not pcall(eq, {"notification", stream, {id, joined}}, msg) then - local sep = (not nobr) and "" or nil - eq({"notification", stream, {id, {line1, sep}}}, msg) - eq({"notification", stream, {id, {line2}}}, next_msg()) + local joined = nobr and { line1 .. line2 } or { line1, line2 } + if not pcall(eq, { 'notification', stream, { id, joined } }, msg) then + local sep = (not nobr) and '' or nil + eq({ 'notification', stream, { id, { line1, sep } } }, msg) + eq({ 'notification', stream, { id, { line2 } } }, next_msg()) end end @@ -153,50 +202,52 @@ describe('channels', function() \ 'pty': v:true, \ } ]]) - meths.set_var("nvim_prog", nvim_prog) - meths.set_var("code", [[ + api.nvim_set_var('nvim_prog', nvim_prog) + api.nvim_set_var( + 'code', + [[ function! OnEvent(id, data, event) dict let text = string([a:id, a:data, a:event]) call chansend(g:x, text) endfunction let g:x = stdioopen({'on_stdin':'OnEvent'}) - ]]) - command("let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)") - local id = eval("g:id") + ]] + ) + command( + "let g:id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)" + ) + local id = eval('g:id') ok(id > 0) command("call chansend(id, 'TEXT\n')") - expect_twoline(id, "stdout", "TEXT\r", "[1, ['TEXT', ''], 'stdin']") + expect_twoline(id, 'stdout', 'TEXT\r', "[1, ['TEXT', ''], 'stdin']") - command("call chansend(id, 0z426c6f6273210a)") - expect_twoline(id, "stdout", "Blobs!\r", "[1, ['Blobs!', ''], 'stdin']") + command('call chansend(id, 0z426c6f6273210a)') + expect_twoline(id, 'stdout', 'Blobs!\r', "[1, ['Blobs!', ''], 'stdin']") command("call chansend(id, 'neovan')") - eq({"notification", "stdout", {id, {"neovan"}}}, next_msg()) + eq({ 'notification', 'stdout', { id, { 'neovan' } } }, next_msg()) command("call chansend(id, '\127\127im\n')") - expect_twoline(id, "stdout", "\b \b\b \bim\r", "[1, ['neovim', ''], 'stdin']") + expect_twoline(id, 'stdout', '\b \b\b \bim\r', "[1, ['neovim', ''], 'stdin']") command("call chansend(id, 'incomplet\004')") local bsdlike = is_os('bsd') or is_os('mac') - local extra = bsdlike and "^D\008\008" or "" - expect_twoline(id, "stdout", - "incomplet"..extra, "[1, ['incomplet'], 'stdin']", true) - + local extra = bsdlike and '^D\008\008' or '' + expect_twoline(id, 'stdout', 'incomplet' .. extra, "[1, ['incomplet'], 'stdin']", true) command("call chansend(id, '\004')") if bsdlike then - expect_twoline(id, "stdout", extra, "[1, [''], 'stdin']", true) + expect_twoline(id, 'stdout', extra, "[1, [''], 'stdin']", true) else - eq({"notification", "stdout", {id, {"[1, [''], 'stdin']"}}}, next_msg()) + eq({ 'notification', 'stdout', { id, { "[1, [''], 'stdin']" } } }, next_msg()) end -- channel is still open command("call chansend(id, 'hi again!\n')") - eq({"notification", "stdout", {id, {"hi again!\r", ""}}}, next_msg()) + eq({ 'notification', 'stdout', { id, { 'hi again!\r', '' } } }, next_msg()) end) - it('stdio channel can use rpc and stderr simultaneously', function() skip(is_os('win')) source([[ @@ -206,31 +257,37 @@ describe('channels', function() \ 'rpc': v:true, \ } ]]) - meths.set_var("nvim_prog", nvim_prog) - meths.set_var("code", [[ + api.nvim_set_var('nvim_prog', nvim_prog) + api.nvim_set_var( + 'code', + [[ let id = stdioopen({'rpc':v:true}) call rpcnotify(id,"nvim_call_function", "rpcnotify", [1, "message", "hi there!", id]) call chansend(v:stderr, "trouble!") - ]]) - command("let id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)") - eq({"notification", "message", {"hi there!", 1}}, next_msg()) - eq({"notification", "stderr", {3, {"trouble!"}}}, next_msg()) + ]] + ) + command( + "let id = jobstart([ g:nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile', '--headless', '--cmd', g:code], g:job_opts)" + ) + eq({ 'notification', 'message', { 'hi there!', 1 } }, next_msg()) + eq({ 'notification', 'stderr', { 3, { 'trouble!' } } }, next_msg()) eq(30, eval("rpcrequest(id, 'nvim_eval', '[chansend(v:stderr, \"math??\"), 5*6][1]')")) - eq({"notification", "stderr", {3, {"math??"}}}, next_msg()) + eq({ 'notification', 'stderr', { 3, { 'math??' } } }, next_msg()) - local _, err = pcall(command,"call rpcrequest(id, 'nvim_command', 'call chanclose(v:stderr, \"stdin\")')") - ok(string.find(err,"E906: invalid stream for channel") ~= nil) + local _, err = + pcall(command, "call rpcrequest(id, 'nvim_command', 'call chanclose(v:stderr, \"stdin\")')") + ok(string.find(err, 'E906: invalid stream for channel') ~= nil) eq(1, eval("rpcrequest(id, 'nvim_eval', 'chanclose(v:stderr, \"stderr\")')")) - eq({"notification", "stderr", {3, {""}}}, next_msg()) + eq({ 'notification', 'stderr', { 3, { '' } } }, next_msg()) command("call rpcnotify(id, 'nvim_command', 'quit')") - eq({"notification", "exit", {3, 0}}, next_msg()) + eq({ 'notification', 'exit', { 3, 0 } }, next_msg()) end) it('can use buffered output mode', function() - skip(funcs.executable('grep') == 0, 'missing "grep" command') + skip(fn.executable('grep') == 0, 'missing "grep" command') source([[ let g:job_opts = { \ 'on_stdout': function('OnEvent'), @@ -239,31 +296,33 @@ describe('channels', function() \ } ]]) command("let id = jobstart(['grep', '^[0-9]'], g:job_opts)") - local id = eval("g:id") + local id = eval('g:id') command([[call chansend(id, "stuff\n10 PRINT \"NVIM\"\nxx")]]) sleep(10) command([[call chansend(id, "xx\n20 GOTO 10\nzz\n")]]) command("call chanclose(id, 'stdin')") - eq({"notification", "stdout", {id, {'10 PRINT "NVIM"', - '20 GOTO 10', ''}}}, next_msg()) - eq({"notification", "exit", {id, 0}}, next_msg()) + eq({ + 'notification', + 'stdout', + { id, { '10 PRINT "NVIM"', '20 GOTO 10', '' } }, + }, next_msg()) + eq({ 'notification', 'exit', { id, 0 } }, next_msg()) command("let id = jobstart(['grep', '^[0-9]'], g:job_opts)") - id = eval("g:id") + id = eval('g:id') command([[call chansend(id, "is no number\nnot at all")]]) command("call chanclose(id, 'stdin')") -- works correctly with no output - eq({"notification", "stdout", {id, {''}}}, next_msg()) - eq({"notification", "exit", {id, 1}}, next_msg()) - + eq({ 'notification', 'stdout', { id, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { id, 1 } }, next_msg()) end) it('can use buffered output mode with no stream callback', function() - skip(funcs.executable('grep') == 0, 'missing "grep" command') + skip(fn.executable('grep') == 0, 'missing "grep" command') source([[ function! OnEvent(id, data, event) dict call rpcnotify(1, a:event, a:id, a:data, self.stdout) @@ -274,31 +333,40 @@ describe('channels', function() \ } ]]) command("let id = jobstart(['grep', '^[0-9]'], g:job_opts)") - local id = eval("g:id") + local id = eval('g:id') command([[call chansend(id, "stuff\n10 PRINT \"NVIM\"\nxx")]]) sleep(10) command([[call chansend(id, "xx\n20 GOTO 10\nzz\n")]]) command("call chanclose(id, 'stdin')") - eq({"notification", "exit", {id, 0, {'10 PRINT "NVIM"', - '20 GOTO 10', ''}}}, next_msg()) + eq({ + 'notification', + 'exit', + { id, 0, { '10 PRINT "NVIM"', '20 GOTO 10', '' } }, + }, next_msg()) -- if dict is reused the new value is not stored, -- but nvim also does not crash command("let id = jobstart(['cat'], g:job_opts)") - id = eval("g:id") + id = eval('g:id') command([[call chansend(id, "cat text\n")]]) sleep(10) command("call chanclose(id, 'stdin')") -- old value was not overwritten - eq({"notification", "exit", {id, 0, {'10 PRINT "NVIM"', - '20 GOTO 10', ''}}}, next_msg()) + eq({ + 'notification', + 'exit', + { id, 0, { '10 PRINT "NVIM"', '20 GOTO 10', '' } }, + }, next_msg()) -- and an error was thrown. - eq("E5210: dict key 'stdout' already set for buffered stream in channel "..id, eval('v:errmsg')) + eq( + "E5210: dict key 'stdout' already set for buffered stream in channel " .. id, + eval('v:errmsg') + ) -- reset dictionary source([[ @@ -308,13 +376,13 @@ describe('channels', function() \ } ]]) command("let id = jobstart(['grep', '^[0-9]'], g:job_opts)") - id = eval("g:id") + id = eval('g:id') command([[call chansend(id, "is no number\nnot at all")]]) command("call chanclose(id, 'stdin')") -- works correctly with no output - eq({"notification", "exit", {id, 1, {''}}}, next_msg()) + eq({ 'notification', 'exit', { id, 1, { '' } } }, next_msg()) end) end) @@ -325,8 +393,10 @@ describe('loopback', function() end) it('does not crash when sending raw data', function() - eq("Vim(call):Can't send raw data to rpc channel", - pcall_err(command, "call chansend(chan, 'test')")) + eq( + "Vim(call):Can't send raw data to rpc channel", + pcall_err(command, "call chansend(chan, 'test')") + ) assert_alive() end) diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index d474b77806..d9e3cc3f31 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -7,7 +7,7 @@ local feed = helpers.feed local eval = helpers.eval local eq = helpers.eq local run = helpers.run -local funcs = helpers.funcs +local fn = helpers.fn local nvim_prog = helpers.nvim_prog local pcall_err = helpers.pcall_err local exec_capture = helpers.exec_capture @@ -18,7 +18,7 @@ describe('v:exiting', function() before_each(function() helpers.clear() - cid = helpers.nvim('get_api_info')[1] + cid = helpers.api.nvim_get_chan_info(0).id end) it('defaults to v:null', function() @@ -27,8 +27,8 @@ describe('v:exiting', function() local function test_exiting(setup_fn) local function on_setup() - command('autocmd VimLeavePre * call rpcrequest('..cid..', "exit", "VimLeavePre")') - command('autocmd VimLeave * call rpcrequest('..cid..', "exit", "VimLeave")') + command('autocmd VimLeavePre * call rpcrequest(' .. cid .. ', "exit", "VimLeavePre")') + command('autocmd VimLeave * call rpcrequest(' .. cid .. ', "exit", "VimLeave")') setup_fn() end local requests_args = {} @@ -39,7 +39,7 @@ describe('v:exiting', function() return '' end run(on_request, nil, on_setup) - eq({{'VimLeavePre'}, {'VimLeave'}}, requests_args) + eq({ { 'VimLeavePre' }, { 'VimLeave' } }, requests_args) end it('is 0 on normal exit', function() @@ -59,11 +59,16 @@ end) describe(':cquit', function() local function test_cq(cmdline, exit_code, redir_msg) if redir_msg then - eq(redir_msg, pcall_err(function() return exec_capture(cmdline) end)) + eq( + redir_msg, + pcall_err(function() + return exec_capture(cmdline) + end) + ) poke_eventloop() assert_alive() else - funcs.system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline}) + fn.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline }) eq(exit_code, eval('v:shell_error')) end end diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua index 65f947132e..928cab525c 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -1,4 +1,4 @@ -local luv = require('luv') +local uv = vim.uv local helpers = require('test.functional.helpers')(after_each) local assert_log = helpers.assert_log @@ -9,18 +9,18 @@ local eq = helpers.eq local neq = helpers.neq local ok = helpers.ok local feed = helpers.feed -local funcs = helpers.funcs +local fn = helpers.fn local nvim_prog = helpers.nvim_prog local request = helpers.request local retry = helpers.retry local rmdir = helpers.rmdir local matches = helpers.matches -local meths = helpers.meths +local api = helpers.api local mkdir = helpers.mkdir -local sleep = helpers.sleep +local sleep = vim.uv.sleep local read_file = helpers.read_file -local trim = helpers.trim -local currentdir = helpers.funcs.getcwd +local trim = vim.trim +local currentdir = helpers.fn.getcwd local assert_alive = helpers.assert_alive local check_close = helpers.check_close local expect_exit = helpers.expect_exit @@ -30,10 +30,11 @@ local feed_command = helpers.feed_command local skip = helpers.skip local is_os = helpers.is_os local is_ci = helpers.is_ci +local spawn = helpers.spawn +local set_session = helpers.set_session describe('fileio', function() - before_each(function() - end) + before_each(function() end) after_each(function() check_close() os.remove('Xtest_startup_shada') @@ -49,54 +50,91 @@ describe('fileio', function() rmdir('Xtest_backupdir with spaces') end) - it('fsync() codepaths #8304', function() - clear({ args={ '-i', 'Xtest_startup_shada', - '--cmd', 'set nofsync', - '--cmd', 'set directory=Xtest_startup_swapdir' } }) + local args = { nvim_prog, '--clean', '--cmd', 'set nofsync directory=Xtest_startup_swapdir' } + --- Starts a new nvim session and returns an attached screen. + local function startup(extra_args) + extra_args = extra_args or {} + local argv = vim.tbl_flatten({ args, '--embed', extra_args }) + local screen_nvim = spawn(argv) + set_session(screen_nvim) + local screen = Screen.new(70, 10) + screen:attach() + screen:set_default_attr_ids({ + [1] = { foreground = Screen.colors.NvimDarkGrey4 }, + [2] = { background = Screen.colors.NvimDarkGrey1, foreground = Screen.colors.NvimLightGrey3 }, + [3] = { foreground = Screen.colors.NvimLightCyan }, + }) + return screen + end + + it("fsync() with 'nofsync' #8304", function() + clear({ args = { '--cmd', 'set nofsync directory=Xtest_startup_swapdir' } }) -- These cases ALWAYS force fsync (regardless of 'fsync' option): -- 1. Idle (CursorHold) with modified buffers (+ 'swapfile'). command('write Xtest_startup_file1') - feed('ifoo<esc>h') + feed('Afoo<esc>h') command('write') - eq(0, request('nvim__stats').fsync) -- 'nofsync' is the default. + eq(0, request('nvim__stats').fsync) command('set swapfile') command('set updatetime=1') - feed('izub<esc>h') -- File is 'modified'. - sleep(3) -- Allow 'updatetime' to expire. + feed('Azub<esc>h') -- File is 'modified'. + sleep(3) -- Allow 'updatetime' to expire. retry(3, nil, function() eq(1, request('nvim__stats').fsync) end) - command('set updatetime=9999') + command('set updatetime=100000 updatecount=100000') - -- 2. Exit caused by deadly signal (+ 'swapfile'). - local j = funcs.jobstart({ nvim_prog, '-u', 'NONE', '-i', - 'Xtest_startup_shada', '--headless', - '-c', 'set swapfile', - '-c', 'write Xtest_startup_file2', - '-c', 'put =localtime()', }) - sleep(10) -- Let Nvim start. - funcs.jobstop(j) -- Send deadly signal. - - -- 3. SIGPWR signal. - -- ?? - - -- 4. Explicit :preserve command. + -- 2. Explicit :preserve command. command('preserve') - eq(2, request('nvim__stats').fsync) + -- TODO: should be exactly 2; where is the extra fsync() is coming from? #26404 + ok(request('nvim__stats').fsync == 2 or request('nvim__stats').fsync == 3) - -- 5. Enable 'fsync' option, write file. + -- 3. Enable 'fsync' option, write file. command('set fsync') - feed('ibaz<esc>h') + feed('Abaz<esc>h') command('write') - eq(4, request('nvim__stats').fsync) + -- TODO: should be exactly 4; where is the extra fsync() is coming from? #26404 + ok(request('nvim__stats').fsync == 4 or request('nvim__stats').fsync == 5) + eq('foozubbaz', trim(read_file('Xtest_startup_file1'))) + + -- 4. Exit caused by deadly signal (+ 'swapfile'). + local j = fn.jobstart(vim.tbl_flatten({ args, '--embed' }), { rpc = true }) + fn.rpcrequest( + j, + 'nvim_exec2', + [[ + set nofsync directory=Xtest_startup_swapdir + edit Xtest_startup_file2 + write + put ='fsyncd text' + ]], + {} + ) + eq('Xtest_startup_swapdir', fn.rpcrequest(j, 'nvim_eval', '&directory')) + fn.jobstop(j) -- Send deadly signal. + + local screen = startup() + feed(':recover Xtest_startup_file2<cr>') + screen:expect({ any = [[Using swap file "Xtest_startup_swapdir[/\]Xtest_startup_file2%.swp"]] }) + feed('<cr>') + screen:expect({ any = 'fsyncd text' }) + + -- 5. SIGPWR signal. + -- oldtest: Test_signal_PWR() end) it('backup #9709', function() skip(is_ci('cirrus')) - clear({ args={ '-i', 'Xtest_startup_shada', - '--cmd', 'set directory=Xtest_startup_swapdir' } }) + clear({ + args = { + '-i', + 'Xtest_startup_shada', + '--cmd', + 'set directory=Xtest_startup_swapdir', + }, + }) command('write Xtest_startup_file1') feed('ifoo<esc>') @@ -109,8 +147,8 @@ describe('fileio', function() local foobar_contents = trim(read_file('Xtest_startup_file1')) local bar_contents = trim(read_file('Xtest_startup_file1~')) - eq('foobar', foobar_contents); - eq('foo', bar_contents); + eq('foobar', foobar_contents) + eq('foo', bar_contents) end) it('backup with full path #11214', function() @@ -126,13 +164,16 @@ describe('fileio', function() command('write') -- Backup filename = fullpath, separators replaced with "%". - local backup_file_name = string.gsub(currentdir()..'/Xtest_startup_file1', - is_os('win') and '[:/\\]' or '/', '%%') .. '~' - local foo_contents = trim(read_file('Xtest_backupdir/'..backup_file_name)) + local backup_file_name = string.gsub( + currentdir() .. '/Xtest_startup_file1', + is_os('win') and '[:/\\]' or '/', + '%%' + ) .. '~' + local foo_contents = trim(read_file('Xtest_backupdir/' .. backup_file_name)) local foobar_contents = trim(read_file('Xtest_startup_file1')) - eq('foobar', foobar_contents); - eq('foo', foo_contents); + eq('foobar', foobar_contents) + eq('foo', foo_contents) end) it('backup with full path with spaces', function() @@ -148,13 +189,16 @@ describe('fileio', function() command('write') -- Backup filename = fullpath, separators replaced with "%". - local backup_file_name = string.gsub(currentdir()..'/Xtest_startup_file1', - is_os('win') and '[:/\\]' or '/', '%%') .. '~' - local foo_contents = trim(read_file('Xtest_backupdir with spaces/'..backup_file_name)) + local backup_file_name = string.gsub( + currentdir() .. '/Xtest_startup_file1', + is_os('win') and '[:/\\]' or '/', + '%%' + ) .. '~' + local foo_contents = trim(read_file('Xtest_backupdir with spaces/' .. backup_file_name)) local foobar_contents = trim(read_file('Xtest_startup_file1')) - eq('foobar', foobar_contents); - eq('foo', foo_contents); + eq('foobar', foobar_contents) + eq('foo', foo_contents) end) it('backup symlinked files #11349', function() @@ -166,7 +210,7 @@ describe('fileio', function() local backup_file_name = link_file_name .. '~' write_file('Xtest_startup_file1', initial_content, false) - luv.fs_symlink('Xtest_startup_file1', link_file_name) + uv.fs_symlink('Xtest_startup_file1', link_file_name) command('set backup') command('set backupcopy=yes') command('edit ' .. link_file_name) @@ -174,11 +218,10 @@ describe('fileio', function() command('write') local backup_raw = read_file(backup_file_name) - neq(nil, backup_raw, "Expected backup file " .. backup_file_name .. "to exist but did not") + neq(nil, backup_raw, 'Expected backup file ' .. backup_file_name .. 'to exist but did not') eq(initial_content, trim(backup_raw), 'Expected backup to contain original contents') end) - it('backup symlinked files in first available backupdir #11349', function() skip(is_ci('cirrus')) clear() @@ -190,7 +233,7 @@ describe('fileio', function() local backup_file_name = backup_dir .. sep .. link_file_name .. '~' write_file('Xtest_startup_file1', initial_content, false) - luv.fs_symlink('Xtest_startup_file1', link_file_name) + uv.fs_symlink('Xtest_startup_file1', link_file_name) mkdir(backup_dir) command('set backup') command('set backupcopy=yes') @@ -200,7 +243,7 @@ describe('fileio', function() command('write') local backup_raw = read_file(backup_file_name) - neq(nil, backup_raw, "Expected backup file " .. backup_file_name .. " to exist but did not") + neq(nil, backup_raw, 'Expected backup file ' .. backup_file_name .. ' to exist but did not') eq(initial_content, trim(backup_raw), 'Expected backup to contain original contents') end) @@ -215,11 +258,11 @@ describe('fileio', function() '', } local fname = 'Xtest_тест.md' - funcs.writefile(text, fname, 's') + fn.writefile(text, fname, 's') table.insert(text, '') - eq(text, funcs.readfile(fname, 'b')) + eq(text, fn.readfile(fname, 'b')) end) - it('read invalid u8 over INT_MAX doesn\'t segfault', function() + it("read invalid u8 over INT_MAX doesn't segfault", function() clear() command('call writefile(0zFFFFFFFF, "Xtest-u8-int-max")') -- This should not segfault @@ -229,34 +272,32 @@ describe('fileio', function() it(':w! does not show "file has been changed" warning', function() clear() - write_file("Xtest-overwrite-forced", 'foobar') + write_file('Xtest-overwrite-forced', 'foobar') command('set nofixendofline') - local screen = Screen.new(40,4) + local screen = Screen.new(40, 4) screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen4} + [1] = { bold = true, foreground = Screen.colors.Blue1 }, + [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, + [3] = { bold = true, foreground = Screen.colors.SeaGreen4 }, }) screen:attach() - command("set shortmess-=F") + command('set shortmess-=F') - command("e Xtest-overwrite-forced") + command('e Xtest-overwrite-forced') screen:expect([[ ^foobar | - {1:~ }| - {1:~ }| + {1:~ }|*2 "Xtest-overwrite-forced" [noeol] 1L, 6B | ]]) -- Get current unix time. - local cur_unix_time = os.time(os.date("!*t")) + local cur_unix_time = os.time(os.date('!*t')) local future_time = cur_unix_time + 999999 -- Set the file's access/update time to be -- greater than the time at which it was created. - local uv = require("luv") uv.fs_utime('Xtest-overwrite-forced', future_time, future_time) -- use async feed_command because nvim basically hangs on the prompt - feed_command("w") + feed_command('w') screen:expect([[ {2:WARNING: The file has been changed since}| {2: reading it!!!} | @@ -264,20 +305,18 @@ describe('fileio', function() ^ | ]]) - feed("n") - feed("<cr>") + feed('n') + feed('<cr>') screen:expect([[ ^foobar | - {1:~ }| - {1:~ }| + {1:~ }|*2 | ]]) -- Use a screen test because the warning does not set v:errmsg. - command("w!") + command('w!') screen:expect([[ ^foobar | - {1:~ }| - {1:~ }| + {1:~ }|*2 <erwrite-forced" [noeol] 1L, 6B written | ]]) end) @@ -302,13 +341,13 @@ describe('tmpdir', function() -- Tempfiles typically look like: "…/nvim.<user>/xxx/0". -- - "…/nvim.<user>/xxx/" is the per-process tmpdir, not shared with other Nvims. -- - "…/nvim.<user>/" is the tmpdir root, shared by all Nvims (normally). - local tmproot = (funcs.tempname()):match(tmproot_pat) + local tmproot = (fn.tempname()):match(tmproot_pat) ok(tmproot:len() > 4, 'tmproot like "nvim.foo"', tmproot) return tmproot end it('failure modes', function() - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) + clear({ env = { NVIM_LOG_FILE = testlog, TMPDIR = os_tmpdir } }) assert_nolog('tempdir is not a directory', testlog) assert_nolog('tempdir has invalid permissions', testlog) @@ -319,9 +358,9 @@ describe('tmpdir', function() -- "…/nvim.<user>/" is not a directory: expect_exit(command, ':qall!') rmdir(tmproot) - write_file(tmproot, '') -- Not a directory, vim_mktempdir() should skip it. - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) - matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). + write_file(tmproot, '') -- Not a directory, vim_mktempdir() should skip it. + clear({ env = { NVIM_LOG_FILE = testlog, TMPDIR = os_tmpdir } }) + matches(tmproot_pat, fn.stdpath('run')) -- Tickle vim_mktempdir(). -- Assert that broken tmpdir root was handled. assert_log('tempdir root not a directory', testlog, 100) @@ -330,9 +369,9 @@ describe('tmpdir', function() os.remove(testlog) os.remove(tmproot) mkdir(tmproot) - funcs.setfperm(tmproot, 'rwxr--r--') -- Invalid permissions, vim_mktempdir() should skip it. - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) - matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). + fn.setfperm(tmproot, 'rwxr--r--') -- Invalid permissions, vim_mktempdir() should skip it. + clear({ env = { NVIM_LOG_FILE = testlog, TMPDIR = os_tmpdir } }) + matches(tmproot_pat, fn.stdpath('run')) -- Tickle vim_mktempdir(). -- Assert that broken tmpdir root was handled. assert_log('tempdir root has invalid permissions', testlog, 100) end) @@ -340,53 +379,54 @@ describe('tmpdir', function() it('too long', function() local bigname = ('%s/%s'):format(os_tmpdir, ('x'):rep(666)) mkdir(bigname) - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=bigname, } }) - matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir(). - local len = (funcs.tempname()):len() + clear({ env = { NVIM_LOG_FILE = testlog, TMPDIR = bigname } }) + matches(tmproot_pat, fn.stdpath('run')) -- Tickle vim_mktempdir(). + local len = (fn.tempname()):len() ok(len > 4 and len < 256, '4 < len < 256', tostring(len)) end) it('disappeared #1432', function() - clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=os_tmpdir, } }) + clear({ env = { NVIM_LOG_FILE = testlog, TMPDIR = os_tmpdir } }) assert_nolog('tempdir disappeared', testlog) local function rm_tmpdir() - local tmpname1 = funcs.tempname() - local tmpdir1 = funcs.fnamemodify(tmpname1, ':h') - eq(funcs.stdpath('run'), tmpdir1) + local tmpname1 = fn.tempname() + local tmpdir1 = fn.fnamemodify(tmpname1, ':h') + eq(fn.stdpath('run'), tmpdir1) rmdir(tmpdir1) retry(nil, 1000, function() - eq(0, funcs.isdirectory(tmpdir1)) + eq(0, fn.isdirectory(tmpdir1)) end) - local tmpname2 = funcs.tempname() - local tmpdir2 = funcs.fnamemodify(tmpname2, ':h') + local tmpname2 = fn.tempname() + local tmpdir2 = fn.fnamemodify(tmpname2, ':h') neq(tmpdir1, tmpdir2) end -- Your antivirus hates you... rm_tmpdir() assert_log('tempdir disappeared', testlog, 100) - funcs.tempname() - funcs.tempname() - funcs.tempname() - eq('', meths.get_vvar('errmsg')) + fn.tempname() + fn.tempname() + fn.tempname() + eq('', api.nvim_get_vvar('errmsg')) rm_tmpdir() - funcs.tempname() - funcs.tempname() - funcs.tempname() - eq('E5431: tempdir disappeared (2 times)', meths.get_vvar('errmsg')) + fn.tempname() + fn.tempname() + fn.tempname() + eq('E5431: tempdir disappeared (2 times)', api.nvim_get_vvar('errmsg')) rm_tmpdir() - eq('E5431: tempdir disappeared (3 times)', meths.get_vvar('errmsg')) + eq('E5431: tempdir disappeared (3 times)', api.nvim_get_vvar('errmsg')) end) it('$NVIM_APPNAME relative path', function() - clear({ env={ - NVIM_APPNAME='a/b', - NVIM_LOG_FILE=testlog, - TMPDIR=os_tmpdir, - } }) - matches([=[.*[/\\]a%%b%.[^/\\]+]=], funcs.tempname()) + clear({ + env = { + NVIM_APPNAME = 'a/b', + NVIM_LOG_FILE = testlog, + TMPDIR = os_tmpdir, + }, + }) + matches([=[.*[/\\]a%%b%.[^/\\]+]=], fn.tempname()) end) - end) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 038368c387..13f5f9a5e1 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -1,17 +1,28 @@ local helpers = require('test.functional.helpers')(after_each) -local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim, - testprg, ok, source, write_file, mkdir, rmdir = helpers.clear, - helpers.eq, helpers.eval, helpers.exc_exec, helpers.feed_command, helpers.feed, - helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim, - helpers.testprg, helpers.ok, helpers.source, - helpers.write_file, helpers.mkdir, helpers.rmdir +local thelpers = require('test.functional.terminal.helpers') + +local clear = helpers.clear +local eq = helpers.eq +local eval = helpers.eval +local exc_exec = helpers.exc_exec +local feed_command = helpers.feed_command +local feed = helpers.feed +local insert = helpers.insert +local neq = helpers.neq +local next_msg = helpers.next_msg +local testprg = helpers.testprg +local ok = helpers.ok +local source = helpers.source +local write_file = helpers.write_file +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir local assert_alive = helpers.assert_alive local command = helpers.command -local funcs = helpers.funcs +local fn = helpers.fn local os_kill = helpers.os_kill local retry = helpers.retry -local meths = helpers.meths -local NIL = helpers.NIL +local api = helpers.api +local NIL = vim.NIL local poke_eventloop = helpers.poke_eventloop local get_pathsep = helpers.get_pathsep local pathroot = helpers.pathroot @@ -31,8 +42,8 @@ describe('jobs', function() before_each(function() clear() - channel = nvim('get_api_info')[1] - nvim('set_var', 'channel', channel) + channel = api.nvim_get_chan_info(0).id + api.nvim_set_var('channel', channel) source([[ function! Normalize(data) abort " Windows: remove ^M and term escape sequences @@ -54,66 +65,60 @@ describe('jobs', function() end) it('must specify env option as a dict', function() - command("let g:job_opts.env = v:true") + command('let g:job_opts.env = v:true') local _, err = pcall(function() if is_os('win') then - nvim('command', "let j = jobstart('set', g:job_opts)") + command("let j = jobstart('set', g:job_opts)") else - nvim('command', "let j = jobstart('env', g:job_opts)") + command("let j = jobstart('env', g:job_opts)") end end) - ok(string.find(err, "E475: Invalid argument: env") ~= nil) + ok(string.find(err, 'E475: Invalid argument: env') ~= nil) end) it('append environment #env', function() - nvim('command', "let $VAR = 'abc'") - nvim('command', "let $TOTO = 'goodbye world'") - nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}") + command("let $VAR = 'abc'") + command("let $TOTO = 'goodbye world'") + command("let g:job_opts.env = {'TOTO': 'hello world'}") if is_os('win') then - nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) + command([[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) else - nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]]) + command([[call jobstart('echo $TOTO $VAR', g:job_opts)]]) end - expect_msg_seq( - { - {'notification', 'stdout', {0, {'hello world abc'}}}, - {'notification', 'stdout', {0, {'', ''}}}, - }, - { - {'notification', 'stdout', {0, {'hello world abc', ''}}}, - {'notification', 'stdout', {0, {''}}} - } - ) + expect_msg_seq({ + { 'notification', 'stdout', { 0, { 'hello world abc' } } }, + { 'notification', 'stdout', { 0, { '', '' } } }, + }, { + { 'notification', 'stdout', { 0, { 'hello world abc', '' } } }, + { 'notification', 'stdout', { 0, { '' } } }, + }) end) it('append environment with pty #env', function() - nvim('command', "let $VAR = 'abc'") - nvim('command', "let $TOTO = 'goodbye world'") - nvim('command', "let g:job_opts.pty = v:true") - nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}") + command("let $VAR = 'abc'") + command("let $TOTO = 'goodbye world'") + command('let g:job_opts.pty = v:true') + command("let g:job_opts.env = {'TOTO': 'hello world'}") if is_os('win') then - nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) + command([[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) else - nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]]) + command([[call jobstart('echo $TOTO $VAR', g:job_opts)]]) end - expect_msg_seq( - { - {'notification', 'stdout', {0, {'hello world abc'}}}, - {'notification', 'stdout', {0, {'', ''}}}, - }, - { - {'notification', 'stdout', {0, {'hello world abc', ''}}}, - {'notification', 'stdout', {0, {''}}} - } - ) + expect_msg_seq({ + { 'notification', 'stdout', { 0, { 'hello world abc' } } }, + { 'notification', 'stdout', { 0, { '', '' } } }, + }, { + { 'notification', 'stdout', { 0, { 'hello world abc', '' } } }, + { 'notification', 'stdout', { 0, { '' } } }, + }) end) it('replace environment #env', function() - nvim('command', "let $VAR = 'abc'") - nvim('command', "let $TOTO = 'goodbye world'") - nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}") - nvim('command', "let g:job_opts.clear_env = 1") + command("let $VAR = 'abc'") + command("let $TOTO = 'goodbye world'") + command("let g:job_opts.env = {'TOTO': 'hello world'}") + command('let g:job_opts.clear_env = 1') -- libuv ensures that certain "required" environment variables are -- preserved if the user doesn't provide them in a custom environment @@ -123,39 +128,39 @@ describe('jobs', function() -- Rather than expecting a completely empty environment, ensure that $VAR -- is *not* in the environment but $TOTO is. if is_os('win') then - nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) + command([[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) expect_msg_seq({ - {'notification', 'stdout', {0, {'hello world %VAR%', ''}}} + { 'notification', 'stdout', { 0, { 'hello world %VAR%', '' } } }, }) else - nvim('command', "set shell=/bin/sh") - nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]]) + command('set shell=/bin/sh') + command([[call jobstart('echo $TOTO $VAR', g:job_opts)]]) expect_msg_seq({ - {'notification', 'stdout', {0, {'hello world', ''}}} + { 'notification', 'stdout', { 0, { 'hello world', '' } } }, }) end end) it('handles case-insensitively matching #env vars', function() - nvim('command', "let $TOTO = 'abc'") + command("let $TOTO = 'abc'") -- Since $Toto is being set in the job, it should take precedence over the -- global $TOTO on Windows - nvim('command', "let g:job_opts = {'env': {'Toto': 'def'}, 'stdout_buffered': v:true}") + command("let g:job_opts = {'env': {'Toto': 'def'}, 'stdout_buffered': v:true}") if is_os('win') then - nvim('command', [[let j = jobstart('set | find /I "toto="', g:job_opts)]]) + command([[let j = jobstart('set | find /I "toto="', g:job_opts)]]) else - nvim('command', [[let j = jobstart('env | grep -i toto=', g:job_opts)]]) + command([[let j = jobstart('env | grep -i toto=', g:job_opts)]]) end - nvim('command', "call jobwait([j])") - nvim('command', "let g:output = Normalize(g:job_opts.stdout)") + command('call jobwait([j])') + command('let g:output = Normalize(g:job_opts.stdout)') local actual = eval('g:output') local expected if is_os('win') then -- Toto is normalized to TOTO so we can detect duplicates, and because -- Windows doesn't care about case - expected = {'TOTO=def', ''} + expected = { 'TOTO=def', '' } else - expected = {'TOTO=abc', 'Toto=def', ''} + expected = { 'TOTO=abc', 'Toto=def', '' } end table.sort(actual) table.sort(expected) @@ -163,49 +168,50 @@ describe('jobs', function() end) it('uses &shell and &shellcmdflag if passed a string', function() - nvim('command', "let $VAR = 'abc'") + command("let $VAR = 'abc'") if is_os('win') then - nvim('command', "let j = jobstart('echo %VAR%', g:job_opts)") + command("let j = jobstart('echo %VAR%', g:job_opts)") else - nvim('command', "let j = jobstart('echo $VAR', g:job_opts)") + command("let j = jobstart('echo $VAR', g:job_opts)") end - eq({'notification', 'stdout', {0, {'abc', ''}}}, next_msg()) - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 0}}, next_msg()) + eq({ 'notification', 'stdout', { 0, { 'abc', '' } } }, next_msg()) + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 0 } }, next_msg()) end) it('changes to given / directory', function() - nvim('command', "let g:job_opts.cwd = '/'") + command("let g:job_opts.cwd = '/'") if is_os('win') then - nvim('command', "let j = jobstart('cd', g:job_opts)") + command("let j = jobstart('cd', g:job_opts)") else - nvim('command', "let j = jobstart('pwd', g:job_opts)") + command("let j = jobstart('pwd', g:job_opts)") end - eq({'notification', 'stdout', - {0, {pathroot(), ''}}}, next_msg()) - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 0}}, next_msg()) + eq({ 'notification', 'stdout', { 0, { pathroot(), '' } } }, next_msg()) + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 0 } }, next_msg()) end) it('changes to given `cwd` directory', function() - local dir = eval("resolve(tempname())"):gsub("/", get_pathsep()) + local dir = eval('resolve(tempname())'):gsub('/', get_pathsep()) mkdir(dir) - nvim('command', "let g:job_opts.cwd = '" .. dir .. "'") + command("let g:job_opts.cwd = '" .. dir .. "'") if is_os('win') then - nvim('command', "let j = jobstart('cd', g:job_opts)") + command("let j = jobstart('cd', g:job_opts)") else - nvim('command', "let j = jobstart('pwd', g:job_opts)") + command("let j = jobstart('pwd', g:job_opts)") end expect_msg_seq( - { {'notification', 'stdout', {0, {dir, ''} } }, - {'notification', 'stdout', {0, {''} } }, - {'notification', 'exit', {0, 0} } + { + { 'notification', 'stdout', { 0, { dir, '' } } }, + { 'notification', 'stdout', { 0, { '' } } }, + { 'notification', 'exit', { 0, 0 } }, }, -- Alternative sequence: - { {'notification', 'stdout', {0, {dir} } }, - {'notification', 'stdout', {0, {'', ''} } }, - {'notification', 'stdout', {0, {''} } }, - {'notification', 'exit', {0, 0} } + { + { 'notification', 'stdout', { 0, { dir } } }, + { 'notification', 'stdout', { 0, { '', '' } } }, + { 'notification', 'stdout', { 0, { '' } } }, + { 'notification', 'exit', { 0, 0 } }, } ) rmdir(dir) @@ -214,14 +220,14 @@ describe('jobs', function() it('fails to change to invalid `cwd`', function() local dir = eval('resolve(tempname())."-bogus"') local _, err = pcall(function() - nvim('command', "let g:job_opts.cwd = '" .. dir .. "'") + command("let g:job_opts.cwd = '" .. dir .. "'") if is_os('win') then - nvim('command', "let j = jobstart('cd', g:job_opts)") + command("let j = jobstart('cd', g:job_opts)") else - nvim('command', "let j = jobstart('pwd', g:job_opts)") + command("let j = jobstart('pwd', g:job_opts)") end end) - ok(string.find(err, "E475: Invalid argument: expected valid directory$") ~= nil) + ok(string.find(err, 'E475: Invalid argument: expected valid directory$') ~= nil) end) it('error on non-executable `cwd`', function() @@ -229,17 +235,19 @@ describe('jobs', function() local dir = 'Xtest_not_executable_dir' mkdir(dir) - funcs.setfperm(dir, 'rw-------') - matches('^Vim%(call%):E903: Process failed to start: permission denied: .*', - pcall_err(nvim, 'command', "call jobstart(['pwd'], {'cwd': '"..dir.."'})")) + fn.setfperm(dir, 'rw-------') + matches( + '^Vim%(call%):E903: Process failed to start: permission denied: .*', + pcall_err(command, "call jobstart(['pwd'], {'cwd': '" .. dir .. "'})") + ) rmdir(dir) end) it('returns 0 when it fails to start', function() - eq("", eval("v:errmsg")) - feed_command("let g:test_jobid = jobstart([])") - eq(0, eval("g:test_jobid")) - eq("E474:", string.match(eval("v:errmsg"), "E%d*:")) + eq('', eval('v:errmsg')) + feed_command('let g:test_jobid = jobstart([])') + eq(0, eval('g:test_jobid')) + eq('E474:', string.match(eval('v:errmsg'), 'E%d*:')) end) it('returns -1 when target is not executable #5465', function() @@ -248,124 +256,131 @@ describe('jobs', function() end local executable_jobid = new_job() - local exe = is_os('win') and './test/functional/fixtures' or './test/functional/fixtures/non_executable.txt' - eq("Vim:E475: Invalid value for argument cmd: '"..exe.."' is not executable", - pcall_err(eval, "jobstart(['"..exe.."'])")) - eq("", eval("v:errmsg")) + local exe = is_os('win') and './test/functional/fixtures' + or './test/functional/fixtures/non_executable.txt' + eq( + "Vim:E475: Invalid value for argument cmd: '" .. exe .. "' is not executable", + pcall_err(eval, "jobstart(['" .. exe .. "'])") + ) + eq('', eval('v:errmsg')) -- Non-executable job should not increment the job ids. #5465 eq(executable_jobid + 1, new_job()) end) it('invokes callbacks when the job writes and exits', function() - nvim('command', "let g:job_opts.on_stderr = function('OnEvent')") - nvim('command', [[call jobstart(has('win32') ? 'echo:' : 'echo', g:job_opts)]]) - expect_twostreams({{'notification', 'stdout', {0, {'', ''}}}, - {'notification', 'stdout', {0, {''}}}}, - {{'notification', 'stderr', {0, {''}}}}) - eq({'notification', 'exit', {0, 0}}, next_msg()) + command("let g:job_opts.on_stderr = function('OnEvent')") + command([[call jobstart(has('win32') ? 'echo:' : 'echo', g:job_opts)]]) + expect_twostreams({ + { 'notification', 'stdout', { 0, { '', '' } } }, + { 'notification', 'stdout', { 0, { '' } } }, + }, { { 'notification', 'stderr', { 0, { '' } } } }) + eq({ 'notification', 'exit', { 0, 0 } }, next_msg()) end) it('interactive commands', function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + command("let j = jobstart(['cat', '-'], g:job_opts)") neq(0, eval('j')) - nvim('command', 'call jobsend(j, "abc\\n")') - eq({'notification', 'stdout', {0, {'abc', ''}}}, next_msg()) - nvim('command', 'call jobsend(j, "123\\nxyz\\n")') + command('call jobsend(j, "abc\\n")') + eq({ 'notification', 'stdout', { 0, { 'abc', '' } } }, next_msg()) + command('call jobsend(j, "123\\nxyz\\n")') expect_msg_seq( - { {'notification', 'stdout', {0, {'123', 'xyz', ''}}} - }, + { { 'notification', 'stdout', { 0, { '123', 'xyz', '' } } } }, -- Alternative sequence: - { {'notification', 'stdout', {0, {'123', ''}}}, - {'notification', 'stdout', {0, {'xyz', ''}}} + { + { 'notification', 'stdout', { 0, { '123', '' } } }, + { 'notification', 'stdout', { 0, { 'xyz', '' } } }, } ) - nvim('command', 'call jobsend(j, [123, "xyz", ""])') + command('call jobsend(j, [123, "xyz", ""])') expect_msg_seq( - { {'notification', 'stdout', {0, {'123', 'xyz', ''}}} - }, + { { 'notification', 'stdout', { 0, { '123', 'xyz', '' } } } }, -- Alternative sequence: - { {'notification', 'stdout', {0, {'123', ''}}}, - {'notification', 'stdout', {0, {'xyz', ''}}} + { + { 'notification', 'stdout', { 0, { '123', '' } } }, + { 'notification', 'stdout', { 0, { 'xyz', '' } } }, } ) - nvim('command', "call jobstop(j)") - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 143}}, next_msg()) + command('call jobstop(j)') + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 143 } }, next_msg()) end) it('preserves NULs', function() -- Make a file with NULs in it. local filename = helpers.tmpname() - write_file(filename, "abc\0def\n") + write_file(filename, 'abc\0def\n') - nvim('command', "let j = jobstart(['cat', '"..filename.."'], g:job_opts)") - eq({'notification', 'stdout', {0, {'abc\ndef', ''}}}, next_msg()) - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 0}}, next_msg()) + command("let j = jobstart(['cat', '" .. filename .. "'], g:job_opts)") + eq({ 'notification', 'stdout', { 0, { 'abc\ndef', '' } } }, next_msg()) + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 0 } }, next_msg()) os.remove(filename) -- jobsend() preserves NULs. - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('command', [[call jobsend(j, ["123\n456",""])]]) - eq({'notification', 'stdout', {0, {'123\n456', ''}}}, next_msg()) - nvim('command', "call jobstop(j)") + command("let j = jobstart(['cat', '-'], g:job_opts)") + command([[call jobsend(j, ["123\n456",""])]]) + eq({ 'notification', 'stdout', { 0, { '123\n456', '' } } }, next_msg()) + command('call jobstop(j)') end) - it("emits partial lines (does NOT buffer data lacking newlines)", function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('command', 'call jobsend(j, "abc\\nxyz")') - eq({'notification', 'stdout', {0, {'abc', 'xyz'}}}, next_msg()) - nvim('command', "call jobstop(j)") - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 143}}, next_msg()) + it('emits partial lines (does NOT buffer data lacking newlines)', function() + command("let j = jobstart(['cat', '-'], g:job_opts)") + command('call jobsend(j, "abc\\nxyz")') + eq({ 'notification', 'stdout', { 0, { 'abc', 'xyz' } } }, next_msg()) + command('call jobstop(j)') + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 143 } }, next_msg()) end) it('preserves newlines', function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('command', 'call jobsend(j, "a\\n\\nc\\n\\n\\n\\nb\\n\\n")') - eq({'notification', 'stdout', - {0, {'a', '', 'c', '', '', '', 'b', '', ''}}}, next_msg()) + command("let j = jobstart(['cat', '-'], g:job_opts)") + command('call jobsend(j, "a\\n\\nc\\n\\n\\n\\nb\\n\\n")') + eq({ 'notification', 'stdout', { 0, { 'a', '', 'c', '', '', '', 'b', '', '' } } }, next_msg()) end) it('preserves NULs', function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('command', 'call jobsend(j, ["\n123\n", "abc\\nxyz\n", ""])') - eq({'notification', 'stdout', {0, {'\n123\n', 'abc\nxyz\n', ''}}}, - next_msg()) - nvim('command', "call jobstop(j)") - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 143}}, next_msg()) + command("let j = jobstart(['cat', '-'], g:job_opts)") + command('call jobsend(j, ["\n123\n", "abc\\nxyz\n", ""])') + eq({ 'notification', 'stdout', { 0, { '\n123\n', 'abc\nxyz\n', '' } } }, next_msg()) + command('call jobstop(j)') + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 143 } }, next_msg()) end) it('avoids sending final newline', function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('command', 'call jobsend(j, ["some data", "without\nfinal nl"])') - eq({'notification', 'stdout', {0, {'some data', 'without\nfinal nl'}}}, - next_msg()) - nvim('command', "call jobstop(j)") - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 143}}, next_msg()) + command("let j = jobstart(['cat', '-'], g:job_opts)") + command('call jobsend(j, ["some data", "without\nfinal nl"])') + eq({ 'notification', 'stdout', { 0, { 'some data', 'without\nfinal nl' } } }, next_msg()) + command('call jobstop(j)') + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 143 } }, next_msg()) end) it('closes the job streams with jobclose', function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('command', 'call jobclose(j, "stdin")') - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 0}}, next_msg()) + command("let j = jobstart(['cat', '-'], g:job_opts)") + command('call jobclose(j, "stdin")') + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 0 } }, next_msg()) end) - it("disallows jobsend on a job that closed stdin", function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('command', 'call jobclose(j, "stdin")') - eq(false, pcall(function() - nvim('command', 'call jobsend(j, ["some data"])') - end)) + it('disallows jobsend on a job that closed stdin', function() + command("let j = jobstart(['cat', '-'], g:job_opts)") + command('call jobclose(j, "stdin")') + eq( + false, + pcall(function() + command('call jobsend(j, ["some data"])') + end) + ) command("let g:job_opts.stdin = 'null'") - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - eq(false, pcall(function() - nvim('command', 'call jobsend(j, ["some data"])') - end)) + command("let j = jobstart(['cat', '-'], g:job_opts)") + eq( + false, + pcall(function() + command('call jobsend(j, ["some data"])') + end) + ) end) it('disallows jobsend on a non-existent job', function() @@ -374,91 +389,99 @@ describe('jobs', function() end) it('jobstop twice on the stopped or exited job return 0', function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + command("let j = jobstart(['cat', '-'], g:job_opts)") neq(0, eval('j')) - eq(1, eval("jobstop(j)")) - eq(0, eval("jobstop(j)")) + eq(1, eval('jobstop(j)')) + eq(0, eval('jobstop(j)')) end) it('will not leak memory if we leave a job running', function() - nvim('command', "call jobstart(['cat', '-'], g:job_opts)") + command("call jobstart(['cat', '-'], g:job_opts)") end) it('can get the pid value using getpid', function() - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + command("let j = jobstart(['cat', '-'], g:job_opts)") local pid = eval('jobpid(j)') - neq(NIL, meths.get_proc(pid)) - nvim('command', 'call jobstop(j)') - eq({'notification', 'stdout', {0, {''}}}, next_msg()) - eq({'notification', 'exit', {0, 143}}, next_msg()) - eq(NIL, meths.get_proc(pid)) + neq(NIL, api.nvim_get_proc(pid)) + command('call jobstop(j)') + eq({ 'notification', 'stdout', { 0, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 0, 143 } }, next_msg()) + eq(NIL, api.nvim_get_proc(pid)) end) - it("disposed on Nvim exit", function() + it('disposed on Nvim exit', function() -- use sleep, which doesn't die on stdin close - nvim('command', "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)") + command( + "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)" + ) local pid = eval('jobpid(g:j)') - neq(NIL, meths.get_proc(pid)) + neq(NIL, api.nvim_get_proc(pid)) clear() - eq(NIL, meths.get_proc(pid)) + eq(NIL, api.nvim_get_proc(pid)) end) it('can survive the exit of nvim with "detach"', function() - nvim('command', 'let g:job_opts.detach = 1') - nvim('command', "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)") + command('let g:job_opts.detach = 1') + command( + "let g:j = jobstart(has('win32') ? ['ping', '-n', '1001', '127.0.0.1'] : ['sleep', '1000'], g:job_opts)" + ) local pid = eval('jobpid(g:j)') - neq(NIL, meths.get_proc(pid)) + neq(NIL, api.nvim_get_proc(pid)) clear() - neq(NIL, meths.get_proc(pid)) + neq(NIL, api.nvim_get_proc(pid)) -- clean up after ourselves eq(0, os_kill(pid)) end) it('can pass user data to the callback', function() - nvim('command', 'let g:job_opts.user = {"n": 5, "s": "str", "l": [1]}') - nvim('command', [[call jobstart('echo foo', g:job_opts)]]) - local data = {n = 5, s = 'str', l = {1}} + command('let g:job_opts.user = {"n": 5, "s": "str", "l": [1]}') + command([[call jobstart('echo foo', g:job_opts)]]) + local data = { n = 5, s = 'str', l = { 1 } } expect_msg_seq( - { {'notification', 'stdout', {data, {'foo', ''}}}, - {'notification', 'stdout', {data, {''}}}, + { + { 'notification', 'stdout', { data, { 'foo', '' } } }, + { 'notification', 'stdout', { data, { '' } } }, }, -- Alternative sequence: - { {'notification', 'stdout', {data, {'foo'}}}, - {'notification', 'stdout', {data, {'', ''}}}, - {'notification', 'stdout', {data, {''}}}, + { + { 'notification', 'stdout', { data, { 'foo' } } }, + { 'notification', 'stdout', { data, { '', '' } } }, + { 'notification', 'stdout', { data, { '' } } }, } ) - eq({'notification', 'exit', {data, 0}}, next_msg()) + eq({ 'notification', 'exit', { data, 0 } }, next_msg()) end) it('can omit data callbacks', function() - nvim('command', 'unlet g:job_opts.on_stdout') - nvim('command', 'let g:job_opts.user = 5') - nvim('command', [[call jobstart('echo foo', g:job_opts)]]) - eq({'notification', 'exit', {5, 0}}, next_msg()) + command('unlet g:job_opts.on_stdout') + command('let g:job_opts.user = 5') + command([[call jobstart('echo foo', g:job_opts)]]) + eq({ 'notification', 'exit', { 5, 0 } }, next_msg()) end) it('can omit exit callback', function() - nvim('command', 'unlet g:job_opts.on_exit') - nvim('command', 'let g:job_opts.user = 5') - nvim('command', [[call jobstart('echo foo', g:job_opts)]]) + command('unlet g:job_opts.on_exit') + command('let g:job_opts.user = 5') + command([[call jobstart('echo foo', g:job_opts)]]) expect_msg_seq( - { {'notification', 'stdout', {5, {'foo', ''} } }, - {'notification', 'stdout', {5, {''} } }, + { + { 'notification', 'stdout', { 5, { 'foo', '' } } }, + { 'notification', 'stdout', { 5, { '' } } }, }, -- Alternative sequence: - { {'notification', 'stdout', {5, {'foo'} } }, - {'notification', 'stdout', {5, {'', ''} } }, - {'notification', 'stdout', {5, {''} } }, + { + { 'notification', 'stdout', { 5, { 'foo' } } }, + { 'notification', 'stdout', { 5, { '', '' } } }, + { 'notification', 'stdout', { 5, { '' } } }, } ) end) it('will pass return code with the exit event', function() - nvim('command', 'let g:job_opts.user = 5') - nvim('command', "call jobstart('exit 55', g:job_opts)") - eq({'notification', 'stdout', {5, {''}}}, next_msg()) - eq({'notification', 'exit', {5, 55}}, next_msg()) + command('let g:job_opts.user = 5') + command("call jobstart('exit 55', g:job_opts)") + eq({ 'notification', 'stdout', { 5, { '' } } }, next_msg()) + eq({ 'notification', 'exit', { 5, 55 } }, next_msg()) end) it('can receive dictionary functions', function() @@ -469,14 +492,14 @@ describe('jobs', function() endfunction call jobstart('exit 45', g:dict) ]]) - eq({'notification', 'exit', {45, 10}}, next_msg()) + eq({ 'notification', 'exit', { 45, 10 } }, next_msg()) end) it('can redefine callbacks being used by a job', function() local screen = Screen.new() screen:attach() screen:set_default_attr_ids({ - [1] = {bold=true, foreground=Screen.colors.Blue}, + [1] = { bold = true, foreground = Screen.colors.Blue }, }) source([[ function! g:JobHandler(job_id, data, event) @@ -495,16 +518,16 @@ describe('jobs', function() endfunction ]]) - eq("", eval("v:errmsg")) + eq('', eval('v:errmsg')) end) it('requires funcrefs for script-local (s:) functions', function() local screen = Screen.new(60, 5) screen:attach() screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, - [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, - [3] = {bold = true, foreground = Screen.colors.SeaGreen4} + [1] = { bold = true, foreground = Screen.colors.Blue1 }, + [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, + [3] = { bold = true, foreground = Screen.colors.SeaGreen4 }, }) -- Pass job callback names _without_ `function(...)`. @@ -519,7 +542,7 @@ describe('jobs', function() \ }) ]]) - screen:expect{any="{2:E120: Using <SID> not in a script context: s:OnEvent}"} + screen:expect { any = '{2:E120: Using <SID> not in a script context: s:OnEvent}' } end) it('does not repeat output with slow output handlers', function() @@ -542,22 +565,22 @@ describe('jobs', function() call jobwait([g:id]) ]]) - local expected = {'1', '2', '3', '4', '5', ''} + local expected = { '1', '2', '3', '4', '5', '' } local chunks = eval('d.data') -- check nothing was received after exit, including EOF eq(eval('g:exit_data'), chunks) - local received = {''} + local received = { '' } for i, chunk in ipairs(chunks) do if i < #chunks then -- if chunks got joined, a spurious [''] callback was not sent - neq({''}, chunk) + neq({ '' }, chunk) else -- but EOF callback is still sent - eq({''}, chunk) + eq({ '' }, chunk) end - received[#received] = received[#received]..chunk[1] + received[#received] = received[#received] .. chunk[1] for j = 2, #chunk do - received[#received+1] = chunk[j] + received[#received + 1] = chunk[j] end end eq(expected, received) @@ -585,22 +608,22 @@ describe('jobs', function() call jobwait([g:id]) ]]) - local expected = {'1', '2', '3', '4', '5', ''} + local expected = { '1', '2', '3', '4', '5', '' } local chunks = eval('d.data') -- check nothing was received after exit, including EOF eq(eval('g:exit_data'), chunks) - local received = {''} + local received = { '' } for i, chunk in ipairs(chunks) do if i < #chunks then -- if chunks got joined, a spurious [''] callback was not sent - neq({''}, chunk) + neq({ '' }, chunk) else -- but EOF callback is still sent - eq({''}, chunk) + eq({ '' }, chunk) end - received[#received] = received[#received]..chunk[1] + received[#received] = received[#received] .. chunk[1] for j = 2, #chunk do - received[#received+1] = chunk[j] + received[#received + 1] = chunk[j] end end eq(expected, received) @@ -618,11 +641,11 @@ describe('jobs', function() call jobstart('echo some text', g:job_opts) ]]) expect_msg_seq( - { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, - }, + { { 'notification', '1', { 'foo', 'bar', { 'some text', '' }, 'stdout' } } }, -- Alternative sequence: - { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, - {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + { + { 'notification', '1', { 'foo', 'bar', { 'some text' }, 'stdout' } }, + { 'notification', '1', { 'foo', 'bar', { '', '' }, 'stdout' } }, } ) end) @@ -638,11 +661,11 @@ describe('jobs', function() call jobstart('echo some text', g:job_opts) ]]) expect_msg_seq( - { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, - }, + { { 'notification', '1', { 'foo', 'bar', { 'some text', '' }, 'stdout' } } }, -- Alternative sequence: - { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, - {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + { + { 'notification', '1', { 'foo', 'bar', { 'some text' }, 'stdout' } }, + { 'notification', '1', { 'foo', 'bar', { '', '' }, 'stdout' } }, } ) end) @@ -653,18 +676,19 @@ describe('jobs', function() call jobstart('echo some text', g:job_opts) ]]) expect_msg_seq( - { {'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, - }, + { { 'notification', '1', { 'foo', 'bar', { 'some text', '' }, 'stdout' } } }, -- Alternative sequence: - { {'notification', '1', {'foo', 'bar', {'some text'}, 'stdout'}}, - {'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, + { + { 'notification', '1', { 'foo', 'bar', { 'some text' }, 'stdout' } }, + { 'notification', '1', { 'foo', 'bar', { '', '' }, 'stdout' } }, } ) end) it('jobstart() environment: $NVIM, $NVIM_LISTEN_ADDRESS #11009', function() local function get_env_in_child_job(envname, env) - return exec_lua([[ + return exec_lua( + [[ local envname, env = ... local join = function(s) return vim.fn.join(s, '') end local stdout = {} @@ -676,12 +700,13 @@ describe('jobs', function() on_stderr = function(chan, data, name) stderr = data end, on_stdout = function(chan, data, name) stdout = data end, } - local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1',( '+echo "%s="..getenv("%s")'):format(envname, envname), '+qa!' }, opt) + local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1',('+echo "%s="..getenv("%s")'):format(envname, envname), '+qa!' }, opt) vim.fn.jobwait({ j1 }, 10000) return join({ join(stdout), join(stderr) }) ]], - envname, - env) + envname, + env + ) end local addr = eval('v:servername') @@ -689,14 +714,18 @@ describe('jobs', function() -- $NVIM is _not_ defined in the top-level Nvim process. eq('', eval('$NVIM')) -- jobstart() shares its v:servername with the child via $NVIM. - eq('NVIM='..addr, get_env_in_child_job('NVIM')) + eq('NVIM=' .. addr, get_env_in_child_job('NVIM')) -- $NVIM_LISTEN_ADDRESS is unset by server_init in the child. eq('NVIM_LISTEN_ADDRESS=v:null', get_env_in_child_job('NVIM_LISTEN_ADDRESS')) - eq('NVIM_LISTEN_ADDRESS=v:null', get_env_in_child_job('NVIM_LISTEN_ADDRESS', - { NVIM_LISTEN_ADDRESS='Xtest_jobstart_env' })) + eq( + 'NVIM_LISTEN_ADDRESS=v:null', + get_env_in_child_job('NVIM_LISTEN_ADDRESS', { NVIM_LISTEN_ADDRESS = 'Xtest_jobstart_env' }) + ) -- User can explicitly set $NVIM_LOG_FILE, $VIM, $VIMRUNTIME. - eq('NVIM_LOG_FILE=Xtest_jobstart_env', - get_env_in_child_job('NVIM_LOG_FILE', { NVIM_LOG_FILE='Xtest_jobstart_env' })) + eq( + 'NVIM_LOG_FILE=Xtest_jobstart_env', + get_env_in_child_job('NVIM_LOG_FILE', { NVIM_LOG_FILE = 'Xtest_jobstart_env' }) + ) os.remove('Xtest_jobstart_env') end) @@ -721,7 +750,7 @@ describe('jobs', function() \ jobstart('sleep 0.310; exit 7') \ ])) ]]) - eq({'notification', 'wait', {{4, 5, 6, 7}}}, next_msg()) + eq({ 'notification', 'wait', { { 4, 5, 6, 7 } } }, next_msg()) end) it('will run callbacks while waiting', function() @@ -751,8 +780,7 @@ describe('jobs', function() \ ]) call rpcnotify(g:channel, 'wait', sort(g:jobs), sort(g:exits)) ]]) - eq({'notification', 'wait', - {{3,4,5,6}, {3,4,5,6}}}, next_msg()) + eq({ 'notification', 'wait', { { 3, 4, 5, 6 }, { 3, 4, 5, 6 } } }, next_msg()) end) it('will return status codes in the order of passed ids', function() @@ -769,7 +797,7 @@ describe('jobs', function() \ jobstart('sleep 0.010; exit 7') \ ])) ]]) - eq({'notification', 'wait', {{4, 5, 6, 7}}}, next_msg()) + eq({ 'notification', 'wait', { { 4, 5, 6, 7 } } }, next_msg()) end) it('will return -3 for invalid job ids', function() @@ -779,29 +807,33 @@ describe('jobs', function() \ jobstart((has('win32') ? 'Start-Sleep -Milliseconds 100' : 'sleep 0.01').'; exit 5'), \ ])) ]]) - eq({'notification', 'wait', {{-3, 5}}}, next_msg()) + eq({ 'notification', 'wait', { { -3, 5 } } }, next_msg()) end) it('will return -2 when interrupted without timeout', function() - feed_command('call rpcnotify(g:channel, "ready") | '.. - 'call rpcnotify(g:channel, "wait", '.. - 'jobwait([jobstart("'.. - (is_os('win') and 'Start-Sleep 10' or 'sleep 10').. - '; exit 55")]))') - eq({'notification', 'ready', {}}, next_msg()) + feed_command( + 'call rpcnotify(g:channel, "ready") | ' + .. 'call rpcnotify(g:channel, "wait", ' + .. 'jobwait([jobstart("' + .. (is_os('win') and 'Start-Sleep 10' or 'sleep 10') + .. '; exit 55")]))' + ) + eq({ 'notification', 'ready', {} }, next_msg()) feed('<c-c>') - eq({'notification', 'wait', {{-2}}}, next_msg()) + eq({ 'notification', 'wait', { { -2 } } }, next_msg()) end) it('will return -2 when interrupted with timeout', function() - feed_command('call rpcnotify(g:channel, "ready") | '.. - 'call rpcnotify(g:channel, "wait", '.. - 'jobwait([jobstart("'.. - (is_os('win') and 'Start-Sleep 10' or 'sleep 10').. - '; exit 55")], 10000))') - eq({'notification', 'ready', {}}, next_msg()) + feed_command( + 'call rpcnotify(g:channel, "ready") | ' + .. 'call rpcnotify(g:channel, "wait", ' + .. 'jobwait([jobstart("' + .. (is_os('win') and 'Start-Sleep 10' or 'sleep 10') + .. '; exit 55")], 10000))' + ) + eq({ 'notification', 'ready', {} }, next_msg()) feed('<c-c>') - eq({'notification', 'wait', {{-2}}}, next_msg()) + eq({ 'notification', 'wait', { { -2 } } }, next_msg()) end) it('can be called recursively', function() @@ -844,11 +876,11 @@ describe('jobs', function() local r for i = 10, 1, -1 do r = next_msg() - eq('job '..i..' closed', r[3][1]) + eq('job ' .. i .. ' closed', r[3][1]) r = next_msg() - eq('job '..i..' exited', r[3][1]) + eq('job ' .. i .. ' exited', r[3][1]) end - eq(10, nvim('eval', 'g:counter')) + eq(10, api.nvim_eval('g:counter')) end) describe('with timeout argument', function() @@ -858,7 +890,7 @@ describe('jobs', function() \ jobstart((has('win32') ? 'Start-Sleep 10' : 'sleep 10').'; exit 5'), \ ], 100)) ]]) - eq({'notification', 'wait', {{-1}}}, next_msg()) + eq({ 'notification', 'wait', { { -1 } } }, next_msg()) end) it('can pass 0 to check if a job exists', function() @@ -871,16 +903,16 @@ describe('jobs', function() \ jobstart('sleep 0.3; exit 5'), \ ], 0)) ]]) - eq({'notification', 'wait', {{-1, -1}}}, next_msg()) + eq({ 'notification', 'wait', { { -1, -1 } } }, next_msg()) end) end) it('hides cursor and flushes messages before blocking', function() local screen = Screen.new(50, 6) screen:set_default_attr_ids({ - [0] = {foreground = Screen.colors.Blue, bold = true}; -- NonText - [1] = {bold = true, reverse = true}; -- MsgSeparator - [2] = {bold = true, foreground = Screen.colors.SeaGreen}; -- MoreMsg + [0] = { foreground = Screen.colors.Blue, bold = true }, -- NonText + [1] = { bold = true, reverse = true }, -- MsgSeparator + [2] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg }) screen:attach() command([[let g:id = jobstart([v:progpath, '--clean', '--headless'])]]) @@ -892,56 +924,66 @@ describe('jobs', function() endfunc ]]) feed_command('call PrintAndWait()') - screen:expect{grid=[[ + screen:expect { + grid = [[ | - {0:~ }| - {0:~ }| + {0:~ }|*2 {1: }| aaa | bbb | - ]], timeout=100} - screen:expect{grid=[[ + ]], + timeout = 100, + } + screen:expect { + grid = [[ | {1: }| aaa | bbb | ccc | {2:Press ENTER or type command to continue}^ | - ]]} + ]], + } feed('<CR>') - funcs.jobstop(meths.get_var('id')) + fn.jobstop(api.nvim_get_var('id')) end) end) pending('exit event follows stdout, stderr', function() - nvim('command', "let g:job_opts.on_stderr = function('OnEvent')") - nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") - nvim('eval', 'jobsend(j, "abcdef")') - nvim('eval', 'jobstop(j)') + command("let g:job_opts.on_stderr = function('OnEvent')") + command("let j = jobstart(['cat', '-'], g:job_opts)") + api.nvim_eval('jobsend(j, "abcdef")') + api.nvim_eval('jobstop(j)') expect_msg_seq( - { {'notification', 'stdout', {0, {'abcdef'}}}, - {'notification', 'stdout', {0, {''}}}, - {'notification', 'stderr', {0, {''}}}, + { + { 'notification', 'stdout', { 0, { 'abcdef' } } }, + { 'notification', 'stdout', { 0, { '' } } }, + { 'notification', 'stderr', { 0, { '' } } }, }, -- Alternative sequence: - { {'notification', 'stderr', {0, {''}}}, - {'notification', 'stdout', {0, {'abcdef'}}}, - {'notification', 'stdout', {0, {''}}}, + { + { 'notification', 'stderr', { 0, { '' } } }, + { 'notification', 'stdout', { 0, { 'abcdef' } } }, + { 'notification', 'stdout', { 0, { '' } } }, }, -- Alternative sequence: - { {'notification', 'stdout', {0, {'abcdef'}}}, - {'notification', 'stderr', {0, {''}}}, - {'notification', 'stdout', {0, {''}}}, + { + { 'notification', 'stdout', { 0, { 'abcdef' } } }, + { 'notification', 'stderr', { 0, { '' } } }, + { 'notification', 'stdout', { 0, { '' } } }, } ) - eq({'notification', 'exit', {0, 143}}, next_msg()) + eq({ 'notification', 'exit', { 0, 143 } }, next_msg()) end) it('cannot have both rpc and pty options', function() - command("let g:job_opts.pty = v:true") - command("let g:job_opts.rpc = v:true") + command('let g:job_opts.pty = v:true') + command('let g:job_opts.rpc = v:true') local _, err = pcall(command, "let j = jobstart(['cat', '-'], g:job_opts)") - ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil) + ok( + string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") + ~= nil + ) end) it('does not crash when repeatedly failing to start shell', function() @@ -954,7 +996,7 @@ describe('jobs', function() ]]) -- The crash only triggered if both jobs are cleaned up on the same event -- loop tick. This is also prevented by try-block, so feed must be used. - feed_command("call DoIt()") + feed_command('call DoIt()') feed('<cr>') -- press RETURN assert_alive() end) @@ -991,7 +1033,7 @@ describe('jobs', function() \ 'substitute(v:val, "\r", "", "")'), \ 'split(v:val, "\\s\\+")') if len(proc) == 6 - let s:procs[proc[1]] ..']]'..[[= {'name': proc[0], + let s:procs[proc[1]] .. ']]' .. [[= {'name': proc[0], \ 'Session Name': proc[2], \ 'Session': proc[3]} endif @@ -1015,15 +1057,13 @@ describe('jobs', function() endfunction ]]) end - local sleep_cmd = (is_os('win') - and 'ping -n 31 127.0.0.1' - or 'sleep 30') - local j = eval("jobstart('"..sleep_cmd..' | '..sleep_cmd..' | '..sleep_cmd.."')") - local ppid = funcs.jobpid(j) + local sleep_cmd = (is_os('win') and 'ping -n 31 127.0.0.1' or 'sleep 30') + local j = eval("jobstart('" .. sleep_cmd .. ' | ' .. sleep_cmd .. ' | ' .. sleep_cmd .. "')") + local ppid = fn.jobpid(j) local children if is_os('win') then local status, result = pcall(retry, nil, nil, function() - children = meths.get_proc_children(ppid) + children = api.nvim_get_proc_children(ppid) -- On Windows conhost.exe may exist, and -- e.g. vctip.exe might appear. #10783 ok(#children >= 3 and #children <= 5) @@ -1034,36 +1074,38 @@ describe('jobs', function() error(result) end else - retry(nil, nil, function() - children = meths.get_proc_children(ppid) + retry(nil, nil, function() + children = api.nvim_get_proc_children(ppid) eq(3, #children) end) end -- Assert that nvim_get_proc() sees the children. for _, child_pid in ipairs(children) do - local info = meths.get_proc(child_pid) + local info = api.nvim_get_proc(child_pid) -- eq((is_os('win') and 'nvim.exe' or 'nvim'), info.name) eq(ppid, info.ppid) end -- Kill the root of the tree. - eq(1, funcs.jobstop(j)) + eq(1, fn.jobstop(j)) -- Assert that the children were killed. retry(nil, nil, function() for _, child_pid in ipairs(children) do - eq(NIL, meths.get_proc(child_pid)) + eq(NIL, api.nvim_get_proc(child_pid)) end end) end) it('jobstop on same id before stopped', function() - nvim('command', 'let j = jobstart(["cat", "-"], g:job_opts)') + command('let j = jobstart(["cat", "-"], g:job_opts)') neq(0, eval('j')) - eq({1, 0}, eval('[jobstop(j), jobstop(j)]')) + eq({ 1, 0 }, eval('[jobstop(j), jobstop(j)]')) end) describe('running tty-test program', function() - if skip(is_os('win')) then return end + if skip(is_os('win')) then + return + end local function next_chunk() local rv while true do @@ -1084,7 +1126,7 @@ describe('jobs', function() local j local function send(str) -- check no nvim_chan_free double free with pty job (#14198) - meths.chan_send(j, str) + api.nvim_chan_send(j, str) end before_each(function() @@ -1095,10 +1137,10 @@ describe('jobs', function() endfunction ]]) insert(testprg('tty-test')) - nvim('command', 'let g:job_opts.pty = 1') - nvim('command', 'let exec = [expand("<cfile>:p")]') - nvim('command', "let j = jobstart(exec, g:job_opts)") - j = eval'j' + command('let g:job_opts.pty = 1') + command('let exec = [expand("<cfile>:p")]') + command('let j = jobstart(exec, g:job_opts)') + j = eval 'j' eq('tty ready', next_chunk()) end) @@ -1108,17 +1150,17 @@ describe('jobs', function() end) it('resizing window', function() - nvim('command', 'call jobresize(j, 40, 10)') + command('call jobresize(j, 40, 10)') eq('rows: 10, cols: 40', next_chunk()) - nvim('command', 'call jobresize(j, 10, 40)') + command('call jobresize(j, 10, 40)') eq('rows: 40, cols: 10', next_chunk()) end) it('jobclose() sends SIGHUP', function() - nvim('command', 'call jobclose(j)') + command('call jobclose(j)') local msg = next_msg() - msg = (msg[2] == 'stdout') and next_msg() or msg -- Skip stdout, if any. - eq({'notification', 'exit', {0, 42}}, msg) + msg = (msg[2] == 'stdout') and next_msg() or msg -- Skip stdout, if any. + eq({ 'notification', 'exit', { 0, 42 } }, msg) end) it('jobstart() does not keep ptmx file descriptor open', function() @@ -1132,7 +1174,7 @@ describe('jobs', function() -- Have to wait so that the SIGHUP can be processed by tty-test on time. -- Can't wait for the next message in case this test fails, if it fails -- there won't be any more messages, and the test would hang. - helpers.sleep(100) + vim.uv.sleep(100) local err = exc_exec('call jobpid(j)') eq('Vim(call):E900: Invalid channel id', err) @@ -1141,9 +1183,49 @@ describe('jobs', function() command('call jobstop(' .. other_jobid .. ')') end) end) + + it('does not close the same handle twice on exit #25086', function() + local filename = string.format('%s.lua', helpers.tmpname()) + write_file( + filename, + [[ + vim.api.nvim_create_autocmd('VimLeavePre', { + callback = function() + local id = vim.fn.jobstart('sleep 0') + vim.fn.jobwait({id}) + end, + }) + ]] + ) + + local screen = thelpers.setup_child_nvim({ + '--cmd', + 'set notermguicolors', + '-i', + 'NONE', + '-u', + filename, + }) + -- Wait for startup to complete, so that all terminal responses are received. + screen:expect([[ + {1: } | + ~ |*3 + {1:[No Name] 0,0-1 All}| + | + {3:-- TERMINAL --} | + ]]) + + feed(':q<CR>') + screen:expect([[ + | + [Process exited 0]{1: } | + |*4 + {3:-- TERMINAL --} | + ]]) + end) end) -describe("pty process teardown", function() +describe('pty process teardown', function() local screen before_each(function() clear() @@ -1151,31 +1233,33 @@ describe("pty process teardown", function() screen:attach() screen:expect([[ ^ | - ~ | - ~ | - ~ | - ~ | + ~ |*4 | ]]) end) - it("does not prevent/delay exit. #4798 #4900", function() - skip(is_os('win')) + it('does not prevent/delay exit. #4798 #4900', function() -- Use a nested nvim (in :term) to test without --headless. - feed_command(":terminal '"..helpers.nvim_prog - .."' -u NONE -i NONE --cmd '"..nvim_set.."' " + fn.termopen({ + helpers.nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '--cmd', + nvim_set, -- Use :term again in the _nested_ nvim to get a PTY process. -- Use `sleep` to simulate a long-running child of the PTY. - .."+terminal +'!(sleep 300 &)' +qa") + '+terminal', + '+!(sleep 300 &)', + '+qa', + }, { env = { VIMRUNTIME = os.getenv('VIMRUNTIME') } }) -- Exiting should terminate all descendants (PTY, its children, ...). screen:expect([[ ^ | [Process exited 0] | - | - | - | - | + |*4 ]]) end) end) diff --git a/test/functional/core/log_spec.lua b/test/functional/core/log_spec.lua index f682df4155..1637e683c1 100644 --- a/test/functional/core/log_spec.lua +++ b/test/functional/core/log_spec.lua @@ -23,7 +23,7 @@ describe('log', function() -- calls, that needs investigation. clear() eq(0, request('nvim__stats').log_skip) - clear{env={CDPATH='~doesnotexist'}} + clear { env = { CDPATH = '~doesnotexist' } } assert(request('nvim__stats').log_skip <= 13) end) @@ -32,14 +32,16 @@ describe('log', function() -- ERR 2022-05-29T12:30:03.800 T2 log_init:110: test log message -- ERR 2022-05-29T12:30:03.814 T2/child log_init:110: test log message - clear({env={ - NVIM_LOG_FILE=testlog, - -- TODO: remove this after nvim_log #7062 is merged. - __NVIM_TEST_LOG='1' - }}) + clear({ + env = { + NVIM_LOG_FILE = testlog, + -- TODO: remove this after nvim_log #7062 is merged. + __NVIM_TEST_LOG = '1', + }, + }) local tid = _G._nvim_test_id - assert_log(tid..'%.%d+%.%d +server_init:%d+: test log message', testlog, 100) + assert_log(tid .. '%.%d+%.%d +server_init:%d+: test log message', testlog, 100) exec_lua([[ local j1 = vim.fn.jobstart({ vim.v.progpath, '-es', '-V1', '+foochild', '+qa!' }, vim.empty_dict()) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 19c7a93730..9d8d64c82d 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -1,4 +1,4 @@ -local luv = require('luv') +local uv = vim.uv local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') @@ -7,7 +7,7 @@ local matches = helpers.matches local feed = helpers.feed local eval = helpers.eval local clear = helpers.clear -local funcs = helpers.funcs +local fn = helpers.fn local nvim_prog_abs = helpers.nvim_prog_abs local write_file = helpers.write_file local is_os = helpers.is_os @@ -32,28 +32,57 @@ describe('command-line option', function() end) it('treats - as stdin', function() - eq(nil, luv.fs_stat(fname)) - funcs.system( - {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', - '-s', '-', fname}, - {':call setline(1, "42")', ':wqall!', ''}) + eq(nil, uv.fs_stat(fname)) + fn.system({ + nvim_prog_abs(), + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '--cmd', + 'set noswapfile shortmess+=IFW fileformats=unix', + '-s', + '-', + fname, + }, { ':call setline(1, "42")', ':wqall!', '' }) eq(0, eval('v:shell_error')) - local attrs = luv.fs_stat(fname) - eq(#('42\n'), attrs.size) + local attrs = uv.fs_stat(fname) + eq(#'42\n', attrs.size) end) it('does not expand $VAR', function() - eq(nil, luv.fs_stat(fname)) + eq(nil, uv.fs_stat(fname)) eq(true, not not dollar_fname:find('%$%w+')) write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') - funcs.system( - {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', - '-s', dollar_fname, fname}) + fn.system({ + nvim_prog_abs(), + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '--cmd', + 'set noswapfile shortmess+=IFW fileformats=unix', + '-s', + dollar_fname, + fname, + }) + eq(0, eval('v:shell_error')) + local attrs = uv.fs_stat(fname) + eq(#'100500\n', attrs.size) + end) + + it('does not crash when run completion in ex mode', function() + fn.system({ + nvim_prog_abs(), + '--clean', + '-e', + '-s', + '--cmd', + 'exe "norm! i\\<C-X>\\<C-V>"', + }) eq(0, eval('v:shell_error')) - local attrs = luv.fs_stat(fname) - eq(#('100500\n'), attrs.size) end) it('does not crash after reading from stdin in non-headless mode', function() @@ -61,36 +90,38 @@ describe('command-line option', function() local screen = Screen.new(40, 8) screen:attach() local args = { - nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', - '--cmd', '"set noswapfile shortmess+=IFW fileformats=unix"', - '-s', '-' + nvim_prog_abs(), + '-u', + 'NONE', + '-i', + 'NONE', + '--cmd', + '"set noswapfile shortmess+=IFW fileformats=unix notermguicolors"', + '-s', + '-', } -- Need to explicitly pipe to stdin so that the embedded Nvim instance doesn't try to read -- data from the terminal #18181 - funcs.termopen(string.format([[echo "" | %s]], table.concat(args, " "))) - screen:expect([[ - ^ | - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {2:[No Name] 0,0-1 All}| - | - | - ]], { - [1] = {foreground = tonumber('0x4040ff'), fg_indexed=true}, - [2] = {bold = true, reverse = true} + fn.termopen(string.format([[echo "" | %s]], table.concat(args, ' ')), { + env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) + screen:expect( + [[ + ^ | + ~ |*4 + {1:[No Name] 0,0-1 All}| + |*2 + ]], + { + [1] = { reverse = true }, + } + ) feed('i:cq<CR>') screen:expect([[ | [Process exited 1] | - | - | - | - | - | + |*5 -- TERMINAL -- | ]]) --[=[ Example of incorrect output: @@ -101,20 +132,29 @@ describe('command-line option', function() LENO' failed. | | [Process exited 6] | - | - | + |*2 ]]) ]=] end) it('errors out when trying to use nonexistent file with -s', function() eq( - 'Cannot open for reading: "'..nonexistent_fname..'": no such file or directory\n', - funcs.system( - {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', - '--cmd', 'language C', - '-s', nonexistent_fname})) + 'Cannot open for reading: "' .. nonexistent_fname .. '": no such file or directory\n', + fn.system({ + nvim_prog_abs(), + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '--cmd', + 'set noswapfile shortmess+=IFW fileformats=unix', + '--cmd', + 'language C', + '-s', + nonexistent_fname, + }) + ) eq(2, eval('v:shell_error')) end) @@ -122,21 +162,34 @@ describe('command-line option', function() write_file(fname, ':call setline(1, "1")\n:wqall!\n') write_file(dollar_fname, ':call setline(1, "2")\n:wqall!\n') eq( - 'Attempt to open script file again: "-s '..dollar_fname..'"\n', - funcs.system( - {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', - '--cmd', 'language C', - '-s', fname, '-s', dollar_fname, fname_2})) + 'Attempt to open script file again: "-s ' .. dollar_fname .. '"\n', + fn.system({ + nvim_prog_abs(), + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '--cmd', + 'set noswapfile shortmess+=IFW fileformats=unix', + '--cmd', + 'language C', + '-s', + fname, + '-s', + dollar_fname, + fname_2, + }) + ) eq(2, eval('v:shell_error')) - eq(nil, luv.fs_stat(fname_2)) + eq(nil, uv.fs_stat(fname_2)) end) end) it('nvim -v, :version', function() - matches('Run ":verbose version"', funcs.execute(':version')) - matches('Compilation: .*Run :checkhealth', funcs.execute(':verbose version')) - matches('Run "nvim %-V1 %-v"', funcs.system({nvim_prog_abs(), '-v'})) - matches('Compilation: .*Run :checkhealth', funcs.system({nvim_prog_abs(), '-V1', '-v'})) + matches('Run ":verbose version"', fn.execute(':version')) + matches('Compilation: .*Run :checkhealth', fn.execute(':verbose version')) + matches('Run "nvim %-V1 %-v"', fn.system({ nvim_prog_abs(), '-v' })) + matches('Compilation: .*Run :checkhealth', fn.system({ nvim_prog_abs(), '-V1', '-v' })) end) end) diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index 97c32f7de6..e98bfc0d45 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -4,7 +4,7 @@ local command = helpers.command local eq = helpers.eq local eval = helpers.eval local feed = helpers.feed -local funcs = helpers.funcs +local fn = helpers.fn local insert = helpers.insert local is_os = helpers.is_os local mkdir = helpers.mkdir @@ -13,7 +13,7 @@ local write_file = helpers.write_file local function join_path(...) local pathsep = (is_os('win') and '\\' or '/') - return table.concat({...}, pathsep) + return table.concat({ ... }, pathsep) end describe('path collapse', function() @@ -23,40 +23,40 @@ describe('path collapse', function() before_each(function() targetdir = join_path('test', 'functional', 'fixtures') clear() - command('edit '..join_path(targetdir, 'tty-test.c')) + command('edit ' .. join_path(targetdir, 'tty-test.c')) expected_path = eval('expand("%:p")') end) it('with /./ segment #7117', function() - command('edit '..join_path(targetdir, '.', 'tty-test.c')) + command('edit ' .. join_path(targetdir, '.', 'tty-test.c')) eq(expected_path, eval('expand("%:p")')) end) it('with ./ prefix #7117', function() - command('edit '..join_path('.', targetdir, 'tty-test.c')) + command('edit ' .. join_path('.', targetdir, 'tty-test.c')) eq(expected_path, eval('expand("%:p")')) end) it('with ./ prefix, after directory change #7117', function() - command('edit '..join_path('.', targetdir, 'tty-test.c')) + command('edit ' .. join_path('.', targetdir, 'tty-test.c')) command('cd test') eq(expected_path, eval('expand("%:p")')) end) it('with /../ segment #7117', function() - command('edit '..join_path(targetdir, '..', 'fixtures', 'tty-test.c')) + command('edit ' .. join_path(targetdir, '..', 'fixtures', 'tty-test.c')) eq(expected_path, eval('expand("%:p")')) end) it('with ../ and different starting directory #7117', function() command('cd test') - command('edit '..join_path('..', targetdir, 'tty-test.c')) + command('edit ' .. join_path('..', targetdir, 'tty-test.c')) eq(expected_path, eval('expand("%:p")')) end) it('with ./../ and different starting directory #7117', function() command('cd test') - command('edit '..join_path('.', '..', targetdir, 'tty-test.c')) + command('edit ' .. join_path('.', '..', targetdir, 'tty-test.c')) eq(expected_path, eval('expand("%:p")')) end) end) @@ -67,16 +67,16 @@ describe('expand wildcard', function() it('with special characters #24421', function() local folders = is_os('win') and { '{folder}', - 'folder$name' + 'folder$name', } or { 'folder-name', - 'folder#name' + 'folder#name', } for _, folder in ipairs(folders) do mkdir(folder) local file = join_path(folder, 'file.txt') write_file(file, '') - eq(file, eval('expand("'..folder..'/*")')) + eq(file, eval('expand("' .. folder .. '/*")')) rmdir(folder) end end) @@ -131,14 +131,30 @@ describe('file search', function() test_cfile([[c:foo]], [[c]]) -- Examples from: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#example-ways-to-refer-to-the-same-file test_cfile([[c:\temp\test-file.txt]], [[c:]], [[c:\temp\test-file.txt]]) - test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) - test_cfile([[\\LOCALHOST\c$\temp\test-file.txt]], [[LOCALHOST]], [[\\LOCALHOST\c$\temp\test-file.txt]]) + test_cfile( + [[\\127.0.0.1\c$\temp\test-file.txt]], + [[127.0.0.1]], + [[\\127.0.0.1\c$\temp\test-file.txt]] + ) + test_cfile( + [[\\LOCALHOST\c$\temp\test-file.txt]], + [[LOCALHOST]], + [[\\LOCALHOST\c$\temp\test-file.txt]] + ) -- not supported yet test_cfile([[\\.\c:\temp\test-file.txt]], [[.]], [[\\.\c]]) -- not supported yet test_cfile([[\\?\c:\temp\test-file.txt]], [[c:]], [[\\]]) - test_cfile([[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]], [[.]], [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]]) - test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) + test_cfile( + [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]], + [[.]], + [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]] + ) + test_cfile( + [[\\127.0.0.1\c$\temp\test-file.txt]], + [[127.0.0.1]], + [[\\127.0.0.1\c$\temp\test-file.txt]] + ) end) ---@param funcname 'finddir' | 'findfile' @@ -151,7 +167,7 @@ describe('file search', function() else write_file(expected, '') end - eq(expected, funcs[funcname](item, d:gsub(' ', [[\ ]]))) + eq(expected, fn[funcname](item, d:gsub(' ', [[\ ]]))) end it('finddir()', function() diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua index a0ec748446..caff06f6ab 100644 --- a/test/functional/core/remote_spec.lua +++ b/test/functional/core/remote_spec.lua @@ -6,7 +6,7 @@ local eq = helpers.eq local exec_capture = helpers.exec_capture local exec_lua = helpers.exec_lua local expect = helpers.expect -local funcs = helpers.funcs +local fn = helpers.fn local insert = helpers.insert local nvim_prog = helpers.nvim_prog local new_argv = helpers.new_argv @@ -42,7 +42,7 @@ describe('Remote', function() -- Run a `nvim --remote*` command and return { stdout, stderr } of the process local function run_remote(...) set_session(server) - local addr = funcs.serverlist()[1] + local addr = fn.serverlist()[1] -- Create an nvim instance just to run the remote-invoking nvim. We want -- to wait for the remote instance to exit and calling jobwait blocks @@ -51,7 +51,10 @@ describe('Remote', function() local client_starter = spawn(new_argv(), false, nil, true) set_session(client_starter) -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness. - eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart({...}, { + eq( + { 0 }, + exec_lua( + [[return vim.fn.jobwait({ vim.fn.jobstart({...}, { stdout_buffered = true, stderr_buffered = true, on_stdout = function(_, data, _) @@ -60,7 +63,15 @@ describe('Remote', function() on_stderr = function(_, data, _) _G.Remote_stderr = table.concat(data, '\n') end, - }) })]], nvim_prog, '--clean', '--headless', '--server', addr, ...)) + }) })]], + nvim_prog, + '--clean', + '--headless', + '--server', + addr, + ... + ) + ) local res = exec_lua([[return { _G.Remote_stdout, _G.Remote_stderr }]]) client_starter:close() set_session(server) @@ -70,20 +81,20 @@ describe('Remote', function() it('edit a single file', function() eq({ '', '' }, run_remote('--remote', fname)) expect(contents) - eq(2, #funcs.getbufinfo()) + eq(1, #fn.getbufinfo()) end) it('tab edit a single file with a non-changed buffer', function() eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) - eq(1, #funcs.gettabinfo()) + eq(1, #fn.gettabinfo()) end) it('tab edit a single file with a changed buffer', function() insert('hello') eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) - eq(2, #funcs.gettabinfo()) + eq(2, #fn.gettabinfo()) end) it('edit multiple files', function() @@ -91,15 +102,15 @@ describe('Remote', function() expect(contents) command('next') expect(other_contents) - eq(3, #funcs.getbufinfo()) + eq(2, #fn.getbufinfo()) end) it('send keys', function() - eq({ '', '' }, run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v')) + eq({ '', '' }, run_remote('--remote-send', ':edit ' .. fname .. '<CR><C-W>v')) expect(contents) - eq(2, #funcs.getwininfo()) + eq(2, #fn.getwininfo()) -- Only a single buffer as we're using edit and not drop like --remote does - eq(1, #funcs.getbufinfo()) + eq(1, #fn.getbufinfo()) end) it('evaluate expressions', function() @@ -116,7 +127,7 @@ describe('Remote', function() it('creates server if not found', function() clear('--remote', fname) expect(contents) - eq(1, #funcs.getbufinfo()) + eq(1, #fn.getbufinfo()) -- Since we didn't pass silent, we should get a complaint neq(nil, string.find(exec_capture('messages'), 'E247:')) end) @@ -124,8 +135,8 @@ describe('Remote', function() it('creates server if not found with tabs', function() clear('--remote-tab-silent', fname, other_fname) expect(contents) - eq(2, #funcs.gettabinfo()) - eq(2, #funcs.getbufinfo()) + eq(2, #fn.gettabinfo()) + eq(2, #fn.getbufinfo()) -- We passed silent, so no message should be issued about the server not being found eq(nil, string.find(exec_capture('messages'), 'E247:')) end) diff --git a/test/functional/core/spellfile_spec.lua b/test/functional/core/spellfile_spec.lua index e3a59085cf..57953b8f80 100644 --- a/test/functional/core/spellfile_spec.lua +++ b/test/functional/core/spellfile_spec.lua @@ -2,8 +2,9 @@ local helpers = require('test.functional.helpers')(after_each) local eq = helpers.eq local clear = helpers.clear -local meths = helpers.meths +local api = helpers.api local exc_exec = helpers.exc_exec +local fn = helpers.fn local rmdir = helpers.rmdir local write_file = helpers.write_file local mkdir = helpers.mkdir @@ -24,7 +25,8 @@ describe('spellfile', function() -- │ ┌ Spell file version (#VIMSPELLVERSION) local spellheader = 'VIMspell\050' it('errors out when prefcond section is truncated', function() - meths.set_option_value('runtimepath', testdir, {}) + api.nvim_set_option_value('runtimepath', testdir, {}) + -- stylua: ignore write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_PREFCOND) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -34,12 +36,12 @@ describe('spellfile', function() -- │ ┌ Condition length (1 byte) -- │ │ ┌ Condition regex (missing!) .. '\000\001\001') - meths.set_option_value('spelllang', 'en', {}) - eq('Vim(set):E758: Truncated spell file', - exc_exec('set spell')) + api.nvim_set_option_value('spelllang', 'en', {}) + eq('Vim(set):E758: Truncated spell file', exc_exec('set spell')) end) it('errors out when prefcond regexp contains NUL byte', function() - meths.set_option_value('runtimepath', testdir, {}) + api.nvim_set_option_value('runtimepath', testdir, {}) + -- stylua: ignore write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_PREFCOND) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -54,12 +56,12 @@ describe('spellfile', function() -- │ ┌ KWORDTREE tree length (4 bytes) -- │ │ ┌ PREFIXTREE tree length .. '\000\000\000\000\000\000\000\000\000\000\000\000') - meths.set_option_value('spelllang', 'en', {}) - eq('Vim(set):E759: Format error in spell file', - exc_exec('set spell')) + api.nvim_set_option_value('spelllang', 'en', {}) + eq('Vim(set):E759: Format error in spell file', exc_exec('set spell')) end) it('errors out when region contains NUL byte', function() - meths.set_option_value('runtimepath', testdir, {}) + api.nvim_set_option_value('runtimepath', testdir, {}) + -- stylua: ignore write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_REGION) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -71,12 +73,12 @@ describe('spellfile', function() -- │ ┌ KWORDTREE tree length (4 bytes) -- │ │ ┌ PREFIXTREE tree length .. '\000\000\000\000\000\000\000\000\000\000\000\000') - meths.set_option_value('spelllang', 'en', {}) - eq('Vim(set):E759: Format error in spell file', - exc_exec('set spell')) + api.nvim_set_option_value('spelllang', 'en', {}) + eq('Vim(set):E759: Format error in spell file', exc_exec('set spell')) end) it('errors out when SAL section contains NUL byte', function() - meths.set_option_value('runtimepath', testdir, {}) + api.nvim_set_option_value('runtimepath', testdir, {}) + -- stylua: ignore write_file(testdir .. '/spell/en.ascii.spl', -- ┌ Section identifier (#SN_SAL) -- │ ┌ Section flags (#SNF_REQUIRED or zero) @@ -95,16 +97,23 @@ describe('spellfile', function() -- │ ┌ KWORDTREE tree length (4 bytes) -- │ │ ┌ PREFIXTREE tree length .. '\000\000\000\000\000\000\000\000\000\000\000\000') - meths.set_option_value('spelllang', 'en', {}) - eq('Vim(set):E759: Format error in spell file', - exc_exec('set spell')) + api.nvim_set_option_value('spelllang', 'en', {}) + eq('Vim(set):E759: Format error in spell file', exc_exec('set spell')) end) it('errors out when spell header contains NUL bytes', function() - meths.set_option_value('runtimepath', testdir, {}) - write_file(testdir .. '/spell/en.ascii.spl', - spellheader:sub(1, -3) .. '\000\000') - meths.set_option_value('spelllang', 'en', {}) - eq('Vim(set):E757: This does not look like a spell file', - exc_exec('set spell')) + api.nvim_set_option_value('runtimepath', testdir, {}) + write_file(testdir .. '/spell/en.ascii.spl', spellheader:sub(1, -3) .. '\000\000') + api.nvim_set_option_value('spelllang', 'en', {}) + eq('Vim(set):E757: This does not look like a spell file', exc_exec('set spell')) + end) + + it('can be set to a relative path', function() + local fname = testdir .. '/spell/spell.add' + api.nvim_set_option_value('spellfile', fname, {}) + end) + + it('can be set to an absolute path', function() + local fname = fn.fnamemodify(testdir .. '/spell/spell.add', ':p') + api.nvim_set_option_value('spellfile', fname, {}) end) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 94ec3d4907..cc58226f48 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -13,8 +13,8 @@ local exec = helpers.exec local exec_capture = helpers.exec_capture local exec_lua = helpers.exec_lua local feed = helpers.feed -local funcs = helpers.funcs -local pesc = helpers.pesc +local fn = helpers.fn +local pesc = vim.pesc local mkdir = helpers.mkdir local mkdir_p = helpers.mkdir_p local nvim_prog = helpers.nvim_prog @@ -22,23 +22,37 @@ local nvim_set = helpers.nvim_set local read_file = helpers.read_file local retry = helpers.retry local rmdir = helpers.rmdir -local sleep = helpers.sleep -local startswith = helpers.startswith +local sleep = vim.uv.sleep +local startswith = vim.startswith local write_file = helpers.write_file -local meths = helpers.meths +local api = helpers.api local alter_slashes = helpers.alter_slashes local is_os = helpers.is_os local dedent = helpers.dedent -local tbl_map = helpers.tbl_map -local tbl_filter = helpers.tbl_filter -local endswith = helpers.endswith +local tbl_map = vim.tbl_map +local tbl_filter = vim.tbl_filter +local endswith = vim.endswith describe('startup', function() it('--clean', function() clear() - ok(string.find(alter_slashes(meths.get_option_value('runtimepath', {})), funcs.stdpath('config'), 1, true) ~= nil) + ok( + string.find( + alter_slashes(api.nvim_get_option_value('runtimepath', {})), + fn.stdpath('config'), + 1, + true + ) ~= nil + ) clear('--clean') - ok(string.find(alter_slashes(meths.get_option_value('runtimepath', {})), funcs.stdpath('config'), 1, true) == nil) + ok( + string.find( + alter_slashes(api.nvim_get_option_value('runtimepath', {})), + fn.stdpath('config'), + 1, + true + ) == nil + ) end) it('prevents remote UI infinite loop', function() @@ -46,11 +60,10 @@ describe('startup', function() local screen screen = Screen.new(84, 3) screen:attach() - funcs.termopen({ nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' }) + fn.termopen({ nvim_prog, '-u', 'NONE', '--server', eval('v:servername'), '--remote-ui' }) screen:expect([[ ^Cannot attach UI of :terminal child to its parent. (Unset $NVIM to skip this check) | - | - | + |*2 ]]) end) @@ -59,7 +72,8 @@ describe('startup', function() finally(function() os.remove(testfile) end) - clear({ args = {'--startuptime', testfile}}) + clear({ args = { '--startuptime', testfile } }) + assert_log('Embedded', testfile, 100) assert_log('sourcing', testfile, 100) assert_log("require%('vim%._editor'%)", testfile, 100) end) @@ -69,8 +83,20 @@ describe('startup', function() local screen screen = Screen.new(60, 7) screen:attach() - command([[let g:id = termopen('"]]..nvim_prog.. - [[" -u NONE -i NONE --cmd "set noruler" -D')]]) + local id = fn.termopen({ + nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '--cmd', + 'set noruler', + '-D', + }, { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + }) screen:expect([[ ^ | | @@ -80,15 +106,12 @@ describe('startup', function() > | | ]]) - command([[call chansend(g:id, "cont\n")]]) + fn.chansend(id, 'cont\n') screen:expect([[ ^ | - ~ | - ~ | - ~ | + ~ |*3 [No Name] | - | - | + |*2 ]]) end) end) @@ -102,13 +125,13 @@ describe('startup', function() vim.list_extend(args, nvim_args or {}) vim.list_extend(args, { '-l', (script or 'test/functional/fixtures/startup.lua') }) vim.list_extend(args, lua_args or {}) - local out = funcs.system(args, input):gsub('\r\n', '\n') + local out = fn.system(args, input):gsub('\r\n', '\n') return eq(dedent(expected), out) end it('failure modes', function() -- nvim -l <empty> - matches('nvim%.?e?x?e?: Argument missing after: "%-l"', funcs.system({ nvim_prog, '-l' })) + matches('nvim%.?e?x?e?: Argument missing after: "%-l"', fn.system({ nvim_prog, '-l' })) eq(1, eval('v:shell_error')) end) @@ -122,39 +145,48 @@ describe('startup', function() vim.uv.os_setenv('ASAN_OPTIONS', asan_options .. ':detect_leaks=0') ]] -- nvim -l foo.lua -arg1 -- a b c - assert_l_out([[ + assert_l_out( + [[ bufs: nvim args: 7 lua args: { "-arg1", "--exitcode", "73", "--arg2", [0] = "test/functional/fixtures/startup.lua" }]], {}, - { '-arg1', "--exitcode", "73", '--arg2' } + { '-arg1', '--exitcode', '73', '--arg2' } ) eq(73, eval('v:shell_error')) end) it('Lua-error sets Nvim exitcode', function() eq(0, eval('v:shell_error')) - matches('E5113: .* my pearls!!', - funcs.system({ nvim_prog, '-l', 'test/functional/fixtures/startup-fail.lua' })) + matches( + 'E5113: .* my pearls!!', + fn.system({ nvim_prog, '-l', 'test/functional/fixtures/startup-fail.lua' }) + ) eq(1, eval('v:shell_error')) - matches('E5113: .* %[string "error%("whoa"%)"%]:1: whoa', - funcs.system({ nvim_prog, '-l', '-' }, 'error("whoa")')) + matches( + 'E5113: .* %[string "error%("whoa"%)"%]:1: whoa', + fn.system({ nvim_prog, '-l', '-' }, 'error("whoa")') + ) eq(1, eval('v:shell_error')) end) it('executes stdin "-"', function() - assert_l_out('arg0=- args=2 whoa\n', + assert_l_out( + 'arg0=- args=2 whoa\n', nil, { 'arg1', 'arg 2' }, '-', - "print(('arg0=%s args=%d %s'):format(_G.arg[0], #_G.arg, 'whoa'))") - assert_l_out('biiig input: 1000042\n', + "print(('arg0=%s args=%d %s'):format(_G.arg[0], #_G.arg, 'whoa'))" + ) + assert_l_out( + 'biiig input: 1000042\n', nil, nil, '-', - ('print("biiig input: "..("%s"):len())'):format(string.rep('x', (1000 * 1000) + 42))) + ('print("biiig input: "..("%s"):len())'):format(string.rep('x', (1000 * 1000) + 42)) + ) eq(0, eval('v:shell_error')) end) @@ -169,7 +201,8 @@ describe('startup', function() it('sets _G.arg', function() -- nvim -l foo.lua - assert_l_out([[ + assert_l_out( + [[ bufs: nvim args: 3 lua args: { @@ -182,7 +215,8 @@ describe('startup', function() eq(0, eval('v:shell_error')) -- nvim -l foo.lua [args] - assert_l_out([[ + assert_l_out( + [[ bufs: nvim args: 7 lua args: { "-arg1", "--arg2", "--", "arg3", @@ -195,20 +229,22 @@ describe('startup', function() eq(0, eval('v:shell_error')) -- nvim file1 file2 -l foo.lua -arg1 -- file3 file4 - assert_l_out([[ + assert_l_out( + [[ bufs: file1 file2 nvim args: 10 lua args: { "-arg1", "arg 2", "--", "file3", "file4", [0] = "test/functional/fixtures/startup.lua" } ]], - { 'file1', 'file2', }, + { 'file1', 'file2' }, { '-arg1', 'arg 2', '--', 'file3', 'file4' } ) eq(0, eval('v:shell_error')) -- nvim -l foo.lua <vim args> - assert_l_out([[ + assert_l_out( + [[ bufs: nvim args: 5 lua args: { "-c", "set wrap?", @@ -239,30 +275,50 @@ describe('startup', function() end) it('disables swapfile/shada/config/plugins', function() - assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scripts=1\n', + assert_l_out( + 'updatecount=0 shadafile=NONE loadplugins=false scripts=1\n', nil, nil, '-', [[print(('updatecount=%d shadafile=%s loadplugins=%s scripts=%d'):format( - vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]]) + vim.o.updatecount, vim.o.shadafile, tostring(vim.o.loadplugins), math.max(1, #vim.fn.getscriptinfo())))]] + ) end) end) it('--cmd/-c/+ do not truncate long Lua print() message with --headless', function() - local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', 'lua print(("A"):rep(1234))', - '-c', 'lua print(("B"):rep(1234))', - '+lua print(("C"):rep(1234))', - '+q' }) + local out = fn.system({ + nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '--cmd', + 'lua print(("A"):rep(1234))', + '-c', + 'lua print(("B"):rep(1234))', + '+lua print(("C"):rep(1234))', + '+q', + }) eq(('A'):rep(1234) .. '\r\n' .. ('B'):rep(1234) .. '\r\n' .. ('C'):rep(1234), out) end) it('pipe at both ends: has("ttyin")==0 has("ttyout")==0', function() -- system() puts a pipe at both ends. - local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', nvim_set, - '-c', [[echo has('ttyin') has('ttyout')]], - '+q' }) + local out = fn.system({ + nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '--cmd', + nvim_set, + '-c', + [[echo has('ttyin') has('ttyout')]], + '+q', + }) eq('0 0', out) end) @@ -285,10 +341,21 @@ describe('startup', function() command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end -- Running in :terminal - command([[exe printf("terminal %s -u NONE -i NONE --cmd \"]] - ..nvim_set..[[\"]] - ..[[ -c \"echo has('ttyin') has('ttyout')\""]] - ..[[, shellescape(v:progpath))]]) + fn.termopen({ + nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '--cmd', + nvim_set, + '-c', + 'echo has("ttyin") has("ttyout")', + }, { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + }) screen:expect([[ ^ | ~ | @@ -306,15 +373,24 @@ describe('startup', function() os.remove('Xtest_startup_ttyout') end) -- Running in :terminal - command([[exe printf("terminal %s -u NONE -i NONE --cmd \"]] - ..nvim_set..[[\"]] - ..[[ -c \"call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')\"]] - ..[[ -c q | cat -v"]] -- Output to a pipe. - ..[[, shellescape(v:progpath))]]) + fn.termopen( + ( + [["%s" -u NONE -i NONE --cmd "%s"]] + .. [[ -c "call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')"]] + .. [[ -c q | cat -v]] + ):format(nvim_prog, nvim_set), + { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + } + ) retry(nil, 3000, function() sleep(1) - eq('1\n0\n', -- stdin is a TTY, stdout is a pipe - read_file('Xtest_startup_ttyout')) + eq( + '1\n0\n', -- stdin is a TTY, stdout is a pipe + read_file('Xtest_startup_ttyout') + ) end) end) @@ -327,16 +403,25 @@ describe('startup', function() os.remove('Xtest_startup_ttyout') end) -- Running in :terminal - command([[exe printf("terminal echo foo | ]] -- Input from a pipe. - ..[[%s -u NONE -i NONE --cmd \"]] - ..nvim_set..[[\"]] - ..[[ -c \"call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')\"]] - ..[[ -c q -- -"]] - ..[[, shellescape(v:progpath))]]) + fn.termopen( + ( + [[echo foo | ]] -- Input from a pipe. + .. [["%s" -u NONE -i NONE --cmd "%s"]] + .. [[ -c "call writefile([has('ttyin'), has('ttyout')], 'Xtest_startup_ttyout')"]] + .. [[ -c q -- -]] + ):format(nvim_prog, nvim_set), + { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + } + ) retry(nil, 3000, function() sleep(1) - eq('0\n1\n', -- stdin is a pipe, stdout is a TTY - read_file('Xtest_startup_ttyout')) + eq( + '0\n1\n', -- stdin is a pipe, stdout is a TTY + read_file('Xtest_startup_ttyout') + ) end) end) @@ -347,11 +432,18 @@ describe('startup', function() command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end -- Running in :terminal - command([[exe printf("terminal echo foo | ]] -- Input from a pipe. - ..[[%s -u NONE -i NONE --cmd \"]] - ..nvim_set..[[\"]] - ..[[ -c \"echo has('ttyin') has('ttyout')\""]] - ..[[, shellescape(v:progpath))]]) + fn.termopen( + ( + [[echo foo | ]] + .. [["%s" -u NONE -i NONE --cmd "%s"]] + .. [[ -c "echo has('ttyin') has('ttyout')"]] + ):format(nvim_prog, nvim_set), + { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + } + ) screen:expect([[ ^foo | ~ | @@ -361,28 +453,44 @@ describe('startup', function() end) it('input from pipe + file args #7679', function() - eq('ohyeah\r\n0 0 bufs=3', - funcs.system({nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '--headless', - '+.print', - "+echo has('ttyin') has('ttyout') 'bufs='.bufnr('$')", - '+qall!', - '-', - 'test/functional/fixtures/tty-test.c', - 'test/functional/fixtures/shell-test.c', - }, - { 'ohyeah', '' })) + eq( + 'ohyeah\r\n0 0 bufs=3', + fn.system({ + nvim_prog, + '-n', + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '+.print', + "+echo has('ttyin') has('ttyout') 'bufs='.bufnr('$')", + '+qall!', + '-', + 'test/functional/fixtures/tty-test.c', + 'test/functional/fixtures/shell-test.c', + }, { 'ohyeah', '' }) + ) end) it('if stdin is empty: selects buffer 2, deletes buffer 1 #8561', function() - eq('\r\n 2 %a "file1" line 0\r\n 3 "file2" line 0', - funcs.system({nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '--headless', - '+ls!', - '+qall!', - '-', - 'file1', - 'file2', - }, - { '' })) + eq( + '\r\n 2 %a "file1" line 0\r\n 3 "file2" line 0', + fn.system({ + nvim_prog, + '-n', + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '+ls!', + '+qall!', + '-', + 'file1', + 'file2', + }, { '' }) + ) end) it('stdin with -es/-Es #7679', function() @@ -392,48 +500,70 @@ describe('startup', function() -- -- -Es: read stdin as text -- - eq('partylikeits1999\n', - funcs.system({nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '-Es', '+.print', 'test/functional/fixtures/tty-test.c' }, - { 'partylikeits1999', '' })) - eq(inputstr, - funcs.system({nvim_prog, '-i', 'NONE', '-Es', '+%print', '-' }, - input)) + eq( + 'partylikeits1999\n', + fn.system({ + nvim_prog, + '-n', + '-u', + 'NONE', + '-i', + 'NONE', + '-Es', + '+.print', + 'test/functional/fixtures/tty-test.c', + }, { 'partylikeits1999', '' }) + ) + eq(inputstr, fn.system({ nvim_prog, '-i', 'NONE', '-Es', '+%print', '-' }, input)) -- with `-u NORC` - eq('thepartycontinues\n', - funcs.system({nvim_prog, '-n', '-u', 'NORC', '-Es', '+.print' }, - { 'thepartycontinues', '' })) + eq( + 'thepartycontinues\n', + fn.system({ nvim_prog, '-n', '-u', 'NORC', '-Es', '+.print' }, { 'thepartycontinues', '' }) + ) -- without `-u` - eq('thepartycontinues\n', - funcs.system({nvim_prog, '-n', '-Es', '+.print' }, - { 'thepartycontinues', '' })) + eq( + 'thepartycontinues\n', + fn.system({ nvim_prog, '-n', '-Es', '+.print' }, { 'thepartycontinues', '' }) + ) -- -- -es: read stdin as ex-commands -- - eq(' encoding=utf-8\n', - funcs.system({nvim_prog, '-n', '-u', 'NONE', '-i', 'NONE', '-es', 'test/functional/fixtures/tty-test.c' }, - { 'set encoding', '' })) - eq('line1\nline2\n', - funcs.system({nvim_prog, '-i', 'NONE', '-es', '-' }, - input)) + eq( + ' encoding=utf-8\n', + fn.system({ + nvim_prog, + '-n', + '-u', + 'NONE', + '-i', + 'NONE', + '-es', + 'test/functional/fixtures/tty-test.c', + }, { 'set encoding', '' }) + ) + eq('line1\nline2\n', fn.system({ nvim_prog, '-i', 'NONE', '-es', '-' }, input)) -- with `-u NORC` - eq(' encoding=utf-8\n', - funcs.system({nvim_prog, '-n', '-u', 'NORC', '-es' }, - { 'set encoding', '' })) + eq( + ' encoding=utf-8\n', + fn.system({ nvim_prog, '-n', '-u', 'NORC', '-es' }, { 'set encoding', '' }) + ) -- without `-u` - eq(' encoding=utf-8\n', - funcs.system({nvim_prog, '-n', '-es' }, - { 'set encoding', '' })) + eq(' encoding=utf-8\n', fn.system({ nvim_prog, '-n', '-es' }, { 'set encoding', '' })) end) it('-es/-Es disables swapfile, user config #8540', function() - for _,arg in ipairs({'-es', '-Es'}) do - local out = funcs.system({nvim_prog, arg, - '+set swapfile? updatecount? shadafile?', - "+put =map(getscriptinfo(), {-> v:val.name})", '+%print'}) + for _, arg in ipairs({ '-es', '-Es' }) do + local out = fn.system({ + nvim_prog, + arg, + '+set swapfile? updatecount? shadafile?', + '+put =map(getscriptinfo(), {-> v:val.name})', + '+%print', + }) local line1 = string.match(out, '^.-\n') -- updatecount=0 means swapfile was disabled. - eq(" swapfile updatecount=0 shadafile=\n", line1) + eq(' swapfile updatecount=0 shadafile=\n', line1) -- Standard plugins were loaded, but not user config. ok(string.find(out, 'man.lua') ~= nil) ok(string.find(out, 'init.vim') == nil) @@ -441,20 +571,39 @@ describe('startup', function() end) it('fails on --embed with -es/-Es/-l', function() - matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', - funcs.system({nvim_prog, '--embed', '-es' })) - matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', - funcs.system({nvim_prog, '--embed', '-Es' })) - matches('nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', - funcs.system({nvim_prog, '--embed', '-l', 'foo.lua' })) + matches( + 'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', + fn.system({ nvim_prog, '--embed', '-es' }) + ) + matches( + 'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', + fn.system({ nvim_prog, '--embed', '-Es' }) + ) + matches( + 'nvim[.exe]*: %-%-embed conflicts with %-es/%-Es/%-l', + fn.system({ nvim_prog, '--embed', '-l', 'foo.lua' }) + ) end) it('ENTER dismisses early message #7967', function() local screen screen = Screen.new(60, 6) screen:attach() - command([[let g:id = termopen('"]]..nvim_prog.. - [[" -u NONE -i NONE --cmd "set noruler" --cmd "let g:foo = g:bar"')]]) + local id = fn.termopen({ + nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '--cmd', + 'set noruler', + '--cmd', + 'let g:foo = g:bar', + }, { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + }) screen:expect([[ ^ | | @@ -463,14 +612,12 @@ describe('startup', function() Press ENTER or type command to continue | | ]]) - command([[call chansend(g:id, "\n")]]) + fn.chansend(id, '\n') screen:expect([[ ^ | - ~ | - ~ | + ~ |*2 [No Name] | - | - | + |*2 ]]) end) @@ -505,19 +652,32 @@ describe('startup', function() expected, -- FIXME(codehex): We should really set a timeout for the system function. -- If this test fails, there will be a waiting input state. - funcs.system({nvim_prog, '-u', 'NONE', '-c', + fn.system({ + nvim_prog, + '-u', + 'NONE', + '-c', 'for i in range(1, 100) | echo i | endfor | quit', - '--headless' + '--headless', }) ) end) - it("get command line arguments from v:argv", function() - local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', nvim_set, - '-c', [[echo v:argv[-1:] len(v:argv) > 1]], - '+q' }) - eq('[\'+q\'] 1', out) + it('get command line arguments from v:argv', function() + local out = fn.system({ + nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '--headless', + '--cmd', + nvim_set, + '-c', + [[echo v:argv[-1:] len(v:argv) > 1]], + '+q', + }) + eq("['+q'] 1", out) end) end) @@ -549,20 +709,20 @@ describe('startup', function() clear('-e') screen:attach() -- Verify we set the proper mode both before and after :vi. - feed("put =mode(1)<CR>vi<CR>:put =mode(1)<CR>") + feed('put =mode(1)<CR>vi<CR>:put =mode(1)<CR>') screen:expect([[ cv | ^n | :put =mode(1) | ]]) - eq('cv\n', - funcs.system({nvim_prog, '-n', '-es' }, - { 'put =mode(1)', 'print', '' })) + eq('cv\n', fn.system({ nvim_prog, '-n', '-es' }, { 'put =mode(1)', 'print', '' })) end) it('-d does not diff non-arglist windows #13720 #21289', function() - write_file('Xdiff.vim', [[ + write_file( + 'Xdiff.vim', + [[ let bufnr = nvim_create_buf(0, 1) let config = { \ 'relative': 'editor', @@ -572,78 +732,92 @@ describe('startup', function() \ 'row': 3, \ 'col': 3 \ } - autocmd WinEnter * call nvim_open_win(bufnr, v:false, config)]]) + autocmd WinEnter * call nvim_open_win(bufnr, v:false, config)]] + ) finally(function() os.remove('Xdiff.vim') end) - clear{args={'-u', 'Xdiff.vim', '-d', 'Xdiff.vim', 'Xdiff.vim'}} - eq(true, meths.get_option_value('diff', {win = funcs.win_getid(1)})) - eq(true, meths.get_option_value('diff', {win = funcs.win_getid(2)})) - local float_win = funcs.win_getid(3) - eq('editor', meths.win_get_config(float_win).relative) - eq(false, meths.get_option_value('diff', {win = float_win})) + clear { args = { '-u', 'Xdiff.vim', '-d', 'Xdiff.vim', 'Xdiff.vim' } } + eq(true, api.nvim_get_option_value('diff', { win = fn.win_getid(1) })) + eq(true, api.nvim_get_option_value('diff', { win = fn.win_getid(2) })) + local float_win = fn.win_getid(3) + eq('editor', api.nvim_win_get_config(float_win).relative) + eq(false, api.nvim_get_option_value('diff', { win = float_win })) end) it('does not crash if --embed is given twice', function() - clear{args={'--embed'}} + clear { args = { '--embed' } } assert_alive() end) it('does not crash when expanding cdpath during early_init', function() - clear{env={CDPATH='~doesnotexist'}} + clear { env = { CDPATH = '~doesnotexist' } } assert_alive() eq(',~doesnotexist', eval('&cdpath')) end) it("sets 'shortmess' when loading other tabs", function() - clear({args={'-p', 'a', 'b', 'c'}}) + clear({ args = { '-p', 'a', 'b', 'c' } }) local screen = Screen.new(25, 4) screen:attach() - screen:expect({grid=[[ + screen:expect({ + grid = [[ {1: a }{2: b c }{3: }{2:X}| ^ | {4:~ }| | ]], - attr_ids={ - [1] = {bold = true}, - [2] = {background = Screen.colors.LightGrey, underline = true}, - [3] = {reverse = true}, - [4] = {bold = true, foreground = Screen.colors.Blue1}, - }}) + attr_ids = { + [1] = { bold = true }, + [2] = { background = Screen.colors.LightGrey, underline = true }, + [3] = { reverse = true }, + [4] = { bold = true, foreground = Screen.colors.Blue1 }, + }, + }) end) end) describe('startup', function() local function pack_clear(cmd) -- add packages after config dir in rtp but before config/after - clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}, - args_rm={'runtimepath'}, + clear { + args = { + '--cmd', + 'set packpath=test/functional/fixtures', + '--cmd', + 'let paths=split(&rtp, ",")', + '--cmd', + 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', + '--cmd', + cmd, + }, + env = { XDG_CONFIG_HOME = 'test/functional/fixtures/' }, + args_rm = { 'runtimepath' }, } end - it("handles &packpath during startup", function() + it('handles &packpath during startup', function() pack_clear [[ let g:x = bar#test() let g:y = leftpad#pad("heyya") ]] eq(-3, eval 'g:x') - eq(" heyya", eval 'g:y') + eq(' heyya', eval 'g:y') pack_clear [[ lua _G.y = require'bar'.doit() _G.z = require'leftpad''howdy' ]] - eq({9003, '\thowdy'}, exec_lua [[ return { _G.y, _G.z } ]]) + eq({ 9003, '\thowdy' }, exec_lua [[ return { _G.y, _G.z } ]]) end) - it("handles require from &packpath in an async handler", function() - -- NO! you cannot just speed things up by calling async functions during startup! - -- It doesn't make anything actually faster! NOOOO! + it('handles require from &packpath in an async handler', function() + -- NO! you cannot just speed things up by calling async functions during startup! + -- It doesn't make anything actually faster! NOOOO! pack_clear [[ lua require'async_leftpad'('brrrr', 'async_res') ]] -- haha, async leftpad go brrrrr eq('\tbrrrr', exec_lua [[ return _G.async_res ]]) end) - it("handles :packadd during startup", function() + it('handles :packadd during startup', function() -- control group: opt/bonus is not available by default pack_clear [[ try @@ -655,8 +829,10 @@ describe('startup', function() eq('Vim(let):E117: Unknown function: bonus#secret', eval 'g:err') pack_clear [[ lua _G.test = {pcall(function() require'bonus'.launch() end)} ]] - eq({false, [[[string ":lua"]:1: module 'bonus' not found:]]}, - exec_lua [[ _G.test[2] = string.gsub(_G.test[2], '[\r\n].*', '') return _G.test ]]) + eq( + { false, [[[string ":lua"]:1: module 'bonus' not found:]] }, + exec_lua [[ _G.test[2] = string.gsub(_G.test[2], '[\r\n].*', '') return _G.test ]] + ) -- ok, time to launch the nukes: pack_clear [[ packadd! bonus | let g:x = bonus#secret() ]] @@ -666,46 +842,77 @@ describe('startup', function() eq('CPE 1704 TKS', exec_lua [[ return _G.y ]]) end) - it("handles the correct order with start packages and after/", function() + it('handles the correct order with start packages and after/', function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]] - eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq( + { 'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, + exec_lua [[ return _G.test_loadorder ]] + ) end) - it("handles the correct order with start packages and after/ after startup", function() + it('handles the correct order with start packages and after/ after startup', function() pack_clear [[ lua _G.test_loadorder = {} ]] command [[ runtime! filen.lua ]] - eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq( + { 'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, + exec_lua [[ return _G.test_loadorder ]] + ) end) - it("handles the correct order with globpath(&rtp, ...)", function() + it('handles the correct order with globpath(&rtp, ...)', function() pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] command [[ for x in globpath(&rtp, "filen.lua",1,1) call v:lua.dofile(x) endfor ]] - eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq( + { 'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, + exec_lua [[ return _G.test_loadorder ]] + ) - local rtp = meths.get_option_value('rtp', {}) - ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), - 'startswith(…)', 'rtp='..rtp) + local rtp = api.nvim_get_option_value('rtp', {}) + ok( + startswith( + rtp, + 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,' + ), + 'startswith(…)', + 'rtp=' .. rtp + ) end) - it("handles the correct order with opt packages and after/", function() + it('handles the correct order with opt packages and after/', function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! superspecial\nruntime! filen.lua" ]] - eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({ + 'ordinary', + 'SuperSpecial', + 'FANCY', + 'mittel', + 'FANCY after', + 'SuperSpecial after', + 'ordinary after', + }, exec_lua [[ return _G.test_loadorder ]]) end) - it("handles the correct order with opt packages and after/ after startup", function() + it('handles the correct order with opt packages and after/ after startup', function() pack_clear [[ lua _G.test_loadorder = {} ]] command [[ packadd! superspecial runtime! filen.lua ]] - eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({ + 'ordinary', + 'SuperSpecial', + 'FANCY', + 'mittel', + 'FANCY after', + 'SuperSpecial after', + 'ordinary after', + }, exec_lua [[ return _G.test_loadorder ]]) end) - it("handles the correct order with opt packages and globpath(&rtp, ...)", function() + it('handles the correct order with opt packages and globpath(&rtp, ...)', function() pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] command [[ packadd! superspecial @@ -713,18 +920,40 @@ describe('startup', function() call v:lua.dofile(x) endfor ]] - eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'SuperSpecial after', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({ + 'ordinary', + 'SuperSpecial', + 'FANCY', + 'mittel', + 'SuperSpecial after', + 'FANCY after', + 'ordinary after', + }, exec_lua [[ return _G.test_loadorder ]]) end) - it("handles the correct order with a package that changes packpath", function() + it('handles the correct order with a package that changes packpath', function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! funky\nruntime! filen.lua" ]] - eq({'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) - eq({'ordinary', 'funky!', 'mittel', 'ordinary after'}, exec_lua [[ return _G.nested_order ]]) + eq( + { 'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after' }, + exec_lua [[ return _G.test_loadorder ]] + ) + eq({ 'ordinary', 'funky!', 'mittel', 'ordinary after' }, exec_lua [[ return _G.nested_order ]]) end) - it("handles the correct order when prepending packpath", function() - clear{args={'--cmd', 'set packpath^=test/functional/fixtures', '--cmd', [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]]}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}} - eq({'ordinary', 'FANCY', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + it('handles the correct order when prepending packpath', function() + clear { + args = { + '--cmd', + 'set packpath^=test/functional/fixtures', + '--cmd', + [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]], + }, + env = { XDG_CONFIG_HOME = 'test/functional/fixtures/' }, + } + eq( + { 'ordinary', 'FANCY', 'FANCY after', 'ordinary after' }, + exec_lua [[ return _G.test_loadorder ]] + ) end) it('window widths are correct when modelines set &columns with tabpages', function() @@ -734,10 +963,10 @@ describe('startup', function() os.remove('Xtab1.noft') os.remove('Xtab2.noft') end) - clear({args = {'-p', 'Xtab1.noft', 'Xtab2.noft'}}) - eq(81, meths.win_get_width(0)) + clear({ args = { '-p', 'Xtab1.noft', 'Xtab2.noft' } }) + eq(81, api.nvim_win_get_width(0)) command('tabnext') - eq(81, meths.win_get_width(0)) + eq(81, api.nvim_win_get_width(0)) end) end) @@ -754,16 +983,22 @@ describe('sysinit', function() mkdir(xdgdir) mkdir(xdgdir .. pathsep .. 'nvim') - write_file(table.concat({xdgdir, 'nvim', 'sysinit.vim'}, pathsep), [[ + write_file( + table.concat({ xdgdir, 'nvim', 'sysinit.vim' }, pathsep), + [[ let g:loaded = get(g:, "loaded", 0) + 1 let g:xdg = 1 - ]]) + ]] + ) mkdir(vimdir) - write_file(table.concat({vimdir, 'sysinit.vim'}, pathsep), [[ + write_file( + table.concat({ vimdir, 'sysinit.vim' }, pathsep), + [[ let g:loaded = get(g:, "loaded", 0) + 1 let g:vim = 1 - ]]) + ]] + ) mkdir(xhome) end) @@ -774,23 +1009,27 @@ describe('sysinit', function() end) it('prefers XDG_CONFIG_DIRS over VIM', function() - clear{args={'--cmd', 'set nomore undodir=. directory=. belloff='}, - args_rm={'-u', '--cmd'}, - env={ HOME=xhome, - XDG_CONFIG_DIRS=xdgdir, - VIM=vimdir }} - eq('loaded 1 xdg 1 vim 0', - eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))')) + clear { + args = { '--cmd', 'set nomore undodir=. directory=. belloff=' }, + args_rm = { '-u', '--cmd' }, + env = { HOME = xhome, XDG_CONFIG_DIRS = xdgdir, VIM = vimdir }, + } + eq( + 'loaded 1 xdg 1 vim 0', + eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))') + ) end) it('uses VIM if XDG_CONFIG_DIRS unset', function() - clear{args={'--cmd', 'set nomore undodir=. directory=. belloff='}, - args_rm={'-u', '--cmd'}, - env={ HOME=xhome, - XDG_CONFIG_DIRS='', - VIM=vimdir }} - eq('loaded 1 xdg 0 vim 1', - eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))')) + clear { + args = { '--cmd', 'set nomore undodir=. directory=. belloff=' }, + args_rm = { '-u', '--cmd' }, + env = { HOME = xhome, XDG_CONFIG_DIRS = '', VIM = vimdir }, + } + eq( + 'loaded 1 xdg 0 vim 1', + eval('printf("loaded %d xdg %d vim %d", g:loaded, get(g:, "xdg", 0), get(g:, "vim", 0))') + ) end) end) @@ -799,8 +1038,8 @@ describe('user config init', function() local pathsep = helpers.get_pathsep() local xconfig = xhome .. pathsep .. 'Xconfig' local xdata = xhome .. pathsep .. 'Xdata' - local init_lua_path = table.concat({xconfig, 'nvim', 'init.lua'}, pathsep) - local xenv = { XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } + local init_lua_path = table.concat({ xconfig, 'nvim', 'init.lua' }, pathsep) + local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } before_each(function() rmdir(xhome) @@ -808,9 +1047,12 @@ describe('user config init', function() mkdir_p(xconfig .. pathsep .. 'nvim') mkdir_p(xdata) - write_file(init_lua_path, [[ + write_file( + init_lua_path, + [[ vim.g.lua_rc = 1 - ]]) + ]] + ) end) after_each(function() @@ -818,36 +1060,51 @@ describe('user config init', function() end) it('loads init.lua from XDG config home by default', function() - clear{ args_rm={'-u'}, env=xenv } + clear { args_rm = { '-u' }, env = xenv } eq(1, eval('g:lua_rc')) - eq(funcs.fnamemodify(init_lua_path, ':p'), eval('$MYVIMRC')) + eq(fn.fnamemodify(init_lua_path, ':p'), eval('$MYVIMRC')) end) describe('loads existing', function() local exrc_path = '.exrc' local xstate = 'Xstate' - local xstateenv = { XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata, XDG_STATE_HOME=xstate } + local xstateenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata, XDG_STATE_HOME = xstate } local function setup_exrc_file(filename) exrc_path = filename - if string.find(exrc_path, "%.lua$") then - write_file(exrc_path, string.format([[ + if string.find(exrc_path, '%.lua$') then + write_file( + exrc_path, + string.format( + [[ vim.g.exrc_file = "%s" - ]], exrc_path)) + ]], + exrc_path + ) + ) else - write_file(exrc_path, string.format([[ + write_file( + exrc_path, + string.format( + [[ let g:exrc_file = "%s" - ]], exrc_path)) + ]], + exrc_path + ) + ) end end before_each(function() - write_file(init_lua_path, [[ + write_file( + init_lua_path, + [[ vim.o.exrc = true vim.g.exrc_file = '---' - ]]) + ]] + ) mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) end) @@ -860,39 +1117,41 @@ describe('user config init', function() it(filename .. ' in cwd', function() setup_exrc_file(filename) - clear{ args_rm={'-u'}, env=xstateenv } + clear { args_rm = { '-u' }, env = xstateenv } -- The 'exrc' file is not trusted, and the prompt is skipped because there is no UI. eq('---', eval('g:exrc_file')) local screen = Screen.new(50, 8) screen:attach() - funcs.termopen({nvim_prog}) + fn.termopen({ nvim_prog }, { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + }) screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny, (a)llow:') }) -- `i` to enter Terminal mode, `a` to allow feed('ia') screen:expect([[ | - ~ | - ~ | - ~ | - ~ | + ~ |*4 [No Name] 0,0-1 All| | -- TERMINAL -- | ]]) feed(':echo g:exrc_file<CR>') - screen:expect(string.format([[ + screen:expect(string.format( + [[ | - ~ | - ~ | - ~ | - ~ | + ~ |*4 [No Name] 0,0-1 All| %s%s| -- TERMINAL -- | - ]], filename, string.rep(' ', 50 - #filename))) + ]], + filename, + string.rep(' ', 50 - #filename) + )) - clear{ args_rm={'-u'}, env=xstateenv } + clear { args_rm = { '-u' }, env = xstateenv } -- The 'exrc' file is now trusted. eq(filename, eval('g:exrc_file')) end) @@ -900,15 +1159,18 @@ describe('user config init', function() end) describe('with explicitly provided config', function() - local custom_lua_path = table.concat({xhome, 'custom.lua'}, pathsep) + local custom_lua_path = table.concat({ xhome, 'custom.lua' }, pathsep) before_each(function() - write_file(custom_lua_path, [[ + write_file( + custom_lua_path, + [[ vim.g.custom_lua_rc = 1 - ]]) + ]] + ) end) it('loads custom lua config and does not set $MYVIMRC', function() - clear{ args={'-u', custom_lua_path }, env=xenv } + clear { args = { '-u', custom_lua_path }, env = xenv } eq(1, eval('g:custom_lua_rc')) eq('', eval('$MYVIMRC')) end) @@ -916,14 +1178,17 @@ describe('user config init', function() describe('VIMRC also exists', function() before_each(function() - write_file(table.concat({xconfig, 'nvim', 'init.vim'}, pathsep), [[ + write_file( + table.concat({ xconfig, 'nvim', 'init.vim' }, pathsep), + [[ let g:vim_rc = 1 - ]]) + ]] + ) end) it('loads default lua config, but shows an error', function() - clear{ args_rm={'-u'}, env=xenv } - feed('<cr><c-c>') -- Dismiss "Conflicting config …" message. + clear { args_rm = { '-u' }, env = xenv } + feed('<cr><c-c>') -- Dismiss "Conflicting config …" message. eq(1, eval('g:lua_rc')) matches('^E5422: Conflicting configs', exec_capture('messages')) end) @@ -935,7 +1200,7 @@ describe('runtime:', function() local pathsep = helpers.get_pathsep() local xconfig = xhome .. pathsep .. 'Xconfig' local xdata = xhome .. pathsep .. 'Xdata' - local xenv = { XDG_CONFIG_HOME=xconfig, XDG_DATA_HOME=xdata } + local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } setup(function() rmdir(xhome) @@ -948,23 +1213,24 @@ describe('runtime:', function() end) it('loads plugin/*.lua from XDG config home', function() - local plugin_folder_path = table.concat({xconfig, 'nvim', 'plugin'}, pathsep) - local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) + local plugin_folder_path = table.concat({ xconfig, 'nvim', 'plugin' }, pathsep) + local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) mkdir_p(plugin_folder_path) finally(function() rmdir(plugin_folder_path) end) write_file(plugin_file_path, [[ vim.g.lua_plugin = 1 ]]) - clear{ args_rm={'-u'}, env=xenv } + clear { args_rm = { '-u' }, env = xenv } eq(1, eval('g:lua_plugin')) end) it('loads plugin/*.lua from start packages', function() - local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'category', 'start', 'test_plugin'}, pathsep) - local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep) - local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) + local plugin_path = + table.concat({ xconfig, 'nvim', 'pack', 'category', 'start', 'test_plugin' }, pathsep) + local plugin_folder_path = table.concat({ plugin_path, 'plugin' }, pathsep) + local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) local profiler_file = 'test_startuptime.log' mkdir_p(plugin_folder_path) finally(function() @@ -974,12 +1240,16 @@ describe('runtime:', function() write_file(plugin_file_path, [[vim.g.lua_plugin = 2]]) - clear{ args_rm={'-u'}, args={'--startuptime', profiler_file}, env=xenv } + clear { args_rm = { '-u' }, args = { '--startuptime', profiler_file }, env = xenv } eq(2, eval('g:lua_plugin')) -- Check if plugin_file_path is listed in getscriptinfo() - local scripts = tbl_map(function(s) return s.name end, funcs.getscriptinfo()) - ok(#tbl_filter(function(s) return endswith(s, plugin_file_path) end, scripts) > 0) + local scripts = tbl_map(function(s) + return s.name + end, fn.getscriptinfo()) + ok(#tbl_filter(function(s) + return endswith(s, plugin_file_path) + end, scripts) > 0) -- Check if plugin_file_path is listed in startup profile local profile_reader = io.open(profiler_file, 'r') @@ -989,12 +1259,13 @@ describe('runtime:', function() end) it('loads plugin/*.lua from site packages', function() - local nvimdata = is_os('win') and "nvim-data" or "nvim" - local plugin_path = table.concat({xdata, nvimdata, 'site', 'pack', 'xa', 'start', 'yb'}, pathsep) - local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep) - local plugin_after_path = table.concat({plugin_path, 'after', 'plugin'}, pathsep) - local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) - local plugin_after_file_path = table.concat({plugin_after_path, 'helloo.lua'}, pathsep) + local nvimdata = is_os('win') and 'nvim-data' or 'nvim' + local plugin_path = + table.concat({ xdata, nvimdata, 'site', 'pack', 'xa', 'start', 'yb' }, pathsep) + local plugin_folder_path = table.concat({ plugin_path, 'plugin' }, pathsep) + local plugin_after_path = table.concat({ plugin_path, 'after', 'plugin' }, pathsep) + local plugin_file_path = table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep) + local plugin_after_file_path = table.concat({ plugin_after_path, 'helloo.lua' }, pathsep) mkdir_p(plugin_folder_path) mkdir_p(plugin_after_path) finally(function() @@ -1004,49 +1275,64 @@ describe('runtime:', function() write_file(plugin_file_path, [[table.insert(_G.lista, "unos")]]) write_file(plugin_after_file_path, [[table.insert(_G.lista, "dos")]]) - clear{ args_rm={'-u'}, args={'--cmd', 'lua _G.lista = {}'}, env=xenv } + clear { args_rm = { '-u' }, args = { '--cmd', 'lua _G.lista = {}' }, env = xenv } - eq({'unos', 'dos'}, exec_lua "return _G.lista") + eq({ 'unos', 'dos' }, exec_lua 'return _G.lista') end) it('no crash setting &rtp in plugins with :packloadall called before #18315', function() - local plugin_folder_path = table.concat({xconfig, 'nvim', 'plugin'}, pathsep) + local plugin_folder_path = table.concat({ xconfig, 'nvim', 'plugin' }, pathsep) mkdir_p(plugin_folder_path) finally(function() rmdir(plugin_folder_path) end) - write_file(table.concat({plugin_folder_path, 'plugin.vim'}, pathsep), [[ + write_file( + table.concat({ plugin_folder_path, 'plugin.vim' }, pathsep), + [[ let &runtimepath = &runtimepath let g:vim_plugin = 1 - ]]) - write_file(table.concat({plugin_folder_path, 'plugin.lua'}, pathsep), [[ + ]] + ) + write_file( + table.concat({ plugin_folder_path, 'plugin.lua' }, pathsep), + [[ vim.o.runtimepath = vim.o.runtimepath vim.g.lua_plugin = 1 - ]]) + ]] + ) - clear{ args_rm={'-u'}, args = {'--cmd', 'packloadall'}, env=xenv } + clear { args_rm = { '-u' }, args = { '--cmd', 'packloadall' }, env = xenv } eq(1, eval('g:vim_plugin')) eq(1, eval('g:lua_plugin')) end) it("loads ftdetect/*.{vim,lua} respecting 'rtp' order", function() - local ftdetect_folder = table.concat({xconfig, 'nvim', 'ftdetect'}, pathsep) - local after_ftdetect_folder = table.concat({xconfig, 'nvim', 'after', 'ftdetect'}, pathsep) + local ftdetect_folder = table.concat({ xconfig, 'nvim', 'ftdetect' }, pathsep) + local after_ftdetect_folder = table.concat({ xconfig, 'nvim', 'after', 'ftdetect' }, pathsep) mkdir_p(ftdetect_folder) mkdir_p(after_ftdetect_folder) finally(function() rmdir(ftdetect_folder) rmdir(after_ftdetect_folder) end) - -- A .lua file is loaded after a .vim file if they only differ in extension. - -- All files in after/ftdetect/ are loaded after all files in ftdetect/. - write_file(table.concat({ftdetect_folder, 'new-ft.vim'}, pathsep), [[let g:seq ..= 'A']]) - write_file(table.concat({ftdetect_folder, 'new-ft.lua'}, pathsep), [[vim.g.seq = vim.g.seq .. 'B']]) - write_file(table.concat({after_ftdetect_folder, 'new-ft.vim'}, pathsep), [[let g:seq ..= 'a']]) - write_file(table.concat({after_ftdetect_folder, 'new-ft.lua'}, pathsep), [[vim.g.seq = vim.g.seq .. 'b']]) - clear{ args_rm={'-u'}, args = {'--cmd', 'let g:seq = ""'}, env=xenv } + -- A .lua file is loaded after a .vim file if they only differ in extension. + -- All files in after/ftdetect/ are loaded after all files in ftdetect/. + write_file(table.concat({ ftdetect_folder, 'new-ft.vim' }, pathsep), [[let g:seq ..= 'A']]) + write_file( + table.concat({ ftdetect_folder, 'new-ft.lua' }, pathsep), + [[vim.g.seq = vim.g.seq .. 'B']] + ) + write_file( + table.concat({ after_ftdetect_folder, 'new-ft.vim' }, pathsep), + [[let g:seq ..= 'a']] + ) + write_file( + table.concat({ after_ftdetect_folder, 'new-ft.lua' }, pathsep), + [[vim.g.seq = vim.g.seq .. 'b']] + ) + clear { args_rm = { '-u' }, args = { '--cmd', 'let g:seq = ""' }, env = xenv } eq('ABab', eval('g:seq')) end) end) @@ -1054,15 +1340,18 @@ end) describe('user session', function() local xhome = 'Xhome' local pathsep = helpers.get_pathsep() - local session_file = table.concat({xhome, 'session.lua'}, pathsep) + local session_file = table.concat({ xhome, 'session.lua' }, pathsep) before_each(function() rmdir(xhome) mkdir(xhome) - write_file(session_file, [[ + write_file( + session_file, + [[ vim.g.lua_session = 1 - ]]) + ]] + ) end) after_each(function() @@ -1070,7 +1359,45 @@ describe('user session', function() end) it('loads session from the provided lua file', function() - clear{ args={'-S', session_file }, env={ HOME=xhome }} + clear { args = { '-S', session_file }, env = { HOME = xhome } } eq(1, eval('g:lua_session')) end) end) + +describe('inccommand on ex mode', function() + it('should not preview', function() + clear() + local screen + screen = Screen.new(60, 10) + screen:attach() + local id = fn.termopen({ + nvim_prog, + '-u', + 'NONE', + '-i', + 'NONE', + '-c', + 'set termguicolors background=dark', + '-E', + 'test/README.md', + }, { + env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, + }) + fn.chansend(id, '%s/N') + screen:expect { + grid = [[ + {1:^ }| + {1: }|*6 + {1:Entering Ex mode. Type "visual" to go to Normal mode. }| + {1::%s/N }| + | + ]], + attr_ids = { + [1] = { + background = Screen.colors.NvimDarkGrey2, + foreground = Screen.colors.NvimLightGrey2, + }, + }, + } + end) +end) |