2004年11月18日

PHPUnitの使い方

PHPUnitは、オブジェクトのテストにだけ使えるのかと思っていましたが、単純な関数の単体テストでも有効なんですね。(ドキュメントのサンプルは、関数のテストが載ってますが)

とういわけで、PHPUnitの単純な使い方を紹介します。

PHPUnitの使い方を参考にしました。(というかそのまんまです。すみません。というか、ありがとうございます。)


■1. 何をテストするか?
スクリプトにアクセスしたユーザのログをファイルに記録する関数をテストすることにします。

bool loginLogWriter(string user)

userを引数にとり、ファイルに、user、アクセス日時を書き込みます。
user、アクセス日時は、1回のアクセスに付き、1行をファイルの最後にタブ区切りで追記します。
書き込みが正常に終了した場合に、trueを返します。失敗した場合は、falseを返します。


何をテストするかを考えていくときりがありませんが、
タブ区切り、ファイル書込ということで、次の項目をテストすることにします。


  1. 引数で渡されたuserが正しく記録されていること。

  2. userにタブが含まれていても正しく記録されること。

  3. 書き込むファイルが存在しない場合にfalseを返すこと。

■2. テストのスケルトンの作成

まず、始めに(テストする関数を作る前に)、スケルトンを作ります。

test.php

<?php
require_once 'PHPUnit/GUI/HTML.php';
// Test Suites
require_once 'test/loginLogWriterTest.php'; //←テストが書かれたスクリプト

// run Test Suites
$suite = new PHPUnit_TestSuite("loginLogWriterTest"); //←テストするクラス名(1)
$result = new PHPUnit_GUI_HTML($suite);
$result->show();
?>


test/loginLogWriterTest.php

<?php
require_once 'PHPUnit.php';
require 'loginLogWriter.php'; //←テストするスクリプトをインクルード

class loginLogWriterTest extends PHPUnit_TestCase { //←(1)のテストするクラス名
    function loginLogWriterTest($name) //←コンストラクタ
    {
        $this->PHPUnit_TestCase($name);
    }

    function setUp()
    {
    }

    function tearDown()
    {
    }

    function testSetup() //←testで始まるメソッドがテストとして実行されます。
    {
        $this->fail("no implementation"); //←assertのメソッドについては、PHPUnit_Assertを参照
    }
}
?>

loginLogWriter.php

<?php
?>

とりあえずファイルだけ作ります。

ここまでで、とりあえず、test.phpを実行してみます。
(un)check all にチェックをつけて、run testsをクリックします。

failメソッドで、強制的に失敗させていますので、失敗していれば(橙色になっていれば)成功です。


■3. テストの作成
関数を作成する前に、まず、テストを作成します。
「1. 引数で渡されたuserが正しく記録されていること。」から作ります。


test/loginLogWriterTest.php

<?php
require_once 'PHPUnit.php';
require 'loginLogWriter.php';

class loginLogWriterTest extends PHPUnit_TestCase {
    var $logFile;
    function loginLogWriterTest($name)
    {
        $this->PHPUnit_TestCase($name);
    }

    function setUp() //←テストの前に呼び出されます
    {
        $this->logFile = "/tmp/test_log.log";
        if ( !file_exists($this->logFile) ) {
            touch($this->logFile); //←ログを書き出すファイルを生成しています
        }
    }

    function tearDown() //←テストの終了後に呼び出されます
    {
        unlink($this->logFile); //←生成したファイルを削除しています
    }

    function testWriteLog() //←testで始まるメソッドがテストとなります
    {
        $user = "hoge";
        $regexp = "|^".date("Y/m/d")."\t".date("H:i:").".*".$user."\t\n$|";
        loginLogWriter($user);
        $out = file($this->logFile); //←ファイルの内容を配列に取り出します
        $this->assertRegExp($regexp, $out[count($out)-1]); //←正規表現を使って出力結果をチェックします
    }
}
?>

ここまでで、テストを実行します。

まだ、loginLogWriter()を作成していないので、エラーになります。


■4. 関数の作成

まず、関数の定義だけを記述します。

loginLogWriter.php

<?php
function loginLogWriter($user){
}
?>

テストを実行します。

まだ、処理を書いていないので、エラーになりました。
テストスクリプトのエラーも出ていますが、気にしないでおきましょう。

さて、いよいよ、実際の処理を作ります。

loginLogWriter.php

<?php
function loginLogWriter($user){
    $logFile = "/tmp/test_log.log";
    $logData = array(date("Y/m/d"), date("H:i:s"), $user, "\n");
    $logData = implode("\t", $logData); //←区切りはタブとします
    $fp = fopen($logFile,"a");
    flock($fp,2);
    fwrite($fp, $logData);
    fclose($fp);
}
?>

テストを実行します。


■5. 次のテストです。
「2. userにタブが含まれていても正しく記録されること。」

同様に、テストを作ります。

test/loginLogWriterTest.php

<?php
require_once 'PHPUnit.php';
require 'loginLogWriter.php';

class loginLogWriterTest extends PHPUnit_TestCase {
    var $logFile;
    function loginLogWriterTest($name)
    {
        $this->PHPUnit_TestCase($name);
    }

    function setUp()
    {
        $this->logFile = "/tmp/test_log.log";
        if ( !file_exists($this->logFile) ) {
            touch($this->logFile);
        }
    }

    function tearDown()
    {
        unlink($this->logFile);
    }

    function testWriteLog()
    {
        $user = "hoge";
        $regexp = "|^".date("Y/m/d")."\t".date("H:i:").".*".$user."\t\n$|";
        loginLogWriter($user);
        $out = file($this->logFile);
        $this->assertRegExp($regexp, $out[count($out)-1]);
    }

    function testReplaceTab() //←追加したテストです
    {
        $user = "hoge\thoge";
        $repUser = str_replace("\t", "  ", $user); //←タブを空白2個に置換します
        $regexp = "|^".date("Y/m/d")."\t".date("H:i:").".*".$repUser."\t\n$|";
        loginLogWriter($user);
        $out = file($this->logFile);
        $this->assertRegExp($regexp, $out[count($out)-1]);
    }
}
?>

テストを実行します。

エラーになります。


次に、関数の処理を修正します。

loginLogWriter.php

<?php
function loginLogWriter($user){
    $logFile = "/tmp/test_log.log";
    $user = str_replace("\t", "  ", $user); //←追加しました
    $logData = array(date("Y/m/d"), date("H:i:s"), $user, "\n");
    $logData = implode("\t", $logData);
    $fp = fopen($logFile,"a");
    flock($fp,2);
    fwrite($fp, $logData);
    fclose($fp);
}
?>

テストします。

緑色のバーになれば、テスト完了です。

■6. 最後のテストです
「3. 書き込むファイルが存在しない場合にfalseを返すこと。」

同じ手順です。
テストを作ります。

test/loginLogWriterTest.php

<?php
require_once 'PHPUnit.php';
require 'loginLogWriter.php';

class loginLogWriterTest extends PHPUnit_TestCase {
    var $logFile;
    function loginLogWriterTest($name)
    {
        $this->PHPUnit_TestCase($name);
    }

    function setUp()
    {
        $this->logFile = "/tmp/test_log.log";
        if ( !file_exists($this->logFile) ) {
            touch($this->logFile);
        }
    }

    function tearDown()
    {
        unlink($this->logFile);
    }

    function testWriteLog()
    {
        $user = "hoge";
        $regexp = "|^".date("Y/m/d")."\t".date("H:i:").".*".$user."\t\n$|";
        loginLogWriter($user);
        $out = file($this->logFile);
        $this->assertRegExp($regexp, $out[count($out)-1]);
    }

    function testReplaceTab()
    {
        $user = "hoge\thoge";
        $repUser = str_replace("\t", " ", $user);
        $regexp = "|^".date("Y/m/d")."\t".date("H:i:").".*".$repUser."\t\n$|";
        loginLogWriter($user);
        $out = file($this->logFile);
        $this->assertRegExp($regexp, $out[count($out)-1]);
    }

    function testFileExists() //←追加しました
    {
        unlink($this->logFile);
        $user = "hoge";
        $this->assertFalse(loginLogWriter($user));
    }
}
?>

テストを実行します。

テストが失敗することを期待したのですが、緑色のバーになり成功しました。
確認のために、関数に戻り値trueを入れてみます。

loginLogWriter.php

<?php
function loginLogWriter($user){
    $logFile = "/tmp/test_log.log";
    $user = str_replace("\t", "  ", $user);
    $logData = array(date("Y/m/d"), date("H:i:s"), $user, "\n");
    $logData = implode("\t", $logData);
    $fp = fopen($logFile,"a");
    flock($fp,2);
    fwrite($fp, $logData);
    fclose($fp);
    return true; //←追加しました
}

テストします。

橙色になり、失敗しました。関数は、正常に実行されているようです。
ログを書き込むファイルを削除したはずなのに、と考えていると思い出しました。
fopenは、オープンするファイルがない場合には、ファイルを作成します。

テストが失敗の状態ですので、関数の処理を修正して、テストが成功することを確認します。

loginLogWriter.php

<?php
function loginLogWriter($user){
    $logFile = "/tmp/test_log.log";
    if ( file_exists($logFile) ) {
        $user = str_replace("\t", "  ", $user);
        $logData = array(date("Y/m/d"), date("H:i:s"), $user, "\n");
        $logData = implode("\t", $logData);
        $fp = fopen($logFile,"a");
        flock($fp,2);
        fwrite($fp, $logData);
        fclose($fp);
        return true;
    }
    else {
        return false;
    }
?>

テストを実行します。今度は成功しました。


このように、テストの作成→テストの失敗の確認→処理の作成→テストの成功の確認 を何度も繰り返して、プログラムを作成していけばいいのではないでしょうか。

Posted by hosco at 22:44

体内に埋め込むRFIDチップ

VeriChipという米粒ほどのRFIDチップを、肘や右肩の皮下に埋め込むらしい。
何に使うんでしょうか?

APPLIED DIGITAL’S VERICHIP CORPORATION SIGNS MEDICAL DISTRIBUTION AGREEMENT WITH HENRY SCHEIN

Posted by hosco at 20:40

2004年11月17日

世界最大の写真

Largest Digital Photograph in the World
Slashdot

コンピュータ制御されたカメラで撮影した600枚の画像をつなぎ合わせた、25億万画素、7.5Gbyteのデジタル写真。

Slashdotの書き込みでも、被写体はけちょんけちょんに言われているが、その写真を見るFlashは、気になります。
http://www.tpd.tno.nl/Pics/DII/gigazoom/Delft2.htm


あと、書き込みで紹介されているこの人たち、すごいですね。
Max Lyons
Degital Image Gallery
Breaking the Gigapixel Barrier
photographer? programer?


Clifford Ross
news from AMANDTECH
R1 Project
カメラ作っちゃうなんて

Posted by hosco at 09:21

2004年11月07日

v0.8.9 紹介 [Cabi]

●フォトキャビ名、アルバム名に、これまでより長い文字列を入力できるようにしました
●著作権表示に、creative commonsを採用しました
●更新状況を反映するRSSを実装しました

PhotoCabiオーナーのみなさん、こんばんは。
管理人のCabiです。
PhotoCabiのバージョンアップを行ないました。
今回のv0.8.9で追加した機能と、これまでとの変更点をお知らせします。
ログインすると、新しい機能が使用できます。

---------------------------------------------------------
●フォトキャビ名、アルバム名の文字列制限の拡張
---------------------------------------------------------
これまで、フォトキャビ名、アルバム名に長い文字列を入力すると、
途中で切れてしまうことがありました。
今回、長い文字列にも対応できるように改良しました。

これまで、入力した文字列が途中までしか表示できなかった方は、
再度フォトキャビ名またはアルバム名を設定してみてください。

でも、長さには限度があります。
いろいろと試してみてください。

※Macintosh(OS9)でInternetExplorerをお使いの方へ
MacでInternet Explorerをお使いの場合、画像のキャッシュの問題で、
フォトキャビ名やアルバム名を更新しても、ロゴ画像が
更新されていないように見える場合があります。

この場合は、ロゴの上でマウスボタンを押しっぱなしにしてみてください。
しばらくすると矢印の横にメニューが表示されるはずです。
そのメニューの一番下にある「画像の再読み込み」を選んでみてください。

変更されたフォトキャビ名やアルバム名にロゴが変わるはずです。

---------------------------------------------------------
●著作権表示の選択(クリエイティブ・コモンズ)
---------------------------------------------------------
これまでは、PhotoCabiを作成すると、All Right Reserved(全権所有)を
自動的に表示していましたが、今回、creative commonsのライセンスを
取得するフローを取り込み、6種類の権利表示の中から、
あなたが希望する著作権を表示できるようにしました。

あなたのPhotoCabiの写真やテキストは、もしかするとあなたの意思に反して
二次利用されてしまうかもしれません。インターネット上に著作物を置くという便利さと、
不本意な二次利用という著作権侵害は、避けられません。

creative commonsは、あなたの著作物に6種類の著作権を設定し、
それを第三者機関として認定し、著作権の表示を支援しようとする団体です。
あなたのPhotoCabiにログインすると、TOP編集画面のメニューに
「著作権設定」というリンクが新しく表示されています。
それをクリックして、creative commonsを理解の上、利用してください。

設定したcreative commonsは、
あなたのPhotoCabiに登録されている写真およびコメントに適用されます。
また、今後登録される写真およびコメントには、
選択されたライセンスが自動的に適用されます。
クリエイティブ・コモンズのライセンスを選択しない場合は、
今まで通り、全ての著作権を保持します。
クリエイティブ・コモンズについて、よくわからないという方は、
ライセンスを選択しないで下さい。
(ライセンスは、後から選択できます。)

creative commonsを設定した場合、
PhotoCabiが提供する画面の下(フッタ)に表示する文字列が
SOME RIGHTS RESERVEDに変わります。
その文字列は、あなたが選択したcreative commonsの著作権を表示するページへの
リンクとなっています。
また、あなたのPhotoCabiのTOPページには、
creative commonsにライセンスされていることを示すロゴが表示されます。

---------------------------------------------------------
●RSSの実装
---------------------------------------------------------
RSS (RDF Site Summary) は、 XML を利用したコンテンツ配信のためのファイル形式です 。
PhotoCabiでは、今回のv0.8.9より、自動的にRSSを生成するようにしました。

あなたがPhotoCabiを更新したら、RSSも自動的に更新されます。
あなたのPhotoCabiの更新を楽しみにしている人は、RSSアグリゲータを利用して、
更新情報を知ることができます。

以上がv0.8.9の機能概要です。
では、PhotoCabi.netをお楽しみ下さい。

Posted by shed at 23:59 | Comments (0)

2004年11月03日

文化の日だし

文化の日だというのに、ずっと家にいたので、何か少しは文化的なことをしなくては!と思って
先日買っておいた映画のDVDを観ることにしました。
(映画を観ることは私の中では文化的なことなのである)

『チルソクの夏』という邦画です。
ph_01.jpg

この映画、私の高校の先輩が監督をしていて、舞台も私の故郷の下関(山口県)ということで、
前から気になっていたのでした。
(『半落ち』の佐々部 清 監督)

チルソクの夏 公式サイト

しかも、私の出身高校や、隣の女子高も実名で登場します。
これは観なくては。

できれば下関で観たかったのですが、何度か帰省したタイミングと合わず、
今日まで観ていなかったのでした。

そのDVDが10月末に発売になると知って、先日買っておいたのだ。

NHKの『てるてる家族』で注目していた上野樹里も出ているし、さてじっくり観るとしますか。

これはどこだ? ああ、あの商店街ね、長府高じゃん!、これは火の山からの眺めだな、豊浦高校映らないかなあ
お、関門トンネル歩いている、乃木神社だあ、みもすそ川だね など、
つい場所を確認することが主になってしまう。

下関ってこんなに絵になるんだなあ、と別の感想もあったり。

そうそう、下関からは「関釜フェリー」という、釜山に行けるフェリーが出ています。
下関では「関釜」って呼ぶけど、韓国では「釜関」になるんだなってことに、あるシーンで気づいた。

うーん、純粋な鑑賞記録にはならないようです。

2回目はストーリーを重視することにしよう。

好きなタイプの邦画でよかった。

Posted by shed at 23:17

2004年11月02日

たまほっち使用報告

買ってきました、たまほっち。
文房具店では、専用のPOPで華々しく売られていました。

使用報告の視点としては
・ユニバーサルデザインとしての使いやすさはどうか
・机の上にあると嬉しくなるか
である。が・・・

いやあ、これはちょっと。
ちょっとというよりは「コラッ」という感じですね。

まず、思った場所にうまくステイプルできません。
私の使い方が悪いのだとしても、ユニバーサルデザインを謳っているのだから、
もう少しステイプラーとしての基本機能はしっかり作っておかなくては
いけないのではないか。

ステイプルした感じが乾いていて、金属がこすれるヘンな感じがする。
何人かに使ってもらったが、「決して使いやすくはない」という意見。

うーん。

次に机の上においておくと面白いかどうか。
あと、大きさ。
この写真で感じてみてください。
NEC_0015.jpg

マカロンが邪魔してますね。

名前はいいんだけどなあ。

Posted by shed at 00:00