Vim script で Assert

っぽいことを使いやすく定義してみました。
まぁ Assert といっても処理が中断するわけではないのですが。

[使い方]

function! s:plus(a, b)
    return a:a + a:b
endfunction


function! g:minus(a, b)
    return a:a - a:b
endfunction


function! s:test()
    let n = 10
    let m = 7

    " 基本的な使い方
    call g:assert(1)
    " => OK :
    call g:assert(0)
    " => Assert :

    " 偽の時のメッセージを指定
    call g:assert_msg(s:plus(1, 3) == 5, "g:plus(1, 3) == 5")
    " => Assert : g:plus(1, 3) == 5

    " 文字列で式を渡せば、そのまま式がメッセージとして出力される
    call g:assert_expr("g:minus(1, 3) == 2")
    " => Assert : g:minus(1, 3) == 2

    " スクリプトローカル関数も可
    call g:assert_expr("s:plus(1, 3) == 2")
    " => Assert : s:plus(1, 3) == 2

    " ローカル変数を使用する場合は、l: を渡す
    call g:assert_expr("s:plus(1, 3) == n", l:)
    " => Assert : s:plus(1, 3) == n

    " コマンド版
    " 式がそのまま評価される
    Assert g:plus(1, 3) == 4
    " => OK : g:plus(1, 3) == 4

    Assert s:minus(1, 3) == 5
    " => Assert : s:minus(1, 3) == 5

    " ローカル変数も OK
    Assert g:plus(3, m) == n
    " => OK : g:plus(3, m) == n

    " Equal 版
    " スペースで引数を区切っているので、余計なスペースが挿入できない…
    AssertEqual s:plus(2,m) n-5
    " => Assert : s:plus(2,m) == n-5
endfunction
call s:test()


式を文字列と渡して、それを評価してチェックを行います。
スクリプトローカルやローカル変数もそのまま使用できます。
:Assert コマンドを使用する場合は余計な記述を書く必要がないのでだいぶすっきりしていますね。
結構書きやすくていいんじゃないかなーと。


もうちょっと出力メッセージとかを賢くして簡単なテストモジュールとか書いてみたいところではある。
しかし、Vim script の闇が怖い…。

[注意]

スクリプト変数や関数外ではスクリプト関数は参照できませんゆえ…。

" グロバール関数は OK
Assert g:plus(1, 3) == 4

" スクリプト関数は NG
Assert s:minus(1, 3) == -4

let s:value = 10
function! s:test()
    " s:value は参照できない
    Assert s:value == 10
endfunction
call s:test()

[ソース]

function! s:SCaller()
    try
        throw 'abc'
    catch /^abc$/
        return matchstr(v:throwpoint, '^function \zs.\{-}\ze\.\.')
    endtry
endfunction

function! s:SID()
    return (split(s:SCaller(), "_") + [""])[0]
endfunction


let g:assert_fail_message_prefix = "Assert : "
let g:assert_success_message_prefix = "OK : "

function! g:assert_msg(expr, msg)
    if !a:expr
        echo g:assert_fail_message_prefix . a:msg
        return 0
    else
        echo g:assert_success_message_prefix . a:msg
        return 1
    endif
endfunction

function! g:assert(expr)
    return g:assert_msg(a:expr, "")
endfunction

function! g:assert_expr(expr, ...)
    call extend(l:, get(a:, "1", {}))
    try
        return g:assert_msg(eval(substitute(a:expr, "s:", s:SID()."_", "g")), a:expr)
    catch /.*/
        echo "except : ". a:expr
        echo v:exception
        return 0
    endtry
endfunction


command! -nargs=*
\   Assert call g:assert_msg(eval(substitute(<q-args>, "s:", s:SID()."_", "g")), <q-args>)

command! -nargs=*
\   AssertEqual execute "Assert " .join([<f-args>], " == ")