CodeView

PHPUnit test harness for Komodo

DebugRun.php

<?php
 
    /**
     * @author  Michael Heim
     * @link    http://www.zeilenwechsel.de/
     * @version 1.2.0
     * @license http://www.zeilenwechsel.de/it/code/browse/komodo-phpunit-harness/prod/license.txt
     */
 
    /**
     * Manages a debug run.
     */
    class DebugRun {
 
        /* @var string    the full path to the testrunner */
        protected $testrunner_filepath;
 
        /**
         * $dummytest_filepath and $dummytest_xml_dir are mutually exclusive. Only one
         * of them must be set.
         *
         * @var string    the full path to the file containing the dummy tests
         */
        protected $dummytest_filepath;
 
        /* @var string    the dir used for locating an XML config file */
        protected $dummytest_xml_dir;
 
        /* @var string    only used with a test file (ie if $dummytest_filepath is set) */
        protected $dummytest_classname;
 
        /* @var string */
        protected $do_not_print;
 
 
        public function __construct () {
 
            $this->init()
                 ->set_testrunner( 'Base/run.php' );
 
        }
 
        /**
         * Starts the debug run.
         *
         * The $dummy_test can specify a test file (with a file path) or an XML
         * configuration (with a dir path). See set_dummy_test_config() for details.
         *
         * go() returns the output as a string; it also prints it by default. Printing
         * can be suppressed with do_not_print_output() (surprise!).
         *
         * @param  string $dummy_test  a file path to a test file, or a dir path for an XML configuration
         * @param  array  $switches
         *
         * @return string  the output of the debug run
         */
        public function go ( $dummy_test, array $switches ) {
 
            // Boilerplate/Setup
            $this->create_harness_environment( $dummy_test, $switches )
                 ->set_working_dir();
 
            // Run the test harness
            ob_start();
            require( $this->testrunner_filepath );
            $output = ob_get_clean();
 
            if ( ! $this->do_not_print ) print $output;
 
            return $output;
 
        }
 
        /**
         * Creates the test environment (ie, the appropriate setup of the server vars)
         * for a test file or XML configuration. Doesn not need to be called if go()
         * is used.
         *
         * The $dummy_test can specify a test file (with a file path) or an XML
         * configuration (with a dir path). See set_dummy_test_config() for details.
         *
         * @param  string $dummy_test  a file path to a test file, or a dir path for an XML configuration
         * @param  string $switches
         *
         * @return DebugRun
         */
        public function create_harness_environment ( $dummy_test, $switches ) {
 
            // Make sure PHPUnit does not exit after it has run, using a custom environment variable introduced
            // in the PHPUnitXt_Base_Command class. This allows to overrule the setting passed in with the $exit
            // parameter. 
            putenv( 'PHPUNIT_COMMAND_NOEXIT=true' );
 
            $this->set_dummy_test_config( $dummy_test )
                 ->create_server_vars( $switches );
 
            return $this;
 
        }
 
        /**
         * If called, go() will not print the output and return it only as a string.
         * By default, it is printed as well.
         *
         * @return DebugRun
         */
        public function do_not_print_output () {
 
            $this->do_not_print = true;
 
            return $this;
 
        }
 
        /**
         * Overrides the default test runner defined in the constructor.
         * 
         * @param  string $path_from_harness_root   path from test harness root, without leading slash
         * @return DebugRun
         */
        public function set_testrunner ( $path_from_harness_root ) {
 
            $this->testrunner_filepath = PHPUnitXt_Utils_Loader::root() . "/$path_from_harness_root";
            return $this;
 
        }
 
        /**
         * Returns the full file path to the testrunner.
         *
         * Used for logging.
         *
         * @return string
         */
        public function get_testrunner_path () {
 
            return $this->testrunner_filepath;
 
        }
 
        /**
         * @return DebugRun
         */
        protected function init () {
 
            require_once( HARNESS_LOADER );
            return $this;
 
        }
 
        /**
         * Sets the test file or XML configuration to be used for the debug run.
         *
         * If the $dummy_test is a file path, it is treated as a test file.
         *
         * If it is a directory, an XML configuration is assumed. The harness will
         * either use a  default config file in the directory, or expect the config to
         * be passed as an argument with the -c/--configuration switch. In that case,
         * the directory is used to resolve a relative path to the config file.
         *
         * Trailing slashes are removed automatically from directory paths.
         *
         * @param  string $dummy_test
         * @return DebugRun
         */
        protected function set_dummy_test_config ( $dummy_test ) {
 
            $dummy_test = Tools::remove_trailing_slash( $dummy_test );
 
            if ( is_dir( $dummy_test ) ) {
 
                // Directory: do a debug run for an XML config file.
                $this->dummytest_xml_dir = $dummy_test;
 
            } else {
 
                // Anything else is treated as a file path to a PHPUnit test file.
                $this->dummytest_filepath  = $dummy_test;
                $this->dummytest_classname = $this->get_classname( $dummy_test );
 
            }
 
            return $this;
 
        }
 
        /**
         * Sets up $_SERVER[ 'argv' ] and $_SERVER[ 'argc' ] with the content of the command line
         * corresponding to a given set of arguments.
         * 
         * @param  array     $arg_array     the argument set the $_SERVER vars are generated for. 
         * @return DebugRun
         */
        protected function create_server_vars ( array $arg_array ) {
 
            if ( empty( $this->testrunner_filepath ) ) throw new BadMethodCallException ( 'Testrunner filepath must be set' );
 
            $_SERVER[ 'argv' ]   = array();               // Clear existing content.
            $_SERVER[ 'argv' ][] = $this->testrunner_filepath;
            $_SERVER[ 'argv' ]   = array_merge( $_SERVER[ 'argv' ], $arg_array );
            if ( isset( $this->dummytest_filepath ) ) {
 
                $_SERVER[ 'argv' ][] = $this->dummytest_classname;
                $_SERVER[ 'argv' ][] = $this->dummytest_filepath;
 
            }
 
            $_SERVER[ 'argc' ] = count( $_SERVER[ 'argv' ] );
 
            return $this;
 
        }
 
        /**
         * @return DebugRun
         */
        protected function set_working_dir () {
 
            if ( isset( $this->dummytest_xml_dir  )) {
 
                chdir ( $this->dummytest_xml_dir );
 
            } else {
 
                chdir( dirname( $_SERVER['SCRIPT_FILENAME'] ) );
 
            }
 
            return $this;
 
        }
 
        /**
         * Returns the name of the class defined in a given PHP file.
         * 
         * Throws an exception if the file doesn't contain a class definition.
         * 
         * @param  string $filepath  the path to the PHP file
         * @return string            the name of the class
         */
        protected function get_classname ( $filepath ) {
 
            $content = file_get_contents( $filepath );
            $content = str_replace( "\r\n", "\n", $content );   // normalize line endings
 
            if ( preg_match( '%^\s*class +(\w+) .*{ *$%m', $content, $matches ) ) {
 
                $classname = $matches[1];
 
            } else {
 
                throw new Exception( "No class found in $filepath" );
 
            }
 
            return $classname;
 
        }
 
        /**
         * Creates the class and starts the debug run.
         *
         * See go() for details.
         *
         * @param  string $dummy_test  a file path to a test file, or a dir path for an XML configuration
         * @param  array  $switches
         *
         * @return string  the output of the debug run
         */
        public static function exec ( $dummy_test, array $switches ) {
 
            $debug_run = new self();
            return $debug_run->go( $dummy_test, $switches );
 
        }
 
    }
 
?>