Diff
Added: library/trunk/README.txt ( => )
Added: library/trunk/docs/TODO.txt
===================================================================
--- library/trunk/docs/TODO.txt 2006-08-07 13:45:57 UTC (rev 1)
+++ library/trunk/docs/TODO.txt 2006-08-09 14:26:37 UTC (rev 2)
@@ -0,0 +1,15 @@
+Test:
+
+1) Testing
+ a) timing/profiling
+ b) code coverage (xdebug)
+ c) runner script with setup options for reporting
+ d) line count/comment/stripping information
+ e) Reflection for grabbing assert statement? Invoking method?
+2) Reporting
+ a) html, xml, text formatting
+ b) echo, store as files (compressed or not), email (compressed or not)
+ c) profiling, code coverage, timing, line/comment count information
+
+Runner needs to - run via command line (shebang or windows assoc or bat or shortcut)
+and run via html (and tell the difference) - allow command line args, get or post args, php, ini, xml files or forms/command line questions for setup, needs to register any observers and run any/all suites requested...sigh.. and do any/all including on top of that
\ No newline at end of file
Added: library/trunk/lib/test/error.class.php (1 => 2)
--- library/trunk/lib/test/error.class.php 2006-08-07 13:45:57 UTC (rev 1)
+++ library/trunk/lib/test/error.class.php 2006-08-09 14:26:37 UTC (rev 2)
@@ -0,0 +1,46 @@
+<?php
+/**
+ * error.class.php - exception class to move php errors into exceptions
+ *
+ * wrapper for unit testing php error -> exception conversion
+ *
+ * This is released under the GPL, see license.txt for details
+ *
+ * @author Elizabeth Smith <emsmith@callicore.net>
+ * @copyright Elizabeth Smith (c)2006
+ * @link http://callicore.net
+ * @license http://www.opensource.org/licenses/gpl-license.php GPL
+ * @version $Id$
+ * @since Php 5.2.0
+ * @package callicore library
+ * @subpackage test
+ * @category lib
+ * @filesource
+ */
+
+/**
+ * CCL_TestError - wrapper for php error -> exception conversion
+ */
+class CCL_TestError extends Exception
+{
+ /**
+ * public function __construct
+ *
+ * throw php error information into an exception
+ *
+ * @param string $message error message
+ * @param int $code error level
+ * @param string $file error location
+ * @param int $line error line number
+ * @param array $trace backtrace for exception
+ * @return void
+ */
+ public function __construct($message, $code, $file, $line, $trace)
+ {
+ parent::__construct($message, $code);
+ $this->file = $file;
+ $this->line = $line;
+ $this->trace = $trace;
+ }
+}
+?>
\ No newline at end of file
Added: library/trunk/lib/test/result.class.php (1 => 2)
--- library/trunk/lib/test/result.class.php 2006-08-07 13:45:57 UTC (rev 1)
+++ library/trunk/lib/test/result.class.php 2006-08-09 14:26:37 UTC (rev 2)
@@ -0,0 +1,431 @@
+<?php
+/**
+ * result.class.php - class holding the final results for the test
+ *
+ * contains basic information about the results of the test
+ *
+ * This is released under the GPL, see license.txt for details
+ *
+ * @author Elizabeth Smith <emsmith@callicore.net>
+ * @copyright Elizabeth Smith (c)2006
+ * @link http://callicore.net
+ * @license http://www.opensource.org/licenses/gpl-license.php GPL
+ * @version $Id$
+ * @since Php 5.2.0
+ * @package callicore library
+ * @subpackage test
+ * @category lib
+ * @filesource
+ */
+
+/**
+ * CCL_TestResult - result class for testing
+ *
+ * Handles report generation and consolidating test results
+ */
+class CCL_TestResult implements SplSubject, Countable
+{
+
+ /**
+ * holding observers to call
+ * @var $observers array of observers
+ */
+ private $observers = array();
+
+ /**
+ * last call name
+ * @var $call string
+ */
+ protected $call;
+
+ /**
+ * argument stored from last call
+ * @var $arg mixed
+ */
+ protected $arg;
+
+ /**
+ * count the number of test units shown
+ * @var $units int
+ */
+ protected $units = 0;
+
+ /**
+ * total number of tests completed, for countable
+ * @var $total int
+ */
+ protected $total = 0;
+
+ /**
+ * count the number of tests shown
+ * @var $tests int
+ */
+ protected $tests = 0;
+
+ /**
+ * count the number of tests
+ * @var $testpass int
+ */
+ protected $testpass = 0;
+
+ /**
+ * count the number of tests
+ * @var $testsfail int
+ */
+ protected $testsfail = 0;
+
+ /**
+ * count the number of tests
+ * @var $testsskip int
+ */
+ protected $testsskip = 0;
+
+ /**
+ * count the number of tests
+ * @var $testserror int
+ */
+ protected $testserror = 0;
+
+ /**
+ * count the number of tests
+ * @var $testsimcomplete int
+ */
+ protected $testsimcomplete = 0;
+
+ /**
+ * count the number of assertions
+ * @var $asserts int
+ */
+ protected $asserts = 0;
+
+ /**
+ * count the number of assertions
+ * @var $assertspass int
+ */
+ protected $assertspass = 0;
+
+ /**
+ * count the number of assertions
+ * @var $assertsfail int
+ */
+ protected $assertsfail = 0;
+
+
+ //----------------------------------------------------------------
+ // Observer Handling
+ //----------------------------------------------------------------
+
+ /**
+ * public function attach
+ *
+ * registers observer object
+ *
+ * @param object $observer object to add to observer stack
+ * @return void
+ */
+ public function attach(SplObserver $observer)
+ {
+ $this->observers[] = $observer;
+ }
+
+ /**
+ * public function detach
+ *
+ * removes a specific observer
+ *
+ * @param object $observer object to remove to observer stack
+ * @return void
+ */
+ public function detach(SplObserver $observer)
+ {
+ $key = array_search($observer, $this->observers);
+ if($key !== FALSE && isset($this->observers[$key]))
+ {
+ unset($this->observers[$key]);
+ }
+ }
+
+ /**
+ * public function notify
+ *
+ * notify all the observers that a change has been made
+ *
+ * @return void
+ */
+ public function notify()
+ {
+ foreach ($this->observers as $object)
+ {
+ $object->update($this);
+ }
+ }
+
+ /**
+ * public function count
+ *
+ * count the total number of tests
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return $this->total;
+ }
+
+ //----------------------------------------------------------------
+ // testUnit Methods
+ //----------------------------------------------------------------
+
+ /**
+ * public function startTestUnit
+ *
+ * signals the start of a testUnit
+ *
+ * @param string $name name of the testUnit running
+ * @return void
+ */
+ public function startTestUnit($name)
+ {
+ $this->call = 'startTestUnit';
+ $this->arg = $name;
+ $this->tests = 0;
+ $this->testspass = 0;
+ $this->testsfail = 0;
+ $this->testserror = 0;
+ $this->testsskip = 0;
+ $this->testsincomplete = 0;
+ $this->units++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function endTestUnit
+ *
+ * signals a completed testUnit
+ *
+ * @return void
+ */
+ public function endTestUnit()
+ {
+ $this->call = 'endTestUnit';
+ $this->arg = NULL;
+ $this->notify();
+ return;
+ }
+
+ //----------------------------------------------------------------
+ // Test Methods
+ //----------------------------------------------------------------
+
+ /**
+ * public function startTest
+ *
+ * signals the start of a test
+ *
+ * @param string $name name of the test
+ * @return void
+ */
+ public function startTest($name)
+ {
+ $this->call = 'startTest';
+ $this->arg = $name;
+ $this->asserts = 0;
+ $this->assertspass = 0;
+ $this->assertsfail = 0;
+ $this->tests++;
+ $this->total++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function passTest
+ *
+ * signals that a test has passed
+ *
+ * @return void
+ */
+ public function passTest()
+ {
+ $this->call = 'passTest';
+ $this->arg = NULL;
+ $this->testspass++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function failTest
+ *
+ * signals that a test has failed
+ *
+ * @param string $exception an exception
+ * @return void
+ */
+ public function failTest($exception)
+ {
+ $this->call = 'failTest';
+ $this->arg = $exception;
+ $this->testsfail++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function skipTest
+ *
+ * signals that a test has been skipped
+ *
+ * @param string $exception an exception
+ * @return void
+ */
+ public function skipTest($exception)
+ {
+ $this->call = 'skipTest';
+ $this->arg = $exception;
+ $this->testsskip++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function errorTest
+ *
+ * signals that there has been an error in the test
+ *
+ * @param object $exception the exception thrown
+ * @return void
+ */
+ public function errorTest($exception)
+ {
+ $this->call = 'errorTest';
+ $this->arg = $exception;
+ $this->testserror++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function incompleteTest
+ *
+ * signals that the test is incomplete
+ *
+ * @param object $exception the exception thrown
+ * @return void
+ */
+ public function incompleteTest($exception)
+ {
+ $this->call = 'incompleteTest';
+ $this->arg = $exception;
+ $this->testsincomplete++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function endTest
+ *
+ * signals that the last test is complete
+ *
+ * @return void
+ */
+ public function endTest()
+ {
+ $this->call = 'endTest';
+ $this->arg = NULL;
+ $this->notify();
+ return;
+ }
+
+ //----------------------------------------------------------------
+ // Assert Methods
+ //----------------------------------------------------------------
+
+ /**
+ * public function startAssert
+ *
+ * signals the start of an assert
+ *
+ * @param string $code code of the assert
+ * @return void
+ */
+ public function startAssert($code)
+ {
+ $this->call = 'startAssert';
+ $this->arg = $code;
+ $this->asserts++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function passAssert
+ *
+ * signals that an assert has passed
+ *
+ * @return void
+ */
+ public function passAssert()
+ {
+ $this->call = 'passAssert';
+ $this->arg = NULL;
+ $this->assertspass++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function failAssert
+ *
+ * signals that an assert has failed
+ *
+ * @param object $exception the exception thrown
+ * @return void
+ */
+ public function failAssert($exception)
+ {
+ $this->call = 'failAssert';
+ $this->arg = $exception;
+ $this->assertsfail++;
+ $this->notify();
+ return;
+ }
+
+ /**
+ * public function endAssert
+ *
+ * signals that an assert is complete
+ *
+ * @return void
+ */
+ public function endAssert()
+ {
+ $this->call = 'endAssert';
+ $this->arg = NULL;
+ $this->notify();
+ return;
+ }
+
+ //----------------------------------------------------------------
+ // All properties are read-only
+ //----------------------------------------------------------------
+
+ /**
+ * public function __get
+ *
+ * make all properties read-only
+ *
+ * @param string $name name of property to grab
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if (isset($this->$name))
+ {
+ return $this->$name;
+ }
+ return;
+ }
+}
+?>
\ No newline at end of file
Added: library/trunk/lib/test/unit.abstract.php (1 => 2)
--- library/trunk/lib/test/unit.abstract.php 2006-08-07 13:45:57 UTC (rev 1)
+++ library/trunk/lib/test/unit.abstract.php 2006-08-09 14:26:37 UTC (rev 2)
@@ -0,0 +1,286 @@
+<?php
+/**
+ * unit.abstract.php - contains abstract class CCL_TestUnit
+ *
+ * extend this to create a single test unit - a group of tests usually for
+ * a single class
+ *
+ * This is released under the GPL, see license.txt for details
+ *
+ * @author Elizabeth Smith <emsmith@callicore.net>
+ * @copyright Elizabeth Smith (c)2006
+ * @link http://callicore.net
+ * @license http://www.opensource.org/licenses/gpl-license.php GPL
+ * @version $Id$
+ * @since Php 5.2.0
+ * @package callicore library
+ * @subpackage test
+ * @category lib
+ * @filesource
+ */
+
+/**
+ * CCL_TestUnit - groups methods that start with test into a cohesive group
+ *
+ * Extend this class in order to create a test unit - each method starting with
+ * test (case sensitive) will be executed in isolation when run is called, you
+ * can run all the tests or run just a single test. Results are passed to the object
+ * sent to the constructor which implements the ccl_testreporter interface
+ */
+abstract class CCL_TestUnit
+{
+
+ /**
+ * a name for the testcase - if left blank by extending class use classname
+ * @var $name string
+ */
+ protected $name;
+
+ /**
+ * result class to ping
+ * @var $result object
+ */
+ protected $result;
+
+ /**
+ * test message - transient per test
+ * @var $message string
+ */
+ protected $message;
+
+ //----------------------------------------------------------------
+ // Setup
+ //----------------------------------------------------------------
+
+ /**
+ * public function __construct
+ *
+ * the constructor just sets up the testcase name
+ *
+ * @param object $reporter instanceof CC_Reporter subclass
+ * @return void
+ */
+ public function __construct()
+ {
+ if (is_null($this->name))
+ {
+ $this->name = get_class($this);
+ }
+ $this->result = new CCL_TestResult();
+ return;
+ }
+
+ /**
+ * public function getResult
+ *
+ * returns the result class, only really useful for attaching observers
+ *
+ * @return object
+ */
+ public function getResult()
+ {
+ return $this->result;
+ }
+
+ //----------------------------------------------------------------
+ // Overrideable by Subclass
+ //----------------------------------------------------------------
+
+ /**
+ * protected function listTests
+ *
+ * returns a list of all methods that begin with test
+ * can be overridden if you want a different method of
+ * naming tests
+ *
+ * @return array list of methods
+ */
+ protected function listTests()
+ {
+ static $list;
+ if (is_null($list))
+ {
+ $list = array();
+ $temp = get_class_methods($this);
+ foreach ($temp as $method)
+ {
+ if (preg_match('/^test/', $method))
+ {
+ $list[] = $method;
+ }
+ }
+ }
+ return $list;
+ }
+
+ /**
+ * protected function setUp
+ *
+ * override this in extending class if you want a setup method
+ *
+ * @return void
+ */
+ protected function setUp()
+ {
+ return;
+ }
+
+ /**
+ * protected function tearDown
+ *
+ * override this in extending class if you want a teardown method
+ *
+ * @return void
+ */
+ protected function tearDown()
+ {
+ return;
+ }
+
+ //----------------------------------------------------------------
+ // Run and Assert
+ //----------------------------------------------------------------
+
+ /**
+ * final public function assert
+ *
+ * asserts whether something is true or false - will throw an exception to
+ * stop the test on an error
+ *
+ * @param string $string data to check if it's true
+ * @param string $message message on failure
+ * @return void
+ */
+ final protected function assert($item, $message = '')
+ {
+ // rip assertion code out - yuck
+ $info = debug_backtrace();
+ $file = file($info[0]['file']);
+ preg_match('/\$this->assert\((.*),/', $file[$info[0]['line'] - 1], $matches);
+ $code = $matches[1];
+ unset($info, $file, $matches);
+ $this->result->startAssert($code);
+ if ($item == TRUE)
+ {
+ $this->result->passAssert();
+ $this->result->endAssert();
+ }
+ else
+ {
+ $this->result->failAssert($message);
+ $this->result->endAssert();
+ throw new CCL_TestFailedException();
+ }
+ return;
+ }
+
+ /**
+ * public function run
+ *
+ * run all tests in the test class or just one class
+ *
+ * @param string $test name of test to run
+ * @return void
+ */
+ final public function run($test = NULL)
+ {
+ $this->result->startTestUnit($this->name);
+ if (is_null($test))
+ {
+ foreach ($this->listTests() as $method)
+ {
+ $this->runTest($method);
+ }
+ }
+ else
+ {
+ $this->runTest($test);
+ }
+ $this->result->endTestUnit();
+ return;
+ }
+
+ /**
+ * final protected function runTest
+ *
+ * run a single test from this class in isolate environment
+ *
+ * @param string $method name of a test method to run
+ * @return void
+ */
+ final protected function runTest($method)
+ {
+ if (!in_array($method, $this->listTests()))
+ {
+ throw new Exception('The method ' . $method .' is not in the class '
+ . get_class($this) . ' or is not a test method');
+ return;
+ }
+ set_error_handler(array($this, 'errorHandler'));
+ $this->message = NULL;
+ $this->setUp();
+ $this->result->startTest($method);
+ try
+ {
+ $this->$method();
+ $this->result->passTest();
+ }
+ catch (CCL_TestSkippedException $e)
+ {
+ $this->result->skipTest($e);
+ }
+ catch (CCL_TestIncompleteException $e)
+ {
+ $this->result->incompleteTest($e);
+ }
+ catch (CCL_TestFailedException $e)
+ {
+ $this->result->failTest(new CCL_TestFailedException($this->message));
+ }
+ catch (Exception $e)
+ {
+ $this->result->errorTest($e);
+ }
+ restore_error_handler();
+ $this->result->endTest();
+ $this->tearDown();
+ return;
+ }
+
+ /**
+ * protected function errorHandler
+ *
+ * error handler -> basic exception
+ *
+ * @param int $errno error level number
+ * @param string $errstr error message as string
+ * @param string $errfile file where message occured
+ * @param int $errline line where message occured
+ * @return void
+ */
+ public function errorHandler($errno, $errstr, $errfile, $errline)
+ {
+ $trace = debug_backtrace();
+ array_shift($trace);
+ throw new CCL_TestError($errstr, $errno, $errfile, $errline, $trace);
+ }
+}
+
+/**
+ * CCL_TestFailedException - empty exception class used to break out for
+ * failed test
+ */
+class CCL_TestFailedException extends Exception{}
+
+/**
+ * CCL_TestSkippedException - empty exception class used to break out for
+ * skipped test
+ */
+class CCL_TestSkippedException extends Exception{}
+
+/**
+ * CCL_TestIncompleteException - empty exception class used to break out for
+ * incomplete test
+ */
+class CCL_TestIncompleteException extends Exception{}
+?>
\ No newline at end of file