CC-2166: Packaging Improvements. Moved the Zend app into airtime_mvc. It is now installed to /var/www/airtime. Storage is now set to /srv/airtime/stor. Utils are now installed to /usr/lib/airtime/utils/. Added install/airtime-dircheck.php as a simple test to see if everything is install/uninstalled correctly.
This commit is contained in:
parent
514777e8d2
commit
b11cbd8159
4546 changed files with 138 additions and 51 deletions
124
airtime_mvc/library/phing/tasks/ext/coverage/CoverageMerger.php
Normal file
124
airtime_mvc/library/phing/tasks/ext/coverage/CoverageMerger.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
/**
|
||||
* $Id: CoverageMerger.php 905 2010-10-05 16:28:03Z mrook $
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information please see
|
||||
* <http://phing.info>.
|
||||
*/
|
||||
|
||||
require_once 'phing/system/util/Properties.php';
|
||||
|
||||
/**
|
||||
* Saves coverage output of the test to a specified database
|
||||
*
|
||||
* @author Michiel Rook <michiel.rook@gmail.com>
|
||||
* @version $Id: CoverageMerger.php 905 2010-10-05 16:28:03Z mrook $
|
||||
* @package phing.tasks.ext.coverage
|
||||
* @since 2.1.0
|
||||
*/
|
||||
class CoverageMerger
|
||||
{
|
||||
private static function mergeCodeCoverage($left, $right)
|
||||
{
|
||||
$coverageMerged = array();
|
||||
|
||||
reset($left);
|
||||
reset($right);
|
||||
|
||||
while (current($left) && current($right)) {
|
||||
$linenr_left = key($left);
|
||||
$linenr_right = key($right);
|
||||
|
||||
if ($linenr_left < $linenr_right) {
|
||||
$coverageMerged[$linenr_left] = current($left);
|
||||
next($left);
|
||||
} elseif ($linenr_right < $linenr_left) {
|
||||
$coverageMerged[$linenr_right] = current($right);
|
||||
next($right);
|
||||
} else {
|
||||
if ((current($left) < 0) || (current($right) < 0)) {
|
||||
$coverageMerged[$linenr_right] = current($right);
|
||||
} else {
|
||||
$coverageMerged[$linenr_right] = current($left) + current($right);
|
||||
}
|
||||
|
||||
next($left);
|
||||
next($right);
|
||||
}
|
||||
}
|
||||
|
||||
while (current($left)) {
|
||||
$coverageMerged[key($left)] = current($left);
|
||||
next($left);
|
||||
}
|
||||
|
||||
while (current($right)) {
|
||||
$coverageMerged[key($right)] = current($right);
|
||||
next($right);
|
||||
}
|
||||
|
||||
return $coverageMerged;
|
||||
}
|
||||
|
||||
static function merge($project, $codeCoverageInformation)
|
||||
{
|
||||
$coverageDatabase = $project->getProperty('coverage.database');
|
||||
|
||||
if (!$coverageDatabase) {
|
||||
throw new BuildException("Property coverage.database is not set - please include coverage-setup in your build file");
|
||||
}
|
||||
|
||||
$database = new PhingFile($coverageDatabase);
|
||||
|
||||
$props = new Properties();
|
||||
$props->load($database);
|
||||
|
||||
$coverageTotal = $codeCoverageInformation;
|
||||
|
||||
foreach ($coverageTotal as $filename => $data) {
|
||||
$lines = array();
|
||||
$filename = strtolower($filename);
|
||||
|
||||
if ($props->getProperty($filename) != null) {
|
||||
foreach ($data as $_line => $_data) {
|
||||
if (is_array($_data)) {
|
||||
$count = count($_data);
|
||||
} else if ($_data == -1) {
|
||||
// not executed
|
||||
$count = -1;
|
||||
} else if ($_data == -2) {
|
||||
// dead code
|
||||
$count = -2;
|
||||
}
|
||||
|
||||
$lines[$_line] = $count;
|
||||
}
|
||||
|
||||
ksort($lines);
|
||||
|
||||
$file = unserialize($props->getProperty($filename));
|
||||
$left = $file['coverage'];
|
||||
|
||||
$coverageMerged = CoverageMerger::mergeCodeCoverage($left, $lines);
|
||||
|
||||
$file['coverage'] = $coverageMerged;
|
||||
$props->setProperty($filename, serialize($file));
|
||||
}
|
||||
}
|
||||
|
||||
$props->store($database);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/**
|
||||
* $Id: CoverageMergerTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information please see
|
||||
* <http://phing.info>.
|
||||
*/
|
||||
|
||||
require_once 'phing/Task.php';
|
||||
require_once 'phing/system/io/PhingFile.php';
|
||||
require_once 'phing/system/io/Writer.php';
|
||||
require_once 'phing/system/util/Properties.php';
|
||||
require_once 'phing/tasks/ext/coverage/CoverageMerger.php';
|
||||
|
||||
/**
|
||||
* Merges code coverage snippets into a code coverage database
|
||||
*
|
||||
* @author Michiel Rook <michiel.rook@gmail.com>
|
||||
* @version $Id: CoverageMergerTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
* @package phing.tasks.ext.coverage
|
||||
* @since 2.1.0
|
||||
*/
|
||||
class CoverageMergerTask extends Task
|
||||
{
|
||||
/** the list of filesets containing the .php filename rules */
|
||||
private $filesets = array();
|
||||
|
||||
/**
|
||||
* Add a new fileset containing the .php files to process
|
||||
*
|
||||
* @param FileSet the new fileset containing .php files
|
||||
*/
|
||||
function addFileSet(FileSet $fileset)
|
||||
{
|
||||
$this->filesets[] = $fileset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all filesets and return all the filenames.
|
||||
*
|
||||
* @return array an array of filenames
|
||||
*/
|
||||
private function getFilenames()
|
||||
{
|
||||
$files = array();
|
||||
|
||||
foreach ($this->filesets as $fileset)
|
||||
{
|
||||
$ds = $fileset->getDirectoryScanner($this->project);
|
||||
$ds->scan();
|
||||
|
||||
$includedFiles = $ds->getIncludedFiles();
|
||||
|
||||
foreach ($includedFiles as $file)
|
||||
{
|
||||
$fs = new PhingFile(basename($ds->getBaseDir()), $file);
|
||||
|
||||
$files[] = $fs->getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
$files = $this->getFilenames();
|
||||
|
||||
$this->log("Merging " . count($files) . " coverage files");
|
||||
|
||||
foreach ($files as $file)
|
||||
{
|
||||
$coverageInformation = unserialize(file_get_contents($file));
|
||||
|
||||
CoverageMerger::merge($this->project, array($coverageInformation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,556 @@
|
|||
<?php
|
||||
/**
|
||||
* $Id: CoverageReportTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information please see
|
||||
* <http://phing.info>.
|
||||
*/
|
||||
|
||||
require_once 'phing/Task.php';
|
||||
require_once 'phing/system/io/PhingFile.php';
|
||||
require_once 'phing/system/io/Writer.php';
|
||||
require_once 'phing/system/util/Properties.php';
|
||||
require_once 'phing/tasks/ext/phpunit/PHPUnitUtil.php';
|
||||
require_once 'phing/tasks/ext/coverage/CoverageReportTransformer.php';
|
||||
|
||||
/**
|
||||
* Transforms information in a code coverage database to XML
|
||||
*
|
||||
* @author Michiel Rook <michiel.rook@gmail.com>
|
||||
* @version $Id: CoverageReportTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
* @package phing.tasks.ext.coverage
|
||||
* @since 2.1.0
|
||||
*/
|
||||
class CoverageReportTask extends Task
|
||||
{
|
||||
private $outfile = "coverage.xml";
|
||||
|
||||
private $transformers = array();
|
||||
|
||||
/** the classpath to use (optional) */
|
||||
private $classpath = NULL;
|
||||
|
||||
/** the path to the GeSHi library (optional) */
|
||||
private $geshipath = "";
|
||||
|
||||
/** the path to the GeSHi language files (optional) */
|
||||
private $geshilanguagespath = "";
|
||||
|
||||
function setClasspath(Path $classpath)
|
||||
{
|
||||
if ($this->classpath === null)
|
||||
{
|
||||
$this->classpath = $classpath;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->classpath->append($classpath);
|
||||
}
|
||||
}
|
||||
|
||||
function createClasspath()
|
||||
{
|
||||
$this->classpath = new Path();
|
||||
return $this->classpath;
|
||||
}
|
||||
|
||||
function setGeshiPath($path)
|
||||
{
|
||||
$this->geshipath = $path;
|
||||
}
|
||||
|
||||
function setGeshiLanguagesPath($path)
|
||||
{
|
||||
$this->geshilanguagespath = $path;
|
||||
}
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->doc = new DOMDocument();
|
||||
$this->doc->encoding = 'UTF-8';
|
||||
$this->doc->formatOutput = true;
|
||||
$this->doc->appendChild($this->doc->createElement('snapshot'));
|
||||
}
|
||||
|
||||
function setOutfile($outfile)
|
||||
{
|
||||
$this->outfile = $outfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a report based on the XML created by this task
|
||||
*/
|
||||
function createReport()
|
||||
{
|
||||
$transformer = new CoverageReportTransformer($this);
|
||||
$this->transformers[] = $transformer;
|
||||
return $transformer;
|
||||
}
|
||||
|
||||
protected function getPackageElement($packageName)
|
||||
{
|
||||
$packages = $this->doc->documentElement->getElementsByTagName('package');
|
||||
|
||||
foreach ($packages as $package)
|
||||
{
|
||||
if ($package->getAttribute('name') == $packageName)
|
||||
{
|
||||
return $package;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
protected function addClassToPackage($classname, $element)
|
||||
{
|
||||
$packageName = PHPUnitUtil::getPackageName($classname);
|
||||
|
||||
$package = $this->getPackageElement($packageName);
|
||||
|
||||
if ($package === NULL)
|
||||
{
|
||||
$package = $this->doc->createElement('package');
|
||||
$package->setAttribute('name', $packageName);
|
||||
$this->doc->documentElement->appendChild($package);
|
||||
}
|
||||
|
||||
$package->appendChild($element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subpackage to their package
|
||||
*
|
||||
* @param string $packageName The name of the package
|
||||
* @param string $subpackageName The name of the subpackage
|
||||
*
|
||||
* @author Benjamin Schultz <bschultz@proqrent.de>
|
||||
* @return void
|
||||
*/
|
||||
protected function addSubpackageToPackage($packageName, $subpackageName)
|
||||
{
|
||||
$package = $this->getPackageElement($packageName);
|
||||
$subpackage = $this->getSubpackageElement($subpackageName);
|
||||
|
||||
if ($package === null) {
|
||||
$package = $this->doc->createElement('package');
|
||||
$package->setAttribute('name', $packageName);
|
||||
$this->doc->documentElement->appendChild($package);
|
||||
}
|
||||
|
||||
if ($subpackage === null) {
|
||||
$subpackage = $this->doc->createElement('subpackage');
|
||||
$subpackage->setAttribute('name', $subpackageName);
|
||||
}
|
||||
|
||||
$package->appendChild($subpackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subpackage element
|
||||
*
|
||||
* @param string $subpackageName The name of the subpackage
|
||||
*
|
||||
* @author Benjamin Schultz <bschultz@proqrent.de>
|
||||
* @return DOMNode|null null when no DOMNode with the given name exists
|
||||
*/
|
||||
protected function getSubpackageElement($subpackageName)
|
||||
{
|
||||
$subpackages = $this->doc->documentElement->getElementsByTagName('subpackage');
|
||||
|
||||
foreach ($subpackages as $subpackage) {
|
||||
if ($subpackage->getAttribute('name') == $subpackageName) {
|
||||
return $subpackage;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a class to their subpackage
|
||||
*
|
||||
* @param string $classname The name of the class
|
||||
* @param DOMNode $element The dom node to append to the subpackage element
|
||||
*
|
||||
* @author Benjamin Schultz <bschultz@proqrent.de>
|
||||
* @return void
|
||||
*/
|
||||
protected function addClassToSubpackage($classname, $element)
|
||||
{
|
||||
$subpackageName = PHPUnitUtil::getSubpackageName($classname);
|
||||
|
||||
$subpackage = $this->getSubpackageElement($subpackageName);
|
||||
|
||||
if ($subpackage === null) {
|
||||
$subpackage = $this->doc->createElement('subpackage');
|
||||
$subpackage->setAttribute('name', $subpackageName);
|
||||
$this->doc->documentElement->appendChild($subpackage);
|
||||
}
|
||||
|
||||
$subpackage->appendChild($element);
|
||||
}
|
||||
|
||||
protected function stripDiv($source)
|
||||
{
|
||||
$openpos = strpos($source, "<div");
|
||||
$closepos = strpos($source, ">", $openpos);
|
||||
|
||||
$line = substr($source, $closepos + 1);
|
||||
|
||||
$tagclosepos = strpos($line, "</div>");
|
||||
|
||||
$line = substr($line, 0, $tagclosepos);
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
protected function highlightSourceFile($filename)
|
||||
{
|
||||
if ($this->geshipath)
|
||||
{
|
||||
require_once $this->geshipath . '/geshi.php';
|
||||
|
||||
$source = file_get_contents($filename);
|
||||
|
||||
$geshi = new GeSHi($source, 'php', $this->geshilanguagespath);
|
||||
|
||||
$geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
|
||||
|
||||
$geshi->enable_strict_mode(true);
|
||||
|
||||
$geshi->enable_classes(true);
|
||||
|
||||
$geshi->set_url_for_keyword_group(3, '');
|
||||
|
||||
$html = $geshi->parse_code();
|
||||
|
||||
$lines = split("<li>|</li>", $html);
|
||||
|
||||
// skip first and last line
|
||||
array_pop($lines);
|
||||
array_shift($lines);
|
||||
|
||||
$lines = array_filter($lines);
|
||||
|
||||
$lines = array_map(array($this, 'stripDiv'), $lines);
|
||||
|
||||
return $lines;
|
||||
}
|
||||
else
|
||||
{
|
||||
$lines = file($filename);
|
||||
|
||||
for ($i = 0; $i < count($lines); $i++)
|
||||
{
|
||||
$line = $lines[$i];
|
||||
|
||||
$line = rtrim($line);
|
||||
|
||||
if (function_exists('mb_convert_encoding'))
|
||||
{
|
||||
$lines[$i] = mb_convert_encoding($line, 'UTF-8');
|
||||
}
|
||||
else
|
||||
{
|
||||
$lines[$i] = utf8_encode($line);
|
||||
}
|
||||
}
|
||||
|
||||
return $lines;
|
||||
}
|
||||
}
|
||||
|
||||
protected function transformSourceFile($filename, $coverageInformation, $classStartLine = 1)
|
||||
{
|
||||
$sourceElement = $this->doc->createElement('sourcefile');
|
||||
$sourceElement->setAttribute('name', basename($filename));
|
||||
|
||||
/**
|
||||
* Add original/full filename to document
|
||||
*/
|
||||
$sourceElement->setAttribute('sourcefile', $filename);
|
||||
|
||||
$filelines = $this->highlightSourceFile($filename);
|
||||
|
||||
$linenr = 1;
|
||||
|
||||
foreach ($filelines as $line)
|
||||
{
|
||||
$lineElement = $this->doc->createElement('sourceline');
|
||||
$lineElement->setAttribute('coveredcount', (isset($coverageInformation[$linenr]) ? $coverageInformation[$linenr] : '0'));
|
||||
|
||||
if ($linenr == $classStartLine)
|
||||
{
|
||||
$lineElement->setAttribute('startclass', 1);
|
||||
}
|
||||
|
||||
$textnode = $this->doc->createTextNode($line);
|
||||
$lineElement->appendChild($textnode);
|
||||
|
||||
$sourceElement->appendChild($lineElement);
|
||||
|
||||
$linenr++;
|
||||
}
|
||||
|
||||
return $sourceElement;
|
||||
}
|
||||
|
||||
protected function filterCovered($var)
|
||||
{
|
||||
return ($var >= 0 || $var == -2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the coverage information
|
||||
*
|
||||
* @param string $filename The filename
|
||||
* @param array $coverageInformation Array with covergae information
|
||||
*
|
||||
* @author Michiel Rook <michiel.rook@gmail.com>
|
||||
* @author Benjamin Schultz <bschultz@proqrent.de>
|
||||
* @return void
|
||||
*/
|
||||
protected function transformCoverageInformation($filename, $coverageInformation)
|
||||
{
|
||||
$classes = PHPUnitUtil::getDefinedClasses($filename, $this->classpath);
|
||||
|
||||
if (is_array($classes))
|
||||
{
|
||||
foreach ($classes as $classname)
|
||||
{
|
||||
$reflection = new ReflectionClass($classname);
|
||||
|
||||
$methods = $reflection->getMethods();
|
||||
|
||||
$classElement = $this->doc->createElement('class');
|
||||
$classElement->setAttribute('name', $reflection->getName());
|
||||
|
||||
$packageName = PHPUnitUtil::getPackageName($reflection->getName());
|
||||
$subpackageName = PHPUnitUtil::getSubpackageName($reflection->getName());
|
||||
|
||||
if ($subpackageName !== null) {
|
||||
$this->addSubpackageToPackage($packageName, $subpackageName);
|
||||
$this->addClassToSubpackage($reflection->getName(), $classElement);
|
||||
} else {
|
||||
$this->addClassToPackage($reflection->getName(), $classElement);
|
||||
}
|
||||
|
||||
$classStartLine = $reflection->getStartLine();
|
||||
|
||||
$methodscovered = 0;
|
||||
$methodcount = 0;
|
||||
|
||||
// Strange PHP5 reflection bug, classes without parent class or implemented interfaces seem to start one line off
|
||||
if ($reflection->getParentClass() == NULL && count($reflection->getInterfaces()) == 0)
|
||||
{
|
||||
unset($coverageInformation[$classStartLine + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($coverageInformation[$classStartLine]);
|
||||
}
|
||||
|
||||
reset($coverageInformation);
|
||||
|
||||
foreach ($methods as $method)
|
||||
{
|
||||
// PHP5 reflection considers methods of a parent class to be part of a subclass, we don't
|
||||
if ($method->getDeclaringClass()->getName() != $reflection->getName())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// small fix for XDEBUG_CC_UNUSED
|
||||
if (isset($coverageInformation[$method->getStartLine()]))
|
||||
{
|
||||
unset($coverageInformation[$method->getStartLine()]);
|
||||
}
|
||||
|
||||
if (isset($coverageInformation[$method->getEndLine()]))
|
||||
{
|
||||
unset($coverageInformation[$method->getEndLine()]);
|
||||
}
|
||||
|
||||
if ($method->isAbstract())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$linenr = key($coverageInformation);
|
||||
|
||||
while ($linenr !== null && $linenr < $method->getStartLine())
|
||||
{
|
||||
next($coverageInformation);
|
||||
$linenr = key($coverageInformation);
|
||||
}
|
||||
|
||||
$methodCoveredCount = 0;
|
||||
$methodTotalCount = 0;
|
||||
|
||||
$methodHasCoveredLine = false;
|
||||
|
||||
while ($linenr !== null && $linenr <= $method->getEndLine()) {
|
||||
$methodTotalCount++;
|
||||
$methodHasCoveredLine = true;
|
||||
|
||||
// set covered when CODE is other than -1 (not executed)
|
||||
if ($coverageInformation[$linenr] > 0 || $coverageInformation[$linenr] == -2) {
|
||||
$methodCoveredCount++;
|
||||
}
|
||||
|
||||
next($coverageInformation);
|
||||
$linenr = key($coverageInformation);
|
||||
}
|
||||
|
||||
if (($methodTotalCount == $methodCoveredCount) && $methodHasCoveredLine) {
|
||||
$methodscovered++;
|
||||
}
|
||||
|
||||
$methodcount++;
|
||||
}
|
||||
|
||||
$statementcount = count($coverageInformation);
|
||||
$statementscovered = count(array_filter($coverageInformation, array($this, 'filterCovered')));
|
||||
|
||||
$classElement->appendChild($this->transformSourceFile($filename, $coverageInformation, $classStartLine));
|
||||
|
||||
$classElement->setAttribute('methodcount', $methodcount);
|
||||
$classElement->setAttribute('methodscovered', $methodscovered);
|
||||
$classElement->setAttribute('statementcount', $statementcount);
|
||||
$classElement->setAttribute('statementscovered', $statementscovered);
|
||||
$classElement->setAttribute('totalcount', $methodcount + $statementcount);
|
||||
$classElement->setAttribute('totalcovered', $methodscovered + $statementscovered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function calculateStatistics()
|
||||
{
|
||||
$packages = $this->doc->documentElement->getElementsByTagName('package');
|
||||
|
||||
$totalmethodcount = 0;
|
||||
$totalmethodscovered = 0;
|
||||
|
||||
$totalstatementcount = 0;
|
||||
$totalstatementscovered = 0;
|
||||
|
||||
foreach ($packages as $package) {
|
||||
$methodcount = 0;
|
||||
$methodscovered = 0;
|
||||
|
||||
$statementcount = 0;
|
||||
$statementscovered = 0;
|
||||
|
||||
$subpackages = $package->getElementsByTagName('subpackage');
|
||||
|
||||
foreach ($subpackages as $subpackage) {
|
||||
$subpackageMethodCount = 0;
|
||||
$subpackageMethodsCovered = 0;
|
||||
|
||||
$subpackageStatementCount = 0;
|
||||
$subpackageStatementsCovered = 0;
|
||||
|
||||
$subpackageClasses = $subpackage->getElementsByTagName('class');
|
||||
|
||||
foreach ($subpackageClasses as $subpackageClass) {
|
||||
$subpackageMethodCount += $subpackageClass->getAttribute('methodcount');
|
||||
$subpackageMethodsCovered += $subpackageClass->getAttribute('methodscovered');
|
||||
|
||||
$subpackageStatementCount += $subpackageClass->getAttribute('statementcount');
|
||||
$subpackageStatementsCovered += $subpackageClass->getAttribute('statementscovered');
|
||||
}
|
||||
|
||||
$subpackage->setAttribute('methodcount', $subpackageMethodCount);
|
||||
$subpackage->setAttribute('methodscovered', $subpackageMethodsCovered);
|
||||
|
||||
$subpackage->setAttribute('statementcount', $subpackageStatementCount);
|
||||
$subpackage->setAttribute('statementscovered', $subpackageStatementsCovered);
|
||||
|
||||
$subpackage->setAttribute('totalcount', $subpackageMethodCount + $subpackageStatementCount);
|
||||
$subpackage->setAttribute('totalcovered', $subpackageMethodsCovered + $subpackageStatementsCovered);
|
||||
}
|
||||
|
||||
$classes = $package->getElementsByTagName('class');
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$methodcount += $class->getAttribute('methodcount');
|
||||
$methodscovered += $class->getAttribute('methodscovered');
|
||||
|
||||
$statementcount += $class->getAttribute('statementcount');
|
||||
$statementscovered += $class->getAttribute('statementscovered');
|
||||
}
|
||||
|
||||
$package->setAttribute('methodcount', $methodcount);
|
||||
$package->setAttribute('methodscovered', $methodscovered);
|
||||
|
||||
$package->setAttribute('statementcount', $statementcount);
|
||||
$package->setAttribute('statementscovered', $statementscovered);
|
||||
|
||||
$package->setAttribute('totalcount', $methodcount + $statementcount);
|
||||
$package->setAttribute('totalcovered', $methodscovered + $statementscovered);
|
||||
|
||||
$totalmethodcount += $methodcount;
|
||||
$totalmethodscovered += $methodscovered;
|
||||
|
||||
$totalstatementcount += $statementcount;
|
||||
$totalstatementscovered += $statementscovered;
|
||||
}
|
||||
|
||||
$this->doc->documentElement->setAttribute('methodcount', $totalmethodcount);
|
||||
$this->doc->documentElement->setAttribute('methodscovered', $totalmethodscovered);
|
||||
|
||||
$this->doc->documentElement->setAttribute('statementcount', $totalstatementcount);
|
||||
$this->doc->documentElement->setAttribute('statementscovered', $totalstatementscovered);
|
||||
|
||||
$this->doc->documentElement->setAttribute('totalcount', $totalmethodcount + $totalstatementcount);
|
||||
$this->doc->documentElement->setAttribute('totalcovered', $totalmethodscovered + $totalstatementscovered);
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
$coverageDatabase = $this->project->getProperty('coverage.database');
|
||||
|
||||
if (!$coverageDatabase)
|
||||
{
|
||||
throw new BuildException("Property coverage.database is not set - please include coverage-setup in your build file");
|
||||
}
|
||||
|
||||
$database = new PhingFile($coverageDatabase);
|
||||
|
||||
$this->log("Transforming coverage report");
|
||||
|
||||
$props = new Properties();
|
||||
$props->load($database);
|
||||
|
||||
foreach ($props->keys() as $filename)
|
||||
{
|
||||
$file = unserialize($props->getProperty($filename));
|
||||
|
||||
$this->transformCoverageInformation($file['fullname'], $file['coverage']);
|
||||
}
|
||||
|
||||
$this->calculateStatistics();
|
||||
|
||||
$this->doc->save($this->outfile);
|
||||
|
||||
foreach ($this->transformers as $transformer)
|
||||
{
|
||||
$transformer->setXmlDocument($this->doc);
|
||||
$transformer->transform();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
/**
|
||||
* $Id: CoverageReportTransformer.php 905 2010-10-05 16:28:03Z mrook $
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information please see
|
||||
* <http://phing.info>.
|
||||
*/
|
||||
|
||||
require_once 'phing/Task.php';
|
||||
require_once 'phing/system/io/PhingFile.php';
|
||||
require_once 'phing/system/io/FileWriter.php';
|
||||
require_once 'phing/util/ExtendedFileStream.php';
|
||||
|
||||
/**
|
||||
* Transform a Phing/Xdebug code coverage xml report.
|
||||
* The default transformation generates an html report in framed style.
|
||||
*
|
||||
* @author Michiel Rook <michiel.rook@gmail.com>
|
||||
* @version $Id: CoverageReportTransformer.php 905 2010-10-05 16:28:03Z mrook $
|
||||
* @package phing.tasks.ext.coverage
|
||||
* @since 2.1.0
|
||||
*/
|
||||
class CoverageReportTransformer
|
||||
{
|
||||
private $task = NULL;
|
||||
private $styleDir = "";
|
||||
private $toDir = "";
|
||||
private $document = NULL;
|
||||
|
||||
function __construct(Task $task)
|
||||
{
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
function setStyleDir($styleDir)
|
||||
{
|
||||
$this->styleDir = $styleDir;
|
||||
}
|
||||
|
||||
function setToDir($toDir)
|
||||
{
|
||||
$this->toDir = $toDir;
|
||||
}
|
||||
|
||||
function setXmlDocument($document)
|
||||
{
|
||||
$this->document = $document;
|
||||
}
|
||||
|
||||
function transform()
|
||||
{
|
||||
$dir = new PhingFile($this->toDir);
|
||||
|
||||
if (!$dir->exists())
|
||||
{
|
||||
throw new BuildException("Directory '" . $this->toDir . "' does not exist");
|
||||
}
|
||||
|
||||
$xslfile = $this->getStyleSheet();
|
||||
|
||||
$xsl = new DOMDocument();
|
||||
$xsl->load($xslfile->getAbsolutePath());
|
||||
|
||||
$proc = new XSLTProcessor();
|
||||
$proc->importStyleSheet($xsl);
|
||||
|
||||
ExtendedFileStream::registerStream();
|
||||
|
||||
// no output for the framed report
|
||||
// it's all done by extension...
|
||||
$proc->setParameter('', 'output.dir', $dir->toString());
|
||||
$proc->transformToXML($this->document);
|
||||
|
||||
ExtendedFileStream::unregisterStream();
|
||||
}
|
||||
|
||||
private function getStyleSheet()
|
||||
{
|
||||
$xslname = "coverage-frames.xsl";
|
||||
|
||||
if ($this->styleDir)
|
||||
{
|
||||
$file = new PhingFile($this->styleDir, $xslname);
|
||||
}
|
||||
else
|
||||
{
|
||||
$path = Phing::getResourcePath("phing/etc/$xslname");
|
||||
|
||||
if ($path === NULL)
|
||||
{
|
||||
$path = Phing::getResourcePath("etc/$xslname");
|
||||
|
||||
if ($path === NULL)
|
||||
{
|
||||
throw new BuildException("Could not find $xslname in resource path");
|
||||
}
|
||||
}
|
||||
|
||||
$file = new PhingFile($path);
|
||||
}
|
||||
|
||||
if (!$file->exists())
|
||||
{
|
||||
throw new BuildException("Could not find file " . $file->getPath());
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
/**
|
||||
* $Id: CoverageSetupTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information please see
|
||||
* <http://phing.info>.
|
||||
*/
|
||||
|
||||
require_once 'phing/Task.php';
|
||||
require_once 'phing/system/io/PhingFile.php';
|
||||
require_once 'phing/system/io/Writer.php';
|
||||
require_once 'phing/system/util/Properties.php';
|
||||
require_once 'phing/tasks/ext/coverage/CoverageMerger.php';
|
||||
|
||||
/**
|
||||
* Initializes a code coverage database
|
||||
*
|
||||
* @author Michiel Rook <michiel.rook@gmail.com>
|
||||
* @version $Id: CoverageSetupTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
* @package phing.tasks.ext.coverage
|
||||
* @since 2.1.0
|
||||
*/
|
||||
class CoverageSetupTask extends Task
|
||||
{
|
||||
/** the list of filesets containing the .php filename rules */
|
||||
private $filesets = array();
|
||||
|
||||
/** the filename of the coverage database */
|
||||
private $database = "coverage.db";
|
||||
|
||||
/** the classpath to use (optional) */
|
||||
private $classpath = NULL;
|
||||
|
||||
/**
|
||||
* Add a new fileset containing the .php files to process
|
||||
*
|
||||
* @param FileSet the new fileset containing .php files
|
||||
*/
|
||||
function addFileSet(FileSet $fileset)
|
||||
{
|
||||
$this->filesets[] = $fileset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filename of the coverage database to use
|
||||
*
|
||||
* @param string the filename of the database
|
||||
*/
|
||||
function setDatabase($database)
|
||||
{
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
function setClasspath(Path $classpath)
|
||||
{
|
||||
if ($this->classpath === null)
|
||||
{
|
||||
$this->classpath = $classpath;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->classpath->append($classpath);
|
||||
}
|
||||
}
|
||||
|
||||
function createClasspath()
|
||||
{
|
||||
$this->classpath = new Path();
|
||||
return $this->classpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all filesets and return the filename of all files.
|
||||
*
|
||||
* @return array an array of (basedir, filenames) pairs
|
||||
*/
|
||||
private function getFilenames()
|
||||
{
|
||||
$files = array();
|
||||
|
||||
foreach ($this->filesets as $fileset)
|
||||
{
|
||||
$ds = $fileset->getDirectoryScanner($this->project);
|
||||
$ds->scan();
|
||||
|
||||
$includedFiles = $ds->getIncludedFiles();
|
||||
|
||||
foreach ($includedFiles as $file)
|
||||
{
|
||||
$fs = new PhingFile(realpath($ds->getBaseDir()), $file);
|
||||
|
||||
$files[] = array('key' => strtolower($fs->getAbsolutePath()), 'fullname' => $fs->getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
function init()
|
||||
{
|
||||
}
|
||||
|
||||
function main()
|
||||
{
|
||||
$files = $this->getFilenames();
|
||||
|
||||
$this->log("Setting up coverage database for " . count($files) . " files");
|
||||
|
||||
$props = new Properties();
|
||||
|
||||
foreach ($files as $file)
|
||||
{
|
||||
$fullname = $file['fullname'];
|
||||
$filename = $file['key'];
|
||||
|
||||
$props->setProperty($filename, serialize(array('fullname' => $fullname, 'coverage' => array())));
|
||||
}
|
||||
|
||||
$dbfile = new PhingFile($this->database);
|
||||
|
||||
$props->store($dbfile);
|
||||
|
||||
$this->project->setProperty('coverage.database', $dbfile->getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
<?php
|
||||
/**
|
||||
* $Id: CoverageThresholdTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information please see
|
||||
* <http://phing.info>.
|
||||
*/
|
||||
|
||||
require_once 'phing/Task.php';
|
||||
require_once 'phing/system/io/PhingFile.php';
|
||||
require_once 'phing/system/util/Properties.php';
|
||||
|
||||
/**
|
||||
* Stops the build if any of the specified coverage threshold was not reached
|
||||
*
|
||||
* @author Benjamin Schultz <bschultz@proqrent.de>
|
||||
* @version $Id: CoverageThresholdTask.php 905 2010-10-05 16:28:03Z mrook $
|
||||
* @package phing.tasks.ext.coverage
|
||||
* @since 2.4.1
|
||||
*/
|
||||
class CoverageThresholdTask extends Task
|
||||
{
|
||||
/**
|
||||
* Holds an optional classpath
|
||||
*
|
||||
* @var Path
|
||||
*/
|
||||
private $_classpath = null;
|
||||
|
||||
/**
|
||||
* Holds an optional database file
|
||||
*
|
||||
* @var PhingFile
|
||||
*/
|
||||
private $_database = null;
|
||||
|
||||
/**
|
||||
* Holds the coverage threshold for the entire project
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_perProject = 25;
|
||||
|
||||
/**
|
||||
* Holds the coverage threshold for any class
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_perClass = 25;
|
||||
|
||||
/**
|
||||
* Holds the coverage threshold for any method
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_perMethod = 25;
|
||||
|
||||
/**
|
||||
* Holds the minimum found coverage value for a class
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_minClassCoverageFound = null;
|
||||
|
||||
/**
|
||||
* Holds the minimum found coverage value for a method
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_minMethodCoverageFound = null;
|
||||
|
||||
/**
|
||||
* Number of statements in the entire project
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_projectStatementCount = 0;
|
||||
|
||||
/**
|
||||
* Number of covered statements in the entire project
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $_projectStatementsCovered = 0;
|
||||
|
||||
/**
|
||||
* Whether to enable detailed logging
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $_verbose = false;
|
||||
|
||||
/**
|
||||
* Sets an optional classpath
|
||||
*
|
||||
* @param Path $classpath The classpath
|
||||
*/
|
||||
public function setClasspath(Path $classpath)
|
||||
{
|
||||
if ($this->_classpath === null) {
|
||||
$this->_classpath = $classpath;
|
||||
} else {
|
||||
$this->_classpath->append($classpath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the optional coverage database to use
|
||||
*
|
||||
* @param PhingFile The database file
|
||||
*/
|
||||
public function setDatabase(PhingFile $database)
|
||||
{
|
||||
$this->_database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create classpath object
|
||||
*
|
||||
* @return Path
|
||||
*/
|
||||
public function createClasspath()
|
||||
{
|
||||
$this->classpath = new Path();
|
||||
return $this->classpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the coverage threshold for entire project
|
||||
*
|
||||
* @param integer $threshold Coverage threshold for entire project
|
||||
*/
|
||||
public function setPerProject($threshold)
|
||||
{
|
||||
$this->_perProject = $threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the coverage threshold for any class
|
||||
*
|
||||
* @param integer $threshold Coverage threshold for any class
|
||||
*/
|
||||
public function setPerClass($threshold)
|
||||
{
|
||||
$this->_perClass = $threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the coverage threshold for any method
|
||||
*
|
||||
* @param integer $threshold Coverage threshold for any method
|
||||
*/
|
||||
public function setPerMethod($threshold)
|
||||
{
|
||||
$this->_perMethod = $threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to enable detailed logging or not
|
||||
*
|
||||
* @param boolean $verbose
|
||||
*/
|
||||
public function setVerbose($verbose)
|
||||
{
|
||||
$this->_verbose = StringHelper::booleanValue($verbose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter covered statements
|
||||
*
|
||||
* @param integer $var Coverage CODE/count
|
||||
* @return boolean
|
||||
*/
|
||||
protected function filterCovered($var)
|
||||
{
|
||||
return ($var >= 0 || $var === -2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the coverage threshold
|
||||
*
|
||||
* @param string $filename The filename to analyse
|
||||
* @param array $coverageInformation Array with coverage information
|
||||
*/
|
||||
protected function calculateCoverageThreshold($filename, $coverageInformation)
|
||||
{
|
||||
$classes = PHPUnitUtil::getDefinedClasses($filename, $this->_classpath);
|
||||
|
||||
if (is_array($classes)) {
|
||||
foreach ($classes as $className) {
|
||||
$reflection = new ReflectionClass($className);
|
||||
$classStartLine = $reflection->getStartLine();
|
||||
|
||||
// Strange PHP5 reflection bug, classes without parent class
|
||||
// or implemented interfaces seem to start one line off
|
||||
if ($reflection->getParentClass() === null
|
||||
&& count($reflection->getInterfaces()) === 0
|
||||
) {
|
||||
unset($coverageInformation[$classStartLine + 1]);
|
||||
} else {
|
||||
unset($coverageInformation[$classStartLine]);
|
||||
}
|
||||
|
||||
reset($coverageInformation);
|
||||
|
||||
$methods = $reflection->getMethods();
|
||||
|
||||
foreach ($methods as $method) {
|
||||
// PHP5 reflection considers methods of a parent class
|
||||
// to be part of a subclass, we don't
|
||||
if ($method->getDeclaringClass()->getName() != $reflection->getName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodStartLine = $method->getStartLine();
|
||||
$methodEndLine = $method->getEndLine();
|
||||
|
||||
// small fix for XDEBUG_CC_UNUSED
|
||||
if (isset($coverageInformation[$methodStartLine])) {
|
||||
unset($coverageInformation[$methodStartLine]);
|
||||
}
|
||||
|
||||
if (isset($coverageInformation[$methodEndLine])) {
|
||||
unset($coverageInformation[$methodEndLine]);
|
||||
}
|
||||
|
||||
if ($method->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lineNr = key($coverageInformation);
|
||||
|
||||
while ($lineNr !== null && $lineNr < $methodStartLine) {
|
||||
next($coverageInformation);
|
||||
$lineNr = key($coverageInformation);
|
||||
}
|
||||
|
||||
$methodStatementsCovered = 0;
|
||||
$methodStatementCount = 0;
|
||||
|
||||
while ($lineNr !== null && $lineNr <= $methodEndLine) {
|
||||
$methodStatementCount++;
|
||||
|
||||
$lineCoverageInfo = $coverageInformation[$lineNr];
|
||||
// set covered when CODE is other than -1 (not executed)
|
||||
if ($lineCoverageInfo > 0 || $lineCoverageInfo === -2) {
|
||||
$methodStatementsCovered++;
|
||||
}
|
||||
|
||||
next($coverageInformation);
|
||||
$lineNr = key($coverageInformation);
|
||||
}
|
||||
|
||||
if ($methodStatementCount > 0) {
|
||||
$methodCoverage = ( $methodStatementsCovered
|
||||
/ $methodStatementCount) * 100;
|
||||
} else {
|
||||
$methodCoverage = 0;
|
||||
}
|
||||
|
||||
if ($methodCoverage < $this->_perMethod
|
||||
&& !$method->isAbstract()
|
||||
) {
|
||||
throw new BuildException(
|
||||
'The coverage (' . $methodCoverage . '%) '
|
||||
. 'for method "' . $method->getName() . '" is lower'
|
||||
. ' than the specified threshold ('
|
||||
. $this->_perMethod . '%), see file: "'
|
||||
. $filename . '"'
|
||||
);
|
||||
} elseif ($methodCoverage < $this->_perMethod
|
||||
&& $method->isAbstract()
|
||||
) {
|
||||
if ($this->_verbose === true) {
|
||||
$this->log(
|
||||
'Skipped coverage threshold for abstract method "'
|
||||
. $method->getName() . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// store the minimum coverage value for logging (see #466)
|
||||
if ($this->_minMethodCoverageFound !== null) {
|
||||
if ($this->_minMethodCoverageFound > $methodCoverage) {
|
||||
$this->_minMethodCoverageFound = $methodCoverage;
|
||||
}
|
||||
} else {
|
||||
$this->_minMethodCoverageFound = $methodCoverage;
|
||||
}
|
||||
}
|
||||
|
||||
$classStatementCount = count($coverageInformation);
|
||||
$classStatementsCovered = count(
|
||||
array_filter(
|
||||
$coverageInformation,
|
||||
array($this, 'filterCovered')
|
||||
)
|
||||
);
|
||||
|
||||
if ($classStatementCount > 0) {
|
||||
$classCoverage = ( $classStatementsCovered
|
||||
/ $classStatementCount) * 100;
|
||||
} else {
|
||||
$classCoverage = 0;
|
||||
}
|
||||
|
||||
if ($classCoverage < $this->_perClass
|
||||
&& !$reflection->isAbstract()
|
||||
) {
|
||||
throw new BuildException(
|
||||
'The coverage (' . $classCoverage . '%) for class "'
|
||||
. $reflection->getName() . '" is lower than the '
|
||||
. 'specified threshold (' . $this->_perClass . '%), '
|
||||
. 'see file: "' . $filename . '"'
|
||||
);
|
||||
} elseif ($classCoverage < $this->_perClass
|
||||
&& $reflection->isAbstract()
|
||||
) {
|
||||
if ($this->_verbose === true) {
|
||||
$this->log(
|
||||
'Skipped coverage threshold for abstract class "'
|
||||
. $reflection->getName() . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// store the minimum coverage value for logging (see #466)
|
||||
if ($this->_minClassCoverageFound !== null) {
|
||||
if ($this->_minClassCoverageFound > $classCoverage) {
|
||||
$this->_minClassCoverageFound = $classCoverage;
|
||||
}
|
||||
} else {
|
||||
$this->_minClassCoverageFound = $classCoverage;
|
||||
}
|
||||
|
||||
$this->_projectStatementCount += $classStatementCount;
|
||||
$this->_projectStatementsCovered += $classStatementsCovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function main()
|
||||
{
|
||||
if ($this->_database === null) {
|
||||
$coverageDatabase = $this->project
|
||||
->getProperty('coverage.database');
|
||||
|
||||
if (! $coverageDatabase) {
|
||||
throw new BuildException(
|
||||
'Either include coverage-setup in your build file or set '
|
||||
. 'the "database" attribute'
|
||||
);
|
||||
}
|
||||
|
||||
$database = new PhingFile($coverageDatabase);
|
||||
} else {
|
||||
$database = $this->_database;
|
||||
}
|
||||
|
||||
$this->log(
|
||||
'Calculating coverage threshold: min. '
|
||||
. $this->_perProject . '% per project, '
|
||||
. $this->_perClass . '% per class and '
|
||||
. $this->_perMethod . '% per method is required'
|
||||
);
|
||||
|
||||
$props = new Properties();
|
||||
$props->load($database);
|
||||
|
||||
foreach ($props->keys() as $filename) {
|
||||
$file = unserialize($props->getProperty($filename));
|
||||
|
||||
$this->calculateCoverageThreshold(
|
||||
$file['fullname'],
|
||||
$file['coverage']
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->_projectStatementCount > 0) {
|
||||
$coverage = ( $this->_projectStatementsCovered
|
||||
/ $this->_projectStatementCount) * 100;
|
||||
} else {
|
||||
$coverage = 0;
|
||||
}
|
||||
|
||||
if ($coverage < $this->_perProject) {
|
||||
throw new BuildException(
|
||||
'The coverage (' . $coverage . '%) for the entire project '
|
||||
. 'is lower than the specified threshold ('
|
||||
. $this->_perProject . '%)'
|
||||
);
|
||||
}
|
||||
|
||||
$this->log(
|
||||
'Passed coverage threshold. Minimum found coverage values are: '
|
||||
. round($coverage, 2) . '% per project, '
|
||||
. round($this->_minClassCoverageFound, 2) . '% per class and '
|
||||
. round($this->_minMethodCoverageFound, 2) . '% per method'
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue