追記: 1.30でsleepとretryのデフォルトが変更になりました。詳しくはドキュメントを参考にしてください
追記2: 2.00でsleepとretryがなくなりました。代わりにmax_waitが追加されました。詳しくはドキュメントを参考にしてください。blogも書いた
TCPサーバ・クライアントのテストには欠かせない、みんな大好きtokuhiromのみんな大好きTest::TCP。Plackのサーバのテストなら
use Plack::Loader;
use Test::TCP;
test_tcp(
    server => sub {
        my $loader = Plack::Loader->load(
            'Monoceros',
            port => $port,
            max_workers => 5
        );
        $loader->run($app);
        exit;
    },
    client => sub {
        my $port = shift;
        my $ua = LWP::UserAgent->new;
        my $res = $ua->get("http://localhost:$port/");
        ok($res->is_success)
    }
);
このように書くだけで、空いているTCPポートを探して、fork()した上でserver、clientのコードを実行してくれます。便利!!
内部的には
1. 空いているポートを探す
2. fork()
3. [子プロセス] serverのコードを実行
4. [親プロセス] 見つけたポートがconnectできるようになるまで待つ
5. [親プロセス] clientのコードを実行
6. [親プロセス] 子プロセスにTERMを送って、終了まで待つ
という順になってます。
ところが、最近Test::TCPを使ったテストが手元やtravis-ciでは問題ないのにcpantestersでコケるということが多くなっていて、そのほとんどが「4」でtimeoutしていました。なんでかなぁと思ってTest::TCPを見ていると1.27でポートに接続できるようになるまで待つ時間が短くなっていたのに気付きました。
https://metacpan.org/diff/release/TOKUHIROM/Test-TCP-1.26/TOKUHIROM/Test-TCP-1.27
0.1秒sleepの100回、10秒だったのが0.001秒sleepの100回retryの0.1秒になってます。テストを速く終わらせるためかなと思いますが、手元では0.1秒以内でサーバが起動しても、cpantestersではかなり重い・遅い環境もあるので、0.1秒ではforkとサーバの起動が間に合わなくなってしまっているのでしょう。
ということでとりあえず、MonocerosではTest::TCPのwait_portを上書きしてみました。
{
    no warnings 'redefine';
    *Test::TCP::wait_port = sub {
        my $port = shift;
        Net::EmptyPort::wait_port($port, 0.1, 40) 
            or die "cannot open port: $port";
    };
}
このようにしたところ、Monocerosのテストが失敗することはなくなりました。
この件をtokuhirom氏に報告したところ、オプションでsleep時間とretry回数を指定できるようにアップデートしてくれました。
use Test::TCP;
test_tcp(
    server => sub { .. },
    client => sub { .. },
    wait_port_retry => 40,
    wait_port_sleep => 0.1
);
これで安心ですね。
あとは、Test::TCPを使っているPlack::Test::Serverでも同じ問題起きるので、どうにかしたいと思う五月末
 
 