powered by nequal
Home » Maple4_DocTest » Timeline » 192

Changeset 192 -- 2008-12-10 21:12:53

Comment
[Add Tag:Release] Maple4_DocTest

Diffs

Maple4_DocTest/tags/0.2.0-alpha/Maple4/Utils/Array.php

@@ -0,0 +1,162 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Utils
+ * @package    Maple4_Utils
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+/**
+ * 配列関連のユーティリティークラス
+ *
+ * @category   Utils
+ * @package    Maple4_Utils
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_Utils_Array
+{
+    /**
+     * @var array 各要素のデフォルト値
+     */
+    private $defaults = array();
+
+    /**
+     * @var array 処理する配列
+     */
+    private $array = array();
+
+    /**
+     * コンストラクタ
+     *
+     * @params array $array 処理する配列
+     */
+    public function __construct($array = array())
+    {
+        $this->array = $array;
+    }
+
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @params array $array 処理する配列
+     * @return object このオブジェクト自身
+     */
+    static public function create($array = array())
+    {
+        return new self($array);
+    }
+
+    /**
+     * 配列から安全に値を取得する
+     *
+     * @param string $key 配列要素
+     * @param mixed $default 値がなかった場合のデフォルト値
+     * @return mixed 取得した値
+     * @access public
+     */
+    public function get($key, $default = null)
+    {
+        if (is_array($this->array) && isset($this->array[$key])) {
+            $result = $this->array[$key];
+        } else {
+            $result = $default;
+        }
+
+        return $result;
+    }
+
+    /**
+     * 配列に値をセットする
+     *
+     * @param string $key 配列要素
+     * @param mixed $value 要素に対する値
+     * @return object このオブジェクト自身
+     * @access public
+     */
+    public function set($key, $value = null)
+    {
+        $this->array[$key] = $value;
+        return $this;
+    }
+
+    /**
+     * 配列ということを隠蔽したアクセス
+     *
+     * @param mixed $key 配列要素
+     * @return mixed 要素に対する値
+     */
+    public function __get($key)
+    {
+        $default = null;
+        if (isset($this->default[$key])) {
+            $default = $this->default[$key];
+        }
+
+        return $this->get($key, $default);
+    }
+
+    /**
+     * 配列ということを隠蔽したアクセス
+     *
+     * @param mixed $key 配列要素
+     * @param mixed $value 要素に対する値
+     */
+    public function __set($key, $value)
+    {
+        $this->set($key, $value);
+    }
+
+    /**
+     * 要素に対してデフォルト値をセット
+     *
+     * @param mixed $key 配列要素
+     * @param mixed $default 要素に対するデフォルト値
+     * @return object このオブジェクト自身
+     */
+    public function setDefault($key, $default = null)
+    {
+        $this->array[$key] = $default;
+
+        return $this;
+    }
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/Utils/File.php

@@ -0,0 +1,464 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Utils
+ * @package    Maple4_Utils
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.1.0
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/Exception.php');
+
+/**
+ * ファイル関連のユーティリティークラス
+ *
+ * @category   Utils
+ * @package    Maple4_Utils
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @author     Kazunobu Ichihashi <bobchin@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.1.0
+ */
+class Maple4_Utils_File
+{
+    const MARKER_FILENAME = '__BASEDIR__';
+
+    /**
+     * @var array 処理対象外パターンを保持する
+     * @access private
+     */
+    private $ignore = array();
+
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @return object このオブジェクト自身
+     */
+    static public function create()
+    {
+        return new self();
+    }
+
+    /**
+     * ディレクトリ区切り文字をOSのものに統一する
+     *
+     * @param string $pathname ディレクトリ文字列
+     * @return string 置き換え後のディレクトリ文字列
+     * @access public
+     */
+    public function fixDirectorySeparator($pathname)
+    {
+        return str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $pathname);
+    }
+
+    /**
+     * 末尾のディレクトリ区切り文字を削除する
+     *
+     * @param string $pathname ディレクトリ文字列
+     * @return string 削除後のディレクトリ文字列
+     * @access public
+     */
+    public function removeTailSlash($pathname)
+    {
+        return rtrim($this->fixDirectorySeparator($pathname), DIRECTORY_SEPARATOR);
+    }
+
+    /**
+     * 末尾にディレクトリ区切り文字を追加する
+     *
+     * @param string $pathname ディレクトリ文字列
+     * @return string 追加後のディレクトリ文字列
+     * @access public
+     */
+    public function addTailSlash($pathname)
+    {
+        return $this->removeTailSlash($pathname) . DIRECTORY_SEPARATOR;
+    }
+
+    /**
+     * 先頭のディレクトリ区切り文字を削除する
+     *
+     * @param string $pathname ディレクトリ文字列
+     * @return string 削除後のディレクトリ文字列
+     * @access public
+     */
+    public function removeHeadSlash($pathname)
+    {
+        return ltrim($this->fixDirectorySeparator($pathname), DIRECTORY_SEPARATOR);
+    }
+
+    /**
+     * 先頭にディレクトリ区切り文字を追加する
+     *
+     * @param string $pathname ディレクトリ文字列
+     * @return string 追加後のディレクトリ文字列
+     * @access public
+     */
+    public function addHeadSlash($pathname)
+    {
+        return DIRECTORY_SEPARATOR . $this->removeHeadSlash($pathname);
+    }
+
+    /**
+     * 指定した文字列を処理対象外とする
+     *
+     * @param mixed $str 処理対象外とする文字列
+     * @access public
+     */
+    public function setIgnore($path)
+    {
+        if (is_array($path)) {
+            $this->ignore = array_merge($this->ignore, $path);
+        } else {
+            $this->ignore[] = $path;
+        }
+    }
+
+    /**
+     * 処理対象外リストをクリアする
+     *
+     * @access public
+     */
+    public function clearIgnore()
+    {
+        $this->ignore = array();
+    }
+
+    /**
+     * 処理対象外かどうか?
+     *
+     * @param string $pathname ディレクトリ名
+     * @return boolean 処理対象外かどうか
+     * @access public
+     */
+    public function isIgnore($pathname)
+    {
+        $match = false;
+        foreach ($this->ignore as $ignore) {
+            if (preg_match("|{$ignore}|", $pathname)) {
+                $match = true;
+                break;
+            }
+        }
+
+        return $match;
+    }
+
+    /**
+     * 基準ディレクトリを返却
+     *
+     * 基準となるディレクトリに設置されているマーカーファイルを探す
+     *
+     * @param string $pathname ディレクトリ名もしくはファイル
+     * @return string 基準となるディレクトリ
+     * @access public
+     */
+    public function searchBasePathname($pathname)
+    {
+        if (is_dir($pathname)) {
+            $path = $pathname;
+        } else {
+            $path = dirname($pathname);
+        }
+
+        while (!$this->isTopDir($path)) {
+            $marker = $this->addTailSlash($path) . self::MARKER_FILENAME;
+            if (file_exists($marker)) {
+                break;
+            }
+            $path = dirname($path);
+        }
+
+        return $this->removeTailSlash($path);
+    }
+
+    /**
+     * トップディレクトリか?
+     *
+     * @param $pathname ディレクトリ名
+     * @return boolean トップディレクトリかどうか
+     * @access private
+     */
+    private function isTopDir($pathname)
+    {
+        if (strstr(PHP_OS, "WIN")) {
+            $result = preg_match('|^[A-Za-z]+:\\\\$|', $pathname);
+        } else {
+            $result = ($this->removeHeadSlash($pathname) === '');
+        }
+
+        return $result;
+    }
+
+    /**
+     * ディレクトリとファイルのリストを取得する
+     *
+     * @param string $pathname ディレクトリ名
+     * @return array ディレクトリとファイルの配列
+     * @access public
+     */
+    public function ls($pathname)
+    {
+        $dirs = array();
+        $files = array();
+
+        if (!is_dir($pathname) || (!$dh = opendir($pathname))) {
+            return array($dirs, $files);
+        }
+
+        while (($filename = readdir($dh)) !== false) {
+            if (preg_match("/^[.]{1,2}$/", $filename)) {
+                continue;
+            }
+
+            $realpath = $this->addTailSlash($pathname) . $filename;
+
+            if ($this->isIgnore($realpath)) {
+                continue;
+            }
+
+            if (is_dir($realpath)) {
+                $dirs[] = $realpath;
+            } else {
+                $files[] = $realpath;
+            }
+        }
+
+        closedir($dh);
+
+        sort($dirs);
+        sort($files);
+
+        return array($dirs, $files);
+    }
+
+    /**
+     * 指定したディレクトリのファイルリストを取得する
+     *
+     * @param string $pathname ディレクトリ名
+     * @param string $regex 取得するファイル名の正規表現
+     * @param function $callback コールバック
+     * @return array ファイルの配列
+     * @access public
+     */
+    public function find($pathname, $regex = null, $callback = null)
+    {
+        list (, $files) = $this->ls($pathname);
+
+        if (is_null($regex)) {
+            if (is_callable($callback)) {
+                $files = array_map($callback, $files);
+            }
+            return $files;
+        }
+
+        $result = array();
+        foreach ($files as $filename) {
+            if (preg_match($regex, $filename)) {
+                if (is_callable($callback)) {
+                    $filename = call_user_func($callback, $filename);
+                }
+                $result[] = $filename;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 指定したディレクトリ以下のファイルリストを取得する
+     *
+     * サブディレクトリがある場合は再帰的に取得する
+     *
+     * @param string $pathname ディレクトリ名
+     * @param string $regex 取得するファイル名の正規表現
+     * @param function $callback コールバック
+     * @return array ファイルの配列
+     * @access public
+     */
+    public function findRecursive($pathname, $regex = null, $callback = null)
+    {
+        list ($dirs,) = $this->ls($pathname);
+
+        $found = $this->find($pathname, $regex, $callback);
+
+        foreach ($dirs as $dir) {
+            $found = array_merge($found, $this->findRecursive($dir, $regex, $callback));
+        }
+
+        sort($found);
+        reset($found);
+
+        return $found;
+    }
+
+    /**
+     * ファイルを削除する
+     *
+     * @param string $filename ファイル名
+     * @return boolean 処理結果
+     * @access public
+     */
+    public function unlink($filename)
+    {
+        $result = true;
+        if (file_exists($filename)) {
+            $result = unlink($filename);
+        }
+
+        return $result;
+    }
+
+    /**
+     * ディレクトリを作成する。
+     *
+     * 複数階層のディレクトリを指定した場合に、
+     * 途中のディレクトリも自動的に作成する。
+     *
+     * @param string $pathname 作成するディレクトリ名
+     * @param int $mode 権限
+     * @param int $umask umaskの値
+     * @return boolean 処理結果
+     * @access public
+     */
+    public function makeDir($pathname, $mode = 0777, $umask = 0)
+    {
+        umask($umask);
+
+        $result = true;
+        if (!file_exists($pathname)) {
+            $result = mkdir($pathname, $mode, true);
+        }
+
+        return $result;
+    }
+
+    /**
+     * ディレクトリを削除する
+     *
+     * 指定したディレクトリより下のディレクトリ・ファイルも
+     * 自動的に削除する。
+     *
+     * @param string $pathname 削除するディレクトリ名
+     * @return boolean 処理結果
+     * @access public
+     */
+    public function removeDir($pathname)
+    {
+        list ($dirs, $files) = $this->ls($pathname);
+
+        array_map(array($this, 'unlink'), $files);
+        array_map(array($this, 'removeDir'), $dirs);
+
+        $result = true;
+        if (file_exists($pathname)) {
+            $result = rmdir($pathname);
+        }
+
+        return $result;
+    }
+
+    /**
+     * ファイルを読み込む
+     *
+     * @param string $filename ファイル名
+     * @return string ファイルの内容
+     * @access public
+     */
+    public function read($filename)
+    {
+        $result = null;
+
+        if (!file_exists($filename)) {
+            throw new Maple4_Exception("file not found({$filename})");
+        }
+
+        if (!is_readable($filename)) {
+            throw new Maple4_Exception("file not readable({$filename})");
+        }
+
+        if (version_compare('6.0.0', phpversion(), '<=')) {
+            $result = @file_get_contents($filename, FILE_USE_INCLUDE_PATH);
+        } else {
+            $result = @file_get_contents($filename, true);
+        }
+
+        if ($result === false) {
+            $result = null;
+        }
+
+        return $result;
+    }
+
+    /**
+     * ファイルに書き込む
+     *
+     * 指定した内容をファイルに上書きで書き込む。
+     * 途中のフォルダが存在しない場合は自動的に作成する。
+     *
+     * @param string $filename  ファイル名
+     * @param string $buf  書き込む内容
+     * @param string $mode  書き込みモード
+     * @return boolean
+     * @access public
+     */
+    public function write($filename, $buf, $mode = "wb")
+    {
+        if (file_exists($filename) && !is_writable($filename)) {
+            throw new Maple4_Exception("file not writable({$filename})");
+        }
+
+        $this->makeDir(dirname($filename));
+
+        if (!($fh = fopen($filename, $mode))) {
+            return false;
+        }
+
+        if (!fwrite($fh, $buf)) {
+            return false;
+        }
+
+        if (!fclose($fh)) {
+            return false;
+        }
+
+        return true;
+    }
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/Utils/Class.php

@@ -0,0 +1,117 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Utils
+ * @package    Maple4_Utils
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.1.0
+ */
+
+/**
+ * クラス関連のユーティリティークラス
+ *
+ * @category   Utils
+ * @package    Maple4_Utils
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.1.0
+ */
+class Maple4_Utils_Class
+{
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @return object このオブジェクト自身
+     */
+    static public function create()
+    {
+        return new self();
+    }
+
+    /**
+     * 指定されたファイル名に対するクラス名を返却
+     *
+     * 返却されるクラス名は小文字に変換後返却される
+     *
+     * オプション
+     *  ucfirst:   クラス名の各パートの先頭を大文字にするか
+     *  namespace: 変換時に使用する擬似名前空間
+     *
+     * @param string $filename クラス名に変換したいファイル名
+     * @param array $options 変換時に使用するオプション
+     * @return string クラス名
+     * @access public
+     */
+    public function toClassname($filename, $options = array())
+    {
+        $doUcfirst = true;
+        if (isset($options['ucfirst']) &&
+            !is_null($options['ucfirst'])) {
+            $doUcfirst = $options['ucfirst'];
+        }
+
+        $namespace = '';
+        if (isset($options['namespace']) &&
+            !is_null($options['namespace'])) {
+            $namespace = $options['namespace'];
+        }
+
+        $result = null;
+        if (!preg_match('|\.php|', $filename)) {
+            return $result;
+        }
+
+        if ($namespace) {
+            $pathname = join('/', preg_split('|_|', $namespace));
+            $filename = "{$pathname}/{$filename}";
+        }
+
+        $filename = preg_replace('|\.php|', '', $filename);
+        $parts = preg_split('|[\\\\/]|', $filename);
+
+        if ($doUcfirst) {
+            $result = join('_', array_map('ucfirst', $parts));
+        } else {
+            $result = strtolower(join('_', $parts));
+        }
+
+        return $result;
+    }
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/Exception.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Exception
+ * @package    Maple4_Exception
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.1.0
+ */
+
+/**
+ * Maple4の例外のベースとなる例外
+ *
+ * @category   Exception
+ * @package    Maple4_Exception
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.1.0
+ */
+class Maple4_Exception extends Exception
+{
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Runner.php

@@ -0,0 +1,284 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once('PHPUnit/Framework.php');
+require_once('PHPUnit/TextUI/TestRunner.php');
+require_once(dirname(dirname(__FILE__)) . '/Utils/File.php');
+require_once(dirname(dirname(__FILE__)) . '/Utils/Class.php');
+require_once(dirname(dirname(__FILE__)) . '/Utils/Array.php');
+
+/**
+ * テストケースを実行する
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_Runner
+{
+    /**
+     * @var object Maple4_Utils_Fileのインスタンス
+     * @access private
+     */
+    private $fileUtils;
+
+    /**
+     * @var object Maple4_Utils_Classのインスタンス
+     * @access private
+     */
+    private $classUtils;
+
+    /**
+     * コンストラクタ
+     */
+    public function __construct()
+    {
+        $this->fileUtils = new Maple4_Utils_File();
+        $this->classUtils = new Maple4_Utils_Class();
+    }
+
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @return object このオブジェクト自身
+     */
+    static public function create()
+    {
+        return new self();
+    }
+
+    /**
+     * テストを実行する
+     *
+     * @param array $options DocTestの動作オプション
+     * @param string $pathname テスト対象となるディレクトリ
+     * @param array $testcases 実行するファイルリスト
+     * @return string 実行結果
+     * @access public
+     */
+    public function run($options, $pathname, $testcases)
+    {
+        // ファイル単体で指定された場合のための対策
+        if (preg_match('|\.php$|', $pathname)) {
+            $testcases = $this->filterTestCases($pathname, $testcases);
+        }
+
+        $suite = new PHPUnit_Framework_TestSuite();
+
+        foreach ($testcases as $realpath => $path) {
+            if (is_null($realpath) || is_null($path) ||
+                !file_exists($realpath)) {
+                continue;
+            }
+
+            require_once($realpath);
+
+            $classname = $this->classUtils->toClassname($path);
+
+            if (!is_null($classname)) {
+                $suite->addTestSuite($classname);
+            }
+        }
+
+        $parameters = array();
+
+        $options = new Maple4_Utils_Array($options);
+
+        if (!strstr(PHP_OS, "WIN") && $options->color) {
+            include_once(dirname(__FILE__) . '/Runner/ResultPrinter.php');
+            $parameters['printer'] = new Maple4_DocTest_Runner_ResultPrinter();
+        }
+
+        if (!is_null($options->report) &&
+            is_dir($options->report) &&
+            file_exists($options->report)) {
+            $parameters['reportDirectory'] = $options->report;
+        }
+
+        ob_start();
+        PHPUnit_TextUI_TestRunner::run($suite, $parameters);
+        $output = ob_get_contents();
+        ob_end_clean();
+
+        echo $output;
+
+        if ($options->notify) {
+            $notify = new Maple4_Utils_Array($options->notify);
+            if ($notify->type === 'growl') {
+                $this->growlNotify($notify, $output);
+            }
+        }
+    }
+
+    /**
+     * ファイル単体で指定された場合はテストケースを絞る
+     *
+     * @param string $pathname テスト対象となるディレクトリ
+     * @param array $testcases 実行するファイルリスト
+     * @return array 絞り込んだテストケース
+     * @access private
+     */
+    private function filterTestCases($pathname, $testcases)
+    {
+        $pathname = $this->fileUtils->fixDirectorySeparator($pathname);
+
+        foreach ($testcases as $realpath => $path) {
+            $original = $this->makeOriginalFilename($path);
+            if (strstr($pathname, $original)) {
+                $testcases = array($realpath => $path);
+                break;
+            }
+        }
+
+        return $testcases;
+    }
+
+    /**
+     * テストケースファイルから元のPHPファイル名を生成する
+     *
+     * @param string $pathname テストケースのファイル名
+     * @return string 元のPHPファイル名
+     * @access private
+     */
+    private function makeOriginalFilename($pathname)
+    {
+        $result = preg_replace('|^Maple4_DocTest_|', '', $pathname);
+        $result = preg_replace('|Test\.php$|', '', $result);
+        $result = $this->fileUtils->removeHeadSlash($result) . '.php';
+
+        $parts = preg_split('|_|', $this->classUtils->toClassname($result));
+
+        $result = $this->fileUtils->fixDirectorySeparator(join('/', $parts) . '.php');
+
+        return $result;
+    }
+
+    /**
+     * Growlを利用する
+     *
+     * @param array $options DocTestの動作オプション(Notify部分)
+     * @param string $output テスト結果
+     * @access private
+     */
+    private function growlNotify($notify, $output)
+    {
+        @include_once 'Net/Growl.php';
+        if (!class_exists('Net_Growl', false)) {
+            return;
+        }
+
+        $appName = 'Maple4_DocTest';
+        $notifications = array('Green', 'Red');
+
+        $greenPriority = $notify->get('greePriority', 0);
+        $greenSticky = $notify->get('greeStickey', false);
+        $redPriority = $notify->get('redPriority', 2);
+        $redSticky = $notify->get('redSticky', false);
+
+        $status = 'Red';
+        $title = $notify->get('title', 'Test Results');
+        $description = 'No output';
+        $notifyOptions = array(
+            'priority' => $redPriority,
+            'sticky' => $redSticky,
+        );
+
+        $output = preg_replace('|\033\[[\d;]+m|', '', $output);
+
+        if (preg_match("|(OK \(\d+ test[s]{0,1}\))|m", $output, $matches)) {
+            $status = 'Green';
+            $description = $matches[1];
+            $notifyOptions = array(
+                'priority' => $greenPriority,
+                'sticky' => $greenSticky,
+            );
+        } else if (preg_match("|(OK, but incomplete or skipped tests!\s*\nTests: .+\.)|m", $output, $matches)) {
+            $status = 'Red';
+            $description = $matches[1];
+            $notifyOptions = array(
+                'priority' => $redPriority,
+                'sticky' => $redSticky,
+            );
+        } else if (preg_match("|(FAILURES!\s*\nTests: .+\.)|m", $output, $matches)) {
+            $status = 'Red';
+            $description = $matches[1];
+            $notifyOptions = array(
+                'priority' => $redPriority,
+                'sticky' => $redSticky,
+            );
+        }
+
+        set_error_handler(array($this, 'handler'));
+
+        $application = new Net_Growl_Application($appName, $notifications, $notify->password);
+        $growl = new Net_Growl($application);
+        $growl->notify($status, $title, $description, $notifyOptions);
+    }
+
+    /**
+     * Net_GrowlのStrict Errorだけを封じ込める
+     *
+     * 本当はこんなことしたくないのだが・・・
+     * しかしUnitTestは E_ALL | E_STRICT で実行されるべき
+     *
+     * @param integer $errno エラーレベル
+     * @param string $errstr エラーメッセージ
+     * @param string $errfile エラーが発生したファイル名
+     * @param string $errline エラーが発生した行数
+     * @access public
+     */
+    public function handler($errno, $errstr, $errfile, $errline)
+    {
+        if (($errno === E_STRICT) &&
+            preg_match('|Non-static method PEAR::getStaticProperty\(\) should not be called statically|', $errstr)) {
+            return true;
+        }
+
+        return false;
+    }
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Parser.php

@@ -0,0 +1,236 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/Utils/Class.php');
+
+/**
+ * PHPファイルからDocTestコメントを抜き出す
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_Parser
+{
+    /**
+     * PHPファイルからDocTestコメントを抜き出す
+     *
+     * @param string $realpath 対象となるPHPファイル(フルパス)
+     * @param string $path 対象となるPHPファイル
+     * @return string 抜き出した文字列
+     * @access public
+     */
+    public function parse($realpath, $path)
+    {
+        $result = array();
+
+        if (!file_exists($realpath)) {
+            return $result;
+        }
+
+        include_once($realpath);
+
+        $classname = Maple4_Utils_Class::create()->toClassname($path);
+
+        $docComments = $this->getDocComments($classname);
+
+        foreach ($docComments as $methodname => $methodComment) {
+            $this->getTestData($result, $methodComment, $methodname);
+        }
+
+        return $result;
+    }
+
+    /**
+     * クラスおよびメソッドに対するDocコメントを取得
+     *
+     * 返却値はクラスおよび各メソッドに対するDocコメント
+     *
+     * @param string $classname クラス名
+     * @return array Docコメントの配列
+     * @access private
+     */
+    private function getDocComments($classname)
+    {
+        $refClass = new ReflectionClass($classname);
+
+        $result = array();
+
+        if ($comment = $refClass->getDocComment()) {
+            $result[''] = $this->removeCommentStr($comment);
+        }
+
+        // 親クラスのコメントは削除する
+        $method_comments = $this->getDocCommentsOfMethods($refClass);
+
+        while($refClass = $refClass->getParentClass()) {
+            $parent_comments = $this->getDocCommentsOfMethods($refClass);
+            foreach ($parent_comments as $method => $comment) {
+                if (isset($method_comments[$method]) &&
+                    $method_comments[$method] == $comment) {
+                    unset($method_comments[$method]);
+                }
+            }
+        }
+
+        $result = array_merge($result, $method_comments);
+
+        return $result;
+    }
+
+    /**
+     * 文字列からコメント文字列を除去
+     *
+     * 正規表現があまりいけてないような気がするので
+     * 後で直す・・・
+     *
+     * @param string $str 元の文字列
+     * @return string コメント文字列を除去した文字列
+     * @access private
+     */
+    private function removeCommentStr($str)
+    {
+        $str = preg_replace('|^\s*/\*\*|m', '', $str);
+        $str = preg_replace('|^\s*\*/|m', '', $str);
+        $str = preg_replace('|^\s*\*[ ]{1}|m', '', $str);
+        $str = preg_replace('|^\s*\*[ ]{0}|m', '', $str);
+
+        return trim($str);
+    }
+
+    /**
+     * メソッドのDocコメントを取得する
+     *
+     * @param object $refClass リフレクションクラスのインスタンス
+     * @return array メソッドに対するコメントの配列
+     * @access private
+     */
+    private function getDocCommentsOfMethods($refClass)
+    {
+        $result = array();
+        foreach ($refClass->getMethods() as $refMethod) {
+            if ($comment = $refMethod->getDocComment()) {
+                $result[$refMethod->getName()]
+                    = $this->removeCommentStr($comment);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 指定した文字列からテストに関する情報のみを抜き出す
+     *
+     * 第1引数で結果を組み立てていく
+     *
+     * @param array &$result テスト情報の配列
+     * @param string $str Docコメント文字列
+     * @param string $methodname メソッド名
+     * @access private
+     */
+    private function getTestData(&$result, $str, $methodname = null)
+    {
+        $methodname = strtolower($methodname);
+
+        if (preg_match_all('|(#test (.*?))?<code>(.*?)</code>|s', $str, $matches, PREG_SET_ORDER)) {
+            foreach ($matches as $match) {
+                $userdefine = trim($match[2]);
+                if (($methodname === '') && ($userdefine === '')) {
+                    throw new Maple4_Exception('missing testname');
+                    break;
+                }
+
+                $testname = $this->createTestName($methodname, $userdefine);
+                $body = trim($match[3]);
+
+                if (preg_match('|#f\(|', $body)) {
+                    if (!$methodname) {
+                        throw new Maple4_Exception('missing testname');
+                        break;
+                    }
+
+                    $body = preg_replace('|#f\(|', "\$this->obj->{$methodname}(", $body);
+                }
+
+                if (isset($result[$testname])) {
+                    $result[$testname]['body'] .= "\n\n" . $body;
+                } else {
+                    $result[$testname] = array(
+                        'method' => $methodname,
+                        'userdefine' => $userdefine,
+                        'body' => $body,
+                    );
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * テスト名を生成する
+     *
+     * @param string $methodname テスト対象のメソッド
+     * @param string $userdefine ユーザが記述したテスト名
+     * @return string 生成されたテスト名
+     * @access private
+     */
+    private function createTestName($methodname, $userdefine)
+    {
+        $specialKeys = array('__noop', '__setup', '__teardown');
+        if (in_array(strtolower($userdefine), $specialKeys)) {
+            $testname = strtolower($userdefine);
+        } else {
+            $testname = $methodname;
+            if ($userdefine) {
+                $testname .= '_' . $userdefine;
+            }
+        }
+
+        return $testname;
+    }
+}
\ No newline at end of file

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/PHPFinder.php

@@ -0,0 +1,87 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/DocTest/Finder.php');
+
+/**
+ * PHPファイルをサーチしてリスト生成するクラス
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_PHPFinder
+{
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @return object このオブジェクト自身
+     */
+    static public function create()
+    {
+        return new self();
+    }
+
+    /**
+     * 引数で指定されたディレクトリ(もしくはファイル)に対して
+     * ファイルリストを返却
+     *
+     * @param array $options DocTestの動作オプション
+     * @param string $pathname ディレクトリ名もしくはファイル
+     * @param string $basePathname 基準となるディレクトリ
+     * @return array ファイルリスト
+     * @access publicp
+     */
+    public function find($options, $pathname, $basePathname = null)
+    {
+        $regex = '|.+\.php$|';
+
+        $finder = new Maple4_DocTest_Finder();
+        return $finder->find($options, $regex, $pathname, $basePathname);
+    }
+}
\ No newline at end of file

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Builder/specialMethod.tpl

@@ -0,0 +1,11 @@
+private $obj;
+
+public function setUp()
+{
+    $this->obj = new #class;
+}
+
+public function tearDown()
+{
+    $this->obj = null;
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Builder/class.tpl

@@ -0,0 +1,4 @@
+class Maple4_DocTest_%sTest extends PHPUnit_Framework_TestCase
+{
+%s
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Builder/file.tpl

@@ -0,0 +1,4 @@
+<?php
+require_once('%s');
+
+%s

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Builder/method.tpl

@@ -0,0 +1,4 @@
+    public function %s()
+    {
+%s
+    }

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Runner/ResultPrinter.php

@@ -0,0 +1,183 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once 'PHPUnit/TextUI/ResultPrinter.php';
+require_once 'PHPUnit/Util/Filter.php';
+
+PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
+
+/**
+ * PHPUnit3の出力結果に色をつけるためのクラス
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_Runner_ResultPrinter extends PHPUnit_TextUI_ResultPrinter
+{
+    const LINE_WIDTH = 80;
+
+    const STYLE_BOLD = 1;
+    const FG_WHITE = 37;
+    const BG_RED = 41;
+    const BG_GREEN = 42;
+    const BG_MAGENTA = 45;
+
+    /**
+     * PHPUnitの出力部分にフックする
+     *
+     * @param  array   $defects
+     * @param  integer $count
+     * @param  string  $type
+     */
+    protected function printDefects(array $defects, $count, $type)
+    {
+        if ($count == 0) {
+            return;
+        }
+
+        if (($type == 'error') || ($type == 'failure')) {
+            $bgColor = self::BG_RED;
+        } else {
+            $bgColor = self::BG_MAGENTA;
+        }
+
+        $str = sprintf(
+            "There %s %d %s%s:\n",
+
+            ($count == 1) ? 'was' : 'were',
+            $count,
+            $type,
+            ($count == 1) ? '' : 's'
+        );
+
+        $this->write($this->convert($bgColor, $str));
+
+        $i = 1;
+
+        foreach ($defects as $defect) {
+            $this->printDefect($defect, $i++);
+        }
+    }
+
+    /**
+     * PHPUnitの出力部分にフックする
+     *
+     * @param  PHPUnit_Framework_TestResult  $result
+     */
+    protected function printFooter(PHPUnit_Framework_TestResult $result)
+    {
+        if ($result->wasSuccessful() &&
+            $result->allCompletlyImplemented() &&
+            $result->noneSkipped()) {
+            $str = sprintf(
+                "OK (%d test%s)\n",
+
+                count($result),
+                (count($result) == 1) ? '' : 's'
+            );
+
+            $this->write($this->convert(self::BG_GREEN, $str));
+        } else if ((!$result->allCompletlyImplemented() ||
+                    !$result->noneSkipped()) &&
+                    $result->wasSuccessful()) {
+            $str = sprintf(
+                "OK, but incomplete or skipped tests!\n" .
+                "Tests: %d%s%s.\n",
+
+                count($result),
+                $this->getCountString($result->notImplementedCount(), 'Incomplete'),
+                $this->getCountString($result->skippedCount(), 'Skipped')
+            );
+
+            $this->write("\n" . $this->convert(self::BG_MAGENTA, $str));
+        } else {
+            $str = sprintf(
+                "FAILURES!\n" .
+                "Tests: %d%s%s%s%s.\n",
+
+                count($result),
+                $this->getCountString($result->failureCount(), 'Failures'),
+                $this->getCountString($result->errorCount(), 'Errors'),
+                $this->getCountString($result->notImplementedCount(), 'Incomplete'),
+                $this->getCountString($result->skippedCount(), 'Skipped')
+            );
+
+            $this->write("\n" . $this->convert(self::BG_RED, $str));
+        }
+    }
+
+    /**
+     * 指定された色コードに対するエスケープシーケンスを発行
+     *
+     * @param integer $code 色コード
+     * @param string $str 表示文字列
+     * @return string 装飾済みの文字列
+     * @access private
+     */
+    private function convert($code, $str)
+    {
+        $lines = preg_split("|\n|", rtrim($str));
+
+        $result = null;
+
+        foreach ($lines as $line) {
+            if (strlen($line) < self::LINE_WIDTH) {
+                $line = $line . str_repeat(' ', self::LINE_WIDTH - strlen($line));
+            }
+
+            $codes = array();
+            $codes[] = self::STYLE_BOLD;
+            $codes[] = self::FG_WHITE;
+            $codes[] = $code;
+
+            $result .= sprintf("\033[%sm%s\033[0m\n", implode(';', $codes), $line);
+        }
+
+        return $result;
+   }
+}
\ No newline at end of file

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/TestCaseFinder.php

@@ -0,0 +1,87 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/DocTest/Finder.php');
+
+/**
+ * テストケースファイルをサーチしてリスト生成するクラス
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_TestCaseFinder
+{
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @return object このオブジェクト自身
+     */
+    static public function create()
+    {
+        return new self();
+    }
+
+    /**
+     * 引数で指定されたディレクトリ(もしくはファイル)に対して
+     * ファイルリストを返却
+     *
+     * @param array $options DocTestの動作オプション
+     * @param string $pathname ディレクトリ名もしくはファイル
+     * @param string $basePathname 基準となるディレクトリ
+     * @return array ファイルリスト
+     * @access public
+     */
+    public function find($options, $pathname, $basePathname = null)
+    {
+        $regex = '|.+Test\.php$|';
+
+        $finder = new Maple4_DocTest_Finder();
+        return $finder->find($options, $regex, $pathname, $basePathname);
+    }
+}
\ No newline at end of file

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Builder.php

@@ -0,0 +1,313 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/Utils/Class.php');
+require_once(dirname(dirname(__FILE__)) . '/Utils/File.php');
+
+/**
+ * DocTestコメントから生成された文字列からテストケースを生成する
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_Builder
+{
+    /**
+     * @var object Maple4_Utils_Fileのインスタンス
+     * @access private
+     */
+    private $fileUtils = null;
+
+    /**
+     * @static string インデントで使用する空白の数
+     * @access private
+     */
+    static private $indentWidth = 4;
+
+    /**
+     * コンストラクタ
+     */
+    public function __construct()
+    {
+        $this->fileUtils = new Maple4_Utils_File();
+    }
+
+    /**
+     * DocTestコメントから抜き出された文字列からテストケースを生成
+     *
+     * @param string $realpath 対象となるPHPファイル(フルパス)
+     * @param string $path 対象となるPHPファイル
+     * @param array $comments DocTestコメントから抜き出された
+     *                        文字列の配列
+     * @return string 生成したテストケース
+     * @access public
+     */
+    public function build($realpath, $path, $comments)
+    {
+        if (is_null($realpath) || is_null($path) ||
+            !is_array($comments)) {
+            return null;
+        }
+
+        $definition = $this->createTestClassDefinition($realpath, $path, $comments);
+        if (is_null($definition)) {
+            return null;
+        }
+
+        $template = $this->getFileTemplate();
+        return rtrim(sprintf($template, $realpath, rtrim($definition)));
+    }
+
+    /**
+     * テストで使用するクラス定義を生成する
+     *
+     * @param string $realpath 対象となるPHPファイル(フルパス)
+     * @param string $path 対象となるPHPファイル
+     * @param array $comments DocTestコメントから抜き出された
+     *                        文字列の配列
+     * @return string 生成されたクラス定義
+     * @access private
+     */
+    private function createTestClassDefinition($realpath, $path, $comments)
+    {
+        $definition = null;
+        $existsSetUp = false;
+        $existsTearDown = false;
+
+        foreach ($comments as $testname => $commentData) {
+            $body = $commentData['body'];
+            $method = $commentData['method'];
+
+            if ($testname === '__noop') {
+                $definition .= $this->concatIndent($body, self::$indentWidth) . "\n\n";
+            } else {
+                $definition .= $this->createMethodDefinition($testname, $commentData);
+            }
+
+            if ($testname === '__setup') {
+                $existsSetUp = true;
+            } else if ($testname === '__teardown') {
+                $existsTearDown = true;
+            }
+        }
+
+        if (is_null($definition)) {
+            return null;
+        }
+
+        if (!$existsSetUp && !$existsTearDown) {
+            $body = $this->getSpecialMethodTemplate();
+            $definition = $this->concatIndent($body, self::$indentWidth) . "\n\n" . $definition;
+        }
+
+        $replaceStrings = $this->getReplaceStrings();
+
+        $classname = Maple4_Utils_Class::create()->toClassname($path);
+        $replaceStrings['class'] = "{$classname}()";
+
+        foreach ($replaceStrings as $from => $to) {
+            $definition = preg_replace("|#{$from}|", $to, $definition);
+        }
+
+        $template = $this->getClassTemplate();
+        return rtrim(sprintf($template, $classname, rtrim($definition)));
+    }
+
+    /**
+     * 文字列の各行の先頭にインデントを挿入
+     *
+     * 文字列の各行に引数で指定された数だけのインデントを挿入
+     *
+     * @param string $str 元の文字列
+     * @param integer $indent インデントの数
+     * @return string インデント挿入後の文字列
+     * @access private
+     */
+    private function concatIndent($str, $indent = null)
+    {
+        if ($indent === null) {
+            $indent = self::$indentWidth * 2;
+        }
+
+        $lines = preg_split("|\n|", $str);
+        $lines = array_map('rtrim', $lines);
+
+        $hereDocumentString = null;
+        $quoteString = null;
+
+        foreach ($lines as $key => $line) {
+            if ($hereDocumentString || $quoteString) {
+                $lines[$key] = rtrim($line);
+
+                if ($hereDocumentString &&
+                    preg_match("|^{$hereDocumentString}[;,]*|", $line)) {
+                    $hereDocumentString = null;
+                }
+
+                if ((($quoteString == "'") &&
+                     preg_match('/(^\'|[^\\\\]?\')/', $line)) ||
+                    (($quoteString == '"') &&
+                     preg_match('/(^"|[^\\\\]?")/', $line))) {
+                    $quoteString = null;
+                }
+            } else {
+                $lines[$key] = rtrim(str_repeat(chr(32), $indent) . $line);
+
+                if (preg_match('|<<<(.+)|', $line, $matches)) {
+                    $hereDocumentString = $matches[1];
+                } else if (preg_match_all('/(^\'|[^\\\\]?\')/', $line, $matches, PREG_SET_ORDER) &&
+                           (count($matches) == 1)) {
+                    $quoteString = "'";
+                } else if (preg_match_all('/(^"|[^\\\\]?")/', $line, $matches, PREG_SET_ORDER) &&
+                           (count($matches) == 1)) {
+                    $quoteString = '"';
+                }
+            }
+        }
+
+        return rtrim(join("\n", $lines));
+    }
+
+    /**
+     * 文字列からメソッドを生成する
+     *
+     * @param string $testname テスト名
+     * @param array $commentData コメント情報
+     * @return string メソッド定義
+     * @access private
+     */
+    private function createMethodDefinition($testname, $commentData)
+    {
+        $specialKeys = array(
+            '__setup'    => 'setUp',
+            '__teardown' => 'tearDown',
+        );
+
+        $methodname = $commentData['method'];
+        $userdefine = $commentData['userdefine'];
+        $header = ($userdefine) ? ($userdefine) : $methodname;
+        $body = sprintf("// %s\n%s", $header, $commentData['body']);
+
+        if (in_array(strtolower($userdefine), array_keys($specialKeys))) {
+            $methodname = $specialKeys[strtolower($userdefine)];
+        } else {
+            $methodname = sprintf('test%s', ucfirst($testname));
+        }
+
+        $body = $this->concatIndent($body, self::$indentWidth * 2);
+
+        $template = $this->getMethodTemplate();
+        return rtrim(sprintf($template, $methodname, $body)) ."\n\n";
+    }
+
+    /**
+     * メソッドのテンプレートを返却
+     *
+     * @return string メソッドのテンプレート
+     * @access private
+     */
+    private function getMethodTemplate()
+    {
+        $filename = dirname(__FILE__) . '/Builder/method.tpl';
+        return $this->fileUtils->read($filename);
+    }
+
+    /**
+     * スペシャルメソッド(setUp/TearDown)のテンプレートを返却
+     *
+     * @return string スペシャルメソッドのテンプレート
+     * @access private
+     */
+    private function getSpecialMethodTemplate()
+    {
+        $filename = dirname(__FILE__) . '/Builder/specialMethod.tpl';
+        return $this->fileUtils->read($filename);
+    }
+
+    /**
+     * クラスのテンプレートを返却
+     *
+     * @return string クラスのテンプレート
+     * @access public
+     */
+    public function getClassTemplate()
+    {
+        $filename = dirname(__FILE__) . '/Builder/class.tpl';
+        return $this->fileUtils->read($filename);
+    }
+
+    /**
+     * テストケースファイルのテンプレートを返却
+     *
+     * @return string テストケースクラスのテンプレート
+     * @access public
+     */
+    public function getFileTemplate()
+    {
+        $filename = dirname(__FILE__) . '/Builder/file.tpl';
+        return $this->fileUtils->read($filename);
+    }
+
+    /**
+     * メソッドの省略可能文字列を返却
+     *
+     * @return string 省略文字列の配列
+     * @access public
+     */
+    public function getReplaceStrings()
+    {
+        return array(
+            'eq\('      => '$this->assertEquals(',
+            'ne\('      => '$this->assertNotEquals(',
+            'true\('    => '$this->assertTrue(',
+            'false\('   => '$this->assertFalse(',
+            'null\('    => '$this->assertNull(',
+            'notnull\(' => '$this->assertNotNull(',
+        );
+    }
+}
\ No newline at end of file

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/Finder.php

@@ -0,0 +1,108 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/Utils/File.php');
+
+/**
+ * PHPファイルをサーチしてリスト生成するクラス
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_Finder
+{
+    /**
+     * 引数で指定されたディレクトリ(もしくはファイル)に対して
+     * ファイルリストを返却
+     *
+     * @param array $options DocTestの動作オプション
+     * @param string $regex ファイルリストに含めたい正規表現パターン
+     * @param string $pathname ディレクトリ名もしくはファイル
+     * @param string $basePathname 基準となるディレクトリ
+     * @return array ファイルリスト
+     * @access public
+     */
+    public function find($options, $regex, $pathname, $basePathname = null)
+    {
+        $fileUtils = new Maple4_Utils_File();
+
+        if (isset($options['ignore'])) {
+            $fileUtils->setIgnore($options['ignore']);
+        }
+
+        $pathname = $fileUtils->fixDirectorySeparator($pathname);
+
+        $files = array();
+
+        if (is_dir($pathname)) {
+            $files = $fileUtils->findRecursive($pathname, $regex);
+        } else if (file_exists($pathname) &&
+                   preg_match($regex, $pathname)) {
+            $files = array($fileUtils->fixDirectorySeparator($pathname));
+        }
+
+        $result = array();
+
+        if (!is_array($files) || (count($files) < 1)) {
+            return $result;
+        }
+
+        if (is_null($basePathname)) {
+            $basePathname = $fileUtils->searchBasePathname($pathname);
+        } else {
+            $basePathname = $fileUtils->fixDirectorySeparator($basePathname);
+        }
+
+        foreach ($files as $filename) {
+            $path = str_replace($basePathname, '', $filename);
+            $result[$filename] = $fileUtils->removeHeadSlash($path);
+        }
+
+        return $result;
+    }
+}
\ No newline at end of file

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest/UpdateChecker.php

@@ -0,0 +1,174 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.2.0
+ */
+
+require_once(dirname(dirname(__FILE__)) . '/Utils/File.php');
+
+/**
+ * ファイルの更新状況をチェックする
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.2.0
+ */
+class Maple4_DocTest_UpdateChecker
+{
+    /**
+     * @var object Maple4_Utils_Fileのインスタンス
+     * @access private
+     */
+    private $fileUtils = null;
+
+    /**
+     * コンストラクタ
+     */
+    public function __construct()
+    {
+        $this->fileUtils = new Maple4_Utils_File();
+    }
+
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @return object このオブジェクト自身
+     */
+    static public function create()
+    {
+        return new self();
+    }
+
+    /**
+     * 引数で指定されたファイルリストに対して、更新状況をチェックする
+     *
+     * @param array $options DocTestの動作オプション
+     * @param string $pathname ディレクトリ名もしくはファイル
+     * @param array $files ファイルリスト
+     * @return array 更新状況
+     * @access public
+     */
+    public function check($options, $pathname, $files)
+    {
+        $result = array(
+             'delete' => array(),
+             'new' => array(),
+             'update' => array(),
+        );
+
+        $options = new Maple4_Utils_Array($options);
+        $compileDir = $options->compileDir;
+        if (is_null($compileDir)) {
+            return $result;
+        }
+
+        $forceCompile = $options->forceCompile;
+        if (is_null($forceCompile)) {
+            $forceCompile = false;
+        }
+
+        $filename = $this->makeStatusFilename($compileDir, $pathname);
+
+        $lines = array();
+        if ($forceCompile === true) {
+            $this->fileUtils->unlink($filename);
+        } else if (file_exists($filename)) {
+            $buf = $this->fileUtils->read($filename);
+            if (!is_null($buf)) {
+                $lines = preg_split('|\n|', trim($buf));
+            }
+        }
+
+        if (!is_array($lines)) {
+            $lines = array();
+        }
+
+        $basePathname = $this->fileUtils->addTailSlash($this->fileUtils->searchBasePathname($pathname));
+
+        $checkList = array();
+        foreach ($lines as $line) {
+            if (strlen(trim($line)) < 1) {
+                continue;
+            }
+
+            list ($realpath, $mtime) = preg_split('|\t|', trim($line));
+            $checkList[$realpath] = $realpath;
+            $path = str_replace($basePathname, '', $realpath);
+
+            if (!isset($files[$realpath])) {
+                $result['delete'][$realpath] = $path;
+            } else if (filemtime($realpath) > $mtime) {
+                $result['update'][$realpath] = $path;
+            }
+        }
+
+        $saveLines = null;
+        foreach ($files as $realpath => $path) {
+            if (!isset($checkList[$realpath])) {
+                $result['new'][$realpath] = $path;
+            }
+
+            $saveLines .= sprintf("%s\t%s\n", $realpath, filemtime($realpath));
+        }
+
+        $this->fileUtils->write($filename, $saveLines);
+
+        return $result;
+    }
+
+    /**
+     * 更新状況を保存しているファイル名の生成
+     *
+     * @param string $compileDir コンパイルディレクトリ
+     * @param string $pathname チェックしたいディレクトリ
+     *                         もしくはファイル名
+     * @return string ファイル名
+     * @access private
+     */
+    private function makeStatusFilename($compileDir, $pathname)
+    {
+        return sprintf("%s/%s.txt", $this->fileUtils->removeTailSlash($compileDir), sha1($pathname));
+    }
+}

Maple4_DocTest/tags/0.2.0-alpha/Maple4/DocTest.php

@@ -0,0 +1,271 @@
+<?php
+/**
+ * PHP versions 5
+ *
+ * Copyright (c) 2008 Maple Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *
+ *   * Neither the name of Sebastian Bergmann nor the names of his
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    SVN: $Id$
+ * @since      File available since Release 0.1.0
+ */
+
+require_once('PHPUnit/Framework.php');
+require_once('PHPUnit/TextUI/TestRunner.php');
+require_once(dirname(__FILE__) . '/Utils/File.php');
+require_once(dirname(__FILE__) . '/Utils/Class.php');
+require_once(dirname(__FILE__) . '/Utils/Array.php');
+require_once(dirname(__FILE__) . '/DocTest/PHPFinder.php');
+require_once(dirname(__FILE__) . '/DocTest/UpdateChecker.php');
+require_once(dirname(__FILE__) . '/DocTest/Parser.php');
+require_once(dirname(__FILE__) . '/DocTest/Builder.php');
+require_once(dirname(__FILE__) . '/DocTest/TestCaseFinder.php');
+require_once(dirname(__FILE__) . '/DocTest/Runner.php');
+
+/**
+ * 指定されたディレクトリにあるPHPファイルに対するテストを実行
+ *
+ * @category   Testing
+ * @package    Maple4_DocTest
+ * @author     TAKAHASHI Kunihiko <kunit@maple-project.com>
+ * @copyright  2008 Maple Project
+ * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
+ * @version    Release: @package_version@
+ * @since      Class available since Release 0.1.0
+ */
+class Maple4_DocTest
+{
+    /**
+     * @var object Maple4_Utils_Fileのインスタンス
+     * @access private
+     */
+    private $fileUtils;
+
+    /**
+     * @var object Maple4_Utils_Classのインスタンス
+     * @access private
+     */
+    private $classUtils;
+
+    /**
+     * コンストラクタ
+     */
+    public function __construct()
+    {
+        $this->fileUtils = new Maple4_Utils_File();
+        $this->classUtils = new Maple4_Utils_Class();
+    }
+
+    /**
+     * fluent interface(流れるようなインタフェースで使用するための
+     * スタティックコンストラクタ
+     *
+     * @return object このオブジェクト自身
+     */
+    static public function create()
+    {
+        return new self();
+    }
+
+    /**
+     * 指定されたディレクトリにあるPHPファイルに対するテストを実行
+     *
+     * @param string $pathname テスト対象となるディレクトリ
+     * @param array $options テストで使用するオプション
+     * @return string 実行結果
+     * @access public
+     */
+    public function run($pathname, $options)
+    {
+        if (!isset($options['compileDir'])) {
+            throw new Maple4_Exception('compileDir not set');
+        }
+
+        $phpFiles = Maple4_DocTest_PHPFinder::create()->find($options, $pathname);
+        $updateFiles = Maple4_DocTest_UpdateChecker::create()->check($options, $pathname, $phpFiles);
+        $this->updateTestCases($options, $updateFiles);
+
+        if ($this->isUpdated($options, $updateFiles)) {
+            $testcases = $this->findTestCases($options, $pathname, $phpFiles);
+        } else {
+            $testcases = array();
+        }
+
+        if (count($testcases) > 0) {
+            Maple4_DocTest_Runner::create()->run($options, $pathname, $testcases);
+        }
+    }
+
+    /**
+     * テストケースファイルの更新を行う
+     *
+     * @param array $options テストで使用するオプション
+     * @param array $updateFiles 更新ファイルリスト
+     * @access private
+     */
+    private function updateTestcases($options, $updateFiles)
+    {
+        $compileDir = $options['compileDir'];
+        $this->fileUtils->makeDir($compileDir);
+
+        if (isset($updateFiles['delete']) &&
+            (count($updateFiles['delete'] > 0))) {
+            foreach ($updateFiles['delete'] as $realpath => $path) {
+                $filename = $this->toTestCase($options, $path);
+                $this->fileUtils->unlink($filename);
+            }
+        }
+
+        $parser = new Maple4_DocTest_Parser();
+        $builder = new Maple4_DocTest_Builder();
+
+        foreach (array('new', 'update') as $key) {
+            if (!isset($updateFiles[$key])) {
+                continue;
+            }
+
+            foreach ($updateFiles[$key] as $realpath => $path) {
+                $comments = $parser->parse($realpath, $path);
+                $data = $builder->build($realpath, $path, $comments);
+                if (is_null($data)) {
+                    continue;
+                }
+
+                $filename = $this->toTestCase($options, $path);
+                $this->fileUtils->write($filename, $data);
+            }
+        }
+    }
+
+    /**
+     * テストケースのファイルリストを取得する
+     *
+     * @param array $options テストで使用するオプション
+     * @param string $pathname PHPファイル名
+     * @param array $files PHPFinderが検索したファイルリスト
+     * @return array テストケースのファイルリスト
+     * @access private
+     */
+    private function findTestCases($options, $pathname, $files)
+    {
+        // ファイル単体で指定された場合のための対策
+        // 余計なファイル探索が走らないように
+        if (preg_match('|\.php$|', $pathname)) {
+            $testcases = Maple4_DocTest_TestCaseFinder::create()->find($options, $this->convertTestCase($options, $pathname, $files));
+        } else {
+            $list = Maple4_DocTest_TestCaseFinder::create()->find($options, $options['compileDir']);
+
+            $pickup = array();
+            foreach ($files as $realpath => $path) {
+                $filename = $this->toTestCase($options, $path);
+                if (isset($list[$filename])) {
+                    $pickup[$filename] = $list[$filename];
+                }
+            }
+
+            $testcases = $pickup;
+        }
+
+        return $testcases;
+    }
+
+    /**
+     * 指定されたPHPファイルからテストケースファイル名を生成する
+     *
+     * @param array $options テストで使用するオプション
+     * @param string $pathname PHPファイル名
+     * @param array $files PHPFinderが検索したファイルリスト
+     * @return string テストケースのファイル名
+     * @access private
+     */
+    private function convertTestCase($options, $pathname, $files)
+    {
+        $pathname = $this->fileUtils->fixDirectorySeparator($pathname);
+
+        $result = null;
+        foreach ($files as $realpath => $path) {
+            $realpath = $this->fileUtils->fixDirectorySeparator($realpath);
+            if ($pathname === $realpath) {
+                $result = $this->toTestCase($options, $path);
+                break;
+            }
+        }
+
+         return $result;
+    }
+
+    /**
+     * PHPファイル名からテストケースファイル名を生成する
+     *
+     * @param array $options テストで使用するオプション
+     * @param string $pathname PHPファイル名
+     * @return string テストケースのファイル名
+     * @access private
+     */
+    private function toTestCase($options, $pathname)
+    {
+        $compileDir = $options['compileDir'];
+        $classname = $this->classUtils->toClassname($pathname);
+        $filename = "{$compileDir}/Maple4_DocTest_{$classname}Test.php";
+
+        return $this->fileUtils->fixDirectorySeparator($filename);
+    }
+
+    /**
+     * 更新されたファイルがあるか?
+     *
+     * @param array $options テストで使用するオプション
+     * @param array $files UpdateCheckerが生成したファイルリスト
+     * @return boolean 更新されたファイルがあるか?
+     * @access private
+     */
+    private function isUpdated($options, $files)
+    {
+        if (!isset($options['forceRun']) || $options['forceRun']) {
+            return true;
+        }
+
+        $files = new Maple4_Utils_Array($files);
+
+        if (!is_array($files->new) ||
+            !is_array($files->update) ||
+            !is_array($files->delete)) {
+            return false;
+        }
+
+        return ((count($files->new) > 0) ||
+                (count($files->update) > 0) ||
+                (count($files->delete) > 0));
+    }
+}

Maple4_DocTest/tags/0.2.0-alpha/package.xml

@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.7.2" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+    http://pear.php.net/dtd/tasks-1.0.xsd
+    http://pear.php.net/dtd/package-2.0
+    http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Maple4_DocTest</name>
+ <channel>__uri</channel>
+ <summary>TDD/BDD Tool for PHPUnit3</summary>
+ <description>TDD/BDD Tool for PHPUnit3</description>
+ <lead>
+  <name>TAKAHASHI Kunihiko</name>
+  <user>kunit</user>
+  <email>kunit@maple-project.com</email>
+  <active>yes</active>
+ </lead>
+ <date>2008-08-20</date>
+ <time>20:56:28</time>
+ <version>
+  <release>0.2.0</release>
+  <api>0.2.0</api>
+ </version>
+ <stability>
+  <release>alpha</release>
+  <api>alpha</api>
+ </stability>
+ <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD License</license>
+ <notes>TDD/BDD Tool for PHPUnit3</notes>
+ <contents>
+  <dir baseinstalldir="/" name="/">
+   <dir name="Maple4">
+    <dir name="DocTest">
+     <dir name="Builder">
+      <file name="class.tpl" role="php">
+       <tasks:replace from="@package_version@" to="version" type="package-info" />
+      </file>
+      <file name="file.tpl" role="php">
+       <tasks:replace from="@package_version@" to="version" type="package-info" />
+      </file>
+      <file name="method.tpl" role="php">
+       <tasks:replace from="@package_version@" to="version" type="package-info" />
+      </file>
+      <file name="specialMethod.tpl" role="php">
+       <tasks:replace from="@package_version@" to="version" type="package-info" />
+      </file>
+     </dir> <!-- //Maple4/DocTest/Builder -->
+     <dir name="Runner">
+      <file name="ResultPrinter.php" role="php">
+       <tasks:replace from="@package_version@" to="version" type="package-info" />
+      </file>
+     </dir> <!-- //Maple4/DocTest/Runner -->
+     <file name="Builder.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="Finder.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="Parser.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="PHPFinder.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="Runner.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="TestCaseFinder.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="UpdateChecker.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+    </dir> <!-- //Maple4/DocTest -->
+    <dir name="Utils">
+     <file name="Array.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="Class.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+     <file name="File.php" role="php">
+      <tasks:replace from="@package_version@" to="version" type="package-info" />
+     </file>
+    </dir> <!-- //Maple4/Utils -->
+    <file name="DocTest.php" role="php">
+     <tasks:replace from="@package_version@" to="version" type="package-info" />
+    </file>
+    <file name="Exception.php" role="php">
+     <tasks:replace from="@package_version@" to="version" type="package-info" />
+    </file>
+   </dir> <!-- //Maple4 -->
+  </dir> <!-- / -->
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.1.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.4.3</min>
+   </pearinstaller>
+   <package>
+    <name>PEAR</name>
+    <channel>pear.php.net</channel>
+    <min>1.4.3</min>
+   </package>
+   <package>
+    <name>PHPUnit</name>
+    <channel>pear.phpunit.de</channel>
+    <min>3.2.21</min>
+   </package>
+  </required>
+  <optional>
+   <package>
+    <name>Net_Growl</name>
+    <channel>pear.php.net</channel>
+    <min>0.7.0</min>
+   </package>
+  </optional>
+ </dependencies>
+ <phprelease />
+ <changelog>
+  <release>
+   <version>
+    <release>0.2.0</release>
+    <api>0.2.0</api>
+   </version>
+   <stability>
+    <release>alpha</release>
+    <api>alpha</api>
+   </stability>
+   <date>2008-08-20</date>
+   <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD License</license>
+   <notes>TDD/BDD Tool for PHPUnit3</notes>
+  </release>
+ </changelog>
+</package>