upgraded dependencies
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,17 +9,25 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use function sprintf;
|
||||
use SebastianBergmann\CodeCoverage\BranchAndPathCoverageNotSupportedException;
|
||||
use SebastianBergmann\CodeCoverage\DeadCodeDetectionNotSupportedException;
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException;
|
||||
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
|
||||
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;
|
||||
|
||||
/**
|
||||
* Interface for code coverage drivers.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
interface Driver
|
||||
abstract class Driver
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @see http://xdebug.org/docs/code_coverage
|
||||
*/
|
||||
public const LINE_EXECUTED = 1;
|
||||
public const LINE_NOT_EXECUTABLE = -2;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
@@ -33,15 +41,127 @@ interface Driver
|
||||
*
|
||||
* @see http://xdebug.org/docs/code_coverage
|
||||
*/
|
||||
public const LINE_NOT_EXECUTABLE = -2;
|
||||
public const LINE_EXECUTED = 1;
|
||||
|
||||
/**
|
||||
* Start collection of code coverage information.
|
||||
* @var int
|
||||
*
|
||||
* @see http://xdebug.org/docs/code_coverage
|
||||
*/
|
||||
public function start(bool $determineUnusedAndDead = true): void;
|
||||
public const BRANCH_NOT_HIT = 0;
|
||||
|
||||
/**
|
||||
* Stop collection of code coverage information.
|
||||
* @var int
|
||||
*
|
||||
* @see http://xdebug.org/docs/code_coverage
|
||||
*/
|
||||
public function stop(): array;
|
||||
public const BRANCH_HIT = 1;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $collectBranchAndPathCoverage = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $detectDeadCode = false;
|
||||
|
||||
/**
|
||||
* @throws NoCodeCoverageDriverAvailableException
|
||||
* @throws PcovNotAvailableException
|
||||
* @throws PhpdbgNotAvailableException
|
||||
* @throws Xdebug2NotEnabledException
|
||||
* @throws Xdebug3NotEnabledException
|
||||
* @throws XdebugNotAvailableException
|
||||
*
|
||||
* @deprecated Use DriverSelector::forLineCoverage() instead
|
||||
*/
|
||||
public static function forLineCoverage(Filter $filter): self
|
||||
{
|
||||
return (new Selector)->forLineCoverage($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException
|
||||
* @throws Xdebug2NotEnabledException
|
||||
* @throws Xdebug3NotEnabledException
|
||||
* @throws XdebugNotAvailableException
|
||||
*
|
||||
* @deprecated Use DriverSelector::forLineAndPathCoverage() instead
|
||||
*/
|
||||
public static function forLineAndPathCoverage(Filter $filter): self
|
||||
{
|
||||
return (new Selector)->forLineAndPathCoverage($filter);
|
||||
}
|
||||
|
||||
public function canCollectBranchAndPathCoverage(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function collectsBranchAndPathCoverage(): bool
|
||||
{
|
||||
return $this->collectBranchAndPathCoverage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws BranchAndPathCoverageNotSupportedException
|
||||
*/
|
||||
public function enableBranchAndPathCoverage(): void
|
||||
{
|
||||
if (!$this->canCollectBranchAndPathCoverage()) {
|
||||
throw new BranchAndPathCoverageNotSupportedException(
|
||||
sprintf(
|
||||
'%s does not support branch and path coverage',
|
||||
$this->nameAndVersion()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->collectBranchAndPathCoverage = true;
|
||||
}
|
||||
|
||||
public function disableBranchAndPathCoverage(): void
|
||||
{
|
||||
$this->collectBranchAndPathCoverage = false;
|
||||
}
|
||||
|
||||
public function canDetectDeadCode(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function detectsDeadCode(): bool
|
||||
{
|
||||
return $this->detectDeadCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DeadCodeDetectionNotSupportedException
|
||||
*/
|
||||
public function enableDeadCodeDetection(): void
|
||||
{
|
||||
if (!$this->canDetectDeadCode()) {
|
||||
throw new DeadCodeDetectionNotSupportedException(
|
||||
sprintf(
|
||||
'%s does not support dead code detection',
|
||||
$this->nameAndVersion()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->detectDeadCode = true;
|
||||
}
|
||||
|
||||
public function disableDeadCodeDetection(): void
|
||||
{
|
||||
$this->detectDeadCode = false;
|
||||
}
|
||||
|
||||
abstract public function nameAndVersion(): string;
|
||||
|
||||
abstract public function start(): void;
|
||||
|
||||
abstract public function stop(): RawCodeCoverageData;
|
||||
}
|
||||
|
@@ -1,45 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
/**
|
||||
* Driver for PCOV code coverage functionality.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class PCOV implements Driver
|
||||
{
|
||||
/**
|
||||
* Start collection of code coverage information.
|
||||
*/
|
||||
public function start(bool $determineUnusedAndDead = true): void
|
||||
{
|
||||
\pcov\start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop collection of code coverage information.
|
||||
*/
|
||||
public function stop(): array
|
||||
{
|
||||
\pcov\stop();
|
||||
|
||||
$waiting = \pcov\waiting();
|
||||
$collect = [];
|
||||
|
||||
if ($waiting) {
|
||||
$collect = \pcov\collect(\pcov\inclusive, $waiting);
|
||||
|
||||
\pcov\clear();
|
||||
}
|
||||
|
||||
return $collect;
|
||||
}
|
||||
}
|
75
vendor/phpunit/php-code-coverage/src/Driver/PcovDriver.php
vendored
Normal file
75
vendor/phpunit/php-code-coverage/src/Driver/PcovDriver.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use const pcov\inclusive;
|
||||
use function array_intersect;
|
||||
use function extension_loaded;
|
||||
use function pcov\clear;
|
||||
use function pcov\collect;
|
||||
use function pcov\start;
|
||||
use function pcov\stop;
|
||||
use function pcov\waiting;
|
||||
use function phpversion;
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class PcovDriver extends Driver
|
||||
{
|
||||
/**
|
||||
* @var Filter
|
||||
*/
|
||||
private $filter;
|
||||
|
||||
/**
|
||||
* @throws PcovNotAvailableException
|
||||
*/
|
||||
public function __construct(Filter $filter)
|
||||
{
|
||||
if (!extension_loaded('pcov')) {
|
||||
throw new PcovNotAvailableException;
|
||||
}
|
||||
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
public function stop(): RawCodeCoverageData
|
||||
{
|
||||
stop();
|
||||
|
||||
$filesToCollectCoverageFor = waiting();
|
||||
$collected = [];
|
||||
|
||||
if ($filesToCollectCoverageFor) {
|
||||
if (!$this->filter->isEmpty()) {
|
||||
$filesToCollectCoverageFor = array_intersect($filesToCollectCoverageFor, $this->filter->files());
|
||||
}
|
||||
|
||||
$collected = collect(inclusive, $filesToCollectCoverageFor);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
return RawCodeCoverageData::fromXdebugWithoutPathCoverage($collected);
|
||||
}
|
||||
|
||||
public function nameAndVersion(): string
|
||||
{
|
||||
return 'PCOV ' . phpversion('pcov');
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,54 +9,47 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
use const PHP_SAPI;
|
||||
use const PHP_VERSION;
|
||||
use function array_diff;
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function get_included_files;
|
||||
use function phpdbg_end_oplog;
|
||||
use function phpdbg_get_executable;
|
||||
use function phpdbg_start_oplog;
|
||||
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;
|
||||
|
||||
/**
|
||||
* Driver for PHPDBG's code coverage functionality.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class PHPDBG implements Driver
|
||||
final class PhpdbgDriver extends Driver
|
||||
{
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws PhpdbgNotAvailableException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (\PHP_SAPI !== 'phpdbg') {
|
||||
throw new RuntimeException(
|
||||
'This driver requires the PHPDBG SAPI'
|
||||
);
|
||||
}
|
||||
|
||||
if (!\function_exists('phpdbg_start_oplog')) {
|
||||
throw new RuntimeException(
|
||||
'This build of PHPDBG does not support code coverage'
|
||||
);
|
||||
if (PHP_SAPI !== 'phpdbg') {
|
||||
throw new PhpdbgNotAvailableException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start collection of code coverage information.
|
||||
*/
|
||||
public function start(bool $determineUnusedAndDead = true): void
|
||||
public function start(): void
|
||||
{
|
||||
\phpdbg_start_oplog();
|
||||
phpdbg_start_oplog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop collection of code coverage information.
|
||||
*/
|
||||
public function stop(): array
|
||||
public function stop(): RawCodeCoverageData
|
||||
{
|
||||
static $fetchedLines = [];
|
||||
|
||||
$dbgData = \phpdbg_end_oplog();
|
||||
$dbgData = phpdbg_end_oplog();
|
||||
|
||||
if ($fetchedLines == []) {
|
||||
$sourceLines = \phpdbg_get_executable();
|
||||
if ($fetchedLines === []) {
|
||||
$sourceLines = phpdbg_get_executable();
|
||||
} else {
|
||||
$newFiles = \array_diff(\get_included_files(), \array_keys($fetchedLines));
|
||||
$newFiles = array_diff(get_included_files(), array_keys($fetchedLines));
|
||||
|
||||
$sourceLines = [];
|
||||
|
||||
@@ -71,14 +64,18 @@ final class PHPDBG implements Driver
|
||||
}
|
||||
}
|
||||
|
||||
$fetchedLines = \array_merge($fetchedLines, $sourceLines);
|
||||
$fetchedLines = array_merge($fetchedLines, $sourceLines);
|
||||
|
||||
return $this->detectExecutedLines($fetchedLines, $dbgData);
|
||||
return RawCodeCoverageData::fromXdebugWithoutPathCoverage(
|
||||
$this->detectExecutedLines($fetchedLines, $dbgData)
|
||||
);
|
||||
}
|
||||
|
||||
public function nameAndVersion(): string
|
||||
{
|
||||
return 'PHPDBG ' . PHP_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert phpdbg based data into the format CodeCoverage expects
|
||||
*/
|
||||
private function detectExecutedLines(array $sourceLines, array $dbgData): array
|
||||
{
|
||||
foreach ($dbgData as $file => $coveredLines) {
|
79
vendor/phpunit/php-code-coverage/src/Driver/Selector.php
vendored
Normal file
79
vendor/phpunit/php-code-coverage/src/Driver/Selector.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use function phpversion;
|
||||
use function version_compare;
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException;
|
||||
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
|
||||
final class Selector
|
||||
{
|
||||
/**
|
||||
* @throws NoCodeCoverageDriverAvailableException
|
||||
* @throws PcovNotAvailableException
|
||||
* @throws PhpdbgNotAvailableException
|
||||
* @throws Xdebug2NotEnabledException
|
||||
* @throws Xdebug3NotEnabledException
|
||||
* @throws XdebugNotAvailableException
|
||||
*/
|
||||
public function forLineCoverage(Filter $filter): Driver
|
||||
{
|
||||
$runtime = new Runtime;
|
||||
|
||||
if ($runtime->hasPHPDBGCodeCoverage()) {
|
||||
return new PhpdbgDriver;
|
||||
}
|
||||
|
||||
if ($runtime->hasPCOV()) {
|
||||
return new PcovDriver($filter);
|
||||
}
|
||||
|
||||
if ($runtime->hasXdebug()) {
|
||||
if (version_compare(phpversion('xdebug'), '3', '>=')) {
|
||||
$driver = new Xdebug3Driver($filter);
|
||||
} else {
|
||||
$driver = new Xdebug2Driver($filter);
|
||||
}
|
||||
|
||||
$driver->enableDeadCodeDetection();
|
||||
|
||||
return $driver;
|
||||
}
|
||||
|
||||
throw new NoCodeCoverageDriverAvailableException;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NoCodeCoverageDriverWithPathCoverageSupportAvailableException
|
||||
* @throws Xdebug2NotEnabledException
|
||||
* @throws Xdebug3NotEnabledException
|
||||
* @throws XdebugNotAvailableException
|
||||
*/
|
||||
public function forLineAndPathCoverage(Filter $filter): Driver
|
||||
{
|
||||
if ((new Runtime)->hasXdebug()) {
|
||||
if (version_compare(phpversion('xdebug'), '3', '>=')) {
|
||||
$driver = new Xdebug3Driver($filter);
|
||||
} else {
|
||||
$driver = new Xdebug2Driver($filter);
|
||||
}
|
||||
|
||||
$driver->enableDeadCodeDetection();
|
||||
$driver->enableBranchAndPathCoverage();
|
||||
|
||||
return $driver;
|
||||
}
|
||||
|
||||
throw new NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
|
||||
}
|
||||
}
|
@@ -1,123 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
|
||||
/**
|
||||
* Driver for Xdebug's code coverage functionality.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
final class Xdebug implements Driver
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $cacheNumLines = [];
|
||||
|
||||
/**
|
||||
* @var Filter
|
||||
*/
|
||||
private $filter;
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __construct(Filter $filter = null)
|
||||
{
|
||||
if (!\extension_loaded('xdebug')) {
|
||||
throw new RuntimeException('This driver requires Xdebug');
|
||||
}
|
||||
|
||||
if (\version_compare(\phpversion('xdebug'), '3', '>=')) {
|
||||
$mode = \getenv('XDEBUG_MODE');
|
||||
|
||||
if ($mode === false) {
|
||||
$mode = \ini_get('xdebug.mode');
|
||||
}
|
||||
|
||||
if ($mode === false ||
|
||||
!\in_array('coverage', \explode(',', $mode), true)) {
|
||||
throw new RuntimeException('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set');
|
||||
}
|
||||
} elseif (!\ini_get('xdebug.coverage_enable')) {
|
||||
throw new RuntimeException('xdebug.coverage_enable=On has to be set');
|
||||
}
|
||||
|
||||
if ($filter === null) {
|
||||
$filter = new Filter;
|
||||
}
|
||||
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start collection of code coverage information.
|
||||
*/
|
||||
public function start(bool $determineUnusedAndDead = true): void
|
||||
{
|
||||
if ($determineUnusedAndDead) {
|
||||
\xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
|
||||
} else {
|
||||
\xdebug_start_code_coverage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop collection of code coverage information.
|
||||
*/
|
||||
public function stop(): array
|
||||
{
|
||||
$data = \xdebug_get_code_coverage();
|
||||
|
||||
\xdebug_stop_code_coverage();
|
||||
|
||||
return $this->cleanup($data);
|
||||
}
|
||||
|
||||
private function cleanup(array $data): array
|
||||
{
|
||||
foreach (\array_keys($data) as $file) {
|
||||
unset($data[$file][0]);
|
||||
|
||||
if (!$this->filter->isFile($file)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$numLines = $this->getNumberOfLinesInFile($file);
|
||||
|
||||
foreach (\array_keys($data[$file]) as $line) {
|
||||
if ($line > $numLines) {
|
||||
unset($data[$file][$line]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getNumberOfLinesInFile(string $fileName): int
|
||||
{
|
||||
if (!isset($this->cacheNumLines[$fileName])) {
|
||||
$buffer = \file_get_contents($fileName);
|
||||
$lines = \substr_count($buffer, "\n");
|
||||
|
||||
if (\substr($buffer, -1) !== "\n") {
|
||||
$lines++;
|
||||
}
|
||||
|
||||
$this->cacheNumLines[$fileName] = $lines;
|
||||
}
|
||||
|
||||
return $this->cacheNumLines[$fileName];
|
||||
}
|
||||
}
|
128
vendor/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php
vendored
Normal file
128
vendor/phpunit/php-code-coverage/src/Driver/Xdebug2Driver.php
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use const XDEBUG_CC_BRANCH_CHECK;
|
||||
use const XDEBUG_CC_DEAD_CODE;
|
||||
use const XDEBUG_CC_UNUSED;
|
||||
use const XDEBUG_FILTER_CODE_COVERAGE;
|
||||
use const XDEBUG_PATH_INCLUDE;
|
||||
use const XDEBUG_PATH_WHITELIST;
|
||||
use function defined;
|
||||
use function extension_loaded;
|
||||
use function ini_get;
|
||||
use function phpversion;
|
||||
use function sprintf;
|
||||
use function version_compare;
|
||||
use function xdebug_get_code_coverage;
|
||||
use function xdebug_set_filter;
|
||||
use function xdebug_start_code_coverage;
|
||||
use function xdebug_stop_code_coverage;
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Xdebug2Driver extends Driver
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $pathCoverageIsMixedCoverage;
|
||||
|
||||
/**
|
||||
* @throws WrongXdebugVersionException
|
||||
* @throws Xdebug2NotEnabledException
|
||||
* @throws XdebugNotAvailableException
|
||||
*/
|
||||
public function __construct(Filter $filter)
|
||||
{
|
||||
if (!extension_loaded('xdebug')) {
|
||||
throw new XdebugNotAvailableException;
|
||||
}
|
||||
|
||||
if (version_compare(phpversion('xdebug'), '3', '>=')) {
|
||||
throw new WrongXdebugVersionException(
|
||||
sprintf(
|
||||
'This driver requires Xdebug 2 but version %s is loaded',
|
||||
phpversion('xdebug')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!ini_get('xdebug.coverage_enable')) {
|
||||
throw new Xdebug2NotEnabledException;
|
||||
}
|
||||
|
||||
if (!$filter->isEmpty()) {
|
||||
if (defined('XDEBUG_PATH_WHITELIST')) {
|
||||
$listType = XDEBUG_PATH_WHITELIST;
|
||||
} else {
|
||||
$listType = XDEBUG_PATH_INCLUDE;
|
||||
}
|
||||
|
||||
xdebug_set_filter(
|
||||
XDEBUG_FILTER_CODE_COVERAGE,
|
||||
$listType,
|
||||
$filter->files()
|
||||
);
|
||||
}
|
||||
|
||||
$this->pathCoverageIsMixedCoverage = version_compare(phpversion('xdebug'), '2.9.6', '<');
|
||||
}
|
||||
|
||||
public function canCollectBranchAndPathCoverage(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canDetectDeadCode(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$flags = XDEBUG_CC_UNUSED;
|
||||
|
||||
if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) {
|
||||
$flags |= XDEBUG_CC_DEAD_CODE;
|
||||
}
|
||||
|
||||
if ($this->collectsBranchAndPathCoverage()) {
|
||||
$flags |= XDEBUG_CC_BRANCH_CHECK;
|
||||
}
|
||||
|
||||
xdebug_start_code_coverage($flags);
|
||||
}
|
||||
|
||||
public function stop(): RawCodeCoverageData
|
||||
{
|
||||
$data = xdebug_get_code_coverage();
|
||||
|
||||
xdebug_stop_code_coverage();
|
||||
|
||||
if ($this->collectsBranchAndPathCoverage()) {
|
||||
if ($this->pathCoverageIsMixedCoverage) {
|
||||
return RawCodeCoverageData::fromXdebugWithMixedCoverage($data);
|
||||
}
|
||||
|
||||
return RawCodeCoverageData::fromXdebugWithPathCoverage($data);
|
||||
}
|
||||
|
||||
return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data);
|
||||
}
|
||||
|
||||
public function nameAndVersion(): string
|
||||
{
|
||||
return 'Xdebug ' . phpversion('xdebug');
|
||||
}
|
||||
}
|
119
vendor/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php
vendored
Normal file
119
vendor/phpunit/php-code-coverage/src/Driver/Xdebug3Driver.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use const XDEBUG_CC_BRANCH_CHECK;
|
||||
use const XDEBUG_CC_DEAD_CODE;
|
||||
use const XDEBUG_CC_UNUSED;
|
||||
use const XDEBUG_FILTER_CODE_COVERAGE;
|
||||
use const XDEBUG_PATH_INCLUDE;
|
||||
use function explode;
|
||||
use function extension_loaded;
|
||||
use function getenv;
|
||||
use function in_array;
|
||||
use function ini_get;
|
||||
use function phpversion;
|
||||
use function sprintf;
|
||||
use function version_compare;
|
||||
use function xdebug_get_code_coverage;
|
||||
use function xdebug_set_filter;
|
||||
use function xdebug_start_code_coverage;
|
||||
use function xdebug_stop_code_coverage;
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\RawCodeCoverageData;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Xdebug3Driver extends Driver
|
||||
{
|
||||
/**
|
||||
* @throws WrongXdebugVersionException
|
||||
* @throws Xdebug3NotEnabledException
|
||||
* @throws XdebugNotAvailableException
|
||||
*/
|
||||
public function __construct(Filter $filter)
|
||||
{
|
||||
if (!extension_loaded('xdebug')) {
|
||||
throw new XdebugNotAvailableException;
|
||||
}
|
||||
|
||||
if (version_compare(phpversion('xdebug'), '3', '<')) {
|
||||
throw new WrongXdebugVersionException(
|
||||
sprintf(
|
||||
'This driver requires Xdebug 3 but version %s is loaded',
|
||||
phpversion('xdebug')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$mode = getenv('XDEBUG_MODE');
|
||||
|
||||
if ($mode === false || $mode === '') {
|
||||
$mode = ini_get('xdebug.mode');
|
||||
}
|
||||
|
||||
if ($mode === false ||
|
||||
!in_array('coverage', explode(',', $mode), true)) {
|
||||
throw new Xdebug3NotEnabledException;
|
||||
}
|
||||
|
||||
if (!$filter->isEmpty()) {
|
||||
xdebug_set_filter(
|
||||
XDEBUG_FILTER_CODE_COVERAGE,
|
||||
XDEBUG_PATH_INCLUDE,
|
||||
$filter->files()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function canCollectBranchAndPathCoverage(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canDetectDeadCode(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$flags = XDEBUG_CC_UNUSED;
|
||||
|
||||
if ($this->detectsDeadCode() || $this->collectsBranchAndPathCoverage()) {
|
||||
$flags |= XDEBUG_CC_DEAD_CODE;
|
||||
}
|
||||
|
||||
if ($this->collectsBranchAndPathCoverage()) {
|
||||
$flags |= XDEBUG_CC_BRANCH_CHECK;
|
||||
}
|
||||
|
||||
xdebug_start_code_coverage($flags);
|
||||
}
|
||||
|
||||
public function stop(): RawCodeCoverageData
|
||||
{
|
||||
$data = xdebug_get_code_coverage();
|
||||
|
||||
xdebug_stop_code_coverage();
|
||||
|
||||
if ($this->collectsBranchAndPathCoverage()) {
|
||||
return RawCodeCoverageData::fromXdebugWithPathCoverage($data);
|
||||
}
|
||||
|
||||
return RawCodeCoverageData::fromXdebugWithoutPathCoverage($data);
|
||||
}
|
||||
|
||||
public function nameAndVersion(): string
|
||||
{
|
||||
return 'Xdebug ' . phpversion('xdebug');
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,9 +9,8 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
/**
|
||||
* Exception that is raised when covered code is not executed.
|
||||
*/
|
||||
final class CoveredCodeNotExecutedException extends RuntimeException
|
||||
use RuntimeException;
|
||||
|
||||
final class BranchAndPathCoverageNotSupportedException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,9 +9,8 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
/**
|
||||
* Exception that is raised when @covers must be used but is not.
|
||||
*/
|
||||
final class MissingCoversAnnotationException extends RuntimeException
|
||||
use RuntimeException;
|
||||
|
||||
final class DeadCodeDetectionNotSupportedException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
17
vendor/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php
vendored
Normal file
17
vendor/phpunit/php-code-coverage/src/Exception/DirectoryCouldNotBeCreatedException.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Util;
|
||||
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class DirectoryCouldNotBeCreatedException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,9 +9,8 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
/**
|
||||
* Exception interface for php-code-coverage component.
|
||||
*/
|
||||
interface Exception
|
||||
use Throwable;
|
||||
|
||||
interface Exception extends Throwable
|
||||
{
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -11,26 +11,4 @@ namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
final class InvalidArgumentException extends \InvalidArgumentException implements Exception
|
||||
{
|
||||
/**
|
||||
* @param int $argument
|
||||
* @param string $type
|
||||
* @param null|mixed $value
|
||||
*
|
||||
* @return InvalidArgumentException
|
||||
*/
|
||||
public static function create($argument, $type, $value = null): self
|
||||
{
|
||||
$stack = \debug_backtrace(0);
|
||||
|
||||
return new self(
|
||||
\sprintf(
|
||||
'Argument #%d%sof %s::%s() must be a %s',
|
||||
$argument,
|
||||
$value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ',
|
||||
$stack[1]['class'],
|
||||
$stack[1]['function'],
|
||||
$type
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
20
vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php
vendored
Normal file
20
vendor/phpunit/php-code-coverage/src/Exception/NoCodeCoverageDriverAvailableException.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class NoCodeCoverageDriverAvailableException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('No code coverage driver available');
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class NoCodeCoverageDriverWithPathCoverageSupportAvailableException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('No code coverage driver with path coverage support available');
|
||||
}
|
||||
}
|
16
vendor/phpunit/php-code-coverage/src/Exception/ParserException.php
vendored
Normal file
16
vendor/phpunit/php-code-coverage/src/Exception/ParserException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class ParserException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
22
vendor/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php
vendored
Normal file
22
vendor/phpunit/php-code-coverage/src/Exception/PathExistsButIsNotDirectoryException.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use function sprintf;
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class PathExistsButIsNotDirectoryException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(sprintf('"%s" exists but is not a directory', $path));
|
||||
}
|
||||
}
|
21
vendor/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php
vendored
Normal file
21
vendor/phpunit/php-code-coverage/src/Exception/PcovNotAvailableException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class PcovNotAvailableException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The PCOV extension is not available');
|
||||
}
|
||||
}
|
21
vendor/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php
vendored
Normal file
21
vendor/phpunit/php-code-coverage/src/Exception/PhpdbgNotAvailableException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class PhpdbgNotAvailableException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The PHPDBG SAPI is not available');
|
||||
}
|
||||
}
|
16
vendor/phpunit/php-code-coverage/src/Exception/ReflectionException.php
vendored
Normal file
16
vendor/phpunit/php-code-coverage/src/Exception/ReflectionException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class ReflectionException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
20
vendor/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php
vendored
Normal file
20
vendor/phpunit/php-code-coverage/src/Exception/ReportAlreadyFinalizedException.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class ReportAlreadyFinalizedException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The code coverage report has already been finalized');
|
||||
}
|
||||
}
|
16
vendor/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php
vendored
Normal file
16
vendor/phpunit/php-code-coverage/src/Exception/StaticAnalysisCacheNotConfiguredException.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class StaticAnalysisCacheNotConfiguredException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
20
vendor/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php
vendored
Normal file
20
vendor/phpunit/php-code-coverage/src/Exception/TestIdMissingException.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class TestIdMissingException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Test ID is missing');
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,15 +9,14 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
/**
|
||||
* Exception that is raised when code is unintentionally covered.
|
||||
*/
|
||||
final class UnintentionallyCoveredCodeException extends RuntimeException
|
||||
use RuntimeException;
|
||||
|
||||
final class UnintentionallyCoveredCodeException extends RuntimeException implements Exception
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $unintentionallyCoveredUnits = [];
|
||||
private $unintentionallyCoveredUnits;
|
||||
|
||||
public function __construct(array $unintentionallyCoveredUnits)
|
||||
{
|
||||
|
22
vendor/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php
vendored
Normal file
22
vendor/phpunit/php-code-coverage/src/Exception/WriteOperationFailedException.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use function sprintf;
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class WriteOperationFailedException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(sprintf('Cannot write to "%s"', $path));
|
||||
}
|
||||
}
|
17
vendor/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php
vendored
Normal file
17
vendor/phpunit/php-code-coverage/src/Exception/WrongXdebugVersionException.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class WrongXdebugVersionException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
21
vendor/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php
vendored
Normal file
21
vendor/phpunit/php-code-coverage/src/Exception/Xdebug2NotEnabledException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class Xdebug2NotEnabledException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('xdebug.coverage_enable=On has to be set');
|
||||
}
|
||||
}
|
21
vendor/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php
vendored
Normal file
21
vendor/phpunit/php-code-coverage/src/Exception/Xdebug3NotEnabledException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class Xdebug3NotEnabledException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set');
|
||||
}
|
||||
}
|
21
vendor/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php
vendored
Normal file
21
vendor/phpunit/php-code-coverage/src/Exception/XdebugNotAvailableException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Driver;
|
||||
|
||||
use RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Exception;
|
||||
|
||||
final class XdebugNotAvailableException extends RuntimeException implements Exception
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('The Xdebug extension is not available');
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
class RuntimeException extends \RuntimeException implements Exception
|
||||
use RuntimeException;
|
||||
|
||||
final class XmlException extends RuntimeException implements Exception
|
||||
{
|
||||
}
|
154
vendor/phpunit/php-code-coverage/src/Filter.php
vendored
154
vendor/phpunit/php-code-coverage/src/Filter.php
vendored
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,166 +9,110 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use function array_keys;
|
||||
use function is_file;
|
||||
use function realpath;
|
||||
use function strpos;
|
||||
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
|
||||
|
||||
/**
|
||||
* Filter for whitelisting of code coverage information.
|
||||
*/
|
||||
final class Filter
|
||||
{
|
||||
/**
|
||||
* Source files that are whitelisted.
|
||||
*
|
||||
* @var array
|
||||
* @psalm-var array<string,true>
|
||||
*/
|
||||
private $whitelistedFiles = [];
|
||||
private $files = [];
|
||||
|
||||
/**
|
||||
* Remembers the result of the `is_file()` calls.
|
||||
*
|
||||
* @var bool[]
|
||||
* @psalm-var array<string,bool>
|
||||
*/
|
||||
private $isFileCallsCache = [];
|
||||
private $isFileCache = [];
|
||||
|
||||
/**
|
||||
* Adds a directory to the whitelist (recursively).
|
||||
*/
|
||||
public function addDirectoryToWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void
|
||||
public function includeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void
|
||||
{
|
||||
$facade = new FileIteratorFacade;
|
||||
$files = $facade->getFilesAsArray($directory, $suffix, $prefix);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$this->addFileToWhitelist($file);
|
||||
foreach ((new FileIteratorFacade)->getFilesAsArray($directory, $suffix, $prefix) as $file) {
|
||||
$this->includeFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to the whitelist.
|
||||
* @psalm-param list<string> $files
|
||||
*/
|
||||
public function addFileToWhitelist(string $filename): void
|
||||
public function includeFiles(array $filenames): void
|
||||
{
|
||||
$filename = \realpath($filename);
|
||||
foreach ($filenames as $filename) {
|
||||
$this->includeFile($filename);
|
||||
}
|
||||
}
|
||||
|
||||
public function includeFile(string $filename): void
|
||||
{
|
||||
$filename = realpath($filename);
|
||||
|
||||
if (!$filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->whitelistedFiles[$filename] = true;
|
||||
$this->files[$filename] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds files to the whitelist.
|
||||
*
|
||||
* @param string[] $files
|
||||
*/
|
||||
public function addFilesToWhitelist(array $files): void
|
||||
public function excludeDirectory(string $directory, string $suffix = '.php', string $prefix = ''): void
|
||||
{
|
||||
foreach ($files as $file) {
|
||||
$this->addFileToWhitelist($file);
|
||||
foreach ((new FileIteratorFacade)->getFilesAsArray($directory, $suffix, $prefix) as $file) {
|
||||
$this->excludeFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory from the whitelist (recursively).
|
||||
*/
|
||||
public function removeDirectoryFromWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void
|
||||
public function excludeFile(string $filename): void
|
||||
{
|
||||
$facade = new FileIteratorFacade;
|
||||
$files = $facade->getFilesAsArray($directory, $suffix, $prefix);
|
||||
$filename = realpath($filename);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$this->removeFileFromWhitelist($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from the whitelist.
|
||||
*/
|
||||
public function removeFileFromWhitelist(string $filename): void
|
||||
{
|
||||
$filename = \realpath($filename);
|
||||
|
||||
if (!$filename || !isset($this->whitelistedFiles[$filename])) {
|
||||
if (!$filename || !isset($this->files[$filename])) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($this->whitelistedFiles[$filename]);
|
||||
unset($this->files[$filename]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a filename is a real filename.
|
||||
*/
|
||||
public function isFile(string $filename): bool
|
||||
{
|
||||
if (isset($this->isFileCallsCache[$filename])) {
|
||||
return $this->isFileCallsCache[$filename];
|
||||
if (isset($this->isFileCache[$filename])) {
|
||||
return $this->isFileCache[$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 ||
|
||||
\strpos($filename, 'Standard input code') !== false) {
|
||||
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 ||
|
||||
strpos($filename, 'Standard input code') !== false) {
|
||||
$isFile = false;
|
||||
} else {
|
||||
$isFile = \file_exists($filename);
|
||||
$isFile = is_file($filename);
|
||||
}
|
||||
|
||||
$this->isFileCallsCache[$filename] = $isFile;
|
||||
$this->isFileCache[$filename] = $isFile;
|
||||
|
||||
return $isFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a file is filtered.
|
||||
*/
|
||||
public function isFiltered(string $filename): bool
|
||||
public function isExcluded(string $filename): bool
|
||||
{
|
||||
if (!$this->isFile($filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isset($this->whitelistedFiles[$filename]);
|
||||
return !isset($this->files[$filename]) || !$this->isFile($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of whitelisted files.
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public function getWhitelist(): array
|
||||
public function files(): array
|
||||
{
|
||||
return \array_keys($this->whitelistedFiles);
|
||||
return array_keys($this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this filter has a whitelist.
|
||||
*/
|
||||
public function hasWhitelist(): bool
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return !empty($this->whitelistedFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the whitelisted files.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getWhitelistedFiles(): array
|
||||
{
|
||||
return $this->whitelistedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the whitelisted files.
|
||||
*/
|
||||
public function setWhitelistedFiles(array $whitelistedFiles): void
|
||||
{
|
||||
$this->whitelistedFiles = $whitelistedFiles;
|
||||
return empty($this->files);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,12 +9,17 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Node;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\Util;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use function array_merge;
|
||||
use function str_replace;
|
||||
use function substr;
|
||||
use Countable;
|
||||
use SebastianBergmann\CodeCoverage\Util\Percentage;
|
||||
|
||||
/**
|
||||
* Base class for nodes in the code coverage information tree.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
abstract class AbstractNode implements \Countable
|
||||
abstract class AbstractNode implements Countable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -24,12 +29,12 @@ abstract class AbstractNode implements \Countable
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
private $pathAsString;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $pathArray;
|
||||
private $pathAsArray;
|
||||
|
||||
/**
|
||||
* @var AbstractNode
|
||||
@@ -43,31 +48,31 @@ abstract class AbstractNode implements \Countable
|
||||
|
||||
public function __construct(string $name, self $parent = null)
|
||||
{
|
||||
if (\substr($name, -1) == \DIRECTORY_SEPARATOR) {
|
||||
$name = \substr($name, 0, -1);
|
||||
if (substr($name, -1) === DIRECTORY_SEPARATOR) {
|
||||
$name = substr($name, 0, -1);
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
public function id(): string
|
||||
{
|
||||
if ($this->id === null) {
|
||||
$parent = $this->getParent();
|
||||
$parent = $this->parent();
|
||||
|
||||
if ($parent === null) {
|
||||
$this->id = 'index';
|
||||
} else {
|
||||
$parentId = $parent->getId();
|
||||
$parentId = $parent->id();
|
||||
|
||||
if ($parentId === 'index') {
|
||||
$this->id = \str_replace(':', '_', $this->name);
|
||||
$this->id = str_replace(':', '_', $this->name);
|
||||
} else {
|
||||
$this->id = $parentId . '/' . $this->name;
|
||||
}
|
||||
@@ -77,252 +82,172 @@ abstract class AbstractNode implements \Countable
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
public function pathAsString(): string
|
||||
{
|
||||
if ($this->path === null) {
|
||||
if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) {
|
||||
$this->path = $this->name;
|
||||
} else {
|
||||
$this->path = $this->parent->getPath() . \DIRECTORY_SEPARATOR . $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getPathAsArray(): array
|
||||
{
|
||||
if ($this->pathArray === null) {
|
||||
if ($this->pathAsString === null) {
|
||||
if ($this->parent === null) {
|
||||
$this->pathArray = [];
|
||||
$this->pathAsString = $this->name;
|
||||
} else {
|
||||
$this->pathArray = $this->parent->getPathAsArray();
|
||||
$this->pathAsString = $this->parent->pathAsString() . DIRECTORY_SEPARATOR . $this->name;
|
||||
}
|
||||
|
||||
$this->pathArray[] = $this;
|
||||
}
|
||||
|
||||
return $this->pathArray;
|
||||
return $this->pathAsString;
|
||||
}
|
||||
|
||||
public function getParent(): ?self
|
||||
public function pathAsArray(): array
|
||||
{
|
||||
if ($this->pathAsArray === null) {
|
||||
if ($this->parent === null) {
|
||||
$this->pathAsArray = [];
|
||||
} else {
|
||||
$this->pathAsArray = $this->parent->pathAsArray();
|
||||
}
|
||||
|
||||
$this->pathAsArray[] = $this;
|
||||
}
|
||||
|
||||
return $this->pathAsArray;
|
||||
}
|
||||
|
||||
public function parent(): ?self
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of classes that has been tested.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getTestedClassesPercent(bool $asString = true)
|
||||
public function percentageOfTestedClasses(): Percentage
|
||||
{
|
||||
return Util::percent(
|
||||
$this->getNumTestedClasses(),
|
||||
$this->getNumClasses(),
|
||||
$asString
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfTestedClasses(),
|
||||
$this->numberOfClasses(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of traits that has been tested.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getTestedTraitsPercent(bool $asString = true)
|
||||
public function percentageOfTestedTraits(): Percentage
|
||||
{
|
||||
return Util::percent(
|
||||
$this->getNumTestedTraits(),
|
||||
$this->getNumTraits(),
|
||||
$asString
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfTestedTraits(),
|
||||
$this->numberOfTraits(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of classes and traits that has been tested.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getTestedClassesAndTraitsPercent(bool $asString = true)
|
||||
public function percentageOfTestedClassesAndTraits(): Percentage
|
||||
{
|
||||
return Util::percent(
|
||||
$this->getNumTestedClassesAndTraits(),
|
||||
$this->getNumClassesAndTraits(),
|
||||
$asString
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfTestedClassesAndTraits(),
|
||||
$this->numberOfClassesAndTraits(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of functions that has been tested.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getTestedFunctionsPercent(bool $asString = true)
|
||||
public function percentageOfTestedFunctions(): Percentage
|
||||
{
|
||||
return Util::percent(
|
||||
$this->getNumTestedFunctions(),
|
||||
$this->getNumFunctions(),
|
||||
$asString
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfTestedFunctions(),
|
||||
$this->numberOfFunctions(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of methods that has been tested.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getTestedMethodsPercent(bool $asString = true)
|
||||
public function percentageOfTestedMethods(): Percentage
|
||||
{
|
||||
return Util::percent(
|
||||
$this->getNumTestedMethods(),
|
||||
$this->getNumMethods(),
|
||||
$asString
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfTestedMethods(),
|
||||
$this->numberOfMethods(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of functions and methods that has been tested.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getTestedFunctionsAndMethodsPercent(bool $asString = true)
|
||||
public function percentageOfTestedFunctionsAndMethods(): Percentage
|
||||
{
|
||||
return Util::percent(
|
||||
$this->getNumTestedFunctionsAndMethods(),
|
||||
$this->getNumFunctionsAndMethods(),
|
||||
$asString
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfTestedFunctionsAndMethods(),
|
||||
$this->numberOfFunctionsAndMethods(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the percentage of executed lines.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getLineExecutedPercent(bool $asString = true)
|
||||
public function percentageOfExecutedLines(): Percentage
|
||||
{
|
||||
return Util::percent(
|
||||
$this->getNumExecutedLines(),
|
||||
$this->getNumExecutableLines(),
|
||||
$asString
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfExecutedLines(),
|
||||
$this->numberOfExecutableLines(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of classes and traits.
|
||||
*/
|
||||
public function getNumClassesAndTraits(): int
|
||||
public function percentageOfExecutedBranches(): Percentage
|
||||
{
|
||||
return $this->getNumClasses() + $this->getNumTraits();
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfExecutedBranches(),
|
||||
$this->numberOfExecutableBranches()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested classes and traits.
|
||||
*/
|
||||
public function getNumTestedClassesAndTraits(): int
|
||||
public function percentageOfExecutedPaths(): Percentage
|
||||
{
|
||||
return $this->getNumTestedClasses() + $this->getNumTestedTraits();
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$this->numberOfExecutedPaths(),
|
||||
$this->numberOfExecutablePaths()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the classes and traits of this node.
|
||||
*/
|
||||
public function getClassesAndTraits(): array
|
||||
public function numberOfClassesAndTraits(): int
|
||||
{
|
||||
return \array_merge($this->getClasses(), $this->getTraits());
|
||||
return $this->numberOfClasses() + $this->numberOfTraits();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of functions and methods.
|
||||
*/
|
||||
public function getNumFunctionsAndMethods(): int
|
||||
public function numberOfTestedClassesAndTraits(): int
|
||||
{
|
||||
return $this->getNumFunctions() + $this->getNumMethods();
|
||||
return $this->numberOfTestedClasses() + $this->numberOfTestedTraits();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested functions and methods.
|
||||
*/
|
||||
public function getNumTestedFunctionsAndMethods(): int
|
||||
public function classesAndTraits(): array
|
||||
{
|
||||
return $this->getNumTestedFunctions() + $this->getNumTestedMethods();
|
||||
return array_merge($this->classes(), $this->traits());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the functions and methods of this node.
|
||||
*/
|
||||
public function getFunctionsAndMethods(): array
|
||||
public function numberOfFunctionsAndMethods(): int
|
||||
{
|
||||
return \array_merge($this->getFunctions(), $this->getMethods());
|
||||
return $this->numberOfFunctions() + $this->numberOfMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the classes of this node.
|
||||
*/
|
||||
abstract public function getClasses(): array;
|
||||
public function numberOfTestedFunctionsAndMethods(): int
|
||||
{
|
||||
return $this->numberOfTestedFunctions() + $this->numberOfTestedMethods();
|
||||
}
|
||||
|
||||
abstract public function classes(): array;
|
||||
|
||||
abstract public function traits(): array;
|
||||
|
||||
abstract public function functions(): array;
|
||||
|
||||
/**
|
||||
* Returns the traits of this node.
|
||||
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
abstract public function getTraits(): array;
|
||||
abstract public function linesOfCode(): array;
|
||||
|
||||
/**
|
||||
* Returns the functions of this node.
|
||||
*/
|
||||
abstract public function getFunctions(): array;
|
||||
abstract public function numberOfExecutableLines(): int;
|
||||
|
||||
/**
|
||||
* Returns the LOC/CLOC/NCLOC of this node.
|
||||
*/
|
||||
abstract public function getLinesOfCode(): array;
|
||||
abstract public function numberOfExecutedLines(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of executable lines.
|
||||
*/
|
||||
abstract public function getNumExecutableLines(): int;
|
||||
abstract public function numberOfExecutableBranches(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of executed lines.
|
||||
*/
|
||||
abstract public function getNumExecutedLines(): int;
|
||||
abstract public function numberOfExecutedBranches(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of classes.
|
||||
*/
|
||||
abstract public function getNumClasses(): int;
|
||||
abstract public function numberOfExecutablePaths(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of tested classes.
|
||||
*/
|
||||
abstract public function getNumTestedClasses(): int;
|
||||
abstract public function numberOfExecutedPaths(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of traits.
|
||||
*/
|
||||
abstract public function getNumTraits(): int;
|
||||
abstract public function numberOfClasses(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of tested traits.
|
||||
*/
|
||||
abstract public function getNumTestedTraits(): int;
|
||||
abstract public function numberOfTestedClasses(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of methods.
|
||||
*/
|
||||
abstract public function getNumMethods(): int;
|
||||
abstract public function numberOfTraits(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of tested methods.
|
||||
*/
|
||||
abstract public function getNumTestedMethods(): int;
|
||||
abstract public function numberOfTestedTraits(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of functions.
|
||||
*/
|
||||
abstract public function getNumFunctions(): int;
|
||||
abstract public function numberOfMethods(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of tested functions.
|
||||
*/
|
||||
abstract public function getNumTestedFunctions(): int;
|
||||
abstract public function numberOfTestedMethods(): int;
|
||||
|
||||
abstract public function numberOfFunctions(): int;
|
||||
|
||||
abstract public function numberOfTestedFunctions(): int;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,14 +9,40 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Node;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use function array_shift;
|
||||
use function basename;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function is_file;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
use function substr;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData;
|
||||
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Builder
|
||||
{
|
||||
/**
|
||||
* @var FileAnalyser
|
||||
*/
|
||||
private $analyser;
|
||||
|
||||
public function __construct(FileAnalyser $analyser)
|
||||
{
|
||||
$this->analyser = $analyser;
|
||||
}
|
||||
|
||||
public function build(CodeCoverage $coverage): Directory
|
||||
{
|
||||
$files = $coverage->getData();
|
||||
$commonPath = $this->reducePaths($files);
|
||||
$data = clone $coverage->getData(); // clone because path munging is destructive to the original data
|
||||
$commonPath = $this->reducePaths($data);
|
||||
$root = new Directory(
|
||||
$commonPath,
|
||||
null
|
||||
@@ -24,28 +50,41 @@ final class Builder
|
||||
|
||||
$this->addItems(
|
||||
$root,
|
||||
$this->buildDirectoryStructure($files),
|
||||
$coverage->getTests(),
|
||||
$coverage->getCacheTokens()
|
||||
$this->buildDirectoryStructure($data),
|
||||
$coverage->getTests()
|
||||
);
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
private function addItems(Directory $root, array $items, array $tests, bool $cacheTokens): void
|
||||
private function addItems(Directory $root, array $items, array $tests): void
|
||||
{
|
||||
foreach ($items as $key => $value) {
|
||||
$key = (string) $key;
|
||||
|
||||
if (\substr($key, -2) === '/f') {
|
||||
$key = \substr($key, 0, -2);
|
||||
if (substr($key, -2) === '/f') {
|
||||
$key = substr($key, 0, -2);
|
||||
$filename = $root->pathAsString() . DIRECTORY_SEPARATOR . $key;
|
||||
|
||||
if (\file_exists($root->getPath() . \DIRECTORY_SEPARATOR . $key)) {
|
||||
$root->addFile($key, $value, $tests, $cacheTokens);
|
||||
if (is_file($filename)) {
|
||||
$root->addFile(
|
||||
new File(
|
||||
$key,
|
||||
$root,
|
||||
$value['lineCoverage'],
|
||||
$value['functionCoverage'],
|
||||
$tests,
|
||||
$this->analyser->classesIn($filename),
|
||||
$this->analyser->traitsIn($filename),
|
||||
$this->analyser->functionsIn($filename),
|
||||
$this->analyser->linesOfCodeFor($filename)
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$child = $root->addDirectory($key);
|
||||
$this->addItems($child, $value, $tests, $cacheTokens);
|
||||
|
||||
$this->addItems($child, $value, $tests);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,14 +129,14 @@ final class Builder
|
||||
* )
|
||||
* </code>
|
||||
*/
|
||||
private function buildDirectoryStructure(array $files): array
|
||||
private function buildDirectoryStructure(ProcessedCodeCoverageData $data): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($files as $path => $file) {
|
||||
$path = \explode(\DIRECTORY_SEPARATOR, $path);
|
||||
foreach ($data->coveredFiles() as $originalPath) {
|
||||
$path = explode(DIRECTORY_SEPARATOR, $originalPath);
|
||||
$pointer = &$result;
|
||||
$max = \count($path);
|
||||
$max = count($path);
|
||||
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
$type = '';
|
||||
@@ -109,7 +148,10 @@ final class Builder
|
||||
$pointer = &$pointer[$path[$i] . $type];
|
||||
}
|
||||
|
||||
$pointer = $file;
|
||||
$pointer = [
|
||||
'lineCoverage' => $data->lineCoverage()[$originalPath] ?? [],
|
||||
'functionCoverage' => $data->functionCoverage()[$originalPath] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -152,41 +194,39 @@ final class Builder
|
||||
* )
|
||||
* </code>
|
||||
*/
|
||||
private function reducePaths(array &$files): string
|
||||
private function reducePaths(ProcessedCodeCoverageData $coverage): string
|
||||
{
|
||||
if (empty($files)) {
|
||||
if (empty($coverage->coveredFiles())) {
|
||||
return '.';
|
||||
}
|
||||
|
||||
$commonPath = '';
|
||||
$paths = \array_keys($files);
|
||||
$paths = $coverage->coveredFiles();
|
||||
|
||||
if (\count($files) === 1) {
|
||||
$commonPath = \dirname($paths[0]) . \DIRECTORY_SEPARATOR;
|
||||
$files[\basename($paths[0])] = $files[$paths[0]];
|
||||
|
||||
unset($files[$paths[0]]);
|
||||
if (count($paths) === 1) {
|
||||
$commonPath = dirname($paths[0]) . DIRECTORY_SEPARATOR;
|
||||
$coverage->renameFile($paths[0], basename($paths[0]));
|
||||
|
||||
return $commonPath;
|
||||
}
|
||||
|
||||
$max = \count($paths);
|
||||
$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] = \str_replace('/', \DIRECTORY_SEPARATOR, $paths[$i]);
|
||||
if (strpos($paths[$i], 'phar://') === 0) {
|
||||
$paths[$i] = substr($paths[$i], 7);
|
||||
$paths[$i] = str_replace('/', DIRECTORY_SEPARATOR, $paths[$i]);
|
||||
}
|
||||
$paths[$i] = \explode(\DIRECTORY_SEPARATOR, $paths[$i]);
|
||||
$paths[$i] = explode(DIRECTORY_SEPARATOR, $paths[$i]);
|
||||
|
||||
if (empty($paths[$i][0])) {
|
||||
$paths[$i][0] = \DIRECTORY_SEPARATOR;
|
||||
$paths[$i][0] = DIRECTORY_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
$done = false;
|
||||
$max = \count($paths);
|
||||
$max = count($paths);
|
||||
|
||||
while (!$done) {
|
||||
for ($i = 0; $i < $max - 1; $i++) {
|
||||
@@ -202,26 +242,23 @@ final class Builder
|
||||
if (!$done) {
|
||||
$commonPath .= $paths[0][0];
|
||||
|
||||
if ($paths[0][0] !== \DIRECTORY_SEPARATOR) {
|
||||
$commonPath .= \DIRECTORY_SEPARATOR;
|
||||
if ($paths[0][0] !== DIRECTORY_SEPARATOR) {
|
||||
$commonPath .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
\array_shift($paths[$i]);
|
||||
array_shift($paths[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$original = \array_keys($files);
|
||||
$max = \count($original);
|
||||
$original = $coverage->coveredFiles();
|
||||
$max = count($original);
|
||||
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
$files[\implode(\DIRECTORY_SEPARATOR, $paths[$i])] = $files[$original[$i]];
|
||||
unset($files[$original[$i]]);
|
||||
$coverage->renameFile($original[$i], implode(DIRECTORY_SEPARATOR, $paths[$i]));
|
||||
}
|
||||
|
||||
\ksort($files);
|
||||
|
||||
return \substr($commonPath, 0, -1);
|
||||
return substr($commonPath, 0, -1);
|
||||
}
|
||||
}
|
||||
|
50
vendor/phpunit/php-code-coverage/src/Node/CrapIndex.php
vendored
Normal file
50
vendor/phpunit/php-code-coverage/src/Node/CrapIndex.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Node;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class CrapIndex
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $cyclomaticComplexity;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $codeCoverage;
|
||||
|
||||
public function __construct(int $cyclomaticComplexity, float $codeCoverage)
|
||||
{
|
||||
$this->cyclomaticComplexity = $cyclomaticComplexity;
|
||||
$this->codeCoverage = $codeCoverage;
|
||||
}
|
||||
|
||||
public function asString(): string
|
||||
{
|
||||
if ($this->codeCoverage === 0.0) {
|
||||
return (string) ($this->cyclomaticComplexity ** 2 + $this->cyclomaticComplexity);
|
||||
}
|
||||
|
||||
if ($this->codeCoverage >= 95) {
|
||||
return (string) $this->cyclomaticComplexity;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%01.2F',
|
||||
$this->cyclomaticComplexity ** 2 * (1 - $this->codeCoverage / 100) ** 3 + $this->cyclomaticComplexity
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,12 +9,15 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Node;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\InvalidArgumentException;
|
||||
use function array_merge;
|
||||
use function count;
|
||||
use IteratorAggregate;
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
/**
|
||||
* Represents a directory in the code coverage information tree.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Directory extends AbstractNode implements \IteratorAggregate
|
||||
final class Directory extends AbstractNode implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var AbstractNode[]
|
||||
@@ -47,7 +50,7 @@ final class Directory extends AbstractNode implements \IteratorAggregate
|
||||
private $functions;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @psalm-var null|array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
private $linesOfCode;
|
||||
|
||||
@@ -66,6 +69,26 @@ final class Directory extends AbstractNode implements \IteratorAggregate
|
||||
*/
|
||||
private $numExecutedLines = -1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutableBranches = -1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutedBranches = -1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutablePaths = -1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutedPaths = -1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
@@ -106,100 +129,70 @@ final class Directory extends AbstractNode implements \IteratorAggregate
|
||||
*/
|
||||
private $numTestedFunctions = -1;
|
||||
|
||||
/**
|
||||
* Returns the number of files in/under this node.
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
if ($this->numFiles === -1) {
|
||||
$this->numFiles = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numFiles += \count($child);
|
||||
$this->numFiles += count($child);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for this node.
|
||||
*/
|
||||
public function getIterator(): \RecursiveIteratorIterator
|
||||
public function getIterator(): RecursiveIteratorIterator
|
||||
{
|
||||
return new \RecursiveIteratorIterator(
|
||||
return new RecursiveIteratorIterator(
|
||||
new Iterator($this),
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new directory.
|
||||
*/
|
||||
public function addDirectory(string $name): self
|
||||
{
|
||||
$directory = new self($name, $this);
|
||||
|
||||
$this->children[] = $directory;
|
||||
$this->directories[] = &$this->children[\count($this->children) - 1];
|
||||
$this->directories[] = &$this->children[count($this->children) - 1];
|
||||
|
||||
return $directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new file.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function addFile(string $name, array $coverageData, array $testData, bool $cacheTokens): File
|
||||
public function addFile(File $file): void
|
||||
{
|
||||
$file = new File($name, $this, $coverageData, $testData, $cacheTokens);
|
||||
|
||||
$this->children[] = $file;
|
||||
$this->files[] = &$this->children[\count($this->children) - 1];
|
||||
$this->files[] = &$this->children[count($this->children) - 1];
|
||||
|
||||
$this->numExecutableLines = -1;
|
||||
$this->numExecutedLines = -1;
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directories in this directory.
|
||||
*/
|
||||
public function getDirectories(): array
|
||||
public function directories(): array
|
||||
{
|
||||
return $this->directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the files in this directory.
|
||||
*/
|
||||
public function getFiles(): array
|
||||
public function files(): array
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child nodes of this node.
|
||||
*/
|
||||
public function getChildNodes(): array
|
||||
public function children(): array
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the classes of this node.
|
||||
*/
|
||||
public function getClasses(): array
|
||||
public function classes(): array
|
||||
{
|
||||
if ($this->classes === null) {
|
||||
$this->classes = [];
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->classes = \array_merge(
|
||||
$this->classes = array_merge(
|
||||
$this->classes,
|
||||
$child->getClasses()
|
||||
$child->classes()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -207,18 +200,15 @@ final class Directory extends AbstractNode implements \IteratorAggregate
|
||||
return $this->classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the traits of this node.
|
||||
*/
|
||||
public function getTraits(): array
|
||||
public function traits(): array
|
||||
{
|
||||
if ($this->traits === null) {
|
||||
$this->traits = [];
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->traits = \array_merge(
|
||||
$this->traits = array_merge(
|
||||
$this->traits,
|
||||
$child->getTraits()
|
||||
$child->traits()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -226,18 +216,15 @@ final class Directory extends AbstractNode implements \IteratorAggregate
|
||||
return $this->traits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the functions of this node.
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
public function functions(): array
|
||||
{
|
||||
if ($this->functions === null) {
|
||||
$this->functions = [];
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->functions = \array_merge(
|
||||
$this->functions = array_merge(
|
||||
$this->functions,
|
||||
$child->getFunctions()
|
||||
$child->functions()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -246,179 +233,205 @@ final class Directory extends AbstractNode implements \IteratorAggregate
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LOC/CLOC/NCLOC of this node.
|
||||
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
public function getLinesOfCode(): array
|
||||
public function linesOfCode(): array
|
||||
{
|
||||
if ($this->linesOfCode === null) {
|
||||
$this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0];
|
||||
$this->linesOfCode = [
|
||||
'linesOfCode' => 0,
|
||||
'commentLinesOfCode' => 0,
|
||||
'nonCommentLinesOfCode' => 0,
|
||||
];
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$linesOfCode = $child->getLinesOfCode();
|
||||
$childLinesOfCode = $child->linesOfCode();
|
||||
|
||||
$this->linesOfCode['loc'] += $linesOfCode['loc'];
|
||||
$this->linesOfCode['cloc'] += $linesOfCode['cloc'];
|
||||
$this->linesOfCode['ncloc'] += $linesOfCode['ncloc'];
|
||||
$this->linesOfCode['linesOfCode'] += $childLinesOfCode['linesOfCode'];
|
||||
$this->linesOfCode['commentLinesOfCode'] += $childLinesOfCode['commentLinesOfCode'];
|
||||
$this->linesOfCode['nonCommentLinesOfCode'] += $childLinesOfCode['nonCommentLinesOfCode'];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->linesOfCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of executable lines.
|
||||
*/
|
||||
public function getNumExecutableLines(): int
|
||||
public function numberOfExecutableLines(): int
|
||||
{
|
||||
if ($this->numExecutableLines === -1) {
|
||||
$this->numExecutableLines = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numExecutableLines += $child->getNumExecutableLines();
|
||||
$this->numExecutableLines += $child->numberOfExecutableLines();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numExecutableLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of executed lines.
|
||||
*/
|
||||
public function getNumExecutedLines(): int
|
||||
public function numberOfExecutedLines(): int
|
||||
{
|
||||
if ($this->numExecutedLines === -1) {
|
||||
$this->numExecutedLines = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numExecutedLines += $child->getNumExecutedLines();
|
||||
$this->numExecutedLines += $child->numberOfExecutedLines();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numExecutedLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of classes.
|
||||
*/
|
||||
public function getNumClasses(): int
|
||||
public function numberOfExecutableBranches(): int
|
||||
{
|
||||
if ($this->numExecutableBranches === -1) {
|
||||
$this->numExecutableBranches = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numExecutableBranches += $child->numberOfExecutableBranches();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numExecutableBranches;
|
||||
}
|
||||
|
||||
public function numberOfExecutedBranches(): int
|
||||
{
|
||||
if ($this->numExecutedBranches === -1) {
|
||||
$this->numExecutedBranches = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numExecutedBranches += $child->numberOfExecutedBranches();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numExecutedBranches;
|
||||
}
|
||||
|
||||
public function numberOfExecutablePaths(): int
|
||||
{
|
||||
if ($this->numExecutablePaths === -1) {
|
||||
$this->numExecutablePaths = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numExecutablePaths += $child->numberOfExecutablePaths();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numExecutablePaths;
|
||||
}
|
||||
|
||||
public function numberOfExecutedPaths(): int
|
||||
{
|
||||
if ($this->numExecutedPaths === -1) {
|
||||
$this->numExecutedPaths = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numExecutedPaths += $child->numberOfExecutedPaths();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numExecutedPaths;
|
||||
}
|
||||
|
||||
public function numberOfClasses(): int
|
||||
{
|
||||
if ($this->numClasses === -1) {
|
||||
$this->numClasses = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numClasses += $child->getNumClasses();
|
||||
$this->numClasses += $child->numberOfClasses();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested classes.
|
||||
*/
|
||||
public function getNumTestedClasses(): int
|
||||
public function numberOfTestedClasses(): int
|
||||
{
|
||||
if ($this->numTestedClasses === -1) {
|
||||
$this->numTestedClasses = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numTestedClasses += $child->getNumTestedClasses();
|
||||
$this->numTestedClasses += $child->numberOfTestedClasses();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numTestedClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of traits.
|
||||
*/
|
||||
public function getNumTraits(): int
|
||||
public function numberOfTraits(): int
|
||||
{
|
||||
if ($this->numTraits === -1) {
|
||||
$this->numTraits = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numTraits += $child->getNumTraits();
|
||||
$this->numTraits += $child->numberOfTraits();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numTraits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested traits.
|
||||
*/
|
||||
public function getNumTestedTraits(): int
|
||||
public function numberOfTestedTraits(): int
|
||||
{
|
||||
if ($this->numTestedTraits === -1) {
|
||||
$this->numTestedTraits = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numTestedTraits += $child->getNumTestedTraits();
|
||||
$this->numTestedTraits += $child->numberOfTestedTraits();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numTestedTraits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of methods.
|
||||
*/
|
||||
public function getNumMethods(): int
|
||||
public function numberOfMethods(): int
|
||||
{
|
||||
if ($this->numMethods === -1) {
|
||||
$this->numMethods = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numMethods += $child->getNumMethods();
|
||||
$this->numMethods += $child->numberOfMethods();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested methods.
|
||||
*/
|
||||
public function getNumTestedMethods(): int
|
||||
public function numberOfTestedMethods(): int
|
||||
{
|
||||
if ($this->numTestedMethods === -1) {
|
||||
$this->numTestedMethods = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numTestedMethods += $child->getNumTestedMethods();
|
||||
$this->numTestedMethods += $child->numberOfTestedMethods();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numTestedMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of functions.
|
||||
*/
|
||||
public function getNumFunctions(): int
|
||||
public function numberOfFunctions(): int
|
||||
{
|
||||
if ($this->numFunctions === -1) {
|
||||
$this->numFunctions = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numFunctions += $child->getNumFunctions();
|
||||
$this->numFunctions += $child->numberOfFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->numFunctions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested functions.
|
||||
*/
|
||||
public function getNumTestedFunctions(): int
|
||||
public function numberOfTestedFunctions(): int
|
||||
{
|
||||
if ($this->numTestedFunctions === -1) {
|
||||
$this->numTestedFunctions = 0;
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$this->numTestedFunctions += $child->getNumTestedFunctions();
|
||||
$this->numTestedFunctions += $child->numberOfTestedFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
|
542
vendor/phpunit/php-code-coverage/src/Node/File.php
vendored
542
vendor/phpunit/php-code-coverage/src/Node/File.php
vendored
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,15 +9,24 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Node;
|
||||
|
||||
use function array_filter;
|
||||
use function count;
|
||||
use function range;
|
||||
|
||||
/**
|
||||
* Represents a file in the code coverage information tree.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class File extends AbstractNode
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $coverageData;
|
||||
private $lineCoverageData;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $functionCoverageData;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
@@ -34,6 +43,26 @@ final class File extends AbstractNode
|
||||
*/
|
||||
private $numExecutedLines = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutableBranches = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutedBranches = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutablePaths = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numExecutedPaths = 0;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -50,9 +79,9 @@ final class File extends AbstractNode
|
||||
private $functions = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @psalm-var array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
private $linesOfCode = [];
|
||||
private $linesOfCode;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
@@ -89,103 +118,100 @@ final class File extends AbstractNode
|
||||
*/
|
||||
private $numTestedFunctions;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $cacheTokens;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $codeUnitsByLine = [];
|
||||
|
||||
public function __construct(string $name, AbstractNode $parent, array $coverageData, array $testData, bool $cacheTokens)
|
||||
/**
|
||||
* @psalm-param array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int} $linesOfCode
|
||||
*/
|
||||
public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, array $linesOfCode)
|
||||
{
|
||||
parent::__construct($name, $parent);
|
||||
|
||||
$this->coverageData = $coverageData;
|
||||
$this->testData = $testData;
|
||||
$this->cacheTokens = $cacheTokens;
|
||||
$this->lineCoverageData = $lineCoverageData;
|
||||
$this->functionCoverageData = $functionCoverageData;
|
||||
$this->testData = $testData;
|
||||
$this->linesOfCode = $linesOfCode;
|
||||
|
||||
$this->calculateStatistics();
|
||||
$this->calculateStatistics($classes, $traits, $functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of files in/under this node.
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code coverage data of this node.
|
||||
*/
|
||||
public function getCoverageData(): array
|
||||
public function lineCoverageData(): array
|
||||
{
|
||||
return $this->coverageData;
|
||||
return $this->lineCoverageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the test data of this node.
|
||||
*/
|
||||
public function getTestData(): array
|
||||
public function functionCoverageData(): array
|
||||
{
|
||||
return $this->functionCoverageData;
|
||||
}
|
||||
|
||||
public function testData(): array
|
||||
{
|
||||
return $this->testData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the classes of this node.
|
||||
*/
|
||||
public function getClasses(): array
|
||||
public function classes(): array
|
||||
{
|
||||
return $this->classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the traits of this node.
|
||||
*/
|
||||
public function getTraits(): array
|
||||
public function traits(): array
|
||||
{
|
||||
return $this->traits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the functions of this node.
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
public function functions(): array
|
||||
{
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LOC/CLOC/NCLOC of this node.
|
||||
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
public function getLinesOfCode(): array
|
||||
public function linesOfCode(): array
|
||||
{
|
||||
return $this->linesOfCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of executable lines.
|
||||
*/
|
||||
public function getNumExecutableLines(): int
|
||||
public function numberOfExecutableLines(): int
|
||||
{
|
||||
return $this->numExecutableLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of executed lines.
|
||||
*/
|
||||
public function getNumExecutedLines(): int
|
||||
public function numberOfExecutedLines(): int
|
||||
{
|
||||
return $this->numExecutedLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of classes.
|
||||
*/
|
||||
public function getNumClasses(): int
|
||||
public function numberOfExecutableBranches(): int
|
||||
{
|
||||
return $this->numExecutableBranches;
|
||||
}
|
||||
|
||||
public function numberOfExecutedBranches(): int
|
||||
{
|
||||
return $this->numExecutedBranches;
|
||||
}
|
||||
|
||||
public function numberOfExecutablePaths(): int
|
||||
{
|
||||
return $this->numExecutablePaths;
|
||||
}
|
||||
|
||||
public function numberOfExecutedPaths(): int
|
||||
{
|
||||
return $this->numExecutedPaths;
|
||||
}
|
||||
|
||||
public function numberOfClasses(): int
|
||||
{
|
||||
if ($this->numClasses === null) {
|
||||
$this->numClasses = 0;
|
||||
@@ -204,18 +230,12 @@ final class File extends AbstractNode
|
||||
return $this->numClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested classes.
|
||||
*/
|
||||
public function getNumTestedClasses(): int
|
||||
public function numberOfTestedClasses(): int
|
||||
{
|
||||
return $this->numTestedClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of traits.
|
||||
*/
|
||||
public function getNumTraits(): int
|
||||
public function numberOfTraits(): int
|
||||
{
|
||||
if ($this->numTraits === null) {
|
||||
$this->numTraits = 0;
|
||||
@@ -234,18 +254,12 @@ final class File extends AbstractNode
|
||||
return $this->numTraits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested traits.
|
||||
*/
|
||||
public function getNumTestedTraits(): int
|
||||
public function numberOfTestedTraits(): int
|
||||
{
|
||||
return $this->numTestedTraits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of methods.
|
||||
*/
|
||||
public function getNumMethods(): int
|
||||
public function numberOfMethods(): int
|
||||
{
|
||||
if ($this->numMethods === null) {
|
||||
$this->numMethods = 0;
|
||||
@@ -270,10 +284,7 @@ final class File extends AbstractNode
|
||||
return $this->numMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested methods.
|
||||
*/
|
||||
public function getNumTestedMethods(): int
|
||||
public function numberOfTestedMethods(): int
|
||||
{
|
||||
if ($this->numTestedMethods === null) {
|
||||
$this->numTestedMethods = 0;
|
||||
@@ -300,18 +311,12 @@ final class File extends AbstractNode
|
||||
return $this->numTestedMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of functions.
|
||||
*/
|
||||
public function getNumFunctions(): int
|
||||
public function numberOfFunctions(): int
|
||||
{
|
||||
return \count($this->functions);
|
||||
return count($this->functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tested functions.
|
||||
*/
|
||||
public function getNumTestedFunctions(): int
|
||||
public function numberOfTestedFunctions(): int
|
||||
{
|
||||
if ($this->numTestedFunctions === null) {
|
||||
$this->numTestedFunctions = 0;
|
||||
@@ -327,32 +332,18 @@ final class File extends AbstractNode
|
||||
return $this->numTestedFunctions;
|
||||
}
|
||||
|
||||
private function calculateStatistics(): void
|
||||
private function calculateStatistics(array $classes, array $traits, array $functions): void
|
||||
{
|
||||
if ($this->cacheTokens) {
|
||||
$tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath());
|
||||
} else {
|
||||
$tokens = new \PHP_Token_Stream($this->getPath());
|
||||
}
|
||||
|
||||
$this->linesOfCode = $tokens->getLinesOfCode();
|
||||
|
||||
foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) {
|
||||
foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
|
||||
$this->codeUnitsByLine[$lineNumber] = [];
|
||||
}
|
||||
|
||||
try {
|
||||
$this->processClasses($tokens);
|
||||
$this->processTraits($tokens);
|
||||
$this->processFunctions($tokens);
|
||||
} catch (\OutOfBoundsException $e) {
|
||||
// This can happen with PHP_Token_Stream if the file is syntactically invalid,
|
||||
// and probably affects a file that wasn't executed.
|
||||
}
|
||||
unset($tokens);
|
||||
$this->processClasses($classes);
|
||||
$this->processTraits($traits);
|
||||
$this->processFunctions($functions);
|
||||
|
||||
foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) {
|
||||
if (isset($this->coverageData[$lineNumber])) {
|
||||
foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
|
||||
if (isset($this->lineCoverageData[$lineNumber])) {
|
||||
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
|
||||
$codeUnit['executableLines']++;
|
||||
}
|
||||
@@ -361,7 +352,7 @@ final class File extends AbstractNode
|
||||
|
||||
$this->numExecutableLines++;
|
||||
|
||||
if (\count($this->coverageData[$lineNumber]) > 0) {
|
||||
if (count($this->lineCoverageData[$lineNumber]) > 0) {
|
||||
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
|
||||
$codeUnit['executedLines']++;
|
||||
}
|
||||
@@ -375,134 +366,111 @@ final class File extends AbstractNode
|
||||
|
||||
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;
|
||||
}
|
||||
$methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100;
|
||||
$methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0;
|
||||
$methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0;
|
||||
|
||||
$method['crap'] = $this->crap(
|
||||
$method['ccn'],
|
||||
$method['coverage']
|
||||
);
|
||||
$method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage;
|
||||
$method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString();
|
||||
|
||||
$trait['ccn'] += $method['ccn'];
|
||||
}
|
||||
|
||||
unset($method);
|
||||
|
||||
if ($trait['executableLines'] > 0) {
|
||||
$trait['coverage'] = ($trait['executedLines'] /
|
||||
$trait['executableLines']) * 100;
|
||||
$traitLineCoverage = $trait['executableLines'] ? ($trait['executedLines'] / $trait['executableLines']) * 100 : 100;
|
||||
$traitBranchCoverage = $trait['executableBranches'] ? ($trait['executedBranches'] / $trait['executableBranches']) * 100 : 0;
|
||||
$traitPathCoverage = $trait['executablePaths'] ? ($trait['executedPaths'] / $trait['executablePaths']) * 100 : 0;
|
||||
|
||||
if ($trait['coverage'] === 100) {
|
||||
$this->numTestedClasses++;
|
||||
}
|
||||
} else {
|
||||
$trait['coverage'] = 100;
|
||||
$trait['coverage'] = $traitBranchCoverage ?: $traitLineCoverage;
|
||||
$trait['crap'] = (new CrapIndex($trait['ccn'], $traitPathCoverage ?: $traitLineCoverage))->asString();
|
||||
|
||||
if ($trait['executableLines'] > 0 && $trait['coverage'] === 100) {
|
||||
$this->numTestedClasses++;
|
||||
}
|
||||
|
||||
$trait['crap'] = $this->crap(
|
||||
$trait['ccn'],
|
||||
$trait['coverage']
|
||||
);
|
||||
}
|
||||
|
||||
unset($trait);
|
||||
|
||||
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;
|
||||
}
|
||||
$methodLineCoverage = $method['executableLines'] ? ($method['executedLines'] / $method['executableLines']) * 100 : 100;
|
||||
$methodBranchCoverage = $method['executableBranches'] ? ($method['executedBranches'] / $method['executableBranches']) * 100 : 0;
|
||||
$methodPathCoverage = $method['executablePaths'] ? ($method['executedPaths'] / $method['executablePaths']) * 100 : 0;
|
||||
|
||||
$method['crap'] = $this->crap(
|
||||
$method['ccn'],
|
||||
$method['coverage']
|
||||
);
|
||||
$method['coverage'] = $methodBranchCoverage ?: $methodLineCoverage;
|
||||
$method['crap'] = (new CrapIndex($method['ccn'], $methodPathCoverage ?: $methodLineCoverage))->asString();
|
||||
|
||||
$class['ccn'] += $method['ccn'];
|
||||
}
|
||||
|
||||
unset($method);
|
||||
|
||||
if ($class['executableLines'] > 0) {
|
||||
$class['coverage'] = ($class['executedLines'] /
|
||||
$class['executableLines']) * 100;
|
||||
$classLineCoverage = $class['executableLines'] ? ($class['executedLines'] / $class['executableLines']) * 100 : 100;
|
||||
$classBranchCoverage = $class['executableBranches'] ? ($class['executedBranches'] / $class['executableBranches']) * 100 : 0;
|
||||
$classPathCoverage = $class['executablePaths'] ? ($class['executedPaths'] / $class['executablePaths']) * 100 : 0;
|
||||
|
||||
if ($class['coverage'] === 100) {
|
||||
$this->numTestedClasses++;
|
||||
}
|
||||
} else {
|
||||
$class['coverage'] = 100;
|
||||
$class['coverage'] = $classBranchCoverage ?: $classLineCoverage;
|
||||
$class['crap'] = (new CrapIndex($class['ccn'], $classPathCoverage ?: $classLineCoverage))->asString();
|
||||
|
||||
if ($class['executableLines'] > 0 && $class['coverage'] === 100) {
|
||||
$this->numTestedClasses++;
|
||||
}
|
||||
|
||||
$class['crap'] = $this->crap(
|
||||
$class['ccn'],
|
||||
$class['coverage']
|
||||
);
|
||||
}
|
||||
|
||||
unset($class);
|
||||
|
||||
foreach ($this->functions as &$function) {
|
||||
if ($function['executableLines'] > 0) {
|
||||
$function['coverage'] = ($function['executedLines'] /
|
||||
$function['executableLines']) * 100;
|
||||
} else {
|
||||
$function['coverage'] = 100;
|
||||
}
|
||||
$functionLineCoverage = $function['executableLines'] ? ($function['executedLines'] / $function['executableLines']) * 100 : 100;
|
||||
$functionBranchCoverage = $function['executableBranches'] ? ($function['executedBranches'] / $function['executableBranches']) * 100 : 0;
|
||||
$functionPathCoverage = $function['executablePaths'] ? ($function['executedPaths'] / $function['executablePaths']) * 100 : 0;
|
||||
|
||||
$function['coverage'] = $functionBranchCoverage ?: $functionLineCoverage;
|
||||
$function['crap'] = (new CrapIndex($function['ccn'], $functionPathCoverage ?: $functionLineCoverage))->asString();
|
||||
|
||||
if ($function['coverage'] === 100) {
|
||||
$this->numTestedFunctions++;
|
||||
}
|
||||
|
||||
$function['crap'] = $this->crap(
|
||||
$function['ccn'],
|
||||
$function['coverage']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function processClasses(\PHP_Token_Stream $tokens): void
|
||||
private function processClasses(array $classes): void
|
||||
{
|
||||
$classes = $tokens->getClasses();
|
||||
$link = $this->getId() . '.html#';
|
||||
$link = $this->id() . '.html#';
|
||||
|
||||
foreach ($classes as $className => $class) {
|
||||
if (\strpos($className, 'anonymous') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($class['package']['namespace'])) {
|
||||
$className = $class['package']['namespace'] . '\\' . $className;
|
||||
}
|
||||
|
||||
$this->classes[$className] = [
|
||||
'className' => $className,
|
||||
'methods' => [],
|
||||
'startLine' => $class['startLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'ccn' => 0,
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'package' => $class['package'],
|
||||
'link' => $link . $class['startLine'],
|
||||
'className' => $className,
|
||||
'namespace' => $class['namespace'],
|
||||
'methods' => [],
|
||||
'startLine' => $class['startLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'executableBranches' => 0,
|
||||
'executedBranches' => 0,
|
||||
'executablePaths' => 0,
|
||||
'executedPaths' => 0,
|
||||
'ccn' => 0,
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'link' => $link . $class['startLine'],
|
||||
];
|
||||
|
||||
foreach ($class['methods'] as $methodName => $method) {
|
||||
if (\strpos($methodName, 'anonymous') === 0) {
|
||||
continue;
|
||||
}
|
||||
$methodData = $this->newMethod($className, $methodName, $method, $link);
|
||||
$this->classes[$className]['methods'][$methodName] = $methodData;
|
||||
|
||||
$this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);
|
||||
$this->classes[$className]['executableBranches'] += $methodData['executableBranches'];
|
||||
$this->classes[$className]['executedBranches'] += $methodData['executedBranches'];
|
||||
$this->classes[$className]['executablePaths'] += $methodData['executablePaths'];
|
||||
$this->classes[$className]['executedPaths'] += $methodData['executedPaths'];
|
||||
|
||||
foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) {
|
||||
$this->numExecutableBranches += $methodData['executableBranches'];
|
||||
$this->numExecutedBranches += $methodData['executedBranches'];
|
||||
$this->numExecutablePaths += $methodData['executablePaths'];
|
||||
$this->numExecutedPaths += $methodData['executedPaths'];
|
||||
|
||||
foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
|
||||
$this->codeUnitsByLine[$lineNumber] = [
|
||||
&$this->classes[$className],
|
||||
&$this->classes[$className]['methods'][$methodName],
|
||||
@@ -512,33 +480,43 @@ final class File extends AbstractNode
|
||||
}
|
||||
}
|
||||
|
||||
private function processTraits(\PHP_Token_Stream $tokens): void
|
||||
private function processTraits(array $traits): void
|
||||
{
|
||||
$traits = $tokens->getTraits();
|
||||
$link = $this->getId() . '.html#';
|
||||
$link = $this->id() . '.html#';
|
||||
|
||||
foreach ($traits as $traitName => $trait) {
|
||||
$this->traits[$traitName] = [
|
||||
'traitName' => $traitName,
|
||||
'methods' => [],
|
||||
'startLine' => $trait['startLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'ccn' => 0,
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'package' => $trait['package'],
|
||||
'link' => $link . $trait['startLine'],
|
||||
'traitName' => $traitName,
|
||||
'namespace' => $trait['namespace'],
|
||||
'methods' => [],
|
||||
'startLine' => $trait['startLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'executableBranches' => 0,
|
||||
'executedBranches' => 0,
|
||||
'executablePaths' => 0,
|
||||
'executedPaths' => 0,
|
||||
'ccn' => 0,
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'link' => $link . $trait['startLine'],
|
||||
];
|
||||
|
||||
foreach ($trait['methods'] as $methodName => $method) {
|
||||
if (\strpos($methodName, 'anonymous') === 0) {
|
||||
continue;
|
||||
}
|
||||
$methodData = $this->newMethod($traitName, $methodName, $method, $link);
|
||||
$this->traits[$traitName]['methods'][$methodName] = $methodData;
|
||||
|
||||
$this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link);
|
||||
$this->traits[$traitName]['executableBranches'] += $methodData['executableBranches'];
|
||||
$this->traits[$traitName]['executedBranches'] += $methodData['executedBranches'];
|
||||
$this->traits[$traitName]['executablePaths'] += $methodData['executablePaths'];
|
||||
$this->traits[$traitName]['executedPaths'] += $methodData['executedPaths'];
|
||||
|
||||
foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) {
|
||||
$this->numExecutableBranches += $methodData['executableBranches'];
|
||||
$this->numExecutedBranches += $methodData['executedBranches'];
|
||||
$this->numExecutablePaths += $methodData['executablePaths'];
|
||||
$this->numExecutedPaths += $methodData['executedPaths'];
|
||||
|
||||
foreach (range($method['startLine'], $method['endLine']) as $lineNumber) {
|
||||
$this->codeUnitsByLine[$lineNumber] = [
|
||||
&$this->traits[$traitName],
|
||||
&$this->traits[$traitName]['methods'][$methodName],
|
||||
@@ -548,64 +526,126 @@ final class File extends AbstractNode
|
||||
}
|
||||
}
|
||||
|
||||
private function processFunctions(\PHP_Token_Stream $tokens): void
|
||||
private function processFunctions(array $functions): void
|
||||
{
|
||||
$functions = $tokens->getFunctions();
|
||||
$link = $this->getId() . '.html#';
|
||||
$link = $this->id() . '.html#';
|
||||
|
||||
foreach ($functions as $functionName => $function) {
|
||||
if (\strpos($functionName, 'anonymous') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->functions[$functionName] = [
|
||||
'functionName' => $functionName,
|
||||
'signature' => $function['signature'],
|
||||
'startLine' => $function['startLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'ccn' => $function['ccn'],
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'link' => $link . $function['startLine'],
|
||||
'functionName' => $functionName,
|
||||
'namespace' => $function['namespace'],
|
||||
'signature' => $function['signature'],
|
||||
'startLine' => $function['startLine'],
|
||||
'endLine' => $function['endLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'executableBranches' => 0,
|
||||
'executedBranches' => 0,
|
||||
'executablePaths' => 0,
|
||||
'executedPaths' => 0,
|
||||
'ccn' => $function['ccn'],
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'link' => $link . $function['startLine'],
|
||||
];
|
||||
|
||||
foreach (\range($function['startLine'], $function['endLine']) as $lineNumber) {
|
||||
foreach (range($function['startLine'], $function['endLine']) as $lineNumber) {
|
||||
$this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]];
|
||||
}
|
||||
|
||||
if (isset($this->functionCoverageData[$functionName]['branches'])) {
|
||||
$this->functions[$functionName]['executableBranches'] = count(
|
||||
$this->functionCoverageData[$functionName]['branches']
|
||||
);
|
||||
|
||||
$this->functions[$functionName]['executedBranches'] = count(
|
||||
array_filter(
|
||||
$this->functionCoverageData[$functionName]['branches'],
|
||||
static function (array $branch)
|
||||
{
|
||||
return (bool) $branch['hit'];
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($this->functionCoverageData[$functionName]['paths'])) {
|
||||
$this->functions[$functionName]['executablePaths'] = count(
|
||||
$this->functionCoverageData[$functionName]['paths']
|
||||
);
|
||||
|
||||
$this->functions[$functionName]['executedPaths'] = count(
|
||||
array_filter(
|
||||
$this->functionCoverageData[$functionName]['paths'],
|
||||
static function (array $path)
|
||||
{
|
||||
return (bool) $path['hit'];
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->numExecutableBranches += $this->functions[$functionName]['executableBranches'];
|
||||
$this->numExecutedBranches += $this->functions[$functionName]['executedBranches'];
|
||||
$this->numExecutablePaths += $this->functions[$functionName]['executablePaths'];
|
||||
$this->numExecutedPaths += $this->functions[$functionName]['executedPaths'];
|
||||
}
|
||||
}
|
||||
|
||||
private function crap(int $ccn, float $coverage): string
|
||||
private function newMethod(string $className, string $methodName, array $method, string $link): array
|
||||
{
|
||||
if ($coverage === 0.0) {
|
||||
return (string) ($ccn ** 2 + $ccn);
|
||||
}
|
||||
|
||||
if ($coverage >= 95) {
|
||||
return (string) $ccn;
|
||||
}
|
||||
|
||||
return \sprintf(
|
||||
'%01.2F',
|
||||
$ccn ** 2 * (1 - $coverage / 100) ** 3 + $ccn
|
||||
);
|
||||
}
|
||||
|
||||
private function newMethod(string $methodName, array $method, string $link): array
|
||||
{
|
||||
return [
|
||||
'methodName' => $methodName,
|
||||
'visibility' => $method['visibility'],
|
||||
'signature' => $method['signature'],
|
||||
'startLine' => $method['startLine'],
|
||||
'endLine' => $method['endLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'ccn' => $method['ccn'],
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'link' => $link . $method['startLine'],
|
||||
$methodData = [
|
||||
'methodName' => $methodName,
|
||||
'visibility' => $method['visibility'],
|
||||
'signature' => $method['signature'],
|
||||
'startLine' => $method['startLine'],
|
||||
'endLine' => $method['endLine'],
|
||||
'executableLines' => 0,
|
||||
'executedLines' => 0,
|
||||
'executableBranches' => 0,
|
||||
'executedBranches' => 0,
|
||||
'executablePaths' => 0,
|
||||
'executedPaths' => 0,
|
||||
'ccn' => $method['ccn'],
|
||||
'coverage' => 0,
|
||||
'crap' => 0,
|
||||
'link' => $link . $method['startLine'],
|
||||
];
|
||||
|
||||
$key = $className . '->' . $methodName;
|
||||
|
||||
if (isset($this->functionCoverageData[$key]['branches'])) {
|
||||
$methodData['executableBranches'] = count(
|
||||
$this->functionCoverageData[$key]['branches']
|
||||
);
|
||||
|
||||
$methodData['executedBranches'] = count(
|
||||
array_filter(
|
||||
$this->functionCoverageData[$key]['branches'],
|
||||
static function (array $branch)
|
||||
{
|
||||
return (bool) $branch['hit'];
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($this->functionCoverageData[$key]['paths'])) {
|
||||
$methodData['executablePaths'] = count(
|
||||
$this->functionCoverageData[$key]['paths']
|
||||
);
|
||||
|
||||
$methodData['executedPaths'] = count(
|
||||
array_filter(
|
||||
$this->functionCoverageData[$key]['paths'],
|
||||
static function (array $path)
|
||||
{
|
||||
return (bool) $path['hit'];
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $methodData;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,10 +9,13 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Node;
|
||||
|
||||
use function count;
|
||||
use RecursiveIterator;
|
||||
|
||||
/**
|
||||
* Recursive iterator for node object graphs.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Iterator implements \RecursiveIterator
|
||||
final class Iterator implements RecursiveIterator
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
@@ -26,7 +29,7 @@ final class Iterator implements \RecursiveIterator
|
||||
|
||||
public function __construct(Directory $node)
|
||||
{
|
||||
$this->nodes = $node->getChildNodes();
|
||||
$this->nodes = $node->children();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +45,7 @@ final class Iterator implements \RecursiveIterator
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return $this->position < \count($this->nodes);
|
||||
return $this->position < count($this->nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +59,7 @@ final class Iterator implements \RecursiveIterator
|
||||
/**
|
||||
* Returns the current element.
|
||||
*/
|
||||
public function current(): AbstractNode
|
||||
public function current(): ?AbstractNode
|
||||
{
|
||||
return $this->valid() ? $this->nodes[$this->position] : null;
|
||||
}
|
||||
@@ -71,8 +74,6 @@ final class Iterator implements \RecursiveIterator
|
||||
|
||||
/**
|
||||
* Returns the sub iterator for the current element.
|
||||
*
|
||||
* @return Iterator
|
||||
*/
|
||||
public function getChildren(): self
|
||||
{
|
||||
|
255
vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php
vendored
Normal file
255
vendor/phpunit/php-code-coverage/src/ProcessedCodeCoverageData.php
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use function array_key_exists;
|
||||
use function array_keys;
|
||||
use function array_merge;
|
||||
use function array_unique;
|
||||
use function count;
|
||||
use function is_array;
|
||||
use function ksort;
|
||||
use SebastianBergmann\CodeCoverage\Driver\Driver;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class ProcessedCodeCoverageData
|
||||
{
|
||||
/**
|
||||
* Line coverage data.
|
||||
* An array of filenames, each having an array of linenumbers, each executable line having an array of testcase ids.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $lineCoverage = [];
|
||||
|
||||
/**
|
||||
* Function coverage data.
|
||||
* Maintains base format of raw data (@see https://xdebug.org/docs/code_coverage), but each 'hit' entry is an array
|
||||
* of testcase ids.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $functionCoverage = [];
|
||||
|
||||
public function initializeUnseenData(RawCodeCoverageData $rawData): void
|
||||
{
|
||||
foreach ($rawData->lineCoverage() as $file => $lines) {
|
||||
if (!isset($this->lineCoverage[$file])) {
|
||||
$this->lineCoverage[$file] = [];
|
||||
|
||||
foreach ($lines as $k => $v) {
|
||||
$this->lineCoverage[$file][$k] = $v === Driver::LINE_NOT_EXECUTABLE ? null : [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rawData->functionCoverage() as $file => $functions) {
|
||||
foreach ($functions as $functionName => $functionData) {
|
||||
if (isset($this->functionCoverage[$file][$functionName])) {
|
||||
$this->initPreviouslySeenFunction($file, $functionName, $functionData);
|
||||
} else {
|
||||
$this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function markCodeAsExecutedByTestCase(string $testCaseId, RawCodeCoverageData $executedCode): void
|
||||
{
|
||||
foreach ($executedCode->lineCoverage() as $file => $lines) {
|
||||
foreach ($lines as $k => $v) {
|
||||
if ($v === Driver::LINE_EXECUTED) {
|
||||
$this->lineCoverage[$file][$k][] = $testCaseId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($executedCode->functionCoverage() as $file => $functions) {
|
||||
foreach ($functions as $functionName => $functionData) {
|
||||
foreach ($functionData['branches'] as $branchId => $branchData) {
|
||||
if ($branchData['hit'] === Driver::BRANCH_HIT) {
|
||||
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'][] = $testCaseId;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($functionData['paths'] as $pathId => $pathData) {
|
||||
if ($pathData['hit'] === Driver::BRANCH_HIT) {
|
||||
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'][] = $testCaseId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setLineCoverage(array $lineCoverage): void
|
||||
{
|
||||
$this->lineCoverage = $lineCoverage;
|
||||
}
|
||||
|
||||
public function lineCoverage(): array
|
||||
{
|
||||
ksort($this->lineCoverage);
|
||||
|
||||
return $this->lineCoverage;
|
||||
}
|
||||
|
||||
public function setFunctionCoverage(array $functionCoverage): void
|
||||
{
|
||||
$this->functionCoverage = $functionCoverage;
|
||||
}
|
||||
|
||||
public function functionCoverage(): array
|
||||
{
|
||||
ksort($this->functionCoverage);
|
||||
|
||||
return $this->functionCoverage;
|
||||
}
|
||||
|
||||
public function coveredFiles(): array
|
||||
{
|
||||
ksort($this->lineCoverage);
|
||||
|
||||
return array_keys($this->lineCoverage);
|
||||
}
|
||||
|
||||
public function renameFile(string $oldFile, string $newFile): void
|
||||
{
|
||||
$this->lineCoverage[$newFile] = $this->lineCoverage[$oldFile];
|
||||
|
||||
if (isset($this->functionCoverage[$oldFile])) {
|
||||
$this->functionCoverage[$newFile] = $this->functionCoverage[$oldFile];
|
||||
}
|
||||
|
||||
unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]);
|
||||
}
|
||||
|
||||
public function merge(self $newData): void
|
||||
{
|
||||
foreach ($newData->lineCoverage as $file => $lines) {
|
||||
if (!isset($this->lineCoverage[$file])) {
|
||||
$this->lineCoverage[$file] = $lines;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// we should compare the lines if any of two contains data
|
||||
$compareLineNumbers = array_unique(
|
||||
array_merge(
|
||||
array_keys($this->lineCoverage[$file]),
|
||||
array_keys($newData->lineCoverage[$file])
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($compareLineNumbers as $line) {
|
||||
$thatPriority = $this->priorityForLine($newData->lineCoverage[$file], $line);
|
||||
$thisPriority = $this->priorityForLine($this->lineCoverage[$file], $line);
|
||||
|
||||
if ($thatPriority > $thisPriority) {
|
||||
$this->lineCoverage[$file][$line] = $newData->lineCoverage[$file][$line];
|
||||
} elseif ($thatPriority === $thisPriority && is_array($this->lineCoverage[$file][$line])) {
|
||||
$this->lineCoverage[$file][$line] = array_unique(
|
||||
array_merge($this->lineCoverage[$file][$line], $newData->lineCoverage[$file][$line])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($newData->functionCoverage as $file => $functions) {
|
||||
if (!isset($this->functionCoverage[$file])) {
|
||||
$this->functionCoverage[$file] = $functions;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($functions as $functionName => $functionData) {
|
||||
if (isset($this->functionCoverage[$file][$functionName])) {
|
||||
$this->initPreviouslySeenFunction($file, $functionName, $functionData);
|
||||
} else {
|
||||
$this->initPreviouslyUnseenFunction($file, $functionName, $functionData);
|
||||
}
|
||||
|
||||
foreach ($functionData['branches'] as $branchId => $branchData) {
|
||||
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'], $branchData['hit']));
|
||||
}
|
||||
|
||||
foreach ($functionData['paths'] as $pathId => $pathData) {
|
||||
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = array_unique(array_merge($this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'], $pathData['hit']));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the priority for a line.
|
||||
*
|
||||
* 1 = the line is not set
|
||||
* 2 = the line has not been tested
|
||||
* 3 = the line is dead code
|
||||
* 4 = the line has been tested
|
||||
*
|
||||
* During a merge, a higher number is better.
|
||||
*/
|
||||
private function priorityForLine(array $data, int $line): int
|
||||
{
|
||||
if (!array_key_exists($line, $data)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_array($data[$line]) && count($data[$line]) === 0) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ($data[$line] === null) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a function we have never seen before, copy all data over and simply init the 'hit' array.
|
||||
*/
|
||||
private function initPreviouslyUnseenFunction(string $file, string $functionName, array $functionData): void
|
||||
{
|
||||
$this->functionCoverage[$file][$functionName] = $functionData;
|
||||
|
||||
foreach (array_keys($functionData['branches']) as $branchId) {
|
||||
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
|
||||
}
|
||||
|
||||
foreach (array_keys($functionData['paths']) as $pathId) {
|
||||
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a function we have seen before, only copy over and init the 'hit' array for any unseen branches and paths.
|
||||
* Techniques such as mocking and where the contents of a file are different vary during tests (e.g. compiling
|
||||
* containers) mean that the functions inside a file cannot be relied upon to be static.
|
||||
*/
|
||||
private function initPreviouslySeenFunction(string $file, string $functionName, array $functionData): void
|
||||
{
|
||||
foreach ($functionData['branches'] as $branchId => $branchData) {
|
||||
if (!isset($this->functionCoverage[$file][$functionName]['branches'][$branchId])) {
|
||||
$this->functionCoverage[$file][$functionName]['branches'][$branchId] = $branchData;
|
||||
$this->functionCoverage[$file][$functionName]['branches'][$branchId]['hit'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($functionData['paths'] as $pathId => $pathData) {
|
||||
if (!isset($this->functionCoverage[$file][$functionName]['paths'][$pathId])) {
|
||||
$this->functionCoverage[$file][$functionName]['paths'][$pathId] = $pathData;
|
||||
$this->functionCoverage[$file][$functionName]['paths'][$pathId]['hit'] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
274
vendor/phpunit/php-code-coverage/src/RawCodeCoverageData.php
vendored
Normal file
274
vendor/phpunit/php-code-coverage/src/RawCodeCoverageData.php
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use function array_diff;
|
||||
use function array_diff_key;
|
||||
use function array_flip;
|
||||
use function array_intersect;
|
||||
use function array_intersect_key;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function file_get_contents;
|
||||
use function in_array;
|
||||
use function is_file;
|
||||
use function range;
|
||||
use function trim;
|
||||
use SebastianBergmann\CodeCoverage\Driver\Driver;
|
||||
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class RawCodeCoverageData
|
||||
{
|
||||
/**
|
||||
* @var array<string, array<int>>
|
||||
*/
|
||||
private static $emptyLineCache = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*
|
||||
* @see https://xdebug.org/docs/code_coverage for format
|
||||
*/
|
||||
private $lineCoverage;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*
|
||||
* @see https://xdebug.org/docs/code_coverage for format
|
||||
*/
|
||||
private $functionCoverage;
|
||||
|
||||
public static function fromXdebugWithoutPathCoverage(array $rawCoverage): self
|
||||
{
|
||||
return new self($rawCoverage, []);
|
||||
}
|
||||
|
||||
public static function fromXdebugWithPathCoverage(array $rawCoverage): self
|
||||
{
|
||||
$lineCoverage = [];
|
||||
$functionCoverage = [];
|
||||
|
||||
foreach ($rawCoverage as $file => $fileCoverageData) {
|
||||
$lineCoverage[$file] = $fileCoverageData['lines'];
|
||||
$functionCoverage[$file] = $fileCoverageData['functions'];
|
||||
}
|
||||
|
||||
return new self($lineCoverage, $functionCoverage);
|
||||
}
|
||||
|
||||
public static function fromXdebugWithMixedCoverage(array $rawCoverage): self
|
||||
{
|
||||
$lineCoverage = [];
|
||||
$functionCoverage = [];
|
||||
|
||||
foreach ($rawCoverage as $file => $fileCoverageData) {
|
||||
if (!isset($fileCoverageData['functions'])) {
|
||||
// Current file does not have functions, so line coverage
|
||||
// is stored in $fileCoverageData, not in $fileCoverageData['lines']
|
||||
$lineCoverage[$file] = $fileCoverageData;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$lineCoverage[$file] = $fileCoverageData['lines'];
|
||||
$functionCoverage[$file] = $fileCoverageData['functions'];
|
||||
}
|
||||
|
||||
return new self($lineCoverage, $functionCoverage);
|
||||
}
|
||||
|
||||
public static function fromUncoveredFile(string $filename, FileAnalyser $analyser): self
|
||||
{
|
||||
$lineCoverage = [];
|
||||
|
||||
foreach ($analyser->executableLinesIn($filename) as $line => $branch) {
|
||||
$lineCoverage[$line] = Driver::LINE_NOT_EXECUTED;
|
||||
}
|
||||
|
||||
return new self([$filename => $lineCoverage], []);
|
||||
}
|
||||
|
||||
private function __construct(array $lineCoverage, array $functionCoverage)
|
||||
{
|
||||
$this->lineCoverage = $lineCoverage;
|
||||
$this->functionCoverage = $functionCoverage;
|
||||
|
||||
$this->skipEmptyLines();
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->lineCoverage = $this->functionCoverage = [];
|
||||
}
|
||||
|
||||
public function lineCoverage(): array
|
||||
{
|
||||
return $this->lineCoverage;
|
||||
}
|
||||
|
||||
public function functionCoverage(): array
|
||||
{
|
||||
return $this->functionCoverage;
|
||||
}
|
||||
|
||||
public function removeCoverageDataForFile(string $filename): void
|
||||
{
|
||||
unset($this->lineCoverage[$filename], $this->functionCoverage[$filename]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $lines
|
||||
*/
|
||||
public function keepLineCoverageDataOnlyForLines(string $filename, array $lines): void
|
||||
{
|
||||
if (!isset($this->lineCoverage[$filename])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lineCoverage[$filename] = array_intersect_key(
|
||||
$this->lineCoverage[$filename],
|
||||
array_flip($lines)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $linesToBranchMap
|
||||
*/
|
||||
public function markExecutableLineByBranch(string $filename, array $linesToBranchMap): void
|
||||
{
|
||||
if (!isset($this->lineCoverage[$filename])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$linesByBranch = [];
|
||||
|
||||
foreach ($linesToBranchMap as $line => $branch) {
|
||||
$linesByBranch[$branch][] = $line;
|
||||
}
|
||||
|
||||
foreach ($this->lineCoverage[$filename] as $line => $lineStatus) {
|
||||
if (!isset($linesToBranchMap[$line])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$branch = $linesToBranchMap[$line];
|
||||
|
||||
if (!isset($linesByBranch[$branch])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($linesByBranch[$branch] as $lineInBranch) {
|
||||
$this->lineCoverage[$filename][$lineInBranch] = $lineStatus;
|
||||
}
|
||||
|
||||
if (Driver::LINE_EXECUTED === $lineStatus) {
|
||||
unset($linesByBranch[$branch]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $lines
|
||||
*/
|
||||
public function keepFunctionCoverageDataOnlyForLines(string $filename, array $lines): void
|
||||
{
|
||||
if (!isset($this->functionCoverage[$filename])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->functionCoverage[$filename] as $functionName => $functionData) {
|
||||
foreach ($functionData['branches'] as $branchId => $branch) {
|
||||
if (count(array_diff(range($branch['line_start'], $branch['line_end']), $lines)) > 0) {
|
||||
unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]);
|
||||
|
||||
foreach ($functionData['paths'] as $pathId => $path) {
|
||||
if (in_array($branchId, $path['path'], true)) {
|
||||
unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $lines
|
||||
*/
|
||||
public function removeCoverageDataForLines(string $filename, array $lines): void
|
||||
{
|
||||
if (empty($lines)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($this->lineCoverage[$filename])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lineCoverage[$filename] = array_diff_key(
|
||||
$this->lineCoverage[$filename],
|
||||
array_flip($lines)
|
||||
);
|
||||
|
||||
if (isset($this->functionCoverage[$filename])) {
|
||||
foreach ($this->functionCoverage[$filename] as $functionName => $functionData) {
|
||||
foreach ($functionData['branches'] as $branchId => $branch) {
|
||||
if (count(array_intersect($lines, range($branch['line_start'], $branch['line_end']))) > 0) {
|
||||
unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]);
|
||||
|
||||
foreach ($functionData['paths'] as $pathId => $path) {
|
||||
if (in_array($branchId, $path['path'], true)) {
|
||||
unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* At the end of a file, the PHP interpreter always sees an implicit return. Where this occurs in a file that has
|
||||
* e.g. a class definition, that line cannot be invoked from a test and results in confusing coverage. This engine
|
||||
* implementation detail therefore needs to be masked which is done here by simply ensuring that all empty lines
|
||||
* are skipped over for coverage purposes.
|
||||
*
|
||||
* @see https://github.com/sebastianbergmann/php-code-coverage/issues/799
|
||||
*/
|
||||
private function skipEmptyLines(): void
|
||||
{
|
||||
foreach ($this->lineCoverage as $filename => $coverage) {
|
||||
foreach ($this->getEmptyLinesForFile($filename) as $emptyLine) {
|
||||
unset($this->lineCoverage[$filename][$emptyLine]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getEmptyLinesForFile(string $filename): array
|
||||
{
|
||||
if (!isset(self::$emptyLineCache[$filename])) {
|
||||
self::$emptyLineCache[$filename] = [];
|
||||
|
||||
if (is_file($filename)) {
|
||||
$sourceLines = explode("\n", file_get_contents($filename));
|
||||
|
||||
foreach ($sourceLines as $line => $source) {
|
||||
if (trim($source) === '') {
|
||||
self::$emptyLineCache[$filename][] = ($line + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::$emptyLineCache[$filename];
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,31 +9,40 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report;
|
||||
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_put_contents;
|
||||
use function is_string;
|
||||
use function ksort;
|
||||
use function max;
|
||||
use function range;
|
||||
use function time;
|
||||
use DOMDocument;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
|
||||
use SebastianBergmann\CodeCoverage\Node\File;
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||
|
||||
/**
|
||||
* Generates a Clover XML logfile from a code coverage object.
|
||||
*/
|
||||
final class Clover
|
||||
{
|
||||
/**
|
||||
* @throws \RuntimeException
|
||||
* @throws WriteOperationFailedException
|
||||
*/
|
||||
public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string
|
||||
{
|
||||
$xmlDocument = new \DOMDocument('1.0', 'UTF-8');
|
||||
$time = (string) time();
|
||||
|
||||
$xmlDocument = new DOMDocument('1.0', 'UTF-8');
|
||||
$xmlDocument->formatOutput = true;
|
||||
|
||||
$xmlCoverage = $xmlDocument->createElement('coverage');
|
||||
$xmlCoverage->setAttribute('generated', (string) $_SERVER['REQUEST_TIME']);
|
||||
$xmlCoverage->setAttribute('generated', $time);
|
||||
$xmlDocument->appendChild($xmlCoverage);
|
||||
|
||||
$xmlProject = $xmlDocument->createElement('project');
|
||||
$xmlProject->setAttribute('timestamp', (string) $_SERVER['REQUEST_TIME']);
|
||||
$xmlProject->setAttribute('timestamp', $time);
|
||||
|
||||
if (\is_string($name)) {
|
||||
if (is_string($name)) {
|
||||
$xmlProject->setAttribute('name', $name);
|
||||
}
|
||||
|
||||
@@ -50,10 +59,10 @@ final class Clover
|
||||
/* @var File $item */
|
||||
|
||||
$xmlFile = $xmlDocument->createElement('file');
|
||||
$xmlFile->setAttribute('name', $item->getPath());
|
||||
$xmlFile->setAttribute('name', $item->pathAsString());
|
||||
|
||||
$classes = $item->getClassesAndTraits();
|
||||
$coverageData = $item->getCoverageData();
|
||||
$classes = $item->classesAndTraits();
|
||||
$coverageData = $item->lineCoverageData();
|
||||
$lines = [];
|
||||
$namespace = 'global';
|
||||
|
||||
@@ -78,19 +87,19 @@ final class Clover
|
||||
|
||||
$methodCount = 0;
|
||||
|
||||
foreach (\range($method['startLine'], $method['endLine']) as $line) {
|
||||
foreach (range($method['startLine'], $method['endLine']) as $line) {
|
||||
if (isset($coverageData[$line]) && ($coverageData[$line] !== null)) {
|
||||
$methodCount = \max($methodCount, \count($coverageData[$line]));
|
||||
$methodCount = max($methodCount, count($coverageData[$line]));
|
||||
}
|
||||
}
|
||||
|
||||
$lines[$method['startLine']] = [
|
||||
'ccn' => $method['ccn'],
|
||||
'count' => $methodCount,
|
||||
'crap' => $method['crap'],
|
||||
'type' => 'method',
|
||||
'visibility' => $method['visibility'],
|
||||
'name' => $methodName,
|
||||
'ccn' => $method['ccn'],
|
||||
'count' => $methodCount,
|
||||
'crap' => $method['crap'],
|
||||
'type' => 'method',
|
||||
'visibility' => $method['visibility'],
|
||||
'name' => $methodName,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -136,12 +145,12 @@ final class Clover
|
||||
$xmlMetrics->setAttribute('complexity', (string) $class['ccn']);
|
||||
$xmlMetrics->setAttribute('methods', (string) $classMethods);
|
||||
$xmlMetrics->setAttribute('coveredmethods', (string) $coveredMethods);
|
||||
$xmlMetrics->setAttribute('conditionals', '0');
|
||||
$xmlMetrics->setAttribute('coveredconditionals', '0');
|
||||
$xmlMetrics->setAttribute('conditionals', (string) $class['executableBranches']);
|
||||
$xmlMetrics->setAttribute('coveredconditionals', (string) $class['executedBranches']);
|
||||
$xmlMetrics->setAttribute('statements', (string) $classStatements);
|
||||
$xmlMetrics->setAttribute('coveredstatements', (string) $coveredClassStatements);
|
||||
$xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements /* + conditionals */));
|
||||
$xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements /* + coveredconditionals */));
|
||||
$xmlMetrics->setAttribute('elements', (string) ($classMethods + $classStatements + $class['executableBranches']));
|
||||
$xmlMetrics->setAttribute('coveredelements', (string) ($coveredMethods + $coveredClassStatements + $class['executedBranches']));
|
||||
$xmlClass->appendChild($xmlMetrics);
|
||||
}
|
||||
|
||||
@@ -151,11 +160,11 @@ final class Clover
|
||||
}
|
||||
|
||||
$lines[$line] = [
|
||||
'count' => \count($data), 'type' => 'stmt',
|
||||
'count' => count($data), 'type' => 'stmt',
|
||||
];
|
||||
}
|
||||
|
||||
\ksort($lines);
|
||||
ksort($lines);
|
||||
|
||||
foreach ($lines as $line => $data) {
|
||||
$xmlLine = $xmlDocument->createElement('line');
|
||||
@@ -182,20 +191,20 @@ final class Clover
|
||||
$xmlFile->appendChild($xmlLine);
|
||||
}
|
||||
|
||||
$linesOfCode = $item->getLinesOfCode();
|
||||
$linesOfCode = $item->linesOfCode();
|
||||
|
||||
$xmlMetrics = $xmlDocument->createElement('metrics');
|
||||
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['loc']);
|
||||
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['ncloc']);
|
||||
$xmlMetrics->setAttribute('classes', (string) $item->getNumClassesAndTraits());
|
||||
$xmlMetrics->setAttribute('methods', (string) $item->getNumMethods());
|
||||
$xmlMetrics->setAttribute('coveredmethods', (string) $item->getNumTestedMethods());
|
||||
$xmlMetrics->setAttribute('conditionals', '0');
|
||||
$xmlMetrics->setAttribute('coveredconditionals', '0');
|
||||
$xmlMetrics->setAttribute('statements', (string) $item->getNumExecutableLines());
|
||||
$xmlMetrics->setAttribute('coveredstatements', (string) $item->getNumExecutedLines());
|
||||
$xmlMetrics->setAttribute('elements', (string) ($item->getNumMethods() + $item->getNumExecutableLines() /* + conditionals */));
|
||||
$xmlMetrics->setAttribute('coveredelements', (string) ($item->getNumTestedMethods() + $item->getNumExecutedLines() /* + coveredconditionals */));
|
||||
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['linesOfCode']);
|
||||
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['nonCommentLinesOfCode']);
|
||||
$xmlMetrics->setAttribute('classes', (string) $item->numberOfClassesAndTraits());
|
||||
$xmlMetrics->setAttribute('methods', (string) $item->numberOfMethods());
|
||||
$xmlMetrics->setAttribute('coveredmethods', (string) $item->numberOfTestedMethods());
|
||||
$xmlMetrics->setAttribute('conditionals', (string) $item->numberOfExecutableBranches());
|
||||
$xmlMetrics->setAttribute('coveredconditionals', (string) $item->numberOfExecutedBranches());
|
||||
$xmlMetrics->setAttribute('statements', (string) $item->numberOfExecutableLines());
|
||||
$xmlMetrics->setAttribute('coveredstatements', (string) $item->numberOfExecutedLines());
|
||||
$xmlMetrics->setAttribute('elements', (string) ($item->numberOfMethods() + $item->numberOfExecutableLines() + $item->numberOfExecutableBranches()));
|
||||
$xmlMetrics->setAttribute('coveredelements', (string) ($item->numberOfTestedMethods() + $item->numberOfExecutedLines() + $item->numberOfExecutedBranches()));
|
||||
$xmlFile->appendChild($xmlMetrics);
|
||||
|
||||
if ($namespace === 'global') {
|
||||
@@ -214,45 +223,33 @@ final class Clover
|
||||
}
|
||||
}
|
||||
|
||||
$linesOfCode = $report->getLinesOfCode();
|
||||
$linesOfCode = $report->linesOfCode();
|
||||
|
||||
$xmlMetrics = $xmlDocument->createElement('metrics');
|
||||
$xmlMetrics->setAttribute('files', (string) \count($report));
|
||||
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['loc']);
|
||||
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['ncloc']);
|
||||
$xmlMetrics->setAttribute('classes', (string) $report->getNumClassesAndTraits());
|
||||
$xmlMetrics->setAttribute('methods', (string) $report->getNumMethods());
|
||||
$xmlMetrics->setAttribute('coveredmethods', (string) $report->getNumTestedMethods());
|
||||
$xmlMetrics->setAttribute('conditionals', '0');
|
||||
$xmlMetrics->setAttribute('coveredconditionals', '0');
|
||||
$xmlMetrics->setAttribute('statements', (string) $report->getNumExecutableLines());
|
||||
$xmlMetrics->setAttribute('coveredstatements', (string) $report->getNumExecutedLines());
|
||||
$xmlMetrics->setAttribute('elements', (string) ($report->getNumMethods() + $report->getNumExecutableLines() /* + conditionals */));
|
||||
$xmlMetrics->setAttribute('coveredelements', (string) ($report->getNumTestedMethods() + $report->getNumExecutedLines() /* + coveredconditionals */));
|
||||
$xmlMetrics->setAttribute('files', (string) count($report));
|
||||
$xmlMetrics->setAttribute('loc', (string) $linesOfCode['linesOfCode']);
|
||||
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode['nonCommentLinesOfCode']);
|
||||
$xmlMetrics->setAttribute('classes', (string) $report->numberOfClassesAndTraits());
|
||||
$xmlMetrics->setAttribute('methods', (string) $report->numberOfMethods());
|
||||
$xmlMetrics->setAttribute('coveredmethods', (string) $report->numberOfTestedMethods());
|
||||
$xmlMetrics->setAttribute('conditionals', (string) $report->numberOfExecutableBranches());
|
||||
$xmlMetrics->setAttribute('coveredconditionals', (string) $report->numberOfExecutedBranches());
|
||||
$xmlMetrics->setAttribute('statements', (string) $report->numberOfExecutableLines());
|
||||
$xmlMetrics->setAttribute('coveredstatements', (string) $report->numberOfExecutedLines());
|
||||
$xmlMetrics->setAttribute('elements', (string) ($report->numberOfMethods() + $report->numberOfExecutableLines() + $report->numberOfExecutableBranches()));
|
||||
$xmlMetrics->setAttribute('coveredelements', (string) ($report->numberOfTestedMethods() + $report->numberOfExecutedLines() + $report->numberOfExecutedBranches()));
|
||||
$xmlProject->appendChild($xmlMetrics);
|
||||
|
||||
$buffer = $xmlDocument->saveXML();
|
||||
|
||||
if ($target !== null) {
|
||||
if (!$this->createDirectory(\dirname($target))) {
|
||||
throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target)));
|
||||
}
|
||||
Filesystem::createDirectory(dirname($target));
|
||||
|
||||
if (@\file_put_contents($target, $buffer) === false) {
|
||||
throw new RuntimeException(
|
||||
\sprintf(
|
||||
'Could not write to "%s',
|
||||
$target
|
||||
)
|
||||
);
|
||||
if (@file_put_contents($target, $buffer) === false) {
|
||||
throw new WriteOperationFailedException($target);
|
||||
}
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
private function createDirectory(string $directory): bool
|
||||
{
|
||||
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
|
||||
}
|
||||
}
|
||||
|
306
vendor/phpunit/php-code-coverage/src/Report/Cobertura.php
vendored
Normal file
306
vendor/phpunit/php-code-coverage/src/Report/Cobertura.php
vendored
Normal file
@@ -0,0 +1,306 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report;
|
||||
|
||||
use function basename;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_put_contents;
|
||||
use function preg_match;
|
||||
use function range;
|
||||
use function str_replace;
|
||||
use function time;
|
||||
use DOMImplementation;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
|
||||
use SebastianBergmann\CodeCoverage\Node\File;
|
||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||
|
||||
final class Cobertura
|
||||
{
|
||||
/**
|
||||
* @throws WriteOperationFailedException
|
||||
*/
|
||||
public function process(CodeCoverage $coverage, ?string $target = null): string
|
||||
{
|
||||
$time = (string) time();
|
||||
|
||||
$report = $coverage->getReport();
|
||||
|
||||
$implementation = new DOMImplementation;
|
||||
|
||||
$documentType = $implementation->createDocumentType(
|
||||
'coverage',
|
||||
'',
|
||||
'http://cobertura.sourceforge.net/xml/coverage-04.dtd'
|
||||
);
|
||||
|
||||
$document = $implementation->createDocument('', '', $documentType);
|
||||
$document->xmlVersion = '1.0';
|
||||
$document->encoding = 'UTF-8';
|
||||
$document->formatOutput = true;
|
||||
|
||||
$coverageElement = $document->createElement('coverage');
|
||||
|
||||
$linesValid = $report->numberOfExecutableLines();
|
||||
$linesCovered = $report->numberOfExecutedLines();
|
||||
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
|
||||
$coverageElement->setAttribute('line-rate', (string) $lineRate);
|
||||
|
||||
$branchesValid = $report->numberOfExecutableBranches();
|
||||
$branchesCovered = $report->numberOfExecutedBranches();
|
||||
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);
|
||||
$coverageElement->setAttribute('branch-rate', (string) $branchRate);
|
||||
|
||||
$coverageElement->setAttribute('lines-covered', (string) $report->numberOfExecutedLines());
|
||||
$coverageElement->setAttribute('lines-valid', (string) $report->numberOfExecutableLines());
|
||||
$coverageElement->setAttribute('branches-covered', (string) $report->numberOfExecutedBranches());
|
||||
$coverageElement->setAttribute('branches-valid', (string) $report->numberOfExecutableBranches());
|
||||
$coverageElement->setAttribute('complexity', '');
|
||||
$coverageElement->setAttribute('version', '0.4');
|
||||
$coverageElement->setAttribute('timestamp', $time);
|
||||
|
||||
$document->appendChild($coverageElement);
|
||||
|
||||
$sourcesElement = $document->createElement('sources');
|
||||
$coverageElement->appendChild($sourcesElement);
|
||||
|
||||
$sourceElement = $document->createElement('source', $report->pathAsString());
|
||||
$sourcesElement->appendChild($sourceElement);
|
||||
|
||||
$packagesElement = $document->createElement('packages');
|
||||
$coverageElement->appendChild($packagesElement);
|
||||
|
||||
$complexity = 0;
|
||||
|
||||
foreach ($report as $item) {
|
||||
if (!$item instanceof File) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$packageElement = $document->createElement('package');
|
||||
$packageComplexity = 0;
|
||||
|
||||
$packageElement->setAttribute('name', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString()));
|
||||
|
||||
$linesValid = $item->numberOfExecutableLines();
|
||||
$linesCovered = $item->numberOfExecutedLines();
|
||||
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
|
||||
|
||||
$packageElement->setAttribute('line-rate', (string) $lineRate);
|
||||
|
||||
$branchesValid = $item->numberOfExecutableBranches();
|
||||
$branchesCovered = $item->numberOfExecutedBranches();
|
||||
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);
|
||||
|
||||
$packageElement->setAttribute('branch-rate', (string) $branchRate);
|
||||
|
||||
$packageElement->setAttribute('complexity', '');
|
||||
$packagesElement->appendChild($packageElement);
|
||||
|
||||
$classesElement = $document->createElement('classes');
|
||||
|
||||
$packageElement->appendChild($classesElement);
|
||||
|
||||
$classes = $item->classesAndTraits();
|
||||
$coverageData = $item->lineCoverageData();
|
||||
|
||||
foreach ($classes as $className => $class) {
|
||||
$complexity += $class['ccn'];
|
||||
$packageComplexity += $class['ccn'];
|
||||
|
||||
if (!empty($class['package']['namespace'])) {
|
||||
$className = $class['package']['namespace'] . '\\' . $className;
|
||||
}
|
||||
|
||||
$linesValid = $class['executableLines'];
|
||||
$linesCovered = $class['executedLines'];
|
||||
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
|
||||
|
||||
$branchesValid = $class['executableBranches'];
|
||||
$branchesCovered = $class['executedBranches'];
|
||||
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);
|
||||
|
||||
$classElement = $document->createElement('class');
|
||||
|
||||
$classElement->setAttribute('name', $className);
|
||||
$classElement->setAttribute('filename', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString()));
|
||||
$classElement->setAttribute('line-rate', (string) $lineRate);
|
||||
$classElement->setAttribute('branch-rate', (string) $branchRate);
|
||||
$classElement->setAttribute('complexity', (string) $class['ccn']);
|
||||
|
||||
$classesElement->appendChild($classElement);
|
||||
|
||||
$methodsElement = $document->createElement('methods');
|
||||
|
||||
$classElement->appendChild($methodsElement);
|
||||
|
||||
$classLinesElement = $document->createElement('lines');
|
||||
|
||||
$classElement->appendChild($classLinesElement);
|
||||
|
||||
foreach ($class['methods'] as $methodName => $method) {
|
||||
if ($method['executableLines'] === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
preg_match("/\((.*?)\)/", $method['signature'], $signature);
|
||||
|
||||
$linesValid = $method['executableLines'];
|
||||
$linesCovered = $method['executedLines'];
|
||||
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
|
||||
|
||||
$branchesValid = $method['executableBranches'];
|
||||
$branchesCovered = $method['executedBranches'];
|
||||
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);
|
||||
|
||||
$methodElement = $document->createElement('method');
|
||||
|
||||
$methodElement->setAttribute('name', $methodName);
|
||||
$methodElement->setAttribute('signature', $signature[1]);
|
||||
$methodElement->setAttribute('line-rate', (string) $lineRate);
|
||||
$methodElement->setAttribute('branch-rate', (string) $branchRate);
|
||||
$methodElement->setAttribute('complexity', (string) $method['ccn']);
|
||||
|
||||
$methodLinesElement = $document->createElement('lines');
|
||||
|
||||
$methodElement->appendChild($methodLinesElement);
|
||||
|
||||
foreach (range($method['startLine'], $method['endLine']) as $line) {
|
||||
if (!isset($coverageData[$line]) || $coverageData[$line] === null) {
|
||||
continue;
|
||||
}
|
||||
$methodLineElement = $document->createElement('line');
|
||||
|
||||
$methodLineElement->setAttribute('number', (string) $line);
|
||||
$methodLineElement->setAttribute('hits', (string) count($coverageData[$line]));
|
||||
|
||||
$methodLinesElement->appendChild($methodLineElement);
|
||||
|
||||
$classLineElement = $methodLineElement->cloneNode();
|
||||
|
||||
$classLinesElement->appendChild($classLineElement);
|
||||
}
|
||||
|
||||
$methodsElement->appendChild($methodElement);
|
||||
}
|
||||
}
|
||||
|
||||
if ($report->numberOfFunctions() === 0) {
|
||||
$packageElement->setAttribute('complexity', (string) $packageComplexity);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$functionsComplexity = 0;
|
||||
$functionsLinesValid = 0;
|
||||
$functionsLinesCovered = 0;
|
||||
$functionsBranchesValid = 0;
|
||||
$functionsBranchesCovered = 0;
|
||||
|
||||
$classElement = $document->createElement('class');
|
||||
$classElement->setAttribute('name', basename($item->pathAsString()));
|
||||
$classElement->setAttribute('filename', str_replace($report->pathAsString() . DIRECTORY_SEPARATOR, '', $item->pathAsString()));
|
||||
|
||||
$methodsElement = $document->createElement('methods');
|
||||
|
||||
$classElement->appendChild($methodsElement);
|
||||
|
||||
$classLinesElement = $document->createElement('lines');
|
||||
|
||||
$classElement->appendChild($classLinesElement);
|
||||
|
||||
$functions = $report->functions();
|
||||
|
||||
foreach ($functions as $functionName => $function) {
|
||||
if ($function['executableLines'] === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$complexity += $function['ccn'];
|
||||
$packageComplexity += $function['ccn'];
|
||||
$functionsComplexity += $function['ccn'];
|
||||
|
||||
$linesValid = $function['executableLines'];
|
||||
$linesCovered = $function['executedLines'];
|
||||
$lineRate = $linesValid === 0 ? 0 : ($linesCovered / $linesValid);
|
||||
|
||||
$functionsLinesValid += $linesValid;
|
||||
$functionsLinesCovered += $linesCovered;
|
||||
|
||||
$branchesValid = $function['executableBranches'];
|
||||
$branchesCovered = $function['executedBranches'];
|
||||
$branchRate = $branchesValid === 0 ? 0 : ($branchesCovered / $branchesValid);
|
||||
|
||||
$functionsBranchesValid += $branchesValid;
|
||||
$functionsBranchesCovered += $branchesValid;
|
||||
|
||||
$methodElement = $document->createElement('method');
|
||||
|
||||
$methodElement->setAttribute('name', $functionName);
|
||||
$methodElement->setAttribute('signature', $function['signature']);
|
||||
$methodElement->setAttribute('line-rate', (string) $lineRate);
|
||||
$methodElement->setAttribute('branch-rate', (string) $branchRate);
|
||||
$methodElement->setAttribute('complexity', (string) $function['ccn']);
|
||||
|
||||
$methodLinesElement = $document->createElement('lines');
|
||||
|
||||
$methodElement->appendChild($methodLinesElement);
|
||||
|
||||
foreach (range($function['startLine'], $function['endLine']) as $line) {
|
||||
if (!isset($coverageData[$line]) || $coverageData[$line] === null) {
|
||||
continue;
|
||||
}
|
||||
$methodLineElement = $document->createElement('line');
|
||||
|
||||
$methodLineElement->setAttribute('number', (string) $line);
|
||||
$methodLineElement->setAttribute('hits', (string) count($coverageData[$line]));
|
||||
|
||||
$methodLinesElement->appendChild($methodLineElement);
|
||||
|
||||
$classLineElement = $methodLineElement->cloneNode();
|
||||
|
||||
$classLinesElement->appendChild($classLineElement);
|
||||
}
|
||||
|
||||
$methodsElement->appendChild($methodElement);
|
||||
}
|
||||
|
||||
$packageElement->setAttribute('complexity', (string) $packageComplexity);
|
||||
|
||||
if ($functionsLinesValid === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lineRate = $functionsLinesCovered / $functionsLinesValid;
|
||||
$branchRate = $functionsBranchesValid === 0 ? 0 : ($functionsBranchesCovered / $functionsBranchesValid);
|
||||
|
||||
$classElement->setAttribute('line-rate', (string) $lineRate);
|
||||
$classElement->setAttribute('branch-rate', (string) $branchRate);
|
||||
$classElement->setAttribute('complexity', (string) $functionsComplexity);
|
||||
|
||||
$classesElement->appendChild($classElement);
|
||||
}
|
||||
|
||||
$coverageElement->setAttribute('complexity', (string) $complexity);
|
||||
|
||||
$buffer = $document->saveXML();
|
||||
|
||||
if ($target !== null) {
|
||||
Filesystem::createDirectory(dirname($target));
|
||||
|
||||
if (@file_put_contents($target, $buffer) === false) {
|
||||
throw new WriteOperationFailedException($target);
|
||||
}
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,9 +9,17 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report;
|
||||
|
||||
use function date;
|
||||
use function dirname;
|
||||
use function file_put_contents;
|
||||
use function htmlspecialchars;
|
||||
use function is_string;
|
||||
use function round;
|
||||
use DOMDocument;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
|
||||
use SebastianBergmann\CodeCoverage\Node\File;
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||
|
||||
final class Crap4j
|
||||
{
|
||||
@@ -26,19 +34,19 @@ final class Crap4j
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException
|
||||
* @throws WriteOperationFailedException
|
||||
*/
|
||||
public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string
|
||||
{
|
||||
$document = new \DOMDocument('1.0', 'UTF-8');
|
||||
$document = new DOMDocument('1.0', 'UTF-8');
|
||||
$document->formatOutput = true;
|
||||
|
||||
$root = $document->createElement('crap_result');
|
||||
$document->appendChild($root);
|
||||
|
||||
$project = $document->createElement('project', \is_string($name) ? $name : '');
|
||||
$project = $document->createElement('project', is_string($name) ? $name : '');
|
||||
$root->appendChild($project);
|
||||
$root->appendChild($document->createElement('timestamp', \date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME'])));
|
||||
$root->appendChild($document->createElement('timestamp', date('Y-m-d H:i:s')));
|
||||
|
||||
$stats = $document->createElement('stats');
|
||||
$methodsNode = $document->createElement('methods');
|
||||
@@ -59,13 +67,13 @@ final class Crap4j
|
||||
}
|
||||
|
||||
$file = $document->createElement('file');
|
||||
$file->setAttribute('name', $item->getPath());
|
||||
$file->setAttribute('name', $item->pathAsString());
|
||||
|
||||
$classes = $item->getClassesAndTraits();
|
||||
$classes = $item->classesAndTraits();
|
||||
|
||||
foreach ($classes as $className => $class) {
|
||||
foreach ($class['methods'] as $methodName => $method) {
|
||||
$crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']);
|
||||
$crapLoad = $this->crapLoad((float) $method['crap'], $method['ccn'], $method['coverage']);
|
||||
|
||||
$fullCrap += $method['crap'];
|
||||
$fullCrapLoad += $crapLoad;
|
||||
@@ -77,19 +85,19 @@ final class Crap4j
|
||||
|
||||
$methodNode = $document->createElement('method');
|
||||
|
||||
if (!empty($class['package']['namespace'])) {
|
||||
$namespace = $class['package']['namespace'];
|
||||
if (!empty($class['namespace'])) {
|
||||
$namespace = $class['namespace'];
|
||||
}
|
||||
|
||||
$methodNode->appendChild($document->createElement('package', $namespace));
|
||||
$methodNode->appendChild($document->createElement('className', $className));
|
||||
$methodNode->appendChild($document->createElement('methodName', $methodName));
|
||||
$methodNode->appendChild($document->createElement('methodSignature', \htmlspecialchars($method['signature'])));
|
||||
$methodNode->appendChild($document->createElement('fullMethod', \htmlspecialchars($method['signature'])));
|
||||
$methodNode->appendChild($document->createElement('crap', (string) $this->roundValue($method['crap'])));
|
||||
$methodNode->appendChild($document->createElement('methodSignature', htmlspecialchars($method['signature'])));
|
||||
$methodNode->appendChild($document->createElement('fullMethod', htmlspecialchars($method['signature'])));
|
||||
$methodNode->appendChild($document->createElement('crap', (string) $this->roundValue((float) $method['crap'])));
|
||||
$methodNode->appendChild($document->createElement('complexity', (string) $method['ccn']));
|
||||
$methodNode->appendChild($document->createElement('coverage', (string) $this->roundValue($method['coverage'])));
|
||||
$methodNode->appendChild($document->createElement('crapLoad', (string) \round($crapLoad)));
|
||||
$methodNode->appendChild($document->createElement('crapLoad', (string) round($crapLoad)));
|
||||
|
||||
$methodsNode->appendChild($methodNode);
|
||||
}
|
||||
@@ -99,7 +107,7 @@ final class Crap4j
|
||||
$stats->appendChild($document->createElement('name', 'Method Crap Stats'));
|
||||
$stats->appendChild($document->createElement('methodCount', (string) $fullMethodCount));
|
||||
$stats->appendChild($document->createElement('crapMethodCount', (string) $fullCrapMethodCount));
|
||||
$stats->appendChild($document->createElement('crapLoad', (string) \round($fullCrapLoad)));
|
||||
$stats->appendChild($document->createElement('crapLoad', (string) round($fullCrapLoad)));
|
||||
$stats->appendChild($document->createElement('totalCrap', (string) $fullCrap));
|
||||
|
||||
$crapMethodPercent = 0;
|
||||
@@ -116,29 +124,17 @@ final class Crap4j
|
||||
$buffer = $document->saveXML();
|
||||
|
||||
if ($target !== null) {
|
||||
if (!$this->createDirectory(\dirname($target))) {
|
||||
throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target)));
|
||||
}
|
||||
Filesystem::createDirectory(dirname($target));
|
||||
|
||||
if (@\file_put_contents($target, $buffer) === false) {
|
||||
throw new RuntimeException(
|
||||
\sprintf(
|
||||
'Could not write to "%s',
|
||||
$target
|
||||
)
|
||||
);
|
||||
if (@file_put_contents($target, $buffer) === false) {
|
||||
throw new WriteOperationFailedException($target);
|
||||
}
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $crapValue
|
||||
* @param int $cyclomaticComplexity
|
||||
* @param float $coveragePercent
|
||||
*/
|
||||
private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent): float
|
||||
private function crapLoad(float $crapValue, int $cyclomaticComplexity, float $coveragePercent): float
|
||||
{
|
||||
$crapLoad = 0;
|
||||
|
||||
@@ -150,16 +146,8 @@ final class Crap4j
|
||||
return $crapLoad;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $value
|
||||
*/
|
||||
private function roundValue($value): float
|
||||
private function roundValue(float $value): float
|
||||
{
|
||||
return \round($value, 2);
|
||||
}
|
||||
|
||||
private function createDirectory(string $directory): bool
|
||||
{
|
||||
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
|
||||
return round($value, 2);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,13 +9,16 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Html;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use function copy;
|
||||
use function date;
|
||||
use function dirname;
|
||||
use function substr;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\InvalidArgumentException;
|
||||
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||
|
||||
/**
|
||||
* Generates an HTML report from a code coverage object.
|
||||
*/
|
||||
final class Facade
|
||||
{
|
||||
/**
|
||||
@@ -40,34 +43,31 @@ final class Facade
|
||||
|
||||
public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, string $generator = '')
|
||||
{
|
||||
if ($lowUpperBound > $highLowerBound) {
|
||||
throw new InvalidArgumentException(
|
||||
'$lowUpperBound must not be larger than $highLowerBound'
|
||||
);
|
||||
}
|
||||
|
||||
$this->generator = $generator;
|
||||
$this->highLowerBound = $highLowerBound;
|
||||
$this->lowUpperBound = $lowUpperBound;
|
||||
$this->templatePath = __DIR__ . '/Renderer/Template/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function process(CodeCoverage $coverage, string $target): void
|
||||
{
|
||||
$target = $this->getDirectory($target);
|
||||
$target = $this->directory($target);
|
||||
$report = $coverage->getReport();
|
||||
|
||||
if (!isset($_SERVER['REQUEST_TIME'])) {
|
||||
$_SERVER['REQUEST_TIME'] = \time();
|
||||
}
|
||||
|
||||
$date = \date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']);
|
||||
$date = date('D M j G:i:s T Y');
|
||||
|
||||
$dashboard = new Dashboard(
|
||||
$this->templatePath,
|
||||
$this->generator,
|
||||
$date,
|
||||
$this->lowUpperBound,
|
||||
$this->highLowerBound
|
||||
$this->highLowerBound,
|
||||
$coverage->collectsBranchAndPathCoverage()
|
||||
);
|
||||
|
||||
$directory = new Directory(
|
||||
@@ -75,7 +75,8 @@ final class Facade
|
||||
$this->generator,
|
||||
$date,
|
||||
$this->lowUpperBound,
|
||||
$this->highLowerBound
|
||||
$this->highLowerBound,
|
||||
$coverage->collectsBranchAndPathCoverage()
|
||||
);
|
||||
|
||||
$file = new File(
|
||||
@@ -83,85 +84,64 @@ final class Facade
|
||||
$this->generator,
|
||||
$date,
|
||||
$this->lowUpperBound,
|
||||
$this->highLowerBound
|
||||
$this->highLowerBound,
|
||||
$coverage->collectsBranchAndPathCoverage()
|
||||
);
|
||||
|
||||
$directory->render($report, $target . 'index.html');
|
||||
$dashboard->render($report, $target . 'dashboard.html');
|
||||
|
||||
foreach ($report as $node) {
|
||||
$id = $node->getId();
|
||||
$id = $node->id();
|
||||
|
||||
if ($node instanceof DirectoryNode) {
|
||||
if (!$this->createDirectory($target . $id)) {
|
||||
throw new \RuntimeException(\sprintf('Directory "%s" was not created', $target . $id));
|
||||
}
|
||||
Filesystem::createDirectory($target . $id);
|
||||
|
||||
$directory->render($node, $target . $id . '/index.html');
|
||||
$dashboard->render($node, $target . $id . '/dashboard.html');
|
||||
} else {
|
||||
$dir = \dirname($target . $id);
|
||||
$dir = dirname($target . $id);
|
||||
|
||||
if (!$this->createDirectory($dir)) {
|
||||
throw new \RuntimeException(\sprintf('Directory "%s" was not created', $dir));
|
||||
}
|
||||
Filesystem::createDirectory($dir);
|
||||
|
||||
$file->render($node, $target . $id . '.html');
|
||||
$file->render($node, $target . $id);
|
||||
}
|
||||
}
|
||||
|
||||
$this->copyFiles($target);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function copyFiles(string $target): void
|
||||
{
|
||||
$dir = $this->getDirectory($target . '_css');
|
||||
$dir = $this->directory($target . '_css');
|
||||
|
||||
\copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
|
||||
\copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css');
|
||||
\copy($this->templatePath . 'css/style.css', $dir . 'style.css');
|
||||
\copy($this->templatePath . 'css/custom.css', $dir . 'custom.css');
|
||||
\copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css');
|
||||
copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
|
||||
copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css');
|
||||
copy($this->templatePath . 'css/style.css', $dir . 'style.css');
|
||||
copy($this->templatePath . 'css/custom.css', $dir . 'custom.css');
|
||||
copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css');
|
||||
|
||||
$dir = $this->getDirectory($target . '_icons');
|
||||
\copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg');
|
||||
\copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg');
|
||||
$dir = $this->directory($target . '_icons');
|
||||
copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg');
|
||||
copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg');
|
||||
|
||||
$dir = $this->getDirectory($target . '_js');
|
||||
\copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
|
||||
\copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js');
|
||||
\copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
|
||||
\copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
|
||||
\copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
|
||||
\copy($this->templatePath . 'js/file.js', $dir . 'file.js');
|
||||
$dir = $this->directory($target . '_js');
|
||||
copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
|
||||
copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js');
|
||||
copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
|
||||
copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
|
||||
copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
|
||||
copy($this->templatePath . 'js/file.js', $dir . 'file.js');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function getDirectory(string $directory): string
|
||||
private function directory(string $directory): string
|
||||
{
|
||||
if (\substr($directory, -1, 1) != \DIRECTORY_SEPARATOR) {
|
||||
$directory .= \DIRECTORY_SEPARATOR;
|
||||
if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) {
|
||||
$directory .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
if (!$this->createDirectory($directory)) {
|
||||
throw new RuntimeException(
|
||||
\sprintf(
|
||||
'Directory "%s" does not exist.',
|
||||
$directory
|
||||
)
|
||||
);
|
||||
}
|
||||
Filesystem::createDirectory($directory);
|
||||
|
||||
return $directory;
|
||||
}
|
||||
|
||||
private function createDirectory(string $directory): bool
|
||||
{
|
||||
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,14 +9,20 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Html;
|
||||
|
||||
use function array_pop;
|
||||
use function count;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use function substr_count;
|
||||
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
|
||||
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
|
||||
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
|
||||
use SebastianBergmann\CodeCoverage\Version;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
use SebastianBergmann\Template\Template;
|
||||
|
||||
/**
|
||||
* Base class for node renderers.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
abstract class Renderer
|
||||
{
|
||||
@@ -45,32 +51,38 @@ abstract class Renderer
|
||||
*/
|
||||
protected $highLowerBound;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasBranchCoverage;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound)
|
||||
public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound, bool $hasBranchCoverage)
|
||||
{
|
||||
$this->templatePath = $templatePath;
|
||||
$this->generator = $generator;
|
||||
$this->date = $date;
|
||||
$this->lowUpperBound = $lowUpperBound;
|
||||
$this->highLowerBound = $highLowerBound;
|
||||
$this->version = Version::id();
|
||||
$this->templatePath = $templatePath;
|
||||
$this->generator = $generator;
|
||||
$this->date = $date;
|
||||
$this->lowUpperBound = $lowUpperBound;
|
||||
$this->highLowerBound = $highLowerBound;
|
||||
$this->version = Version::id();
|
||||
$this->hasBranchCoverage = $hasBranchCoverage;
|
||||
}
|
||||
|
||||
protected function renderItemTemplate(\Text_Template $template, array $data): string
|
||||
protected function renderItemTemplate(Template $template, array $data): string
|
||||
{
|
||||
$numSeparator = ' / ';
|
||||
$numSeparator = ' / ';
|
||||
|
||||
if (isset($data['numClasses']) && $data['numClasses'] > 0) {
|
||||
$classesLevel = $this->getColorLevel($data['testedClassesPercent']);
|
||||
$classesLevel = $this->colorLevel($data['testedClassesPercent']);
|
||||
|
||||
$classesNumber = $data['numTestedClasses'] . $numSeparator .
|
||||
$data['numClasses'];
|
||||
|
||||
$classesBar = $this->getCoverageBar(
|
||||
$classesBar = $this->coverageBar(
|
||||
$data['testedClassesPercent']
|
||||
);
|
||||
} else {
|
||||
@@ -81,12 +93,12 @@ abstract class Renderer
|
||||
}
|
||||
|
||||
if ($data['numMethods'] > 0) {
|
||||
$methodsLevel = $this->getColorLevel($data['testedMethodsPercent']);
|
||||
$methodsLevel = $this->colorLevel($data['testedMethodsPercent']);
|
||||
|
||||
$methodsNumber = $data['numTestedMethods'] . $numSeparator .
|
||||
$data['numMethods'];
|
||||
|
||||
$methodsBar = $this->getCoverageBar(
|
||||
$methodsBar = $this->coverageBar(
|
||||
$data['testedMethodsPercent']
|
||||
);
|
||||
} else {
|
||||
@@ -97,12 +109,12 @@ abstract class Renderer
|
||||
}
|
||||
|
||||
if ($data['numExecutableLines'] > 0) {
|
||||
$linesLevel = $this->getColorLevel($data['linesExecutedPercent']);
|
||||
$linesLevel = $this->colorLevel($data['linesExecutedPercent']);
|
||||
|
||||
$linesNumber = $data['numExecutedLines'] . $numSeparator .
|
||||
$data['numExecutableLines'];
|
||||
|
||||
$linesBar = $this->getCoverageBar(
|
||||
$linesBar = $this->coverageBar(
|
||||
$data['linesExecutedPercent']
|
||||
);
|
||||
} else {
|
||||
@@ -112,40 +124,80 @@ abstract class Renderer
|
||||
$data['linesExecutedPercentAsString'] = 'n/a';
|
||||
}
|
||||
|
||||
if ($data['numExecutablePaths'] > 0) {
|
||||
$pathsLevel = $this->colorLevel($data['pathsExecutedPercent']);
|
||||
|
||||
$pathsNumber = $data['numExecutedPaths'] . $numSeparator .
|
||||
$data['numExecutablePaths'];
|
||||
|
||||
$pathsBar = $this->coverageBar(
|
||||
$data['pathsExecutedPercent']
|
||||
);
|
||||
} else {
|
||||
$pathsLevel = '';
|
||||
$pathsNumber = '0' . $numSeparator . '0';
|
||||
$pathsBar = '';
|
||||
$data['pathsExecutedPercentAsString'] = 'n/a';
|
||||
}
|
||||
|
||||
if ($data['numExecutableBranches'] > 0) {
|
||||
$branchesLevel = $this->colorLevel($data['branchesExecutedPercent']);
|
||||
|
||||
$branchesNumber = $data['numExecutedBranches'] . $numSeparator .
|
||||
$data['numExecutableBranches'];
|
||||
|
||||
$branchesBar = $this->coverageBar(
|
||||
$data['branchesExecutedPercent']
|
||||
);
|
||||
} else {
|
||||
$branchesLevel = '';
|
||||
$branchesNumber = '0' . $numSeparator . '0';
|
||||
$branchesBar = '';
|
||||
$data['branchesExecutedPercentAsString'] = 'n/a';
|
||||
}
|
||||
|
||||
$template->setVar(
|
||||
[
|
||||
'icon' => $data['icon'] ?? '',
|
||||
'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' => $data['testedClassesPercentAsString'] ?? '',
|
||||
'classes_level' => $classesLevel,
|
||||
'classes_number' => $classesNumber,
|
||||
'icon' => $data['icon'] ?? '',
|
||||
'crap' => $data['crap'] ?? '',
|
||||
'name' => $data['name'],
|
||||
'lines_bar' => $linesBar,
|
||||
'lines_executed_percent' => $data['linesExecutedPercentAsString'],
|
||||
'lines_level' => $linesLevel,
|
||||
'lines_number' => $linesNumber,
|
||||
'paths_bar' => $pathsBar,
|
||||
'paths_executed_percent' => $data['pathsExecutedPercentAsString'],
|
||||
'paths_level' => $pathsLevel,
|
||||
'paths_number' => $pathsNumber,
|
||||
'branches_bar' => $branchesBar,
|
||||
'branches_executed_percent' => $data['branchesExecutedPercentAsString'],
|
||||
'branches_level' => $branchesLevel,
|
||||
'branches_number' => $branchesNumber,
|
||||
'methods_bar' => $methodsBar,
|
||||
'methods_tested_percent' => $data['testedMethodsPercentAsString'],
|
||||
'methods_level' => $methodsLevel,
|
||||
'methods_number' => $methodsNumber,
|
||||
'classes_bar' => $classesBar,
|
||||
'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '',
|
||||
'classes_level' => $classesLevel,
|
||||
'classes_number' => $classesNumber,
|
||||
]
|
||||
);
|
||||
|
||||
return $template->render();
|
||||
}
|
||||
|
||||
protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node): void
|
||||
protected function setCommonTemplateVariables(Template $template, AbstractNode $node): void
|
||||
{
|
||||
$template->setVar(
|
||||
[
|
||||
'id' => $node->getId(),
|
||||
'full_path' => $node->getPath(),
|
||||
'path_to_root' => $this->getPathToRoot($node),
|
||||
'breadcrumbs' => $this->getBreadcrumbs($node),
|
||||
'id' => $node->id(),
|
||||
'full_path' => $node->pathAsString(),
|
||||
'path_to_root' => $this->pathToRoot($node),
|
||||
'breadcrumbs' => $this->breadcrumbs($node),
|
||||
'date' => $this->date,
|
||||
'version' => $this->version,
|
||||
'runtime' => $this->getRuntimeString(),
|
||||
'runtime' => $this->runtimeString(),
|
||||
'generator' => $this->generator,
|
||||
'low_upper_bound' => $this->lowUpperBound,
|
||||
'high_lower_bound' => $this->highLowerBound,
|
||||
@@ -153,40 +205,40 @@ abstract class Renderer
|
||||
);
|
||||
}
|
||||
|
||||
protected function getBreadcrumbs(AbstractNode $node): string
|
||||
protected function breadcrumbs(AbstractNode $node): string
|
||||
{
|
||||
$breadcrumbs = '';
|
||||
$path = $node->getPathAsArray();
|
||||
$path = $node->pathAsArray();
|
||||
$pathToRoot = [];
|
||||
$max = \count($path);
|
||||
$max = count($path);
|
||||
|
||||
if ($node instanceof FileNode) {
|
||||
$max--;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
$pathToRoot[] = \str_repeat('../', $i);
|
||||
$pathToRoot[] = str_repeat('../', $i);
|
||||
}
|
||||
|
||||
foreach ($path as $step) {
|
||||
if ($step !== $node) {
|
||||
$breadcrumbs .= $this->getInactiveBreadcrumb(
|
||||
$breadcrumbs .= $this->inactiveBreadcrumb(
|
||||
$step,
|
||||
\array_pop($pathToRoot)
|
||||
array_pop($pathToRoot)
|
||||
);
|
||||
} else {
|
||||
$breadcrumbs .= $this->getActiveBreadcrumb($step);
|
||||
$breadcrumbs .= $this->activeBreadcrumb($step);
|
||||
}
|
||||
}
|
||||
|
||||
return $breadcrumbs;
|
||||
}
|
||||
|
||||
protected function getActiveBreadcrumb(AbstractNode $node): string
|
||||
protected function activeBreadcrumb(AbstractNode $node): string
|
||||
{
|
||||
$buffer = \sprintf(
|
||||
$buffer = sprintf(
|
||||
' <li class="breadcrumb-item active">%s</li>' . "\n",
|
||||
$node->getName()
|
||||
$node->name()
|
||||
);
|
||||
|
||||
if ($node instanceof DirectoryNode) {
|
||||
@@ -196,44 +248,45 @@ abstract class Renderer
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
protected function getInactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string
|
||||
protected function inactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string
|
||||
{
|
||||
return \sprintf(
|
||||
return sprintf(
|
||||
' <li class="breadcrumb-item"><a href="%sindex.html">%s</a></li>' . "\n",
|
||||
$pathToRoot,
|
||||
$node->getName()
|
||||
$node->name()
|
||||
);
|
||||
}
|
||||
|
||||
protected function getPathToRoot(AbstractNode $node): string
|
||||
protected function pathToRoot(AbstractNode $node): string
|
||||
{
|
||||
$id = $node->getId();
|
||||
$depth = \substr_count($id, '/');
|
||||
$id = $node->id();
|
||||
$depth = substr_count($id, '/');
|
||||
|
||||
if ($id !== 'index' &&
|
||||
$node instanceof DirectoryNode) {
|
||||
$depth++;
|
||||
}
|
||||
|
||||
return \str_repeat('../', $depth);
|
||||
return str_repeat('../', $depth);
|
||||
}
|
||||
|
||||
protected function getCoverageBar(float $percent): string
|
||||
protected function coverageBar(float $percent): string
|
||||
{
|
||||
$level = $this->getColorLevel($percent);
|
||||
$level = $this->colorLevel($percent);
|
||||
|
||||
$template = new \Text_Template(
|
||||
$this->templatePath . 'coverage_bar.html',
|
||||
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'coverage_bar_branch.html' : 'coverage_bar.html');
|
||||
$template = new Template(
|
||||
$templateName,
|
||||
'{{',
|
||||
'}}'
|
||||
);
|
||||
|
||||
$template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]);
|
||||
$template->setVar(['level' => $level, 'percent' => sprintf('%.2F', $percent)]);
|
||||
|
||||
return $template->render();
|
||||
}
|
||||
|
||||
protected function getColorLevel(float $percent): string
|
||||
protected function colorLevel(float $percent): string
|
||||
{
|
||||
if ($percent <= $this->lowUpperBound) {
|
||||
return 'danger';
|
||||
@@ -247,31 +300,15 @@ abstract class Renderer
|
||||
return 'success';
|
||||
}
|
||||
|
||||
private function getRuntimeString(): string
|
||||
private function runtimeString(): string
|
||||
{
|
||||
$runtime = new Runtime;
|
||||
|
||||
$buffer = \sprintf(
|
||||
return sprintf(
|
||||
'<a href="%s" target="_top">%s %s</a>',
|
||||
$runtime->getVendorUrl(),
|
||||
$runtime->getName(),
|
||||
$runtime->getVersion()
|
||||
);
|
||||
|
||||
if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) {
|
||||
$buffer .= \sprintf(
|
||||
' with <a href="https://xdebug.org/">Xdebug %s</a>',
|
||||
\phpversion('xdebug')
|
||||
);
|
||||
}
|
||||
|
||||
if ($runtime->hasPCOV() && !$runtime->hasPHPDBGCodeCoverage()) {
|
||||
$buffer .= \sprintf(
|
||||
' with <a href="https://github.com/krakjoe/pcov">PCOV %s</a>',
|
||||
\phpversion('pcov')
|
||||
);
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,30 +9,37 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Html;
|
||||
|
||||
use function array_values;
|
||||
use function arsort;
|
||||
use function asort;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function floor;
|
||||
use function json_encode;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
|
||||
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
|
||||
use SebastianBergmann\Template\Template;
|
||||
|
||||
/**
|
||||
* Renders the dashboard for a directory node.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Dashboard extends Renderer
|
||||
{
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function render(DirectoryNode $node, string $file): void
|
||||
{
|
||||
$classes = $node->getClassesAndTraits();
|
||||
$template = new \Text_Template(
|
||||
$this->templatePath . 'dashboard.html',
|
||||
$classes = $node->classesAndTraits();
|
||||
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'dashboard_branch.html' : 'dashboard.html');
|
||||
$template = new Template(
|
||||
$templateName,
|
||||
'{{',
|
||||
'}}'
|
||||
);
|
||||
|
||||
$this->setCommonTemplateVariables($template, $node);
|
||||
|
||||
$baseLink = $node->getId() . '/';
|
||||
$baseLink = $node->id() . '/';
|
||||
$complexity = $this->complexity($classes, $baseLink);
|
||||
$coverageDistribution = $this->coverageDistribution($classes);
|
||||
$insufficientCoverage = $this->insufficientCoverage($classes, $baseLink);
|
||||
@@ -54,10 +61,19 @@ final class Dashboard extends Renderer
|
||||
$template->renderTo($file);
|
||||
}
|
||||
|
||||
protected function activeBreadcrumb(AbstractNode $node): string
|
||||
{
|
||||
return sprintf(
|
||||
' <li class="breadcrumb-item"><a href="index.html">%s</a></li>' . "\n" .
|
||||
' <li class="breadcrumb-item active">(Dashboard)</li>' . "\n",
|
||||
$node->name()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data for the Class/Method Complexity charts.
|
||||
*/
|
||||
protected function complexity(array $classes, string $baseLink): array
|
||||
private function complexity(array $classes, string $baseLink): array
|
||||
{
|
||||
$result = ['class' => [], 'method' => []];
|
||||
|
||||
@@ -70,9 +86,9 @@ final class Dashboard extends Renderer
|
||||
$result['method'][] = [
|
||||
$method['coverage'],
|
||||
$method['ccn'],
|
||||
\sprintf(
|
||||
sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
\str_replace($baseLink, '', $method['link']),
|
||||
str_replace($baseLink, '', $method['link']),
|
||||
$methodName
|
||||
),
|
||||
];
|
||||
@@ -81,27 +97,27 @@ final class Dashboard extends Renderer
|
||||
$result['class'][] = [
|
||||
$class['coverage'],
|
||||
$class['ccn'],
|
||||
\sprintf(
|
||||
sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
\str_replace($baseLink, '', $class['link']),
|
||||
str_replace($baseLink, '', $class['link']),
|
||||
$className
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'class' => \json_encode($result['class']),
|
||||
'method' => \json_encode($result['method']),
|
||||
'class' => json_encode($result['class']),
|
||||
'method' => json_encode($result['method']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data for the Class / Method Coverage Distribution chart.
|
||||
*/
|
||||
protected function coverageDistribution(array $classes): array
|
||||
private function coverageDistribution(array $classes): array
|
||||
{
|
||||
$result = [
|
||||
'class' => [
|
||||
'class' => [
|
||||
'0%' => 0,
|
||||
'0-10%' => 0,
|
||||
'10-20%' => 0,
|
||||
@@ -138,7 +154,7 @@ final class Dashboard extends Renderer
|
||||
} elseif ($method['coverage'] === 100) {
|
||||
$result['method']['100%']++;
|
||||
} else {
|
||||
$key = \floor($method['coverage'] / 10) * 10;
|
||||
$key = floor($method['coverage'] / 10) * 10;
|
||||
$key = $key . '-' . ($key + 10) . '%';
|
||||
$result['method'][$key]++;
|
||||
}
|
||||
@@ -149,22 +165,22 @@ final class Dashboard extends Renderer
|
||||
} elseif ($class['coverage'] === 100) {
|
||||
$result['class']['100%']++;
|
||||
} else {
|
||||
$key = \floor($class['coverage'] / 10) * 10;
|
||||
$key = floor($class['coverage'] / 10) * 10;
|
||||
$key = $key . '-' . ($key + 10) . '%';
|
||||
$result['class'][$key]++;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'class' => \json_encode(\array_values($result['class'])),
|
||||
'method' => \json_encode(\array_values($result['method'])),
|
||||
'class' => json_encode(array_values($result['class'])),
|
||||
'method' => json_encode(array_values($result['method'])),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the classes / methods with insufficient coverage.
|
||||
*/
|
||||
protected function insufficientCoverage(array $classes, string $baseLink): array
|
||||
private function insufficientCoverage(array $classes, string $baseLink): array
|
||||
{
|
||||
$leastTestedClasses = [];
|
||||
$leastTestedMethods = [];
|
||||
@@ -188,24 +204,24 @@ final class Dashboard extends Renderer
|
||||
}
|
||||
}
|
||||
|
||||
\asort($leastTestedClasses);
|
||||
\asort($leastTestedMethods);
|
||||
asort($leastTestedClasses);
|
||||
asort($leastTestedMethods);
|
||||
|
||||
foreach ($leastTestedClasses as $className => $coverage) {
|
||||
$result['class'] .= \sprintf(
|
||||
$result['class'] .= sprintf(
|
||||
' <tr><td><a href="%s">%s</a></td><td class="text-right">%d%%</td></tr>' . "\n",
|
||||
\str_replace($baseLink, '', $classes[$className]['link']),
|
||||
str_replace($baseLink, '', $classes[$className]['link']),
|
||||
$className,
|
||||
$coverage
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($leastTestedMethods as $methodName => $coverage) {
|
||||
[$class, $method] = \explode('::', $methodName);
|
||||
[$class, $method] = explode('::', $methodName);
|
||||
|
||||
$result['method'] .= \sprintf(
|
||||
$result['method'] .= sprintf(
|
||||
' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d%%</td></tr>' . "\n",
|
||||
\str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
|
||||
str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
|
||||
$methodName,
|
||||
$method,
|
||||
$coverage
|
||||
@@ -218,7 +234,7 @@ final class Dashboard extends Renderer
|
||||
/**
|
||||
* Returns the project risks according to the CRAP index.
|
||||
*/
|
||||
protected function projectRisks(array $classes, string $baseLink): array
|
||||
private function projectRisks(array $classes, string $baseLink): array
|
||||
{
|
||||
$classRisks = [];
|
||||
$methodRisks = [];
|
||||
@@ -238,29 +254,29 @@ final class Dashboard extends Renderer
|
||||
}
|
||||
|
||||
if ($class['coverage'] < $this->highLowerBound &&
|
||||
$class['ccn'] > \count($class['methods'])) {
|
||||
$class['ccn'] > count($class['methods'])) {
|
||||
$classRisks[$className] = $class['crap'];
|
||||
}
|
||||
}
|
||||
|
||||
\arsort($classRisks);
|
||||
\arsort($methodRisks);
|
||||
arsort($classRisks);
|
||||
arsort($methodRisks);
|
||||
|
||||
foreach ($classRisks as $className => $crap) {
|
||||
$result['class'] .= \sprintf(
|
||||
$result['class'] .= sprintf(
|
||||
' <tr><td><a href="%s">%s</a></td><td class="text-right">%d</td></tr>' . "\n",
|
||||
\str_replace($baseLink, '', $classes[$className]['link']),
|
||||
str_replace($baseLink, '', $classes[$className]['link']),
|
||||
$className,
|
||||
$crap
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($methodRisks as $methodName => $crap) {
|
||||
[$class, $method] = \explode('::', $methodName);
|
||||
[$class, $method] = explode('::', $methodName);
|
||||
|
||||
$result['method'] .= \sprintf(
|
||||
$result['method'] .= sprintf(
|
||||
' <tr><td><a href="%s"><abbr title="%s">%s</abbr></a></td><td class="text-right">%d</td></tr>' . "\n",
|
||||
\str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
|
||||
str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']),
|
||||
$methodName,
|
||||
$method,
|
||||
$crap
|
||||
@@ -269,13 +285,4 @@ final class Dashboard extends Renderer
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getActiveBreadcrumb(AbstractNode $node): string
|
||||
{
|
||||
return \sprintf(
|
||||
' <li class="breadcrumb-item"><a href="index.html">%s</a></li>' . "\n" .
|
||||
' <li class="breadcrumb-item active">(Dashboard)</li>' . "\n",
|
||||
$node->getName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,37 +9,38 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Html;
|
||||
|
||||
use function count;
|
||||
use function sprintf;
|
||||
use function str_repeat;
|
||||
use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node;
|
||||
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
|
||||
use SebastianBergmann\Template\Template;
|
||||
|
||||
/**
|
||||
* Renders a directory node.
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Directory extends Renderer
|
||||
{
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function render(DirectoryNode $node, string $file): void
|
||||
{
|
||||
$template = new \Text_Template($this->templatePath . 'directory.html', '{{', '}}');
|
||||
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_branch.html' : 'directory.html');
|
||||
$template = new Template($templateName, '{{', '}}');
|
||||
|
||||
$this->setCommonTemplateVariables($template, $node);
|
||||
|
||||
$items = $this->renderItem($node, true);
|
||||
|
||||
foreach ($node->getDirectories() as $item) {
|
||||
foreach ($node->directories() as $item) {
|
||||
$items .= $this->renderItem($item);
|
||||
}
|
||||
|
||||
foreach ($node->getFiles() as $item) {
|
||||
foreach ($node->files() as $item) {
|
||||
$items .= $this->renderItem($item);
|
||||
}
|
||||
|
||||
$template->setVar(
|
||||
[
|
||||
'id' => $node->getId(),
|
||||
'id' => $node->id(),
|
||||
'items' => $items,
|
||||
]
|
||||
);
|
||||
@@ -47,51 +48,65 @@ final class Directory extends Renderer
|
||||
$template->renderTo($file);
|
||||
}
|
||||
|
||||
protected function renderItem(Node $node, bool $total = false): string
|
||||
private function renderItem(Node $node, bool $total = false): string
|
||||
{
|
||||
$data = [
|
||||
'numClasses' => $node->getNumClassesAndTraits(),
|
||||
'numTestedClasses' => $node->getNumTestedClassesAndTraits(),
|
||||
'numMethods' => $node->getNumFunctionsAndMethods(),
|
||||
'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(),
|
||||
'linesExecutedPercent' => $node->getLineExecutedPercent(false),
|
||||
'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
|
||||
'numExecutedLines' => $node->getNumExecutedLines(),
|
||||
'numExecutableLines' => $node->getNumExecutableLines(),
|
||||
'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false),
|
||||
'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(),
|
||||
'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false),
|
||||
'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(),
|
||||
'numClasses' => $node->numberOfClassesAndTraits(),
|
||||
'numTestedClasses' => $node->numberOfTestedClassesAndTraits(),
|
||||
'numMethods' => $node->numberOfFunctionsAndMethods(),
|
||||
'numTestedMethods' => $node->numberOfTestedFunctionsAndMethods(),
|
||||
'linesExecutedPercent' => $node->percentageOfExecutedLines()->asFloat(),
|
||||
'linesExecutedPercentAsString' => $node->percentageOfExecutedLines()->asString(),
|
||||
'numExecutedLines' => $node->numberOfExecutedLines(),
|
||||
'numExecutableLines' => $node->numberOfExecutableLines(),
|
||||
'branchesExecutedPercent' => $node->percentageOfExecutedBranches()->asFloat(),
|
||||
'branchesExecutedPercentAsString' => $node->percentageOfExecutedBranches()->asString(),
|
||||
'numExecutedBranches' => $node->numberOfExecutedBranches(),
|
||||
'numExecutableBranches' => $node->numberOfExecutableBranches(),
|
||||
'pathsExecutedPercent' => $node->percentageOfExecutedPaths()->asFloat(),
|
||||
'pathsExecutedPercentAsString' => $node->percentageOfExecutedPaths()->asString(),
|
||||
'numExecutedPaths' => $node->numberOfExecutedPaths(),
|
||||
'numExecutablePaths' => $node->numberOfExecutablePaths(),
|
||||
'testedMethodsPercent' => $node->percentageOfTestedFunctionsAndMethods()->asFloat(),
|
||||
'testedMethodsPercentAsString' => $node->percentageOfTestedFunctionsAndMethods()->asString(),
|
||||
'testedClassesPercent' => $node->percentageOfTestedClassesAndTraits()->asFloat(),
|
||||
'testedClassesPercentAsString' => $node->percentageOfTestedClassesAndTraits()->asString(),
|
||||
];
|
||||
|
||||
if ($total) {
|
||||
$data['name'] = 'Total';
|
||||
} else {
|
||||
$up = str_repeat('../', count($node->pathAsArray()) - 2);
|
||||
$data['icon'] = sprintf('<img src="%s_icons/file-code.svg" class="octicon" />', $up);
|
||||
|
||||
if ($node instanceof DirectoryNode) {
|
||||
$data['name'] = \sprintf(
|
||||
$data['name'] = sprintf(
|
||||
'<a href="%s/index.html">%s</a>',
|
||||
$node->getName(),
|
||||
$node->getName()
|
||||
$node->name(),
|
||||
$node->name()
|
||||
);
|
||||
$data['icon'] = sprintf('<img src="%s_icons/file-directory.svg" class="octicon" />', $up);
|
||||
} elseif ($this->hasBranchCoverage) {
|
||||
$data['name'] = sprintf(
|
||||
'%s <a class="small" href="%s.html">[line]</a> <a class="small" href="%s_branch.html">[branch]</a> <a class="small" href="%s_path.html">[path]</a>',
|
||||
$node->name(),
|
||||
$node->name(),
|
||||
$node->name(),
|
||||
$node->name()
|
||||
);
|
||||
|
||||
$up = \str_repeat('../', \count($node->getPathAsArray()) - 2);
|
||||
|
||||
$data['icon'] = \sprintf('<img src="%s_icons/file-directory.svg" class="octicon" />', $up);
|
||||
} else {
|
||||
$data['name'] = \sprintf(
|
||||
$data['name'] = sprintf(
|
||||
'<a href="%s.html">%s</a>',
|
||||
$node->getName(),
|
||||
$node->getName()
|
||||
$node->name(),
|
||||
$node->name()
|
||||
);
|
||||
|
||||
$up = \str_repeat('../', \count($node->getPathAsArray()) - 2);
|
||||
|
||||
$data['icon'] = \sprintf('<img src="%s_icons/file-code.svg" class="octicon" />', $up);
|
||||
}
|
||||
}
|
||||
|
||||
$templateName = $this->templatePath . ($this->hasBranchCoverage ? 'directory_item_branch.html' : 'directory_item.html');
|
||||
|
||||
return $this->renderItemTemplate(
|
||||
new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'),
|
||||
new Template($templateName, '{{', '}}'),
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
9
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/branches.html.dist
vendored
Normal file
9
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/branches.html.dist
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<hr/>
|
||||
<h4>Branches</h4>
|
||||
<p>
|
||||
Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not
|
||||
necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once.
|
||||
Please also be aware that some branches may be implicit rather than explicit, e.g. an <code>if</code> statement
|
||||
<i>always</i> has an <code>else</code> as part of its logical flow even if you didn't write one.
|
||||
</p>
|
||||
{{branches}}
|
@@ -0,0 +1,5 @@
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-{{level}}" role="progressbar" aria-valuenow="{{percent}}" aria-valuemin="0" aria-valuemax="100" style="width: {{percent}}%">
|
||||
<span class="sr-only">{{percent}}% covered ({{level}})</span>
|
||||
</div>
|
||||
</div>
|
File diff suppressed because one or more lines are too long
@@ -1,4 +1,9 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
font-kerning: normal;
|
||||
font-variant-ligatures: common-ligatures;
|
||||
text-rendering: optimizeLegibility;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@@ -8,6 +13,8 @@ body {
|
||||
|
||||
.octicon {
|
||||
margin-right:.25em;
|
||||
vertical-align: baseline;
|
||||
width: 0.75em;
|
||||
}
|
||||
|
||||
.table-bordered>thead>tr>td {
|
||||
@@ -48,7 +55,7 @@ body {
|
||||
background-color: #f2dede;
|
||||
}
|
||||
|
||||
.table tbody td.warning, li.warning, span.warning {
|
||||
.table tbody tr.warning, .table tbody td.warning, li.warning, span.warning {
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
@@ -57,6 +64,7 @@ body {
|
||||
}
|
||||
|
||||
td.big {
|
||||
vertical-align: middle;
|
||||
width: 117px;
|
||||
}
|
||||
|
||||
@@ -65,7 +73,7 @@ td.small {
|
||||
|
||||
td.codeLine {
|
||||
font-family: "Source Code Pro", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
td span.comment {
|
||||
@@ -96,11 +104,6 @@ span.success, span.warning, span.danger {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#classCoverageDistribution, #classComplexity {
|
||||
height: 200px;
|
||||
width: 475px;
|
||||
}
|
||||
|
||||
#toplink {
|
||||
position: fixed;
|
||||
left: 5px;
|
||||
@@ -120,3 +123,8 @@ svg text {
|
||||
overflow-x:hidden;
|
||||
overflow-y:scroll;
|
||||
}
|
||||
|
||||
table + .structure-heading {
|
||||
border-top: 1px solid lightgrey;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
281
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/dashboard_branch.html.dist
vendored
Normal file
281
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/dashboard_branch.html.dist
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard for {{full_path}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/nv.d3.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
{{breadcrumbs}}
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>Classes</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3>Coverage Distribution</h3>
|
||||
<div id="classCoverageDistribution" style="height: 300px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h3>Complexity</h3>
|
||||
<div id="classComplexity" style="height: 300px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3>Insufficient Coverage</h3>
|
||||
<div class="scrollbox">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<th class="text-right">Coverage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{insufficient_coverage_classes}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h3>Project Risks</h3>
|
||||
<div class="scrollbox">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{project_risks_classes}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>Methods</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3>Coverage Distribution</h3>
|
||||
<div id="methodCoverageDistribution" style="height: 300px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h3>Complexity</h3>
|
||||
<div id="methodComplexity" style="height: 300px;">
|
||||
<svg></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3>Insufficient Coverage</h3>
|
||||
<div class="scrollbox">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method</th>
|
||||
<th class="text-right">Coverage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{insufficient_coverage_methods}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h3>Project Risks</h3>
|
||||
<div class="scrollbox">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method</th>
|
||||
<th class="text-right"><abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{project_risks_methods}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<hr/>
|
||||
<p>
|
||||
<small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="{{path_to_root}}_js/d3.min.js" type="text/javascript"></script>
|
||||
<script src="{{path_to_root}}_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({{class_coverage_distribution}}, "Class Coverage"))
|
||||
.transition().duration(500).call(chart);
|
||||
|
||||
nv.utils.windowResize(chart.update);
|
||||
|
||||
return chart;
|
||||
});
|
||||
|
||||
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('#methodCoverageDistribution svg')
|
||||
.datum(getCoverageDistributionData({{method_coverage_distribution}}, "Method 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.tooltipContent(function(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({{complexity_class}}, 'Class Complexity'))
|
||||
.transition()
|
||||
.duration(500)
|
||||
.call(chart);
|
||||
|
||||
nv.utils.windowResize(chart.update);
|
||||
|
||||
return chart;
|
||||
});
|
||||
|
||||
nv.addGraph(function() {
|
||||
var chart = nv.models.scatterChart()
|
||||
.showDistX(true)
|
||||
.showDistY(true)
|
||||
.showLegend(false)
|
||||
.forceX([0, 100]);
|
||||
chart.tooltipContent(function(graph) {
|
||||
return '<p>' + graph.point.class + '</p>';
|
||||
});
|
||||
|
||||
chart.xAxis.axisLabel('Code Coverage (in percent)');
|
||||
chart.yAxis.axisLabel('Method Complexity');
|
||||
|
||||
d3.select('#methodComplexity svg')
|
||||
.datum(getComplexityData({{complexity_method}}, 'Method 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>
|
62
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_branch.html.dist
vendored
Normal file
62
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/directory_branch.html.dist
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Code Coverage for {{full_path}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
{{breadcrumbs}}
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container-fluid">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td colspan="15"><div align="center"><strong>Code Coverage</strong></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td colspan="3"><div align="center"><strong>Lines</strong></div></td>
|
||||
<td colspan="3"><div align="center"><strong>Branches</strong></div></td>
|
||||
<td colspan="3"><div align="center"><strong>Paths</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>
|
||||
</div>
|
||||
<footer>
|
||||
<hr/>
|
||||
<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="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@@ -0,0 +1,19 @@
|
||||
<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="{{branches_level}} big">{{branches_bar}}</td>
|
||||
<td class="{{branches_level}} small"><div align="right">{{branches_executed_percent}}</div></td>
|
||||
<td class="{{branches_level}} small"><div align="right">{{branches_number}}</div></td>
|
||||
<td class="{{paths_level}} big">{{paths_bar}}</td>
|
||||
<td class="{{paths_level}} small"><div align="right">{{paths_executed_percent}}</div></td>
|
||||
<td class="{{paths_level}} small"><div align="right">{{paths_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>
|
||||
|
@@ -33,9 +33,9 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </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>
|
||||
<td colspan="4"><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>
|
||||
@@ -43,19 +43,12 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<table id="code" class="table table-borderless table-condensed">
|
||||
<tbody>
|
||||
{{lines}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{structure}}
|
||||
<footer>
|
||||
<hr/>
|
||||
<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>
|
||||
{{legend}}
|
||||
<p>
|
||||
<small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
|
||||
</p>
|
||||
|
67
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_branch.html.dist
vendored
Normal file
67
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_branch.html.dist
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Code Coverage for {{full_path}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="{{path_to_root}}_css/bootstrap.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/octicons.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/style.css" rel="stylesheet" type="text/css">
|
||||
<link href="{{path_to_root}}_css/custom.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
{{breadcrumbs}}
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container-fluid">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td colspan="16"><div align="center"><strong>Code Coverage</strong></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td colspan="3"><div align="center"><strong>Lines</strong></div></td>
|
||||
<td colspan="3"><div align="center"><strong>Branches</strong></div></td>
|
||||
<td colspan="3"><div align="center"><strong>Paths</strong></div></td>
|
||||
<td colspan="4"><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>
|
||||
</div>
|
||||
{{lines}}
|
||||
{{structure}}
|
||||
<footer>
|
||||
<hr/>
|
||||
<h4>Legend</h4>
|
||||
{{legend}}
|
||||
<p>
|
||||
<small>Generated by <a href="https://github.com/sebastianbergmann/php-code-coverage" target="_top">php-code-coverage {{version}}</a> using {{runtime}}{{generator}} at {{date}}.</small>
|
||||
</p>
|
||||
<a title="Back to the top" id="toplink" href="#">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M12 11L6 5l-6 6h12z"/></svg>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="{{path_to_root}}_js/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="{{path_to_root}}_js/popper.min.js" type="text/javascript"></script>
|
||||
<script src="{{path_to_root}}_js/bootstrap.min.js" type="text/javascript"></script>
|
||||
<script src="{{path_to_root}}_js/file.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
@@ -1,14 +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="{{lines_level}}">{{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="{{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>
|
||||
<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>
|
||||
|
||||
|
20
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item_branch.html.dist
vendored
Normal file
20
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/file_item_branch.html.dist
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<tr>
|
||||
<td class="{{lines_level}}">{{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="{{branches_level}} big">{{branches_bar}}</td>
|
||||
<td class="{{branches_level}} small"><div align="right">{{branches_executed_percent}}</div></td>
|
||||
<td class="{{branches_level}} small"><div align="right">{{branches_number}}</div></td>
|
||||
<td class="{{paths_level}} big">{{paths_bar}}</td>
|
||||
<td class="{{paths_level}} small"><div align="right">{{paths_executed_percent}}</div></td>
|
||||
<td class="{{paths_level}} small"><div align="right">{{paths_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="{{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>
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/line.html.dist
vendored
Normal file
1
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/line.html.dist
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<tr class="{{class}} d-flex"><td {{popover}} class="col-1 text-right"><a id="{{lineNumber}}" href="#{{lineNumber}}">{{lineNumber}}</a></td><td class="col-11 codeLine">{{lineContent}}</td></tr>
|
5
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/lines.html.dist
vendored
Normal file
5
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/lines.html.dist
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<table id="code" class="table table-borderless table-condensed">
|
||||
<tbody>
|
||||
{{lines}}
|
||||
</tbody>
|
||||
</table>
|
@@ -1,11 +1,12 @@
|
||||
<tr>
|
||||
<td class="{{methods_level}}" colspan="4">{{name}}</td>
|
||||
<td class="{{lines_level}}">{{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="{{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>
|
||||
<td class="{{methods_level}}" colspan="3"></td>
|
||||
</tr>
|
||||
|
||||
|
18
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/method_item_branch.html.dist
vendored
Normal file
18
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/method_item_branch.html.dist
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<tr>
|
||||
<td class="{{lines_level}}">{{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="{{branches_level}} big">{{branches_bar}}</td>
|
||||
<td class="{{branches_level}} small"><div align="right">{{branches_executed_percent}}</div></td>
|
||||
<td class="{{branches_level}} small"><div align="right">{{branches_number}}</div></td>
|
||||
<td class="{{paths_level}} big">{{paths_bar}}</td>
|
||||
<td class="{{paths_level}} small"><div align="right">{{paths_executed_percent}}</div></td>
|
||||
<td class="{{paths_level}} small"><div align="right">{{paths_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="{{methods_level}}" colspan="3"></td>
|
||||
</tr>
|
||||
|
9
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/paths.html.dist
vendored
Normal file
9
vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/paths.html.dist
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<hr/>
|
||||
<h4>Paths</h4>
|
||||
<p>
|
||||
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
|
||||
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
|
||||
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an <code>if</code> statement
|
||||
<i>always</i> has an <code>else</code> as part of its logical flow even if you didn't write one.
|
||||
</p>
|
||||
{{paths}}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,56 +9,28 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report;
|
||||
|
||||
use function dirname;
|
||||
use function file_put_contents;
|
||||
use function serialize;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
|
||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||
|
||||
/**
|
||||
* Uses var_export() to write a SebastianBergmann\CodeCoverage\CodeCoverage object to a file.
|
||||
*/
|
||||
final class PHP
|
||||
{
|
||||
/**
|
||||
* @throws \SebastianBergmann\CodeCoverage\RuntimeException
|
||||
*/
|
||||
public function process(CodeCoverage $coverage, ?string $target = null): string
|
||||
{
|
||||
$filter = $coverage->filter();
|
||||
|
||||
$buffer = \sprintf(
|
||||
'<?php
|
||||
$coverage = new SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
$coverage->setData(%s);
|
||||
$coverage->setTests(%s);
|
||||
|
||||
$filter = $coverage->filter();
|
||||
$filter->setWhitelistedFiles(%s);
|
||||
|
||||
return $coverage;',
|
||||
\var_export($coverage->getData(true), true),
|
||||
\var_export($coverage->getTests(), true),
|
||||
\var_export($filter->getWhitelistedFiles(), true)
|
||||
);
|
||||
$buffer = "<?php
|
||||
return \unserialize(<<<'END_OF_COVERAGE_SERIALIZATION'" . PHP_EOL . serialize($coverage) . PHP_EOL . 'END_OF_COVERAGE_SERIALIZATION' . PHP_EOL . ');';
|
||||
|
||||
if ($target !== null) {
|
||||
if (!$this->createDirectory(\dirname($target))) {
|
||||
throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target)));
|
||||
}
|
||||
Filesystem::createDirectory(dirname($target));
|
||||
|
||||
if (@\file_put_contents($target, $buffer) === false) {
|
||||
throw new RuntimeException(
|
||||
\sprintf(
|
||||
'Could not write to "%s',
|
||||
$target
|
||||
)
|
||||
);
|
||||
if (@file_put_contents($target, $buffer) === false) {
|
||||
throw new WriteOperationFailedException($target);
|
||||
}
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
private function createDirectory(string $directory): bool
|
||||
{
|
||||
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
|
||||
}
|
||||
}
|
||||
|
248
vendor/phpunit/php-code-coverage/src/Report/Text.php
vendored
248
vendor/phpunit/php-code-coverage/src/Report/Text.php
vendored
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,15 +9,18 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report;
|
||||
|
||||
use const PHP_EOL;
|
||||
use function array_map;
|
||||
use function date;
|
||||
use function ksort;
|
||||
use function max;
|
||||
use function sprintf;
|
||||
use function str_pad;
|
||||
use function strlen;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Node\File;
|
||||
use SebastianBergmann\CodeCoverage\Util;
|
||||
use SebastianBergmann\CodeCoverage\Util\Percentage;
|
||||
|
||||
/**
|
||||
* Generates human readable output from a code coverage object.
|
||||
*
|
||||
* The output gets put into a text file our written to the CLI.
|
||||
*/
|
||||
final class Text
|
||||
{
|
||||
/**
|
||||
@@ -80,32 +83,46 @@ final class Text
|
||||
|
||||
public function process(CodeCoverage $coverage, bool $showColors = false): string
|
||||
{
|
||||
$output = \PHP_EOL . \PHP_EOL;
|
||||
$hasBranchCoverage = !empty($coverage->getData(true)->functionCoverage());
|
||||
|
||||
$output = PHP_EOL . PHP_EOL;
|
||||
$report = $coverage->getReport();
|
||||
|
||||
$colors = [
|
||||
'header' => '',
|
||||
'classes' => '',
|
||||
'methods' => '',
|
||||
'lines' => '',
|
||||
'reset' => '',
|
||||
'eol' => '',
|
||||
'header' => '',
|
||||
'classes' => '',
|
||||
'methods' => '',
|
||||
'lines' => '',
|
||||
'branches' => '',
|
||||
'paths' => '',
|
||||
'reset' => '',
|
||||
'eol' => '',
|
||||
];
|
||||
|
||||
if ($showColors) {
|
||||
$colors['classes'] = $this->getCoverageColor(
|
||||
$report->getNumTestedClassesAndTraits(),
|
||||
$report->getNumClassesAndTraits()
|
||||
$colors['classes'] = $this->coverageColor(
|
||||
$report->numberOfTestedClassesAndTraits(),
|
||||
$report->numberOfClassesAndTraits()
|
||||
);
|
||||
|
||||
$colors['methods'] = $this->getCoverageColor(
|
||||
$report->getNumTestedMethods(),
|
||||
$report->getNumMethods()
|
||||
$colors['methods'] = $this->coverageColor(
|
||||
$report->numberOfTestedMethods(),
|
||||
$report->numberOfMethods()
|
||||
);
|
||||
|
||||
$colors['lines'] = $this->getCoverageColor(
|
||||
$report->getNumExecutedLines(),
|
||||
$report->getNumExecutableLines()
|
||||
$colors['lines'] = $this->coverageColor(
|
||||
$report->numberOfExecutedLines(),
|
||||
$report->numberOfExecutableLines()
|
||||
);
|
||||
|
||||
$colors['branches'] = $this->coverageColor(
|
||||
$report->numberOfExecutedBranches(),
|
||||
$report->numberOfExecutableBranches()
|
||||
);
|
||||
|
||||
$colors['paths'] = $this->coverageColor(
|
||||
$report->numberOfExecutedPaths(),
|
||||
$report->numberOfExecutablePaths()
|
||||
);
|
||||
|
||||
$colors['reset'] = self::COLOR_RESET;
|
||||
@@ -113,48 +130,70 @@ final class Text
|
||||
$colors['eol'] = self::COLOR_EOL;
|
||||
}
|
||||
|
||||
$classes = \sprintf(
|
||||
$classes = sprintf(
|
||||
' Classes: %6s (%d/%d)',
|
||||
Util::percent(
|
||||
$report->getNumTestedClassesAndTraits(),
|
||||
$report->getNumClassesAndTraits(),
|
||||
true
|
||||
),
|
||||
$report->getNumTestedClassesAndTraits(),
|
||||
$report->getNumClassesAndTraits()
|
||||
Percentage::fromFractionAndTotal(
|
||||
$report->numberOfTestedClassesAndTraits(),
|
||||
$report->numberOfClassesAndTraits()
|
||||
)->asString(),
|
||||
$report->numberOfTestedClassesAndTraits(),
|
||||
$report->numberOfClassesAndTraits()
|
||||
);
|
||||
|
||||
$methods = \sprintf(
|
||||
$methods = sprintf(
|
||||
' Methods: %6s (%d/%d)',
|
||||
Util::percent(
|
||||
$report->getNumTestedMethods(),
|
||||
$report->getNumMethods(),
|
||||
true
|
||||
),
|
||||
$report->getNumTestedMethods(),
|
||||
$report->getNumMethods()
|
||||
Percentage::fromFractionAndTotal(
|
||||
$report->numberOfTestedMethods(),
|
||||
$report->numberOfMethods(),
|
||||
)->asString(),
|
||||
$report->numberOfTestedMethods(),
|
||||
$report->numberOfMethods()
|
||||
);
|
||||
|
||||
$lines = \sprintf(
|
||||
$paths = '';
|
||||
$branches = '';
|
||||
|
||||
if ($hasBranchCoverage) {
|
||||
$paths = sprintf(
|
||||
' Paths: %6s (%d/%d)',
|
||||
Percentage::fromFractionAndTotal(
|
||||
$report->numberOfExecutedPaths(),
|
||||
$report->numberOfExecutablePaths(),
|
||||
)->asString(),
|
||||
$report->numberOfExecutedPaths(),
|
||||
$report->numberOfExecutablePaths()
|
||||
);
|
||||
|
||||
$branches = sprintf(
|
||||
' Branches: %6s (%d/%d)',
|
||||
Percentage::fromFractionAndTotal(
|
||||
$report->numberOfExecutedBranches(),
|
||||
$report->numberOfExecutableBranches(),
|
||||
)->asString(),
|
||||
$report->numberOfExecutedBranches(),
|
||||
$report->numberOfExecutableBranches()
|
||||
);
|
||||
}
|
||||
|
||||
$lines = sprintf(
|
||||
' Lines: %6s (%d/%d)',
|
||||
Util::percent(
|
||||
$report->getNumExecutedLines(),
|
||||
$report->getNumExecutableLines(),
|
||||
true
|
||||
),
|
||||
$report->getNumExecutedLines(),
|
||||
$report->getNumExecutableLines()
|
||||
Percentage::fromFractionAndTotal(
|
||||
$report->numberOfExecutedLines(),
|
||||
$report->numberOfExecutableLines(),
|
||||
)->asString(),
|
||||
$report->numberOfExecutedLines(),
|
||||
$report->numberOfExecutableLines()
|
||||
);
|
||||
|
||||
$padding = \max(\array_map('strlen', [$classes, $methods, $lines]));
|
||||
$padding = max(array_map('strlen', [$classes, $methods, $lines]));
|
||||
|
||||
if ($this->showOnlySummary) {
|
||||
$title = 'Code Coverage Report Summary:';
|
||||
$padding = \max($padding, \strlen($title));
|
||||
$padding = max($padding, strlen($title));
|
||||
|
||||
$output .= $this->format($colors['header'], $padding, $title);
|
||||
} else {
|
||||
$date = \date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']);
|
||||
$date = date(' Y-m-d H:i:s');
|
||||
$title = 'Code Coverage Report:';
|
||||
|
||||
$output .= $this->format($colors['header'], $padding, $title);
|
||||
@@ -165,10 +204,15 @@ final class Text
|
||||
|
||||
$output .= $this->format($colors['classes'], $padding, $classes);
|
||||
$output .= $this->format($colors['methods'], $padding, $methods);
|
||||
|
||||
if ($hasBranchCoverage) {
|
||||
$output .= $this->format($colors['paths'], $padding, $paths);
|
||||
$output .= $this->format($colors['branches'], $padding, $branches);
|
||||
}
|
||||
$output .= $this->format($colors['lines'], $padding, $lines);
|
||||
|
||||
if ($this->showOnlySummary) {
|
||||
return $output . \PHP_EOL;
|
||||
return $output . PHP_EOL;
|
||||
}
|
||||
|
||||
$classCoverage = [];
|
||||
@@ -178,13 +222,17 @@ final class Text
|
||||
continue;
|
||||
}
|
||||
|
||||
$classes = $item->getClassesAndTraits();
|
||||
$classes = $item->classesAndTraits();
|
||||
|
||||
foreach ($classes as $className => $class) {
|
||||
$classStatements = 0;
|
||||
$coveredClassStatements = 0;
|
||||
$coveredMethods = 0;
|
||||
$classMethods = 0;
|
||||
$classExecutableLines = 0;
|
||||
$classExecutedLines = 0;
|
||||
$classExecutableBranches = 0;
|
||||
$classExecutedBranches = 0;
|
||||
$classExecutablePaths = 0;
|
||||
$classExecutedPaths = 0;
|
||||
$coveredMethods = 0;
|
||||
$classMethods = 0;
|
||||
|
||||
foreach ($class['methods'] as $method) {
|
||||
if ($method['executableLines'] == 0) {
|
||||
@@ -192,68 +240,77 @@ final class Text
|
||||
}
|
||||
|
||||
$classMethods++;
|
||||
$classStatements += $method['executableLines'];
|
||||
$coveredClassStatements += $method['executedLines'];
|
||||
$classExecutableLines += $method['executableLines'];
|
||||
$classExecutedLines += $method['executedLines'];
|
||||
$classExecutableBranches += $method['executableBranches'];
|
||||
$classExecutedBranches += $method['executedBranches'];
|
||||
$classExecutablePaths += $method['executablePaths'];
|
||||
$classExecutedPaths += $method['executedPaths'];
|
||||
|
||||
if ($method['coverage'] == 100) {
|
||||
$coveredMethods++;
|
||||
}
|
||||
}
|
||||
|
||||
$namespace = '';
|
||||
|
||||
if (!empty($class['package']['namespace'])) {
|
||||
$namespace = '\\' . $class['package']['namespace'] . '::';
|
||||
} elseif (!empty($class['package']['fullPackage'])) {
|
||||
$namespace = '@' . $class['package']['fullPackage'] . '::';
|
||||
}
|
||||
|
||||
$classCoverage[$namespace . $className] = [
|
||||
'namespace' => $namespace,
|
||||
'className ' => $className,
|
||||
$classCoverage[$className] = [
|
||||
'namespace' => $class['namespace'],
|
||||
'className' => $className,
|
||||
'methodsCovered' => $coveredMethods,
|
||||
'methodCount' => $classMethods,
|
||||
'statementsCovered' => $coveredClassStatements,
|
||||
'statementCount' => $classStatements,
|
||||
'statementsCovered' => $classExecutedLines,
|
||||
'statementCount' => $classExecutableLines,
|
||||
'branchesCovered' => $classExecutedBranches,
|
||||
'branchesCount' => $classExecutableBranches,
|
||||
'pathsCovered' => $classExecutedPaths,
|
||||
'pathsCount' => $classExecutablePaths,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
\ksort($classCoverage);
|
||||
ksort($classCoverage);
|
||||
|
||||
$methodColor = '';
|
||||
$linesColor = '';
|
||||
$resetColor = '';
|
||||
$methodColor = '';
|
||||
$pathsColor = '';
|
||||
$branchesColor = '';
|
||||
$linesColor = '';
|
||||
$resetColor = '';
|
||||
|
||||
foreach ($classCoverage as $fullQualifiedPath => $classInfo) {
|
||||
if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) {
|
||||
if ($showColors) {
|
||||
$methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
|
||||
$linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']);
|
||||
$resetColor = $colors['reset'];
|
||||
$methodColor = $this->coverageColor($classInfo['methodsCovered'], $classInfo['methodCount']);
|
||||
$pathsColor = $this->coverageColor($classInfo['pathsCovered'], $classInfo['pathsCount']);
|
||||
$branchesColor = $this->coverageColor($classInfo['branchesCovered'], $classInfo['branchesCount']);
|
||||
$linesColor = $this->coverageColor($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;
|
||||
$output .= PHP_EOL . $fullQualifiedPath . PHP_EOL
|
||||
. ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' ';
|
||||
|
||||
if ($hasBranchCoverage) {
|
||||
$output .= ' ' . $pathsColor . 'Paths: ' . $this->printCoverageCounts($classInfo['pathsCovered'], $classInfo['pathsCount'], 3) . $resetColor . ' '
|
||||
. ' ' . $branchesColor . 'Branches: ' . $this->printCoverageCounts($classInfo['branchesCovered'], $classInfo['branchesCount'], 3) . $resetColor . ' ';
|
||||
}
|
||||
$output .= ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor;
|
||||
}
|
||||
}
|
||||
|
||||
return $output . \PHP_EOL;
|
||||
return $output . PHP_EOL;
|
||||
}
|
||||
|
||||
private function getCoverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string
|
||||
private function coverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string
|
||||
{
|
||||
$coverage = Util::percent(
|
||||
$coverage = Percentage::fromFractionAndTotal(
|
||||
$numberOfCoveredElements,
|
||||
$totalNumberOfElements
|
||||
);
|
||||
|
||||
if ($coverage >= $this->highLowerBound) {
|
||||
if ($coverage->asFloat() >= $this->highLowerBound) {
|
||||
return self::COLOR_GREEN;
|
||||
}
|
||||
|
||||
if ($coverage > $this->lowUpperBound) {
|
||||
if ($coverage->asFloat() > $this->lowUpperBound) {
|
||||
return self::COLOR_YELLOW;
|
||||
}
|
||||
|
||||
@@ -264,20 +321,21 @@ final class Text
|
||||
{
|
||||
$format = '%' . $precision . 's';
|
||||
|
||||
return Util::percent(
|
||||
return Percentage::fromFractionAndTotal(
|
||||
$numberOfCoveredElements,
|
||||
$totalNumberOfElements,
|
||||
true,
|
||||
true
|
||||
) .
|
||||
' (' . \sprintf($format, $numberOfCoveredElements) . '/' .
|
||||
\sprintf($format, $totalNumberOfElements) . ')';
|
||||
$totalNumberOfElements
|
||||
)->asFixedWidthString() .
|
||||
' (' . sprintf($format, $numberOfCoveredElements) . '/' .
|
||||
sprintf($format, $totalNumberOfElements) . ')';
|
||||
}
|
||||
|
||||
private function format($color, $padding, $string): string
|
||||
/**
|
||||
* @param false|string $string
|
||||
*/
|
||||
private function format(string $color, int $padding, $string): string
|
||||
{
|
||||
$reset = $color ? self::COLOR_RESET : '';
|
||||
|
||||
return $color . \str_pad($string, $padding) . $reset . \PHP_EOL;
|
||||
return $color . str_pad((string) $string, $padding) . $reset . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,47 +9,54 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use function constant;
|
||||
use function phpversion;
|
||||
use DateTimeImmutable;
|
||||
use DOMElement;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class BuildInformation
|
||||
{
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $contextNode;
|
||||
|
||||
public function __construct(\DOMElement $contextNode)
|
||||
public function __construct(DOMElement $contextNode)
|
||||
{
|
||||
$this->contextNode = $contextNode;
|
||||
}
|
||||
|
||||
public function setRuntimeInformation(Runtime $runtime): void
|
||||
{
|
||||
$runtimeNode = $this->getNodeByName('runtime');
|
||||
$runtimeNode = $this->nodeByName('runtime');
|
||||
|
||||
$runtimeNode->setAttribute('name', $runtime->getName());
|
||||
$runtimeNode->setAttribute('version', $runtime->getVersion());
|
||||
$runtimeNode->setAttribute('url', $runtime->getVendorUrl());
|
||||
|
||||
$driverNode = $this->getNodeByName('driver');
|
||||
$driverNode = $this->nodeByName('driver');
|
||||
|
||||
if ($runtime->hasPHPDBGCodeCoverage()) {
|
||||
$driverNode->setAttribute('name', 'phpdbg');
|
||||
$driverNode->setAttribute('version', \constant('PHPDBG_VERSION'));
|
||||
$driverNode->setAttribute('version', constant('PHPDBG_VERSION'));
|
||||
}
|
||||
|
||||
if ($runtime->hasXdebug()) {
|
||||
$driverNode->setAttribute('name', 'xdebug');
|
||||
$driverNode->setAttribute('version', \phpversion('xdebug'));
|
||||
$driverNode->setAttribute('version', phpversion('xdebug'));
|
||||
}
|
||||
|
||||
if ($runtime->hasPCOV()) {
|
||||
$driverNode->setAttribute('name', 'pcov');
|
||||
$driverNode->setAttribute('version', \phpversion('pcov'));
|
||||
$driverNode->setAttribute('version', phpversion('pcov'));
|
||||
}
|
||||
}
|
||||
|
||||
public function setBuildTime(\DateTime $date): void
|
||||
public function setBuildTime(DateTimeImmutable $date): void
|
||||
{
|
||||
$this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y'));
|
||||
}
|
||||
@@ -60,7 +67,7 @@ final class BuildInformation
|
||||
$this->contextNode->setAttribute('coverage', $coverageVersion);
|
||||
}
|
||||
|
||||
private function getNodeByName(string $name): \DOMElement
|
||||
private function nodeByName(string $name): DOMElement
|
||||
{
|
||||
$node = $this->contextNode->getElementsByTagNameNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,17 +9,22 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
use DOMElement;
|
||||
use SebastianBergmann\CodeCoverage\ReportAlreadyFinalizedException;
|
||||
use XMLWriter;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Coverage
|
||||
{
|
||||
/**
|
||||
* @var \XMLWriter
|
||||
* @var XMLWriter
|
||||
*/
|
||||
private $writer;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $contextNode;
|
||||
|
||||
@@ -28,23 +33,23 @@ final class Coverage
|
||||
*/
|
||||
private $finalized = false;
|
||||
|
||||
public function __construct(\DOMElement $context, string $line)
|
||||
public function __construct(DOMElement $context, string $line)
|
||||
{
|
||||
$this->contextNode = $context;
|
||||
|
||||
$this->writer = new \XMLWriter();
|
||||
$this->writer = new XMLWriter;
|
||||
$this->writer->openMemory();
|
||||
$this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0');
|
||||
$this->writer->writeAttribute('nr', $line);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws ReportAlreadyFinalizedException
|
||||
*/
|
||||
public function addTest(string $test): void
|
||||
{
|
||||
if ($this->finalized) {
|
||||
throw new RuntimeException('Coverage Report already finalized');
|
||||
throw new ReportAlreadyFinalizedException;
|
||||
}
|
||||
|
||||
$this->writer->startElement('covered');
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Directory extends Node
|
||||
{
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,12 +9,33 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
use const PHP_EOL;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function is_array;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function is_writable;
|
||||
use function libxml_clear_errors;
|
||||
use function libxml_get_errors;
|
||||
use function libxml_use_internal_errors;
|
||||
use function sprintf;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
use DateTimeImmutable;
|
||||
use DOMDocument;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException;
|
||||
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
|
||||
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
|
||||
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
|
||||
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
|
||||
use SebastianBergmann\CodeCoverage\RuntimeException;
|
||||
use SebastianBergmann\CodeCoverage\Util\Filesystem as DirectoryUtil;
|
||||
use SebastianBergmann\CodeCoverage\Version;
|
||||
use SebastianBergmann\CodeCoverage\XmlException;
|
||||
use SebastianBergmann\Environment\Runtime;
|
||||
|
||||
final class Facade
|
||||
@@ -40,12 +61,12 @@ final class Facade
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws XmlException
|
||||
*/
|
||||
public function process(CodeCoverage $coverage, string $target): void
|
||||
{
|
||||
if (\substr($target, -1, 1) !== \DIRECTORY_SEPARATOR) {
|
||||
$target .= \DIRECTORY_SEPARATOR;
|
||||
if (substr($target, -1, 1) !== DIRECTORY_SEPARATOR) {
|
||||
$target .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
$this->target = $target;
|
||||
@@ -54,7 +75,7 @@ final class Facade
|
||||
$report = $coverage->getReport();
|
||||
|
||||
$this->project = new Project(
|
||||
$coverage->getReport()->getName()
|
||||
$coverage->getReport()->name()
|
||||
);
|
||||
|
||||
$this->setBuildInformation();
|
||||
@@ -66,92 +87,90 @@ final class Facade
|
||||
|
||||
private function setBuildInformation(): void
|
||||
{
|
||||
$buildNode = $this->project->getBuildInformation();
|
||||
$buildNode->setRuntimeInformation(new Runtime());
|
||||
$buildNode->setBuildTime(\DateTime::createFromFormat('U', (string) $_SERVER['REQUEST_TIME']));
|
||||
$buildNode = $this->project->buildInformation();
|
||||
$buildNode->setRuntimeInformation(new Runtime);
|
||||
$buildNode->setBuildTime(new DateTimeImmutable);
|
||||
$buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws PathExistsButIsNotDirectoryException
|
||||
* @throws WriteOperationFailedException
|
||||
*/
|
||||
private function initTargetDirectory(string $directory): void
|
||||
{
|
||||
if (\file_exists($directory)) {
|
||||
if (!\is_dir($directory)) {
|
||||
throw new RuntimeException(
|
||||
"'$directory' exists but is not a directory."
|
||||
);
|
||||
if (is_file($directory)) {
|
||||
if (!is_dir($directory)) {
|
||||
throw new PathExistsButIsNotDirectoryException($directory);
|
||||
}
|
||||
|
||||
if (!\is_writable($directory)) {
|
||||
throw new RuntimeException(
|
||||
"'$directory' exists but is not writable."
|
||||
);
|
||||
if (!is_writable($directory)) {
|
||||
throw new WriteOperationFailedException($directory);
|
||||
}
|
||||
} elseif (!$this->createDirectory($directory)) {
|
||||
throw new RuntimeException(
|
||||
"'$directory' could not be created."
|
||||
);
|
||||
}
|
||||
|
||||
DirectoryUtil::createDirectory($directory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws XmlException
|
||||
*/
|
||||
private function processDirectory(DirectoryNode $directory, Node $context): void
|
||||
{
|
||||
$directoryName = $directory->getName();
|
||||
$directoryName = $directory->name();
|
||||
|
||||
if ($this->project->getProjectSourceDirectory() === $directoryName) {
|
||||
if ($this->project->projectSourceDirectory() === $directoryName) {
|
||||
$directoryName = '/';
|
||||
}
|
||||
|
||||
$directoryObject = $context->addDirectory($directoryName);
|
||||
|
||||
$this->setTotals($directory, $directoryObject->getTotals());
|
||||
$this->setTotals($directory, $directoryObject->totals());
|
||||
|
||||
foreach ($directory->getDirectories() as $node) {
|
||||
foreach ($directory->directories() as $node) {
|
||||
$this->processDirectory($node, $directoryObject);
|
||||
}
|
||||
|
||||
foreach ($directory->getFiles() as $node) {
|
||||
foreach ($directory->files() as $node) {
|
||||
$this->processFile($node, $directoryObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws XmlException
|
||||
*/
|
||||
private function processFile(FileNode $file, Directory $context): void
|
||||
{
|
||||
$fileObject = $context->addFile(
|
||||
$file->getName(),
|
||||
$file->getId() . '.xml'
|
||||
$file->name(),
|
||||
$file->id() . '.xml'
|
||||
);
|
||||
|
||||
$this->setTotals($file, $fileObject->getTotals());
|
||||
$this->setTotals($file, $fileObject->totals());
|
||||
|
||||
$path = \substr(
|
||||
$file->getPath(),
|
||||
\strlen($this->project->getProjectSourceDirectory())
|
||||
$path = substr(
|
||||
$file->pathAsString(),
|
||||
strlen($this->project->projectSourceDirectory())
|
||||
);
|
||||
|
||||
$fileReport = new Report($path);
|
||||
|
||||
$this->setTotals($file, $fileReport->getTotals());
|
||||
$this->setTotals($file, $fileReport->totals());
|
||||
|
||||
foreach ($file->getClassesAndTraits() as $unit) {
|
||||
foreach ($file->classesAndTraits() as $unit) {
|
||||
$this->processUnit($unit, $fileReport);
|
||||
}
|
||||
|
||||
foreach ($file->getFunctions() as $function) {
|
||||
foreach ($file->functions() as $function) {
|
||||
$this->processFunction($function, $fileReport);
|
||||
}
|
||||
|
||||
foreach ($file->getCoverageData() as $line => $tests) {
|
||||
if (!\is_array($tests) || \count($tests) === 0) {
|
||||
foreach ($file->lineCoverageData() as $line => $tests) {
|
||||
if (!is_array($tests) || count($tests) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$coverage = $fileReport->getLineCoverage((string) $line);
|
||||
$coverage = $fileReport->lineCoverage((string) $line);
|
||||
|
||||
foreach ($tests as $test) {
|
||||
$coverage->addTest($test);
|
||||
@@ -160,19 +179,19 @@ final class Facade
|
||||
$coverage->finalize();
|
||||
}
|
||||
|
||||
$fileReport->getSource()->setSourceCode(
|
||||
\file_get_contents($file->getPath())
|
||||
$fileReport->source()->setSourceCode(
|
||||
file_get_contents($file->pathAsString())
|
||||
);
|
||||
|
||||
$this->saveDocument($fileReport->asDom(), $file->getId());
|
||||
$this->saveDocument($fileReport->asDom(), $file->id());
|
||||
}
|
||||
|
||||
private function processUnit(array $unit, Report $report): void
|
||||
{
|
||||
if (isset($unit['className'])) {
|
||||
$unitObject = $report->getClassObject($unit['className']);
|
||||
$unitObject = $report->classObject($unit['className']);
|
||||
} else {
|
||||
$unitObject = $report->getTraitObject($unit['traitName']);
|
||||
$unitObject = $report->traitObject($unit['traitName']);
|
||||
}
|
||||
|
||||
$unitObject->setLines(
|
||||
@@ -182,15 +201,7 @@ final class Facade
|
||||
);
|
||||
|
||||
$unitObject->setCrap((float) $unit['crap']);
|
||||
|
||||
$unitObject->setPackage(
|
||||
$unit['package']['fullPackage'],
|
||||
$unit['package']['package'],
|
||||
$unit['package']['subpackage'],
|
||||
$unit['package']['category']
|
||||
);
|
||||
|
||||
$unitObject->setNamespace($unit['package']['namespace']);
|
||||
$unitObject->setNamespace($unit['namespace']);
|
||||
|
||||
foreach ($unit['methods'] as $method) {
|
||||
$methodObject = $unitObject->addMethod($method['methodName']);
|
||||
@@ -207,7 +218,7 @@ final class Facade
|
||||
|
||||
private function processFunction(array $function, Report $report): void
|
||||
{
|
||||
$functionObject = $report->getFunctionObject($function['functionName']);
|
||||
$functionObject = $report->functionObject($function['functionName']);
|
||||
|
||||
$functionObject->setSignature($function['signature']);
|
||||
$functionObject->setLines((string) $function['startLine']);
|
||||
@@ -217,71 +228,88 @@ final class Facade
|
||||
|
||||
private function processTests(array $tests): void
|
||||
{
|
||||
$testsObject = $this->project->getTests();
|
||||
$testsObject = $this->project->tests();
|
||||
|
||||
foreach ($tests as $test => $result) {
|
||||
if ($test === 'UNCOVERED_FILES_FROM_WHITELIST') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$testsObject->addTest($test, $result);
|
||||
}
|
||||
}
|
||||
|
||||
private function setTotals(AbstractNode $node, Totals $totals): void
|
||||
{
|
||||
$loc = $node->getLinesOfCode();
|
||||
$loc = $node->linesOfCode();
|
||||
|
||||
$totals->setNumLines(
|
||||
$loc['loc'],
|
||||
$loc['cloc'],
|
||||
$loc['ncloc'],
|
||||
$node->getNumExecutableLines(),
|
||||
$node->getNumExecutedLines()
|
||||
$loc['linesOfCode'],
|
||||
$loc['commentLinesOfCode'],
|
||||
$loc['nonCommentLinesOfCode'],
|
||||
$node->numberOfExecutableLines(),
|
||||
$node->numberOfExecutedLines()
|
||||
);
|
||||
|
||||
$totals->setNumClasses(
|
||||
$node->getNumClasses(),
|
||||
$node->getNumTestedClasses()
|
||||
$node->numberOfClasses(),
|
||||
$node->numberOfTestedClasses()
|
||||
);
|
||||
|
||||
$totals->setNumTraits(
|
||||
$node->getNumTraits(),
|
||||
$node->getNumTestedTraits()
|
||||
$node->numberOfTraits(),
|
||||
$node->numberOfTestedTraits()
|
||||
);
|
||||
|
||||
$totals->setNumMethods(
|
||||
$node->getNumMethods(),
|
||||
$node->getNumTestedMethods()
|
||||
$node->numberOfMethods(),
|
||||
$node->numberOfTestedMethods()
|
||||
);
|
||||
|
||||
$totals->setNumFunctions(
|
||||
$node->getNumFunctions(),
|
||||
$node->getNumTestedFunctions()
|
||||
$node->numberOfFunctions(),
|
||||
$node->numberOfTestedFunctions()
|
||||
);
|
||||
}
|
||||
|
||||
private function getTargetDirectory(): string
|
||||
private function targetDirectory(): string
|
||||
{
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws XmlException
|
||||
*/
|
||||
private function saveDocument(\DOMDocument $document, string $name): void
|
||||
private function saveDocument(DOMDocument $document, string $name): void
|
||||
{
|
||||
$filename = \sprintf('%s/%s.xml', $this->getTargetDirectory(), $name);
|
||||
$filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name);
|
||||
|
||||
$document->formatOutput = true;
|
||||
$document->preserveWhiteSpace = false;
|
||||
$this->initTargetDirectory(\dirname($filename));
|
||||
$this->initTargetDirectory(dirname($filename));
|
||||
|
||||
$document->save($filename);
|
||||
file_put_contents($filename, $this->documentAsString($document));
|
||||
}
|
||||
|
||||
private function createDirectory(string $directory): bool
|
||||
/**
|
||||
* @throws XmlException
|
||||
*
|
||||
* @see https://bugs.php.net/bug.php?id=79191
|
||||
*/
|
||||
private function documentAsString(DOMDocument $document): string
|
||||
{
|
||||
return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory));
|
||||
$xmlErrorHandling = libxml_use_internal_errors(true);
|
||||
$xml = $document->saveXML();
|
||||
|
||||
if ($xml === false) {
|
||||
$message = 'Unable to generate the XML';
|
||||
|
||||
foreach (libxml_get_errors() as $error) {
|
||||
$message .= PHP_EOL . $error->message;
|
||||
}
|
||||
|
||||
throw new XmlException($message);
|
||||
}
|
||||
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($xmlErrorHandling);
|
||||
|
||||
return $xml;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,25 +9,31 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
class File
|
||||
{
|
||||
/**
|
||||
* @var \DOMDocument
|
||||
* @var DOMDocument
|
||||
*/
|
||||
private $dom;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $contextNode;
|
||||
|
||||
public function __construct(\DOMElement $context)
|
||||
public function __construct(DOMElement $context)
|
||||
{
|
||||
$this->dom = $context->ownerDocument;
|
||||
$this->contextNode = $context;
|
||||
}
|
||||
|
||||
public function getTotals(): Totals
|
||||
public function totals(): Totals
|
||||
{
|
||||
$totalsContainer = $this->contextNode->firstChild;
|
||||
|
||||
@@ -43,7 +49,7 @@ class File
|
||||
return new Totals($totalsContainer);
|
||||
}
|
||||
|
||||
public function getLineCoverage(string $line): Coverage
|
||||
public function lineCoverage(string $line): Coverage
|
||||
{
|
||||
$coverage = $this->contextNode->getElementsByTagNameNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
@@ -69,12 +75,12 @@ class File
|
||||
return new Coverage($lineNode, $line);
|
||||
}
|
||||
|
||||
protected function getContextNode(): \DOMElement
|
||||
protected function contextNode(): DOMElement
|
||||
{
|
||||
return $this->contextNode;
|
||||
}
|
||||
|
||||
protected function getDomDocument(): \DOMDocument
|
||||
protected function dom(): DOMDocument
|
||||
{
|
||||
return $this->dom;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,14 +9,19 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use DOMElement;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Method
|
||||
{
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $contextNode;
|
||||
|
||||
public function __construct(\DOMElement $context, string $name)
|
||||
public function __construct(DOMElement $context, string $name)
|
||||
{
|
||||
$this->contextNode = $context;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,34 +9,40 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
abstract class Node
|
||||
{
|
||||
/**
|
||||
* @var \DOMDocument
|
||||
* @var DOMDocument
|
||||
*/
|
||||
private $dom;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $contextNode;
|
||||
|
||||
public function __construct(\DOMElement $context)
|
||||
public function __construct(DOMElement $context)
|
||||
{
|
||||
$this->setContextNode($context);
|
||||
}
|
||||
|
||||
public function getDom(): \DOMDocument
|
||||
public function dom(): DOMDocument
|
||||
{
|
||||
return $this->dom;
|
||||
}
|
||||
|
||||
public function getTotals(): Totals
|
||||
public function totals(): Totals
|
||||
{
|
||||
$totalsContainer = $this->getContextNode()->firstChild;
|
||||
$totalsContainer = $this->contextNode()->firstChild;
|
||||
|
||||
if (!$totalsContainer) {
|
||||
$totalsContainer = $this->getContextNode()->appendChild(
|
||||
$totalsContainer = $this->contextNode()->appendChild(
|
||||
$this->dom->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'totals'
|
||||
@@ -49,38 +55,38 @@ abstract class Node
|
||||
|
||||
public function addDirectory(string $name): Directory
|
||||
{
|
||||
$dirNode = $this->getDom()->createElementNS(
|
||||
$dirNode = $this->dom()->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'directory'
|
||||
);
|
||||
|
||||
$dirNode->setAttribute('name', $name);
|
||||
$this->getContextNode()->appendChild($dirNode);
|
||||
$this->contextNode()->appendChild($dirNode);
|
||||
|
||||
return new Directory($dirNode);
|
||||
}
|
||||
|
||||
public function addFile(string $name, string $href): File
|
||||
{
|
||||
$fileNode = $this->getDom()->createElementNS(
|
||||
$fileNode = $this->dom()->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'file'
|
||||
);
|
||||
|
||||
$fileNode->setAttribute('name', $name);
|
||||
$fileNode->setAttribute('href', $href);
|
||||
$this->getContextNode()->appendChild($fileNode);
|
||||
$this->contextNode()->appendChild($fileNode);
|
||||
|
||||
return new File($fileNode);
|
||||
}
|
||||
|
||||
protected function setContextNode(\DOMElement $context): void
|
||||
protected function setContextNode(DOMElement $context): void
|
||||
{
|
||||
$this->dom = $context->ownerDocument;
|
||||
$this->contextNode = $context;
|
||||
}
|
||||
|
||||
protected function getContextNode(): \DOMElement
|
||||
protected function contextNode(): DOMElement
|
||||
{
|
||||
return $this->contextNode;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,6 +9,11 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use DOMDocument;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Project extends Node
|
||||
{
|
||||
public function __construct(string $directory)
|
||||
@@ -17,21 +22,21 @@ final class Project extends Node
|
||||
$this->setProjectSourceDirectory($directory);
|
||||
}
|
||||
|
||||
public function getProjectSourceDirectory(): string
|
||||
public function projectSourceDirectory(): string
|
||||
{
|
||||
return $this->getContextNode()->getAttribute('source');
|
||||
return $this->contextNode()->getAttribute('source');
|
||||
}
|
||||
|
||||
public function getBuildInformation(): BuildInformation
|
||||
public function buildInformation(): BuildInformation
|
||||
{
|
||||
$buildNode = $this->getDom()->getElementsByTagNameNS(
|
||||
$buildNode = $this->dom()->getElementsByTagNameNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'build'
|
||||
)->item(0);
|
||||
|
||||
if (!$buildNode) {
|
||||
$buildNode = $this->getDom()->documentElement->appendChild(
|
||||
$this->getDom()->createElementNS(
|
||||
$buildNode = $this->dom()->documentElement->appendChild(
|
||||
$this->dom()->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'build'
|
||||
)
|
||||
@@ -41,16 +46,16 @@ final class Project extends Node
|
||||
return new BuildInformation($buildNode);
|
||||
}
|
||||
|
||||
public function getTests(): Tests
|
||||
public function tests(): Tests
|
||||
{
|
||||
$testsNode = $this->getContextNode()->getElementsByTagNameNS(
|
||||
$testsNode = $this->contextNode()->getElementsByTagNameNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'tests'
|
||||
)->item(0);
|
||||
|
||||
if (!$testsNode) {
|
||||
$testsNode = $this->getContextNode()->appendChild(
|
||||
$this->getDom()->createElementNS(
|
||||
$testsNode = $this->contextNode()->appendChild(
|
||||
$this->dom()->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'tests'
|
||||
)
|
||||
@@ -60,14 +65,14 @@ final class Project extends Node
|
||||
return new Tests($testsNode);
|
||||
}
|
||||
|
||||
public function asDom(): \DOMDocument
|
||||
public function asDom(): DOMDocument
|
||||
{
|
||||
return $this->getDom();
|
||||
return $this->dom();
|
||||
}
|
||||
|
||||
private function init(): void
|
||||
{
|
||||
$dom = new \DOMDocument;
|
||||
$dom = new DOMDocument;
|
||||
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><build/><project/></phpunit>');
|
||||
|
||||
$this->setContextNode(
|
||||
@@ -80,6 +85,6 @@ final class Project extends Node
|
||||
|
||||
private function setProjectSourceDirectory(string $name): void
|
||||
{
|
||||
$this->getContextNode()->setAttribute('source', $name);
|
||||
$this->contextNode()->setAttribute('source', $name);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,11 +9,18 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use function basename;
|
||||
use function dirname;
|
||||
use DOMDocument;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Report extends File
|
||||
{
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$dom = new \DOMDocument();
|
||||
$dom = new DOMDocument;
|
||||
$dom->loadXML('<?xml version="1.0" ?><phpunit xmlns="https://schema.phpunit.de/coverage/1.0"><file /></phpunit>');
|
||||
|
||||
$contextNode = $dom->getElementsByTagNameNS(
|
||||
@@ -26,15 +33,15 @@ final class Report extends File
|
||||
$this->setName($name);
|
||||
}
|
||||
|
||||
public function asDom(): \DOMDocument
|
||||
public function asDom(): DOMDocument
|
||||
{
|
||||
return $this->getDomDocument();
|
||||
return $this->dom();
|
||||
}
|
||||
|
||||
public function getFunctionObject($name): Method
|
||||
public function functionObject($name): Method
|
||||
{
|
||||
$node = $this->getContextNode()->appendChild(
|
||||
$this->getDomDocument()->createElementNS(
|
||||
$node = $this->contextNode()->appendChild(
|
||||
$this->dom()->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'function'
|
||||
)
|
||||
@@ -43,26 +50,26 @@ final class Report extends File
|
||||
return new Method($node, $name);
|
||||
}
|
||||
|
||||
public function getClassObject($name): Unit
|
||||
public function classObject($name): Unit
|
||||
{
|
||||
return $this->getUnitObject('class', $name);
|
||||
return $this->unitObject('class', $name);
|
||||
}
|
||||
|
||||
public function getTraitObject($name): Unit
|
||||
public function traitObject($name): Unit
|
||||
{
|
||||
return $this->getUnitObject('trait', $name);
|
||||
return $this->unitObject('trait', $name);
|
||||
}
|
||||
|
||||
public function getSource(): Source
|
||||
public function source(): Source
|
||||
{
|
||||
$source = $this->getContextNode()->getElementsByTagNameNS(
|
||||
$source = $this->contextNode()->getElementsByTagNameNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'source'
|
||||
)->item(0);
|
||||
|
||||
if (!$source) {
|
||||
$source = $this->getContextNode()->appendChild(
|
||||
$this->getDomDocument()->createElementNS(
|
||||
$source = $this->contextNode()->appendChild(
|
||||
$this->dom()->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'source'
|
||||
)
|
||||
@@ -72,16 +79,16 @@ final class Report extends File
|
||||
return new Source($source);
|
||||
}
|
||||
|
||||
private function setName($name): void
|
||||
private function setName(string $name): void
|
||||
{
|
||||
$this->getContextNode()->setAttribute('name', \basename($name));
|
||||
$this->getContextNode()->setAttribute('path', \dirname($name));
|
||||
$this->contextNode()->setAttribute('name', basename($name));
|
||||
$this->contextNode()->setAttribute('path', dirname($name));
|
||||
}
|
||||
|
||||
private function getUnitObject($tagName, $name): Unit
|
||||
private function unitObject(string $tagName, $name): Unit
|
||||
{
|
||||
$node = $this->getContextNode()->appendChild(
|
||||
$this->getDomDocument()->createElementNS(
|
||||
$node = $this->contextNode()->appendChild(
|
||||
$this->dom()->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
$tagName
|
||||
)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,16 +9,20 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use DOMElement;
|
||||
use TheSeer\Tokenizer\NamespaceUri;
|
||||
use TheSeer\Tokenizer\Tokenizer;
|
||||
use TheSeer\Tokenizer\XMLSerializer;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Source
|
||||
{
|
||||
/** @var \DOMElement */
|
||||
/** @var DOMElement */
|
||||
private $context;
|
||||
|
||||
public function __construct(\DOMElement $context)
|
||||
public function __construct(DOMElement $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
@@ -27,7 +31,7 @@ final class Source
|
||||
{
|
||||
$context = $this->context;
|
||||
|
||||
$tokens = (new Tokenizer())->parse($source);
|
||||
$tokens = (new Tokenizer)->parse($source);
|
||||
$srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens);
|
||||
|
||||
$context->parentNode->replaceChild(
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,6 +9,11 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use DOMElement;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Tests
|
||||
{
|
||||
private $contextNode;
|
||||
@@ -24,7 +29,7 @@ final class Tests
|
||||
6 => 'WARNING', // PHPUnit_Runner_BaseTestRunner::STATUS_WARNING
|
||||
];
|
||||
|
||||
public function __construct(\DOMElement $context)
|
||||
public function __construct(DOMElement $context)
|
||||
{
|
||||
$this->contextNode = $context;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,41 +9,47 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\Util;
|
||||
use function sprintf;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use SebastianBergmann\CodeCoverage\Util\Percentage;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Totals
|
||||
{
|
||||
/**
|
||||
* @var \DOMNode
|
||||
* @var DOMNode
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $linesNode;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $methodsNode;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $functionsNode;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $classesNode;
|
||||
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $traitsNode;
|
||||
|
||||
public function __construct(\DOMElement $container)
|
||||
public function __construct(DOMElement $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$dom = $container->ownerDocument;
|
||||
@@ -80,7 +86,7 @@ final class Totals
|
||||
$container->appendChild($this->traitsNode);
|
||||
}
|
||||
|
||||
public function getContainer(): \DOMNode
|
||||
public function container(): DOMNode
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
@@ -94,7 +100,7 @@ final class Totals
|
||||
$this->linesNode->setAttribute('executed', (string) $executed);
|
||||
$this->linesNode->setAttribute(
|
||||
'percent',
|
||||
$executable === 0 ? '0' : \sprintf('%01.2F', Util::percent($executed, $executable))
|
||||
$executable === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($executed, $executable)->asFloat())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,7 +110,7 @@ final class Totals
|
||||
$this->classesNode->setAttribute('tested', (string) $tested);
|
||||
$this->classesNode->setAttribute(
|
||||
'percent',
|
||||
$count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count))
|
||||
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,7 +120,7 @@ final class Totals
|
||||
$this->traitsNode->setAttribute('tested', (string) $tested);
|
||||
$this->traitsNode->setAttribute(
|
||||
'percent',
|
||||
$count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count))
|
||||
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +130,7 @@ final class Totals
|
||||
$this->methodsNode->setAttribute('tested', (string) $tested);
|
||||
$this->methodsNode->setAttribute(
|
||||
'percent',
|
||||
$count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count))
|
||||
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -134,7 +140,7 @@ final class Totals
|
||||
$this->functionsNode->setAttribute('tested', (string) $tested);
|
||||
$this->functionsNode->setAttribute(
|
||||
'percent',
|
||||
$count === 0 ? '0' : \sprintf('%01.2F', Util::percent($tested, $count))
|
||||
$count === 0 ? '0' : sprintf('%01.2F', Percentage::fromFractionAndTotal($tested, $count)->asFloat())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,14 +9,19 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Report\Xml;
|
||||
|
||||
use DOMElement;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Unit
|
||||
{
|
||||
/**
|
||||
* @var \DOMElement
|
||||
* @var DOMElement
|
||||
*/
|
||||
private $contextNode;
|
||||
|
||||
public function __construct(\DOMElement $context, string $name)
|
||||
public function __construct(DOMElement $context, string $name)
|
||||
{
|
||||
$this->contextNode = $context;
|
||||
|
||||
@@ -35,28 +40,6 @@ final class Unit
|
||||
$this->contextNode->setAttribute('crap', (string) $crap);
|
||||
}
|
||||
|
||||
public function setPackage(string $full, string $package, string $sub, string $category): void
|
||||
{
|
||||
$node = $this->contextNode->getElementsByTagNameNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'package'
|
||||
)->item(0);
|
||||
|
||||
if (!$node) {
|
||||
$node = $this->contextNode->appendChild(
|
||||
$this->contextNode->ownerDocument->createElementNS(
|
||||
'https://schema.phpunit.de/coverage/1.0',
|
||||
'package'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$node->setAttribute('full', $full);
|
||||
$node->setAttribute('name', $package);
|
||||
$node->setAttribute('sub', $sub);
|
||||
$node->setAttribute('category', $category);
|
||||
}
|
||||
|
||||
public function setNamespace(string $namespace): void
|
||||
{
|
||||
$node = $this->contextNode->getElementsByTagNameNS(
|
||||
|
30
vendor/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php
vendored
Normal file
30
vendor/phpunit/php-code-coverage/src/StaticAnalysis/CacheWarmer.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
|
||||
final class CacheWarmer
|
||||
{
|
||||
public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter): void
|
||||
{
|
||||
$analyser = new CachingFileAnalyser(
|
||||
$cacheDirectory,
|
||||
new ParsingFileAnalyser(
|
||||
$useAnnotationsForIgnoringCode,
|
||||
$ignoreDeprecatedCode
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($filter->files() as $file) {
|
||||
$analyser->process($file);
|
||||
}
|
||||
}
|
||||
}
|
184
vendor/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.php
vendored
Normal file
184
vendor/phpunit/php-code-coverage/src/StaticAnalysis/CachingFileAnalyser.php
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||
|
||||
use function file_get_contents;
|
||||
use function file_put_contents;
|
||||
use function implode;
|
||||
use function is_file;
|
||||
use function md5;
|
||||
use function serialize;
|
||||
use function unserialize;
|
||||
use SebastianBergmann\CodeCoverage\Util\Filesystem;
|
||||
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class CachingFileAnalyser implements FileAnalyser
|
||||
{
|
||||
/**
|
||||
* @var ?string
|
||||
*/
|
||||
private static $cacheVersion;
|
||||
|
||||
/**
|
||||
* @var FileAnalyser
|
||||
*/
|
||||
private $analyser;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $cache = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $directory;
|
||||
|
||||
public function __construct(string $directory, FileAnalyser $analyser)
|
||||
{
|
||||
Filesystem::createDirectory($directory);
|
||||
|
||||
$this->analyser = $analyser;
|
||||
$this->directory = $directory;
|
||||
}
|
||||
|
||||
public function classesIn(string $filename): array
|
||||
{
|
||||
if (!isset($this->cache[$filename])) {
|
||||
$this->process($filename);
|
||||
}
|
||||
|
||||
return $this->cache[$filename]['classesIn'];
|
||||
}
|
||||
|
||||
public function traitsIn(string $filename): array
|
||||
{
|
||||
if (!isset($this->cache[$filename])) {
|
||||
$this->process($filename);
|
||||
}
|
||||
|
||||
return $this->cache[$filename]['traitsIn'];
|
||||
}
|
||||
|
||||
public function functionsIn(string $filename): array
|
||||
{
|
||||
if (!isset($this->cache[$filename])) {
|
||||
$this->process($filename);
|
||||
}
|
||||
|
||||
return $this->cache[$filename]['functionsIn'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
public function linesOfCodeFor(string $filename): array
|
||||
{
|
||||
if (!isset($this->cache[$filename])) {
|
||||
$this->process($filename);
|
||||
}
|
||||
|
||||
return $this->cache[$filename]['linesOfCodeFor'];
|
||||
}
|
||||
|
||||
public function executableLinesIn(string $filename): array
|
||||
{
|
||||
if (!isset($this->cache[$filename])) {
|
||||
$this->process($filename);
|
||||
}
|
||||
|
||||
return $this->cache[$filename]['executableLinesIn'];
|
||||
}
|
||||
|
||||
public function ignoredLinesFor(string $filename): array
|
||||
{
|
||||
if (!isset($this->cache[$filename])) {
|
||||
$this->process($filename);
|
||||
}
|
||||
|
||||
return $this->cache[$filename]['ignoredLinesFor'];
|
||||
}
|
||||
|
||||
public function process(string $filename): void
|
||||
{
|
||||
$cache = $this->read($filename);
|
||||
|
||||
if ($cache !== false) {
|
||||
$this->cache[$filename] = $cache;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cache[$filename] = [
|
||||
'classesIn' => $this->analyser->classesIn($filename),
|
||||
'traitsIn' => $this->analyser->traitsIn($filename),
|
||||
'functionsIn' => $this->analyser->functionsIn($filename),
|
||||
'linesOfCodeFor' => $this->analyser->linesOfCodeFor($filename),
|
||||
'ignoredLinesFor' => $this->analyser->ignoredLinesFor($filename),
|
||||
'executableLinesIn' => $this->analyser->executableLinesIn($filename),
|
||||
];
|
||||
|
||||
$this->write($filename, $this->cache[$filename]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function read(string $filename)
|
||||
{
|
||||
$cacheFile = $this->cacheFile($filename);
|
||||
|
||||
if (!is_file($cacheFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return unserialize(
|
||||
file_get_contents($cacheFile),
|
||||
['allowed_classes' => false]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
*/
|
||||
private function write(string $filename, $data): void
|
||||
{
|
||||
file_put_contents(
|
||||
$this->cacheFile($filename),
|
||||
serialize($data)
|
||||
);
|
||||
}
|
||||
|
||||
private function cacheFile(string $filename): string
|
||||
{
|
||||
return $this->directory . DIRECTORY_SEPARATOR . md5($filename . "\0" . file_get_contents($filename) . "\0" . self::cacheVersion());
|
||||
}
|
||||
|
||||
private static function cacheVersion(): string
|
||||
{
|
||||
if (self::$cacheVersion !== null) {
|
||||
return self::$cacheVersion;
|
||||
}
|
||||
|
||||
$buffer = [];
|
||||
|
||||
foreach ((new FileIteratorFacade)->getFilesAsArray(__DIR__, '.php') as $file) {
|
||||
$buffer[] = $file;
|
||||
$buffer[] = file_get_contents($file);
|
||||
}
|
||||
|
||||
self::$cacheVersion = md5(implode("\0", $buffer));
|
||||
|
||||
return self::$cacheVersion;
|
||||
}
|
||||
}
|
326
vendor/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php
vendored
Normal file
326
vendor/phpunit/php-code-coverage/src/StaticAnalysis/CodeUnitFindingVisitor.php
vendored
Normal file
@@ -0,0 +1,326 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||
|
||||
use function assert;
|
||||
use function implode;
|
||||
use function rtrim;
|
||||
use function trim;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\ComplexType;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\IntersectionType;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Enum_;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PhpParser\Node\UnionType;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use SebastianBergmann\Complexity\CyclomaticComplexityCalculatingVisitor;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class CodeUnitFindingVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @psalm-var array<string,array{name: string, namespacedName: string, namespace: string, startLine: int, endLine: int, methods: array<string,array{methodName: string, signature: string, visibility: string, startLine: int, endLine: int, ccn: int}>}>
|
||||
*/
|
||||
private $classes = [];
|
||||
|
||||
/**
|
||||
* @psalm-var array<string,array{name: string, namespacedName: string, namespace: string, startLine: int, endLine: int, methods: array<string,array{methodName: string, signature: string, visibility: string, startLine: int, endLine: int, ccn: int}>}>
|
||||
*/
|
||||
private $traits = [];
|
||||
|
||||
/**
|
||||
* @psalm-var array<string,array{name: string, namespacedName: string, namespace: string, signature: string, startLine: int, endLine: int, ccn: int}>
|
||||
*/
|
||||
private $functions = [];
|
||||
|
||||
public function enterNode(Node $node): void
|
||||
{
|
||||
if ($node instanceof Class_) {
|
||||
if ($node->isAnonymous()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->processClass($node);
|
||||
}
|
||||
|
||||
if ($node instanceof Trait_) {
|
||||
$this->processTrait($node);
|
||||
}
|
||||
|
||||
if (!$node instanceof ClassMethod && !$node instanceof Function_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof ClassMethod) {
|
||||
$parentNode = $node->getAttribute('parent');
|
||||
|
||||
if ($parentNode instanceof Class_ && $parentNode->isAnonymous()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->processMethod($node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->processFunction($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return array<string,array{name: string, namespacedName: string, namespace: string, startLine: int, endLine: int, methods: array<string,array{methodName: string, signature: string, visibility: string, startLine: int, endLine: int, ccn: int}>}>
|
||||
*/
|
||||
public function classes(): array
|
||||
{
|
||||
return $this->classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return array<string,array{name: string, namespacedName: string, namespace: string, startLine: int, endLine: int, methods: array<string,array{methodName: string, signature: string, visibility: string, startLine: int, endLine: int, ccn: int}>}>
|
||||
*/
|
||||
public function traits(): array
|
||||
{
|
||||
return $this->traits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return array<string,array{name: string, namespacedName: string, namespace: string, signature: string, startLine: int, endLine: int, ccn: int}>
|
||||
*/
|
||||
public function functions(): array
|
||||
{
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param ClassMethod|Function_ $node
|
||||
*/
|
||||
private function cyclomaticComplexity(Node $node): int
|
||||
{
|
||||
assert($node instanceof ClassMethod || $node instanceof Function_);
|
||||
|
||||
$nodes = $node->getStmts();
|
||||
|
||||
if ($nodes === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$traverser = new NodeTraverser;
|
||||
|
||||
$cyclomaticComplexityCalculatingVisitor = new CyclomaticComplexityCalculatingVisitor;
|
||||
|
||||
$traverser->addVisitor($cyclomaticComplexityCalculatingVisitor);
|
||||
|
||||
/* @noinspection UnusedFunctionResultInspection */
|
||||
$traverser->traverse($nodes);
|
||||
|
||||
return $cyclomaticComplexityCalculatingVisitor->cyclomaticComplexity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param ClassMethod|Function_ $node
|
||||
*/
|
||||
private function signature(Node $node): string
|
||||
{
|
||||
assert($node instanceof ClassMethod || $node instanceof Function_);
|
||||
|
||||
$signature = ($node->returnsByRef() ? '&' : '') . $node->name->toString() . '(';
|
||||
$parameters = [];
|
||||
|
||||
foreach ($node->getParams() as $parameter) {
|
||||
assert(isset($parameter->var->name));
|
||||
|
||||
$parameterAsString = '';
|
||||
|
||||
if ($parameter->type !== null) {
|
||||
$parameterAsString = $this->type($parameter->type) . ' ';
|
||||
}
|
||||
|
||||
$parameterAsString .= '$' . $parameter->var->name;
|
||||
|
||||
/* @todo Handle default values */
|
||||
|
||||
$parameters[] = $parameterAsString;
|
||||
}
|
||||
|
||||
$signature .= implode(', ', $parameters) . ')';
|
||||
|
||||
$returnType = $node->getReturnType();
|
||||
|
||||
if ($returnType !== null) {
|
||||
$signature .= ': ' . $this->type($returnType);
|
||||
}
|
||||
|
||||
return $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param Identifier|Name|ComplexType $type
|
||||
*/
|
||||
private function type(Node $type): string
|
||||
{
|
||||
assert($type instanceof Identifier || $type instanceof Name || $type instanceof ComplexType);
|
||||
|
||||
if ($type instanceof NullableType) {
|
||||
return '?' . $type->type;
|
||||
}
|
||||
|
||||
if ($type instanceof UnionType || $type instanceof IntersectionType) {
|
||||
return $this->unionOrIntersectionAsString($type);
|
||||
}
|
||||
|
||||
return $type->toString();
|
||||
}
|
||||
|
||||
private function visibility(ClassMethod $node): string
|
||||
{
|
||||
if ($node->isPrivate()) {
|
||||
return 'private';
|
||||
}
|
||||
|
||||
if ($node->isProtected()) {
|
||||
return 'protected';
|
||||
}
|
||||
|
||||
return 'public';
|
||||
}
|
||||
|
||||
private function processClass(Class_ $node): void
|
||||
{
|
||||
$name = $node->name->toString();
|
||||
$namespacedName = $node->namespacedName->toString();
|
||||
|
||||
$this->classes[$namespacedName] = [
|
||||
'name' => $name,
|
||||
'namespacedName' => $namespacedName,
|
||||
'namespace' => $this->namespace($namespacedName, $name),
|
||||
'startLine' => $node->getStartLine(),
|
||||
'endLine' => $node->getEndLine(),
|
||||
'methods' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function processTrait(Trait_ $node): void
|
||||
{
|
||||
$name = $node->name->toString();
|
||||
$namespacedName = $node->namespacedName->toString();
|
||||
|
||||
$this->traits[$namespacedName] = [
|
||||
'name' => $name,
|
||||
'namespacedName' => $namespacedName,
|
||||
'namespace' => $this->namespace($namespacedName, $name),
|
||||
'startLine' => $node->getStartLine(),
|
||||
'endLine' => $node->getEndLine(),
|
||||
'methods' => [],
|
||||
];
|
||||
}
|
||||
|
||||
private function processMethod(ClassMethod $node): void
|
||||
{
|
||||
$parentNode = $node->getAttribute('parent');
|
||||
|
||||
if ($parentNode instanceof Interface_) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert($parentNode instanceof Class_ || $parentNode instanceof Trait_ || $parentNode instanceof Enum_);
|
||||
assert(isset($parentNode->name));
|
||||
assert(isset($parentNode->namespacedName));
|
||||
assert($parentNode->namespacedName instanceof Name);
|
||||
|
||||
$parentName = $parentNode->name->toString();
|
||||
$parentNamespacedName = $parentNode->namespacedName->toString();
|
||||
|
||||
if ($parentNode instanceof Class_) {
|
||||
$storage = &$this->classes;
|
||||
} else {
|
||||
$storage = &$this->traits;
|
||||
}
|
||||
|
||||
if (!isset($storage[$parentNamespacedName])) {
|
||||
$storage[$parentNamespacedName] = [
|
||||
'name' => $parentName,
|
||||
'namespacedName' => $parentNamespacedName,
|
||||
'namespace' => $this->namespace($parentNamespacedName, $parentName),
|
||||
'startLine' => $parentNode->getStartLine(),
|
||||
'endLine' => $parentNode->getEndLine(),
|
||||
'methods' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$storage[$parentNamespacedName]['methods'][$node->name->toString()] = [
|
||||
'methodName' => $node->name->toString(),
|
||||
'signature' => $this->signature($node),
|
||||
'visibility' => $this->visibility($node),
|
||||
'startLine' => $node->getStartLine(),
|
||||
'endLine' => $node->getEndLine(),
|
||||
'ccn' => $this->cyclomaticComplexity($node),
|
||||
];
|
||||
}
|
||||
|
||||
private function processFunction(Function_ $node): void
|
||||
{
|
||||
assert(isset($node->name));
|
||||
assert(isset($node->namespacedName));
|
||||
assert($node->namespacedName instanceof Name);
|
||||
|
||||
$name = $node->name->toString();
|
||||
$namespacedName = $node->namespacedName->toString();
|
||||
|
||||
$this->functions[$namespacedName] = [
|
||||
'name' => $name,
|
||||
'namespacedName' => $namespacedName,
|
||||
'namespace' => $this->namespace($namespacedName, $name),
|
||||
'signature' => $this->signature($node),
|
||||
'startLine' => $node->getStartLine(),
|
||||
'endLine' => $node->getEndLine(),
|
||||
'ccn' => $this->cyclomaticComplexity($node),
|
||||
];
|
||||
}
|
||||
|
||||
private function namespace(string $namespacedName, string $name): string
|
||||
{
|
||||
return trim(rtrim($namespacedName, $name), '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param UnionType|IntersectionType $type
|
||||
*/
|
||||
private function unionOrIntersectionAsString(ComplexType $type): string
|
||||
{
|
||||
if ($type instanceof UnionType) {
|
||||
$separator = '|';
|
||||
} else {
|
||||
$separator = '&';
|
||||
}
|
||||
|
||||
$types = [];
|
||||
|
||||
foreach ($type->types as $_type) {
|
||||
if ($_type instanceof Name) {
|
||||
$types[] = $_type->toCodeString();
|
||||
} else {
|
||||
assert($_type instanceof Identifier);
|
||||
|
||||
$types[] = $_type->toString();
|
||||
}
|
||||
}
|
||||
|
||||
return implode($separator, $types);
|
||||
}
|
||||
}
|
365
vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php
vendored
Normal file
365
vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||
|
||||
use function array_diff_key;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function current;
|
||||
use function end;
|
||||
use function explode;
|
||||
use function max;
|
||||
use function preg_match;
|
||||
use function preg_quote;
|
||||
use function range;
|
||||
use function reset;
|
||||
use function sprintf;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $nextBranch = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $source;
|
||||
|
||||
/**
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private $executableLinesGroupedByBranch = [];
|
||||
|
||||
/**
|
||||
* @var array<int, bool>
|
||||
*/
|
||||
private $unsets = [];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private $commentsToCheckForUnset = [];
|
||||
|
||||
public function __construct(string $source)
|
||||
{
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node): void
|
||||
{
|
||||
foreach ($node->getComments() as $comment) {
|
||||
$commentLine = $comment->getStartLine();
|
||||
|
||||
if (!isset($this->executableLinesGroupedByBranch[$commentLine])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (explode("\n", $comment->getText()) as $text) {
|
||||
$this->commentsToCheckForUnset[$commentLine] = $text;
|
||||
$commentLine++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Scalar\String_ ||
|
||||
$node instanceof Node\Scalar\EncapsedStringPart) {
|
||||
$startLine = $node->getStartLine() + 1;
|
||||
$endLine = $node->getEndLine() - 1;
|
||||
|
||||
if ($startLine <= $endLine) {
|
||||
foreach (range($startLine, $endLine) as $line) {
|
||||
unset($this->executableLinesGroupedByBranch[$line]);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\Declare_ ||
|
||||
$node instanceof Node\Stmt\DeclareDeclare ||
|
||||
$node instanceof Node\Stmt\Else_ ||
|
||||
$node instanceof Node\Stmt\EnumCase ||
|
||||
$node instanceof Node\Stmt\Finally_ ||
|
||||
$node instanceof Node\Stmt\Interface_ ||
|
||||
$node instanceof Node\Stmt\Label ||
|
||||
$node instanceof Node\Stmt\Namespace_ ||
|
||||
$node instanceof Node\Stmt\Nop ||
|
||||
$node instanceof Node\Stmt\Switch_ ||
|
||||
$node instanceof Node\Stmt\TryCatch ||
|
||||
$node instanceof Node\Stmt\Use_ ||
|
||||
$node instanceof Node\Stmt\UseUse ||
|
||||
$node instanceof Node\Expr\Match_ ||
|
||||
$node instanceof Node\Expr\Variable ||
|
||||
$node instanceof Node\ComplexType ||
|
||||
$node instanceof Node\Const_ ||
|
||||
$node instanceof Node\Identifier ||
|
||||
$node instanceof Node\Name ||
|
||||
$node instanceof Node\Param ||
|
||||
$node instanceof Node\Scalar) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\Throw_) {
|
||||
$this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\Enum_ ||
|
||||
$node instanceof Node\Stmt\Function_ ||
|
||||
$node instanceof Node\Stmt\Class_ ||
|
||||
$node instanceof Node\Stmt\ClassMethod ||
|
||||
$node instanceof Node\Expr\Closure ||
|
||||
$node instanceof Node\Stmt\Trait_) {
|
||||
$isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_;
|
||||
|
||||
if (null !== $node->stmts) {
|
||||
foreach ($node->stmts as $stmt) {
|
||||
if ($stmt instanceof Node\Stmt\Nop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (range($stmt->getStartLine(), $stmt->getEndLine()) as $line) {
|
||||
unset($this->executableLinesGroupedByBranch[$line]);
|
||||
|
||||
if (
|
||||
$isConcreteClassLike &&
|
||||
!$stmt instanceof Node\Stmt\ClassMethod
|
||||
) {
|
||||
$this->unsets[$line] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($isConcreteClassLike) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hasEmptyBody = [] === $node->stmts ||
|
||||
null === $node->stmts ||
|
||||
(
|
||||
1 === count($node->stmts) &&
|
||||
$node->stmts[0] instanceof Node\Stmt\Nop
|
||||
);
|
||||
|
||||
if ($hasEmptyBody) {
|
||||
if ($node->getEndLine() === $node->getStartLine()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Expr\ArrowFunction) {
|
||||
$startLine = max(
|
||||
$node->getStartLine() + 1,
|
||||
$node->expr->getStartLine()
|
||||
);
|
||||
|
||||
$endLine = $node->expr->getEndLine();
|
||||
|
||||
if ($endLine < $startLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setLineBranch($startLine, $endLine, ++$this->nextBranch);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Expr\Ternary) {
|
||||
if (null !== $node->if &&
|
||||
$node->getStartLine() !== $node->if->getEndLine()) {
|
||||
$this->setLineBranch($node->if->getStartLine(), $node->if->getEndLine(), ++$this->nextBranch);
|
||||
}
|
||||
|
||||
if ($node->getStartLine() !== $node->else->getEndLine()) {
|
||||
$this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Expr\BinaryOp\Coalesce) {
|
||||
if ($node->getStartLine() !== $node->getEndLine()) {
|
||||
$this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\If_ ||
|
||||
$node instanceof Node\Stmt\ElseIf_ ||
|
||||
$node instanceof Node\Stmt\Case_) {
|
||||
if (null === $node->cond) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setLineBranch(
|
||||
$node->cond->getStartLine(),
|
||||
$node->cond->getStartLine(),
|
||||
++$this->nextBranch
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\For_) {
|
||||
$startLine = null;
|
||||
$endLine = null;
|
||||
|
||||
if ([] !== $node->init) {
|
||||
$startLine = $node->init[0]->getStartLine();
|
||||
|
||||
end($node->init);
|
||||
|
||||
$endLine = current($node->init)->getEndLine();
|
||||
|
||||
reset($node->init);
|
||||
}
|
||||
|
||||
if ([] !== $node->cond) {
|
||||
if (null === $startLine) {
|
||||
$startLine = $node->cond[0]->getStartLine();
|
||||
}
|
||||
|
||||
end($node->cond);
|
||||
|
||||
$endLine = current($node->cond)->getEndLine();
|
||||
|
||||
reset($node->cond);
|
||||
}
|
||||
|
||||
if ([] !== $node->loop) {
|
||||
if (null === $startLine) {
|
||||
$startLine = $node->loop[0]->getStartLine();
|
||||
}
|
||||
|
||||
end($node->loop);
|
||||
|
||||
$endLine = current($node->loop)->getEndLine();
|
||||
|
||||
reset($node->loop);
|
||||
}
|
||||
|
||||
if (null === $startLine || null === $endLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setLineBranch(
|
||||
$startLine,
|
||||
$endLine,
|
||||
++$this->nextBranch
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\Foreach_) {
|
||||
$this->setLineBranch(
|
||||
$node->expr->getStartLine(),
|
||||
$node->valueVar->getEndLine(),
|
||||
++$this->nextBranch
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\While_ ||
|
||||
$node instanceof Node\Stmt\Do_) {
|
||||
$this->setLineBranch(
|
||||
$node->cond->getStartLine(),
|
||||
$node->cond->getEndLine(),
|
||||
++$this->nextBranch
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Stmt\Catch_) {
|
||||
assert([] !== $node->types);
|
||||
$startLine = $node->types[0]->getStartLine();
|
||||
end($node->types);
|
||||
$endLine = current($node->types)->getEndLine();
|
||||
|
||||
$this->setLineBranch(
|
||||
$startLine,
|
||||
$endLine,
|
||||
++$this->nextBranch
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Expr\CallLike) {
|
||||
if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
|
||||
$branch = $this->executableLinesGroupedByBranch[$node->getStartLine()];
|
||||
} else {
|
||||
$branch = ++$this->nextBranch;
|
||||
}
|
||||
|
||||
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch);
|
||||
}
|
||||
|
||||
public function afterTraverse(array $nodes): void
|
||||
{
|
||||
$lines = explode("\n", $this->source);
|
||||
|
||||
foreach ($lines as $lineNumber => $line) {
|
||||
$lineNumber++;
|
||||
|
||||
if (1 === preg_match('/^\s*$/', $line) ||
|
||||
(
|
||||
isset($this->commentsToCheckForUnset[$lineNumber]) &&
|
||||
1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)
|
||||
)) {
|
||||
unset($this->executableLinesGroupedByBranch[$lineNumber]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->executableLinesGroupedByBranch = array_diff_key(
|
||||
$this->executableLinesGroupedByBranch,
|
||||
$this->unsets
|
||||
);
|
||||
}
|
||||
|
||||
public function executableLinesGroupedByBranch(): array
|
||||
{
|
||||
return $this->executableLinesGroupedByBranch;
|
||||
}
|
||||
|
||||
private function setLineBranch(int $start, int $end, int $branch): void
|
||||
{
|
||||
foreach (range($start, $end) as $line) {
|
||||
$this->executableLinesGroupedByBranch[$line] = $branch;
|
||||
}
|
||||
}
|
||||
}
|
31
vendor/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php
vendored
Normal file
31
vendor/phpunit/php-code-coverage/src/StaticAnalysis/FileAnalyser.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
interface FileAnalyser
|
||||
{
|
||||
public function classesIn(string $filename): array;
|
||||
|
||||
public function traitsIn(string $filename): array;
|
||||
|
||||
public function functionsIn(string $filename): array;
|
||||
|
||||
/**
|
||||
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
public function linesOfCodeFor(string $filename): array;
|
||||
|
||||
public function executableLinesIn(string $filename): array;
|
||||
|
||||
public function ignoredLinesFor(string $filename): array;
|
||||
}
|
119
vendor/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php
vendored
Normal file
119
vendor/phpunit/php-code-coverage/src/StaticAnalysis/IgnoredLinesFindingVisitor.php
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
use function range;
|
||||
use function strpos;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Attribute;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @psalm-var list<int>
|
||||
*/
|
||||
private $ignoredLines = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $useAnnotationsForIgnoringCode;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $ignoreDeprecated;
|
||||
|
||||
public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecated)
|
||||
{
|
||||
$this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode;
|
||||
$this->ignoreDeprecated = $ignoreDeprecated;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node): void
|
||||
{
|
||||
if (!$node instanceof Class_ &&
|
||||
!$node instanceof Trait_ &&
|
||||
!$node instanceof Interface_ &&
|
||||
!$node instanceof ClassMethod &&
|
||||
!$node instanceof Function_ &&
|
||||
!$node instanceof Attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Class_ && $node->isAnonymous()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Class_ ||
|
||||
$node instanceof Trait_ ||
|
||||
$node instanceof Interface_ ||
|
||||
$node instanceof Attribute) {
|
||||
$this->ignoredLines[] = $node->getStartLine();
|
||||
|
||||
assert($node->name !== null);
|
||||
|
||||
// Workaround for https://github.com/nikic/PHP-Parser/issues/886
|
||||
$this->ignoredLines[] = $node->name->getStartLine();
|
||||
}
|
||||
|
||||
if (!$this->useAnnotationsForIgnoringCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof Interface_) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->processDocComment($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return list<int>
|
||||
*/
|
||||
public function ignoredLines(): array
|
||||
{
|
||||
return $this->ignoredLines;
|
||||
}
|
||||
|
||||
private function processDocComment(Node $node): void
|
||||
{
|
||||
$docComment = $node->getDocComment();
|
||||
|
||||
if ($docComment === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($docComment->getText(), '@codeCoverageIgnore') !== false) {
|
||||
$this->ignoredLines = array_merge(
|
||||
$this->ignoredLines,
|
||||
range($node->getStartLine(), $node->getEndLine())
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->ignoreDeprecated && strpos($docComment->getText(), '@deprecated') !== false) {
|
||||
$this->ignoredLines = array_merge(
|
||||
$this->ignoredLines,
|
||||
range($node->getStartLine(), $node->getEndLine())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
253
vendor/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.php
vendored
Normal file
253
vendor/phpunit/php-code-coverage/src/StaticAnalysis/ParsingFileAnalyser.php
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
|
||||
|
||||
use function array_merge;
|
||||
use function array_unique;
|
||||
use function assert;
|
||||
use function file_get_contents;
|
||||
use function is_array;
|
||||
use function max;
|
||||
use function range;
|
||||
use function sort;
|
||||
use function sprintf;
|
||||
use function substr_count;
|
||||
use function token_get_all;
|
||||
use function trim;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
use PhpParser\NodeVisitor\ParentConnectingVisitor;
|
||||
use PhpParser\ParserFactory;
|
||||
use SebastianBergmann\CodeCoverage\ParserException;
|
||||
use SebastianBergmann\LinesOfCode\LineCountingVisitor;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class ParsingFileAnalyser implements FileAnalyser
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $classes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $traits = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $functions = [];
|
||||
|
||||
/**
|
||||
* @var array<string,array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}>
|
||||
*/
|
||||
private $linesOfCode = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $ignoredLines = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $executableLines = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $useAnnotationsForIgnoringCode;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $ignoreDeprecatedCode;
|
||||
|
||||
public function __construct(bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode)
|
||||
{
|
||||
$this->useAnnotationsForIgnoringCode = $useAnnotationsForIgnoringCode;
|
||||
$this->ignoreDeprecatedCode = $ignoreDeprecatedCode;
|
||||
}
|
||||
|
||||
public function classesIn(string $filename): array
|
||||
{
|
||||
$this->analyse($filename);
|
||||
|
||||
return $this->classes[$filename];
|
||||
}
|
||||
|
||||
public function traitsIn(string $filename): array
|
||||
{
|
||||
$this->analyse($filename);
|
||||
|
||||
return $this->traits[$filename];
|
||||
}
|
||||
|
||||
public function functionsIn(string $filename): array
|
||||
{
|
||||
$this->analyse($filename);
|
||||
|
||||
return $this->functions[$filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
|
||||
*/
|
||||
public function linesOfCodeFor(string $filename): array
|
||||
{
|
||||
$this->analyse($filename);
|
||||
|
||||
return $this->linesOfCode[$filename];
|
||||
}
|
||||
|
||||
public function executableLinesIn(string $filename): array
|
||||
{
|
||||
$this->analyse($filename);
|
||||
|
||||
return $this->executableLines[$filename];
|
||||
}
|
||||
|
||||
public function ignoredLinesFor(string $filename): array
|
||||
{
|
||||
$this->analyse($filename);
|
||||
|
||||
return $this->ignoredLines[$filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ParserException
|
||||
*/
|
||||
private function analyse(string $filename): void
|
||||
{
|
||||
if (isset($this->classes[$filename])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$source = file_get_contents($filename);
|
||||
$linesOfCode = max(substr_count($source, "\n") + 1, substr_count($source, "\r") + 1);
|
||||
|
||||
if ($linesOfCode === 0 && !empty($source)) {
|
||||
$linesOfCode = 1;
|
||||
}
|
||||
|
||||
$parser = (new ParserFactory)->create(
|
||||
ParserFactory::PREFER_PHP7,
|
||||
new Lexer
|
||||
);
|
||||
|
||||
try {
|
||||
$nodes = $parser->parse($source);
|
||||
|
||||
assert($nodes !== null);
|
||||
|
||||
$traverser = new NodeTraverser;
|
||||
$codeUnitFindingVisitor = new CodeUnitFindingVisitor;
|
||||
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
|
||||
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode);
|
||||
$executableLinesFindingVisitor = new ExecutableLinesFindingVisitor($source);
|
||||
|
||||
$traverser->addVisitor(new NameResolver);
|
||||
$traverser->addVisitor(new ParentConnectingVisitor);
|
||||
$traverser->addVisitor($codeUnitFindingVisitor);
|
||||
$traverser->addVisitor($lineCountingVisitor);
|
||||
$traverser->addVisitor($ignoredLinesFindingVisitor);
|
||||
$traverser->addVisitor($executableLinesFindingVisitor);
|
||||
|
||||
/* @noinspection UnusedFunctionResultInspection */
|
||||
$traverser->traverse($nodes);
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Error $error) {
|
||||
throw new ParserException(
|
||||
sprintf(
|
||||
'Cannot parse %s: %s',
|
||||
$filename,
|
||||
$error->getMessage()
|
||||
),
|
||||
$error->getCode(),
|
||||
$error
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$this->classes[$filename] = $codeUnitFindingVisitor->classes();
|
||||
$this->traits[$filename] = $codeUnitFindingVisitor->traits();
|
||||
$this->functions[$filename] = $codeUnitFindingVisitor->functions();
|
||||
$this->executableLines[$filename] = $executableLinesFindingVisitor->executableLinesGroupedByBranch();
|
||||
$this->ignoredLines[$filename] = [];
|
||||
|
||||
$this->findLinesIgnoredByLineBasedAnnotations($filename, $source, $this->useAnnotationsForIgnoringCode);
|
||||
|
||||
$this->ignoredLines[$filename] = array_unique(
|
||||
array_merge(
|
||||
$this->ignoredLines[$filename],
|
||||
$ignoredLinesFindingVisitor->ignoredLines()
|
||||
)
|
||||
);
|
||||
|
||||
sort($this->ignoredLines[$filename]);
|
||||
|
||||
$result = $lineCountingVisitor->result();
|
||||
|
||||
$this->linesOfCode[$filename] = [
|
||||
'linesOfCode' => $result->linesOfCode(),
|
||||
'commentLinesOfCode' => $result->commentLinesOfCode(),
|
||||
'nonCommentLinesOfCode' => $result->nonCommentLinesOfCode(),
|
||||
];
|
||||
}
|
||||
|
||||
private function findLinesIgnoredByLineBasedAnnotations(string $filename, string $source, bool $useAnnotationsForIgnoringCode): void
|
||||
{
|
||||
if (!$useAnnotationsForIgnoringCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$start = false;
|
||||
|
||||
foreach (token_get_all($source) as $token) {
|
||||
if (!is_array($token) ||
|
||||
!(T_COMMENT === $token[0] || T_DOC_COMMENT === $token[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$comment = trim($token[1]);
|
||||
|
||||
if ($comment === '// @codeCoverageIgnore' ||
|
||||
$comment === '//@codeCoverageIgnore') {
|
||||
$this->ignoredLines[$filename][] = $token[2];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($comment === '// @codeCoverageIgnoreStart' ||
|
||||
$comment === '//@codeCoverageIgnoreStart') {
|
||||
$start = $token[2];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($comment === '// @codeCoverageIgnoreEnd' ||
|
||||
$comment === '//@codeCoverageIgnoreEnd') {
|
||||
if (false === $start) {
|
||||
$start = $token[2];
|
||||
}
|
||||
|
||||
$this->ignoredLines[$filename] = array_merge(
|
||||
$this->ignoredLines[$filename],
|
||||
range($start, $token[2])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
vendor/phpunit/php-code-coverage/src/Util.php
vendored
40
vendor/phpunit/php-code-coverage/src/Util.php
vendored
@@ -1,40 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
/**
|
||||
* Utility methods.
|
||||
*/
|
||||
final class Util
|
||||
{
|
||||
/**
|
||||
* @return float|int|string
|
||||
*/
|
||||
public static function percent(float $a, float $b, bool $asString = false, bool $fixedWidth = false)
|
||||
{
|
||||
if ($asString && $b == 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$percent = 100;
|
||||
|
||||
if ($b > 0) {
|
||||
$percent = ($a / $b) * 100;
|
||||
}
|
||||
|
||||
if ($asString) {
|
||||
$format = $fixedWidth ? '%6.2F%%' : '%01.2F%%';
|
||||
|
||||
return \sprintf($format, $percent);
|
||||
}
|
||||
|
||||
return $percent;
|
||||
}
|
||||
}
|
37
vendor/phpunit/php-code-coverage/src/Util/Filesystem.php
vendored
Normal file
37
vendor/phpunit/php-code-coverage/src/Util/Filesystem.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Util;
|
||||
|
||||
use function is_dir;
|
||||
use function mkdir;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Filesystem
|
||||
{
|
||||
/**
|
||||
* @throws DirectoryCouldNotBeCreatedException
|
||||
*/
|
||||
public static function createDirectory(string $directory): void
|
||||
{
|
||||
$success = !(!is_dir($directory) && !@mkdir($directory, 0777, true) && !is_dir($directory));
|
||||
|
||||
if (!$success) {
|
||||
throw new DirectoryCouldNotBeCreatedException(
|
||||
sprintf(
|
||||
'Directory "%s" could not be created',
|
||||
$directory
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
66
vendor/phpunit/php-code-coverage/src/Util/Percentage.php
vendored
Normal file
66
vendor/phpunit/php-code-coverage/src/Util/Percentage.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage\Util;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
|
||||
*/
|
||||
final class Percentage
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $fraction;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $total;
|
||||
|
||||
public static function fromFractionAndTotal(float $fraction, float $total): self
|
||||
{
|
||||
return new self($fraction, $total);
|
||||
}
|
||||
|
||||
private function __construct(float $fraction, float $total)
|
||||
{
|
||||
$this->fraction = $fraction;
|
||||
$this->total = $total;
|
||||
}
|
||||
|
||||
public function asFloat(): float
|
||||
{
|
||||
if ($this->total > 0) {
|
||||
return ($this->fraction / $this->total) * 100;
|
||||
}
|
||||
|
||||
return 100.0;
|
||||
}
|
||||
|
||||
public function asString(): string
|
||||
{
|
||||
if ($this->total > 0) {
|
||||
return sprintf('%01.2F%%', $this->asFloat());
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function asFixedWidthString(): string
|
||||
{
|
||||
if ($this->total > 0) {
|
||||
return sprintf('%6.2F%%', $this->asFloat());
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
<?php declare(strict_types=1);
|
||||
/*
|
||||
* This file is part of the php-code-coverage package.
|
||||
* This file is part of phpunit/php-code-coverage.
|
||||
*
|
||||
* (c) Sebastian Bergmann <sebastian@phpunit.de>
|
||||
*
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
namespace SebastianBergmann\CodeCoverage;
|
||||
|
||||
use function dirname;
|
||||
use SebastianBergmann\Version as VersionId;
|
||||
|
||||
final class Version
|
||||
@@ -21,8 +22,7 @@ final class Version
|
||||
public static function id(): string
|
||||
{
|
||||
if (self::$version === null) {
|
||||
$version = new VersionId('7.0.15', \dirname(__DIR__));
|
||||
self::$version = $version->getVersion();
|
||||
self::$version = (new VersionId('9.2.23', dirname(__DIR__)))->getVersion();
|
||||
}
|
||||
|
||||
return self::$version;
|
||||
|
Reference in New Issue
Block a user