630 lines
9.5 KiB
PHP
630 lines
9.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @package Unit Test
|
|
* @author Geoffrey Sneddon <geoffers@gmail.com>
|
|
* @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);
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|