Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@
"@code-coverage",
"@phpstan",
"@psalm",
"@psalm:security",
"@psalm:shepherd"
],
"clear-build": "rm -rf ./build/*",
Expand Down
58 changes: 32 additions & 26 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,31 +1,37 @@
parameters:
ignoreErrors:
-
rawMessage: 'Method Esi\CoverageCheck\CoverageCheck::loadMetrics() never returns false so it can be removed from the return type.'
identifier: return.unusedType
count: 1
path: src/CoverageCheck.php
ignoreErrors:
-
rawMessage: 'Method Esi\CoverageCheck\CoverageCheck::loadMetrics() never returns false so it can be removed from the return type.'
identifier: return.unusedType
count: 1
path: src/CoverageCheck.php

-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::error() has parameter $message with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php
-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::error() has parameter $message with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php

-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::success() has parameter $message with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php
-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::success() has parameter $message with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php

-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::table() has parameter $headers with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php
-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::table() has parameter $headers with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php

-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::table() has parameter $rows with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php
-
rawMessage: 'Method Esi\CoverageCheck\Style\CoverageCheckStyle::table() has parameter $rows with no value type specified in iterable type array.'
identifier: missingType.iterableValue
count: 1
path: src/Style/CoverageCheckStyle.php

-
rawMessage: 'Readonly property Esi\CoverageCheck\Data\Threshold::$value is assigned outside of its declaring class.'
identifier: property.readOnlyAssignOutOfClass
count: 1
path: tests/src/Data/ThresholdTest.php
31 changes: 17 additions & 14 deletions src/Command/CoverageCheckCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

use Esi\CoverageCheck\Application;
use Esi\CoverageCheck\CoverageCheck;
use Esi\CoverageCheck\Data\Threshold;
use Esi\CoverageCheck\Style\CoverageCheckStyle;
use Esi\CoverageCheck\Utils;
use Symfony\Component\Console\Attribute\Argument;
Expand Down Expand Up @@ -71,7 +72,7 @@
*
* @since 3.0.0
*/
public const string ERROR_COVERAGE_BELOW_THRESHOLD = 'Total code coverage is %s which is below the accepted %d%%';
public const string ERROR_COVERAGE_BELOW_THRESHOLD = 'Total code coverage is %s which is below the accepted %s';

/**
* Matches CoverageCheck::ERROR_INSUFFICIENT_DATA, except for '[ERROR]' prefix.
Expand Down Expand Up @@ -143,7 +144,7 @@
#[Argument(description: self::INPUT_ARGUMENT_CLOVERFILE)]
string $cloverfile,
#[Argument(description: self::INPUT_ARGUMENT_THRESHOLD)]
int $threshold,
string $threshold,
#[Option(description: self::INPUT_ARGUMENT_TABLEWIDTH, name: 'table-width', shortcut: 'W')]
int $tablewidth = 70,
#[Option(description: self::INPUT_OPTION_ONLY_PERCENTAGE, name: 'only-percentage', shortcut: 'O')]
Expand All @@ -157,8 +158,9 @@

$this->coverageCheckStyle = new CoverageCheckStyle($input, $output, $tablewidth);

$this->coverageCheck->setCloverFile($cloverfile)
->setThreshold($threshold)
$this->coverageCheck
->setCloverFile($cloverfile)
->setThreshold(Threshold::from($threshold))
->setOnlyPercentage($onlypercentage);

try {
Expand Down Expand Up @@ -193,10 +195,11 @@
*/
private function getFileTable(array $result): int
{
$threshold = $this->coverageCheck->getThreshold();
$tableRows = [];
$threshold = $this->coverageCheck->getThreshold();
$tableRows = [];
// @todo: Convert to ValueObject?

Check warning on line 200 in src/Command/CoverageCheckCommand.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=ericsizemore_phpunit-coverage-check&issues=AZyYweb3jkAT8tBWpQmj&open=AZyYweb3jkAT8tBWpQmj&pullRequest=528
$totalElements = ['coveredMetrics' => 0, 'totalMetrics' => 0];
$metrics = $result['fileMetrics'];
$metrics = $result['fileMetrics'];
$totalCoverage = $result['totalCoverage'];

unset($result);
Expand All @@ -210,15 +213,15 @@
[
'style' => new TableCellStyle(
[
'cellFormat' => ($file['percentage'] < $threshold) ? '<error>%s</error>' : '<info>%s</info>',
'cellFormat' => ($file['percentage'] < $threshold->value) ? '<error>%s</error>' : '<info>%s</info>',
]
),
]
),
];

$totalElements['coveredMetrics'] += $file['coveredMetrics'];
$totalElements['totalMetrics'] += $file['totalMetrics'];
$totalElements['totalMetrics'] += $file['totalMetrics'];
}

unset($metrics);
Expand All @@ -229,7 +232,7 @@
\sprintf('%d/%d', $totalElements['coveredMetrics'], $totalElements['totalMetrics']),
new TableCell(
Utils::formatCoverage($totalCoverage),
['style' => new TableCellStyle(['cellFormat' => ($totalCoverage < $threshold) ? '<error>%s</error>' : '<info>%s</info>', ])]
['style' => new TableCellStyle(['cellFormat' => ($totalCoverage < $threshold->value) ? '<error>%s</error>' : '<info>%s</info>',])]
),
];

Expand All @@ -240,7 +243,7 @@

unset($tableRows);

if ($totalCoverage < $threshold) {
if ($totalCoverage < $threshold->value) {
return Command::FAILURE;
}

Expand All @@ -249,10 +252,10 @@

private function getResultOutput(float $result): int
{
$threshold = $this->coverageCheck->getThreshold();
$onlyPercentage = $this->coverageCheck->getOnlyPercentage();
$threshold = $this->coverageCheck->getThreshold();
$onlyPercentage = $this->coverageCheck->getOnlyPercentage();
$formattedCoverage = Utils::formatCoverage($result);
$belowThreshold = $result < $threshold;
$belowThreshold = $result < $threshold->value;

// Only display the percentage?
if ($onlyPercentage) {
Expand Down
64 changes: 33 additions & 31 deletions src/CoverageCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

namespace Esi\CoverageCheck;

use Esi\CoverageCheck\Data\Threshold;
use Esi\CoverageCheck\Exceptions\FailedToGetFileContentsException;
use Esi\CoverageCheck\Exceptions\InvalidInputFileException;
use Esi\CoverageCheck\Exceptions\NotAValidCloverFileException;
use Esi\CoverageCheck\Exceptions\ThresholdOutOfBoundsException;
use InvalidArgumentException;
use RuntimeException;
use SimpleXMLElement;
Expand All @@ -40,7 +40,7 @@
* @see Command\CoverageCheckCommand::ERROR_COVERAGE_BELOW_THRESHOLD
* @since 3.0.0
*/
public const string ERROR_COVERAGE_BELOW_THRESHOLD = '[ERROR] Total code coverage is %s%% which is below the accepted %d%%';
public const string ERROR_COVERAGE_BELOW_THRESHOLD = '[ERROR] Total code coverage is %s which is below the accepted %s';

/**
* Message returned if there is not enough data to calculate coverage.
Expand Down Expand Up @@ -85,7 +85,12 @@

private bool $onlyPercentage = false;

private int $threshold = 100;
private Threshold $threshold;

public function __construct()
{
$this->threshold = Threshold::from(100);
}

/**
* Simple getters.
Expand All @@ -101,7 +106,7 @@
return $this->onlyPercentage;
}

public function getThreshold(): int
public function getThreshold(): Threshold
{
return $this->threshold;
}
Expand All @@ -115,8 +120,12 @@
* @throws InvalidArgumentException If the clover file does not exist, or the threshold is not within
* defined range (>= 1 <= 100).
*/
public function nonConsoleCall(string $cloverFile, int $threshold = 100, bool $onlyPercentage = false): string
public function nonConsoleCall(string $cloverFile, float|Threshold $threshold = 100, bool $onlyPercentage = false): string

Check warning on line 123 in src/CoverageCheck.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This method has 4 returns, which is more than the 3 allowed.

See more on https://sonarcloud.io/project/issues?id=ericsizemore_phpunit-coverage-check&issues=AZyYwecPjkAT8tBWpQmk&open=AZyYwecPjkAT8tBWpQmk&pullRequest=528
{
if (\is_float($threshold)) {
$threshold = Threshold::from($threshold);
}

$this->setCloverFile($cloverFile)
->setThreshold($threshold)
->setOnlyPercentage($onlyPercentage);
Expand All @@ -127,11 +136,11 @@
return self::ERROR_INSUFFICIENT_DATA;
}

if ($results < $threshold && !$onlyPercentage) {
if ($results < $threshold->value && !$onlyPercentage) {
return \sprintf(
self::ERROR_COVERAGE_BELOW_THRESHOLD,
Utils::formatCoverage($results),
$threshold
(string) $threshold,
);
}

Expand Down Expand Up @@ -177,7 +186,7 @@
unset($rawMetrics);

$coveredMetrics = $metrics['coveredconditionals'] + $metrics['coveredstatements'] + $metrics['coveredmethods'];
$totalMetrics = $metrics['conditionals'] + $metrics['statements'] + $metrics['methods'];
$totalMetrics = $metrics['conditionals'] + $metrics['statements'] + $metrics['methods'];

unset($metrics);

Expand All @@ -191,22 +200,22 @@
/**
* Parses the clover xml file for coverage metrics by file.
*
* @see self::process()
* @return array{
* fileMetrics: array<string, array{coveredMetrics: int, totalMetrics: int, percentage: float|int}>,
* totalCoverage: float|int
* }|false
* @see self::loadMetrics()
* @see https://confluence.atlassian.com/pages/viewpage.action?pageId=79986990
* @see https://ocramius.github.io/blog/automated-code-coverage-check-for-github-pull-requests-with-travis/
* @since 2.0.0
*
* @return array{
* fileMetrics: array<string, array{coveredMetrics: int, totalMetrics: int, percentage: float|int}>,
* totalCoverage: float|int
* }|false
* @see self::process()
*/
public function processByFile(): array|false
{
$fileMetrics = [];
$fileMetrics = [];
$totalElementsCovered = 0;
$totalElements = 0;
$totalElements = 0;

$rawMetrics = $this->loadMetrics(self::XPATH_FILES) ?? false;

Expand All @@ -227,15 +236,15 @@
$metrics = array_map(\intval(...), $metrics);

$coveredMetrics = ($metrics['coveredconditionals'] + $metrics['coveredstatements'] + $metrics['coveredmethods']);
$totalMetrics = ($metrics['conditionals'] + $metrics['statements'] + $metrics['methods']);
$totalMetrics = ($metrics['conditionals'] + $metrics['statements'] + $metrics['methods']);

if ($totalMetrics === 0) {
continue;
}

$coveragePercentage = (float) ($coveredMetrics / $totalMetrics) * 100.0;
$totalElementsCovered += $coveredMetrics;
$totalElements += $totalMetrics;
$totalElements += $totalMetrics;

// As far as we are concerned, path will only be set in the openclover output of PHPUnit.
// We like having the full path, so in this case, set name to path.
Expand All @@ -245,8 +254,8 @@

$fileMetrics[(string) $rawMetric['name']] = [
'coveredMetrics' => $coveredMetrics,
'totalMetrics' => $totalMetrics,
'percentage' => $coveragePercentage,
'totalMetrics' => $totalMetrics,
'percentage' => $coveragePercentage,
];
}

Expand All @@ -259,7 +268,7 @@
$totalCoverage = (float) ($totalElementsCovered / $totalElements) * 100.0;

return [
'fileMetrics' => $fileMetrics,
'fileMetrics' => $fileMetrics,
'totalCoverage' => $totalCoverage,
];
}
Expand Down Expand Up @@ -289,15 +298,8 @@
return $this;
}

/**
* @throws ThresholdOutOfBoundsException If the threshold is less than 1 or greater than 100.
*/
public function setThreshold(int $threshold): CoverageCheck
public function setThreshold(Threshold $threshold): CoverageCheck
{
if (!Utils::validateThreshold($threshold)) {
throw ThresholdOutOfBoundsException::create($threshold);
}

$this->threshold = $threshold;

return $this;
Expand All @@ -306,14 +308,14 @@
/**
* Loads the clover xml data and runs an XML Xpath query.
*
* @internal
*
* @param self::XPATH_* $xpath
*
* @return null|array<SimpleXMLElement>|false
* @throws RuntimeException If file_get_contents fails or if XML data cannot be parsed, or
* if the given file does not appear to be a valid clover file.
*
* @return null|array<SimpleXMLElement>|false
* @internal
*
*/
private function loadMetrics(string $xpath = self::XPATH_METRICS): null|array|false
{
Expand Down
Loading
Loading