Skip to content

add the support of multi coverage file for the baseline command + rai… #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 21, 2025
Merged
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ Note: if no `--reportGitlab`, `--reportCheckstyle` or `--reportText` is set, it
The third required argument and `--report` has been removed, and should be replaced by:
`--reportGitlab=<file>`, `--reportCheckstyle=<file>` or `--reportText=<file>`

# Migrating from 2 to 3
The baseline now also supports multiple coverage files, as the inspect command.
An error is also raised if the baseline custom entry doesn't match the coverage.

## About us

At 123inkt (Part of Digital Revolution B.V.), every day more than 50 development professionals are working on improving our internal ERP
Expand Down
31 changes: 22 additions & 9 deletions src/Command/BaselineCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use RuntimeException;
use SplFileInfo;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
Expand All @@ -25,26 +26,34 @@ protected function configure(): void
{
$this->setName("baseline")
->setDescription("Generate phpfci.xml based on a given coverage.xml")
->addArgument('coverage', InputOption::VALUE_REQUIRED, 'Path to phpunit\'s coverage.xml')
->addArgument('config', InputOption::VALUE_REQUIRED, 'Path to write the configuration file')
->addArgument('coverage', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Path to phpunit\'s coverage.xml')
->addOption('config', '', InputOption::VALUE_REQUIRED, 'Path to write the configuration file')
->addOption('threshold', '', InputOption::VALUE_REQUIRED, 'Minimum coverage threshold, defaults to 100', 100)
->addOption('baseDir', '', InputOption::VALUE_REQUIRED, 'Base directory from where to determine the relative config paths');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$configArgument = $input->getArgument('config');
$configArgument = $input->getOption('config');
if (is_array($configArgument)) {
if (count($configArgument) === 0) {
throw new RuntimeException('Missing config argument');
}
$configArgument = reset($configArgument);
}

$outputPath = new SplFileInfo((string)$configArgument);
$baseDir = $input->getOption('baseDir') ?? $outputPath->getPath();
$threshold = $input->getOption('threshold');
$coverageFilePath = FileUtil::getExistingFile($input->getArgument('coverage'));
$outputPath = new SplFileInfo((string)$configArgument);
$baseDir = $input->getOption('baseDir') ?? $outputPath->getPath();
$threshold = $input->getOption('threshold');
$coveragesFilepath = [];
$coverageArgument = $input->getArgument('coverage');
if (is_array($coverageArgument) === false) {
$output->writeln('Coverage argument should be an array');
return Command::FAILURE;
}
foreach ($coverageArgument as $coverageFilepath) {
$coveragesFilepath[] = FileUtil::getExistingFile($coverageFilepath);
}

if (is_string($baseDir) === false) {
$output->writeln("--baseDir argument is not valid. Expecting string argument");
Expand All @@ -59,8 +68,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

// default to 100% coverage
$config = new InspectionConfig($baseDir, (int)$threshold, false);
$metrics = MetricsFactory::getFileMetrics(DOMDocumentFactory::getDOMDocument($coverageFilePath));
$config = new InspectionConfig($baseDir, (int)$threshold, false);
$domDocuments = [];
foreach ($coveragesFilepath as $coverageFilepath) {
$domDocuments[] = DOMDocumentFactory::getDOMDocument($coverageFilepath);
}
$metrics = MetricsFactory::getFilesMetrics($domDocuments);

// analyzer
$failures = (new MetricsAnalyzer($metrics, $config))->analyze();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ public function inspect(?PathInspectionConfig $fileConfig, FileMetric $metric):
}

$globalCoverage = $this->config->getMinimumCoverage();
$customCoverage = $fileConfig->getMinimumCoverage();

// custom coverage is lower than global coverage, and file is above global coverage
if ($customCoverage <= $globalCoverage && $metric->getCoverage() >= $globalCoverage) {
if ($metric->getCoverage() >= $globalCoverage) {
return new Failure($metric, $fileConfig->getMinimumCoverage(), Failure::UNNECESSARY_CUSTOM_COVERAGE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@
/**
* File coverage is below custom coverage
*/
class BelowCustomCoverageInspection extends AbstractInspection
class DifferentCustomCoverageInspection extends AbstractInspection
{
public function inspect(?PathInspectionConfig $fileConfig, FileMetric $metric): ?Failure
{
if ($fileConfig !== null && $metric->getCoverage() < $fileConfig->getMinimumCoverage()) {
if ($fileConfig !== null && (int)floor($metric->getCoverage()) < $fileConfig->getMinimumCoverage()) {
return new Failure($metric, $fileConfig->getMinimumCoverage(), Failure::CUSTOM_COVERAGE_TOO_LOW);
}

if ($fileConfig !== null && (int)floor($metric->getCoverage()) > $fileConfig->getMinimumCoverage()) {
return new Failure($metric, $fileConfig->getMinimumCoverage(), Failure::CUSTOM_COVERAGE_TOO_HIGH);
}

return null;
}
}
8 changes: 4 additions & 4 deletions src/Lib/Metrics/MetricsAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace DigitalRevolution\CodeCoverageInspection\Lib\Metrics;

use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\AbstractInspection;
use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\BelowCustomCoverageInspection;
use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\DifferentCustomCoverageInspection;
use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\BelowGlobalCoverageInspection;
use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\CustomCoverageAboveGlobalInspection;
use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\UncoveredMethodsInspection;
Expand All @@ -31,10 +31,10 @@ public function __construct(array $metrics, InspectionConfig $config)
$this->config = $config;

$this->inspections = [
new BelowCustomCoverageInspection($config),
new CustomCoverageAboveGlobalInspection($config),
new DifferentCustomCoverageInspection($config),
new BelowGlobalCoverageInspection($config),
new UncoveredMethodsInspection($config),
new CustomCoverageAboveGlobalInspection($config)
new UncoveredMethodsInspection($config)
];
}

Expand Down
1 change: 1 addition & 0 deletions src/Model/Metric/Failure.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Failure
public const CUSTOM_COVERAGE_TOO_LOW = 2;
public const UNNECESSARY_CUSTOM_COVERAGE = 3;
public const MISSING_METHOD_COVERAGE = 4;
public const CUSTOM_COVERAGE_TOO_HIGH = 5;

private FileMetric $metric;
private int $minimumCoverage;
Expand Down
4 changes: 4 additions & 0 deletions src/Renderer/RendererHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public static function renderReason(InspectionConfig $config, Failure $failure):
case Failure::CUSTOM_COVERAGE_TOO_LOW:
$message = "Custom file coverage is configured at %s%%. Current coverage is at %s%%. Improve coverage for this class.";

return sprintf($message, (string)$failure->getMinimumCoverage(), (string)$failure->getMetric()->getCoverage());
case Failure::CUSTOM_COVERAGE_TOO_HIGH:
$message = "Custom file coverage is configured at %s%%. Current coverage is at %s%%. Edit the phpfci baseline for this class.";

return sprintf($message, (string)$failure->getMinimumCoverage(), (string)$failure->getMetric()->getCoverage());
case Failure::MISSING_METHOD_COVERAGE:
$message = "File coverage is above %s%%, but method(s) `%s` has/have no coverage at all.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ class BaselineCommandTest extends TestCase
/** @var vfsStreamDirectory */
private $fileSystem;

protected function setUp(): void
{
parent::setUp();
$this->fileSystem = vfsStream::setup('output');
}

/**
* @throws Exception
*/
Expand All @@ -39,7 +33,7 @@ public function testBaselineCommand(): void

// prepare command
$command = new BaselineCommand();
$input = new ArgvInput(['phpfci', '--baseDir', $baseDir, $coveragePath, $output]);
$input = new ArgvInput(['phpfci', $coveragePath, '--baseDir', $baseDir, '--config', $output]);
$output = new BufferedOutput();

// run test case
Expand All @@ -53,4 +47,10 @@ public function testBaselineCommand(): void

static::assertSame($expected, $result);
}

protected function setUp(): void
{
parent::setUp();
$this->fileSystem = vfsStream::setup('output');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
<error line="1" column="0" severity="error" message="Project per file coverage is configured at 80%. Current coverage is at 70%. Improve coverage for this class." source="phpunit-file-coverage-inspection"/>
</file>
<file name="/home/workspace/test/directory/standard-below-threshold.php">
<error line="1" column="0" severity="error" message="Custom file coverage is configured at 90%. Current coverage is at 80%. Improve coverage for this class." source="phpunit-file-coverage-inspection"/>
<error line="1" column="0" severity="error" message="Custom file coverage is configured at 90%. Current coverage is at 60%. Improve coverage for this class." source="phpunit-file-coverage-inspection"/>
</file>
<file name="/home/workspace/test/directory/standard-above-threshold.php">
<error line="1" column="0" severity="error" message="A custom file coverage is configured at 90%, but the current file coverage 90% exceeds the project coverage 80%. Remove `/home/workspace/test/directory/standard-above-threshold.php` from phpfci.xml custom-coverage rules." source="phpunit-file-coverage-inspection"/>
</file>
<file name="/home/workspace/test/case/below-threshold.php">
<error line="1" column="0" severity="error" message="Custom file coverage is configured at 55%. Current coverage is at 50%. Improve coverage for this class." source="phpunit-file-coverage-inspection"/>
Expand Down
6 changes: 3 additions & 3 deletions tests/Functional/Command/InspectCommand/Data/coverage.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@
conditionals="0"
coveredconditionals="0"
statements="10"
coveredstatements="8"
coveredstatements="6"
elements="0"
coveredelements="0"/>
<line num="1" type="method" name="methodName" count="1"/>
<line num="2" type="stmt" count="1"/>
<line num="2" type="stmt" count="0"/>
<line num="3" type="stmt" count="1"/>
<line num="4" type="stmt" count="1"/>
<line num="4" type="stmt" count="0"/>
<line num="5" type="stmt" count="1"/>
<line num="6" type="stmt" count="1"/>
<line num="7" type="stmt" count="1"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,4 @@ public function testInspectCoverageAboveGlobalCoverageShouldFail(): void
static::assertSame(Failure::UNNECESSARY_CUSTOM_COVERAGE, $failure->getReason());
static::assertSame(40, $failure->getMinimumCoverage());
}

public function testInspectCoverageCustomCoverageAboveGlobalCoverageShouldPass(): void
{
// global is 80
$fileConfig = new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, '/tmp/b', 85);
$metric = new FileMetric('/tmp/b/', 0, 83, [], []);

static::assertNull($this->inspection->inspect($fileConfig, $metric));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@
namespace DigitalRevolution\CodeCoverageInspection\Tests\Unit\Lib\Metrics\Inspection;

use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\AbstractInspection;
use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\BelowCustomCoverageInspection;
use DigitalRevolution\CodeCoverageInspection\Lib\Metrics\Inspection\DifferentCustomCoverageInspection;
use DigitalRevolution\CodeCoverageInspection\Model\Config\InspectionConfig;
use DigitalRevolution\CodeCoverageInspection\Model\Config\PathInspectionConfig;
use DigitalRevolution\CodeCoverageInspection\Model\Metric\Failure;
use DigitalRevolution\CodeCoverageInspection\Model\Metric\FileMetric;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;

#[CoversClass(BelowCustomCoverageInspection::class)]
#[CoversClass(DifferentCustomCoverageInspection::class)]
#[CoversClass(AbstractInspection::class)]
class BelowCustomCoverageInspectionTest extends TestCase
class DifferentCustomCoverageInspectionTest extends TestCase
{
private BelowCustomCoverageInspection $inspection;
private DifferentCustomCoverageInspection $inspection;

protected function setUp(): void
{
$config = new InspectionConfig('/tmp/', 80);
$this->inspection = new BelowCustomCoverageInspection($config);
$this->inspection = new DifferentCustomCoverageInspection($config);
}

public function testInspectNoCustomCoverageShouldPass(): void
Expand All @@ -43,10 +43,24 @@ public function testInspectCoverageBelowCustomCoverageShouldFail(): void
static::assertSame(40, $failure->getMinimumCoverage());
}

public function testInspectCoverageAboveCustomCoverageShouldPass(): void
/**
* Custom coverage 40%
*/
public function testInspectCoverageAboveCustomCoverageShouldFail(): void
{
$fileConfig = new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, '/tmp/b', 40);
$metric = new FileMetric('/tmp/a/', 0, 60, [], []);
$fileConfig = new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, '/tmp/b', 20);
$metric = new FileMetric('/tmp/a/', 0, 40, [], []);

$failure = $this->inspection->inspect($fileConfig, $metric);
static::assertNotNull($failure);
static::assertSame(Failure::CUSTOM_COVERAGE_TOO_HIGH, $failure->getReason());
static::assertSame(20, $failure->getMinimumCoverage());
}

public function testInspectCoverageCustomCoverageShouldPass(): void
{
$fileConfig = new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, '/tmp/b', 50);
$metric = new FileMetric('/tmp/a/', 0, 50.8, [], []);

static::assertNull($this->inspection->inspect($fileConfig, $metric));
}
Expand Down
6 changes: 3 additions & 3 deletions tests/Unit/Lib/Metrics/MetricsAnalyzerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function testAnalyzeFileWithCustomCoverageRuleShouldPass(): void
{
$metrics[] = new FileMetric('/a/b/c/test.php', 0, 45, [], []);
$config = new InspectionConfig('/a/', 80, false);
$config->addPathInspection(new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, 'b/c/test.php', 40));
$config->addPathInspection(new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, 'b/c/test.php', 45));

$analyzer = new MetricsAnalyzer($metrics, $config);
$result = $analyzer->analyze();
Expand All @@ -66,12 +66,12 @@ public function testAnalyzeFileWithCustomCoverageAboveGlobalCoverageShouldFail()
$metric = new FileMetric('/a/b/c/test.php', 0, 90, [], []);
$metrics = [$metric];
$config = new InspectionConfig('/a/', 80, false);
$config->addPathInspection(new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, 'b/c/test.php', 50));
$config->addPathInspection(new PathInspectionConfig(PathInspectionConfig::TYPE_FILE, 'b/c/test.php', 90));

$analyzer = new MetricsAnalyzer($metrics, $config);
$result = $analyzer->analyze();
static::assertCount(1, $result);
static::assertEquals([new Failure($metric, 50, Failure::UNNECESSARY_CUSTOM_COVERAGE)], $result);
static::assertEquals([new Failure($metric, 90, Failure::UNNECESSARY_CUSTOM_COVERAGE)], $result);
}

public function testAnalyzeFileWithUncoveredMethodsShouldFail(): void
Expand Down
22 changes: 17 additions & 5 deletions tests/Unit/Renderer/RendererHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ class RendererHelperTest extends TestCase
{
private InspectionConfig $config;

protected function setUp(): void
{
$this->config = new InspectionConfig('base-path', 80);
}

public function testRenderReasonGlobalCoverageTooLow(): void
{
$metric = new FileMetric('foobar', 0, 80, [], []);
Expand All @@ -40,6 +35,18 @@ public function testRenderReasonCustomCoverageTooLow(): void
static::assertSame('Custom file coverage is configured at 30%. Current coverage is at 70%. Improve coverage for this class.', $message);
}

public function testRenderReasonCustomCoverageTooHigh(): void
{
$metric = new FileMetric('foobar', 0, 50, [], []);
$failure = new Failure($metric, 70, Failure::CUSTOM_COVERAGE_TOO_HIGH, 5);

$message = RendererHelper::renderReason($this->config, $failure);
static::assertSame(
'Custom file coverage is configured at 70%. Current coverage is at 50%. Edit the phpfci baseline for this class.',
$message
);
}

public function testRenderReasonMissingMethodCoverage(): void
{
$metric = new FileMetric('foobar', 0, 70, [new MethodMetric('coveredMethod', 100, 80), new MethodMetric('uncoveredMethod', 105, 0)], []);
Expand Down Expand Up @@ -70,4 +77,9 @@ public function testRenderReasonShouldThrowExceptionWhenInvalid(): void
$this->expectException(RuntimeException::class);
RendererHelper::renderReason($this->config, $failure);
}

protected function setUp(): void
{
$this->config = new InspectionConfig('base-path', 80);
}
}