* @version $Id: unit_test2.php 16 2007-08-08 14:52:36Z gsnedders $ * @license http://www.opensource.org/licenses/zlib-license.php zlib/libpng license * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License * @copyright Copyright © 2007, Geoffrey Sneddon */ /** * Unit Test * * @abstract * @package Unit Test */ class Unit_Test2 { /** * Sets whether this class is a unit test or not * * @access protected * @var bool */ var $test = true; /** * Test name * * @access protected * @var mixed */ var $name; /** * Test data * * @access protected * @var mixed */ var $data; /** * Expected result * * @access protected * @var mixed */ var $expected; /** * Test result * * @access protected * @var mixed */ var $result; /** * Number of tests passed * * @access protected * @var int */ var $passes = 0; /** * Number of tests failed * * @access protected * @var int */ var $fails = 0; /** * Set the test name to the class name by default, replacing "_" with " " */ function Unit_Test2() { $this->name = str_replace('_', ' ', get_class($this)); } /** * Whether this class is a test * * @final * @access public * @return bool */ function is_test() { return (bool) $this->test; } /** * Test name * * @final * @access public * @return mixed */ function name() { return $this->name; } /** * Number of tests passed * * @final * @access public * @return int */ function passes() { return (int) $this->passes; } /** * Number of tests failed * * @final * @access public * @return int */ function fails() { return (int) $this->fails; } /** * Total number of tests * * @final * @access public * @return int */ function total() { return $this->passes() + $this->fails(); } /** * Run the test * * @final * @access public */ function run() { $this->init(); $this->data(); $this->expected(); $this->test(); $this->result(); } /** * First method called when running the test * * This isn't defined as abstract as it's optional * * @access protected */ function init() { } /** * Set Unit_Test2::$data * * @abstract * @access protected * @see Unit_Test2::$data */ function data() { } /** * Set Unit_Test2::$expected * * @abstract * @access protected * @see Unit_Test2::$expected */ function expected() { } /** * Actually run the test (should set Unit_Test::$result) * * @abstract * @access protected * @see Unit_Test2::$result */ function test() { } /** * Check whether the result is valid (should call Unit_Test2::pass() or Unit_Test2::fail()) * * @abstract * @access protected * @see Unit_Test2::$expected * @see Unit_Test2::$result */ function result() { } /** * Process a pass * * @access protected */ function pass() { $this->passes++; } /** * Process a fail * * @access protected */ function fail() { $this->fails++; } } /** * Unit Test Group * * @package Unit Test */ class Unit_Test2_Group { /** * Unit Test Group Name * * @access protected * @var mixed */ var $name; /** * Tests * * @access protected * @var array */ var $tests = array(array()); /** * Number of tests passed * * @access protected * @var int */ var $passes = 0; /** * Number of tests failed * * @access protected * @var int */ var $fails = 0; /** * Time taken to run tests * * @access protected * @var float */ var $time = 0.0; /** * Create Unit Test Group * * @access public * @param string $name Unit Test Group Name */ function Unit_Test2_Group($name) { $this->name = $name; } /** * Unit Test Group Name * * @final * @access public * @return mixed */ function name() { return $this->name; } /** * Number of tests passed * * @final * @access public * @return int */ function passes() { return (int) $this->passes; } /** * Number of tests failed * * @final * @access public * @return int */ function fails() { return (int) $this->fails; } /** * Total number of tests * * @final * @access public * @return int */ function total() { return $this->passes() + $this->fails(); } /** * Time to run tests * * @final * @access public * @return float */ function time() { return (float) $this->time; } /** * Add a test (a Unit_Test2 child, or a Unit_Test2_Group) * * @access public * @param object $test Test to add */ function add($test) { $this->tests[$test->name()][] = $test; } /** * Remove a test * * @access public * @param string $name Test name */ function remove($name) { unset($this->tests[$name]); } /** * Load tests in folder * * This loads all the Unit_Test2 classes within files with the same * extension as this file within the specified folder * * @access public * @param string $folder Folder name */ function load_folder($folder) { static $extension = null; if (!$extension) { $extension = pathinfo(__FILE__, PATHINFO_EXTENSION); } $files = Unit_Test2_Files::get_files($folder); $count_classes = count(get_declared_classes()); foreach ($files as $file) { if (is_file($file) && pathinfo($file, PATHINFO_EXTENSION) === $extension) { include $file; } } $classes = array_slice(get_declared_classes(), $count_classes); foreach ($classes as $class) { if ($this->is_subclass_of($class, 'Unit_Test2')) { $class = new $class; if ($class->is_test()) { $this->add($class); } } } } /** * Run the tests * * @access public */ function run() { $this->pre(); $start_time = $this->microtime(true); foreach ($this->tests as $tests) { foreach ($tests as $test) { if ($this->is_a($test, 'Unit_Test2') || $this->is_a($test, 'Unit_Test2_Group')) { $test->run(); $this->passes += $test->passes(); $this->fails += $test->fails(); } } } $this->time = $this->microtime(true) - $start_time; $this->post(); } /** * Executed before the tests are executed * * @abstract * @access protected */ function pre() { } /** * Executed after the tests are executed * * @abstract * @access protected */ function post() { } /** * Re-implementation of PHP 5.0.3's is_subclass_of() * * @access public * @param mixed $object * @param string $class_name */ function is_subclass_of($object, $class_name) { if (func_num_args() != 2) { trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING); } else { if (version_compare(phpversion(), '5.0.3', '>=') || is_object($object)) { return is_subclass_of($object, $class_name); } else if (is_string($object) && is_string($class_name)) { if (class_exists($object)) { if (class_exists($class_name)) { $class_name = strtolower($class_name); while ($object = strtolower(get_parent_class($object))) { if ($object == $class_name) { return true; } } } } else { trigger_error('Unknown class passed as parameter', E_USER_WARNNG); } } return false; } } /** * Re-implementation of PHP 4.2.0's is_a() * * @access public * @param object $object The tested object * @param string $class_name The class name * @return bool Returns true if the object is of this class or has this class as one of its parents, false otherwise */ function is_a($object, $class_name) { if (function_exists('is_a')) { return is_a($object, $class_name); } elseif (!is_object($object)) { return false; } elseif (get_class($object) == strtolower($class_name)) { return true; } else { return is_subclass_of($object, $class_name); } } /** * Re-implementation of PHP 5's microtime() * * @access public * @param bool $get_as_float */ function microtime($get_as_float = false) { if ($get_as_float) { if (is_float($time = microtime(true))) { return $time; } else { list($user, $sec) = explode(' ', $time); return ((float) $user + (float) $sec); } } else { // PHP6 will likely return a float by default, so explicitly pass false (this is just ignored under PHP < 5) return microtime(false); } } } /** * File listing class * * @package Unit Test */ class Unit_Test2_Files { /** * Get a list of files/folders within $dir * * @static * @access public * @param string $dir Folder to get listing for * @return array */ function get_files($dir) { $files = array(); if ($dh = opendir($dir)) { while (($file = readdir($dh)) !== false) { if (substr($file, 0, 1) != '.') { $files[] = "$dir/$file"; } } closedir($dh); usort($files, array(__CLASS__, 'sort_files')); foreach ($files as $file) { if (is_dir($file)) { array_splice($files, array_search($file, $files), 0, Unit_Test2_Files::get_files($file)); } } } return $files; } /** * Sort files/folders with files listed before inner folders * * @static * @access public * @param string $a File/folder 1 * @param string $b File/folder 2 * @return int */ function sort_files($a, $b) { if (is_dir($a) && is_dir($b)) { return strnatcmp($a, $b); } else if (is_dir($a)) { return 1; } else if (is_dir($b)) { return -1; } else { return strnatcmp($a, $b); } } } ?>