Emacsでカーソル位置をテストするためのライブラリを書いた #emacs

smart-newline.elを作っててそろそろユニットテストを書かないとつらくなってきたので、カーソル位置をテストをするためのcursor-test.elというライブラリを書いた。本当はキラキラネームにしたかったのだけどいい名前思いつかなかったのでそのまんまな名前をつけた。

https://github.com/ainame/cursor-test.el

emacsユニットテストは標準添付のertというものを使う。ertの日本語の解説はそんなにないんだけど↓みれば何となく分かる。 http://d.hatena.ne.jp/uk-ar/20120912/p1

ただ、ertを使うにしても、改行を扱うライブラリってどうやってテストすればいいのか悩んだ結果、カーソル位置を比較し合えば良いことに気づいた。カーソル位置は、通常pointという関数で取得できるので、テスト用のbufferを作ってその中でpointを呼べばカーソル位置が取得できる。あとは、どうやってバッファを作るかという部分だけど、前にブログでsmart-newline.elの解説書くときに使ってた記法がそのまま使えたらテストコード読みやすいし便利だと思って、あの形式を使えるようにした。

def foo| ← `|`がカーソル位置を示す
end

さて、使い方は簡単で、cursor-test/equalという関数に期待値と実測値の状態をemacsのbufferオブジェクトで渡せば良い。bufferオブジェクトはそんなに簡単には作れないのでヘルパーを用意してある。

cursor-test/setupが、比較対象のbufferを作るためのヘルパーで、初期状態を文字列で、テスト対象コードを呼び出す処理を無名関数で渡すと、その通りのbufferを作ってくれる。カーソル位置は、|という文字列を初期状態の中に書いておけば勝手にセットされる。

サンプルコードとして、Rubyのfooメソッドのメソッド名の末尾でC-jで改行した時に、カーソルが2行目に移動して半角スペース2つ分インデントされてるかどうかのテストを書く。

(require 'cursor-test)

(ert-deftest test-01 ()
  "test sample. compare expect and actual buffer."
  (cursor-test/equal
   :description "test 01"
   :actual (cursor-test/setup
            :init "
def foo|
end
"
            ;; major-modeをruby-modeにして改行+インデントを実行
            :exercise (lambda ()
                        (ruby-mode)
                        (newline-and-indent)))
   :expect (cursor-test/setup
            :init "
def foo
  |
end
")))

このファイルをM-x eval-bufferしてM-x ertすればテストが実行される。 失敗した時はこんな感じのデバッグ用メッセージが表示されて便利。

Fail test: test 01
[buffer]
expect: "
def foo
  |
end
"
actual: "
def foo
|
end
"
[point] expect: 12, actual: 10

ちなみに、cursor-test/equalだとcursor-test/setupを書くのがダルいのでcursor-test/equal*という省略版も用意している。cursor-test/setupの引数とcursor-test/equalの引数を混ぜた感じになる。

(ert-deftest test-03 ()
  "test sample. `cursor-test/equal*` is shorthand version of cursor-test/equal."
  (cursor-test/equal*
   :description "test 03"
   :init "
def foo
|end
"
   :exercise (lambda ()
               (ruby-mode)
               (newline-and-indent))
   :expect "
def foo

|end
"))

目で見て分かりやすいテストかけるので、目に優しいし、人間にも優しい。

今回は、早速melpaにPullRequestしたのでそのうちインストール出来るようになると思う。 https://github.com/milkypostman/melpa/pull/1160