Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

256 lines (233 sloc) 6.996 kB
<?php
/**
* Lithium: the most rad php framework
*
* @copyright Copyright 2011, Union of RAD (http://union-of-rad.org)
* @license http://opensource.org/licenses/bsd-license.php The BSD License
*/
namespace lithium\test;
use lithium\core\Libraries;
use lithium\util\Inflector;
use lithium\core\ClassNotFoundException;
/**
* This `Report` object aggregates tests in a group and allows you to run said tests to
* obtain the results and stats (passes, fails, exceptions, skips) of the test run.
*
* While Lithium already comes with a text-based as well as web-based test interface, you
* may use or extend the `Report` class to create your own test reporter functionality. In
* addition, you can also create your own custom templates for displaying results in a different
* format, such as json.
*
* Example usage, for built-in HTML format/reporter:
*
* {{{
* $report = new Report(array(
* 'title' => 'Test Report Title',
* 'group' => new Group(array('data' => array('\lithium\tests\cases\net\http\MediaTest'))),
* 'format' => 'html',
* 'reporter' => 'html'
* ));
*
* $report->run();
*
* //Get the test stats:
* $report->stats();
*
* //Get test results:
* $report->results
* }}}
*
* You may also choose to filter the results of the test runs to obtain additional information.
* For example, say you wish to calculate the cyclomatic complexity of the classes you are testing:
*
* {{{
* $report = new Report(array(
* 'title' => 'Test Report Title',
* 'group' => new Group(array('data' => array('\lithium\tests\cases\net\http\MediaTest'))),
* 'filters' => array('Complexity')
* ));
*
* $report->run();
*
* //Get test results, including filter results:
* $report->results
* }}}
*
* @see lithium\test\Group
* @see lithium\test\filter
* @see lithium\test\templates
*/
class Report extends \lithium\core\Object {
/**
* Contains an instance of `lithium\test\Group`, which contains all unit tests to be executed
* this test run.
*
* @var object
*/
public $group = null;
/**
* Title of the group being run
*
* @var string
*/
public $title = null;
/**
* group and filter results
*
* @var array
*/
public $results = array('group' => array(), 'filters' => array());
/**
* start and end timers
*
* @var array
*/
public $timer = array('start' => null, 'end' => null);
/**
* An array key on fully-namespaced class names of the filter with options to be
* applied for the filter as the value
*
* @var array
*/
protected $_filters = array();
/**
* Construct Report Object
*
* @param array $config Options array for the test run. Valid options are:
* - 'group': The test group with items to be run.
* - 'filters': An array of filters that the test output should be run through.
*/
public function __construct(array $config = array()) {
$defaults = array(
'title' => null,
'group' => null,
'filters' => array(),
'format' => 'txt',
'reporter' => 'console'
);
parent::__construct($config + $defaults);
}
/**
* undocumented function
*
* @return void
*/
protected function _init() {
$this->group = $this->_config['group'];
$this->title = $this->_config['title'] ?: $this->_config['title'];
}
/**
* undocumented function
*
* @return void
*/
public function run() {
$tests = $this->group->tests();
foreach ($this->filters() as $filter => $options) {
$this->results['filters'][$filter] = array();
$tests = $filter::apply($this, $tests, $options['apply']) ?: $tests;
}
$this->results['group'] = $tests->run();
foreach ($this->filters() as $filter => $options) {
$this->results['filters'][$filter] = $filter::analyze($this, $options['analyze']);
}
}
/**
* Collects Results from the test filters and aggregates them.
*
* @param string $class Classname of the filter for which to aggregate results.
* @param array $results Array of the filter results for
* later analysis by the filter itself.
* @return void
*/
public function collect($class, $results) {
$this->results['filters'][$class][] = $results;
}
/**
* undocumented function
*
* @return void
*/
public function stats() {
$results = (array) $this->results['group'];
$defaults = array(
'asserts' => 0,
'passes' => array(),
'fails' => array(),
'exceptions' => array(),
'errors' => array(),
'skips' => array()
);
$stats = array_reduce($results, function($stats, $result) use ($defaults) {
$stats = (array) $stats + $defaults;
$result = empty($result[0]) ? array($result) : $result;
foreach ($result as $response) {
if (empty($response['result'])) {
continue;
}
$result = $response['result'];
if (in_array($result, array('fail', 'exception'))) {
$response = array_merge(
array('class' => 'unknown', 'method' => 'unknown'), $response
);
$stats['errors'][] = $response;
}
unset($response['file'], $response['result']);
if (in_array($result, array('pass', 'fail'))) {
$stats['asserts']++;
}
if (in_array($result, array('pass', 'fail', 'exception', 'skip'))) {
$stats[Inflector::pluralize($result)][] = $response;
}
}
return $stats;
});
$stats = (array) $stats + $defaults;
$count = array_map(
function($value) { return is_array($value) ? count($value) : $value; }, $stats
);
$success = $count['passes'] == $count['asserts'] && $count['errors'] === 0;
return compact("stats", "count", "success");
}
/**
* Renders the test output (e.g. layouts and filter templates)
*
* @param string $template name of the template (eg: layout)
* @param string $data array from `_data()` method
* @param array $options Array of options (e.g. rendering type)
* @return string
* @filter
*/
public function render($template, $data = array()) {
$config = $this->_config;
if ($template == "stats" && !$data) {
$data = $this->stats();
}
$template = Libraries::locate("test.templates.{$config['reporter']}", $template, array(
'filter' => false, 'type' => 'file', 'suffix' => ".{$config['format']}.php"
));
$params = compact('template', 'data', 'config');
return $this->_filter(__METHOD__, $params, function($self, $params, $chain) {
extract($params['data']);
ob_start();
include $params['template'];
return ob_get_clean();
});
}
public function filters(array $filters = array()) {
if ($this->_filters && !$filters) {
return $this->_filters;
}
$filters += (array) $this->_config['filters'];
$results = array();
foreach ($filters as $filter => $options) {
if (!$class = Libraries::locate('test.filter', $filter)) {
throw new ClassNotFoundException("`{$class}` is not a valid test filter.");
}
$options['name'] = strtolower(join('', array_slice(explode("\\", $class), -1)));
$results[$class] = $options + array('apply' => array(), 'analyze' => array());
}
return $this->_filters = $results;
}
}
?>
Jump to Line
Something went wrong with that request. Please try again.