initial commit

This commit is contained in:
2026-06-25 13:03:45 +06:00
commit 4589f4a8d0
3229 changed files with 941958 additions and 0 deletions

View File

@@ -0,0 +1,801 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.0.0
*/
// @codeCoverageIgnoreStart
// @codingStandardsIgnoreStart
/**
* @SuppressWarnings(PHPMD)
*/
if (!function_exists('trait_exists')) {
function trait_exists($name)
{
return FALSE;
}
}
// @codingStandardsIgnoreEnd
// @codeCoverageIgnoreEnd
/**
* Provides collection functionality for PHP code coverage information.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.0.0
*/
class PHP_CodeCoverage
{
/**
* @var PHP_CodeCoverage_Driver
*/
protected $driver;
/**
* @var PHP_CodeCoverage_Filter
*/
protected $filter;
/**
* @var boolean
*/
protected $cacheTokens = FALSE;
/**
* @var boolean
*/
protected $forceCoversAnnotation = FALSE;
/**
* @var boolean
*/
protected $mapTestClassNameToCoveredClassName = FALSE;
/**
* @var boolean
*/
protected $addUncoveredFilesFromWhitelist = TRUE;
/**
* @var boolean
*/
protected $processUncoveredFilesFromWhitelist = FALSE;
/**
* @var mixed
*/
protected $currentId;
/**
* Code coverage data.
*
* @var array
*/
protected $data = array();
/**
* Test data.
*
* @var array
*/
protected $tests = array();
/**
* Constructor.
*
* @param PHP_CodeCoverage_Driver $driver
* @param PHP_CodeCoverage_Filter $filter
*/
public function __construct(PHP_CodeCoverage_Driver $driver = NULL, PHP_CodeCoverage_Filter $filter = NULL)
{
if ($driver === NULL) {
$driver = new PHP_CodeCoverage_Driver_Xdebug;
}
if ($filter === NULL) {
$filter = new PHP_CodeCoverage_Filter;
}
$this->driver = $driver;
$this->filter = $filter;
}
/**
* Returns the PHP_CodeCoverage_Report_Node_* object graph
* for this PHP_CodeCoverage object.
*
* @return PHP_CodeCoverage_Report_Node_Directory
* @since Method available since Release 1.1.0
*/
public function getReport()
{
$factory = new PHP_CodeCoverage_Report_Factory;
return $factory->create($this);
}
/**
* Clears collected code coverage data.
*/
public function clear()
{
$this->currentId = NULL;
$this->data = array();
$this->tests = array();
}
/**
* Returns the PHP_CodeCoverage_Filter used.
*
* @return PHP_CodeCoverage_Filter
*/
public function filter()
{
return $this->filter;
}
/**
* Returns the collected code coverage data.
*
* @return array
* @since Method available since Release 1.1.0
*/
public function getData()
{
if ($this->addUncoveredFilesFromWhitelist) {
$this->addUncoveredFilesFromWhitelist();
}
// We need to apply the blacklist filter a second time
// when no whitelist is used.
if (!$this->filter->hasWhitelist()) {
$this->applyListsFilter($this->data);
}
return $this->data;
}
/**
* Returns the test data.
*
* @return array
* @since Method available since Release 1.1.0
*/
public function getTests()
{
return $this->tests;
}
/**
* Start collection of code coverage information.
*
* @param mixed $id
* @param boolean $clear
* @throws PHP_CodeCoverage_Exception
*/
public function start($id, $clear = FALSE)
{
if (!is_bool($clear)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
if ($clear) {
$this->clear();
}
$this->currentId = $id;
$this->driver->start();
}
/**
* Stop collection of code coverage information.
*
* @param boolean $append
* @return array
* @throws PHP_CodeCoverage_Exception
*/
public function stop($append = TRUE)
{
if (!is_bool($append)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
$data = $this->driver->stop();
$this->append($data, NULL, $append);
$this->currentId = NULL;
return $data;
}
/**
* Appends code coverage data.
*
* @param array $data
* @param mixed $id
* @param boolean $append
*/
public function append(array $data, $id = NULL, $append = TRUE)
{
if ($id === NULL) {
$id = $this->currentId;
}
if ($id === NULL) {
throw new PHP_CodeCoverage_Exception;
}
$this->applyListsFilter($data);
$this->initializeFilesThatAreSeenTheFirstTime($data);
if (!$append) {
return;
}
if ($id != 'UNCOVERED_FILES_FROM_WHITELIST') {
$this->applyCoversAnnotationFilter($data, $id);
}
if (empty($data)) {
return;
}
$status = NULL;
if ($id instanceof PHPUnit_Framework_TestCase) {
$status = $id->getStatus();
$id = get_class($id) . '::' . $id->getName();
}
else if ($id instanceof PHPUnit_Extensions_PhptTestCase) {
$id = $id->getName();
}
$this->tests[$id] = $status;
foreach ($data as $file => $lines) {
if (!$this->filter->isFile($file)) {
continue;
}
foreach ($lines as $k => $v) {
if ($v == 1) {
$this->data[$file][$k][] = $id;
}
}
}
}
/**
* Merges the data from another instance of PHP_CodeCoverage.
*
* @param PHP_CodeCoverage $that
*/
public function merge(PHP_CodeCoverage $that)
{
foreach ($that->data as $file => $lines) {
if (!isset($this->data[$file])) {
if (!$this->filter->isFiltered($file)) {
$this->data[$file] = $lines;
}
continue;
}
foreach ($lines as $line => $data) {
if ($data !== NULL) {
if (!isset($this->data[$file][$line])) {
$this->data[$file][$line] = $data;
} else {
$this->data[$file][$line] = array_unique(
array_merge($this->data[$file][$line], $data)
);
}
}
}
}
$this->tests = array_merge($this->tests, $that->getTests());
}
/**
* @param boolean $flag
* @throws PHP_CodeCoverage_Exception
* @since Method available since Release 1.1.0
*/
public function setCacheTokens($flag)
{
if (!is_bool($flag)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
$this->cacheTokens = $flag;
}
/**
* @param boolean $flag
* @since Method available since Release 1.1.0
*/
public function getCacheTokens()
{
return $this->cacheTokens;
}
/**
* @param boolean $flag
* @throws PHP_CodeCoverage_Exception
*/
public function setForceCoversAnnotation($flag)
{
if (!is_bool($flag)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
$this->forceCoversAnnotation = $flag;
}
/**
* @param boolean $flag
* @throws PHP_CodeCoverage_Exception
*/
public function setMapTestClassNameToCoveredClassName($flag)
{
if (!is_bool($flag)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
$this->mapTestClassNameToCoveredClassName = $flag;
}
/**
* @param boolean $flag
* @throws PHP_CodeCoverage_Exception
*/
public function setAddUncoveredFilesFromWhitelist($flag)
{
if (!is_bool($flag)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
$this->addUncoveredFilesFromWhitelist = $flag;
}
/**
* @param boolean $flag
* @throws PHP_CodeCoverage_Exception
*/
public function setProcessUncoveredFilesFromWhitelist($flag)
{
if (!is_bool($flag)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
$this->processUncoveredFilesFromWhitelist = $flag;
}
/**
* Applies the @covers annotation filtering.
*
* @param array $data
* @param mixed $id
*/
protected function applyCoversAnnotationFilter(&$data, $id)
{
if ($id instanceof PHPUnit_Framework_TestCase) {
$testClassName = get_class($id);
$linesToBeCovered = $this->getLinesToBeCovered(
$testClassName, $id->getName()
);
if ($linesToBeCovered === FALSE) {
$data = array();
return;
}
if ($this->mapTestClassNameToCoveredClassName &&
empty($linesToBeCovered)) {
$testedClass = substr($testClassName, 0, -4);
if (class_exists($testedClass)) {
$class = new ReflectionClass($testedClass);
$linesToBeCovered = array(
$class->getFileName() => range(
$class->getStartLine(), $class->getEndLine()
)
);
}
}
} else {
$linesToBeCovered = array();
}
if (!empty($linesToBeCovered)) {
$data = array_intersect_key($data, $linesToBeCovered);
foreach (array_keys($data) as $filename) {
$data[$filename] = array_intersect_key(
$data[$filename], array_flip($linesToBeCovered[$filename])
);
}
}
else if ($this->forceCoversAnnotation) {
$data = array();
}
}
/**
* Applies the blacklist/whitelist filtering.
*
* @param array $data
*/
protected function applyListsFilter(&$data)
{
foreach (array_keys($data) as $filename) {
if ($this->filter->isFiltered($filename)) {
unset($data[$filename]);
}
}
}
/**
* @since Method available since Release 1.1.0
*/
protected function initializeFilesThatAreSeenTheFirstTime($data)
{
foreach ($data as $file => $lines) {
if ($this->filter->isFile($file) && !isset($this->data[$file])) {
$this->data[$file] = array();
foreach ($lines as $k => $v) {
$this->data[$file][$k] = $v == -2 ? NULL : array();
}
}
}
}
/**
* Processes whitelisted files that are not covered.
*/
protected function addUncoveredFilesFromWhitelist()
{
$data = array();
$uncoveredFiles = array_diff(
$this->filter->getWhitelist(), array_keys($this->data)
);
foreach ($uncoveredFiles as $uncoveredFile) {
if (!file_exists($uncoveredFile)) {
continue;
}
if ($this->processUncoveredFilesFromWhitelist) {
$this->processUncoveredFileFromWhitelist(
$uncoveredFile, $data, $uncoveredFiles
);
} else {
$data[$uncoveredFile] = array();
$lines = count(file($uncoveredFile));
for ($i = 1; $i <= $lines; $i++) {
$data[$uncoveredFile][$i] = -1;
}
}
}
$this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST');
}
/**
* @param string $uncoveredFile
* @param array $data
* @param array $uncoveredFiles
*/
protected function processUncoveredFileFromWhitelist($uncoveredFile, array &$data, array $uncoveredFiles)
{
$this->driver->start();
include_once $uncoveredFile;
$coverage = $this->driver->stop();
foreach ($coverage as $file => $fileCoverage) {
if (!isset($data[$file]) &&
in_array($file, $uncoveredFiles)) {
foreach (array_keys($fileCoverage) as $key) {
if ($fileCoverage[$key] == 1) {
$fileCoverage[$key] = -1;
}
}
$data[$file] = $fileCoverage;
}
}
}
/**
* Returns the files and lines a test method wants to cover.
*
* @param string $className
* @param string $methodName
* @return array
* @since Method available since Release 1.2.0
*/
protected function getLinesToBeCovered($className, $methodName)
{
$codeToCoverList = array();
$result = array();
// @codeCoverageIgnoreStart
if (($pos = strpos($methodName, ' ')) !== FALSE) {
$methodName = substr($methodName, 0, $pos);
}
// @codeCoverageIgnoreEnd
$class = new ReflectionClass($className);
try {
$method = new ReflectionMethod($className, $methodName);
}
catch (ReflectionException $e) {
return array();
}
$docComment = substr($class->getDocComment(), 3, -2) . PHP_EOL . substr($method->getDocComment(), 3, -2);
$templateMethods = array(
'setUp', 'assertPreConditions', 'assertPostConditions', 'tearDown'
);
foreach ($templateMethods as $templateMethod) {
if ($class->hasMethod($templateMethod)) {
$reflector = $class->getMethod($templateMethod);
$docComment .= PHP_EOL . substr($reflector->getDocComment(), 3, -2);
unset($reflector);
}
}
if (strpos($docComment, '@coversNothing') !== FALSE) {
return FALSE;
}
$classShortcut = preg_match_all(
'(@coversDefaultClass\s+(?P<coveredClass>[^\s]++)\s*$)m',
$class->getDocComment(),
$matches
);
if ($classShortcut) {
if ($classShortcut > 1) {
throw new PHP_CodeCoverage_Exception(
sprintf(
'More than one @coversClass annotation in class or interface "%s".',
$className
)
);
}
$classShortcut = $matches['coveredClass'][0];
}
$match = preg_match_all(
'(@covers\s+(?P<coveredElement>[^\s()]++)[\s()]*$)m',
$docComment,
$matches
);
if ($match) {
foreach ($matches['coveredElement'] as $coveredElement) {
if ($classShortcut && strncmp($coveredElement, '::', 2) === 0) {
$coveredElement = $classShortcut . $coveredElement;
}
$codeToCoverList = array_merge(
$codeToCoverList,
$this->resolveCoversToReflectionObjects($coveredElement)
);
}
foreach ($codeToCoverList as $codeToCover) {
$fileName = $codeToCover->getFileName();
if (!isset($result[$fileName])) {
$result[$fileName] = array();
}
$result[$fileName] = array_unique(
array_merge(
$result[$fileName],
range(
$codeToCover->getStartLine(), $codeToCover->getEndLine()
)
)
);
}
}
return $result;
}
/**
* @param string $coveredElement
* @return array
* @since Method available since Release 1.2.0
*/
protected function resolveCoversToReflectionObjects($coveredElement)
{
$codeToCoverList = array();
if (strpos($coveredElement, '::') !== FALSE) {
list($className, $methodName) = explode('::', $coveredElement);
if (isset($methodName[0]) && $methodName[0] == '<') {
$classes = array($className);
foreach ($classes as $className) {
if (!class_exists($className) &&
!interface_exists($className)) {
throw new PHP_CodeCoverage_Exception(
sprintf(
'Trying to @cover not existing class or ' .
'interface "%s".',
$className
)
);
}
$class = new ReflectionClass($className);
$methods = $class->getMethods();
$inverse = isset($methodName[1]) && $methodName[1] == '!';
if (strpos($methodName, 'protected')) {
$visibility = 'isProtected';
}
else if (strpos($methodName, 'private')) {
$visibility = 'isPrivate';
}
else if (strpos($methodName, 'public')) {
$visibility = 'isPublic';
}
foreach ($methods as $method) {
if ($inverse && !$method->$visibility()) {
$codeToCoverList[] = $method;
}
else if (!$inverse && $method->$visibility()) {
$codeToCoverList[] = $method;
}
}
}
} else {
$classes = array($className);
foreach ($classes as $className) {
if ($className == '' && function_exists($methodName)) {
$codeToCoverList[] = new ReflectionFunction(
$methodName
);
} else {
if (!((class_exists($className) ||
interface_exists($className) ||
trait_exists($className)) &&
method_exists($className, $methodName))) {
throw new PHP_CodeCoverage_Exception(
sprintf(
'Trying to @cover not existing method "%s::%s".',
$className,
$methodName
)
);
}
$codeToCoverList[] = new ReflectionMethod(
$className, $methodName
);
}
}
}
} else {
$extended = FALSE;
if (strpos($coveredElement, '<extended>') !== FALSE) {
$coveredElement = str_replace(
'<extended>', '', $coveredElement
);
$extended = TRUE;
}
$classes = array($coveredElement);
if ($extended) {
$classes = array_merge(
$classes,
class_implements($coveredElement),
class_parents($coveredElement)
);
}
foreach ($classes as $className) {
if (!class_exists($className) &&
!interface_exists($className) &&
!trait_exists($className)) {
throw new PHP_CodeCoverage_Exception(
sprintf(
'Trying to @cover not existing class or ' .
'interface "%s".',
$className
)
);
}
$codeToCoverList[] = new ReflectionClass($className);
}
}
return $codeToCoverList;
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2010 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
if (defined('PHPUNIT_COMPOSER_INSTALL')) {
return;
}
require_once 'File/Iterator/Autoload.php';
require_once 'PHP/Token/Stream/Autoload.php';
require_once 'Text/Template/Autoload.php';
spl_autoload_register(
function ($class)
{
static $classes = NULL;
static $path = NULL;
if ($classes === NULL) {
$classes = array(
'php_codecoverage' => '/CodeCoverage.php',
'php_codecoverage_driver' => '/CodeCoverage/Driver.php',
'php_codecoverage_driver_xdebug' => '/CodeCoverage/Driver/Xdebug.php',
'php_codecoverage_exception' => '/CodeCoverage/Exception.php',
'php_codecoverage_filter' => '/CodeCoverage/Filter.php',
'php_codecoverage_report_clover' => '/CodeCoverage/Report/Clover.php',
'php_codecoverage_report_factory' => '/CodeCoverage/Report/Factory.php',
'php_codecoverage_report_html' => '/CodeCoverage/Report/HTML.php',
'php_codecoverage_report_html_renderer' => '/CodeCoverage/Report/HTML/Renderer.php',
'php_codecoverage_report_html_renderer_dashboard' => '/CodeCoverage/Report/HTML/Renderer/Dashboard.php',
'php_codecoverage_report_html_renderer_directory' => '/CodeCoverage/Report/HTML/Renderer/Directory.php',
'php_codecoverage_report_html_renderer_file' => '/CodeCoverage/Report/HTML/Renderer/File.php',
'php_codecoverage_report_node' => '/CodeCoverage/Report/Node.php',
'php_codecoverage_report_node_directory' => '/CodeCoverage/Report/Node/Directory.php',
'php_codecoverage_report_node_file' => '/CodeCoverage/Report/Node/File.php',
'php_codecoverage_report_node_iterator' => '/CodeCoverage/Report/Node/Iterator.php',
'php_codecoverage_report_php' => '/CodeCoverage/Report/PHP.php',
'php_codecoverage_report_text' => '/CodeCoverage/Report/Text.php',
'php_codecoverage_util' => '/CodeCoverage/Util.php',
'php_codecoverage_util_invalidargumenthelper' => '/CodeCoverage/Util/InvalidArgumentHelper.php',
'php_codecoverage_version' => '/CodeCoverage/Version.php'
);
$path = dirname(dirname(__FILE__));
}
$cn = strtolower($class);
if (isset($classes[$cn])) {
require $path . $classes[$cn];
}
}
);

View File

@@ -0,0 +1,74 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2010 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
if (defined('PHPUNIT_COMPOSER_INSTALL')) {
return;
}
require_once 'File/Iterator/Autoload.php';
require_once 'PHP/Token/Stream/Autoload.php';
require_once 'Text/Template/Autoload.php';
spl_autoload_register(
function ($class)
{
static $classes = NULL;
static $path = NULL;
if ($classes === NULL) {
$classes = array(
___CLASSLIST___
);
$path = dirname(dirname(__FILE__));
}
$cn = strtolower($class);
if (isset($classes[$cn])) {
require $path . $classes[$cn];
}
}
);

View File

@@ -0,0 +1,70 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.0.0
*/
/**
* Interface for code coverage drivers.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.0.0
*/
interface PHP_CodeCoverage_Driver
{
/**
* Start collection of code coverage information.
*/
public function start();
/**
* Stop collection of code coverage information.
*
* @return array
*/
public function stop();
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.0.0
*/
/**
* Driver for Xdebug's code coverage functionality.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.0.0
* @codeCoverageIgnore
*/
class PHP_CodeCoverage_Driver_Xdebug implements PHP_CodeCoverage_Driver
{
/**
* Constructor.
*/
public function __construct()
{
if (!extension_loaded('xdebug')) {
throw new PHP_CodeCoverage_Exception('Xdebug is not loaded.');
}
if (version_compare(phpversion('xdebug'), '2.2.0-dev', '>=') &&
!ini_get('xdebug.coverage_enable')) {
throw new PHP_CodeCoverage_Exception(
'You need to set xdebug.coverage_enable=On in your php.ini.'
);
}
}
/**
* Start collection of code coverage information.
*/
public function start()
{
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
}
/**
* Stop collection of code coverage information.
*
* @return array
*/
public function stop()
{
$codeCoverage = xdebug_get_code_coverage();
xdebug_stop_code_coverage();
return $codeCoverage;
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Exception class for PHP_CodeCoverage component.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Exception extends RuntimeException
{
}

View File

@@ -0,0 +1,349 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.0.0
*/
/**
* Filter for blacklisting and whitelisting of code coverage information.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.0.0
*/
class PHP_CodeCoverage_Filter
{
/**
* Source files that are blacklisted.
*
* @var array
*/
protected $blacklistedFiles = array();
/**
* Source files that are whitelisted.
*
* @var array
*/
protected $whitelistedFiles = array();
/**
* @var boolean
*/
protected $blacklistPrefilled = FALSE;
/**
* Adds a directory to the blacklist (recursively).
*
* @param string $directory
* @param string $suffix
* @param string $prefix
*/
public function addDirectoryToBlacklist($directory, $suffix = '.php', $prefix = '')
{
$facade = new File_Iterator_Facade;
$files = $facade->getFilesAsArray(
$directory, $suffix, $prefix
);
foreach ($files as $file) {
$this->addFileToBlacklist($file);
}
}
/**
* Adds a file to the blacklist.
*
* @param string $filename
*/
public function addFileToBlacklist($filename)
{
$this->blacklistedFiles[realpath($filename)] = TRUE;
}
/**
* Adds files to the blacklist.
*
* @param array $files
*/
public function addFilesToBlacklist(array $files)
{
foreach ($files as $file) {
$this->addFileToBlacklist($file);
}
}
/**
* Removes a directory from the blacklist (recursively).
*
* @param string $directory
* @param string $suffix
* @param string $prefix
*/
public function removeDirectoryFromBlacklist($directory, $suffix = '.php', $prefix = '')
{
$facade = new File_Iterator_Facade;
$files = $facade->getFilesAsArray(
$directory, $suffix, $prefix
);
foreach ($files as $file) {
$this->removeFileFromBlacklist($file);
}
}
/**
* Removes a file from the blacklist.
*
* @param string $filename
*/
public function removeFileFromBlacklist($filename)
{
$filename = realpath($filename);
if (isset($this->blacklistedFiles[$filename])) {
unset($this->blacklistedFiles[$filename]);
}
}
/**
* Adds a directory to the whitelist (recursively).
*
* @param string $directory
* @param string $suffix
* @param string $prefix
*/
public function addDirectoryToWhitelist($directory, $suffix = '.php', $prefix = '')
{
$facade = new File_Iterator_Facade;
$files = $facade->getFilesAsArray(
$directory, $suffix, $prefix
);
foreach ($files as $file) {
$this->addFileToWhitelist($file);
}
}
/**
* Adds a file to the whitelist.
*
* @param string $filename
*/
public function addFileToWhitelist($filename)
{
$this->whitelistedFiles[realpath($filename)] = TRUE;
}
/**
* Adds files to the whitelist.
*
* @param array $files
*/
public function addFilesToWhitelist(array $files)
{
foreach ($files as $file) {
$this->addFileToWhitelist($file);
}
}
/**
* Removes a directory from the whitelist (recursively).
*
* @param string $directory
* @param string $suffix
* @param string $prefix
*/
public function removeDirectoryFromWhitelist($directory, $suffix = '.php', $prefix = '')
{
$facade = new File_Iterator_Facade;
$files = $facade->getFilesAsArray(
$directory, $suffix, $prefix
);
foreach ($files as $file) {
$this->removeFileFromWhitelist($file);
}
}
/**
* Removes a file from the whitelist.
*
* @param string $filename
*/
public function removeFileFromWhitelist($filename)
{
$filename = realpath($filename);
if (isset($this->whitelistedFiles[$filename])) {
unset($this->whitelistedFiles[$filename]);
}
}
/**
* Checks whether a filename is a real filename.
*
* @param string $filename
*/
public function isFile($filename)
{
if ($filename == '-' ||
strpos($filename, 'vfs://') === 0 ||
strpos($filename, 'xdebug://debug-eval') !== FALSE ||
strpos($filename, 'eval()\'d code') !== FALSE ||
strpos($filename, 'runtime-created function') !== FALSE ||
strpos($filename, 'runkit created function') !== FALSE ||
strpos($filename, 'assert code') !== FALSE ||
strpos($filename, 'regexp code') !== FALSE) {
return FALSE;
}
return TRUE;
}
/**
* Checks whether or not a file is filtered.
*
* When the whitelist is empty (default), blacklisting is used.
* When the whitelist is not empty, whitelisting is used.
*
* @param string $filename
* @param boolean $ignoreWhitelist
* @return boolean
* @throws PHP_CodeCoverage_Exception
*/
public function isFiltered($filename)
{
$filename = realpath($filename);
if (!empty($this->whitelistedFiles)) {
return !isset($this->whitelistedFiles[$filename]);
}
if (!$this->blacklistPrefilled) {
$this->prefillBlacklist();
}
return isset($this->blacklistedFiles[$filename]);
}
/**
* Returns the list of blacklisted files.
*
* @return array
*/
public function getBlacklist()
{
return array_keys($this->blacklistedFiles);
}
/**
* Returns the list of whitelisted files.
*
* @return array
*/
public function getWhitelist()
{
return array_keys($this->whitelistedFiles);
}
/**
* Returns whether this filter has a whitelist.
*
* @return boolean
* @since Method available since Release 1.1.0
*/
public function hasWhitelist()
{
return !empty($this->whitelistedFiles);
}
/**
* @since Method available since Release 1.2.3
*/
protected function prefillBlacklist()
{
if (defined('__PHPUNIT_PHAR__')) {
$this->addFileToBlacklist(__PHPUNIT_PHAR__);
}
$this->addDirectoryContainingClassToBlacklist('File_Iterator');
$this->addDirectoryContainingClassToBlacklist('PHP_CodeCoverage');
$this->addDirectoryContainingClassToBlacklist('PHP_Invoker');
$this->addDirectoryContainingClassToBlacklist('PHP_Timer');
$this->addDirectoryContainingClassToBlacklist('PHP_Token');
$this->addDirectoryContainingClassToBlacklist('PHPUnit_Framework_TestCase', 2);
$this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_Database_TestCase', 2);
$this->addDirectoryContainingClassToBlacklist('PHPUnit_Framework_MockObject_Generator', 2);
$this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_SeleniumTestCase', 2);
$this->addDirectoryContainingClassToBlacklist('PHPUnit_Extensions_Story_TestCase', 2);
$this->addDirectoryContainingClassToBlacklist('Text_Template');
$this->addDirectoryContainingClassToBlacklist('Symfony\Component\Yaml\Yaml');
$this->blacklistPrefilled = TRUE;
}
/**
* @param string $className
* @param integer $parent
* @since Method available since Release 1.2.3
*/
protected function addDirectoryContainingClassToBlacklist($className, $parent = 1)
{
if (!class_exists($className)) {
return;
}
$reflector = new ReflectionClass($className);
$directory = $reflector->getFileName();
for ($i = 0; $i < $parent; $i++) {
$directory = dirname($directory);
}
$this->addDirectoryToBlacklist($directory);
}
}

View File

@@ -0,0 +1,346 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.0.0
*/
/**
* Generates a Clover XML logfile from an PHP_CodeCoverage object.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.0.0
*/
class PHP_CodeCoverage_Report_Clover
{
/**
* @param PHP_CodeCoverage $coverage
* @param string $target
* @param string $name
* @return string
*/
public function process(PHP_CodeCoverage $coverage, $target = NULL, $name = NULL)
{
$xmlDocument = new DOMDocument('1.0', 'UTF-8');
$xmlDocument->formatOutput = TRUE;
$xmlCoverage = $xmlDocument->createElement('coverage');
$xmlCoverage->setAttribute('generated', (int)$_SERVER['REQUEST_TIME']);
$xmlDocument->appendChild($xmlCoverage);
$xmlProject = $xmlDocument->createElement('project');
$xmlProject->setAttribute('timestamp', (int)$_SERVER['REQUEST_TIME']);
if (is_string($name)) {
$xmlProject->setAttribute('name', $name);
}
$xmlCoverage->appendChild($xmlProject);
$packages = array();
$report = $coverage->getReport();
unset($coverage);
foreach ($report as $item) {
$namespace = 'global';
if (!$item instanceof PHP_CodeCoverage_Report_Node_File) {
continue;
}
$xmlFile = $xmlDocument->createElement('file');
$xmlFile->setAttribute('name', $item->getPath());
$classes = $item->getClassesAndTraits();
$coverage = $item->getCoverageData();
$lines = array();
$ignoredLines = $item->getIgnoredLines();
foreach ($classes as $className => $class) {
$classStatements = 0;
$coveredClassStatements = 0;
$coveredMethods = 0;
foreach ($class['methods'] as $methodName => $method) {
$methodCount = 0;
$methodLines = 0;
$methodLinesCovered = 0;
for ($i = $method['startLine'];
$i <= $method['endLine'];
$i++) {
if (isset($ignoredLines[$i])) {
continue;
}
$add = TRUE;
$count = 0;
if (isset($coverage[$i])) {
if ($coverage[$i] !== NULL) {
$classStatements++;
$methodLines++;
} else {
$add = FALSE;
}
$count = count($coverage[$i]);
if ($count > 0) {
$coveredClassStatements++;
$methodLinesCovered++;
}
} else {
$add = FALSE;
}
$methodCount = max($methodCount, $count);
if ($add) {
$lines[$i] = array(
'count' => $count, 'type' => 'stmt'
);
}
}
if ($methodCount > 0) {
$coveredMethods++;
}
$lines[$method['startLine']] = array(
'count' => $methodCount,
'crap' => $method['crap'],
'type' => 'method',
'name' => $methodName
);
}
if (!empty($class['package']['namespace'])) {
$namespace = $class['package']['namespace'];
}
$xmlClass = $xmlDocument->createElement('class');
$xmlClass->setAttribute('name', $className);
$xmlClass->setAttribute('namespace', $namespace);
if (!empty($class['package']['fullPackage'])) {
$xmlClass->setAttribute(
'fullPackage', $class['package']['fullPackage']
);
}
if (!empty($class['package']['category'])) {
$xmlClass->setAttribute(
'category', $class['package']['category']
);
}
if (!empty($class['package']['package'])) {
$xmlClass->setAttribute(
'package', $class['package']['package']
);
}
if (!empty($class['package']['subpackage'])) {
$xmlClass->setAttribute(
'subpackage', $class['package']['subpackage']
);
}
$xmlFile->appendChild($xmlClass);
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('methods', count($class['methods']));
$xmlMetrics->setAttribute('coveredmethods', $coveredMethods);
$xmlMetrics->setAttribute('conditionals', 0);
$xmlMetrics->setAttribute('coveredconditionals', 0);
$xmlMetrics->setAttribute('statements', $classStatements);
$xmlMetrics->setAttribute(
'coveredstatements', $coveredClassStatements
);
$xmlMetrics->setAttribute(
'elements',
count($class['methods']) +
$classStatements
/* + conditionals */);
$xmlMetrics->setAttribute(
'coveredelements',
$coveredMethods +
$coveredClassStatements
/* + coveredconditionals */
);
$xmlClass->appendChild($xmlMetrics);
}
foreach ($coverage as $line => $data) {
if ($data === NULL ||
isset($lines[$line]) ||
isset($ignoredLines[$line])) {
continue;
}
$lines[$line] = array(
'count' => count($data), 'type' => 'stmt'
);
}
ksort($lines);
foreach ($lines as $line => $data) {
if (isset($ignoredLines[$line])) {
continue;
}
$xmlLine = $xmlDocument->createElement('line');
$xmlLine->setAttribute('num', $line);
$xmlLine->setAttribute('type', $data['type']);
if (isset($data['name'])) {
$xmlLine->setAttribute('name', $data['name']);
}
if (isset($data['crap'])) {
$xmlLine->setAttribute('crap', $data['crap']);
}
$xmlLine->setAttribute('count', $data['count']);
$xmlFile->appendChild($xmlLine);
}
$linesOfCode = $item->getLinesOfCode();
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('loc', $linesOfCode['loc']);
$xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']);
$xmlMetrics->setAttribute('classes', $item->getNumClassesAndTraits());
$xmlMetrics->setAttribute('methods', $item->getNumMethods());
$xmlMetrics->setAttribute(
'coveredmethods', $item->getNumTestedMethods()
);
$xmlMetrics->setAttribute('conditionals', 0);
$xmlMetrics->setAttribute('coveredconditionals', 0);
$xmlMetrics->setAttribute(
'statements', $item->getNumExecutableLines()
);
$xmlMetrics->setAttribute(
'coveredstatements', $item->getNumExecutedLines()
);
$xmlMetrics->setAttribute(
'elements',
$item->getNumMethods() +
$item->getNumExecutableLines()
/* + conditionals */
);
$xmlMetrics->setAttribute(
'coveredelements',
$item->getNumTestedMethods() +
$item->getNumExecutedLines()
/* + coveredconditionals */
);
$xmlFile->appendChild($xmlMetrics);
if ($namespace == 'global') {
$xmlProject->appendChild($xmlFile);
} else {
if (!isset($packages[$namespace])) {
$packages[$namespace] = $xmlDocument->createElement(
'package'
);
$packages[$namespace]->setAttribute('name', $namespace);
$xmlProject->appendChild($packages[$namespace]);
}
$packages[$namespace]->appendChild($xmlFile);
}
}
$linesOfCode = $report->getLinesOfCode();
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('files', count($report));
$xmlMetrics->setAttribute('loc', $linesOfCode['loc']);
$xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']);
$xmlMetrics->setAttribute(
'classes', $report->getNumClassesAndTraits()
);
$xmlMetrics->setAttribute('methods', $report->getNumMethods());
$xmlMetrics->setAttribute(
'coveredmethods', $report->getNumTestedMethods()
);
$xmlMetrics->setAttribute('conditionals', 0);
$xmlMetrics->setAttribute('coveredconditionals', 0);
$xmlMetrics->setAttribute(
'statements', $report->getNumExecutableLines()
);
$xmlMetrics->setAttribute(
'coveredstatements', $report->getNumExecutedLines()
);
$xmlMetrics->setAttribute(
'elements',
$report->getNumMethods() +
$report->getNumExecutableLines()
/* + conditionals */
);
$xmlMetrics->setAttribute(
'coveredelements',
$report->getNumTestedMethods() +
$report->getNumExecutedLines()
/* + coveredconditionals */
);
$xmlProject->appendChild($xmlMetrics);
if ($target !== NULL) {
if (!is_dir(dirname($target))) {
mkdir(dirname($target), 0777, TRUE);
}
return $xmlDocument->save($target);
} else {
return $xmlDocument->saveXML();
}
}
}

View File

@@ -0,0 +1,280 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Factory for PHP_CodeCoverage_Report_Node_* object graphs.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_Factory
{
/**
* @param PHP_CodeCoverage $coverage
*/
public function create(PHP_CodeCoverage $coverage)
{
$files = $coverage->getData();
$commonPath = $this->reducePaths($files);
$root = new PHP_CodeCoverage_Report_Node_Directory(
$commonPath, NULL
);
$this->addItems(
$root,
$this->buildDirectoryStructure($files),
$coverage->getTests(),
$coverage->getCacheTokens()
);
return $root;
}
/**
* @param PHP_CodeCoverage_Report_Node_Directory $root
* @param array $items
* @param array $tests
* @param boolean $cacheTokens
*/
protected function addItems(PHP_CodeCoverage_Report_Node_Directory $root, array $items, array $tests, $cacheTokens)
{
foreach ($items as $key => $value) {
if (substr($key, -2) == '/f') {
$key = substr($key, 0, -2);
if (file_exists($root->getPath() . DIRECTORY_SEPARATOR . $key)) {
$root->addFile($key, $value, $tests, $cacheTokens);
}
} else {
$child = $root->addDirectory($key);
$this->addItems($child, $value, $tests, $cacheTokens);
}
}
}
/**
* Builds an array representation of the directory structure.
*
* For instance,
*
* <code>
* Array
* (
* [Money.php] => Array
* (
* ...
* )
*
* [MoneyBag.php] => Array
* (
* ...
* )
* )
* </code>
*
* is transformed into
*
* <code>
* Array
* (
* [.] => Array
* (
* [Money.php] => Array
* (
* ...
* )
*
* [MoneyBag.php] => Array
* (
* ...
* )
* )
* )
* </code>
*
* @param array $files
* @return array
*/
protected function buildDirectoryStructure($files)
{
$result = array();
foreach ($files as $path => $file) {
$path = explode('/', $path);
$pointer = &$result;
$max = count($path);
for ($i = 0; $i < $max; $i++) {
if ($i == ($max - 1)) {
$type = '/f';
} else {
$type = '';
}
$pointer = &$pointer[$path[$i] . $type];
}
$pointer = $file;
}
return $result;
}
/**
* Reduces the paths by cutting the longest common start path.
*
* For instance,
*
* <code>
* Array
* (
* [/home/sb/Money/Money.php] => Array
* (
* ...
* )
*
* [/home/sb/Money/MoneyBag.php] => Array
* (
* ...
* )
* )
* </code>
*
* is reduced to
*
* <code>
* Array
* (
* [Money.php] => Array
* (
* ...
* )
*
* [MoneyBag.php] => Array
* (
* ...
* )
* )
* </code>
*
* @param array $files
* @return string
*/
protected function reducePaths(&$files)
{
if (empty($files)) {
return '.';
}
$commonPath = '';
$paths = array_keys($files);
if (count($files) == 1) {
$commonPath = dirname($paths[0]) . '/';
$files[basename($paths[0])] = $files[$paths[0]];
unset($files[$paths[0]]);
return $commonPath;
}
$max = count($paths);
for ($i = 0; $i < $max; $i++) {
// strip phar:// prefixes
if (strpos($paths[$i], 'phar://') === 0) {
$paths[$i] = substr($paths[$i], 7);
}
$paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);
if (empty($paths[$i][0])) {
$paths[$i][0] = DIRECTORY_SEPARATOR;
}
}
$done = FALSE;
$max = count($paths);
while (!$done) {
for ($i = 0; $i < $max - 1; $i++) {
if (!isset($paths[$i][0]) ||
!isset($paths[$i+1][0]) ||
$paths[$i][0] != $paths[$i+1][0]) {
$done = TRUE;
break;
}
}
if (!$done) {
$commonPath .= $paths[0][0];
if ($paths[0][0] != DIRECTORY_SEPARATOR) {
$commonPath .= DIRECTORY_SEPARATOR;
}
for ($i = 0; $i < $max; $i++) {
array_shift($paths[$i]);
}
}
}
$original = array_keys($files);
$max = count($original);
for ($i = 0; $i < $max; $i++) {
$files[join('/', $paths[$i])] = $files[$original[$i]];
unset($files[$original[$i]]);
}
ksort($files);
return substr($commonPath, 0, -1);
}
}

View File

@@ -0,0 +1,224 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.0.0
*/
/**
* Generates an HTML report from an PHP_CodeCoverage object.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.0.0
*/
class PHP_CodeCoverage_Report_HTML
{
/**
* @var string
*/
protected $templatePath;
/**
* @var string
*/
protected $charset;
/**
* @var string
*/
protected $generator;
/**
* @var integer
*/
protected $lowUpperBound;
/**
* @var integer
*/
protected $highLowerBound;
/**
* @var boolean
*/
protected $highlight;
/**
* Constructor.
*
* @param array $options
*/
public function __construct($charset = 'UTF-8', $highlight = FALSE, $lowUpperBound = 35, $highLowerBound = 70, $generator = '')
{
$this->charset = $charset;
$this->generator = $generator;
$this->highLowerBound = $highLowerBound;
$this->highlight = $highlight;
$this->lowUpperBound = $lowUpperBound;
$this->templatePath = sprintf(
'%s%sHTML%sRenderer%sTemplate%s',
dirname(__FILE__),
DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR,
DIRECTORY_SEPARATOR
);
}
/**
* @param PHP_CodeCoverage $coverage
* @param string $target
*/
public function process(PHP_CodeCoverage $coverage, $target)
{
$target = $this->getDirectory($target);
$report = $coverage->getReport();
unset($coverage);
if (!isset($_SERVER['REQUEST_TIME'])) {
$_SERVER['REQUEST_TIME'] = time();
}
$date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']);
$dashboard = new PHP_CodeCoverage_Report_HTML_Renderer_Dashboard(
$this->templatePath,
$this->charset,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound
);
$directory = new PHP_CodeCoverage_Report_HTML_Renderer_Directory(
$this->templatePath,
$this->charset,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound
);
$file = new PHP_CodeCoverage_Report_HTML_Renderer_File(
$this->templatePath,
$this->charset,
$this->generator,
$date,
$this->lowUpperBound,
$this->highLowerBound,
$this->highlight
);
$dashboard->render($report, $target . 'index.dashboard.html');
$directory->render($report, $target . 'index.html');
foreach ($report as $node) {
$id = $node->getId();
if ($node instanceof PHP_CodeCoverage_Report_Node_Directory) {
$dashboard->render($node, $target . $id . '.dashboard.html');
$directory->render($node, $target . $id . '.html');
} else {
$file->render($node, $target . $id . '.html');
}
}
$this->copyFiles($target);
}
/**
* @param string $target
*/
protected function copyFiles($target)
{
$dir = $this->getDirectory($target . 'css');
copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
copy($this->templatePath . 'css/bootstrap-responsive.min.css', $dir . 'bootstrap-responsive.min.css');
copy($this->templatePath . 'css/nv.d3.css', $dir . 'nv.d3.css');
copy($this->templatePath . 'css/style.css', $dir . 'style.css');
$dir = $this->getDirectory($target . 'js');
copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
copy($this->templatePath . 'js/html5shiv.js', $dir . 'html5shiv.js');
$dir = $this->getDirectory($target . 'img');
copy($this->templatePath . 'img/glyphicons-halflings.png', $dir . 'glyphicons-halflings.png');
copy($this->templatePath . 'img/glyphicons-halflings-white.png', $dir . 'glyphicons-halflings-white.png');
}
/**
* @param string $directory
* @return string
* @throws PHP_CodeCoverage_Exception
* @since Method available since Release 1.2.0
*/
protected function getDirectory($directory)
{
if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) {
$directory .= DIRECTORY_SEPARATOR;
}
if (is_dir($directory)) {
return $directory;
}
if (@mkdir($directory, 0777, TRUE)) {
return $directory;
}
throw new PHP_CodeCoverage_Exception(
sprintf(
'Directory "%s" does not exist.',
$directory
)
);
}
}

View File

@@ -0,0 +1,285 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Base class for PHP_CodeCoverage_Report_Node renderers.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
abstract class PHP_CodeCoverage_Report_HTML_Renderer
{
/**
* @var string
*/
protected $templatePath;
/**
* @var string
*/
protected $charset;
/**
* @var string
*/
protected $generator;
/**
* @var string
*/
protected $date;
/**
* @var integer
*/
protected $lowUpperBound;
/**
* @var integer
*/
protected $highLowerBound;
/**
* Constructor.
*
* @param string $templatePath
* @param string $charset
* @param string $generator
* @param string $date
* @param integer $lowUpperBound
* @param integer $highLowerBound
*/
public function __construct($templatePath, $charset, $generator, $date, $lowUpperBound, $highLowerBound)
{
$this->templatePath = $templatePath;
$this->charset = $charset;
$this->generator = $generator;
$this->date = $date;
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
}
/**
* @param Text_Template $template
* @param array $data
* @return string
*/
protected function renderItemTemplate(Text_Template $template, array $data)
{
$numSeperator = '&nbsp;/&nbsp;';
$classesBar = '&nbsp;';
$classesLevel = 'None';
$classesNumber = '&nbsp;';
if (isset($data['numClasses']) && $data['numClasses'] > 0) {
$classesLevel = $this->getColorLevel($data['testedClassesPercent']);
$classesNumber = $data['numTestedClasses'] . $numSeperator .
$data['numClasses'];
$classesBar = $this->getCoverageBar(
$data['testedClassesPercent']
);
}
$methodsBar = '&nbsp;';
$methodsLevel = 'None';
$methodsNumber = '&nbsp;';
if ($data['numMethods'] > 0) {
$methodsLevel = $this->getColorLevel($data['testedMethodsPercent']);
$methodsNumber = $data['numTestedMethods'] . $numSeperator .
$data['numMethods'];
$methodsBar = $this->getCoverageBar(
$data['testedMethodsPercent']
);
}
$linesBar = '&nbsp;';
$linesLevel = 'None';
$linesNumber = '&nbsp;';
if ($data['numExecutableLines'] > 0) {
$linesLevel = $this->getColorLevel($data['linesExecutedPercent']);
$linesNumber = $data['numExecutedLines'] . $numSeperator .
$data['numExecutableLines'];
$linesBar = $this->getCoverageBar(
$data['linesExecutedPercent']
);
}
$template->setVar(
array(
'icon' => isset($data['icon']) ? $data['icon'] : '',
'crap' => isset($data['crap']) ? $data['crap'] : '',
'name' => $data['name'],
'lines_bar' => $linesBar,
'lines_executed_percent' => $data['linesExecutedPercentAsString'],
'lines_level' => $linesLevel,
'lines_number' => $linesNumber,
'methods_bar' => $methodsBar,
'methods_tested_percent' => $data['testedMethodsPercentAsString'],
'methods_level' => $methodsLevel,
'methods_number' => $methodsNumber,
'classes_bar' => $classesBar,
'classes_tested_percent' => isset($data['testedClassesPercentAsString']) ? $data['testedClassesPercentAsString'] : '',
'classes_level' => $classesLevel,
'classes_number' => $classesNumber
)
);
return $template->render();
}
/**
* @param Text_Template $template
* @param PHP_CodeCoverage_Report_Node $node
*/
protected function setCommonTemplateVariables(Text_Template $template, PHP_CodeCoverage_Report_Node $node)
{
$template->setVar(
array(
'id' => $node->getId(),
'full_path' => $node->getPath(),
'breadcrumbs' => $this->getBreadcrumbs($node),
'charset' => $this->charset,
'date' => $this->date,
'version' => PHP_CodeCoverage_Version::id(),
'php_version' => PHP_VERSION,
'generator' => $this->generator,
'low_upper_bound' => $this->lowUpperBound,
'high_lower_bound' => $this->highLowerBound
)
);
}
protected function getBreadcrumbs(PHP_CodeCoverage_Report_Node $node)
{
$breadcrumbs = '';
$path = $node->getPathAsArray();
foreach ($path as $step) {
if ($step !== $node) {
$breadcrumbs .= $this->getInactiveBreadcrumb($step);
} else {
$breadcrumbs .= $this->getActiveBreadcrumb(
$step,
$node instanceof PHP_CodeCoverage_Report_Node_Directory
);
}
}
return $breadcrumbs;
}
protected function getActiveBreadcrumb(PHP_CodeCoverage_Report_Node $node, $isDirectory)
{
$buffer = sprintf(
' <li class="active">%s</li>' . "\n",
$node->getName()
);
if ($isDirectory) {
$buffer .= sprintf(
' <li>(<a href="%s.dashboard.html">Dashboard</a>)</li>' . "\n",
$node->getId()
);
}
return $buffer;
}
protected function getInactiveBreadcrumb(PHP_CodeCoverage_Report_Node $node)
{
return sprintf(
' <li><a href="%s.html">%s</a> <span class="divider">/</span></li>' . "\n",
$node->getId(),
$node->getName()
);
}
protected function getCoverageBar($percent)
{
$level = $this->getColorLevel($percent);
$template = new Text_Template(
$this->templatePath . 'coverage_bar.html', '{{', '}}'
);
$template->setVar(array('level' => $level, 'percent' => sprintf("%.2F", $percent)));
return $template->render();
}
/**
* @param integer $percent
* @return string
*/
protected function getColorLevel($percent)
{
if ($percent < $this->lowUpperBound) {
return 'danger';
}
else if ($percent >= $this->lowUpperBound &&
$percent < $this->highLowerBound) {
return 'warning';
}
else {
return 'success';
}
}
}

View File

@@ -0,0 +1,240 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Renders the dashboard for a PHP_CodeCoverage_Report_Node_Directory node.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_HTML_Renderer_Dashboard extends PHP_CodeCoverage_Report_HTML_Renderer
{
/**
* @param PHP_CodeCoverage_Report_Node_Directory $node
* @param string $file
*/
public function render(PHP_CodeCoverage_Report_Node_Directory $node, $file)
{
$classes = $node->getClassesAndTraits();
$template = new Text_Template(
$this->templatePath . 'dashboard.html', '{{', '}}'
);
$this->setCommonTemplateVariables($template, $node);
$template->setVar(
array(
'least_tested_methods' => $this->leastTestedMethods($classes),
'top_project_risks' => $this->topProjectRisks($classes),
'cc_values' => $this->classComplexity($classes),
'ccd_values' => $this->classCoverageDistribution($classes),
'backlink' => basename(str_replace('.dashboard', '', $file))
)
);
$template->renderTo($file);
}
/**
* Returns the data for the Class Complexity chart.
*
* @param array $classes
* @return string
*/
protected function classComplexity(array $classes)
{
$data = array();
foreach ($classes as $name => $class) {
$data[] = array(
$class['coverage'],
$class['ccn'],
sprintf(
'<a href="%s">%s</a>',
$class['link'],
$name
)
);
}
return json_encode($data);
}
/**
* Returns the data for the Class Coverage Distribution chart.
*
* @param array $classes
* @return string
*/
protected function classCoverageDistribution(array $classes)
{
$data = array(
'0%' => 0,
'0-10%' => 0,
'10-20%' => 0,
'20-30%' => 0,
'30-40%' => 0,
'40-50%' => 0,
'50-60%' => 0,
'60-70%' => 0,
'70-80%' => 0,
'80-90%' => 0,
'90-100%' => 0,
'100%' => 0
);
foreach ($classes as $class) {
if ($class['coverage'] == 0) {
$data['0%']++;
}
else if ($class['coverage'] == 100) {
$data['100%']++;
}
else {
$key = floor($class['coverage']/10)*10;
$key = $key . '-' . ($key + 10) . '%';
$data[$key]++;
}
}
return json_encode(array_values($data));
}
/**
* Returns the least tested methods.
*
* @param array $classes
* @param integer $max
* @return string
*/
protected function leastTestedMethods(array $classes, $max = 10)
{
$methods = array();
foreach ($classes as $className => $class) {
foreach ($class['methods'] as $methodName => $method) {
if ($method['coverage'] < 100) {
if ($className != '*') {
$key = $className . '::' . $methodName;
} else {
$key = $methodName;
}
$methods[$key] = $method['coverage'];
}
}
}
asort($methods);
$methods = array_slice($methods, 0, min($max, count($methods)));
$buffer = '';
foreach ($methods as $name => $coverage) {
list($class, $method) = explode('::', $name);
$buffer .= sprintf(
' <li><a href="%s">%s</a> (%d%%)</li>' . "\n",
$classes[$class]['methods'][$method]['link'],
$name,
$coverage
);
}
return $buffer;
}
/**
* Returns the top project risks according to the CRAP index.
*
* @param array $classes
* @param integer $max
* @return string
*/
protected function topProjectRisks(array $classes, $max = 10)
{
$risks = array();
foreach ($classes as $className => $class) {
if ($class['coverage'] < 100 &&
$class['ccn'] > count($class['methods'])) {
$risks[$className] = $class['crap'];
}
}
arsort($risks);
$buffer = '';
$risks = array_slice($risks, 0, min($max, count($risks)));
foreach ($risks as $name => $crap) {
$buffer .= sprintf(
' <li><a href="%s">%s</a> (%d)</li>' . "\n",
$classes[$name]['link'],
$name,
$crap
);
}
return $buffer;
}
protected function getActiveBreadcrumb(PHP_CodeCoverage_Report_Node $node, $isDirectory)
{
return sprintf(
' <li><a href="%s.html">%s</a></li>' . "\n" .
' <li class="active">(Dashboard)</li>' . "\n",
$node->getId(),
$node->getName()
);
}
}

View File

@@ -0,0 +1,132 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Renders a PHP_CodeCoverage_Report_Node_Directory node.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_HTML_Renderer_Directory extends PHP_CodeCoverage_Report_HTML_Renderer
{
/**
* @param PHP_CodeCoverage_Report_Node_Directory $node
* @param string $file
*/
public function render(PHP_CodeCoverage_Report_Node_Directory $node, $file)
{
$template = new Text_Template($this->templatePath . 'directory.html', '{{', '}}');
$this->setCommonTemplateVariables($template, $node);
$items = $this->renderItem($node, TRUE);
foreach ($node->getDirectories() as $item) {
$items .= $this->renderItem($item);
}
foreach ($node->getFiles() as $item) {
$items .= $this->renderItem($item);
}
$template->setVar(
array(
'id' => $node->getId(),
'items' => $items
)
);
$template->renderTo($file);
}
/**
* @param PHP_CodeCoverage_Report_Node $item
* @param boolean $total
* @return string
*/
protected function renderItem(PHP_CodeCoverage_Report_Node $item, $total = FALSE)
{
$data = array(
'numClasses' => $item->getNumClassesAndTraits(),
'numTestedClasses' => $item->getNumTestedClassesAndTraits(),
'numMethods' => $item->getNumMethods(),
'numTestedMethods' => $item->getNumTestedMethods(),
'linesExecutedPercent' => $item->getLineExecutedPercent(FALSE),
'linesExecutedPercentAsString' => $item->getLineExecutedPercent(),
'numExecutedLines' => $item->getNumExecutedLines(),
'numExecutableLines' => $item->getNumExecutableLines(),
'testedMethodsPercent' => $item->getTestedMethodsPercent(FALSE),
'testedMethodsPercentAsString' => $item->getTestedMethodsPercent(),
'testedClassesPercent' => $item->getTestedClassesAndTraitsPercent(FALSE),
'testedClassesPercentAsString' => $item->getTestedClassesAndTraitsPercent()
);
if ($total) {
$data['name'] = 'Total';
} else {
$data['name'] = sprintf(
'<a href="%s.html">%s</a>',
$item->getId(),
$item->getName()
);
if ($item instanceof PHP_CodeCoverage_Report_Node_Directory) {
$data['icon'] = '<i class="icon-folder-open"></i> ';
} else {
$data['icon'] = '<i class="icon-file"></i> ';
}
}
return $this->renderItemTemplate(
new Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'),
$data
);
}
}

View File

@@ -0,0 +1,583 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
// @codeCoverageIgnoreStart
if (!defined('T_TRAIT')) {
define('T_TRAIT', 1001);
}
if (!defined('T_INSTEADOF')) {
define('T_INSTEADOF', 1002);
}
if (!defined('T_CALLABLE')) {
define('T_CALLABLE', 1003);
}
// @codeCoverageIgnoreEnd
/**
* Renders a PHP_CodeCoverage_Report_Node_File node.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_HTML_Renderer_File extends PHP_CodeCoverage_Report_HTML_Renderer
{
/**
* @var boolean
*/
protected $highlight;
/**
* Constructor.
*
* @param string $templatePath
* @param string $charset
* @param string $generator
* @param string $date
* @param integer $lowUpperBound
* @param integer $highLowerBound
* @param boolean $highlight
*/
public function __construct($templatePath, $charset, $generator, $date, $lowUpperBound, $highLowerBound, $highlight)
{
parent::__construct(
$templatePath,
$charset,
$generator,
$date,
$lowUpperBound,
$highLowerBound
);
$this->highlight = $highlight;
}
/**
* @param PHP_CodeCoverage_Report_Node_File $node
* @param string $file
*/
public function render(PHP_CodeCoverage_Report_Node_File $node, $file)
{
$template = new Text_Template($this->templatePath . 'file.html', '{{', '}}');
$template->setVar(
array(
'items' => $this->renderItems($node),
'lines' => $this->renderSource($node)
)
);
$this->setCommonTemplateVariables($template, $node);
$template->renderTo($file);
}
/**
* @param PHP_CodeCoverage_Report_Node_File $node
* @return string
*/
protected function renderItems(PHP_CodeCoverage_Report_Node_File $node)
{
$template = new Text_Template($this->templatePath . 'file_item.html', '{{', '}}');
$methodItemTemplate = new Text_Template(
$this->templatePath . 'method_item.html', '{{', '}}'
);
$items = $this->renderItemTemplate(
$template,
array(
'name' => 'Total',
'numClasses' => $node->getNumClassesAndTraits(),
'numTestedClasses' => $node->getNumTestedClassesAndTraits(),
'numMethods' => $node->getNumMethods(),
'numTestedMethods' => $node->getNumTestedMethods(),
'linesExecutedPercent' => $node->getLineExecutedPercent(FALSE),
'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
'numExecutedLines' => $node->getNumExecutedLines(),
'numExecutableLines' => $node->getNumExecutableLines(),
'testedMethodsPercent' => $node->getTestedMethodsPercent(FALSE),
'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(),
'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(FALSE),
'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(),
'crap' => '<abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr>'
)
);
$items .= $this->renderFunctionItems(
$node->getFunctions(), $methodItemTemplate
);
$items .= $this->renderTraitOrClassItems(
$node->getTraits(), $template, $methodItemTemplate
);
$items .= $this->renderTraitOrClassItems(
$node->getClasses(), $template, $methodItemTemplate
);
return $items;
}
/**
* @param array $items
* @param Text_Template $template
* @return string
*/
protected function renderTraitOrClassItems(array $items, Text_Template $template, Text_Template $methodItemTemplate)
{
if (empty($items)) {
return '';
}
$buffer = '';
foreach ($items as $name => $item) {
$numMethods = count($item['methods']);
$numTestedMethods = 0;
foreach ($item['methods'] as $method) {
if ($method['executedLines'] == $method['executableLines']) {
$numTestedMethods++;
}
}
$buffer .= $this->renderItemTemplate(
$template,
array(
'name' => $name,
'numClasses' => 1,
'numTestedClasses' => $numTestedMethods == $numMethods ? 1 : 0,
'numMethods' => $numMethods,
'numTestedMethods' => $numTestedMethods,
'linesExecutedPercent' => PHP_CodeCoverage_Util::percent(
$item['executedLines'],
$item['executableLines'],
FALSE
),
'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent(
$item['executedLines'],
$item['executableLines'],
TRUE
),
'numExecutedLines' => $item['executedLines'],
'numExecutableLines' => $item['executableLines'],
'testedMethodsPercent' => PHP_CodeCoverage_Util::percent(
$numTestedMethods,
$numMethods,
FALSE
),
'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent(
$numTestedMethods,
$numMethods,
TRUE
),
'testedClassesPercent' => PHP_CodeCoverage_Util::percent(
$numTestedMethods == $numMethods ? 1 : 0,
1,
FALSE
),
'testedClassesPercentAsString' => PHP_CodeCoverage_Util::percent(
$numTestedMethods == $numMethods ? 1 : 0,
1,
TRUE
),
'crap' => $item['crap']
)
);
foreach ($item['methods'] as $method) {
$buffer .= $this->renderFunctionOrMethodItem(
$methodItemTemplate, $method, '&nbsp;'
);
}
}
return $buffer;
}
/**
* @param array $functions
* @param Text_Template $template
* @return string
*/
protected function renderFunctionItems(array $functions, Text_Template $template)
{
if (empty($functions)) {
return '';
}
$buffer = '';
foreach ($functions as $function) {
$buffer .= $this->renderFunctionOrMethodItem(
$template, $function
);
}
return $buffer;
}
/**
* @param Text_Template $template
* @return string
*/
protected function renderFunctionOrMethodItem(Text_Template $template, array $item, $indent = '')
{
$numTestedItems = $item['executedLines'] == $item['executableLines'] ? 1 : 0;
return $this->renderItemTemplate(
$template,
array(
'name' => sprintf(
'%s<a href="#%d">%s</a>',
$indent,
$item['startLine'],
htmlspecialchars($item['signature'])
),
'numMethods' => 1,
'numTestedMethods' => $numTestedItems,
'linesExecutedPercent' => PHP_CodeCoverage_Util::percent(
$item['executedLines'],
$item['executableLines'],
FALSE
),
'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent(
$item['executedLines'],
$item['executableLines'],
TRUE
),
'numExecutedLines' => $item['executedLines'],
'numExecutableLines' => $item['executableLines'],
'testedMethodsPercent' => PHP_CodeCoverage_Util::percent(
$numTestedItems,
1,
FALSE
),
'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent(
$numTestedItems,
1,
TRUE
),
'crap' => $item['crap']
)
);
}
/**
* @param PHP_CodeCoverage_Report_Node_File $node
* @return string
*/
protected function renderSource(PHP_CodeCoverage_Report_Node_File $node)
{
$coverageData = $node->getCoverageData();
$ignoredLines = $node->getIgnoredLines();
$testData = $node->getTestData();
$codeLines = $this->loadFile($node->getPath());
$lines = '';
$i = 1;
foreach ($codeLines as $line) {
$numTests = '';
$trClass = '';
$popoverContent = '';
$popoverTitle = '';
if (!isset($ignoredLines[$i]) && array_key_exists($i, $coverageData)) {
$numTests = count($coverageData[$i]);
if ($coverageData[$i] === NULL) {
$trClass = ' class="warning"';
}
else if ($numTests == 0) {
$trClass = ' class="danger"';
}
else {
$trClass = ' class="success popin"';
$popoverContent = '<ul>';
if ($numTests > 1) {
$popoverTitle = $numTests . ' tests cover line ' . $i;
} else {
$popoverTitle = '1 test covers line ' . $i;
}
foreach ($coverageData[$i] as $test) {
switch ($testData[$test]) {
case 0: {
$testCSS = ' class="success"';
}
break;
case 1:
case 2: {
$testCSS = ' class="warning"';
}
break;
case 3: {
$testCSS = ' class="danger"';
}
break;
case 4: {
$testCSS = ' class="danger"';
}
break;
default: {
$testCSS = '';
}
}
$popoverContent .= sprintf(
'<li%s>%s</li>',
$testCSS,
htmlspecialchars($test)
);
}
$popoverContent .= '</ul>';
}
}
if (!empty($popoverTitle)) {
$popover = sprintf(
' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"',
$popoverTitle,
htmlspecialchars($popoverContent)
);
} else {
$popover = '';
}
$lines .= sprintf(
' <tr%s%s><td><div align="right"><a name="%d"></a><a href="#%d">%d</a></div></td><td class="codeLine">%s</td></tr>' . "\n",
$trClass,
$popover,
$i,
$i,
$i,
!$this->highlight ? htmlspecialchars($line) : $line
);
$i++;
}
return $lines;
}
/**
* @param string $file
* @return array
*/
protected function loadFile($file)
{
$buffer = file_get_contents($file);
$lines = explode("\n", str_replace("\t", ' ', $buffer));
$result = array();
if (count($lines) == 0) {
return $result;
}
$lines = array_map('rtrim', $lines);
if (!$this->highlight) {
unset($lines[count($lines)-1]);
return $lines;
}
$tokens = token_get_all($buffer);
$stringFlag = FALSE;
$i = 0;
$result[$i] = '';
foreach ($tokens as $j => $token) {
if (is_string($token)) {
if ($token === '"' && $tokens[$j - 1] !== '\\') {
$result[$i] .= sprintf(
'<span class="string">%s</span>',
htmlspecialchars($token)
);
$stringFlag = !$stringFlag;
} else {
$result[$i] .= sprintf(
'<span class="keyword">%s</span>',
htmlspecialchars($token)
);
}
continue;
}
list ($token, $value) = $token;
$value = str_replace(
array("\t", ' '),
array('&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'),
htmlspecialchars($value)
);
if ($value === "\n") {
$result[++$i] = '';
} else {
$lines = explode("\n", $value);
foreach ($lines as $jj => $line) {
$line = trim($line);
if ($line !== '') {
if ($stringFlag) {
$colour = 'string';
} else {
switch ($token) {
case T_INLINE_HTML: {
$colour = 'html';
}
break;
case T_COMMENT:
case T_DOC_COMMENT: {
$colour = 'comment';
}
break;
case T_ABSTRACT:
case T_ARRAY:
case T_AS:
case T_BREAK:
case T_CALLABLE:
case T_CASE:
case T_CATCH:
case T_CLASS:
case T_CLONE:
case T_CONTINUE:
case T_DEFAULT:
case T_ECHO:
case T_ELSE:
case T_ELSEIF:
case T_EMPTY:
case T_ENDDECLARE:
case T_ENDFOR:
case T_ENDFOREACH:
case T_ENDIF:
case T_ENDSWITCH:
case T_ENDWHILE:
case T_EXIT:
case T_EXTENDS:
case T_FINAL:
case T_FOREACH:
case T_FUNCTION:
case T_GLOBAL:
case T_IF:
case T_IMPLEMENTS:
case T_INCLUDE:
case T_INCLUDE_ONCE:
case T_INSTANCEOF:
case T_INSTEADOF:
case T_INTERFACE:
case T_ISSET:
case T_LOGICAL_AND:
case T_LOGICAL_OR:
case T_LOGICAL_XOR:
case T_NAMESPACE:
case T_NEW:
case T_PRIVATE:
case T_PROTECTED:
case T_PUBLIC:
case T_REQUIRE:
case T_REQUIRE_ONCE:
case T_RETURN:
case T_STATIC:
case T_THROW:
case T_TRAIT:
case T_TRY:
case T_UNSET:
case T_USE:
case T_VAR:
case T_WHILE: {
$colour = 'keyword';
}
break;
default: {
$colour = 'default';
}
}
}
$result[$i] .= sprintf(
'<span class="%s">%s</span>',
$colour,
$line
);
}
if (isset($lines[$jj + 1])) {
$result[++$i] = '';
}
}
}
}
unset($result[count($result)-1]);
return $result;
}
}

View File

@@ -0,0 +1,3 @@
<div class="progress progress-{{level}}" style="width: 100px;">
<div class="bar" style="width: {{percent}}%;"></div>
</div>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,769 @@
/********************
* HTML CSS
*/
.chartWrap {
margin: 0;
padding: 0;
overflow: hidden;
}
/********************
Box shadow and border radius styling
*/
.nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip {
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
}
/********************
* TOOLTIP CSS
*/
.nvtooltip {
position: absolute;
background-color: rgba(255,255,255,1.0);
padding: 1px;
border: 1px solid rgba(0,0,0,.2);
z-index: 10000;
font-family: Arial;
font-size: 13px;
text-align: left;
pointer-events: none;
white-space: nowrap;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/*Give tooltips that old fade in transition by
putting a "with-transitions" class on the container div.
*/
.nvtooltip.with-transitions, .with-transitions .nvtooltip {
transition: opacity 250ms linear;
-moz-transition: opacity 250ms linear;
-webkit-transition: opacity 250ms linear;
transition-delay: 250ms;
-moz-transition-delay: 250ms;
-webkit-transition-delay: 250ms;
}
.nvtooltip.x-nvtooltip,
.nvtooltip.y-nvtooltip {
padding: 8px;
}
.nvtooltip h3 {
margin: 0;
padding: 4px 14px;
line-height: 18px;
font-weight: normal;
background-color: rgba(247,247,247,0.75);
text-align: center;
border-bottom: 1px solid #ebebeb;
-webkit-border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
border-radius: 5px 5px 0 0;
}
.nvtooltip p {
margin: 0;
padding: 5px 14px;
text-align: center;
}
.nvtooltip span {
display: inline-block;
margin: 2px 0;
}
.nvtooltip table {
margin: 6px;
border-spacing:0;
}
.nvtooltip table td {
padding: 2px 9px 2px 0;
vertical-align: middle;
}
.nvtooltip table td.key {
font-weight:normal;
}
.nvtooltip table td.value {
text-align: right;
font-weight: bold;
}
.nvtooltip table tr.highlight td {
padding: 1px 9px 1px 0;
border-bottom-style: solid;
border-bottom-width: 1px;
border-top-style: solid;
border-top-width: 1px;
}
.nvtooltip table td.legend-color-guide div {
width: 8px;
height: 8px;
vertical-align: middle;
}
.nvtooltip .footer {
padding: 3px;
text-align: center;
}
.nvtooltip-pending-removal {
position: absolute;
pointer-events: none;
}
/********************
* SVG CSS
*/
svg {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* Trying to get SVG to act like a greedy block in all browsers */
display: block;
width:100%;
height:100%;
}
svg text {
font: normal 12px Arial;
}
svg .title {
font: bold 14px Arial;
}
.nvd3 .nv-background {
fill: white;
fill-opacity: 0;
/*
pointer-events: none;
*/
}
.nvd3.nv-noData {
font-size: 18px;
font-weight: bold;
}
/**********
* Brush
*/
.nv-brush .extent {
fill-opacity: .125;
shape-rendering: crispEdges;
}
/**********
* Legend
*/
.nvd3 .nv-legend .nv-series {
cursor: pointer;
}
.nvd3 .nv-legend .disabled circle {
fill-opacity: 0;
}
/**********
* Axes
*/
.nvd3 .nv-axis {
pointer-events:none;
}
.nvd3 .nv-axis path {
fill: none;
stroke: #000;
stroke-opacity: .75;
shape-rendering: crispEdges;
}
.nvd3 .nv-axis path.domain {
stroke-opacity: .75;
}
.nvd3 .nv-axis.nv-x path.domain {
stroke-opacity: 0;
}
.nvd3 .nv-axis line {
fill: none;
stroke: #e5e5e5;
shape-rendering: crispEdges;
}
.nvd3 .nv-axis .zero line,
/*this selector may not be necessary*/ .nvd3 .nv-axis line.zero {
stroke-opacity: .75;
}
.nvd3 .nv-axis .nv-axisMaxMin text {
font-weight: bold;
}
.nvd3 .x .nv-axis .nv-axisMaxMin text,
.nvd3 .x2 .nv-axis .nv-axisMaxMin text,
.nvd3 .x3 .nv-axis .nv-axisMaxMin text {
text-anchor: middle
}
/**********
* Brush
*/
.nv-brush .resize path {
fill: #eee;
stroke: #666;
}
/**********
* Bars
*/
.nvd3 .nv-bars .negative rect {
zfill: brown;
}
.nvd3 .nv-bars rect {
zfill: steelblue;
fill-opacity: .75;
transition: fill-opacity 250ms linear;
-moz-transition: fill-opacity 250ms linear;
-webkit-transition: fill-opacity 250ms linear;
}
.nvd3 .nv-bars rect.hover {
fill-opacity: 1;
}
.nvd3 .nv-bars .hover rect {
fill: lightblue;
}
.nvd3 .nv-bars text {
fill: rgba(0,0,0,0);
}
.nvd3 .nv-bars .hover text {
fill: rgba(0,0,0,1);
}
/**********
* Bars
*/
.nvd3 .nv-multibar .nv-groups rect,
.nvd3 .nv-multibarHorizontal .nv-groups rect,
.nvd3 .nv-discretebar .nv-groups rect {
stroke-opacity: 0;
transition: fill-opacity 250ms linear;
-moz-transition: fill-opacity 250ms linear;
-webkit-transition: fill-opacity 250ms linear;
}
.nvd3 .nv-multibar .nv-groups rect:hover,
.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,
.nvd3 .nv-discretebar .nv-groups rect:hover {
fill-opacity: 1;
}
.nvd3 .nv-discretebar .nv-groups text,
.nvd3 .nv-multibarHorizontal .nv-groups text {
font-weight: bold;
fill: rgba(0,0,0,1);
stroke: rgba(0,0,0,0);
}
/***********
* Pie Chart
*/
.nvd3.nv-pie path {
stroke-opacity: 0;
transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
-moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
-webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
}
.nvd3.nv-pie .nv-slice text {
stroke: #000;
stroke-width: 0;
}
.nvd3.nv-pie path {
stroke: #fff;
stroke-width: 1px;
stroke-opacity: 1;
}
.nvd3.nv-pie .hover path {
fill-opacity: .7;
}
.nvd3.nv-pie .nv-label {
pointer-events: none;
}
.nvd3.nv-pie .nv-label rect {
fill-opacity: 0;
stroke-opacity: 0;
}
/**********
* Lines
*/
.nvd3 .nv-groups path.nv-line {
fill: none;
stroke-width: 1.5px;
/*
stroke-linecap: round;
shape-rendering: geometricPrecision;
transition: stroke-width 250ms linear;
-moz-transition: stroke-width 250ms linear;
-webkit-transition: stroke-width 250ms linear;
transition-delay: 250ms
-moz-transition-delay: 250ms;
-webkit-transition-delay: 250ms;
*/
}
.nvd3 .nv-groups path.nv-line.nv-thin-line {
stroke-width: 1px;
}
.nvd3 .nv-groups path.nv-area {
stroke: none;
/*
stroke-linecap: round;
shape-rendering: geometricPrecision;
stroke-width: 2.5px;
transition: stroke-width 250ms linear;
-moz-transition: stroke-width 250ms linear;
-webkit-transition: stroke-width 250ms linear;
transition-delay: 250ms
-moz-transition-delay: 250ms;
-webkit-transition-delay: 250ms;
*/
}
.nvd3 .nv-line.hover path {
stroke-width: 6px;
}
/*
.nvd3.scatter .groups .point {
fill-opacity: 0.1;
stroke-opacity: 0.1;
}
*/
.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point {
fill-opacity: 0;
stroke-opacity: 0;
}
.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
fill-opacity: .5 !important;
stroke-opacity: .5 !important;
}
.with-transitions .nvd3 .nv-groups .nv-point {
transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
-moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
-webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
}
.nvd3.nv-scatter .nv-groups .nv-point.hover,
.nvd3 .nv-groups .nv-point.hover {
stroke-width: 7px;
fill-opacity: .95 !important;
stroke-opacity: .95 !important;
}
.nvd3 .nv-point-paths path {
stroke: #aaa;
stroke-opacity: 0;
fill: #eee;
fill-opacity: 0;
}
.nvd3 .nv-indexLine {
cursor: ew-resize;
}
/**********
* Distribution
*/
.nvd3 .nv-distribution {
pointer-events: none;
}
/**********
* Scatter
*/
/* **Attempting to remove this for useVoronoi(false), need to see if it's required anywhere
.nvd3 .nv-groups .nv-point {
pointer-events: none;
}
*/
.nvd3 .nv-groups .nv-point.hover {
stroke-width: 20px;
stroke-opacity: .5;
}
.nvd3 .nv-scatter .nv-point.hover {
fill-opacity: 1;
}
/*
.nv-group.hover .nv-point {
fill-opacity: 1;
}
*/
/**********
* Stacked Area
*/
.nvd3.nv-stackedarea path.nv-area {
fill-opacity: .7;
/*
stroke-opacity: .65;
fill-opacity: 1;
*/
stroke-opacity: 0;
transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
-moz-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
-webkit-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
/*
transition-delay: 500ms;
-moz-transition-delay: 500ms;
-webkit-transition-delay: 500ms;
*/
}
.nvd3.nv-stackedarea path.nv-area.hover {
fill-opacity: .9;
/*
stroke-opacity: .85;
*/
}
/*
.d3stackedarea .groups path {
stroke-opacity: 0;
}
*/
.nvd3.nv-stackedarea .nv-groups .nv-point {
stroke-opacity: 0;
fill-opacity: 0;
}
/*
.nvd3.nv-stackedarea .nv-groups .nv-point.hover {
stroke-width: 20px;
stroke-opacity: .75;
fill-opacity: 1;
}*/
/**********
* Line Plus Bar
*/
.nvd3.nv-linePlusBar .nv-bar rect {
fill-opacity: .75;
}
.nvd3.nv-linePlusBar .nv-bar rect:hover {
fill-opacity: 1;
}
/**********
* Bullet
*/
.nvd3.nv-bullet { font: 10px sans-serif; }
.nvd3.nv-bullet .nv-measure { fill-opacity: .8; }
.nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; }
.nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; }
.nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; }
.nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; }
.nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; }
.nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; }
.nvd3.nv-bullet .nv-range.nv-s2 { fill: #ccc; }
.nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; }
.nvd3.nv-bullet .nv-subtitle { fill: #999; }
.nvd3.nv-bullet .nv-range {
fill: #bababa;
fill-opacity: .4;
}
.nvd3.nv-bullet .nv-range:hover {
fill-opacity: .7;
}
/**********
* Sparkline
*/
.nvd3.nv-sparkline path {
fill: none;
}
.nvd3.nv-sparklineplus g.nv-hoverValue {
pointer-events: none;
}
.nvd3.nv-sparklineplus .nv-hoverValue line {
stroke: #333;
stroke-width: 1.5px;
}
.nvd3.nv-sparklineplus,
.nvd3.nv-sparklineplus g {
pointer-events: all;
}
.nvd3 .nv-hoverArea {
fill-opacity: 0;
stroke-opacity: 0;
}
.nvd3.nv-sparklineplus .nv-xValue,
.nvd3.nv-sparklineplus .nv-yValue {
/*
stroke: #666;
*/
stroke-width: 0;
font-size: .9em;
font-weight: normal;
}
.nvd3.nv-sparklineplus .nv-yValue {
stroke: #f66;
}
.nvd3.nv-sparklineplus .nv-maxValue {
stroke: #2ca02c;
fill: #2ca02c;
}
.nvd3.nv-sparklineplus .nv-minValue {
stroke: #d62728;
fill: #d62728;
}
.nvd3.nv-sparklineplus .nv-currentValue {
/*
stroke: #444;
fill: #000;
*/
font-weight: bold;
font-size: 1.1em;
}
/**********
* historical stock
*/
.nvd3.nv-ohlcBar .nv-ticks .nv-tick {
stroke-width: 2px;
}
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover {
stroke-width: 4px;
}
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive {
stroke: #2ca02c;
}
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative {
stroke: #d62728;
}
.nvd3.nv-historicalStockChart .nv-axis .nv-axislabel {
font-weight: bold;
}
.nvd3.nv-historicalStockChart .nv-dragTarget {
fill-opacity: 0;
stroke: none;
cursor: move;
}
.nvd3 .nv-brush .extent {
/*
cursor: ew-resize !important;
*/
fill-opacity: 0 !important;
}
.nvd3 .nv-brushBackground rect {
stroke: #000;
stroke-width: .4;
fill: #fff;
fill-opacity: .7;
}
/**********
* Indented Tree
*/
/**
* TODO: the following 3 selectors are based on classes used in the example. I should either make them standard and leave them here, or move to a CSS file not included in the library
*/
.nvd3.nv-indentedtree .name {
margin-left: 5px;
}
.nvd3.nv-indentedtree .clickable {
color: #08C;
cursor: pointer;
}
.nvd3.nv-indentedtree span.clickable:hover {
color: #005580;
text-decoration: underline;
}
.nvd3.nv-indentedtree .nv-childrenCount {
display: inline-block;
margin-left: 5px;
}
.nvd3.nv-indentedtree .nv-treeicon {
cursor: pointer;
/*
cursor: n-resize;
*/
}
.nvd3.nv-indentedtree .nv-treeicon.nv-folded {
cursor: pointer;
/*
cursor: s-resize;
*/
}
/**********
* Parallel Coordinates
*/
.nvd3 .background path {
fill: none;
stroke: #ccc;
stroke-opacity: .4;
shape-rendering: crispEdges;
}
.nvd3 .foreground path {
fill: none;
stroke: steelblue;
stroke-opacity: .7;
}
.nvd3 .brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.nvd3 .axis line, .axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.nvd3 .axis text {
text-shadow: 0 1px 0 #fff;
}
/****
Interactive Layer
*/
.nvd3 .nv-interactiveGuideLine {
pointer-events:none;
}
.nvd3 line.nv-guideline {
stroke: #ccc;
}

View File

@@ -0,0 +1,93 @@
body {
padding-top: 10px;
}
.popover {
width: 600px;
}
.table td {
padding-top: 3px;
padding-bottom: 3px;
}
.table-condensed td {
padding-top: 0;
padding-bottom: 0;
}
.table .progress {
margin-bottom: inherit;
}
.table-borderless th, .table-borderless td {
border: 0 !important;
}
.table tbody td.success, li.success, span.success {
background-color: #dff0d8;
}
.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger {
background-color: #f2dede;
}
.table tbody td.warning, li.warning, span.warning {
background-color: #fcf8e3;
}
.table tbody td.info {
background-color: #d9edf7;
}
td.big {
width: 100px;
}
td.small {
}
td.codeLine {
font-family: monospace;
white-space: pre;
}
td span.comment {
color: #888a85;
}
td span.default {
color: #2e3436;
}
td span.html {
color: #888a85;
}
td span.keyword {
color: #2e3436;
font-weight: bold;
}
pre span.string {
color: #2e3436;
}
span.success, span.warning, span.danger {
margin-right: 2px;
padding-left: 10px;
padding-right: 10px;
text-align: center;
}
#classCoverageDistribution, #classComplexity {
height: 200px;
width: 475px;
}
svg text {
font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
font-size: 11px;
color: #666;
fill: #666;
}

View File

@@ -0,0 +1,164 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="{{charset}}">
<title>Dashboard for {{full_path}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="css/nv.d3.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<header>
<div class="container">
<div class="row">
<div class="span12">
<ul class="breadcrumb">
{{breadcrumbs}}
</ul>
</div>
</div>
</div>
</header>
<div class="container">
<div class="row">
<div class="span6">
<h2>Class Coverage Distribution</h2>
<div id="classCoverageDistribution">
<svg></svg>
</div>
</div>
<div class="span6">
<h2>Class Complexity</h2>
<div id="classComplexity">
<svg></svg>
</div>
</div>
</div>
<div class="row">
<div class="span6">
<h2>Top Project Risks</h2>
<ul>
{{top_project_risks}}
</ul>
</div>
<div class="span6">
<h2>Least Tested Methods</h2>
<ul>
{{least_tested_methods}}
</ul>
</div>
</div>
<footer>
<p>
<small>Generated by <a href="http://github.com/sebastianbergmann/php-code-coverage" target="_top">PHP_CodeCoverage {{version}}</a> using <a href="http://www.php.net/" target="_top">PHP {{php_version}}</a>{{generator}} at {{date}}.</small>
</p>
</footer>
</div>
<script src="js/jquery.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
<script src="js/d3.min.js" type="text/javascript"></script>
<script src="js/nv.d3.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
nv.addGraph(function() {
var chart = nv.models.multiBarChart();
chart.tooltips(false)
.showControls(false)
.showLegend(false)
.reduceXTicks(false)
.staggerLabels(true)
.yAxis.tickFormat(d3.format('d'));
d3.select('#classCoverageDistribution svg')
.datum(getCoverageDistributionData({{ccd_values}}, "Class Coverage"))
.transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function getCoverageDistributionData(data, label) {
var labels = [
'0%',
'0-10%',
'10-20%',
'20-30%',
'30-40%',
'40-50%',
'50-60%',
'60-70%',
'70-80%',
'80-90%',
'90-100%',
'100%'
];
var values = [];
$.each(labels, function(key) {
values.push({x: labels[key], y: data[key]});
});
return [
{
key: label,
values: values,
color: "#4572A7"
}
];
}
nv.addGraph(function() {
var chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.showLegend(false)
.forceX([0, 100]);
chart.scatter.onlyCircles(false);
chart.tooltipContent(function(key, y, e, graph) {
return '<p>' + graph.point.class + '</p>';
});
chart.xAxis.axisLabel('Code Coverage (in percent)');
chart.yAxis.axisLabel('Cyclomatic Complexity');
d3.select('#classComplexity svg')
.datum(getComplexityData({{cc_values}}, 'Class Complexity'))
.transition()
.duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function getComplexityData(data, label) {
var values = [];
$.each(data, function(key) {
var value = Math.round(data[key][0]*100) / 100;
values.push({
x: value,
y: data[key][1],
class: data[key][2],
size: 0.05,
shape: 'diamond'
});
});
return [
{
key: label,
values: values,
color: "#4572A7"
}
];
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="{{charset}}">
<title>Code Coverage for {{full_path}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<header>
<div class="container">
<div class="row">
<div class="span12">
<ul class="breadcrumb">
{{breadcrumbs}}
</ul>
</div>
</div>
</div>
</header>
<div class="container">
<table class="table table-bordered">
<thead>
<tr>
<td>&nbsp;</td>
<td colspan="9"><div align="center"><strong>Code Coverage</strong></div></td>
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"><div align="center"><strong>Lines</strong></div></td>
<td colspan="3"><div align="center"><strong>Functions and Methods</strong></div></td>
<td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
</tr>
</thead>
<tbody>
{{items}}
</tbody>
</table>
<footer>
<h4>Legend</h4>
<p>
<span class="danger"><strong>Low</strong>: 0% to {{low_upper_bound}}%</span>
<span class="warning"><strong>Medium</strong>: {{low_upper_bound}}% to {{high_lower_bound}}%</span>
<span class="success"><strong>High</strong>: {{high_lower_bound}}% to 100%</span>
</p>
<p>
<small>Generated by <a href="http://github.com/sebastianbergmann/php-code-coverage" target="_top">PHP_CodeCoverage {{version}}</a> using <a href="http://www.php.net/" target="_top">PHP {{php_version}}</a>{{generator}} at {{date}}.</small>
</p>
</footer>
</div>
<script src="js/jquery.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
</body>
</html>

View File

@@ -0,0 +1,13 @@
<tr>
<td class="{{lines_level}}">{{icon}}{{name}}</td>
<td class="{{lines_level}} big">{{lines_bar}}</td>
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
<td class="{{methods_level}} big">{{methods_bar}}</td>
<td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
<td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
<td class="{{classes_level}} big">{{classes_bar}}</td>
<td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
<td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
</tr>

View File

@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="{{charset}}">
<title>Code Coverage for {{full_path}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<header>
<div class="container">
<div class="row">
<div class="span12">
<ul class="breadcrumb">
{{breadcrumbs}}
</ul>
</div>
</div>
</div>
</header>
<div class="container">
<table class="table table-bordered">
<thead>
<tr>
<td>&nbsp;</td>
<td colspan="10"><div align="center"><strong>Code Coverage</strong></div></td>
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
<td colspan="4"><div align="center"><strong>Functions and Methods</strong></div></td>
<td colspan="3"><div align="center"><strong>Lines</strong></div></td>
</tr>
</thead>
<tbody>
{{items}}
</tbody>
</table>
<table class="table table-borderless table-condensed">
<tbody>
{{lines}}
</tbody>
</table>
<footer>
<h4>Legend</h4>
<p>
<span class="success"><strong>Executed</strong></span>
<span class="danger"><strong>Not Executed</strong></span>
<span class="warning"><strong>Dead Code</strong></span>
</p>
<p>
<small>Generated by <a href="http://github.com/sebastianbergmann/php-code-coverage" target="_top">PHP_CodeCoverage {{version}}</a> using <a href="http://www.php.net/" target="_top">PHP {{php_version}}</a>{{generator}} at {{date}}.</small>
</p>
</footer>
</div>
<script src="js/jquery.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
<script type="text/javascript">$('.popin').popover({trigger: 'hover'});</script>
</body>
</html>

View File

@@ -0,0 +1,14 @@
<tr>
<td class="{{classes_level}}">{{name}}</td>
<td class="{{classes_level}} big">{{classes_bar}}</td>
<td class="{{classes_level}} small"><div align="right">{{classes_tested_percent}}</div></td>
<td class="{{classes_level}} small"><div align="right">{{classes_number}}</div></td>
<td class="{{methods_level}} big">{{methods_bar}}</td>
<td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
<td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
<td class="{{methods_level}} small">{{crap}}</td>
<td class="{{lines_level}} big">{{lines_bar}}</td>
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
</tr>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/*
HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
*/
(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag();
a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x<style>article,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}</style>";
c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="<xyz></xyz>";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode||
"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment();
for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d<h;d++)c.createElement(e[d]);return c}};l.html5=e;q(f)})(this,document);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
<tr>
<td class="{{methods_level}}" colspan="4">{{name}}</td>
<td class="{{methods_level}} big">{{methods_bar}}</td>
<td class="{{methods_level}} small"><div align="right">{{methods_tested_percent}}</div></td>
<td class="{{methods_level}} small"><div align="right">{{methods_number}}</div></td>
<td class="{{methods_level}} small">{{crap}}</td>
<td class="{{lines_level}} big">{{lines_bar}}</td>
<td class="{{lines_level}} small"><div align="right">{{lines_executed_percent}}</div></td>
<td class="{{lines_level}} small"><div align="right">{{lines_number}}</div></td>
</tr>

View File

@@ -0,0 +1,380 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Base class for nodes in the code coverage information tree.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
abstract class PHP_CodeCoverage_Report_Node implements Countable
{
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $path;
/**
* @var array
*/
protected $pathArray;
/**
* @var PHP_CodeCoverage_Report_Node
*/
protected $parent;
/**
* @var string
*/
protected $id;
/**
* Constructor.
*
* @param string $name
* @param PHP_CodeCoverage_Report_Node $parent
*/
public function __construct($name, PHP_CodeCoverage_Report_Node $parent = NULL)
{
if (substr($name, -1) == '/') {
$name = substr($name, 0, -1);
}
$this->name = $name;
$this->parent = $parent;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function getId()
{
if ($this->id === NULL) {
$parent = $this->getParent();
if ($parent === NULL) {
$this->id = 'index';
} else {
$parentId = $parent->getId();
if ($parentId == 'index') {
$this->id = str_replace(':', '_', $this->name);
} else {
$this->id = $parentId . '_' . $this->name;
}
}
}
return $this->id;
}
/**
* @return string
*/
public function getPath()
{
if ($this->path === NULL) {
if ($this->parent === NULL || $this->parent->getPath() === NULL) {
$this->path = $this->name;
} else {
$this->path = $this->parent->getPath() . '/' . $this->name;
}
}
return $this->path;
}
/**
* @return array
*/
public function getPathAsArray()
{
if ($this->pathArray === NULL) {
if ($this->parent === NULL) {
$this->pathArray = array();
} else {
$this->pathArray = $this->parent->getPathAsArray();
}
$this->pathArray[] = $this;
}
return $this->pathArray;
}
/**
* @return PHP_CodeCoverage_Report_Node
*/
public function getParent()
{
return $this->parent;
}
/**
* Returns the percentage of classes that has been tested.
*
* @param boolean $asString
* @return integer
*/
public function getTestedClassesPercent($asString = TRUE)
{
return PHP_CodeCoverage_Util::percent(
$this->getNumTestedClasses(),
$this->getNumClasses(),
$asString
);
}
/**
* Returns the percentage of traits that has been tested.
*
* @param boolean $asString
* @return integer
*/
public function getTestedTraitsPercent($asString = TRUE)
{
return PHP_CodeCoverage_Util::percent(
$this->getNumTestedTraits(),
$this->getNumTraits(),
$asString
);
}
/**
* Returns the percentage of traits that has been tested.
*
* @param boolean $asString
* @return integer
* @since Method available since Release 1.2.0
*/
public function getTestedClassesAndTraitsPercent($asString = TRUE)
{
return PHP_CodeCoverage_Util::percent(
$this->getNumTestedClassesAndTraits(),
$this->getNumClassesAndTraits(),
$asString
);
}
/**
* Returns the percentage of methods that has been tested.
*
* @param boolean $asString
* @return integer
*/
public function getTestedMethodsPercent($asString = TRUE)
{
return PHP_CodeCoverage_Util::percent(
$this->getNumTestedMethods(),
$this->getNumMethods(),
$asString
);
}
/**
* Returns the percentage of executed lines.
*
* @param boolean $asString
* @return integer
*/
public function getLineExecutedPercent($asString = TRUE)
{
return PHP_CodeCoverage_Util::percent(
$this->getNumExecutedLines(),
$this->getNumExecutableLines(),
$asString
);
}
/**
* Returns the number of classes and traits.
*
* @return integer
* @since Method available since Release 1.2.0
*/
public function getNumClassesAndTraits()
{
return $this->getNumClasses() + $this->getNumTraits();
}
/**
* Returns the number of tested classes and traits.
*
* @return integer
* @since Method available since Release 1.2.0
*/
public function getNumTestedClassesAndTraits()
{
return $this->getNumTestedClasses() + $this->getNumTestedTraits();
}
/**
* Returns the classes and traits of this node.
*
* @return array
* @since Method available since Release 1.2.0
*/
public function getClassesAndTraits()
{
return array_merge($this->getClasses(), $this->getTraits());
}
/**
* Returns the classes of this node.
*
* @return array
*/
abstract public function getClasses();
/**
* Returns the traits of this node.
*
* @return array
*/
abstract public function getTraits();
/**
* Returns the functions of this node.
*
* @return array
*/
abstract public function getFunctions();
/**
* Returns the LOC/CLOC/NCLOC of this node.
*
* @return array
*/
abstract public function getLinesOfCode();
/**
* Returns the number of executable lines.
*
* @return integer
*/
abstract public function getNumExecutableLines();
/**
* Returns the number of executed lines.
*
* @return integer
*/
abstract public function getNumExecutedLines();
/**
* Returns the number of classes.
*
* @return integer
*/
abstract public function getNumClasses();
/**
* Returns the number of tested classes.
*
* @return integer
*/
abstract public function getNumTestedClasses();
/**
* Returns the number of traits.
*
* @return integer
*/
abstract public function getNumTraits();
/**
* Returns the number of tested traits.
*
* @return integer
*/
abstract public function getNumTestedTraits();
/**
* Returns the number of methods.
*
* @return integer
*/
abstract public function getNumMethods();
/**
* Returns the number of tested methods.
*
* @return integer
*/
abstract public function getNumTestedMethods();
/**
* Returns the number of functions.
*
* @return integer
*/
abstract public function getNumFunctions();
/**
* Returns the number of tested functions.
*
* @return integer
*/
abstract public function getNumTestedFunctions();
}

View File

@@ -0,0 +1,512 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Represents a directory in the code coverage information tree.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_Node_Directory extends PHP_CodeCoverage_Report_Node implements IteratorAggregate
{
/**
* @var PHP_CodeCoverage_Report_Node[]
*/
protected $children = array();
/**
* @var PHP_CodeCoverage_Report_Node_Directory[]
*/
protected $directories = array();
/**
* @var PHP_CodeCoverage_Report_Node_File[]
*/
protected $files = array();
/**
* @var array
*/
protected $classes;
/**
* @var array
*/
protected $traits;
/**
* @var array
*/
protected $functions;
/**
* @var array
*/
protected $linesOfCode = NULL;
/**
* @var integer
*/
protected $numFiles = -1;
/**
* @var integer
*/
protected $numExecutableLines = -1;
/**
* @var integer
*/
protected $numExecutedLines = -1;
/**
* @var integer
*/
protected $numClasses = -1;
/**
* @var integer
*/
protected $numTestedClasses = -1;
/**
* @var integer
*/
protected $numTraits = -1;
/**
* @var integer
*/
protected $numTestedTraits = -1;
/**
* @var integer
*/
protected $numMethods = -1;
/**
* @var integer
*/
protected $numTestedMethods = -1;
/**
* @var integer
*/
protected $numFunctions = -1;
/**
* @var integer
*/
protected $numTestedFunctions = -1;
/**
* Returns the number of files in/under this node.
*
* @return integer
*/
public function count()
{
if ($this->numFiles == -1) {
$this->numFiles = 0;
foreach ($this->children as $child) {
$this->numFiles += count($child);
}
}
return $this->numFiles;
}
/**
* Returns an iterator for this node.
*
* @return RecursiveIteratorIterator
*/
public function getIterator()
{
return new RecursiveIteratorIterator(
new PHP_CodeCoverage_Report_Node_Iterator($this),
RecursiveIteratorIterator::SELF_FIRST
);
}
/**
* Adds a new directory.
*
* @param string $name
* @return PHP_CodeCoverage_Report_Node_Directory
*/
public function addDirectory($name)
{
$directory = new PHP_CodeCoverage_Report_Node_Directory($name, $this);
$this->children[] = $directory;
$this->directories[] = &$this->children[count($this->children) - 1];
return $directory;
}
/**
* Adds a new file.
*
* @param string $name
* @param array $coverageData
* @param array $testData
* @param boolean $cacheTokens
* @return PHP_CodeCoverage_Report_Node_File
* @throws PHP_CodeCoverage_Exception
*/
public function addFile($name, array $coverageData, array $testData, $cacheTokens)
{
$file = new PHP_CodeCoverage_Report_Node_File(
$name, $this, $coverageData, $testData, $cacheTokens
);
$this->children[] = $file;
$this->files[] = &$this->children[count($this->children) - 1];
$this->numExecutableLines = -1;
$this->numExecutedLines = -1;
return $file;
}
/**
* Returns the directories in this directory.
*
* @return array
*/
public function getDirectories()
{
return $this->directories;
}
/**
* Returns the files in this directory.
*
* @return array
*/
public function getFiles()
{
return $this->files;
}
/**
* Returns the child nodes of this node.
*
* @return array
*/
public function getChildNodes()
{
return $this->children;
}
/**
* Returns the classes of this node.
*
* @return array
*/
public function getClasses()
{
if ($this->classes === NULL) {
$this->classes = array();
foreach ($this->children as $child) {
$this->classes = array_merge(
$this->classes, $child->getClasses()
);
}
}
return $this->classes;
}
/**
* Returns the traits of this node.
*
* @return array
*/
public function getTraits()
{
if ($this->traits === NULL) {
$this->traits = array();
foreach ($this->children as $child) {
$this->traits = array_merge(
$this->traits, $child->getTraits()
);
}
}
return $this->traits;
}
/**
* Returns the functions of this node.
*
* @return array
*/
public function getFunctions()
{
if ($this->functions === NULL) {
$this->functions = array();
foreach ($this->children as $child) {
$this->functions = array_merge(
$this->functions, $child->getFunctions()
);
}
}
return $this->functions;
}
/**
* Returns the LOC/CLOC/NCLOC of this node.
*
* @return array
*/
public function getLinesOfCode()
{
if ($this->linesOfCode === NULL) {
$this->linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0);
foreach ($this->children as $child) {
$linesOfCode = $child->getLinesOfCode();
$this->linesOfCode['loc'] += $linesOfCode['loc'];
$this->linesOfCode['cloc'] += $linesOfCode['cloc'];
$this->linesOfCode['ncloc'] += $linesOfCode['ncloc'];
}
}
return $this->linesOfCode;
}
/**
* Returns the number of executable lines.
*
* @return integer
*/
public function getNumExecutableLines()
{
if ($this->numExecutableLines == -1) {
$this->numExecutableLines = 0;
foreach ($this->children as $child) {
$this->numExecutableLines += $child->getNumExecutableLines();
}
}
return $this->numExecutableLines;
}
/**
* Returns the number of executed lines.
*
* @return integer
*/
public function getNumExecutedLines()
{
if ($this->numExecutedLines == -1) {
$this->numExecutedLines = 0;
foreach ($this->children as $child) {
$this->numExecutedLines += $child->getNumExecutedLines();
}
}
return $this->numExecutedLines;
}
/**
* Returns the number of classes.
*
* @return integer
*/
public function getNumClasses()
{
if ($this->numClasses == -1) {
$this->numClasses = 0;
foreach ($this->children as $child) {
$this->numClasses += $child->getNumClasses();
}
}
return $this->numClasses;
}
/**
* Returns the number of tested classes.
*
* @return integer
*/
public function getNumTestedClasses()
{
if ($this->numTestedClasses == -1) {
$this->numTestedClasses = 0;
foreach ($this->children as $child) {
$this->numTestedClasses += $child->getNumTestedClasses();
}
}
return $this->numTestedClasses;
}
/**
* Returns the number of traits.
*
* @return integer
*/
public function getNumTraits()
{
if ($this->numTraits == -1) {
$this->numTraits = 0;
foreach ($this->children as $child) {
$this->numTraits += $child->getNumTraits();
}
}
return $this->numTraits;
}
/**
* Returns the number of tested traits.
*
* @return integer
*/
public function getNumTestedTraits()
{
if ($this->numTestedTraits == -1) {
$this->numTestedTraits = 0;
foreach ($this->children as $child) {
$this->numTestedTraits += $child->getNumTestedTraits();
}
}
return $this->numTestedTraits;
}
/**
* Returns the number of methods.
*
* @return integer
*/
public function getNumMethods()
{
if ($this->numMethods == -1) {
$this->numMethods = 0;
foreach ($this->children as $child) {
$this->numMethods += $child->getNumMethods();
}
}
return $this->numMethods;
}
/**
* Returns the number of tested methods.
*
* @return integer
*/
public function getNumTestedMethods()
{
if ($this->numTestedMethods == -1) {
$this->numTestedMethods = 0;
foreach ($this->children as $child) {
$this->numTestedMethods += $child->getNumTestedMethods();
}
}
return $this->numTestedMethods;
}
/**
* Returns the number of functions.
*
* @return integer
*/
public function getNumFunctions()
{
if ($this->numFunctions == -1) {
$this->numFunctions = 0;
foreach ($this->children as $child) {
$this->numFunctions += $child->getNumFunctions();
}
}
return $this->numFunctions;
}
/**
* Returns the number of tested functions.
*
* @return integer
*/
public function getNumTestedFunctions()
{
if ($this->numTestedFunctions == -1) {
$this->numTestedFunctions = 0;
foreach ($this->children as $child) {
$this->numTestedFunctions += $child->getNumTestedFunctions();
}
}
return $this->numTestedFunctions;
}
}

View File

@@ -0,0 +1,721 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Represents a file in the code coverage information tree.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_Node_File extends PHP_CodeCoverage_Report_Node
{
/**
* @var array
*/
protected $coverageData;
/**
* @var array
*/
protected $testData;
/**
* @var array
*/
protected $ignoredLines;
/**
* @var integer
*/
protected $numExecutableLines = 0;
/**
* @var integer
*/
protected $numExecutedLines = 0;
/**
* @var array
*/
protected $classes = array();
/**
* @var array
*/
protected $traits = array();
/**
* @var array
*/
protected $functions = array();
/**
* @var array
*/
protected $linesOfCode = array();
/**
* @var integer
*/
protected $numTestedTraits = 0;
/**
* @var integer
*/
protected $numTestedClasses = 0;
/**
* @var integer
*/
protected $numMethods = NULL;
/**
* @var integer
*/
protected $numTestedMethods = NULL;
/**
* @var integer
*/
protected $numTestedFunctions = NULL;
/**
* @var array
*/
protected $startLines = array();
/**
* @var array
*/
protected $endLines = array();
/**
* @var boolean
*/
protected $cacheTokens;
/**
* Constructor.
*
* @param string $name
* @param PHP_CodeCoverage_Report_Node $parent
* @param array $coverageData
* @param array $testData
* @param boolean $cacheTokens
* @throws PHP_CodeCoverage_Exception
*/
public function __construct($name, PHP_CodeCoverage_Report_Node $parent, array $coverageData, array $testData, $cacheTokens)
{
if (!is_bool($cacheTokens)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'boolean'
);
}
parent::__construct($name, $parent);
$this->coverageData = $coverageData;
$this->testData = $testData;
$this->ignoredLines = PHP_CodeCoverage_Util::getLinesToBeIgnored(
$this->getPath(), $cacheTokens
);
$this->cacheTokens = $cacheTokens;
$this->calculateStatistics();
}
/**
* Returns the number of files in/under this node.
*
* @return integer
*/
public function count()
{
return 1;
}
/**
* Returns the code coverage data of this node.
*
* @return array
*/
public function getCoverageData()
{
return $this->coverageData;
}
/**
* Returns the test data of this node.
*
* @return array
*/
public function getTestData()
{
return $this->testData;
}
/**
* @return array
*/
public function getIgnoredLines()
{
return $this->ignoredLines;
}
/**
* Returns the classes of this node.
*
* @return array
*/
public function getClasses()
{
return $this->classes;
}
/**
* Returns the traits of this node.
*
* @return array
*/
public function getTraits()
{
return $this->traits;
}
/**
* Returns the functions of this node.
*
* @return array
*/
public function getFunctions()
{
return $this->functions;
}
/**
* Returns the LOC/CLOC/NCLOC of this node.
*
* @return array
*/
public function getLinesOfCode()
{
return $this->linesOfCode;
}
/**
* Returns the number of executable lines.
*
* @return integer
*/
public function getNumExecutableLines()
{
return $this->numExecutableLines;
}
/**
* Returns the number of executed lines.
*
* @return integer
*/
public function getNumExecutedLines()
{
return $this->numExecutedLines;
}
/**
* Returns the number of classes.
*
* @return integer
*/
public function getNumClasses()
{
return count($this->classes);
}
/**
* Returns the number of tested classes.
*
* @return integer
*/
public function getNumTestedClasses()
{
return $this->numTestedClasses;
}
/**
* Returns the number of traits.
*
* @return integer
*/
public function getNumTraits()
{
return count($this->traits);
}
/**
* Returns the number of tested traits.
*
* @return integer
*/
public function getNumTestedTraits()
{
return $this->numTestedTraits;
}
/**
* Returns the number of methods.
*
* @return integer
*/
public function getNumMethods()
{
if ($this->numMethods === NULL) {
$this->numMethods = 0;
foreach ($this->classes as $class) {
foreach ($class['methods'] as $method) {
if ($method['executableLines'] > 0) {
$this->numMethods++;
}
}
}
foreach ($this->traits as $trait) {
foreach ($trait['methods'] as $method) {
if ($method['executableLines'] > 0) {
$this->numMethods++;
}
}
}
}
return $this->numMethods;
}
/**
* Returns the number of tested methods.
*
* @return integer
*/
public function getNumTestedMethods()
{
if ($this->numTestedMethods === NULL) {
$this->numTestedMethods = 0;
foreach ($this->classes as $class) {
foreach ($class['methods'] as $method) {
if ($method['executableLines'] > 0 &&
$method['coverage'] == 100) {
$this->numTestedMethods++;
}
}
}
foreach ($this->traits as $trait) {
foreach ($trait['methods'] as $method) {
if ($method['executableLines'] > 0 &&
$method['coverage'] == 100) {
$this->numTestedMethods++;
}
}
}
}
return $this->numTestedMethods;
}
/**
* Returns the number of functions.
*
* @return integer
*/
public function getNumFunctions()
{
return count($this->functions);
}
/**
* Returns the number of tested functions.
*
* @return integer
*/
public function getNumTestedFunctions()
{
if ($this->numTestedFunctions === NULL) {
$this->numTestedFunctions = 0;
foreach ($this->functions as $function) {
if ($function['executableLines'] > 0 &&
$function['coverage'] == 100) {
$this->numTestedFunctions++;
}
}
}
return $this->numTestedFunctions;
}
/**
* Calculates coverage statistics for the file.
*/
protected function calculateStatistics()
{
if ($this->cacheTokens) {
$tokens = PHP_Token_Stream_CachingFactory::get($this->getPath());
} else {
$tokens = new PHP_Token_Stream($this->getPath());
}
$this->processClasses($tokens);
$this->processTraits($tokens);
$this->processFunctions($tokens);
$this->linesOfCode = $tokens->getLinesOfCode();
unset($tokens);
for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) {
if (isset($this->startLines[$lineNumber])) {
// Start line of a class.
if (isset($this->startLines[$lineNumber]['className'])) {
$currentClass = &$this->startLines[$lineNumber];
}
// Start line of a trait.
else if (isset($this->startLines[$lineNumber]['traitName'])) {
$currentTrait = &$this->startLines[$lineNumber];
}
// Start line of a method.
else if (isset($this->startLines[$lineNumber]['methodName'])) {
$currentMethod = &$this->startLines[$lineNumber];
}
// Start line of a function.
else if (isset($this->startLines[$lineNumber]['functionName'])) {
$currentFunction = &$this->startLines[$lineNumber];
}
}
if (!isset($this->ignoredLines[$lineNumber]) &&
isset($this->coverageData[$lineNumber]) &&
$this->coverageData[$lineNumber] !== NULL) {
if (isset($currentClass)) {
$currentClass['executableLines']++;
}
if (isset($currentTrait)) {
$currentTrait['executableLines']++;
}
if (isset($currentMethod)) {
$currentMethod['executableLines']++;
}
if (isset($currentFunction)) {
$currentFunction['executableLines']++;
}
$this->numExecutableLines++;
if (count($this->coverageData[$lineNumber]) > 0 ||
isset($this->ignoredLines[$lineNumber])) {
if (isset($currentClass)) {
$currentClass['executedLines']++;
}
if (isset($currentTrait)) {
$currentTrait['executedLines']++;
}
if (isset($currentMethod)) {
$currentMethod['executedLines']++;
}
if (isset($currentFunction)) {
$currentFunction['executedLines']++;
}
$this->numExecutedLines++;
}
}
if (isset($this->endLines[$lineNumber])) {
// End line of a class.
if (isset($this->endLines[$lineNumber]['className'])) {
unset($currentClass);
}
// End line of a trait.
else if (isset($this->endLines[$lineNumber]['traitName'])) {
unset($currentTrait);
}
// End line of a method.
else if (isset($this->endLines[$lineNumber]['methodName'])) {
unset($currentMethod);
}
// End line of a function.
else if (isset($this->endLines[$lineNumber]['functionName'])) {
unset($currentFunction);
}
}
}
foreach ($this->traits as &$trait) {
foreach ($trait['methods'] as &$method) {
if ($method['executableLines'] > 0) {
$method['coverage'] = ($method['executedLines'] /
$method['executableLines']) * 100;
} else {
$method['coverage'] = 100;
}
$method['crap'] = $this->crap(
$method['ccn'], $method['coverage']
);
$trait['ccn'] += $method['ccn'];
}
if ($trait['executableLines'] > 0) {
$trait['coverage'] = ($trait['executedLines'] /
$trait['executableLines']) * 100;
} else {
$trait['coverage'] = 100;
}
if ($trait['coverage'] == 100) {
$this->numTestedClasses++;
}
$trait['crap'] = $this->crap(
$trait['ccn'], $trait['coverage']
);
}
foreach ($this->classes as &$class) {
foreach ($class['methods'] as &$method) {
if ($method['executableLines'] > 0) {
$method['coverage'] = ($method['executedLines'] /
$method['executableLines']) * 100;
} else {
$method['coverage'] = 100;
}
$method['crap'] = $this->crap(
$method['ccn'], $method['coverage']
);
$class['ccn'] += $method['ccn'];
}
if ($class['executableLines'] > 0) {
$class['coverage'] = ($class['executedLines'] /
$class['executableLines']) * 100;
} else {
$class['coverage'] = 100;
}
if ($class['coverage'] == 100) {
$this->numTestedClasses++;
}
$class['crap'] = $this->crap(
$class['ccn'], $class['coverage']
);
}
}
/**
* @param PHP_Token_Stream $tokens
*/
protected function processClasses(PHP_Token_Stream $tokens)
{
$classes = $tokens->getClasses();
unset($tokens);
$link = $this->getId() . '.html#';
foreach ($classes as $className => $class) {
$this->classes[$className] = array(
'className' => $className,
'methods' => array(),
'startLine' => $class['startLine'],
'executableLines' => 0,
'executedLines' => 0,
'ccn' => 0,
'coverage' => 0,
'crap' => 0,
'package' => $class['package'],
'link' => $link . $class['startLine']
);
$this->startLines[$class['startLine']] = &$this->classes[$className];
$this->endLines[$class['endLine']] = &$this->classes[$className];
foreach ($class['methods'] as $methodName => $method) {
$this->classes[$className]['methods'][$methodName] = array(
'methodName' => $methodName,
'signature' => $method['signature'],
'startLine' => $method['startLine'],
'endLine' => $method['endLine'],
'executableLines' => 0,
'executedLines' => 0,
'ccn' => $method['ccn'],
'coverage' => 0,
'crap' => 0,
'link' => $link . $method['startLine']
);
$this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName];
$this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName];
}
}
}
/**
* @param PHP_Token_Stream $tokens
*/
protected function processTraits(PHP_Token_Stream $tokens)
{
$traits = $tokens->getTraits();
unset($tokens);
$link = $this->getId() . '.html#';
foreach ($traits as $traitName => $trait) {
$this->traits[$traitName] = array(
'traitName' => $traitName,
'methods' => array(),
'startLine' => $trait['startLine'],
'executableLines' => 0,
'executedLines' => 0,
'ccn' => 0,
'coverage' => 0,
'crap' => 0,
'package' => $trait['package'],
'link' => $link . $trait['startLine']
);
$this->startLines[$trait['startLine']] = &$this->traits[$traitName];
$this->endLines[$trait['endLine']] = &$this->traits[$traitName];
foreach ($trait['methods'] as $methodName => $method) {
$this->traits[$traitName]['methods'][$methodName] = array(
'methodName' => $methodName,
'signature' => $method['signature'],
'startLine' => $method['startLine'],
'endLine' => $method['endLine'],
'executableLines' => 0,
'executedLines' => 0,
'ccn' => $method['ccn'],
'coverage' => 0,
'crap' => 0,
'link' => $link . $method['startLine']
);
$this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName];
$this->endLines[$method['endLine']] = &$this->traits[$traitName]['methods'][$methodName];
}
}
}
/**
* @param PHP_Token_Stream $tokens
*/
protected function processFunctions(PHP_Token_Stream $tokens)
{
$functions = $tokens->getFunctions();
unset($tokens);
$link = $this->getId() . '.html#';
foreach ($functions as $functionName => $function) {
$this->functions[$functionName] = array(
'functionName' => $functionName,
'signature' => $function['signature'],
'startLine' => $function['startLine'],
'executableLines' => 0,
'executedLines' => 0,
'ccn' => $function['ccn'],
'coverage' => 0,
'crap' => 0,
'link' => $link . $function['startLine']
);
$this->startLines[$function['startLine']] = &$this->functions[$functionName];
$this->endLines[$function['endLine']] = &$this->functions[$functionName];
}
}
/**
* Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code
* based on its cyclomatic complexity and percentage of code coverage.
*
* @param integer $ccn
* @param float $coverage
* @return string
* @since Method available since Release 1.2.0
*/
protected function crap($ccn, $coverage)
{
if ($coverage == 0) {
return (string)pow($ccn, 2) + $ccn;
}
if ($coverage >= 95) {
return (string)$ccn;
}
return sprintf(
'%01.2F', pow($ccn, 2) * pow(1 - $coverage/100, 3) + $ccn
);
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Recursive iterator for PHP_CodeCoverage_Report_Node object graphs.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_Node_Iterator implements RecursiveIterator
{
/**
* @var integer
*/
protected $position;
/**
* @var PHP_CodeCoverage_Report_Node[]
*/
protected $nodes;
/**
* Constructor.
*
* @param PHP_CodeCoverage_Report_Node_Directory $node
*/
public function __construct(PHP_CodeCoverage_Report_Node_Directory $node)
{
$this->nodes = $node->getChildNodes();
}
/**
* Rewinds the Iterator to the first element.
*
*/
public function rewind()
{
$this->position = 0;
}
/**
* Checks if there is a current element after calls to rewind() or next().
*
* @return boolean
*/
public function valid()
{
return $this->position < count($this->nodes);
}
/**
* Returns the key of the current element.
*
* @return integer
*/
public function key()
{
return $this->position;
}
/**
* Returns the current element.
*
* @return PHPUnit_Framework_Test
*/
public function current()
{
return $this->valid() ? $this->nodes[$this->position] : NULL;
}
/**
* Moves forward to next element.
*
*/
public function next()
{
$this->position++;
}
/**
* Returns the sub iterator for the current element.
*
* @return PHP_CodeCoverage_Report_Node_Iterator
*/
public function getChildren()
{
return new PHP_CodeCoverage_Report_Node_Iterator(
$this->nodes[$this->position]
);
}
/**
* Checks whether the current element has children.
*
* @return boolean
*/
public function hasChildren()
{
return $this->nodes[$this->position] instanceof PHP_CodeCoverage_Report_Node_Directory;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Uses serialize() to write a PHP_CodeCoverage object to a file.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_PHP
{
/**
* @param PHP_CodeCoverage $coverage
* @param string $target
* @return string
*/
public function process(PHP_CodeCoverage $coverage, $target = NULL)
{
$coverage = serialize($coverage);
if ($target !== NULL) {
return file_put_contents($target, $coverage);
} else {
return $coverage;
}
}
}

View File

@@ -0,0 +1,278 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.1.0
*/
/**
* Generates human readable output from an PHP_CodeCoverage object.
*
* The output gets put into a text file our written to the CLI.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.1.0
*/
class PHP_CodeCoverage_Report_Text
{
protected $outputStream;
protected $lowUpperBound;
protected $highLowerBound;
protected $showUncoveredFiles;
protected $colors = array(
'green' => "\x1b[30;42m",
'yellow' => "\x1b[30;43m",
'red' => "\x1b[37;41m",
'header' => "\x1b[47;40m",
'reset' => "\x1b[0m",
'eol' => "\x1b[2K",
);
public function __construct(PHPUnit_Util_Printer $outputStream, $lowUpperBound, $highLowerBound, $showUncoveredFiles)
{
$this->outputStream = $outputStream;
$this->lowUpperBound = $lowUpperBound;
$this->highLowerBound = $highLowerBound;
$this->showUncoveredFiles = $showUncoveredFiles;
}
/**
* @param PHP_CodeCoverage $coverage
* @param string $target
* @param string $name
* @return string
*/
public function process(PHP_CodeCoverage $coverage, $showColors = FALSE)
{
$output = '';
$report = $coverage->getReport();
unset($coverage);
$colors = array(
'header' => '',
'classes' => '',
'methods' => '',
'lines' => '',
'reset' => '',
'eol' => ''
);
if ($showColors) {
$colors['classes'] = $this->getCoverageColor(
$report->getNumTestedClassesAndTraits(),
$report->getNumClassesAndTraits()
);
$colors['methods'] = $this->getCoverageColor(
$report->getNumTestedMethods(),
$report->getNumMethods()
);
$colors['lines'] = $this->getCoverageColor(
$report->getNumExecutedLines(),
$report->getNumExecutableLines()
);
$colors['reset'] = $this->colors['reset'];
$colors['header'] = $this->colors['header'];
$colors['eol'] = $this->colors['eol'];
}
$output .= PHP_EOL . PHP_EOL .
$colors['header'] . 'Code Coverage Report ';
$output .= PHP_EOL .
date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) .
PHP_EOL;
$output .= PHP_EOL . ' Summary: ' . PHP_EOL . $colors['reset']
. $colors['classes'] . $colors['eol'] . ' Classes: ' . PHP_CodeCoverage_Util::percent($report->getNumTestedClassesAndTraits(), $report->getNumClassesAndTraits(), TRUE)
. ' (' . $report->getNumTestedClassesAndTraits() . '/' . $report->getNumClassesAndTraits() . ')' . PHP_EOL . $colors ['eol']
. $colors['methods'] . $colors['eol'] . ' Methods: ' . PHP_CodeCoverage_Util::percent($report->getNumTestedMethods(), $report->getNumMethods(), TRUE)
. ' (' . $report->getNumTestedMethods() . '/' . $report->getNumMethods() . ')' . PHP_EOL . $colors ['eol']
. $colors['lines'] . $colors['eol'] . ' Lines: ' . PHP_CodeCoverage_Util::percent($report->getNumExecutedLines(), $report->getNumExecutableLines(), TRUE)
. ' (' . $report->getNumExecutedLines() . '/' . $report->getNumExecutableLines() . ')' . PHP_EOL . $colors['reset'] . $colors ['eol'];
$classCoverage = array();
foreach ($report as $item) {
if (!$item instanceof PHP_CodeCoverage_Report_Node_File) {
continue;
}
$classes = $item->getClassesAndTraits();
$coverage = $item->getCoverageData();
$lines = array();
$ignoredLines = $item->getIgnoredLines();
foreach ($classes as $className => $class) {
$classStatements = 0;
$coveredClassStatements = 0;
$coveredMethods = 0;
foreach ($class['methods'] as $method) {
$methodCount = 0;
$methodLines = 0;
$methodLinesCovered = 0;
for ($i = $method['startLine'];
$i <= $method['endLine'];
$i++) {
if (isset($ignoredLines[$i])) {
continue;
}
$add = TRUE;
$count = 0;
if (isset($coverage[$i])) {
if ($coverage[$i] !== NULL) {
$classStatements++;
$methodLines++;
} else {
$add = FALSE;
}
$count = count($coverage[$i]);
if ($count > 0) {
$coveredClassStatements++;
$methodLinesCovered++;
}
} else {
$add = FALSE;
}
$methodCount = max($methodCount, $count);
if ($add) {
$lines[$i] = array(
'count' => $count, 'type' => 'stmt'
);
}
}
if ($methodCount > 0) {
$coveredMethods++;
}
}
if (!empty($class['package']['namespace'])) {
$namespace = '\\' . $class['package']['namespace'] . '::';
}
else if (!empty($class['package']['fullPackage'])) {
$namespace = '@' . $class['package']['fullPackage'] . '::';
}
else {
$namespace = '';
}
$classCoverage[$namespace . $className] = array(
'namespace' => $namespace,
'className ' => $className,
'methodsCovered' => $coveredMethods,
'methodCount' => count($class['methods']),
'statementsCovered' => $coveredClassStatements,
'statementCount' => $classStatements,
);
}
}
ksort($classCoverage);
$methodColor = '';
$linesColor = '';
$resetColor = '';
foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
if ($classInfo['statementsCovered'] != 0 ||
$this->showUncoveredFiles) {
if ($showColors) {
$methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
$linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
$resetColor = $colors['reset'];
}
$output .= PHP_EOL . $fullQualifiedPath . PHP_EOL
. ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' '
. ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor
;
}
}
$this->outputStream->write($output . PHP_EOL);
}
protected function getCoverageColor($numberOfCoveredElements, $totalNumberOfElements)
{
$coverage = PHP_CodeCoverage_Util::percent(
$numberOfCoveredElements, $totalNumberOfElements
);
if ($coverage > $this->highLowerBound) {
return $this->colors['green'];
}
else if ($coverage > $this->lowUpperBound) {
return $this->colors['yellow'];
}
return $this->colors['red'];
}
protected function printCoverageCounts($numberOfCoveredElements, $totalNumberOfElements, $presicion)
{
$format = '%' . $presicion . 's';
return PHP_CodeCoverage_Util::percent(
$numberOfCoveredElements, $totalNumberOfElements, TRUE, TRUE
) .
' (' . sprintf($format, $numberOfCoveredElements) . '/' .
sprintf($format, $totalNumberOfElements) . ')';
}
}

View File

@@ -0,0 +1,265 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.0.0
*/
/**
* Utility methods.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.0.0
*/
class PHP_CodeCoverage_Util
{
/**
* @var array
*/
protected static $ignoredLines = array();
/**
* @var array
*/
protected static $ids = array();
/**
* Returns the lines of a source file that should be ignored.
*
* @param string $filename
* @param boolean $cacheTokens
* @return array
* @throws PHP_CodeCoverage_Exception
*/
public static function getLinesToBeIgnored($filename, $cacheTokens = TRUE)
{
if (!is_string($filename)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
1, 'string'
);
}
if (!is_bool($cacheTokens)) {
throw PHP_CodeCoverage_Util_InvalidArgumentHelper::factory(
2, 'boolean'
);
}
if (!isset(self::$ignoredLines[$filename])) {
self::$ignoredLines[$filename] = array();
$ignore = FALSE;
$stop = FALSE;
$lines = file($filename);
foreach ($lines as $index => $line) {
if (!trim($line)) {
self::$ignoredLines[$filename][$index+1] = TRUE;
}
}
if ($cacheTokens) {
$tokens = PHP_Token_Stream_CachingFactory::get($filename);
} else {
$tokens = new PHP_Token_Stream($filename);
}
$classes = array_merge($tokens->getClasses(), $tokens->getTraits());
$tokens = $tokens->tokens();
foreach ($tokens as $token) {
switch (get_class($token)) {
case 'PHP_Token_COMMENT':
case 'PHP_Token_DOC_COMMENT': {
$_token = trim($token);
$_line = trim($lines[$token->getLine() - 1]);
if ($_token == '// @codeCoverageIgnore' ||
$_token == '//@codeCoverageIgnore') {
$ignore = TRUE;
$stop = TRUE;
}
else if ($_token == '// @codeCoverageIgnoreStart' ||
$_token == '//@codeCoverageIgnoreStart') {
$ignore = TRUE;
}
else if ($_token == '// @codeCoverageIgnoreEnd' ||
$_token == '//@codeCoverageIgnoreEnd') {
$stop = TRUE;
}
// be sure the comment doesn't have some token BEFORE it on the same line...
// it would not be safe to ignore the whole line in those cases.
if (0 === strpos($_token, $_line)) {
$count = substr_count($token, "\n");
$line = $token->getLine();
for ($i = $line; $i < $line + $count; $i++) {
self::$ignoredLines[$filename][$i] = TRUE;
}
if ($token instanceof PHP_Token_DOC_COMMENT) {
// Workaround for the fact the DOC_COMMENT token
// does not include the final \n character in its
// text.
if (substr(trim($lines[$i-1]), -2) == '*/') {
self::$ignoredLines[$filename][$i] = TRUE;
}
}
}
}
break;
case 'PHP_Token_INTERFACE':
case 'PHP_Token_TRAIT':
case 'PHP_Token_CLASS':
case 'PHP_Token_FUNCTION': {
$docblock = $token->getDocblock();
if (strpos($docblock, '@codeCoverageIgnore')) {
$endLine = $token->getEndLine();
for ($i = $token->getLine(); $i <= $endLine; $i++) {
self::$ignoredLines[$filename][$i] = TRUE;
}
}
else if ($token instanceof PHP_Token_INTERFACE ||
$token instanceof PHP_Token_TRAIT ||
$token instanceof PHP_Token_CLASS) {
if (empty($classes[$token->getName()]['methods'])) {
for ($i = $token->getLine();
$i <= $token->getEndLine();
$i++) {
self::$ignoredLines[$filename][$i] = TRUE;
}
} else {
$firstMethod = array_shift(
$classes[$token->getName()]['methods']
);
do {
$lastMethod = array_pop(
$classes[$token->getName()]['methods']
);
} while ($lastMethod !== NULL && substr($lastMethod['signature'], 0, 18) == 'anonymous function');
if ($lastMethod === NULL) {
$lastMethod = $firstMethod;
}
for ($i = $token->getLine();
$i < $firstMethod['startLine'];
$i++) {
self::$ignoredLines[$filename][$i] = TRUE;
}
for ($i = $token->getEndLine();
$i > $lastMethod['endLine'];
$i--) {
self::$ignoredLines[$filename][$i] = TRUE;
}
}
}
}
break;
case 'PHP_Token_NAMESPACE': {
self::$ignoredLines[$filename][$token->getEndLine()] = TRUE;
} // Intentional fallthrough
case 'PHP_Token_OPEN_TAG':
case 'PHP_Token_CLOSE_TAG':
case 'PHP_Token_USE': {
self::$ignoredLines[$filename][$token->getLine()] = TRUE;
}
break;
}
if ($ignore) {
self::$ignoredLines[$filename][$token->getLine()] = TRUE;
if ($stop) {
$ignore = FALSE;
$stop = FALSE;
}
}
}
}
return self::$ignoredLines[$filename];
}
/**
* @param float $a
* @param float $b
* @return float ($a / $b) * 100
*/
public static function percent($a, $b, $asString = FALSE, $fixedWidth = FALSE)
{
if ($asString && $b == 0) {
return '';
}
if ($b > 0) {
$percent = ($a / $b) * 100;
} else {
$percent = 100;
}
if ($asString) {
if ($fixedWidth) {
return sprintf('%6.2F%%', $percent);
}
return sprintf('%01.2F%%', $percent);
} else {
return $percent;
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.2.0
*/
/**
* Factory for PHP_CodeCoverage_Exception objects that are used to describe
* invalid arguments passed to a function or method.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.2.0
*/
class PHP_CodeCoverage_Util_InvalidArgumentHelper
{
/**
* @param integer $argument
* @param string $type
* @param mixed $value
*/
public static function factory($argument, $type, $value = NULL)
{
$stack = debug_backtrace(FALSE);
return new PHP_CodeCoverage_Exception(
sprintf(
'Argument #%d%sof %s::%s() must be a %s',
$argument,
$value !== NULL ? ' (' . $value . ')' : ' ',
$stack[1]['class'],
$stack[1]['function'],
$type
)
);
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* PHP_CodeCoverage
*
* Copyright (c) 2009-2014, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since File available since Release 1.2.1
*/
/**
*
*
* @category PHP
* @package CodeCoverage
* @author Sebastian Bergmann <sebastian@phpunit.de>
* @copyright 2009-2014 Sebastian Bergmann <sebastian@phpunit.de>
* @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
* @link http://github.com/sebastianbergmann/php-code-coverage
* @since Class available since Release 1.2.1
*/
class PHP_CodeCoverage_Version
{
const VERSION = '1.2.18';
protected static $version;
/**
* Returns the version of PHP_CodeCoverage.
*
* @return string
*/
public static function id()
{
if (self::$version === NULL) {
self::$version = self::VERSION;
if (is_dir(dirname(dirname(__DIR__)) . '/.git')) {
$dir = getcwd();
chdir(__DIR__);
$version = @exec('git describe --tags');
chdir($dir);
if ($version) {
if (count(explode('.', self::VERSION)) == 3) {
self::$version = $version;
} else {
$version = explode('-', $version);
self::$version = self::VERSION . '-' . $version[2];
}
}
}
}
return self::$version;
}
}