PHPテスト失敗の原因を追究する:仕事で使える魔法のLAMP(27)
PHPのソースコードをビルドしてテストしてみたら、いくつかのテストが失敗してしまいました。今回は、テストが失敗した理由を探ってみます(編集部)
テスト失敗の原因を調べる
前回からPHPの具体的なビルド方法を解説しています。手始めにエクステンションをすべて無効にしてビルドしてみました。configureスクリプトの実行とビルドまで問題なく進めることができましたが、その後、「make test」でテストを実行したところ、いくつかのテストが失敗していることが分かりました。
「make test」によるテストは、そのソフトウェアの開発者にとっては、新たにバグを作り込んでいないことを確認する手段となっています。一般向けにソフトウェアをリリースするときは、テストの結果はすべて成功となっているはずです。それなのに失敗になるテストがあるということは、環境による問題があるのか、ビルド時に何らかのトラブルが起きていると考えられます。
テストが失敗したときは、その原因を調査しなければなりません。実際にビルドの方法に問題が見つかることもありますし、環境の違いなどによってたまたま失敗になることもあります。テストが失敗している部分がどれくらい深刻な影響を与えているかということも場合によってまちまちです。深刻でない失敗であれば無視することもできます。
いずれにしても、詳しく調べてみなければこういった判断はできません。というわけで、前回失敗したテストを1つずつ調べてみることにします。
テストファイルの読み方
以下は「make test」を実行した結果、出力されたテキストのうち、失敗したテストを示す部分です。
===================================================================== FAILED TEST SUMMARY --------------------------------------------------------------------- Bug #55156 (ReflectionClass::getDocComment() returns comment even though the class has none) [Zend/tests/bug55156.phpt] DateTime::diff() days -- spring type2 type2 [ext/date/tests/DateTime_days-spring-type2-type2.phpt] DateTime::diff() days -- spring type2 type3 [ext/date/tests/DateTime_days-spring-type2-type3.phpt] DateTime::diff() days -- spring type3 type2 [ext/date/tests/DateTime_days-spring-type3-type2.phpt] DateTime::diff() days -- spring type3 type3 [ext/date/tests/DateTime_days-spring-type3-type3.phpt] DateTime::sub() -- dates [ext/date/tests/DateTime_sub-dates.phpt] DateTime::sub() -- february [ext/date/tests/DateTime_sub-february.phpt] preg_replace() with array of failing regular expressions [ext/pcre/tests/006.phpt] Bug #54971 (Wrong result when using iterator_to_array with use_keys on true) [ext/spl/tests/bug54971.phpt] =====================================================================
失敗となったテスト項目が1行ずつ並んでいます。ファイル名はテストを実行するPHPプログラムです。失敗の詳細を調べるときは、このファイル名がヒントになります。
1つ目の失敗に注目してみると、テストを実行するPHPプログラムのファイル名が「Zend/tests/bug55156.phpt」となっています。これはソースコードを展開したディレクトリからの相対パスによるファイル名です。なお、ZendというのはPHPのスクリプト実行エンジンの名前で、PHPの中核部分ということになります。
「Zend/tests/bug55156.phpt」の内容は次のようになっています。
--TEST-- Bug #55156 (ReflectionClass::getDocComment() returns comment even though the class has none) --FILE-- <?php /** test */ namespace foo { function test() { } $x = new \ReflectionFunction('foo\test'); var_dump($x->getDocComment()); /** test1 */ class bar { } /** test2 */ class foo extends namespace\bar { } $x = new \ReflectionClass('foo\bar'); var_dump($x->getDocComment()); $x = new \ReflectionClass('foo\foo'); var_dump($x->getDocComment()); } ?> --EXPECTF-- bool(false) string(12) "/** test1 */" string(12) "/** test2 */"
いくつかの領域に分かれていて、純粋なPHPプログラムではないことが分かります。このようなテストファイルの1つ1つが1件のテストに該当しています。「make test」を実行すると、テストファイルを順次解析し、テストを実行するようになっているわけです。
このファイルの形式はごく単純です。最初の領域はテストの項目名で、テスト結果一覧に表示されていたものと同じものです。次の領域がPHPによるテストプログラムです。テストプログラムは、テストの趣旨にそったテストロジックを実行し、その結果を出力するようになっています。その次は、テスト結果として期待される出力です。この通りの内容が出力された場合に限り、テストは成功となります。
テストが失敗した場合、テストファイルのファイル名から拡張子を変えたいくつかのファイルが残されます。
$ ls -p Zend/tests/bug55156.* Zend/tests/bug55156.diff Zend/tests/bug55156.out Zend/tests/bug55156.sh Zend/tests/bug55156.exp Zend/tests/bug55156.php Zend/tests/bug55156.log Zend/tests/bug55156.phpt
拡張子.phpと拡張子.expのファイルには、それぞれ先ほどのテストファイル中のプログラムの領域と、期待される出力の領域の内容がそのまま格納されています。拡張子.shのファイルはこのテストを実行するためのシェルスクリプトです。
拡張子.outのファイルは、テストプログラムを実行したときの出力結果です。拡張子.diffは.outと.expとの差分、つまりテスト結果の差異となっています。拡張子.logには、期待する結果と出力結果がまとめられていますので、通常はこのファイルを見ればよいでしょう。「Zend/tests/bug55156.log」は次のような内容になっています。1行目が示す結果が、期待する結果と異なっていることが分かります。
---- EXPECTED OUTPUT bool(false) string(12) "/** test1 */" string(12) "/** test2 */" ---- ACTUAL OUTPUT string(11) "/** test */" string(12) "/** test1 */" string(12) "/** test2 */" ---- FAILED
これで、どのような内容のテストだったのか、具体的な結果がどうだったのかということを調査できるようになりました。次はこの結果で問題ないかということを評価する必要があります。
テスト失敗という結果をどう評価する?
テスト失敗という結果の評価は経験や知識が必要となるため、なかなか難しいものがあります。上辺だけの一般論になってしまいますが、3つの評価ポイントを紹介します。その後、今回の失敗をどのように評価できるかを解説します。
まず、1つ目のポイントです。ビルドをする立場としては、ビルド手順や環境によって起きた失敗かどうかを確認したいところです。依存する外部のソフトウェアやライブラリを正しく使えていないことによる失敗かどうかなどに着目します。また、ビルド時に有効にした機能をきちんと使えていることも重要です。
2つ目は、テストの失敗がインストールしたソフトウェアの利用に悪影響があるのかどうかという点です。ささいな問題であればそのままにしておいてもよいですし、まったく使わない機能であれば、問題自体が大きなものでも無視するという判断ができます。実際の利用者とともに検討するのがよいでしょう。
3つ目は、テストそのものが失敗しているかどうかという点です。テストのための環境初期化に失敗していたり、何らかの制限でテストが実行できないケースが考えられます。原因としては、ビルド時に正しくライブラリをリンクできていない場合や、メモリやネットワークの制限など、さまざまなケースが考えられます。エラーメッセージなどから原因を読み取って、取り除き、再度テストを実行します。
さて、先ほどまで調べてきたテストの失敗例に戻ります。上から見ていくと、「Zend/tests/bug55156.phpt」の内容はソースコードからドキュメントコメントを得るテストです。ドキュメントコメントとは、一定の決まった形式でコメントを置くと、自動でドキュメントに変換できるようなコメントのことです。これはほとんど影響ないので、問題はないと考えられます。
次に6つ並んでいる失敗は、ファイル名から同じエクステンションで発生した失敗であることが分かります。すべて「ext/date/tests/」にあるものだからです。テスト内容を見ると、日付の足し引きといった演算結果が期待と異なっているようです。これをどう評価するかは難しいのですが、ここでは問題なし、としたいと思います。
こう判断する最大の根拠は、この失敗がほかでもたくさん起きているからです。PHPのWebサイトには、どのようなテストでよく失敗が発生しているのかを列記したWebページがあります。このデータはPHPをビルドしている世界中のユーザーが報告してきたものです。アクセスすると、バージョン5.3.8をビルドするときによく失敗しているテストを確認できます(図1)。
リストの上位に、今回の例で問題になっている失敗が並んでいます。普遍的な失敗であるということから、エクステンションかテストそのものに問題がある可能性が高いと考えることができます。そして、1つ目の失敗は第1位にありますので、同じように推測できます。
次の「ext/pcre/tests/006.phpt」も図1のサイトを見ると上位に位置していています。典型的な失敗例ということです。さらにこのテスト名で検索すると、PHP-DEVメーリングリストのやりとりからテストのミスであることが分かります。
最後のテスト「ext/spl/tests/bug54971.phpt」の出力は、次のようになっています。
Fatal error: Class 'DOMDocument' not found in /home/haruhiro/27/php-5.3.8/ext/spl/tests/bug54971.php on line 11
DOMDocumentが存在しないということですが、これは「--disable-all」でエクステンションを無効にしているためです。無効になっているエクステンションに関係するテストは、本来であればスキップするようになっているはずですが、そうなっていないというテストの問題です。よく使われるエクステンションであるがゆえに見つからなかったのだと思われます。これも問題なしと判断できます。
結局、すべてのテスト失敗を見ても、大きな影響はないだろうと判断できます。「PHPはテストが100%成功するわけではないのが普通だから」と結果を無視するというのもいいのですが、詳しく調査することでスキルアップにもつながるはずです。
次回はよく使うエクステンションを有効にしてビルドしてみます。
- CMakeでMySQLをビルドしてみる
- MySQLのビルドに欠かせないCMakeを準備する
- いよいよMySQL編、ソースからビルドすべきか?
- PHPでセッションを利用するための設定
- クライアントがアクセスできる範囲を制限する
- エラーメッセージをどう扱うか?
- ファイルのアップロードを制限する
- リクエストデータを受け取る変数の扱い
- マジッククオート機能には頼らない
- 安全を考えてPHPの実行時設定を調整する
- Apacheの設定ファイルでPHPの設定を変える
- PHPの設定ファイルを作って配置してみる
- PHPスクリプトを実行できるようにする準備
- PHPエクステンション組み込みの仕上げ
- 単純なデータを管理するDBMを使えるようにする
- エクステンションの組み込み状況を確認する
- PHPでデータベースを使う準備をする
- XMLを処理できるようにする
- エクステンションを有効にしてビルドに挑戦!
- PHPテスト失敗の原因を追究する
- 早速PHPをビルド! そしてテスト!
- PHP編に突入! まずはソースをダウンロード
- 設定ファイルを作成してApacheを動作させる
- 設定ファイルや公開ドキュメントの配置を考える
- 1つのサーバに複数の仮想サーバ?
- Apacheの設定ファイルを記述する前に
- サードパーティのApacheモジュールをビルドする
- 認証DBにアクセスするライブラリを組み込む
- Apache同梱ソフトウェアに引数を渡してビルド
- OpenSSLをビルドしてApacheで利用する
- proxyやsslのモジュールを使ってみる
- ライブラリが足りなくてビルドできないときは?
- Apache HTTP Serverのビルドを始めよう
- configureでソフトウェア固有の設定を変更してみる
- configureの設定を変更してみる
- 配布パッケージの中身と、configureの役目を知る
- ダウンロードファイルが真正なものであるかを確認
- Makefileをいろいろ書き換えながらビルドしてみよう
- makeを使ってソフトウェアをビルドしてみよう
- ダイナミックリンクとスタティックリンク
- 「ビルド」という作業は何を指しているのか
- 公開鍵認証でsshを安全に使う
- sshを便利にする公開鍵暗号
- アクセス制限の設定とCentOSのアップデート
- サーバに接続して、一般ユーザーのアカウントを作る
- LAMP環境、自分で作りませんか?
Copyright © ITmedia, Inc. All Rights Reserved.