Composer upgrade

This commit is contained in:
Clemens Schwaighofer
2022-02-24 13:44:33 +09:00
parent f2c0ba737a
commit c8d7b308b3
86 changed files with 961 additions and 716 deletions

View File

@@ -29,12 +29,9 @@ use ReflectionClass;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\Node\Builder;
use SebastianBergmann\CodeCoverage\Node\Directory;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingCoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingUncoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingCoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingUncoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CachingFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\ParsingFileAnalyser;
use SebastianBergmann\CodeUnitReverseLookup\Wizard;
/**
@@ -109,14 +106,9 @@ final class CodeCoverage
private $parentClassesExcludedFromUnintentionallyCoveredCodeCheck = [];
/**
* @var ?CoveredFileAnalyser
* @var ?FileAnalyser
*/
private $coveredFileAnalyser;
/**
* @var ?UncoveredFileAnalyser
*/
private $uncoveredFileAnalyser;
private $analyser;
/**
* @var ?string
@@ -136,7 +128,7 @@ final class CodeCoverage
*/
public function getReport(): Directory
{
return (new Builder($this->coveredFileAnalyser()))->build($this);
return (new Builder($this->analyser()))->build($this);
}
/**
@@ -167,8 +159,6 @@ final class CodeCoverage
$this->processUncoveredFilesFromFilter();
} elseif ($this->includeUncoveredFiles) {
$this->addUncoveredFilesFromFilter();
} else {
$this->data->removeFilesWithNoCoverage();
}
}
@@ -258,6 +248,8 @@ final class CodeCoverage
$this->applyFilter($rawData);
$this->applyExecutableLinesFilter($rawData);
if ($this->useAnnotationsForIgnoringCode) {
$this->applyIgnoredLinesFilter($rawData);
}
@@ -468,7 +460,8 @@ final class CodeCoverage
if (is_array($linesToBeCovered)) {
foreach ($linesToBeCovered as $fileToBeCovered => $includedLines) {
$rawData->keepCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
$rawData->keepLineCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
$rawData->keepFunctionCoverageDataOnlyForLines($fileToBeCovered, $includedLines);
}
}
}
@@ -486,6 +479,20 @@ final class CodeCoverage
}
}
private function applyExecutableLinesFilter(RawCodeCoverageData $data): void
{
foreach (array_keys($data->lineCoverage()) as $filename) {
if (!$this->filter->isFile($filename)) {
continue;
}
$data->keepLineCoverageDataOnlyForLines(
$filename,
$this->analyser()->executableLinesIn($filename)
);
}
}
private function applyIgnoredLinesFilter(RawCodeCoverageData $data): void
{
foreach (array_keys($data->lineCoverage()) as $filename) {
@@ -495,7 +502,7 @@ final class CodeCoverage
$data->removeCoverageDataForLines(
$filename,
$this->coveredFileAnalyser()->ignoredLinesFor($filename)
$this->analyser()->ignoredLinesFor($filename)
);
}
}
@@ -515,7 +522,7 @@ final class CodeCoverage
$this->append(
RawCodeCoverageData::fromUncoveredFile(
$uncoveredFile,
$this->uncoveredFileAnalyser()
$this->analyser()
),
self::UNCOVERED_FILES
);
@@ -646,42 +653,24 @@ final class CodeCoverage
return array_values($unintentionallyCoveredUnits);
}
private function coveredFileAnalyser(): CoveredFileAnalyser
private function analyser(): FileAnalyser
{
if ($this->coveredFileAnalyser !== null) {
return $this->coveredFileAnalyser;
if ($this->analyser !== null) {
return $this->analyser;
}
$this->coveredFileAnalyser = new ParsingCoveredFileAnalyser(
$this->analyser = new ParsingFileAnalyser(
$this->useAnnotationsForIgnoringCode,
$this->ignoreDeprecatedCode
);
if ($this->cachesStaticAnalysis()) {
$this->coveredFileAnalyser = new CachingCoveredFileAnalyser(
$this->analyser = new CachingFileAnalyser(
$this->cacheDirectory,
$this->coveredFileAnalyser
$this->analyser
);
}
return $this->coveredFileAnalyser;
}
private function uncoveredFileAnalyser(): UncoveredFileAnalyser
{
if ($this->uncoveredFileAnalyser !== null) {
return $this->uncoveredFileAnalyser;
}
$this->uncoveredFileAnalyser = new ParsingUncoveredFileAnalyser;
if ($this->cachesStaticAnalysis()) {
$this->uncoveredFileAnalyser = new CachingUncoveredFileAnalyser(
$this->cacheDirectory,
$this->uncoveredFileAnalyser
);
}
return $this->uncoveredFileAnalyser;
return $this->analyser;
}
}

View File

@@ -7,9 +7,10 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
namespace SebastianBergmann\CodeCoverage\Util;
use RuntimeException;
use SebastianBergmann\CodeCoverage\Exception;
final class DirectoryCouldNotBeCreatedException extends RuntimeException implements Exception
{

View File

@@ -14,8 +14,7 @@ use function array_merge;
use function str_replace;
use function substr;
use Countable;
use SebastianBergmann\CodeCoverage\Percentage;
use SebastianBergmann\LinesOfCode\LinesOfCode;
use SebastianBergmann\CodeCoverage\Util\Percentage;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -219,7 +218,10 @@ abstract class AbstractNode implements Countable
abstract public function functions(): array;
abstract public function linesOfCode(): LinesOfCode;
/**
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
*/
abstract public function linesOfCode(): array;
abstract public function numberOfExecutableLines(): int;

View File

@@ -22,7 +22,7 @@ use function strpos;
use function substr;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData;
use SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -30,13 +30,13 @@ use SebastianBergmann\CodeCoverage\StaticAnalysis\CoveredFileAnalyser;
final class Builder
{
/**
* @var CoveredFileAnalyser
* @var FileAnalyser
*/
private $coveredFileAnalyser;
private $analyser;
public function __construct(CoveredFileAnalyser $coveredFileAnalyser)
public function __construct(FileAnalyser $analyser)
{
$this->coveredFileAnalyser = $coveredFileAnalyser;
$this->analyser = $analyser;
}
public function build(CodeCoverage $coverage): Directory
@@ -74,10 +74,10 @@ final class Builder
$value['lineCoverage'],
$value['functionCoverage'],
$tests,
$this->coveredFileAnalyser->classesIn($filename),
$this->coveredFileAnalyser->traitsIn($filename),
$this->coveredFileAnalyser->functionsIn($filename),
$this->coveredFileAnalyser->linesOfCodeFor($filename)
$this->analyser->classesIn($filename),
$this->analyser->traitsIn($filename),
$this->analyser->functionsIn($filename),
$this->analyser->linesOfCodeFor($filename)
)
);
}

View File

@@ -7,7 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
namespace SebastianBergmann\CodeCoverage\Node;
use function sprintf;
@@ -26,11 +26,6 @@ final class CrapIndex
*/
private $codeCoverage;
public static function fromCyclomaticComplexityAndCoveragePercentage(int $cyclomaticComplexity, float $codeCoverage): self
{
return new self($cyclomaticComplexity, $codeCoverage);
}
public function __construct(int $cyclomaticComplexity, float $codeCoverage)
{
$this->cyclomaticComplexity = $cyclomaticComplexity;

View File

@@ -13,7 +13,6 @@ use function array_merge;
use function count;
use IteratorAggregate;
use RecursiveIteratorIterator;
use SebastianBergmann\LinesOfCode\LinesOfCode;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -51,7 +50,7 @@ final class Directory extends AbstractNode implements IteratorAggregate
private $functions;
/**
* @var LinesOfCode
* @psalm-var null|array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
*/
private $linesOfCode;
@@ -233,13 +232,24 @@ final class Directory extends AbstractNode implements IteratorAggregate
return $this->functions;
}
public function linesOfCode(): LinesOfCode
/**
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
*/
public function linesOfCode(): array
{
if ($this->linesOfCode === null) {
$this->linesOfCode = new LinesOfCode(0, 0, 0, 0);
$this->linesOfCode = [
'linesOfCode' => 0,
'commentLinesOfCode' => 0,
'nonCommentLinesOfCode' => 0,
];
foreach ($this->children as $child) {
$this->linesOfCode = $this->linesOfCode->plus($child->linesOfCode());
$childLinesOfCode = $child->linesOfCode();
$this->linesOfCode['linesOfCode'] += $childLinesOfCode['linesOfCode'];
$this->linesOfCode['commentLinesOfCode'] += $childLinesOfCode['commentLinesOfCode'];
$this->linesOfCode['nonCommentLinesOfCode'] += $childLinesOfCode['nonCommentLinesOfCode'];
}
}

View File

@@ -12,8 +12,6 @@ namespace SebastianBergmann\CodeCoverage\Node;
use function array_filter;
use function count;
use function range;
use SebastianBergmann\CodeCoverage\CrapIndex;
use SebastianBergmann\LinesOfCode\LinesOfCode;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -81,7 +79,7 @@ final class File extends AbstractNode
private $functions = [];
/**
* @var LinesOfCode
* @psalm-var array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
*/
private $linesOfCode;
@@ -125,7 +123,10 @@ final class File extends AbstractNode
*/
private $codeUnitsByLine = [];
public function __construct(string $name, AbstractNode $parent, array $lineCoverageData, array $functionCoverageData, array $testData, array $classes, array $traits, array $functions, LinesOfCode $linesOfCode)
/**
* @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);
@@ -172,7 +173,10 @@ final class File extends AbstractNode
return $this->functions;
}
public function linesOfCode(): LinesOfCode
/**
* @psalm-return array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}
*/
public function linesOfCode(): array
{
return $this->linesOfCode;
}
@@ -330,7 +334,7 @@ final class File extends AbstractNode
private function calculateStatistics(array $classes, array $traits, array $functions): void
{
foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) {
foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
$this->codeUnitsByLine[$lineNumber] = [];
}
@@ -338,7 +342,7 @@ final class File extends AbstractNode
$this->processTraits($traits);
$this->processFunctions($functions);
foreach (range(1, $this->linesOfCode->linesOfCode()) as $lineNumber) {
foreach (range(1, $this->linesOfCode['linesOfCode']) as $lineNumber) {
if (isset($this->lineCoverageData[$lineNumber])) {
foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) {
$codeUnit['executableLines']++;
@@ -557,7 +561,8 @@ final class File extends AbstractNode
$this->functions[$functionName]['executedBranches'] = count(
array_filter(
$this->functionCoverageData[$functionName]['branches'],
static function (array $branch) {
static function (array $branch)
{
return (bool) $branch['hit'];
}
)
@@ -572,7 +577,8 @@ final class File extends AbstractNode
$this->functions[$functionName]['executedPaths'] = count(
array_filter(
$this->functionCoverageData[$functionName]['paths'],
static function (array $path) {
static function (array $path)
{
return (bool) $path['hit'];
}
)
@@ -616,7 +622,8 @@ final class File extends AbstractNode
$methodData['executedBranches'] = count(
array_filter(
$this->functionCoverageData[$key]['branches'],
static function (array $branch) {
static function (array $branch)
{
return (bool) $branch['hit'];
}
)
@@ -631,7 +638,8 @@ final class File extends AbstractNode
$methodData['executedPaths'] = count(
array_filter(
$this->functionCoverageData[$key]['paths'],
static function (array $path) {
static function (array $path)
{
return (bool) $path['hit'];
}
)

View File

@@ -132,18 +132,6 @@ final class ProcessedCodeCoverageData
unset($this->lineCoverage[$oldFile], $this->functionCoverage[$oldFile]);
}
public function removeFilesWithNoCoverage(): void
{
foreach ($this->lineCoverage as $file => $lines) {
foreach ($lines as $line) {
if (is_array($line) && !empty($line)) {
continue 2;
}
}
unset($file);
}
}
public function merge(self $newData): void
{
foreach ($newData->lineCoverage as $file => $lines) {

View File

@@ -15,11 +15,10 @@ use function array_flip;
use function array_intersect;
use function array_intersect_key;
use function count;
use function file;
use function in_array;
use function range;
use SebastianBergmann\CodeCoverage\Driver\Driver;
use SebastianBergmann\CodeCoverage\StaticAnalysis\UncoveredFileAnalyser;
use SebastianBergmann\CodeCoverage\StaticAnalysis\FileAnalyser;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
@@ -84,11 +83,11 @@ final class RawCodeCoverageData
return new self($lineCoverage, $functionCoverage);
}
public static function fromUncoveredFile(string $filename, UncoveredFileAnalyser $uncoveredFileAnalyser): self
public static function fromUncoveredFile(string $filename, FileAnalyser $analyser): self
{
$lineCoverage = [];
foreach ($uncoveredFileAnalyser->executableLinesIn($filename) as $line) {
foreach ($analyser->executableLinesIn($filename) as $line) {
$lineCoverage[$line] = Driver::LINE_NOT_EXECUTED;
}
@@ -126,7 +125,7 @@ final class RawCodeCoverageData
/**
* @param int[] $lines
*/
public function keepCoverageDataOnlyForLines(string $filename, array $lines): void
public function keepLineCoverageDataOnlyForLines(string $filename, array $lines): void
{
if (!isset($this->lineCoverage[$filename])) {
return;
@@ -136,17 +135,25 @@ final class RawCodeCoverageData
$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_diff(range($branch['line_start'], $branch['line_end']), $lines)) > 0) {
unset($this->functionCoverage[$filename][$functionName]['branches'][$branchId]);
/**
* @param int[] $lines
*/
public function keepFunctionCoverageDataOnlyForLines(string $filename, array $lines): void
{
if (!isset($this->functionCoverage[$filename])) {
return;
}
foreach ($functionData['paths'] as $pathId => $path) {
if (in_array($branchId, $path['path'], true)) {
unset($this->functionCoverage[$filename][$functionName]['paths'][$pathId]);
}
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]);
}
}
}

View File

@@ -19,9 +19,9 @@ use function range;
use function time;
use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Directory;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
final class Clover
{
@@ -194,8 +194,8 @@ final class Clover
$linesOfCode = $item->linesOfCode();
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode());
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode());
$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());
@@ -227,8 +227,8 @@ final class Clover
$xmlMetrics = $xmlDocument->createElement('metrics');
$xmlMetrics->setAttribute('files', (string) count($report));
$xmlMetrics->setAttribute('loc', (string) $linesOfCode->linesOfCode());
$xmlMetrics->setAttribute('ncloc', (string) $linesOfCode->nonCommentLinesOfCode());
$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());
@@ -243,7 +243,7 @@ final class Clover
$buffer = $xmlDocument->saveXML();
if ($target !== null) {
Directory::create(dirname($target));
Filesystem::createDirectory(dirname($target));
if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);

View File

@@ -16,9 +16,9 @@ use function range;
use function time;
use DOMImplementation;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Directory;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
final class Cobertura
{
@@ -292,7 +292,7 @@ final class Cobertura
$buffer = $document->saveXML();
if ($target !== null) {
Directory::create(dirname($target));
Filesystem::createDirectory(dirname($target));
if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);

View File

@@ -17,9 +17,9 @@ use function is_string;
use function round;
use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Directory;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
final class Crap4j
{
@@ -124,7 +124,7 @@ final class Crap4j
$buffer = $document->saveXML();
if ($target !== null) {
Directory::create(dirname($target));
Filesystem::createDirectory(dirname($target));
if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);

View File

@@ -15,8 +15,9 @@ use function date;
use function dirname;
use function substr;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Directory as DirectoryUtil;
use SebastianBergmann\CodeCoverage\InvalidArgumentException;
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
final class Facade
{
@@ -42,6 +43,12 @@ 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;
@@ -88,14 +95,14 @@ final class Facade
$id = $node->id();
if ($node instanceof DirectoryNode) {
DirectoryUtil::create($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);
DirectoryUtil::create($dir);
Filesystem::createDirectory($dir);
$file->render($node, $target . $id);
}
@@ -133,7 +140,7 @@ final class Facade
$directory .= DIRECTORY_SEPARATOR;
}
DirectoryUtil::create($directory);
Filesystem::createDirectory($directory);
return $directory;
}

View File

@@ -96,7 +96,7 @@ use function token_get_all;
use function trim;
use PHPUnit\Runner\BaseTestRunner;
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
use SebastianBergmann\CodeCoverage\Percentage;
use SebastianBergmann\CodeCoverage\Util\Percentage;
use SebastianBergmann\Template\Template;
/**

File diff suppressed because one or more lines are too long

View File

@@ -96,11 +96,6 @@ span.success, span.warning, span.danger {
text-align: center;
}
#classCoverageDistribution, #classComplexity {
height: 200px;
width: 475px;
}
#toplink {
position: fixed;
left: 5px;

View File

@@ -33,9 +33,9 @@
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
<td colspan="4"><div align="center"><strong>Functions and Methods</strong></div></td>
<td colspan="3"><div align="center"><strong>Lines</strong></div></td>
<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>

View File

@@ -33,11 +33,11 @@
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="3"><div align="center"><strong>Classes and Traits</strong></div></td>
<td colspan="4"><div align="center"><strong>Functions and Methods</strong></div></td>
<td colspan="3"><div align="center"><strong>Paths</strong></div></td>
<td colspan="3"><div align="center"><strong>Branches</strong></div></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>

View File

@@ -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}} 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>

View File

@@ -1,20 +1,20 @@
<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}} 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="{{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="{{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="{{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>

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

View File

@@ -1,11 +1,12 @@
<tr>
<td class="{{methods_level}}" colspan="4">{{name}}</td>
<td class="{{methods_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>

View File

@@ -1,17 +1,18 @@
<tr>
<td class="{{methods_level}}" colspan="4">{{name}}</td>
<td class="{{methods_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="{{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="{{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="{{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>

View File

@@ -14,8 +14,8 @@ use function file_put_contents;
use function serialize;
use function sprintf;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Directory;
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
final class PHP
{
@@ -31,7 +31,7 @@ return \unserialize(<<<'END_OF_COVERAGE_SERIALIZATION'%s%s%sEND_OF_COVERAGE_SERI
);
if ($target !== null) {
Directory::create(dirname($target));
Filesystem::createDirectory(dirname($target));
if (@file_put_contents($target, $buffer) === false) {
throw new WriteOperationFailedException($target);

View File

@@ -19,7 +19,7 @@ use function str_pad;
use function strlen;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Node\File;
use SebastianBergmann\CodeCoverage\Percentage;
use SebastianBergmann\CodeCoverage\Util\Percentage;
final class Text
{

View File

@@ -28,12 +28,12 @@ use function substr;
use DateTimeImmutable;
use DOMDocument;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Directory as DirectoryUtil;
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\Util\Filesystem as DirectoryUtil;
use SebastianBergmann\CodeCoverage\Version;
use SebastianBergmann\CodeCoverage\XmlException;
use SebastianBergmann\Environment\Runtime;
@@ -109,7 +109,7 @@ final class Facade
}
}
DirectoryUtil::create($directory);
DirectoryUtil::createDirectory($directory);
}
/**
@@ -240,9 +240,9 @@ final class Facade
$loc = $node->linesOfCode();
$totals->setNumLines(
$loc->linesOfCode(),
$loc->commentLinesOfCode(),
$loc->nonCommentLinesOfCode(),
$loc['linesOfCode'],
$loc['commentLinesOfCode'],
$loc['nonCommentLinesOfCode'],
$node->numberOfExecutableLines(),
$node->numberOfExecutedLines()
);

View File

@@ -12,7 +12,7 @@ namespace SebastianBergmann\CodeCoverage\Report\Xml;
use function sprintf;
use DOMElement;
use DOMNode;
use SebastianBergmann\CodeCoverage\Percentage;
use SebastianBergmann\CodeCoverage\Util\Percentage;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage

View File

@@ -1,90 +0,0 @@
<?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 const DIRECTORY_SEPARATOR;
use function file_get_contents;
use function file_put_contents;
use function filemtime;
use function hash;
use function is_file;
use function serialize;
use function unserialize;
use SebastianBergmann\CodeCoverage\Directory;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
abstract class Cache
{
/**
* @var string
*/
private $directory;
public function __construct(string $directory)
{
Directory::create($directory);
$this->directory = $directory;
}
protected function has(string $filename, string $key): bool
{
$cacheFile = $this->cacheFile($filename, $key);
if (!is_file($cacheFile)) {
return false;
}
if (filemtime($cacheFile) < filemtime($filename)) {
return false;
}
return true;
}
/**
* @psalm-param list<class-string> $allowedClasses
*
* @return mixed
*/
protected function read(string $filename, string $key, array $allowedClasses = [])
{
$options = ['allowed_classes' => false];
if (!empty($allowedClasses)) {
$options = ['allowed_classes' => $allowedClasses];
}
return unserialize(
file_get_contents(
$this->cacheFile($filename, $key)
),
$options
);
}
/**
* @param mixed $data
*/
protected function write(string $filename, string $key, $data): void
{
file_put_contents(
$this->cacheFile($filename, $key),
serialize($data)
);
}
private function cacheFile(string $filename, string $key): string
{
return $this->directory . DIRECTORY_SEPARATOR . hash('sha256', $filename . $key);
}
}

View File

@@ -15,24 +15,16 @@ final class CacheWarmer
{
public function warmCache(string $cacheDirectory, bool $useAnnotationsForIgnoringCode, bool $ignoreDeprecatedCode, Filter $filter): void
{
$coveredFileAnalyser = new CachingCoveredFileAnalyser(
$analyser = new CachingFileAnalyser(
$cacheDirectory,
new ParsingCoveredFileAnalyser(
new ParsingFileAnalyser(
$useAnnotationsForIgnoringCode,
$ignoreDeprecatedCode
)
);
$uncoveredFileAnalyser = new CachingUncoveredFileAnalyser(
$cacheDirectory,
new ParsingUncoveredFileAnalyser
);
foreach ($filter->files() as $file) {
$coveredFileAnalyser->process($file);
/* @noinspection UnusedFunctionResultInspection */
$uncoveredFileAnalyser->executableLinesIn($file);
$analyser->process($file);
}
}
}

View File

@@ -1,99 +0,0 @@
<?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\LinesOfCode\LinesOfCode;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class CachingCoveredFileAnalyser extends Cache implements CoveredFileAnalyser
{
/**
* @var CoveredFileAnalyser
*/
private $coveredFileAnalyser;
/**
* @var array
*/
private $cache = [];
public function __construct(string $directory, CoveredFileAnalyser $coveredFileAnalyser)
{
parent::__construct($directory);
$this->coveredFileAnalyser = $coveredFileAnalyser;
}
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'];
}
public function linesOfCodeFor(string $filename): LinesOfCode
{
if (!isset($this->cache[$filename])) {
$this->process($filename);
}
return $this->cache[$filename]['linesOfCodeFor'];
}
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
{
if ($this->has($filename, __CLASS__)) {
$this->cache[$filename] = $this->read($filename, __CLASS__, [LinesOfCode::class]);
return;
}
$this->cache[$filename] = [
'classesIn' => $this->coveredFileAnalyser->classesIn($filename),
'traitsIn' => $this->coveredFileAnalyser->traitsIn($filename),
'functionsIn' => $this->coveredFileAnalyser->functionsIn($filename),
'linesOfCodeFor' => $this->coveredFileAnalyser->linesOfCodeFor($filename),
'ignoredLinesFor' => $this->coveredFileAnalyser->ignoredLinesFor($filename),
];
$this->write($filename, __CLASS__, $this->cache[$filename]);
}
}

View File

@@ -0,0 +1,183 @@
<?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 crc32;
use function file_get_contents;
use function file_put_contents;
use function is_file;
use function serialize;
use GlobIterator;
use SebastianBergmann\CodeCoverage\Util\Filesystem;
use SplFileInfo;
/**
* @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;
if (self::$cacheVersion === null) {
$this->calculateCacheVersion();
}
}
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 . hash('sha256', $filename . crc32(file_get_contents($filename)) . self::$cacheVersion);
}
private function calculateCacheVersion(): void
{
$buffer = '';
foreach (new GlobIterator(__DIR__ . '/*.php') as $file) {
assert($file instanceof SplFileInfo);
$buffer .= file_get_contents($file->getPathname());
}
self::$cacheVersion = (string) crc32($buffer);
}
}

View File

@@ -1,41 +0,0 @@
<?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
*/
final class CachingUncoveredFileAnalyser extends Cache implements UncoveredFileAnalyser
{
/**
* @var UncoveredFileAnalyser
*/
private $uncoveredFileAnalyser;
public function __construct(string $directory, UncoveredFileAnalyser $uncoveredFileAnalyser)
{
parent::__construct($directory);
$this->uncoveredFileAnalyser = $uncoveredFileAnalyser;
}
public function executableLinesIn(string $filename): array
{
if ($this->has($filename, __METHOD__)) {
return $this->read($filename, __METHOD__);
}
$data = $this->uncoveredFileAnalyser->executableLinesIn($filename);
$this->write($filename, __METHOD__, $data);
return $data;
}
}

View File

@@ -206,7 +206,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
$this->classes[$namespacedName] = [
'name' => $name,
'namespacedName' => (string) $namespacedName,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),
@@ -221,7 +221,7 @@ final class CodeUnitFindingVisitor extends NodeVisitorAbstract
$this->traits[$namespacedName] = [
'name' => $name,
'namespacedName' => (string) $namespacedName,
'namespacedName' => $namespacedName,
'namespace' => $this->namespace($namespacedName, $name),
'startLine' => $node->getStartLine(),
'endLine' => $node->getEndLine(),

View File

@@ -9,9 +9,19 @@
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
use function array_unique;
use function sort;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\Expr\Cast;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\NullsafePropertyFetch;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Scalar\Encapsed;
use PhpParser\Node\Stmt\Break_;
use PhpParser\Node\Stmt\Case_;
use PhpParser\Node\Stmt\Catch_;
@@ -26,6 +36,7 @@ use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\Goto_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\Throw_;
@@ -40,49 +51,122 @@ use PhpParser\NodeVisitorAbstract;
final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
{
/**
* @psalm-var list<int>
* @psalm-var array<int, int>
*/
private $executableLines = [];
/**
* @psalm-var array<int, int>
*/
private $propertyLines = [];
public function enterNode(Node $node): void
{
$this->savePropertyLines($node);
if (!$this->isExecutable($node)) {
return;
}
$this->executableLines[] = $node->getStartLine();
foreach ($this->getLines($node) as $line) {
if (isset($this->propertyLines[$line])) {
return;
}
$this->executableLines[$line] = $line;
}
}
/**
* @psalm-return list<int>
* @psalm-return array<int, int>
*/
public function executableLines(): array
{
$executableLines = array_unique($this->executableLines);
sort($this->executableLines);
sort($executableLines);
return $this->executableLines;
}
return $executableLines;
private function savePropertyLines(Node $node): void
{
if (!$node instanceof Property && !$node instanceof Node\Stmt\ClassConst) {
return;
}
foreach (range($node->getStartLine(), $node->getEndLine()) as $index) {
$this->propertyLines[$index] = $index;
}
}
/**
* @return int[]
*/
private function getLines(Node $node): array
{
if ($node instanceof Cast ||
$node instanceof PropertyFetch ||
$node instanceof NullsafePropertyFetch ||
$node instanceof StaticPropertyFetch) {
return [$node->getEndLine()];
}
if ($node instanceof ArrayDimFetch) {
if (null === $node->dim) {
return [];
}
return [$node->dim->getStartLine()];
}
if ($node instanceof MethodCall) {
return [$node->name->getStartLine()];
}
if ($node instanceof Ternary) {
$lines = [$node->cond->getStartLine()];
if (null !== $node->if) {
$lines[] = $node->if->getStartLine();
}
$lines[] = $node->else->getStartLine();
return $lines;
}
return [$node->getStartLine()];
}
private function isExecutable(Node $node): bool
{
return $node instanceof Break_ ||
return $node instanceof Assign ||
$node instanceof ArrayDimFetch ||
$node instanceof BinaryOp ||
$node instanceof Break_ ||
$node instanceof CallLike ||
$node instanceof Case_ ||
$node instanceof Cast ||
$node instanceof Catch_ ||
$node instanceof Closure ||
$node instanceof Continue_ ||
$node instanceof Do_ ||
$node instanceof Echo_ ||
$node instanceof ElseIf_ ||
$node instanceof Else_ ||
$node instanceof Encapsed ||
$node instanceof Expression ||
$node instanceof Finally_ ||
$node instanceof Foreach_ ||
$node instanceof For_ ||
$node instanceof Foreach_ ||
$node instanceof Goto_ ||
$node instanceof If_ ||
$node instanceof MethodCall ||
$node instanceof NullsafePropertyFetch ||
$node instanceof PropertyFetch ||
$node instanceof Return_ ||
$node instanceof StaticPropertyFetch ||
$node instanceof Switch_ ||
$node instanceof Ternary ||
$node instanceof Throw_ ||
$node instanceof TryCatch ||
$node instanceof Unset_ ||

View File

@@ -9,12 +9,10 @@
*/
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
use SebastianBergmann\LinesOfCode\LinesOfCode;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
interface CoveredFileAnalyser
interface FileAnalyser
{
public function classesIn(string $filename): array;
@@ -22,7 +20,12 @@ interface CoveredFileAnalyser
public function functionsIn(string $filename): array;
public function linesOfCodeFor(string $filename): LinesOfCode;
/**
* @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;
}

View File

@@ -18,7 +18,6 @@ use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
/**
@@ -47,18 +46,18 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
$this->ignoreDeprecated = $ignoreDeprecated;
}
public function enterNode(Node $node): ?int
public function enterNode(Node $node): void
{
if (!$node instanceof Class_ &&
!$node instanceof Trait_ &&
!$node instanceof Interface_ &&
!$node instanceof ClassMethod &&
!$node instanceof Function_) {
return null;
return;
}
if ($node instanceof Class_ && $node->isAnonymous()) {
return null;
return;
}
// Workaround for https://bugs.xdebug.org/view.php?id=1798
@@ -69,17 +68,17 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
}
if (!$this->useAnnotationsForIgnoringCode) {
return null;
return;
}
if ($node instanceof Interface_) {
return null;
return;
}
$docComment = $node->getDocComment();
if ($docComment === null) {
return null;
return;
}
if (strpos($docComment->getText(), '@codeCoverageIgnore') !== false) {
@@ -95,12 +94,6 @@ final class IgnoredLinesFindingVisitor extends NodeVisitorAbstract
range($node->getStartLine(), $node->getEndLine())
);
}
if ($node instanceof ClassMethod || $node instanceof Function_) {
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
}
return null;
}
/**

View File

@@ -25,12 +25,11 @@ use PhpParser\NodeVisitor\ParentConnectingVisitor;
use PhpParser\ParserFactory;
use SebastianBergmann\CodeCoverage\ParserException;
use SebastianBergmann\LinesOfCode\LineCountingVisitor;
use SebastianBergmann\LinesOfCode\LinesOfCode;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser
final class ParsingFileAnalyser implements FileAnalyser
{
/**
* @var array
@@ -48,7 +47,7 @@ final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser
private $functions = [];
/**
* @var LinesOfCode[]
* @var array<string,array{linesOfCode: int, commentLinesOfCode: int, nonCommentLinesOfCode: int}>
*/
private $linesOfCode = [];
@@ -57,6 +56,11 @@ final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser
*/
private $ignoredLines = [];
/**
* @var array
*/
private $executableLines = [];
/**
* @var bool
*/
@@ -94,13 +98,23 @@ final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser
return $this->functions[$filename];
}
public function linesOfCodeFor(string $filename): LinesOfCode
/**
* @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);
@@ -134,16 +148,18 @@ final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser
assert($nodes !== null);
$traverser = new NodeTraverser;
$codeUnitFindingVisitor = new CodeUnitFindingVisitor;
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode);
$traverser = new NodeTraverser;
$codeUnitFindingVisitor = new CodeUnitFindingVisitor;
$lineCountingVisitor = new LineCountingVisitor($linesOfCode);
$ignoredLinesFindingVisitor = new IgnoredLinesFindingVisitor($this->useAnnotationsForIgnoringCode, $this->ignoreDeprecatedCode);
$executableLinesFindingVisitor = new ExecutableLinesFindingVisitor;
$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);
@@ -161,11 +177,11 @@ final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser
}
// @codeCoverageIgnoreEnd
$this->classes[$filename] = $codeUnitFindingVisitor->classes();
$this->traits[$filename] = $codeUnitFindingVisitor->traits();
$this->functions[$filename] = $codeUnitFindingVisitor->functions();
$this->linesOfCode[$filename] = $lineCountingVisitor->result();
$this->ignoredLines[$filename] = [];
$this->classes[$filename] = $codeUnitFindingVisitor->classes();
$this->traits[$filename] = $codeUnitFindingVisitor->traits();
$this->functions[$filename] = $codeUnitFindingVisitor->functions();
$this->executableLines[$filename] = $executableLinesFindingVisitor->executableLines();
$this->ignoredLines[$filename] = [];
$this->findLinesIgnoredByLineBasedAnnotations($filename, $source, $this->useAnnotationsForIgnoringCode);
@@ -177,6 +193,14 @@ final class ParsingCoveredFileAnalyser implements CoveredFileAnalyser
);
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

View File

@@ -1,51 +0,0 @@
<?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 PhpParser\Error;
use PhpParser\Lexer;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class ParsingUncoveredFileAnalyser implements UncoveredFileAnalyser
{
public function executableLinesIn(string $filename): array
{
$parser = (new ParserFactory)->create(
ParserFactory::PREFER_PHP7,
new Lexer
);
try {
$nodes = $parser->parse(file_get_contents($filename));
assert($nodes !== null);
$traverser = new NodeTraverser;
$visitor = new ExecutableLinesFindingVisitor;
$traverser->addVisitor($visitor);
/* @noinspection UnusedFunctionResultInspection */
$traverser->traverse($nodes);
return $visitor->executableLines();
// @codeCoverageIgnoreStart
} catch (Error $error) {
}
// @codeCoverageIgnoreEnd
return [];
}
}

View File

@@ -1,18 +0,0 @@
<?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 UncoveredFileAnalyser
{
public function executableLinesIn(string $filename): array;
}

View File

@@ -7,7 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
namespace SebastianBergmann\CodeCoverage\Util;
use function is_dir;
use function mkdir;
@@ -16,12 +16,12 @@ use function sprintf;
/**
* @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
*/
final class Directory
final class Filesystem
{
/**
* @throws DirectoryCouldNotBeCreatedException
*/
public static function create(string $directory): void
public static function createDirectory(string $directory): void
{
$success = !(!is_dir($directory) && !@mkdir($directory, 0777, true) && !is_dir($directory));

View File

@@ -7,7 +7,7 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace SebastianBergmann\CodeCoverage;
namespace SebastianBergmann\CodeCoverage\Util;
use function sprintf;

View File

@@ -22,7 +22,7 @@ final class Version
public static function id(): string
{
if (self::$version === null) {
self::$version = (new VersionId('9.2.10', dirname(__DIR__)))->getVersion();
self::$version = (new VersionId('9.2.13', dirname(__DIR__)))->getVersion();
}
return self::$version;