diff options
author | Thomas Mueller <thomas.mueller@tmit.eu> | 2012-08-26 03:35:25 +0400 |
---|---|---|
committer | Thomas Mueller <thomas.mueller@tmit.eu> | 2012-08-26 03:36:06 +0400 |
commit | 27eb6655162743ada7f64ba4157e425ca86ad312 (patch) | |
tree | 3339f1f524411913222b64d3f09b1d0d616042b8 /3rdparty | |
parent | bae07faa3469ff1dcd16c2071946e6a3e7a8b4c2 (diff) |
adding coverage extension for simpletest
Diffstat (limited to '3rdparty')
21 files changed, 1361 insertions, 0 deletions
diff --git a/3rdparty/simpletest/extensions/coverage/autocoverage.php b/3rdparty/simpletest/extensions/coverage/autocoverage.php new file mode 100644 index 00000000000..9fc961bf43a --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/autocoverage.php @@ -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); + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/bin/php-coverage-close.php b/3rdparty/simpletest/extensions/coverage/bin/php-coverage-close.php new file mode 100755 index 00000000000..9a5a52ba134 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/bin/php-coverage-close.php @@ -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(); +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/bin/php-coverage-open.php b/3rdparty/simpletest/extensions/coverage/bin/php-coverage-open.php new file mode 100755 index 00000000000..c04e1fb512f --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/bin/php-coverage-open.php @@ -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(); +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/bin/php-coverage-report.php b/3rdparty/simpletest/extensions/coverage/bin/php-coverage-report.php new file mode 100755 index 00000000000..d61c822d997 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/bin/php-coverage-report.php @@ -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(); +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/coverage.php b/3rdparty/simpletest/extensions/coverage/coverage.php new file mode 100644 index 00000000000..44e5b679b82 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/coverage.php @@ -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; + } +} +?> diff --git a/3rdparty/simpletest/extensions/coverage/coverage_calculator.php b/3rdparty/simpletest/extensions/coverage/coverage_calculator.php new file mode 100644 index 00000000000..f1aa57bbab5 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/coverage_calculator.php @@ -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; + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/coverage_data_handler.php b/3rdparty/simpletest/extensions/coverage/coverage_data_handler.php new file mode 100644 index 00000000000..bbf81106fc5 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/coverage_data_handler.php @@ -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; + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/coverage_reporter.php b/3rdparty/simpletest/extensions/coverage/coverage_reporter.php new file mode 100644 index 00000000000..ba4e7161c2f --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/coverage_reporter.php @@ -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'; + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/coverage_utils.php b/3rdparty/simpletest/extensions/coverage/coverage_utils.php new file mode 100644 index 00000000000..d2c3a635f43 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/coverage_utils.php @@ -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; + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/coverage_writer.php b/3rdparty/simpletest/extensions/coverage/coverage_writer.php new file mode 100644 index 00000000000..0a8519cb509 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/coverage_writer.php @@ -0,0 +1,16 @@ +<?php +/** + * @package SimpleTest + * @subpackage Extensions + */ +/** + * @package SimpleTest + * @subpackage Extensions + */ +interface CoverageWriter { + + function writeSummary($out, $variables); + + function writeByFile($out, $variables); +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/simple_coverage_writer.php b/3rdparty/simpletest/extensions/coverage/simple_coverage_writer.php new file mode 100644 index 00000000000..7eb73fc8ab9 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/simple_coverage_writer.php @@ -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(); + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/templates/file.php b/3rdparty/simpletest/extensions/coverage/templates/file.php new file mode 100644 index 00000000000..70f6903068c --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/templates/file.php @@ -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> diff --git a/3rdparty/simpletest/extensions/coverage/templates/index.php b/3rdparty/simpletest/extensions/coverage/templates/index.php new file mode 100644 index 00000000000..e4374e23809 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/templates/index.php @@ -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> diff --git a/3rdparty/simpletest/extensions/coverage/test/coverage_calculator_test.php b/3rdparty/simpletest/extensions/coverage/test/coverage_calculator_test.php new file mode 100644 index 00000000000..64bd8d463fb --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/coverage_calculator_test.php @@ -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); + } +} + +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/test/coverage_data_handler_test.php b/3rdparty/simpletest/extensions/coverage/test/coverage_data_handler_test.php new file mode 100644 index 00000000000..54c67a4a7b0 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/coverage_data_handler_test.php @@ -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'); + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/test/coverage_reporter_test.php b/3rdparty/simpletest/extensions/coverage/test/coverage_reporter_test.php new file mode 100644 index 00000000000..a8b09962a04 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/coverage_reporter_test.php @@ -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")); + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/test/coverage_test.php b/3rdparty/simpletest/extensions/coverage/test/coverage_test.php new file mode 100644 index 00000000000..f09d03f78a1 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/coverage_test.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); + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/test/coverage_utils_test.php b/3rdparty/simpletest/extensions/coverage/test/coverage_utils_test.php new file mode 100644 index 00000000000..b900c5d2c43 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/coverage_utils_test.php @@ -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')); + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/test/sample/code.php b/3rdparty/simpletest/extensions/coverage/test/sample/code.php new file mode 100644 index 00000000000..a2438f4bee0 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/sample/code.php @@ -0,0 +1,4 @@ +<?php +// sample code +$x = 1 + 2; +if (false) echo "dead";
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/test/simple_coverage_writer_test.php b/3rdparty/simpletest/extensions/coverage/test/simple_coverage_writer_test.php new file mode 100644 index 00000000000..2c9f9abc18b --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/simple_coverage_writer_test.php @@ -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); + } +} +?>
\ No newline at end of file diff --git a/3rdparty/simpletest/extensions/coverage/test/test.php b/3rdparty/simpletest/extensions/coverage/test/test.php new file mode 100644 index 00000000000..0af4dbf3e74 --- /dev/null +++ b/3rdparty/simpletest/extensions/coverage/test/test.php @@ -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); + } + } +} +?>
\ No newline at end of file |