parent
bae07faa34
commit
27eb665516
@ -0,0 +1,29 @@ |
||||
<?php |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/** |
||||
* Include this in any file to start coverage, coverage will automatically end |
||||
* when process dies. |
||||
*/ |
||||
require_once(dirname(__FILE__) .'/coverage.php'); |
||||
|
||||
if (CodeCoverage::isCoverageOn()) { |
||||
$coverage = CodeCoverage::getInstance(); |
||||
$coverage->startCoverage(); |
||||
register_shutdown_function("stop_coverage"); |
||||
} |
||||
|
||||
function stop_coverage() { |
||||
# hack until i can think of a way to run tests first and w/o exiting |
||||
$autorun = function_exists("run_local_tests"); |
||||
if ($autorun) { |
||||
$result = run_local_tests(); |
||||
} |
||||
CodeCoverage::getInstance()->stopCoverage(); |
||||
if ($autorun) { |
||||
exit($result ? 0 : 1); |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,14 @@ |
||||
<?php |
||||
/** |
||||
* Close code coverage data collection, next step is to generate report |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/** |
||||
* include coverage files |
||||
*/ |
||||
require_once(dirname(__FILE__) . '/../coverage.php'); |
||||
$cc = CodeCoverage::getInstance(); |
||||
$cc->readSettings(); |
||||
$cc->writeUntouched(); |
||||
?> |
@ -0,0 +1,31 @@ |
||||
<?php |
||||
/** |
||||
* Initialize code coverage data collection, next step is to run your tests |
||||
* with ini setting auto_prepend_file=autocoverage.php ... |
||||
* |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
# optional arguments: |
||||
# --include=<some filepath regexp> these files should be included coverage report |
||||
# --exclude=<come filepath regexp> these files should not be included in coverage report |
||||
# --maxdepth=2 when considering which file were not touched, scan directories |
||||
# |
||||
# Example: |
||||
# php-coverage-open.php --include='.*\.php$' --include='.*\.inc$' --exclude='.*/tests/.*' |
||||
/**#@+ |
||||
* include coverage files |
||||
*/ |
||||
require_once(dirname(__FILE__) . '/../coverage_utils.php'); |
||||
CoverageUtils::requireSqlite(); |
||||
require_once(dirname(__FILE__) . '/../coverage.php'); |
||||
/**#@-*/ |
||||
$cc = new CodeCoverage(); |
||||
$cc->log = 'coverage.sqlite'; |
||||
$args = CoverageUtils::parseArguments($_SERVER['argv'], TRUE); |
||||
$cc->includes = CoverageUtils::issetOr($args['include[]'], array('.*\.php$')); |
||||
$cc->excludes = CoverageUtils::issetOr($args['exclude[]']); |
||||
$cc->maxDirectoryDepth = (int)CoverageUtils::issetOr($args['maxdepth'], '1'); |
||||
$cc->resetLog(); |
||||
$cc->writeSettings(); |
||||
?> |
@ -0,0 +1,29 @@ |
||||
<?php |
||||
/** |
||||
* Generate a code coverage report |
||||
* |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
# optional arguments: |
||||
# --reportDir=some/directory the default is ./coverage-report |
||||
# --title='My Coverage Report' title the main page of your report |
||||
|
||||
/**#@+ |
||||
* include coverage files |
||||
*/ |
||||
require_once(dirname(__FILE__) . '/../coverage_utils.php'); |
||||
require_once(dirname(__FILE__) . '/../coverage.php'); |
||||
require_once(dirname(__FILE__) . '/../coverage_reporter.php'); |
||||
/**#@-*/ |
||||
$cc = CodeCoverage::getInstance(); |
||||
$cc->readSettings(); |
||||
$handler = new CoverageDataHandler($cc->log); |
||||
$report = new CoverageReporter(); |
||||
$args = CoverageUtils::parseArguments($_SERVER['argv']); |
||||
$report->reportDir = CoverageUtils::issetOr($args['reportDir'], 'coverage-report'); |
||||
$report->title = CoverageUtils::issetOr($args['title'], "Simpletest Coverage"); |
||||
$report->coverage = $handler->read(); |
||||
$report->untouched = $handler->readUntouchedFiles(); |
||||
$report->generate(); |
||||
?> |
@ -0,0 +1,196 @@ |
||||
<?php |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/** |
||||
* load coverage data handle |
||||
*/ |
||||
require_once dirname(__FILE__) . '/coverage_data_handler.php'; |
||||
|
||||
/** |
||||
* Orchestrates code coverage both in this thread and in subthread under apache |
||||
* Assumes this is running on same machine as apache. |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
class CodeCoverage { |
||||
var $log; |
||||
var $root; |
||||
var $includes; |
||||
var $excludes; |
||||
var $directoryDepth; |
||||
var $maxDirectoryDepth = 20; // reasonable, otherwise arbitrary |
||||
var $title = "Code Coverage"; |
||||
|
||||
# NOTE: This assumes all code shares the same current working directory. |
||||
var $settingsFile = './code-coverage-settings.dat'; |
||||
|
||||
static $instance; |
||||
|
||||
function writeUntouched() { |
||||
$touched = array_flip($this->getTouchedFiles()); |
||||
$untouched = array(); |
||||
$this->getUntouchedFiles($untouched, $touched, '.', '.'); |
||||
$this->includeUntouchedFiles($untouched); |
||||
} |
||||
|
||||
function &getTouchedFiles() { |
||||
$handler = new CoverageDataHandler($this->log); |
||||
$touched = $handler->getFilenames(); |
||||
return $touched; |
||||
} |
||||
|
||||
function includeUntouchedFiles($untouched) { |
||||
$handler = new CoverageDataHandler($this->log); |
||||
foreach ($untouched as $file) { |
||||
$handler->writeUntouchedFile($file); |
||||
} |
||||
} |
||||
|
||||
function getUntouchedFiles(&$untouched, $touched, $parentPath, $rootPath, $directoryDepth = 1) { |
||||
$parent = opendir($parentPath); |
||||
while ($file = readdir($parent)) { |
||||
$path = "$parentPath/$file"; |
||||
if (is_dir($path)) { |
||||
if ($file != '.' && $file != '..') { |
||||
if ($this->isDirectoryIncluded($path, $directoryDepth)) { |
||||
$this->getUntouchedFiles($untouched, $touched, $path, $rootPath, $directoryDepth + 1); |
||||
} |
||||
} |
||||
} |
||||
else if ($this->isFileIncluded($path)) { |
||||
$relativePath = CoverageDataHandler::ltrim($rootPath .'/', $path); |
||||
if (!array_key_exists($relativePath, $touched)) { |
||||
$untouched[] = $relativePath; |
||||
} |
||||
} |
||||
} |
||||
closedir($parent); |
||||
} |
||||
|
||||
function resetLog() { |
||||
error_log('reseting log'); |
||||
$new_file = fopen($this->log, "w"); |
||||
if (!$new_file) { |
||||
throw new Exception("Could not create ". $this->log); |
||||
} |
||||
fclose($new_file); |
||||
if (!chmod($this->log, 0666)) { |
||||
throw new Exception("Could not change ownership on file ". $this->log); |
||||
} |
||||
$handler = new CoverageDataHandler($this->log); |
||||
$handler->createSchema(); |
||||
} |
||||
|
||||
function startCoverage() { |
||||
$this->root = getcwd(); |
||||
if(!extension_loaded("xdebug")) { |
||||
throw new Exception("Could not load xdebug extension"); |
||||
}; |
||||
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); |
||||
} |
||||
|
||||
function stopCoverage() { |
||||
$cov = xdebug_get_code_coverage(); |
||||
$this->filter($cov); |
||||
$data = new CoverageDataHandler($this->log); |
||||
chdir($this->root); |
||||
$data->write($cov); |
||||
unset($data); // release sqlite connection |
||||
xdebug_stop_code_coverage(); |
||||
// make sure we wind up on same current working directory, otherwise |
||||
// coverage handler writer doesn't know what directory to chop off |
||||
chdir($this->root); |
||||
} |
||||
|
||||
function readSettings() { |
||||
if (file_exists($this->settingsFile)) { |
||||
$this->setSettings(file_get_contents($this->settingsFile)); |
||||
} else { |
||||
error_log("could not find file ". $this->settingsFile); |
||||
} |
||||
} |
||||
|
||||
function writeSettings() { |
||||
file_put_contents($this->settingsFile, $this->getSettings()); |
||||
} |
||||
|
||||
function getSettings() { |
||||
$data = array( |
||||
'log' => realpath($this->log), |
||||
'includes' => $this->includes, |
||||
'excludes' => $this->excludes); |
||||
return serialize($data); |
||||
} |
||||
|
||||
function setSettings($settings) { |
||||
$data = unserialize($settings); |
||||
$this->log = $data['log']; |
||||
$this->includes = $data['includes']; |
||||
$this->excludes = $data['excludes']; |
||||
} |
||||
|
||||
function filter(&$coverage) { |
||||
foreach ($coverage as $file => $line) { |
||||
if (!$this->isFileIncluded($file)) { |
||||
unset($coverage[$file]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
function isFileIncluded($file) { |
||||
if (!empty($this->excludes)) { |
||||
foreach ($this->excludes as $path) { |
||||
if (preg_match('|' . $path . '|', $file)) { |
||||
return False; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (!empty($this->includes)) { |
||||
foreach ($this->includes as $path) { |
||||
if (preg_match('|' . $path . '|', $file)) { |
||||
return True; |
||||
} |
||||
} |
||||
return False; |
||||
} |
||||
|
||||
return True; |
||||
} |
||||
|
||||
function isDirectoryIncluded($dir, $directoryDepth) { |
||||
if ($directoryDepth >= $this->maxDirectoryDepth) { |
||||
return false; |
||||
} |
||||
if (isset($this->excludes)) { |
||||
foreach ($this->excludes as $path) { |
||||
if (preg_match('|' . $path . '|', $dir)) { |
||||
return False; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return True; |
||||
} |
||||
|
||||
static function isCoverageOn() { |
||||
$coverage = self::getInstance(); |
||||
$coverage->readSettings(); |
||||
if (empty($coverage->log) || !file_exists($coverage->log)) { |
||||
trigger_error('No coverage log'); |
||||
return False; |
||||
} |
||||
return True; |
||||
} |
||||
|
||||
static function getInstance() { |
||||
if (self::$instance == NULL) { |
||||
self::$instance = new CodeCoverage(); |
||||
self::$instance->readSettings(); |
||||
} |
||||
return self::$instance; |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,98 @@ |
||||
<?php |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
class CoverageCalculator { |
||||
|
||||
function coverageByFileVariables($file, $coverage) { |
||||
$hnd = fopen($file, 'r'); |
||||
if ($hnd == null) { |
||||
throw new Exception("File $file is missing"); |
||||
} |
||||
$lines = array(); |
||||
for ($i = 1; !feof($hnd); $i++) { |
||||
$line = fgets($hnd); |
||||
$lineCoverage = $this->lineCoverageCodeToStyleClass($coverage, $i); |
||||
$lines[$i] = array('lineCoverage' => $lineCoverage, 'code' => $line); |
||||
} |
||||
|
||||
fclose($hnd); |
||||
|
||||
$var = compact('file', 'lines', 'coverage'); |
||||
return $var; |
||||
} |
||||
|
||||
function lineCoverageCodeToStyleClass($coverage, $line) { |
||||
if (!array_key_exists($line, $coverage)) { |
||||
return "comment"; |
||||
} |
||||
$code = $coverage[$line]; |
||||
if (empty($code)) { |
||||
return "comment"; |
||||
} |
||||
switch ($code) { |
||||
case -1: |
||||
return "missed"; |
||||
case -2: |
||||
return "dead"; |
||||
} |
||||
|
||||
return "covered"; |
||||
} |
||||
|
||||
function totalLoc($total, $coverage) { |
||||
return $total + sizeof($coverage); |
||||
} |
||||
|
||||
function lineCoverage($total, $line) { |
||||
# NOTE: counting dead code as covered, as it's almost always an executable line |
||||
# strange artifact of xdebug or underlying system |
||||
return $total + ($line > 0 || $line == -2 ? 1 : 0); |
||||
} |
||||
|
||||
function totalCoverage($total, $coverage) { |
||||
return $total + array_reduce($coverage, array(&$this, "lineCoverage")); |
||||
} |
||||
|
||||
static function reportFilename($filename) { |
||||
return preg_replace('|[/\\\\]|', '_', $filename) . '.html'; |
||||
} |
||||
|
||||
function percentCoverageByFile($coverage, $file, &$results) { |
||||
$byFileReport = self::reportFilename($file); |
||||
|
||||
$loc = sizeof($coverage); |
||||
if ($loc == 0) |
||||
return 0; |
||||
$lineCoverage = array_reduce($coverage, array(&$this, "lineCoverage")); |
||||
$percentage = 100 * ($lineCoverage / $loc); |
||||
$results[0][$file] = array('byFileReport' => $byFileReport, 'percentage' => $percentage); |
||||
} |
||||
|
||||
function variables($coverage, $untouched) { |
||||
$coverageByFile = array(); |
||||
array_walk($coverage, array(&$this, "percentCoverageByFile"), array(&$coverageByFile)); |
||||
|
||||
$totalLoc = array_reduce($coverage, array(&$this, "totalLoc")); |
||||
|
||||
if ($totalLoc > 0) { |
||||
$totalLinesOfCoverage = array_reduce($coverage, array(&$this, "totalCoverage")); |
||||
$totalPercentCoverage = 100 * ($totalLinesOfCoverage / $totalLoc); |
||||
} |
||||
|
||||
$untouchedPercentageDenominator = sizeof($coverage) + sizeof($untouched); |
||||
if ($untouchedPercentageDenominator > 0) { |
||||
$filesTouchedPercentage = 100 * sizeof($coverage) / $untouchedPercentageDenominator; |
||||
} |
||||
|
||||
$var = compact('coverageByFile', 'totalPercentCoverage', 'totalLoc', 'totalLinesOfCoverage', 'filesTouchedPercentage'); |
||||
$var['untouched'] = $untouched; |
||||
return $var; |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,125 @@ |
||||
<?php |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/** |
||||
* @todo which db abstraction layer is this? |
||||
*/ |
||||
require_once 'DB/sqlite.php'; |
||||
|
||||
/** |
||||
* Persists code coverage data into SQLite database and aggregate data for convienent |
||||
* interpretation in report generator. Be sure to not to keep an instance longer |
||||
* than you have, otherwise you risk overwriting database edits from another process |
||||
* also trying to make updates. |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
class CoverageDataHandler { |
||||
|
||||
var $db; |
||||
|
||||
function __construct($filename) { |
||||
$this->filename = $filename; |
||||
$this->db = new SQLiteDatabase($filename); |
||||
if (empty($this->db)) { |
||||
throw new Exception("Could not create sqlite db ". $filename); |
||||
} |
||||
} |
||||
|
||||
function createSchema() { |
||||
$this->db->queryExec("create table untouched (filename text)"); |
||||
$this->db->queryExec("create table coverage (name text, coverage text)"); |
||||
} |
||||
|
||||
function &getFilenames() { |
||||
$filenames = array(); |
||||
$cursor = $this->db->unbufferedQuery("select distinct name from coverage"); |
||||
while ($row = $cursor->fetch()) { |
||||
$filenames[] = $row[0]; |
||||
} |
||||
|
||||
return $filenames; |
||||
} |
||||
|
||||
function write($coverage) { |
||||
foreach ($coverage as $file => $lines) { |
||||
$coverageStr = serialize($lines); |
||||
$relativeFilename = self::ltrim(getcwd() . '/', $file); |
||||
$sql = "insert into coverage (name, coverage) values ('$relativeFilename', '$coverageStr')"; |
||||
# if this fails, check you have write permission |
||||
$this->db->queryExec($sql); |
||||
} |
||||
} |
||||
|
||||
function read() { |
||||
$coverage = array_flip($this->getFilenames()); |
||||
foreach($coverage as $file => $garbage) { |
||||
$coverage[$file] = $this->readFile($file); |
||||
} |
||||
return $coverage; |
||||
} |
||||
|
||||
function &readFile($file) { |
||||
$sql = "select coverage from coverage where name = '$file'"; |
||||
$aggregate = array(); |
||||
$result = $this->db->query($sql); |
||||
while ($result->valid()) { |
||||
$row = $result->current(); |
||||
$this->aggregateCoverage($aggregate, unserialize($row[0])); |
||||
$result->next(); |
||||
} |
||||
|
||||
return $aggregate; |
||||
} |
||||
|
||||
function aggregateCoverage(&$total, $next) { |
||||
foreach ($next as $lineno => $code) { |
||||
if (!isset($total[$lineno])) { |
||||
$total[$lineno] = $code; |
||||
} else { |
||||
$total[$lineno] = $this->aggregateCoverageCode($total[$lineno], $code); |
||||
} |
||||
} |
||||
} |
||||
|
||||
function aggregateCoverageCode($code1, $code2) { |
||||
switch($code1) { |
||||
case -2: return -2; |
||||
case -1: return $code2; |
||||
default: |
||||
switch ($code2) { |
||||
case -2: return -2; |
||||
case -1: return $code1; |
||||
} |
||||
} |
||||
return $code1 + $code2; |
||||
} |
||||
|
||||
static function ltrim($cruft, $pristine) { |
||||
if(stripos($pristine, $cruft) === 0) { |
||||
return substr($pristine, strlen($cruft)); |
||||
} |
||||
return $pristine; |
||||
} |
||||
|
||||
function writeUntouchedFile($file) { |
||||
$relativeFile = CoverageDataHandler::ltrim('./', $file); |
||||
$sql = "insert into untouched values ('$relativeFile')"; |
||||
$this->db->queryExec($sql); |
||||
} |
||||
|
||||
function &readUntouchedFiles() { |
||||
$untouched = array(); |
||||
$result = $this->db->query("select filename from untouched order by filename"); |
||||
while ($result->valid()) { |
||||
$row = $result->current(); |
||||
$untouched[] = $row[0]; |
||||
$result->next(); |
||||
} |
||||
|
||||
return $untouched; |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,68 @@ |
||||
<?php |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/**#@+ |
||||
* include additional coverage files |
||||
*/ |
||||
require_once dirname(__FILE__) .'/coverage_calculator.php'; |
||||
require_once dirname(__FILE__) .'/coverage_utils.php'; |
||||
require_once dirname(__FILE__) .'/simple_coverage_writer.php'; |
||||
/**#@-*/ |
||||
|
||||
/** |
||||
* Take aggregated coverage data and generate reports from it using smarty |
||||
* templates |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
class CoverageReporter { |
||||
var $coverage; |
||||
var $untouched; |
||||
var $reportDir; |
||||
var $title = 'Coverage'; |
||||
var $writer; |
||||
var $calculator; |
||||
|
||||
function __construct() { |
||||
$this->writer = new SimpleCoverageWriter(); |
||||
$this->calculator = new CoverageCalculator(); |
||||
} |
||||
|
||||
function generateSummaryReport($out) { |
||||
$variables = $this->calculator->variables($this->coverage, $this->untouched); |
||||
$variables['title'] = $this->title; |
||||
$report = $this->writer->writeSummary($out, $variables); |
||||
fwrite($out, $report); |
||||
} |
||||
|
||||
function generate() { |
||||
CoverageUtils::mkdir($this->reportDir); |
||||
|
||||
$index = $this->reportDir .'/index.html'; |
||||
$hnd = fopen($index, 'w'); |
||||
$this->generateSummaryReport($hnd); |
||||
fclose($hnd); |
||||
|
||||
foreach ($this->coverage as $file => $cov) { |
||||
$byFile = $this->reportDir .'/'. self::reportFilename($file); |
||||
$byFileHnd = fopen($byFile, 'w'); |
||||
$this->generateCoverageByFile($byFileHnd, $file, $cov); |
||||
fclose($byFileHnd); |
||||
} |
||||
|
||||
echo "generated report $index\n"; |
||||
} |
||||
|
||||
function generateCoverageByFile($out, $file, $cov) { |
||||
$variables = $this->calculator->coverageByFileVariables($file, $cov); |
||||
$variables['title'] = $this->title .' - '. $file; |
||||
$this->writer->writeByFile($out, $variables); |
||||
} |
||||
|
||||
static function reportFilename($filename) { |
||||
return preg_replace('|[/\\\\]|', '_', $filename) . '.html'; |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,114 @@ |
||||
<?php |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
class CoverageUtils { |
||||
|
||||
static function mkdir($dir) { |
||||
if (!file_exists($dir)) { |
||||
mkdir($dir, 0777, True); |
||||
} else { |
||||
if (!is_dir($dir)) { |
||||
throw new Exception($dir .' exists as a file, not a directory'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static function requireSqlite() { |
||||
if (!self::isPackageClassAvailable('DB/sqlite.php', 'SQLiteDatabase')) { |
||||
echo "sqlite library is required to be installed and available in include_path"; |
||||
exit(1); |
||||
} |
||||
} |
||||
|
||||
static function isPackageClassAvailable($includeFile, $class) { |
||||
@include_once($includeFile); |
||||
return class_exists($class); |
||||
} |
||||
|
||||
/** |
||||
* Parses simple parameters from CLI. |
||||
* |
||||
* Puts trailing parameters into string array in 'extraArguments' |
||||
* |
||||
* Example: |
||||
* $args = CoverageUtil::parseArguments($_SERVER['argv']); |
||||
* if ($args['verbose']) echo "Verbose Mode On\n"; |
||||
* $files = $args['extraArguments']; |
||||
* |
||||
* Example CLI: |
||||
* --foo=blah -x -h some trailing arguments |
||||
* |
||||
* if multiValueMode is true |
||||
* Example CLI: |
||||
* --include=a --include=b --exclude=c |
||||
* Then |
||||
* $args = CoverageUtil::parseArguments($_SERVER['argv']); |
||||
* $args['include[]'] will equal array('a', 'b') |
||||
* $args['exclude[]'] will equal array('c') |
||||
* $args['exclude'] will equal c |
||||
* $args['include'] will equal b NOTE: only keeps last value |
||||
* |
||||
* @param unknown_type $argv |
||||
* @param supportMutliValue - will store 2nd copy of value in an array with key "foo[]" |
||||
* @return unknown |
||||
*/ |
||||
static public function parseArguments($argv, $mutliValueMode = False) { |
||||
$args = array(); |
||||
$args['extraArguments'] = array(); |
||||
array_shift($argv); // scriptname |
||||
foreach ($argv as $arg) { |
||||
if (ereg('^--([^=]+)=(.*)', $arg, $reg)) { |
||||
$args[$reg[1]] = $reg[2]; |
||||
if ($mutliValueMode) { |
||||
self::addItemAsArray($args, $reg[1], $reg[2]); |
||||
} |
||||
} elseif (ereg('^[-]{1,2}([^[:blank:]]+)', $arg, $reg)) { |
||||
$nonnull = ''; |
||||
$args[$reg[1]] = $nonnull; |
||||
if ($mutliValueMode) { |
||||
self::addItemAsArray($args, $reg[1], $nonnull); |
||||
} |
||||
} else { |
||||
$args['extraArguments'][] = $arg; |
||||
} |
||||
} |
||||
|
||||
return $args; |
||||
} |
||||
|
||||
/** |
||||
* Adds a value as an array of one, or appends to an existing array elements |
||||
* |
||||
* @param unknown_type $array |
||||
* @param unknown_type $item |
||||
*/ |
||||
static function addItemAsArray(&$array, $key, $item) { |
||||
$array_key = $key .'[]'; |
||||
if (array_key_exists($array_key, $array)) { |
||||
$array[$array_key][] = $item; |
||||
} else { |
||||
$array[$array_key] = array($item); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* isset function with default value |
||||
* |
||||
* Example: $z = CoverageUtils::issetOr($array[$key], 'no value given') |
||||
* |
||||
* @param unknown_type $val |
||||
* @param unknown_type $default |
||||
* @return first value unless value is not set then returns 2nd arg or null if no 2nd arg |
||||
*/ |
||||
static public function issetOr(&$val, $default = null) |
||||
{ |
||||
return isset($val) ? $val : $default; |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,16 @@ |
||||
<?php |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
/** |
||||
* @package SimpleTest |
||||
* @subpackage Extensions |
||||
*/ |
||||
interface CoverageWriter { |
||||
|
||||
function writeSummary($out, $variables); |
||||
|
||||
function writeByFile($out, $variables); |
||||
} |
||||
?> |
@ -0,0 +1,39 @@ |
||||
<?php |
||||
/** |
||||
* SimpleCoverageWriter class file |
||||
* @package SimpleTest |
||||
* @subpackage UnitTester |
||||
* @version $Id: unit_tester.php 1882 2009-07-01 14:30:05Z lastcraft $ |
||||
*/ |
||||
/** |
||||
* base coverage writer class |
||||
*/ |
||||
require_once dirname(__FILE__) .'/coverage_writer.php'; |
||||
|
||||
/** |
||||
* SimpleCoverageWriter class |
||||
* @package SimpleTest |
||||
* @subpackage UnitTester |
||||
*/ |
||||
class SimpleCoverageWriter implements CoverageWriter { |
||||
|
||||
function writeSummary($out, $variables) { |
||||
extract($variables); |
||||
$now = date("F j, Y, g:i a"); |
||||
ob_start(); |
||||
include dirname(__FILE__) . '/templates/index.php'; |
||||
$contents = ob_get_contents(); |
||||
fwrite ($out, $contents); |
||||
ob_end_clean(); |
||||
} |
||||
|
||||
function writeByFile($out, $variables) { |
||||
extract($variables); |
||||
ob_start(); |
||||
include dirname(__FILE__) . '/templates/file.php'; |
||||
$contents = ob_get_contents(); |
||||
fwrite ($out, $contents); |
||||
ob_end_clean(); |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,60 @@ |
||||
<html> |
||||
<head> |
||||
<title><?php echo $title ?></title>
|
||||
</head> |
||||
<style type="text/css"> |
||||
body { |
||||
font-family: "Gill Sans MT", "Gill Sans", GillSans, Arial, Helvetica, sans-serif; |
||||
} |
||||
h1 { |
||||
font-size: medium; |
||||
} |
||||
#code { |
||||
border-spacing: 0; |
||||
} |
||||
.lineNo { |
||||
color: #ccc; |
||||
} |
||||
.code, .lineNo { |
||||
white-space: pre; |
||||
font-family: monospace; |
||||
} |
||||
.covered { |
||||
color: #090; |
||||
} |
||||
.missed { |
||||
color: #f00; |
||||
} |
||||
.dead { |
||||
color: #00f; |
||||
} |
||||
.comment { |
||||
color: #333; |
||||
} |
||||
</style> |
||||
<body> |
||||
<h1 id="title"><?php echo $title ?></h1>
|
||||
<table id="code"> |
||||
<tbody> |
||||
<?php foreach ($lines as $lineNo => $line) { ?> |
||||
<tr> |
||||
<td><span class="lineNo"><?php echo $lineNo ?></span></td>
|
||||
<td><span class="<?php echo $line['lineCoverage'] ?> code"><?php echo htmlentities($line['code']) ?></span></td>
|
||||
</tr> |
||||
<?php } ?> |
||||
</tbody> |
||||
</table> |
||||
<h2>Legend</h2> |
||||
<dl> |
||||
<dt><span class="missed">Missed</span></dt> |
||||
<dd>lines code that <strong>were not</strong> excersized during program execution.</dd> |
||||
<dt><span class="covered">Covered</span></dt> |
||||
<dd>lines code <strong>were</strong> excersized during program execution.</dd> |
||||
<dt><span class="comment">Comment/non executable</span></dt> |
||||
<dd>Comment or non-executable line of code.</dd> |
||||
<dt><span class="dead">Dead</span></dt> |
||||
<dd>lines of code that according to xdebug could not be executed. This is counted as coverage code because |
||||
in almost all cases it is code that runnable.</dd> |
||||
</dl> |
||||
</body> |
||||
</html> |
@ -0,0 +1,106 @@ |
||||
<html> |
||||
<head> |
||||
<title><?php echo $title ?></title>
|
||||
</head> |
||||
<style type="text/css"> |
||||
h1 { |
||||
font-size: medium; |
||||
} |
||||
|
||||
body { |
||||
font-family: "Gill Sans MT", "Gill Sans", GillSans, Arial, Helvetica, |
||||
sans-serif; |
||||
} |
||||
|
||||
td.percentage { |
||||
text-align: right; |
||||
} |
||||
|
||||
caption { |
||||
border-bottom: thin solid; |
||||
font-weight: bolder; |
||||
} |
||||
|
||||
dt { |
||||
font-weight: bolder; |
||||
} |
||||
|
||||
table { |
||||
margin: 1em; |
||||
} |
||||
</style> |
||||
<body> |
||||
<h1 id="title"><?php echo $title ?></h1>
|
||||
<table> |
||||
<caption>Summary</caption> |
||||
<tbody> |
||||
<tr> |
||||
<td>Total Coverage (<a href="#total-coverage">?</a>) :</td> |
||||
<td class="percentage"><span class="totalPercentCoverage"><?php echo number_format($totalPercentCoverage, 0) ?>%</span></td>
|
||||
</tr> |
||||
<tr> |
||||
<td>Total Files Covered (<a href="#total-files-covered">?</a>) :</td> |
||||
<td class="percentage"><span class="filesTouchedPercentage"><?php echo number_format($filesTouchedPercentage, 0) ?>%</span></td>
|
||||
</tr> |
||||
<tr> |
||||
<td>Report Generation Date :</td> |
||||
<td><?php echo $now ?></td>
|
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
<table id="covered-files"> |
||||
<caption>Coverage (<a href="#coverage">?</a>)</caption> |
||||
<thead> |
||||
<tr> |
||||
<th>File</th> |
||||
<th>Coverage</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<?php foreach ($coverageByFile as $file => $coverage) { ?> |
||||
<tr> |
||||
<td><a class="byFileReportLink" href="<?php echo $coverage['byFileReport'] ?>"><?php echo $file ?></a></td>
|
||||
<td class="percentage"><span class="percentCoverage"><?php echo number_format($coverage['percentage'], 0) ?>%</span></td>
|
||||
</tr> |
||||
<?php } ?> |
||||
</tbody> |
||||
</table> |
||||
<table> |
||||
<caption>Files Not Covered (<a href="#untouched">?</a>)</caption> |
||||
<tbody> |
||||
<?php foreach ($untouched as $key => $file) { ?> |
||||
<tr> |
||||
<td><span class="untouchedFile"><?php echo $file ?></span></td>
|
||||
</tr> |
||||
<?php } ?> |
||||
</tbody> |
||||
</table> |
||||
|
||||
<h2>Glossary</h2> |
||||
<dl> |
||||
<dt><a name="total-coverage">Total Coverage</a></dt> |
||||
<dd>Ratio of all the lines of executable code that were executed to the |
||||
lines of code that were not executed. This does not include the files |
||||
that were not covered at all.</dd> |
||||
<dt><a name="total-files-covered">Total Files Covered</a></dt> |
||||
<dd>This is the ratio of the number of files tested, to the number of |
||||
files not tested at all.</dd> |
||||
<dt><a name="coverage">Coverage</a></dt> |
||||
<dd>These files were parsed and loaded by the php interpreter while |
||||
running the tests. Percentage is determined by the ratio of number of |
||||
lines of code executed to the number of possible executable lines of |
||||
code. "dead" lines of code, or code that could not be executed |
||||
according to xdebug, are counted as covered because in almost all cases |
||||
it is the end of a logical loop.</dd> |
||||
<dt><a name="untouched">Files Not Covered</a></dt> |
||||
<dd>These files were not loaded by the php interpreter at anytime |
||||
during a unit test. You could consider these files having 0% coverage, |
||||
but because it is difficult to determine the total coverage unless you |
||||
could count the lines for executable code, this is not reflected in the |
||||
Total Coverage calculation.</dd> |
||||
</dl> |
||||
|
||||
<p>Code coverage generated by <a href="http://www.simpletest.org">SimpleTest</a></p> |
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,65 @@ |
||||
<?php |
||||
require_once(dirname(__FILE__) . '/../../../autorun.php'); |
||||
|
||||
class CoverageCalculatorTest extends UnitTestCase { |
||||
function skip() { |
||||
$this->skipIf( |
||||
!file_exists('DB/sqlite.php'), |
||||
'The Coverage extension needs to have PEAR installed'); |
||||
} |
||||
|
||||
function setUp() { |
||||
require_once dirname(__FILE__) .'/../coverage_calculator.php'; |
||||
$this->calc = new CoverageCalculator(); |
||||
} |
||||
|
||||
function testVariables() { |
||||
$coverage = array('file' => array(1,1,1,1)); |
||||
$untouched = array('missed-file'); |
||||
$variables = $this->calc->variables($coverage, $untouched); |
||||
$this->assertEqual(4, $variables['totalLoc']); |
||||
$this->assertEqual(100, $variables['totalPercentCoverage']); |
||||
$this->assertEqual(4, $variables['totalLinesOfCoverage']); |
||||
$expected = array('file' => array('byFileReport' => 'file.html', 'percentage' => 100)); |
||||
$this->assertEqual($expected, $variables['coverageByFile']); |
||||
$this->assertEqual(50, $variables['filesTouchedPercentage']); |
||||
$this->assertEqual($untouched, $variables['untouched']); |
||||
} |
||||
|
||||
function testPercentageCoverageByFile() { |
||||
$coverage = array(0,0,0,1,1,1); |
||||
$results = array(); |
||||
$this->calc->percentCoverageByFile($coverage, 'file', $results); |
||||
$pct = $results[0]; |
||||
$this->assertEqual(50, $pct['file']['percentage']); |
||||
$this->assertEqual('file.html', $pct['file']['byFileReport']); |
||||
} |
||||
|
||||
function testTotalLoc() { |
||||
$this->assertEqual(13, $this->calc->totalLoc(10, array(1,2,3))); |
||||
} |
||||
|
||||
function testLineCoverage() { |
||||
$this->assertEqual(10, $this->calc->lineCoverage(10, -1)); |
||||
$this->assertEqual(10, $this->calc->lineCoverage(10, 0)); |
||||
$this->assertEqual(11, $this->calc->lineCoverage(10, 1)); |
||||
} |
||||
|
||||
function testTotalCoverage() { |
||||
$this->assertEqual(11, $this->calc->totalCoverage(10, array(-1,1))); |
||||
} |
||||
|
||||
static function getAttribute($element, $attribute) { |
||||
$a = $element->attributes(); |
||||
return $a[$attribute]; |
||||
} |
||||
|
||||
static function dom($stream) { |
||||
rewind($stream); |
||||
$actual = stream_get_contents($stream); |
||||
$html = DOMDocument::loadHTML($actual); |
||||
return simplexml_import_dom($html); |
||||
} |
||||
} |
||||
|
||||
?> |
@ -0,0 +1,83 @@ |
||||
<?php |
||||
require_once(dirname(__FILE__) . '/../../../autorun.php'); |
||||
|
||||
class CoverageDataHandlerTest extends UnitTestCase { |
||||
function skip() { |
||||
$this->skipIf( |
||||
!file_exists('DB/sqlite.php'), |
||||
'The Coverage extension needs to have PEAR installed'); |
||||
} |
||||
|
||||
function setUp() { |
||||
require_once dirname(__FILE__) .'/../coverage_data_handler.php'; |
||||
} |
||||
|
||||
function testAggregateCoverageCode() { |
||||
$handler = new CoverageDataHandler($this->tempdb()); |
||||
$this->assertEqual(-2, $handler->aggregateCoverageCode(-2, -2)); |
||||
$this->assertEqual(-2, $handler->aggregateCoverageCode(-2, 10)); |
||||
$this->assertEqual(-2, $handler->aggregateCoverageCode(10, -2)); |
||||
$this->assertEqual(-1, $handler->aggregateCoverageCode(-1, -1)); |
||||
$this->assertEqual(10, $handler->aggregateCoverageCode(-1, 10)); |
||||
$this->assertEqual(10, $handler->aggregateCoverageCode(10, -1)); |
||||
$this->assertEqual(20, $handler->aggregateCoverageCode(10, 10)); |
||||
} |
||||
|
||||
function testSimpleWriteRead() { |
||||
$handler = new CoverageDataHandler($this->tempdb()); |
||||
$handler->createSchema(); |
||||
$coverage = array(10 => -2, 20 => -1, 30 => 0, 40 => 1); |
||||
$handler->write(array('file' => $coverage)); |
||||
|
||||
$actual = $handler->readFile('file'); |
||||
$expected = array(10 => -2, 20 => -1, 30 => 0, 40 => 1); |
||||
$this->assertEqual($expected, $actual); |
||||
} |
||||
|
||||
function testMultiFileWriteRead() { |
||||
$handler = new CoverageDataHandler($this->tempdb()); |
||||
$handler->createSchema(); |
||||
$handler->write(array( |
||||
'file1' => array(-2, -1, 1), |
||||
'file2' => array(-2, -1, 1) |
||||
)); |
||||
$handler->write(array( |
||||
'file1' => array(-2, -1, 1) |
||||
)); |
||||
|
||||
$expected = array( |
||||
'file1' => array(-2, -1, 2), |
||||
'file2' => array(-2, -1, 1) |
||||
); |
||||
$actual = $handler->read(); |
||||
$this->assertEqual($expected, $actual); |
||||
} |
||||
|
||||
function testGetfilenames() { |
||||
$handler = new CoverageDataHandler($this->tempdb()); |
||||
$handler->createSchema(); |
||||
$rawCoverage = array('file0' => array(), 'file1' => array()); |
||||
$handler->write($rawCoverage); |
||||
$actual = $handler->getFilenames(); |
||||
$this->assertEqual(array('file0', 'file1'), $actual); |
||||
} |
||||
|
||||
function testWriteUntouchedFiles() { |
||||
$handler = new CoverageDataHandler($this->tempdb()); |
||||
$handler->createSchema(); |
||||
$handler->writeUntouchedFile('bluejay'); |
||||
$handler->writeUntouchedFile('robin'); |
||||
$this->assertEqual(array('bluejay', 'robin'), $handler->readUntouchedFiles()); |
||||
} |
||||
|
||||
function testLtrim() { |
||||
$this->assertEqual('ber', CoverageDataHandler::ltrim('goo', 'goober')); |
||||
$this->assertEqual('some/file', CoverageDataHandler::ltrim('./', './some/file')); |
||||
$this->assertEqual('/x/y/z/a/b/c', CoverageDataHandler::ltrim('/a/b/', '/x/y/z/a/b/c')); |
||||
} |
||||
|
||||
function tempdb() { |
||||
return tempnam(NULL, 'coverage.test.db'); |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,22 @@ |
||||
<?php |
||||
require_once(dirname(__FILE__) . '/../../../autorun.php'); |
||||
|
||||
class CoverageReporterTest extends UnitTestCase { |
||||
function skip() { |
||||
$this->skipIf( |
||||
!file_exists('DB/sqlite.php'), |
||||
'The Coverage extension needs to have PEAR installed'); |
||||
} |
||||
|
||||
function setUp() { |
||||
require_once dirname(__FILE__) .'/../coverage_reporter.php'; |
||||
new CoverageReporter(); |
||||
} |
||||
|
||||
function testreportFilename() { |
||||
$this->assertEqual("parula.php.html", CoverageReporter::reportFilename("parula.php")); |
||||
$this->assertEqual("warbler_parula.php.html", CoverageReporter::reportFilename("warbler/parula.php")); |
||||
$this->assertEqual("warbler_parula.php.html", CoverageReporter::reportFilename("warbler\\parula.php")); |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,109 @@ |
||||
<?php |
||||
require_once(dirname(__FILE__) . '/../../../autorun.php'); |
||||
require_once(dirname(__FILE__) . '/../../../mock_objects.php'); |
||||
|
||||
class CodeCoverageTest extends UnitTestCase { |
||||
function skip() { |
||||
$this->skipIf( |
||||
!file_exists('DB/sqlite.php'), |
||||
'The Coverage extension needs to have PEAR installed'); |
||||
} |
||||
|
||||
function setUp() { |
||||
require_once dirname(__FILE__) .'/../coverage.php'; |
||||
} |
||||
|
||||
function testIsFileIncluded() { |
||||
$coverage = new CodeCoverage(); |
||||
$this->assertTrue($coverage->isFileIncluded('aaa')); |
||||
$coverage->includes = array('a'); |
||||
$this->assertTrue($coverage->isFileIncluded('aaa')); |
||||
$coverage->includes = array('x'); |
||||
$this->assertFalse($coverage->isFileIncluded('aaa')); |
||||
$coverage->excludes = array('aa'); |
||||
$this->assertFalse($coverage->isFileIncluded('aaa')); |
||||
} |
||||
|
||||
function testIsFileIncludedRegexp() { |
||||
$coverage = new CodeCoverage(); |
||||
$coverage->includes = array('modules/.*\.php$'); |
||||
$coverage->excludes = array('bad-bunny.php'); |
||||
$this->assertFalse($coverage->isFileIncluded('modules/a.test')); |
||||
$this->assertFalse($coverage->isFileIncluded('modules/bad-bunny.test')); |
||||
$this->assertTrue($coverage->isFileIncluded('modules/test.php')); |
||||
$this->assertFalse($coverage->isFileIncluded('module-bad/good-bunny.php')); |
||||
$this->assertTrue($coverage->isFileIncluded('modules/good-bunny.php')); |
||||
} |
||||
|
||||
function testIsDirectoryIncludedPastMaxDepth() { |
||||
$coverage = new CodeCoverage(); |
||||
$coverage->maxDirectoryDepth = 5; |
||||
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 1)); |
||||
$this->assertFalse($coverage->isDirectoryIncluded('aaa', 5)); |
||||
} |
||||
|
||||
function testIsDirectoryIncluded() { |
||||
$coverage = new CodeCoverage(); |
||||
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 0)); |
||||
$coverage->excludes = array('b$'); |
||||
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 0)); |
||||
$coverage->includes = array('a$'); // includes are ignore, all dirs are included unless excluded |
||||
$this->assertTrue($coverage->isDirectoryIncluded('aaa', 0)); |
||||
$coverage->excludes = array('.*a$'); |
||||
$this->assertFalse($coverage->isDirectoryIncluded('aaa', 0)); |
||||
} |
||||
|
||||
function testFilter() { |
||||
$coverage = new CodeCoverage(); |
||||
$data = array('a' => 0, 'b' => 0, 'c' => 0); |
||||
$coverage->includes = array('b'); |
||||
$coverage->filter($data); |
||||
$this->assertEqual(array('b' => 0), $data); |
||||
} |
||||
|
||||
function testUntouchedFiles() { |
||||
$coverage = new CodeCoverage(); |
||||
$touched = array_flip(array("test/coverage_test.php")); |
||||
$actual = array(); |
||||
$coverage->includes = array('coverage_test\.php$'); |
||||
$parentDir = realpath(dirname(__FILE__)); |
||||
$coverage->getUntouchedFiles($actual, $touched, $parentDir, $parentDir); |
||||
$this->assertEqual(array("coverage_test.php"), $actual); |
||||
} |
||||
|
||||
function testResetLog() { |
||||
$coverage = new CodeCoverage(); |
||||
$coverage->log = tempnam(NULL, 'php.xdebug.coverage.test.'); |
||||
$coverage->resetLog(); |
||||
$this->assertTrue(file_exists($coverage->log)); |
||||
} |
||||
|
||||
function testSettingsSerialization() { |
||||
$coverage = new CodeCoverage(); |
||||
$coverage->log = '/banana/boat'; |
||||
$coverage->includes = array('apple', 'orange'); |
||||
$coverage->excludes = array('tomato', 'pea'); |
||||
$data = $coverage->getSettings(); |
||||
$this->assertNotNull($data); |
||||
|
||||
$actual = new CodeCoverage(); |
||||
$actual->setSettings($data); |
||||
$this->assertEqual('/banana/boat', $actual->log); |
||||
$this->assertEqual(array('apple', 'orange'), $actual->includes); |
||||
$this->assertEqual(array('tomato', 'pea'), $actual->excludes); |
||||
} |
||||
|
||||
function testSettingsCanBeReadWrittenToDisk() { |
||||
$settings_file = 'banana-boat-coverage-settings-test.dat'; |
||||
$coverage = new CodeCoverage(); |
||||
$coverage->log = '/banana/boat'; |
||||
$coverage->settingsFile = $settings_file; |
||||
$coverage->writeSettings(); |
||||
|
||||
$actual = new CodeCoverage(); |
||||
$actual->settingsFile = $settings_file; |
||||
$actual->readSettings(); |
||||
$this->assertEqual('/banana/boat', $actual->log); |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,70 @@ |
||||
<?php |
||||
require_once dirname(__FILE__) . '/../../../autorun.php'; |
||||
|
||||
class CoverageUtilsTest extends UnitTestCase { |
||||
function skip() { |
||||
$this->skipIf( |
||||
!file_exists('DB/sqlite.php'), |
||||
'The Coverage extension needs to have PEAR installed'); |
||||
} |
||||
|
||||
function setUp() { |
||||
require_once dirname(__FILE__) .'/../coverage_utils.php'; |
||||
} |
||||
|
||||
function testMkdir() { |
||||
CoverageUtils::mkdir(dirname(__FILE__)); |
||||
try { |
||||
CoverageUtils::mkdir(__FILE__); |
||||
$this->fail("Should give error about cannot create dir of a file"); |
||||
} catch (Exception $expected) { |
||||
} |
||||
} |
||||
|
||||
function testIsPackageClassAvailable() { |
||||
$coverageSource = dirname(__FILE__) .'/../coverage_calculator.php'; |
||||
$this->assertTrue(CoverageUtils::isPackageClassAvailable($coverageSource, 'CoverageCalculator')); |
||||
$this->assertFalse(CoverageUtils::isPackageClassAvailable($coverageSource, 'BogusCoverage')); |
||||
$this->assertFalse(CoverageUtils::isPackageClassAvailable('bogus-file', 'BogusCoverage')); |
||||
$this->assertTrue(CoverageUtils::isPackageClassAvailable('bogus-file', 'CoverageUtils')); |
||||
} |
||||
|
||||
function testParseArgumentsMultiValue() { |
||||
$actual = CoverageUtils::parseArguments(array('scriptname', '--a=b', '--a=c'), True); |
||||
$expected = array('extraArguments' => array(), 'a' => 'c', 'a[]' => array('b', 'c')); |
||||
$this->assertEqual($expected, $actual); |
||||
} |
||||
|
||||
function testParseArguments() { |
||||
$actual = CoverageUtils::parseArguments(array('scriptname', '--a=b', '-c', 'xxx')); |
||||
$expected = array('a' => 'b', 'c' => '', 'extraArguments' => array('xxx')); |
||||
$this->assertEqual($expected, $actual); |
||||
} |
||||
|
||||
function testParseDoubleDashNoArguments() { |
||||
$actual = CoverageUtils::parseArguments(array('scriptname', '--aa')); |
||||
$this->assertTrue(isset($actual['aa'])); |
||||
} |
||||
|
||||
function testParseHyphenedExtraArguments() { |
||||
$actual = CoverageUtils::parseArguments(array('scriptname', '--alpha-beta=b', 'gamma-lambda')); |
||||
$expected = array('alpha-beta' => 'b', 'extraArguments' => array('gamma-lambda')); |
||||
$this->assertEqual($expected, $actual); |
||||
} |
||||
|
||||
function testAddItemAsArray() { |
||||
$actual = array(); |
||||
CoverageUtils::addItemAsArray($actual, 'bird', 'duck'); |
||||
$this->assertEqual(array('bird[]' => array('duck')), $actual); |
||||
|
||||
CoverageUtils::addItemAsArray(&$actual, 'bird', 'pigeon'); |
||||
$this->assertEqual(array('bird[]' => array('duck', 'pigeon')), $actual); |
||||
} |
||||
|
||||
function testIssetOr() { |
||||
$data = array('bird' => 'gull'); |
||||
$this->assertEqual('lab', CoverageUtils::issetOr($data['dog'], 'lab')); |
||||
$this->assertEqual('gull', CoverageUtils::issetOr($data['bird'], 'sparrow')); |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,4 @@ |
||||
<?php |
||||
// sample code |
||||
$x = 1 + 2; |
||||
if (false) echo "dead"; |
@ -0,0 +1,69 @@ |
||||
<?php |
||||
require_once(dirname(__FILE__) . '/../../../autorun.php'); |
||||
|
||||
class SimpleCoverageWriterTest extends UnitTestCase { |
||||
function skip() { |
||||
$this->skipIf( |
||||
!file_exists('DB/sqlite.php'), |
||||
'The Coverage extension needs to have PEAR installed'); |
||||
} |
||||
|
||||
function setUp() { |
||||
require_once dirname(__FILE__) .'/../simple_coverage_writer.php'; |
||||
require_once dirname(__FILE__) .'/../coverage_calculator.php'; |
||||
|
||||
} |
||||
|
||||
function testGenerateSummaryReport() { |
||||
$writer = new SimpleCoverageWriter(); |
||||
$coverage = array('file' => array(0, 1)); |
||||
$untouched = array('missed-file'); |
||||
$calc = new CoverageCalculator(); |
||||
$variables = $calc->variables($coverage, $untouched); |
||||
$variables['title'] = 'coverage'; |
||||
$out = fopen("php://memory", 'w'); |
||||
$writer->writeSummary($out, $variables); |
||||
$dom = self::dom($out); |
||||
$totalPercentCoverage = $dom->elements->xpath("//span[@class='totalPercentCoverage']"); |
||||
$this->assertEqual('50%', (string)$totalPercentCoverage[0]); |
||||
|
||||
$fileLinks = $dom->elements->xpath("//a[@class='byFileReportLink']"); |
||||
$fileLinkAttr = $fileLinks[0]->attributes(); |
||||
$this->assertEqual('file.html', $fileLinkAttr['href']); |
||||
$this->assertEqual('file', (string)($fileLinks[0])); |
||||
|
||||
$untouchedFile = $dom->elements->xpath("//span[@class='untouchedFile']"); |
||||
$this->assertEqual('missed-file', (string)$untouchedFile[0]); |
||||
} |
||||
|
||||
function testGenerateCoverageByFile() { |
||||
$writer = new SimpleCoverageWriter(); |
||||
$cov = array(3 => 1, 4 => -2); // 2 comments, 1 code, 1 dead (1-based indexes) |
||||
$out = fopen("php://memory", 'w'); |
||||
$file = dirname(__FILE__) .'/sample/code.php'; |
||||
$calc = new CoverageCalculator(); |
||||
$variables = $calc->coverageByFileVariables($file, $cov); |
||||
$variables['title'] = 'coverage'; |
||||
$writer->writeByFile($out, $variables); |
||||
$dom = self::dom($out); |
||||
|
||||
$cells = $dom->elements->xpath("//table[@id='code']/tbody/tr/td/span"); |
||||
$this->assertEqual("comment code", self::getAttribute($cells[1], 'class')); |
||||
$this->assertEqual("comment code", self::getAttribute($cells[3], 'class')); |
||||
$this->assertEqual("covered code", self::getAttribute($cells[5], 'class')); |
||||
$this->assertEqual("dead code", self::getAttribute($cells[7], 'class')); |
||||
} |
||||
|
||||
static function getAttribute($element, $attribute) { |
||||
$a = $element->attributes(); |
||||
return $a[$attribute]; |
||||
} |
||||
|
||||
static function dom($stream) { |
||||
rewind($stream); |
||||
$actual = stream_get_contents($stream); |
||||
$html = DOMDocument::loadHTML($actual); |
||||
return simplexml_import_dom($html); |
||||
} |
||||
} |
||||
?> |
@ -0,0 +1,14 @@ |
||||
<?php |
||||
// $Id: $ |
||||
require_once(dirname(__FILE__) . '/../../../autorun.php'); |
||||
|
||||
class CoverageUnitTests extends TestSuite { |
||||
function CoverageUnitTests() { |
||||
$this->TestSuite('Coverage Unit tests'); |
||||
$path = dirname(__FILE__) . '/*_test.php'; |
||||
foreach(glob($path) as $test) { |
||||
$this->addFile($test); |
||||
} |
||||
} |
||||
} |
||||
?> |
Loading…
Reference in new issue