feat(test): use nvim_exec in helpers.source() #16064

helpers.source() was a hack to work around the lack of anonymous
:source. Its "create tempfile" behavior is not a required part of most
tests that use it.

Some tests still need the old "create tempfile" behavior either because
they test SID behavior, or because of missing nvim_exec features: #16071
This commit is contained in:
Justin M. Keyes 2022-03-27 10:25:55 -07:00 committed by GitHub
parent 05edab85d7
commit 72652cbc46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 93 additions and 70 deletions

View File

@ -1,4 +1,6 @@
[![Neovim](https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png)](https://neovim.io)
<h1 align="center">
<img src="https://raw.githubusercontent.com/neovim/neovim.github.io/master/logos/neovim-logo-300x87.png" alt="Neovim">
</h1>
[Documentation](https://neovim.io/doc/general/) |
[Chat](https://app.element.io/#/room/#neovim:matrix.org) |

View File

@ -3670,8 +3670,7 @@ A jump table for the options with a short description can be found at |Q_op|.
0: never
1: only if there are at least two windows
2: always
3: have a global statusline at the bottom instead of one for
each window
3: always and ONLY the last window
The screen looks nicer with a status line if you have several
windows, but it takes another screen line. |status-line|

View File

@ -182,8 +182,7 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
*:so* *:source* *load-vim-script*
:[range]so[urce] [file] Runs |Ex| commands or Lua code (".lua" files) from
[file], or from the current buffer if no [file] is
given.
[file], or current buffer if no [file].
Triggers the |SourcePre| autocommand.
*:source!*
:[range]so[urce]! {file}

View File

@ -180,6 +180,7 @@ Commands:
|:Man| is available by default, with many improvements such as completion
|:sign-define| accepts a `numhl` argument, to highlight the line number
|:match| can be invoked before highlight group is defined
|:source| works with Lua and anonymous (no file) scripts
Events:
|RecordingEnter|

View File

@ -9217,6 +9217,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
/// Finds the dict (g:, l:, s:, …) and hashtable used for a variable.
///
/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994
///
/// @param[in] name Variable name, possibly with scope prefix.
/// @param[in] name_len Variable name length.
/// @param[out] varname Will be set to the start of the name without scope
@ -9304,6 +9306,7 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, cons
}
}
if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) {
// Create SID if s: scope is accessed from Lua or anon Vimscript. #15994
new_script_item(NULL, &current_sctx.sc_sid);
}
*d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict;

View File

@ -65,6 +65,7 @@ describe('nvim_get_commands', function()
local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, keepscript=false, script_id=3, }
local cmd4 = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R4_just_great()', name='Cmd4', nargs='0', range=NIL, register=true, keepscript=false, script_id=4, }
source([[
let s:foo = 1
command -complete=custom,ListUsers -nargs=+ Finger !finger <args>
]])
eq({Finger=cmd1}, meths.get_commands({builtin=false}))
@ -72,12 +73,18 @@ describe('nvim_get_commands', function()
eq({Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
source([[
function! s:foo() abort
endfunction
command -bang -nargs=* Cmd2 call <SID>foo(<q-args>)
]])
source([[
function! s:ohyeah() abort
endfunction
command -bar -nargs=0 Cmd3 call <SID>ohyeah()
]])
source([[
function! s:just_great() abort
endfunction
command -register Cmd4 call <SID>just_great()
]])
-- TODO(justinmk): Order is stable but undefined. Sort before return?

View File

@ -1,21 +1,23 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local feed_command = helpers.feed_command
local feed = helpers.feed
local eq = helpers.eq
local expect = helpers.expect
local eval = helpers.eval
local funcs = helpers.funcs
local insert = helpers.insert
local write_file = helpers.write_file
local exc_exec = helpers.exc_exec
local source = helpers.source
local command = helpers.command
local Screen = require('test.functional.ui.screen')
describe('mappings with <Cmd>', function()
local screen
local tmpfile = 'X_ex_cmds_cmd_map'
local function cmdmap(lhs, rhs)
feed_command('noremap '..lhs..' <Cmd>'..rhs..'<cr>')
feed_command('noremap! '..lhs..' <Cmd>'..rhs..'<cr>')
command('noremap '..lhs..' <Cmd>'..rhs..'<cr>')
command('noremap! '..lhs..' <Cmd>'..rhs..'<cr>')
end
before_each(function()
@ -39,7 +41,7 @@ describe('mappings with <Cmd>', function()
cmdmap('<F4>', 'normal! ww')
cmdmap('<F5>', 'normal! "ay')
cmdmap('<F6>', 'throw "very error"')
feed_command([[
command([[
function! TextObj()
if mode() !=# "v"
normal! v
@ -55,11 +57,15 @@ describe('mappings with <Cmd>', function()
feed('gg')
cmdmap('<F8>', 'startinsert')
cmdmap('<F9>', 'stopinsert')
feed_command("abbr foo <Cmd>let g:y = 17<cr>bar")
command("abbr foo <Cmd>let g:y = 17<cr>bar")
end)
after_each(function()
os.remove(tmpfile)
end)
it('can be displayed', function()
feed_command('map <F3>')
command('map <F3>')
screen:expect([[
^some short lines |
of test text |
@ -73,8 +79,8 @@ describe('mappings with <Cmd>', function()
end)
it('handles invalid mappings', function()
feed_command('let x = 0')
feed_command('noremap <F3> <Cmd><Cmd>let x = 1<cr>')
command('let x = 0')
command('noremap <F3> <Cmd><Cmd>let x = 1<cr>')
feed('<F3>')
screen:expect([[
^some short lines |
@ -87,7 +93,7 @@ describe('mappings with <Cmd>', function()
{2:E5521: <Cmd> mapping must end with <CR> before second <Cmd>} |
]])
feed_command('noremap <F3> <Cmd><F3>let x = 2<cr>')
command('noremap <F3> <Cmd><F3>let x = 2<cr>')
feed('<F3>')
screen:expect([[
^some short lines |
@ -100,7 +106,7 @@ describe('mappings with <Cmd>', function()
{2:E5522: <Cmd> mapping must not include <F3> key} |
]])
feed_command('noremap <F3> <Cmd>let x = 3')
command('noremap <F3> <Cmd>let x = 3')
feed('<F3>')
screen:expect([[
^some short lines |
@ -137,7 +143,7 @@ describe('mappings with <Cmd>', function()
eq('n', eval('mode(1)'))
-- select mode mapping
feed_command('snoremap <F3> <Cmd>let m = mode(1)<cr>')
command('snoremap <F3> <Cmd>let m = mode(1)<cr>')
feed('gh<F3>')
eq('s', eval('m'))
-- didn't leave select mode
@ -184,8 +190,8 @@ describe('mappings with <Cmd>', function()
eq('n', eval('mode(1)'))
-- terminal mode
feed_command('tnoremap <F3> <Cmd>let m = mode(1)<cr>')
feed_command('split | terminal')
command('tnoremap <F3> <Cmd>let m = mode(1)<cr>')
command('split | terminal')
feed('i')
eq('t', eval('mode(1)'))
feed('<F3>')
@ -264,11 +270,11 @@ describe('mappings with <Cmd>', function()
end)
it('works in :normal command', function()
feed_command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>')
feed_command('noremap ,f <Cmd>nosuchcommand<cr>')
feed_command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>')
feed_command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>')
feed_command('noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>')
command('noremap ,x <Cmd>call append(1, "xx")\\| call append(1, "aa")<cr>')
command('noremap ,f <Cmd>nosuchcommand<cr>')
command('noremap ,e <Cmd>throw "very error"\\| call append(1, "yy")<cr>')
command('noremap ,m <Cmd>echoerr "The message."\\| call append(1, "zz")<cr>')
command('noremap ,w <Cmd>for i in range(5)\\|if i==1\\|echoerr "Err"\\|endif\\|call append(1, i)\\|endfor<cr>')
feed(":normal ,x<cr>")
screen:expect([[
@ -297,7 +303,7 @@ describe('mappings with <Cmd>', function()
:normal ,x |
]])
feed_command(':%d')
command(':%d')
eq('Vim(echoerr):Err', exc_exec("normal ,w"))
screen:expect([[
^ |
@ -310,8 +316,8 @@ describe('mappings with <Cmd>', function()
--No lines in buffer-- |
]])
feed_command(':%d')
feed_command(':normal ,w')
command(':%d')
feed(':normal ,w<cr>')
screen:expect([[
^ |
4 |
@ -401,8 +407,8 @@ describe('mappings with <Cmd>', function()
end)
it('works in select mode', function()
feed_command('snoremap <F1> <cmd>throw "very error"<cr>')
feed_command('snoremap <F2> <cmd>normal! <c-g>"by<cr>')
command('snoremap <F1> <cmd>throw "very error"<cr>')
command('snoremap <F2> <cmd>normal! <c-g>"by<cr>')
-- can extend select mode
feed('gh<F4>')
screen:expect([[
@ -830,12 +836,14 @@ describe('mappings with <Cmd>', function()
end)
it("works with <SID> mappings", function()
source([[
command('new!')
write_file(tmpfile, [[
map <f2> <Cmd>call <SID>do_it()<Cr>
function! s:do_it()
let g:x = 10
endfunction
]])
command('source '..tmpfile)
feed('<f2>')
eq('', eval('v:errmsg'))
eq(10, eval('g:x'))

View File

@ -181,9 +181,9 @@ describe(':echo :echon :echomsg :echoerr', function()
end)
it('dumps references to script functions', function()
eq('<SNR>2_Test2', eval('String(Test2_f)'))
eq("function('<SNR>2_Test2')", eval('StringMsg(Test2_f)'))
eq("function('<SNR>2_Test2')", eval('StringErr(Test2_f)'))
eq('<SNR>1_Test2', eval('String(Test2_f)'))
eq("function('<SNR>1_Test2')", eval('StringMsg(Test2_f)'))
eq("function('<SNR>1_Test2')", eval('StringErr(Test2_f)'))
end)
it('dump references to lambdas', function()
@ -205,11 +205,11 @@ describe(':echo :echon :echomsg :echoerr', function()
it('dumps automatically created partials', function()
assert_same_echo_dump(
"function('<SNR>2_Test2', {'f': function('<SNR>2_Test2')})",
"function('<SNR>1_Test2', {'f': function('<SNR>1_Test2')})",
'{"f": Test2_f}.f',
true)
assert_same_echo_dump(
"function('<SNR>2_Test2', [1], {'f': function('<SNR>2_Test2', [1])})",
"function('<SNR>1_Test2', [1], {'f': function('<SNR>1_Test2', [1])})",
'{"f": function(Test2_f, [1])}.f',
true)
end)
@ -227,7 +227,7 @@ describe(':echo :echon :echomsg :echoerr', function()
function()
meths.set_var('d', {v=true})
eq(dedent([[
{'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]]),
{'p': function('<SNR>1_Test2', {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]),
exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
end)
@ -264,7 +264,7 @@ describe(':echo :echon :echomsg :echoerr', function()
eval('add(l, function("Test1", l))')
eval('add(l, function("Test1", d))')
eq(dedent([=[
{'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]=]),
{'p': function('<SNR>1_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]),
exec_capture('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
end)
end)

View File

@ -2,18 +2,30 @@ local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local neq = helpers.neq
local command = helpers.command
local write_file = helpers.write_file
local meths = helpers.meths
local clear = helpers.clear
local dedent = helpers.dedent
local source = helpers.source
local exc_exec = helpers.exc_exec
local missing_provider = helpers.missing_provider
local tmpfile = 'X_ex_cmds_script'
before_each(clear)
local function source(code)
write_file(tmpfile, code)
command('source '..tmpfile)
end
describe('script_get-based command', function()
local garbage = ')}{+*({}]*[;(+}{&[]}{*])('
after_each(function()
os.remove(tmpfile)
end)
local function test_garbage_exec(cmd, check_neq)
describe(cmd, function()
it('works correctly when skipping oneline variant', function()

View File

@ -21,7 +21,6 @@ local map = global_helpers.tbl_map
local ok = global_helpers.ok
local sleep = global_helpers.sleep
local tbl_contains = global_helpers.tbl_contains
local write_file = global_helpers.write_file
local fail = global_helpers.fail
local module = {
@ -54,7 +53,6 @@ if module.nvim_dir == module.nvim_prog then
module.nvim_dir = "."
end
local tmpname = global_helpers.tmpname
local iswin = global_helpers.iswin
local prepend_argv
@ -494,24 +492,9 @@ function module.feed_command(...)
end
end
local sourced_fnames = {}
-- @deprecated use nvim_exec()
function module.source(code)
local fname = tmpname()
write_file(fname, code)
module.command('source '..fname)
-- DO NOT REMOVE FILE HERE.
-- do_source() has a habit of checking whether files are “same” by using inode
-- and device IDs. If you run two source() calls in quick succession there is
-- a good chance that underlying filesystem will reuse the inode, making files
-- appear as “symlinks” to do_source when it checks FileIDs. With current
-- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse
-- inodes, Mac OS machines (again, both QB and travis) do not.
--
-- Files appearing as “symlinks” mean that both the first and the second
-- source() calls will use same SID, which may fail some tests which check for
-- exact numbers after `<SNR>` in e.g. function names.
sourced_fnames[#sourced_fnames + 1] = fname
return fname
module.exec(dedent(code))
end
function module.has_powershell()
@ -525,7 +508,7 @@ function module.set_shell_powershell()
local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin()
and {'alias:cat', 'alias:echo', 'alias:sleep'}
or {'alias:echo'}, ',')..';'
module.source([[
module.exec([[
let &shell = ']]..shell..[['
set shellquote= shellxquote=
let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
@ -887,9 +870,6 @@ module = global_helpers.tbl_extend('error', module, global_helpers)
return function(after_each)
if after_each then
after_each(function()
for _, fname in ipairs(sourced_fnames) do
os.remove(fname)
end
check_logs()
check_cores('build/bin/nvim')
if session then

View File

@ -136,19 +136,19 @@ describe('assert function:', function()
end)
it('should have file names and passed messages', function()
local tmpname_one = source([[
source([[
call assert_equal(1, 100, 'equal assertion failed')
call assert_false('true', 'true assertion failed')
call assert_true('false', 'false assertion failed')
]])
local tmpname_two = source([[
source([[
call assert_true('', 'file two')
]])
expected_errors({
tmpname_one .. " line 1: equal assertion failed: Expected 1 but got 100",
tmpname_one .. " line 2: true assertion failed: Expected False but got 'true'",
tmpname_one .. " line 3: false assertion failed: Expected True but got 'false'",
tmpname_two .. " line 1: file two: Expected True but got ''",
"nvim_exec(): equal assertion failed: Expected 1 but got 100",
"nvim_exec(): true assertion failed: Expected False but got 'true'",
"nvim_exec(): false assertion failed: Expected True but got 'false'",
"nvim_exec(): file two: Expected True but got ''",
})
end)
end)

View File

@ -10,6 +10,7 @@ local source = helpers.source
local poke_eventloop = helpers.poke_eventloop
local uname = helpers.uname
local load_adjust = helpers.load_adjust
local write_file = helpers.write_file
local isCI = helpers.isCI
local function isasan()
@ -84,6 +85,12 @@ setmetatable(monitor_memory_usage,
end})
describe('memory usage', function()
local tmpfile = 'X_memory_usage'
after_each(function()
os.remove(tmpfile)
end)
local function check_result(tbl, status, result)
if not status then
print('')
@ -103,7 +110,7 @@ describe('memory usage', function()
it('function capture vargs', function()
local pid = eval('getpid()')
local before = monitor_memory_usage(pid)
source([[
write_file(tmpfile, [[
func s:f(...)
let x = a:000
endfunc
@ -111,6 +118,8 @@ describe('memory usage', function()
call s:f(0)
endfor
]])
-- TODO: check_result fails if command() is used here. Why? #16064
feed_command('source '..tmpfile)
poke_eventloop()
local after = monitor_memory_usage(pid)
-- Estimate the limit of max usage as 2x initial usage.
@ -136,7 +145,7 @@ describe('memory usage', function()
it('function capture lvars', function()
local pid = eval('getpid()')
local before = monitor_memory_usage(pid)
local fname = source([[
write_file(tmpfile, [[
if !exists('s:defined_func')
func s:f()
let x = l:
@ -147,10 +156,12 @@ describe('memory usage', function()
call s:f()
endfor
]])
feed_command('source '..tmpfile)
poke_eventloop()
local after = monitor_memory_usage(pid)
for _ = 1, 3 do
feed_command('so '..fname)
-- TODO: check_result fails if command() is used here. Why? #16064
feed_command('source '..tmpfile)
poke_eventloop()
end
local last = monitor_memory_usage(pid)

View File

@ -11,6 +11,7 @@ describe('vimscript', function()
return
end
source([[
let s:foo = 1
func! <sid>_dummy_function()
echo 1
endfunc