RushCheckの部分 について、ちょっぴりコメント。
但し、かずひこさんが「ランダムテストした時に、その経緯でバグが出るが、順序を変えると再現できない場合はないか?」と質問したように、ランダムテストで状態が変わってしまうような時は微妙な問題になる。
ランダム入力によるテストという概念は昔からあるもので、 決して新しいものではありません。 どれぐらい古いのかは勉強不足で私は知りませんが、 とにかくずっと昔からあることだけは確かです。
そして、経験的に知られていることとして、この種のテストで重要なのは、再現性に当然絞られます。 もう一回走らせて、ちゃんと直っていることが確認できなければ、 テストの意味がありません。
では再現性をどう確保するべきでしょうか。 Binary Hacksの#84と被ってしまいますが(そう言えば、続きを書いていませんね...)、 Jockey execution record/replay library を利用するいう手があります。 起きたことを記録して、再現可能するツールです。 ただし、プログラムの構造を変化させた時に正しく再現できるかというと、 少々怪しいことになります。 テストよりもデバッグ向きでしょう。
コアを吐かせるという方法も同じ弱点を持っています。 また、コアを吐いた時点で、デバッグが既に困難になっているケースも多々あり(特に、どっかのメモリを誤って書き換えたりした場合)、 デバッグが必ずしも容易になるとは言えません。
私が勝手に「これがいい」と思っている方法は、 決して真の乱数を使わず、疑似乱数を用いて、 シードをちゃんと記録しておくという手段です。 実際、GRUB 2のメモリ管理コードはこの方法でテストしました。 ランダムにmalloc、free、reallocなどを繰り返し、 おかしなことが起きないか確認し続けます。 シードを変えて、何度か同じことを繰り返します。 シードさえ記録しておけば、確実に同じことを再現できます。
もちろん、この方法にも弱点はあります。 まず、こうやって相当テストしたコードでも、 一度だけバグが発見されました。 ですから、ランダム・テストの過信は禁物です。 コーナーケースを、秘孔を突くかの如く、 ピンポイントで狙い撃ちするのが望ましいのです。 しかし、残念ながら、人間はそんなに完璧にはできていないので、 コーナーケースを網羅するのは大変難しいです。 だから、両用する方が一層望ましいと言えるかもしれません。
より技術的に難しいのは、実行時情報に頼っているコードや、 プログラム内部で本物の乱数(/dev/randomとか)を使っている場合です。 例えば、クロック数を勘定していたりすると、 再現は難しくなります。 Jockeyのように、そういう部分をDLLなどで(再現可能なコードに)差し替えてしまうしかないでしょう。 私はGDBでトラップして、関数呼び出しをスクリプト化してしまうのが好きですが、 こういう本質的に再現性に乏しいプログラムをテストすることが困難なことに、 変わりはありません。
ところで、 かずひこさんのプレゼン は非常に面白いですね。 私はこういうことを主張する人が日本にもっと現れてほしいと思ってます。 人間のあらゆる活動は、 本人が意識するしないにかかわらず、 必ず何らかの思想・哲学の影響を受けています。 哲学者でもない限り、そんなことばかり四六時中考えている余裕はないわけですけど、 時には意識してみるべき問題であると思います。
プレゼン前にいろいろアドバイスをもらえて本当に助かりました。ありがとうございます。