<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * PHP Version 5
 *
 * Copyright (c) 2002-2006, Sebastian Bergmann <sb@sebastian-bergmann.de>.
 * 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, STRIC
 * 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    PHPUnit2
 * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @copyright  2002-2006 Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @version    CVS: $Id$
 * @link       http://pear.php.net/package/PHPUnit2
 * @since      File available since Release 2.1.0
 */

/**
 * Class for creating a PHPUnit2_Framework_TestCase skeleton file.
 *
 * This class will take a classname as a parameter on construction and will
 * create a PHP file that contains the skeleton of a PHPUnit2_Framework_TestCase
 * subclass.
 *
 * <code>
 * <?php
 * require_once 'PHPUnit2/Util/Skeleton.php';
 *
 * $skeleton = new PHPUnit2_Util_Skeleton(
 *   'PHPUnit2_Util_Skeleton',
 *   'PHPUnit2/Util/Skeleton.php'
 * );
 *
 * $skeleton->write();
 * ?>
 * </code>
 *
 * @category   Testing
 * @package    PHPUnit2
 * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @copyright  2002-2006 Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
 * @version    Release: 2.3.6
 * @link       http://pear.php.net/package/PHPUnit2
 * @since      Class available since Release 2.1.0
 */
class PHPUnit2_Util_Skeleton {
    protected $templateClassHeader =
'<?php
// Call {className}Test::main() if this source file is executed directly.
if (!defined("PHPUnit2_MAIN_METHOD")) {
    define("PHPUnit2_MAIN_METHOD", "{className}Test::main");
}

require_once "PHPUnit2/Framework/TestCase.php";
require_once "PHPUnit2/Framework/TestSuite.php";

// You may remove the following line when all tests have been implemented.
require_once "PHPUnit2/Framework/IncompleteTestError.php";

require_once "{classFile}";

/**
 * Test class for {className}.
 * Generated by PHPUnit2_Util_Skeleton on {date} at {time}.
 */
class {className}Test extends PHPUnit2_Framework_TestCase {
    /**
     * Runs the test methods of this class.
     *
     * @access public
     * @static
     */
    public static function main() {
        require_once "PHPUnit2/TextUI/TestRunner.php";

        $suite  = new PHPUnit2_Framework_TestSuite("{className}Test");
        $result = PHPUnit2_TextUI_TestRunner::run($suite);
    }

    /**
     * Sets up the fixture, for example, open a network connection.
     * This method is called before a test is executed.
     *
     * @access protected
     */
    protected function setUp() {
    }

    /**
     * Tears down the fixture, for example, close a network connection.
     * This method is called after a test is executed.
     *
     * @access protected
     */
    protected function tearDown() {
    }
';

    protected $templateClassFooter =
'}

// Call {className}Test::main() if this source file is executed directly.
if (PHPUnit2_MAIN_METHOD == "{className}Test::main") {
    {className}Test::main();
}
?>
';

    protected $templateMethod =
'
    /**
     * @todo Implement test{methodName}().
     */
    public function test{methodName}() {
        // Remove the following line when you implement this test.
        throw new PHPUnit2_Framework_IncompleteTestError;
    }
';

    /**
     * @var    string
     * @access protected
     */
    protected $className;

    /**
     * @var    string
     * @access protected
     */
    protected $classSourceFile;

    /**
     * Constructor.
     *
     * @param  string  $className
     * @param  string  $classSourceFile
     * @throws Exception
     * @access public
     */
    public function __construct($className, $classSourceFile = '') {
        if ($classSourceFile == '') {
            $classSourceFile = $className . '.php';
        }

        if (file_exists($classSourceFile)) {
            $this->classSourceFile = $classSourceFile;
        } else {
            throw new Exception(
              sprintf(
                'Could not open %s.',

                $classSourceFile
              )
            );
        }

        @include_once $this->classSourceFile;

        if (class_exists($className)) {
            $this->className = $className;
        } else {
            throw new Exception(
              sprintf(
                'Could not find class "%s" in %s.',

                $className,
                $classSourceFile
              )
            );
        }
    }

    /**
     * Generates the test class' source.
     *
     * @return string
     * @access public
     */
    public function generate() {
        $testClassSource = $this->testClassHeader($this->className, $this->classSourceFile);

        $class = new ReflectionClass($this->className);

        foreach ($class->getMethods() as $method) {
            if (!$method->isConstructor() &&
                !$method->isAbstract() &&
                 $method->isUserDefined() &&
                 $method->isPublic() &&
                 $method->getDeclaringClass()->getName() == $this->className) {
                $testClassSource .= $this->testMethod($method->getName());
            }
        }

        $testClassSource .= $this->testClassFooter($this->className);

        return $testClassSource;
    }

    /**
     * Generates the test class and writes it to a source file.
     *
     * @param  string  $file
     * @access public
     */
    public function write($file = '') {
        if ($file == '') {
            $file = $this->className . 'Test.php';
        }

        if ($fp = @fopen($file, 'w')) {
            @fputs($fp, $this->generate());
            @fclose($fp);
        }
    }

    /**
     * Sets the templates for class header, class footer, and method.
     *
     * @param  string  $classHeader
     * @param  string  $classFooter
     * @param  string  $method
     * @access public
     * @since  Method available since Release 2.2.0
     */
    public function setTemplates($classHeader, $classFooter, $method) {
        if (is_file($classHeader)) {
            $this->templateClassHeader = file_get_contents($classHeader);
        } else {
            $this->templateClassHeader = $classHeader;
        }

        if (is_file($classFooter)) {
            $this->templateClassFooter = file_get_contents($classFooter);
        } else {
            $this->templateClassFooter = $classFooter;
        }

        if (is_file($method)) {
            $this->templateMethod = file_get_contents($method);
        } else {
            $this->templateMethod = $method;
        }
    }

    /**
     * @param  string  $className
     * @param  string  $classSourceFile
     * @access protected
     */
    protected function testClassHeader($className, $classSourceFile) {
        return str_replace(
          array(
            '{className}',
            '{classFile}',
            '{date}',
            '{time}'
          ),
          array(
            $className,
            $classSourceFile,
            date('Y-m-d'),
            date('H:i:s')
          ),
          $this->templateClassHeader
        );
    }

    /**
     * @param  string  $className
     * @access protected
     */
    protected function testClassFooter($className) {
        return str_replace(
          array(
            '{className}'
          ),
          array(
            $className
          ),
          $this->templateClassFooter
        );
    }

    /**
     * @param  string  $methodName
     * @access protected
     */
    protected function testMethod($methodName) {
        return str_replace(
          array(
            '{methodName}'
          ),
          array(
            ucfirst($methodName)
          ),
          $this->templateMethod
        );
    }
}

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * c-hanging-comment-ender-p: nil
 * End:
 */
?>
