I'm using Vimrunner to unit-test a Vim plugin. Everything is working, but I'm looking for a better/canonical way to execute script-local functions. Since they're not directly visible outside the script, I'm currently exposing the script's <SID>
and prepending that to my calls in order to run them.
I have to add this code to my plugin to expose the SID:
function! s:SID()
let fullname = expand("<sfile>")
return matchstr(fullname, '<SNR>\d\+_')
endfunction
let g:my_plugin_SID = s:SID()
That will expose the SID as e.g. <SNR>18_
. Since Vim functions are all global, and just name-munged, script-local functions can be invoked outside the script by prefixing the SID:
:call <SNR>18_some_function()
Then I do this in a spec:
describe "s:reverse_string" do
let!(:sid) { VIM.command("echo g:my_plugin_SID") }
def reverse_string(string)
VIM.command("echo #{sid}reverse_string('#{string}')")
end
it "does something" do
reverse_string("foo").should == "oof"
end
end
Is there a better way to do this?
The easiest way is to simply expose script-local functions: Turn s:MyFunc
into MyPlugin#MyFunc
. After all, function visibility is only by convention, anyway; nothing (except the cumbersome name lookup) prevents script-local functions from being called, anyway.
Sometimes, I deviate from that and do want to invoke script-local functions from tests. My approach is quite similar to yours, but instead of exposing the <SID>
from within the plugin, I've written helper functions to parse the <SID>
from the :scriptnames
output. It's slower (I don't care; my tests are integration tests), but I don't have to pollute the plugin itself with test code. Here's an example showing the Sid()
and SidInvoke()
helpers:
let s:SID = Sid('autoload/EditSimilar/Substitute.vim')
function! s:Is( input, expected, description )
let l:got = SidInvoke(s:SID, printf("IsWildcardPathPattern('%s')", a:input))
call vimtap#Is(l:got, a:expected, a:description)
endfunction
Use this to get the script number:
let sid = matchlist(execute('scriptnames'), '\([0-9]\+\): [^ ]*autoload/my_vimscript.vim')[1]
and this to invoke a script-local function (s:Test()
in this example)
execute("call <snr>" . sid . "_Test()")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With