サブプロセスのタイムアウト

うーん、意図した様に動いてくれません。

昔作った C のプログラムをデバッグしなければならず、検証用に Ruby のテストコードを書いて、そこから呼び出そうとしています。

当初、Ruby/DL を利用して、公開している API を直接呼び出すことを検討していたのですが、DL::PtrData が上手く使えず、ポインタを渡して変更された内容を参照することができなかったため、今回は断念しました。
一応、コマンドベースでも検証できるプログラムなので、API でなくても良いのが救いでした。
それに、良く考えてみると、C プログラム側は、32bit/64bit のそれぞれを提供しているので、それらの両方をテストする場合に DL で API 越しにやろうとすると、Ruby 自体も 32/64 bit の両方を用意しなければなりません (よね?)。そうなるとちょっと面倒なので、コマンドベースに逃げることにしました。

しかし、コマンドベースで検証するためにも実は課題が残っています。
今回のバグでは、副次的な挙動として、コマンドが loop してしまって戻らなくなるという事象も発生しており、そのため、検証時に実行した C プログラムが戻って来ないことがあり得るので、ruby 側で popen して実行した後, select でタイムアウトを設定してやろうと思った訳です。

で、単純に、popen した後で select したのですが、パイプから何も読み込めないときにも select で block されません。

$ irb
irb(main):001:0> p RUBY_VERSION
"1.8.4"
nil
irb(main):002:0> pi = IO.popen("sleep 30 && uname", "r")
#<IO:0x100385a18>
irb(main):003:0> re = IO.select([pi], nil, nil, 5)
[[#<IO:0x100385a18>], [], []]     <--- これがブロックされずにすぐ返ってくる。
irb(main):004:0> p pi.eof?
false
nil
irb(main):005:0> print pi.gets
SunOS                             <--- ここでブロックされる。
nil
irb(main):006:0> pi.close
nil
irb(main):007:0> quit

となって、実際の pipe からの読み取り時にブロックされてしまい、select で待つ意味がないのです。

げ、popen って select で待てないんだっけ? と、C で書いて確認してみたところ、

$ cat wait-proc.c
#ifdef TO_COPILE_AND_USE_THIS_FILE /* Type "sh wait-proc.c"
out=`echo $0|sed 's/\..$//g'`
command="${CC:-cc} -g -xs $0 -o $out && ./$out && rm -f $out; exit"
echo $command
eval $command
*/
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/select.h>

int main(int argc, char *argv[])
{
  char str[PIPE_BUF];
  char * command = "sleep 5 && uname";
  int result = 0;
  FILE * pi;
  fd_set rdfds;

  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 500000;

  printf ("execute command: <%s>\n", command);
  pi = popen (command, "r");
  printf ("waiting."); fflush (stdout);

  while (1) {
    FD_ZERO(&rdfds);
    FD_SET(fileno(pi), &rdfds);

    result = select (fileno(pi) + 1,
                     &rdfds, (fd_set *)NULL, (fd_set *)NULL, &tv);
    if (result < 0)
      perror("select()");
    if (FD_ISSET(fileno(pi), &rdfds))
      break;
    printf ("."); fflush (stdout);
  }

  fread (str, 1, PIPE_BUF, pi);
  fclose (pi);

  printf ("\nresult str: %s\n", str);

  return (EXIT_SUCCESS);
}
/* wait-proc.c ends here. */
$ sh wait-proc.c
cc -g wait-proc.c -o wait-proc && ./wait-proc && rm -f wait-proc; exit
execute command: <sleep 5 && uname>
waiting..........
result str: SunOS

と、手抜きのコードですが、どうやら popen だからといって、select で block されない訳ではなさそうです。そりゃそうですよね……

実は、今回のプラットフォームは SPARC (しかも sparcv9 で 64bit) でして、今回、安定版の 1.8.4 を `-mcpu=v9 -m64' を指定して build しています。それにより、

$ file `type -p ruby`
ruby: ELF 64-ビット MSB 実行可能 SPARCV9 バージョン 1[動的にリンクされています][取り除かれていません]

という具合になってます。
さて、64 bit が問題なのか、そもそも sparc だとダメなのか、安定版の 1.8.4 ではダメなのか……
いいえ、多分、sparcv9 上での 64 bit バイナリの build に失敗している気がします……

気を取り直して、Linux (Debian/Sarge) 上の Ruby で確認してみました。すると、

$ irb
irb(main):001:0> p RUBY_VERSION
"1.8.2"
=> nil
irb(main):002:0> pi = IO.popen("sleep 30 && uname", "r")
=> #<IO:0xb7d69114>
irb(main):003:0> re = IO.select([pi], nil, nil, 5)
=> nil                   <--- ちゃんと select がブロックされ、timeout している。
irb(main):004:0> re = IO.select([pi], nil, nil, 5)
=> nil
irb(main):005:0> re = IO.select([pi], nil, nil, 5)
=> nil
irb(main):006:0> re = IO.select([pi], nil, nil, 5)
=> nil
irb(main):007:0> re = IO.select([pi], nil, nil, 5)
=> [[#<IO:0xb7d69114>], [], []]        <--- ここでブロックが解ける。
irb(main):008:0> p pi.eof?
false
=> nil
irb(main):009:0> print pi.gets
Linux
=> nil
irb(main):010:0> p pi.eof?
true
=> nil
irb(main):011:0> pi.close
=> nil
irb(main):012:0> quit

と、意図した通りの動きをしてくれます。

兎に角、現状の sparcv9 環境だとダメだと言うことは判りました。
しかし、64 bit だからなのか、sparc だとダメなのか、そもそも build に失敗してんじゃないの? といったところまでは切り分けできていません。1.8.4 を `-m32' で build し直すなどの手当てで動くのかもしれませんが、それらを確認している時間はありませんでした。

という訳で最新の安定版 snapshot を 32 bit で build したものに入れ替えてみました。折角なので最新にしてしまおうという腹積もりもあっての選択です。

入れ替えた結果、

$ irb
irb(main):001:0> p RUBY_VERSION
"1.9.0"
=> nil
irb(main):002:0> pi = IO.popen("sleep 5 && uname", "r")
=> #<IO:0x112248>
irb(main):003:0> print "." until IO.select([pi], nil, nil, 1)
....=> nil                       <--- 4回ブロックされている。
irb(main):004:0> p pi.eof?
false
=> nil
irb(main):005:0> print pi.gets
SunOS
=> nil
irb(main):006:0> p pi.eof?
true
=> nil
irb(main):007:0> pi.close
=> nil
irb(main):008:0> quit

と、こちらも意図した通りに動作する様になりました。

こうなると、冒頭に書いた DL::PtrData が上手く使えなかった件も、build し直した今なら何とか行くのかもしれません。時間ができたらやってみようかな。

ともあれ、これでテストに入れます。
しかし、勢い余ってここに書いてしまいましたが、結局、非常に情報量の乏しいエントリとなってしまいました。まあ、いつものことですが。

また、お気付きになった方もいらっしゃるかと思いますが、実は、安定版の snapshot を入れるつもりが、CVS HEAD の snapshot を入れてしまっています。はあ、また入れ直しかなあ……

TAGS を作るコマンドライン

因みに、上のエントリで言語ごとに作成した、配布モジュールなどを入力とした TAGS ファイルを作成するコマンドは、それぞれこの様な感じにしてます。

Perl

$ find `perl -e 'print "@INC";'` -name \*.p[lm] | xargs etags -l perl

Ruby

$ find `ruby -e 'print $:.join(" ")'` -name \*.rb | \
  xargs etags --language=none --regex='/[ \t]*\(def\|class\)[ \t]\([^ \t]+\)/\2/'

Gauche

$ find `gosh -E'map (lambda (x) (display (string-append x " "))) *load-path*' -Eexit` -name \*.scm | \
  xargs etags

といった感じです。
それぞれ etags への言語の指定方法を変えてみました。いえ、特に意味はありません。

Gauche (gosh) の one liner って初めて書いたかも。
最初、`-Eexit' に気付けなかった。

タグファイルの階層化

上のエントリの最後に書きました様に、XEmacs では find-tag の際に、カレントディレクトリからルートディレクトリに向って、TAGS ファイルを探してくれます。そして、見付かった全ての TAGS ファイルの中から tag を探してくれます。
そのため、私の環境では、C 言語向けの TAGS ファイルについて、先ず自分の home ディレクトリに、システムのインクルードファイルを入力にして作成した TAGS ファイルを置いておき、実際に C のコードを書いたディレクトリでは、適宜、そのディレクトリ近辺の *.[ch] を入力にした TAGS が置いてあります。

システムのインクルードファイルについては、カレントディレクトリの TAGS に tag が無くても、カレントディレクトリが自分の home ディレクトリ配下であれば、どこからでも同じ TAGS ファイルを参照してタグジャンプできるという訳です。

さて、この find-tag の際の `buffer-tag-table-list' なんですが、実は、非常に嫌な問題を抱えています。上で紹介している様に、ディレクトリを遡って TAGS ファイルを探してくれるのは良いのですが、探す対象となる `TAGS' というファイル名を、何とハードコードして抱えてしまっているのです。

そうです、そのために Ruby のコードで M-. などしてタグジャンプしようとしたときに、直近の TAGS に存在しないと home ディレクトリに置いてある *C 言語の* TAGS を見て、そこに tag があるとそこに飛んでしまうのです。これがまた結構あるのですよ。

しかし、それを回避する方法もありました。鍵を握るのは `tag-table-alist' という連想リストです。
このリストに、

("\\.rb$" . "~/TAGS.d/ruby")

の様な cons cell を設定しておくことで、該当するファイル名を開いているバッファに於いては、上位ディレクトリを辿るだけではなく、指定されたディレクトリを TAGS ファイルの検索パスとして扱う様になります。

この仕組みを利用して、home ディレクトリ直下に TAGS を置くことを避け、各言語ごとに用意した特定のディレクトリにそれぞれの TAGS を置いておけば、異なる言語の TAGS ファイルを参照してしまうことを避けることができます。
私の場合はこんな感じですね。

(eval-after-load "etags"
  '(progn
     (add-to-list  'tag-table-alist
                   '("\\.rb$" . "~/TAGS.d/ruby"))
     (add-to-list  'tag-table-alist
                   '("\\.[ch]$" . "~/TAGS.d/c"))
     (add-to-list  'tag-table-alist
                   '("\\.scm$" . "~/TAGS.d/gauche"))
     (add-to-list  'tag-table-alist
                   '("\\.p[lm]$" . "~/TAGS.d/perl"))))

ふむ、もっとスマートに設定できないもんですかね。

ともあれ、この様にすることで、各言語ごとに階層化した TAGS ファイルを活用することができています。

しかし、C なんかは良いんですけど、RubyPerl などは、/usr/lib/{ruby,perl} 配下に置いてある様なファイルへの tag があったとしても、適切な tag にジャンプできることは少ないんですよね。find-tag が class や module を意識できないことには、数ある同名のメソッドを順に M-, で探して行かなければなりません。
RubyPerl では、オブジェクトの型を識別するのは無理でしょうから、せめて require や include, use などを見て、バッファ上のコードに関連するファイルを拾うとかでしょうかね。
こいつが解決できたら随分と快適になるとは思うのですが……

今のところは思っているだけです。

タグジャンプ

久し振りに Ruby に触れていて、色々と Web で検索しているうちに、新たな情報を入手しました。
情報自体は随分と古いものなんですが。

■ Emacs 使いの方へ etags

こちらで、Ruby のコードの TAGS ファイルを作成するスクリプトが紹介されていました。
慌てて man を引いて読み直してみたところ、適切な正規表現を指定すれば何にでも対応できるんですね。これは知りませんでした。英語ドキュメントだとちゃんと読まない (読めない) んですよねえ……
;; 開き直りか ;-p

で、早速作ってみました。
実はこれまで、Ruby でもタグジャンプを使いたいと思いながら、etags コマンドが対応してくれないとダメだと思っていたので、migemo にお世話になりながらコードを書いていたもので。

しかし、etags の正規表現は、特に指定していなくとも行頭からのマッチングになる様 (に見える) で、紹介されているパターンでは、`def', `class' のキーワードが行頭にある定義、つまりトップレベルで定義される関数とクラスにしか合致しませんでした。
その方が良いという考えもあろうかと思いますが、私はモジュールや、入れ子のクラス、メソッド、更にモジュール内の各種定義にも合致して欲しいので、

--regex='/[ \t]*\(def\|class\|module\)[ \t]\([^ \t]+\)/\2/'

の様に指定することにしました。

通常、タグファイルはスクリプトがあるディレクトリに作れば良いのですが、異なるディレクトリごとに毎回タグファイルを作らなければなりません。
その度に shell buffer でコマンドを投入するのは面倒なので、

(defvar etags-tag-rebuild-command-ruby
      (concat "find ./ -name \\*.rb | xargs etags --language=none --regex="
              "'/[ \\t]*\\(def\\|class\\|module\\)[ \\t]\\([^ \\t]+\\)/\\2/'")
      "Ruby スクリプト用のタグファイル作成コマンド。")

としてコマンドラインを定義しておき、

(defun etags-tag-rebuild-ruby (command)
  (interactive
   (list (read-shell-command "Etags command: "
                             etags-tag-rebuild-command-ruby))
   (list etags-tag-rebuild-command-ruby))

  (shell-command command))

なんてコマンドを定義してみました。

M-x etags-tag-rebuild-ruby

とすると、ミニバッファに定義した etags のコマンドが表示されるので、そのままで良ければそのまま C-m します。何か変更を加えたい場合には、その場で変更すれば良いだけです。

本当は、cperl-etags の様に、内部でコマンドを生成して call-process とかしようかとも思ったんですが、これはこれ、その場でコマンドを編集できるのも良いですし、何より簡単なのでこうしてます。
実は、C の TAGS もこんな感じで作ってあり、そちらはもう少し手が込んでます。それを流用しようかとも思ったのですが、そちらはもう随分と昔に定義した関数で、それこそ from scratch で直さないとならない程の代物なもので諦めました。

実はちょっとしたポイントもありまして、XEmacs の etags (コマンドではなく Emacs Lisp パッケージの方) は、`find-etag' などを実行した際、`buffer-tag-table-list' という関数の中で、カレントディレクトリからルートディレクトリまで上位に向って遡り、TAGS ファイルを探してくれます。(FSF Emacs だとどうなんでしょうか。ちょっと違った様な気がしますが……)
そのため、どこかのディレクトリで TAGS ファイルを作成するときに、サブディレクトリまで再帰的に *.rb を探して指定しておくと、それらのファイルを開いたバッファで M-x find-tag するときには、上位ディレクトリで作った TAGS ファイルを勝手に利用してタグジャンプしてくれます。

その様な理由もあって、上の `etags-tag-rebuild-command-ruby' では、find コマンドで下位ディレクトリも含めて *.rb なファイルを探している訳です。

やっぱりシンプルに。

上のエントリでも書きましたが、私自身は、テストコードはシンプルであるべき、複雑なテストコードは百害あって一利無し、と思っていますので、実は、上の様なテストコードはなるべく書かない方が良い、と考えています。
場合によってはアリ、とは思っていますけれど。

やっぱり、

$ cat test_iter_test.rb
require 'test/unit'
require 'iter_test'

class TC_DefineReader < Test::Unit::TestCase
  def setup
    # テストデータの配列を定義
    @test_data = [
      "* start line.\n",
      "# comment line.\n",
      "{ header start line.\n",
      "} header end line.\n",
      "/ separater line.\n",
      "[ block start line.\n",
      "] block end line.\n",
      "	 body line.\n",
    ];
    # イテレータに渡すパターンを定義
    @valid_regexp = /^[\{\}\[\]]/;     # valid.
    @invalid_regexp = /^[\{\}\[\]\s]/; # number of actuals more than expects.
  end

  def test_by_collect
    # 配列で一括確認する方法。

    # テスト対象のオブジェクト生成
    obj = Iter_Test.new(@test_data);

    # 期待値を一括照合してテストを実施。
    assert_equal([
      "{ header start line.\n",
      "} header end line.\n",
      "[ block start line.\n",
      "] block end line.\n",
    ],
    obj.get(@valid_regexp) { |l| l});
  end
end
$ ruby test_iter_test.rb
Loaded suite test_iter_test
Started
.
Finished in 0.140404 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

とかで宜しいのではないでしょうか。

但し、イテレータに渡したブロックの中で順次取得できる要素と、イテレータの戻り値となるコレクションとでは内容が異なることがあり得ます。
select, collect, each などが戻り値で返す要素のパターンが異なりますし、そもそもどの様にイテレータを実装するのかに依存しますよね。

それも含めて、より厳密なテストとするならば、ブロック内での要素の参照と、最終的に戻り値として取得されるコレクションの両方を確認すべきなのかもしれません。

Rubyist の皆さんは、どの様にテストされておられるのでしょう。

クロージャにしてみる。

ということで、裸の配列を順に参照している部分を、クロージャにしてみようかと。
そもそも、配列をそのまま参照することが悪い訳では決して無いです。しかし、今回例示している様な簡単なイテレータならばそれでも良いですが、何やら複雑怪奇なメソッドだった場合には、配列をそのまま参照という単純な構造ではなく、手続きを内包するクロージャの様な概念を持ち込むことで解決できることがあるのではないか、という希望的観測に基づく実験的試みです。(遊んでいるだけとも言えるかもしれませんが……)

ただ、本来、私はテストコードはシンプルであるべき、と考えていますので、余り複雑なテストコードにはしたくありません。
それでも書いてみるというのは、やはり単なる遊びなのかもしれません。

さて、上のエントリで書いたテストコードを、そのままクロージャにしてみます。どんどん長くなってしまっているので一気に行きます。

$ cat test_iter_test.rb
require 'test/unit'
require 'iter_test'

class TC_DefineReader < Test::Unit::TestCase
  def setup
    # テストデータの配列を定義
    @test_data = [
      "* start line.\n",
      "# comment line.\n",
      "{ header start line.\n",
      "} header end line.\n",
      "/ separater line.\n",
      "[ block start line.\n",
      "] block end line.\n",
      "	 body line.\n",
    ];
    # イテレータに渡すパターンを定義
    @valid_regexp = /^[\{\}\[\]]/;     # valid.
    @invalid_regexp = /^[\{\}\[\]\s]/; # number of actuals more than expects.
  end

  def test_by_closure
    # クロージャを作ってアクセサとする方法。

    # テスト対象のオブジェクト生成
    obj = Iter_Test.new(@test_data);

    # 期待値を返すクロージャを生成するメソッドを定義。
    # 併せて「返した期待値のインデックス」を返すアクセサも返す。
    expects = [
      "{ header start line.\n",
      "} header end line.\n",
      "[ block start line.\n",
      "] block end line.\n",
    ];
    def make_expect expt
      i = 0;
      e = nil;
      [                         # 多値で返す。
        lambda {
          e = expt[i];
          i += 1;
          e;
        },
        lambda {
          i - 1;
        }
      ]
    end
    get_expect, get_num = make_expect(expects);

    # 期待値を順次取得してテストを実施。
    obj.get(@valid_regexp) { |l| # valid.
      assert_equal(get_expect.call, l,
                   "*** Error index: #{get_num.call}"); # 失敗時には添字を出力。
    };
    # 用意した全ての期待値について検証が行なわれたか。
    assert_nil(get_expect.call, "*** number of expects more than actuals.");
  end
end
$ ruby test_iter_test.rb
Loaded suite test_iter_test
Started
.
Finished in 0.177584 seconds.

1 tests, 5 assertions, 0 failures, 0 errors

となりました。

ミソは、二つのクロージャを生成しているところでしょうか。
先の配列直接参照のテストコードで、assert 失敗時に配列の添字を表示していることと同じことを行なうために、期待値を返すクロージャが直前に返却した期待値の添字を得るためだけのクロージャも生成しています。

配列を直接参照していたコードと比較して、面倒になってるだけな気がします……
まあ、冒頭に書いた様に、もっと複雑なデータ構造をテストする場合に、ひょっとしたら使えるかも……、くらいのものでしかないですかね。

クロージャとか言って、こんなことする位なら、普通にオブジェクト作れば良いだろ、というのは真っ当な意見だと思います。ということで、オブジェクトを生成するバージョンも書いてはあったんですが、もう長くなったので止めにします。クロージャ作るケースと代わり映えしませんしね。

イテレータのテストコード。

久し振りに、仕事絡みで Ruby のコードを書く機会がやってきました。

これまでにも仕事絡みで Ruby のコードを書いたことはあるのですが、要求開発のためのプロトタイプ (要求開発に於けるプロトタイピング) だったり、雑務のツールだったりで、余り厳密にテストコードを書いていませんでした。コード自体も簡単なものばかりです。

今回も簡単なコードではありますが、複数メンバに共通的な位置付けのコードとなるため、多少なりともシビアにテストコードを起こすことにしました。

久し振りということで、新鮮な気持ちで楽しみながらコードを書いていたのですが、ふと手が停まってしまいました。イテレータのテストコードを書こうとして。

はたと気付いたのですが、どうやら私はこれまでの間、自分で書いたイテレータ (ブロック付きメソッドですか) のテストコードを書いたことが無かったのかもしれません。

イテレータは、

obj.get_elem(target) { |elem| some_process (elem); ... };

の様に利用する訳ですから、テストコードも、

obj.get_elem(target) { |elem| assert_equal(EXPECTS_VALUE, elem) };

の様に書ければ、何の問題もないでしょう。
ところが、このイディオムは、テストコードにそのまま適用することはできません。

繰り返し処理で渡される要素の内容は、要素ごとに異なる訳ですから、期待値も同様に複数の値を順次、切り替えてやる必要がありますね。

取り敢えず、単純に実装してみます。

$ cat iter_test.rb
class Iter_Test < Array

  def initialize arg
    @data = arg;
  end

  def get regexp
    @data.select { |line|       # 条件に合致した要素のみ、戻り値に含まれる。
      yield line  if line =~ regexp;
    }
  end
end

# Ends here.

といった感じのクラスを想定してみます。
インスタンス生成時に与えられた配列をコレクションとして保持するクラスで、`get' メソッドは、与えたパターンにマッチする要素だけを返すコレクション操作メソッドです。

一番単純なテストの実装としては、

$ cat test_iter_test.rb
require 'test/unit'
require 'iter_test'

class TC_DefineReader < Test::Unit::TestCase
  def setup
    # テストデータの配列を定義
    @test_data = [
      "* start line.\n",
      "# comment line.\n",
      "{ header start line.\n",
      "} header end line.\n",
      "/ separater line.\n",
      "[ block start line.\n",
      "] block end line.\n",
      "	 body line.\n",
    ];
    # イテレータに渡すパターンを定義
    @valid_regexp = /^[\{\}\[\]]/;     # valid.
    @invalid_regexp = /^[\{\}\[\]\s]/; # number of actuals more than expects.
  end

  def test_by_array
    # 配列を直接参照する方法。

    # テスト対象のオブジェクト生成
    obj = Iter_Test.new(@test_data);

    # 期待値を順次参照してテストを実施。
    idx = 0;
    expects = [
      "{ header start line.\n",
      "} header end line.\n",
      "[ block start line.\n",
      "] block end line.\n",
    ];
    obj.get(@valid_regexp) { |l| # valid.
      assert_equal(expects[idx], l);
      idx += 1;
    };
  end
end

の様なものが考えられます。
イテレータが返す期待値を予め配列に保持しておき、イテレータに渡すブロック内で、順次、配列の内容を参照しながら assert して行きます。

フィクスチャとして、先頭カラムに記号か空白が置かれた行 (改行文字を終端とする文字列) を保持する配列を用意し、先頭カラムの文字によって行を選択する正規表現イテレータに渡しています。ここでは `{', `}', `[', `]' が行頭に置かれた行を要素とするコレクションが返されることになります。

テストコードは、上記に該当する行を期待値として保持する配列を用意し、それらと合致する要素が返されるかを検証するコードになっています。

このコードを実行すると、

$ ./test_iter_test.rb
Loaded suite ./test_iter_test
Started
.
Finished in 0.140324 seconds.

1 tests, 4 assertions, 0 failures, 0 errors

となり、正しい事が確認できました。

異常なケースはどうでしょうか。
テストデータを少し変更して、定義した期待値と異なる要素が返される様にしてみます。

$ head -20 test_iter_test.rb
require 'test/unit'
require 'iter_test'

class TC_DefineReader < Test::Unit::TestCase
  def setup
    # テストデータの配列を定義
    @test_data = [
      "* start line.\n",
      "# comment line.\n",
      "{ header start line.\n",
      # "} header end line.\n",
      "/ separater line.\n",
      "[ block start line.\n",
      "] block end line.\n",
      "	 body line.\n",
    ];
    # イテレータに渡すパターンを定義
    @valid_regexp = /^[\{\}\[\]]/;     # valid.
    @invalid_regexp = /^[\{\}\[\]\s]/; # number of actuals more than expects.
  end

本来であれば二つ目の要素として返されるデータをコメントアウトして実行してみます。

$ ruby test_iter_test.rb
Loaded suite test_iter_test
Started
F
Finished in 0.189173 seconds.

  1) Failure:
test_by_array(TC_DefineReader)
    [test_iter_test.rb:54:in `test_by_array'
     test_iter_test.rb:53:in `get'
     ./iter_test.rb:13:in `select'
     ./iter_test.rb:13:in `get'
     test_iter_test.rb:53:in `test_by_array']:
<"} header end line.\n"> expected but was
<"[ block start line.\n">.

1 tests, 2 assertions, 1 failures, 0 errors

異常が検出されていますね。しかし、この程度のデータ量でのテストなら異常となったデータを簡単に判別できますが、データ量が非常に多かったり、内容が判別し難いデータ (中身がとても似ている様な) だった場合には、余り親切とは言えないかもしれませんね。
そこで、どの要素が failure したのかをもう少し判り易くしてみます。
最初に紹介したテストコード中の、イテレータにブロックを渡して assert している、

    obj.get(@valid_regexp) { |l| # valid.
      assert_equal(expects[idx], l);
      idx += 1;
    };

というブロックを、

    obj.get(@valid_regexp) { |l| # valid.
      assert_equal(expects[idx], l,
                   "*** Error index: #{idx}"); # 失敗時には添字を出力。
      idx += 1;
    };

と書き換えてみると、

$ ruby test_iter_test.rb
Loaded suite test_iter_test
Started
F
Finished in 0.14927 seconds.

  1) Failure:
test_by_array(TC_DefineReader)
    [test_iter_test.rb:54:in `test_by_array'
     test_iter_test.rb:53:in `get'
     ./iter_test.rb:13:in `select'
     ./iter_test.rb:13:in `get'
     test_iter_test.rb:53:in `test_by_array']:
*** Error index: 1.
<"} header end line.\n"> expected but was
<"[ block start line.\n">.

1 tests, 2 assertions, 1 failures, 0 errors

となり、少し判り易くなりました。(いや、単に添字を表示する様にしただけですが、それでも判別し難いデータだった場合には判り易くなったと言えると思います……)

さて、もう一つ異常なパターンを。
今度は、要素数が正しく無いケースです。この様なテストでは、期待値として定義した配列の要素数と、実際にイテレータが返却するコレクションの要素数は等しくなければなりません。
但し、期待値の要素数が少ない場合は、実際に返された要素と nil とを比較することになるので、今のコードで十分判別可能です。しかし、

$ head -20 test_iter_test.rb
require 'test/unit'
require 'iter_test'

class TC_DefineReader < Test::Unit::TestCase
  def setup
    # テストデータの配列を定義
    @test_data = [
      "* start line.\n",
      "# comment line.\n",
      "{ header start line.\n",
      "} header end line.\n",
      "/ separater line.\n",
      "[ block start line.\n",
      # "] block end line.\n",
      "	 body line.\n",
    ];
    # イテレータに渡すパターンを定義
    @valid_regexp = /^[\{\}\[\]]/;     # valid.
    @invalid_regexp = /^[\{\}\[\]\s]/; # number of actuals more than expects.
  end

の様にして (イテレータが最後に返却する筈のデータをコメントアウトした)、イテレータが返却する要素数の方が少なくなる場合には、

$ ruby test_iter_test.rb
Loaded suite test_iter_test
Started
.
Finished in 0.175241 seconds.

1 tests, 3 assertions, 0 failures, 0 errors

と、正常にテストが終了してしまうことになります。
これでは困りますので、

    obj.get(@valid_regexp) { |l| # valid.
      assert_equal(expects[idx], l,
                   "*** Error index: #{idx}"); # 失敗時には添字を出力。
      idx += 1;
    };
    # 用意した全ての期待値について検証が行なわれたか。
    assert_equal(expects.size, idx, "*** number of expects more than actuals.");

と、各要素を順に確認するブロックを終えた後に、期待値として定義したデータを全て検証したかの確認を入れておきます。
すると、

$ ruby test_iter_test.rb
Loaded suite test_iter_test
Started
F
Finished in 0.190942 seconds.

  1) Failure:
test_by_array(TC_DefineReader) [test_iter_test.rb:59]:
*** number of expects more than actuals.
<4> expected but was
<3>.

1 tests, 4 assertions, 1 failures, 0 errors

期待した要素数と異なることがレポートされるので一安心ですね。

と、ここまでは配列を定義して、その要素を順に検証して行く方法を採ってみました。
しかし、配列を裸で使うのも芸が無い気がします。(いや、そもそも私は芸なんざ持ってないですけども)
もう少しスマートな方法は無いでしょうか。

インタプリタを模倣。

上のエントリで、

私は、結構、確認のためだけのコードを書くことが多いので、Makefile など用意せずに compile することが多いです。そのため、make コマンドだけでは物足りません。

などと書いていますが、実は、確認のためだけのコードの場合は、

#ifdef COMPILES_AND_PERFORMS_THIS_FILE
### Type "sh FILENAME"
out=`echo $0|sed 's/.[^.]*$//g'`
CC="gcc -g -Wall"
${CC:-cc} $0 -o $out && ./$out && rm -f $out
exit
#endif

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char * argv[]) {
  printf ("%s executed.\n", argv[0]);
  exit (EXIT_SUCCESS);
}

の様に書いていて、

$ sh test.c
./test executed.

などとすることが多いです。昔、fj.comp.lang.c 辺りで知ったノウハウです。
プリプロセッサディレクティブの先頭文字と、sh script のコメント書式に同じ `#' が使われていることを利用したトリックですね。

あくまで、コンパイルと実行を 1アクションで行なうと言うだけの Tips で、インタプリタ並みの生産性になるとかということではありませんが。

それでも色々と工夫することはできて、例えば、

$ cat test.c
#ifdef COMPILES_AND_PERFORMS_THIS_FILE
### Type "sh FILENAME"
out=`echo $0|sed 's/.[^.]*$//g'`
debugger=$*
CC="gcc -g -Wall"
${CC:-cc} $0 -o $out && $debugger ./$out && rm -f $out
if [ -f core ]; then
	cat > .gdbinit.tmp <<EOD
echo backtrace:\n
bt
EOD
	gdb -silent -command .gdbinit.tmp ./$out -core core;
	rm .gdbinit.tmp
fi
exit
#endif

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char * argv[]) {
  printf ("%s executed.\n", argv[0]);
	abort();
  exit (EXIT_SUCCESS);
}

などとしておくと、

$ sh test.c gdb -silent
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x80483d4: file test.c, line 23.
(gdb) r
Starting program: /tmp/test 

Breakpoint 1, main (argc=1, argv=0xbffffb14) at test.c:23
23	  printf ("%s executed.\n", argv[0]);
(gdb) p argv[0]
$1 = 0xbffffc42 "/tmp/test"
(gdb) q
The program is running.  Exit anyway? (y or n) y

として、コンパイルした上でデバッガ配下で実行したり、

$ sh test.c
./test executed.
test.c: line 6:  6981 アボートしました        (core dumped) $debugger ./$out
Using host libthread_db library "/lib/tls/libthread_db.so.1".
Core was generated by `./test'.
Program terminated with signal 6, Aborted.

warning: current_sos: Can't read pathname for load map: 入力/出力エラーです

Reading symbols from /lib/tls/libc.so.6...done.
Loaded symbols for /lib/tls/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0  0xb7ecc83b in raise () from /lib/tls/libc.so.6
backtrace:
#0  0xb7ecc83b in raise () from /lib/tls/libc.so.6
#1  0xb7ecdfa2 in abort () from /lib/tls/libc.so.6
#2  0x080483ee in main (argc=1, argv=0xbffffb74) at test.c:24
(gdb)

と、core を吐いたらそれを gdb に喰わせてすぐさまデバッグに入れるとか。
まあ、意味あんの? という感じではありますが、やろうと思えば色々できますというところでしょうか。

C 言語のコンパイル

久し振りの更新となってしまいました。

id:higepon さんのところで、


開発時にM-x compileを常用しているのですが以下2点が不満でした。

という話が。
;; どうも higepon さんのエントリに反応してしまうことが多い気がします……

session.el の方はコメントが既にあり、不満も解消されている様なので、compile command について少しだけ。
こちらも higepon さんご自身により既に対処されていて、不満は解消されているものとは思いますが、どうせならば、単に compile-command 等を変更するだけでなく、もう少し工夫しておくと更に便利かも、ということで。

先ず一点目は、command の切り替え。
私は、結構、確認のためだけのコードを書くことが多いので、Makefile など用意せずに compile することが多いです。そのため、make コマンドだけでは物足りません。
また、Cygwin, Linux, Solaris といった環境によって compile command の内容に相違があるため、それらも使い分けられると良いということで、こんな具合にコマンドを生成しています。

(defun set-compile-command-for-c ()
  (interactive)
  (let* ((filename (file-name-nondirectory buffer-file-name))
         (index (string-match "\\.c$" filename))
         (filename-no-suffix (substring filename 0 index)))
    (setq compile-command-local
          (cond
           ;; Cygwin or Linux
           ((string-match "cygwin\\|linux" system-configuration)
            "gcc -g -ansi -pedantic -Wall -I../inc ")
           ;; Solaris
           ((string-match "solaris" system-configuration)
            "cc -Xa -xtransition -g -xs -I../inc ")))

    (setq compile-command
          (cond
           ;; Cygwin 用の Makefile があればそれを指定。
           ((or (file-exists-p "Makefile.cygwin"))
            "make -k -f Makefile.cygwin")
           ;; Makefile があれば "make -k"
           ((or (file-exists-p "Makefile")
                (file-exists-p "makefile"))
            "make -k")
           ;; それ以外
           (t
            (concat compile-command-local "-o "
                    filename-no-suffix " " filename))))))

プラットフォームごとに compile-command-local を設定した上で、そのバッファに表示されているソースに適合する compile-command を組み立てています。
Makefile があれば make コマンドを、無ければ直接 gcc で、というだけですけど。
;; コマンドのオプションはお好みで。

そして、もう一点、ここで生成された gcc のコマンドはファイル名まで指定しているので、当然の如くそのバッファでしか有効ではありません。
なので、compile-command(-local) 変数は、バッファローカルでなければなりません。

(add-hook 'c-mode-common-hook
 '(lambda ()
    ;; コンパイルコマンドの設定
    (make-local-variable 'compile-command)
    (make-local-variable 'compile-command-local)
    (set-compile-command-for-c)))

これでソースを開いているバッファごとに、その状況に応じたコンパイルが行なえる様になりますので、結構便利です。
;; 実は compile-command-local の方は local variable にしなくても良さそうですが。

ここでは判断した結果としてコマンドを一つだけ決定している訳ですが、higepon さんの様に、compile-history に複数設定しておく、というのは良いアイディアですね。
私も使わせて頂こうかと思います。

クロージャとブロック (追記)

今日になって、昨日の一連のエントリを読み直してみましたが、細かい話ですが注釈を入れさせて貰った方が良さそうな部分を発見してしまいました。
それなりに推敲しているつもりですが、私が書く場合は、やはり少し寝かせてからでないとダメですね。

昨日のエントリで、Scheme と対比させるコードとして、クロージャを持たない言語である Emacs Lisp を使いましたが、その理由として動的スコープ、有限エクステントを挙げています。
しかし、単に動的スコープというだけでなく、

  • 動的スコープで、且つ環境を特定できる手段を持たないから。

としておかないとなりませんでした。
以前のエントリにも書きましたが、動的スコープであっても、特定の環境にのみ結び付けられる仕組みさえあれば良い筈だからです。shiro さんからご紹介頂いた The Function of FUNCTION in LISP or Why the FUNARG Problem Should be Called the Environment Problem (邦訳) でも、動的スコープの場合に function 特殊形式を利用して環境へのポインタを保持する手法について述べられています。

因みに、Emacs Lispクロージャが無いというのは、Emacs Lisp の Info (Node: Extent) にも、

Lispの方言のいくつかには『クロージャ』(closure)があります。それは関数のようなオブジェクトですが、追加の変数束縛を記録します。Emacs Lispにはクロージャはありません。

と、明確に書いてありますが、それじゃ仕方無いのかと言うと、Emacs Lisp 使い達が黙っている筈がありません。
昨日のエントリのコードですと、

;; cl を導入すれば、定義時に束縛された y が利用される。
(let (a1 a2 (y 5) f)
  (eval-when-compile (require 'cl))
  (setq a1 '(1 2 3))
  (setq a2 '(4 5))
  (setq f
    (lexical-let ((y 2))
      (lambda (x) (* x (setq y (+ y 1))))))
  (let ()
    (cons
     (mapcar f a1)
     (mapcar f a2))))
=> ((3 8 15) 24 35)

という具合に、Common Lisp のエミュレートを行なう cl パッケージを利用することでクロージャとして動作させることができます。
ご存じの方が多いでしょうが、そもそも Emacs Lisp を使わない方も多いと思いますので、一応、紹介させて頂きました。