314 lines
7.5 KiB
PHP
314 lines
7.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* PHPUnit test runner for kohana
|
|
*
|
|
* @package Kohana/UnitTest
|
|
* @author Kohana Team
|
|
* @author BRMatt <matthew@sigswitch.com>
|
|
* @author Paul Banks
|
|
* @copyright (c) 2008-2009 Kohana Team
|
|
* @license http://kohanaphp.com/license
|
|
*/
|
|
class Kohana_Unittest_Runner implements PHPUnit_Framework_TestListener {
|
|
/**
|
|
* Results
|
|
* @var array
|
|
*/
|
|
protected $results = array(
|
|
'errors' => array(),
|
|
'failures' => array(),
|
|
'skipped' => array(),
|
|
'incomplete' => array(),
|
|
);
|
|
|
|
/**
|
|
* Test result totals
|
|
* @var array
|
|
*/
|
|
protected $totals = array(
|
|
'tests' => 0,
|
|
'passed' => 0,
|
|
'errors' => 0,
|
|
'failures' => 0,
|
|
'skipped' => 0,
|
|
'incomplete' => 0,
|
|
'assertions' => 0,
|
|
);
|
|
|
|
/**
|
|
* Info about the current test running
|
|
* @var array
|
|
*/
|
|
protected $current = array();
|
|
|
|
/**
|
|
* Time for tests to run (seconds)
|
|
* @var float
|
|
*/
|
|
protected $time = 0;
|
|
|
|
/**
|
|
* Result collector
|
|
* @var PHPUnit_Framework_TestResult
|
|
*/
|
|
protected $result = NULL;
|
|
|
|
/**
|
|
* the test suite to run
|
|
* @var PHPUnit_Framework_TestSuite
|
|
*/
|
|
protected $suite = NULL;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param PHPUnit_Framework_TestSuite $suite The suite to test
|
|
* @param PHPUnit_Framework_TestResult $result Optional result object to use
|
|
*/
|
|
function __construct(PHPUnit_Framework_TestSuite $suite, PHPUnit_Framework_TestResult $result = NULL)
|
|
{
|
|
if ($result === NULL)
|
|
{
|
|
$result = new PHPUnit_Framework_TestResult;
|
|
}
|
|
|
|
$result->addListener($this);
|
|
|
|
$this->suite = $suite;
|
|
$this->result = $result;
|
|
}
|
|
|
|
/**
|
|
* Magic getter to allow access to member variables
|
|
*
|
|
* @param string $var Variable to get
|
|
* @return mixed
|
|
*/
|
|
function __get($var)
|
|
{
|
|
return $this->$var;
|
|
}
|
|
|
|
/**
|
|
* Calcualtes stats for each file covered by the code testing
|
|
*
|
|
* Each member of the returned array is formatted like so:
|
|
*
|
|
* <code>
|
|
* array(
|
|
* 'coverage' => $coverage_percent_for_file,
|
|
* 'loc' => $lines_of_code,
|
|
* 'locExecutable' => $lines_of_executable_code,
|
|
* 'locExecuted' => $lines_of_code_executed
|
|
* );
|
|
* </code>
|
|
*
|
|
* @return array Statistics for code coverage of each file
|
|
*/
|
|
public function calculate_cc()
|
|
{
|
|
if ($this->result->getCollectCodeCoverageInformation())
|
|
{
|
|
$coverage = $this->result->getCodeCoverageInformation();
|
|
|
|
$coverage_summary = PHPUnit_Util_CodeCoverage::getSummary($coverage);
|
|
|
|
$stats = array();
|
|
|
|
foreach ($coverage_summary as $file => $_lines)
|
|
{
|
|
$stats[$file] = PHPUnit_Util_CodeCoverage::getStatistics($coverage_summary, $file);
|
|
}
|
|
|
|
return $stats;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Calculates the percentage code coverage information
|
|
*
|
|
* @return boolean|float FALSE if cc is not enabled, float for coverage percent
|
|
*/
|
|
public function calculate_cc_percentage()
|
|
{
|
|
if ($stats = $this->calculate_cc())
|
|
{
|
|
$executable = 0;
|
|
$executed = 0;
|
|
|
|
foreach ($stats as $stat)
|
|
{
|
|
$executable += $stat['locExecutable'];
|
|
$executed += $stat['locExecuted'];
|
|
}
|
|
|
|
return ($executable > 0) ? ($executed * 100 / $executable) : 100;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Generate a report using the specified $temp_path
|
|
*
|
|
* @param array $groups Groups to test
|
|
* @param string $temp_path Temporary path to use while generating report
|
|
*/
|
|
public function generate_report(array $groups, $temp_path, $create_sub_dir = TRUE)
|
|
{
|
|
if ( ! is_writable($temp_path))
|
|
throw new Kohana_Exception('Temp path :path does not exist or is not writable by the webserver', array(':path' => $temp_path));
|
|
|
|
$folder_path = $temp_path;
|
|
|
|
if ($create_sub_dir === TRUE)
|
|
{
|
|
// Icky, highly unlikely, but do it anyway
|
|
// Basically adds "(n)" to the end of the filename until there's a free file
|
|
$count = 0;
|
|
do
|
|
{
|
|
$folder_name = date('Y-m-d_H:i:s')
|
|
.(empty($groups) ? '' : ('['.implode(',', $groups).']'))
|
|
.(($count > 0) ? ('('.$count.')') : '');
|
|
++$count;
|
|
}
|
|
while (is_dir($folder_path.$folder_name));
|
|
|
|
$folder_path .= $folder_name;
|
|
|
|
mkdir($folder_path, 0777);
|
|
}
|
|
else
|
|
{
|
|
$folder_name = basename($folder_path);
|
|
}
|
|
|
|
$this->run($groups, TRUE);
|
|
|
|
require_once 'PHPUnit/Runner/Version.php';
|
|
require_once 'PHPUnit/Util/Report.php';
|
|
|
|
PHPUnit_Util_Report::render($this->result, $folder_path);
|
|
|
|
return array($folder_path, $folder_name);
|
|
}
|
|
|
|
/**
|
|
* Runs the test suite using the result specified in the constructor
|
|
*
|
|
* @param array $groups Optional array of groups to test
|
|
* @param bool $collect_cc Optional, Should code coverage be collected?
|
|
* @return Kohana_PHPUnit Instance of $this
|
|
*/
|
|
public function run(array $groups = array(), $collect_cc = FALSE)
|
|
{
|
|
if ($collect_cc AND ! extension_loaded('xdebug'))
|
|
throw new Kohana_Exception('Code coverage cannot be collected because the xdebug extension is not loaded');
|
|
|
|
$this->result->collectCodeCoverageInformation( (bool) $collect_cc);
|
|
|
|
// Run the tests.
|
|
$this->suite->run($this->result, FALSE, $groups);
|
|
|
|
return $this;
|
|
}
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
|
|
// @codingStandardsIgnoreEnd
|
|
{
|
|
$this->totals['errors']++;
|
|
$this->current['result'] = 'errors';
|
|
$this->current['message'] = $test->getStatusMessage();
|
|
}
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
|
|
// @codingStandardsIgnoreEnd
|
|
{
|
|
$this->totals['failures']++;
|
|
$this->current['result'] = 'failures';
|
|
$this->current['message'] = $test->getStatusMessage();
|
|
}
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
|
|
// @codingStandardsIgnoreEnd
|
|
{
|
|
$this->totals['incomplete']++;
|
|
$this->current['result'] = 'incomplete';
|
|
$this->current['message'] = $test->getStatusMessage();
|
|
}
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
|
|
// @codingStandardsIgnoreEnd
|
|
{
|
|
$this->totals['skipped']++;
|
|
$this->current['result'] = 'skipped';
|
|
$this->current['message'] = $test->getStatusMessage();
|
|
}
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function startTest(PHPUnit_Framework_Test $test)
|
|
// @codingStandardsIgnoreEnd
|
|
{
|
|
$this->current['name'] = $test->getName(FALSE);
|
|
$this->current['description'] = $test->toString();
|
|
$this->current['result'] = 'passed';
|
|
}
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function endTest(PHPUnit_Framework_Test $test, $time)
|
|
// @codingStandardsIgnoreEnd
|
|
{
|
|
// Add totals
|
|
$this->totals['tests']++;
|
|
$this->totals['assertions'] += $test->getNumAssertions();
|
|
|
|
// Handle passed tests
|
|
if ($this->current['result'] == 'passed')
|
|
{
|
|
// Add to total
|
|
$this->totals['passed']++;
|
|
}
|
|
else
|
|
{
|
|
// Add to results
|
|
$this->results[$this->current['result']][] = $this->current;
|
|
}
|
|
|
|
$this->current = array();
|
|
|
|
$this->time += $time;
|
|
}
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {}
|
|
// @codingStandardsIgnoreEnd
|
|
|
|
// @codingStandardsIgnoreStart
|
|
public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
|
|
// @codingStandardsIgnoreEnd
|
|
{
|
|
// Parse test descriptions to make them look nicer
|
|
foreach ($this->results as $case => $testresults)
|
|
{
|
|
foreach ($testresults as $type => $result)
|
|
{
|
|
preg_match('/^(?:([a-z0-9_]+?)::)?([a-z0-9_]+)(?: with data set (#\d+ \(.*?\)))?/i', $result['description'], $m);
|
|
|
|
$this->results[$case][$type] += array(
|
|
'class' => $m[1],
|
|
'test' => $m[2],
|
|
'data_set' => isset($m[3]) ? $m[3] : FALSE,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|