Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing Vim script-local functions with Vimrunner

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?

like image 490
Jim Stewart Avatar asked Mar 24 '13 05:03

Jim Stewart


2 Answers

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
like image 72
Ingo Karkat Avatar answered Oct 05 '22 11:10

Ingo Karkat


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()")
like image 33
XPlatformer Avatar answered Oct 05 '22 13:10

XPlatformer