トップ 最新 追記

follow ikegami__ at http://twitter.com

イネムリネズミ日記

いけがみを召喚するには、出現予定を参考にしてください。三週間前までにメールをくだされば、日程を追加するなどしてスケジュールに組み込むことができるかもしれません。勉強会や個人的な会合、中途採用面接などに応じます。日記に書かないことはこちら

2003|04|05|06|07|11|12|
2004|01|02|03|04|05|06|07|10|11|
2005|01|02|03|04|05|06|07|08|11|
2006|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|11|12|
2010|03|04|12|
2011|02|03|04|06|08|09|10|

2006-09-05 一時復活(?) [長年日記]

_ ごぶさたしておりました

やー、いろいろあって日記をつけてませんでした。その間は元気だったり、そうでもなかったりです。

以前日記が中断したときもそうだったんだけど、ローカルにメモをとるようになってからは見えるところに日記を書く気持ちが少なくなってしまったんだよね。コミュニケーションはメールとか IRC で満足してしまいますから。

ローカルにとるメモは ChangeLogメモ (clmemo-mode) と併用して、planner-modeを利用するようになった。planner-mode は多機能スケジューラで、私はとても気に入っている。

_ Emacs を使ってメモやスケジュールをとる(clmemo & planner-mode)

clmemo (ないしは ChangeLog メモ) については日本語の情報が充実している。私は Meadow Memo の ChangeLog メモに関する記述を参考にした。Meadow Memo はとても充実していて、Meadow ではない emacsen を使うときにも非常に参考になりありがたい。一方、planner についてはまとまった日本語の記述はあまり見当たらない。planner はずっと多機能で、開発もどんどん進んでいるので日本語のドキュメントを書こうという気があまり起きないかもしれない(少なくとも私はそう)。インストールにあたっては EmacsWiki の planner-mode に関する記述 を参考にした。EmacsWiki も Meadow Memo 同様いろいろな情報が集約されており大変ありがたい。こちらに書かれているように、planner-mode は他の emacs lisp library (特に muse-mode は必須) を要求するので手間がかかる。Debian などの Linux ディストリビューションなら簡単にインストールできて便利である。

_ planner-mode を使うきっかけはDavid Allen の GTD (Get Things Done)に興味をもったからである(といっても私は書籍はまだ手に入れていないのだが)。仕事と趣味と私事が多岐にわたり、完全にパンクしてしまった私は今の上司から物事に優先度をつけるようにと言われて、どのように優先度をつけたらいいかを調べ始めた。GTD に関するHoriさんの記事が大変わかりやすかった。まずは計算機を使わずに紙ベースでやってみて、これは私に合っているなと感じた。しかし、計算機なしで GTD を続けることができる人はそもそも几帳面なのだと思う。私は三日坊主な人なので、すぐに破綻してしまった。そこで、支援するツールを探したところ、最初に web application based なツールが見付かった。

_ 私は web application が一般に嫌いである。なにしろブラウザに付随しているエディタは emacs ではない。FireFox なら emacs を経由してテキストを渡す方法が存在するが、それにしてもいちいちボタンを押してリロードというのは私には苦痛であった。編集も検索も emacs を経由したいと思うのは emacs 好きな人ならわかってくれると思う。名はあえて出さないが、web application based な GTD は触って面白かったが、私にとって毎日触りたいと思えるものではなかった。

_ そこで、Emacs に GTD mode はないのだろうかと調べてみた。すると GTD with Emacs PlannerMode という記事が見付かった(注:日本から見ようと思うとこのページを表示するのは遅いかもしれない)。This is one for the geeks.とは書いてあるが、試してみたくなった。同じ著者の書いた GTD's dirty secrets という記事も大変面白い。この人はきっと毎日 GTD を続けることができているのだろうという気がした。

_ Emacs の planner-mode で GTD (Get things done)を初めてちょうど 1 ヶ月になるが、なんとか続けてこられている。この日記同様、いつか中断するときも来るのかもしれないが、今のところは調子がよい。仕事や体調のほうは調子がよいとはいいがたい状況だが、好転するといいなあと思っている。これからもなんとかがんばります。


2006-09-06 プログラマがテストも書くならば lightweight なランダムテストを試すといいと思う [長年日記]

_ RushCheck - a lightweight random testing tool for Ruby

Ruby のランダムテストライブラリ RushCheck を公開している。これは3年前からつくり出したもので、PC で眠っていたものを今年の夏休みに公開したものである。Haskell の QuickCheck を Ruby でも使いたいなあと思ったのがきっかけであった。

ランダムテストというのはテスト手法のひとつである。テストケースに入力するデータをテストインスタンスと言うことにしよう。このとき、テストインスタンスを自動生成するというのがランダムテストの特徴である。たとえば文字列を入力とするテストならば、ランダムテストではその入力文字列をランダムに生成する。いくつもの異なった入力をランダムに生成して、同じテストケースを自動的に何度も実行するというテストの考え方である。

_ ランダムテストの有効性

ランダムテストが有効な場合について、Dick Hamlet 氏は When only random testing will doの中で、次の二通りに分類している:

  1. テストインスタンスが複雑かつ疎(sparse)で、テストを設計する際にどんな入力をためしたらよいかを考えるのが難しい場合
  2. テスト対象が状態を保持しており、テスト結果が入力だけでなくテストの実行時の状態にも依存してしまう場合

このふたつの場合ではどちらにせよ、一番有効なテストがすぐに決まらない。そこで、テストについて考える時間を費すかわりに、ランダムなテストをたくさん行ってしまって満足してはどうですか、という提案である。

一般に、テストというのは、どれくらいテストに時間をかけるか(設計、準備、テスト回数、テスト時間)と、その結果テストによる幸せ(バグが見付かった、あるいは満足の行く範囲でバグが見付からない)のトレードオフがさっぱりわからないという性格を持っている(わたしは専門家ではないので、知らないことがたくさんあると思う、これは間違っているかもしれない)テストにおけるコスト評価は難しい、というのが私の感想である。なんらかの事情でテストしなければならないときは、できるだけ簡単な方法で、かつ広範囲な入力を試したい、と思うのが当然だろう。

ユニットテストを支援するライブラリ (JUnit など) は、便利な assert 関数や、テスト回数の表示を提供してくれはするが、どのようなテストケースを考えるか、どのようなテストインスタンスを考えるか、については何も支援してくれない(と思う、間違っているだろうか)。その点、ランダムテストはテストインスタンスについて何も考えなくてもよくなるので(代わりにたくさんのランダムな入力を試してくれるから)、テスト実行時のコスト(心理的なものも含めて)を下げてくれる。

Haskell の QuickCheck はランダムテストを提供するだけでなく、テストケースを一階述語論理(もどき)で書くことを支援する。プログラムの性質を記述するために論理の記号(任意の a について○○ならば××)を使えるのは、見やすく組織的にテストケースを書くためには非常に便利である。そんなわけで、Ruby に移植してみようと思った。さいわい、Ruby には block による手続き渡しと Proc による遅延評価があるから QuickCheck のソースコードそのまま (高階関数や遅延、モナド) をほとんどそのままの形で移植することができた。Haskell の記述のままでは効率が悪いところは、手続的なループに書き直したが。

実際にランダムテストを書いてみると、ランダムテストにはメリットがあるような気がしてきた(なので、作ってよかった)。たとえば malformed format string のせいで最近携帯電話がリコールになったようだ(しかも複数の会社で時期をはずして何回も!)。ランダムテストは malformed format string のような、プログラマの想定外かつ境界値的な入力による問題を見付けるのに適していると思う。

_ sprintf の malformed format string bug とランダムテスト

ためしに次のようなコードを考えよう。これは sprintf の malformed format string bug を含んでおり、書いてはいけない手のコードである。

def hello(name)
  printf("Hello, #{name}\n")
end

printf の第一引数は単なる文字列ではなく、フォーマットを意味する文字列である。フォーマットを指定するの特別な文字(たとえば %s)などが与えられたときは、複数の引数を要求する。この場合だと name に %s が含まれていたら実行時エラー(malformed format string)である。% を含むメールを表示できない携帯電話は printf か sprintf などを間違って使ってしまったのだろうと思う。

テストによって、このようなバグは見付かるのだろうか。プログラマがテストも同時に書いているなら、malformed format string バグを見付けるのは難しいと思う。なぜなら、テストインスタンスに %s を含めるというのは、そもそもテスト実行者が「%s」はあやしい、と考えているからで、そう考えるプログラマはそもそも printf の引数に変数を埋め込むようなことはしないはずだからである。これは間違っていない推論だと思う。このようなバグを埋め込んでしまうプログラマは、printf に関するこの手の危険性について無知だったと思う。

ランダムテストならバグがみつかるだろう、というのはランダムな文字列を何度も試すことにより、「偶然」% の入った文字列も試されるからである。

_ 具体例

次のコードは RushCheck を用いた(意図的な)ランダムテストの例であり、テストを繰り返すことによりバグを見付けている。

  malformed_format_string =
    Assertion.new(String) { |s| sprintf(s); true}
  
  irb> malformed_format_string.check
  Falsifiable, after 86 tests:
  Unexpected exception: #
      ... snip error traces ...
  ["\n&'e!]hr(%&\031Vi\003 }ss"]
  false
  irb>

ここで強調したいのは、for any random String s, "sprintf(s) and true" というテストケースである。ここには、 %s といった「特殊」な入力はなにも書かれていない。にもかかわらず、86 回目のテストで運良く問題が見付かっている。

私が思うに、プログラマがテストコードも同時に書く場合、プログラマが思い付くテストインスタンスはテストに合格するに「決まっている」。というのは、プログラマはテストに時間を使いたくないので、テストインスタンスの設計を慎重にやらないからである。バグはたいてい入力の境界値に潜むものであるが(これは経験的に)、境界値を追求するのは簡単ではない。

みなさんは、テストファーストだ、ユニットテストだ、と言いながら、実際は「テストに通って当り前」のテストしか書かずに済ませてしまっていないだろうか。そのようなテストはバグを見付けることにはあまり貢献せず、「次の実装の見直しにも合格する」ためのベンチマークにすぎなくなってしまっている(しかもこれまた「当り前に合格する」)。その点、QuickCheck によるランダムテストは、見ためが単純かつ明解で、かつ「テストに通るのは必ずしも当り前ではない」ようなテストケースをすばやく書け、しかも一回書くだけで異なったテスト入力が 100 回実行される! これは大変便利である。Haskell だけでなく、 Ruby でもランダムテストが役に立つのではないかな、と思う。私の実装はきわめていい加減で、TestUnit との連携もまだうまくないが、そのうち気に入った誰かが書き直してくれるとありがたい。今月〆切とか出張でごたごたしているので、バトンを引き受けてくれる方をお待ちしています。


出現予定(召喚方法 ikegami@madscientist.jp):

RSS feed を再開しました。RSS の思想を尊重するために全文配信はしません、あしからず。