Skip to content

Commit a5da6c3

Browse files
committed
Enforce code coverage via coverage-guard
1 parent 8cc6f12 commit a5da6c3

File tree

7 files changed

+143
-25
lines changed

7 files changed

+143
-25
lines changed

.github/workflows/checks.yml

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,6 @@ jobs:
3333
XDEBUG_MODE: coverage
3434
run: composer check
3535

36-
-
37-
name: Output line coverage
38-
run: head cache/coverage/coverage.txt | grep Lines
39-
40-
-
41-
name: Report patch coverage
42-
if: github.event_name == 'pull_request'
43-
run: |
44-
if git diff --name-only ${{ github.event.pull_request.base.sha }} | grep -q '\.php$'; then
45-
git diff ${{ github.event.pull_request.base.sha }} > changes.patch
46-
vendor/bin/phpcov patch-coverage --path-prefix $(pwd) cache/coverage/coverage.cov changes.patch || true
47-
else
48-
echo "No PHP files changed, skipping patch coverage report"
49-
fi
50-
5136
cda:
5237
runs-on: ubuntu-latest
5338
strategy:

composer.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"phpunit/phpunit": "^9.6.22",
3434
"shipmonk/coding-standard": "^0.2.0",
3535
"shipmonk/composer-dependency-analyser": "^1.8.2",
36+
"shipmonk/coverage-guard": "dev-master",
3637
"shipmonk/name-collision-detector": "^2.1.1",
3738
"shipmonk/phpstan-dev": "^0.1.1",
3839
"shipmonk/phpstan-rules": "^4.1.0",
@@ -78,7 +79,7 @@
7879
"@check:ec",
7980
"@check:cs",
8081
"@check:types",
81-
"@check:tests",
82+
"@check:coverage",
8283
"@check:collisions",
8384
"@check:dependencies"
8485
],
@@ -87,6 +88,10 @@
8788
"composer normalize --dry-run --no-update-lock",
8889
"composer validate --strict"
8990
],
91+
"check:coverage": [
92+
"XDEBUG_MODE=coverage phpunit tests --coverage-clover cache/clover.xml",
93+
"coverage-guard check cache/clover.xml"
94+
],
9095
"check:cs": "phpcs",
9196
"check:dependencies": "composer-dependency-analyser",
9297
"check:ec": "ec src tests",

composer.lock

Lines changed: 65 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coverage-guard.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
use PHPStan\Collectors\Collector;
4+
use PHPStan\Rules\Rule;
5+
use ShipMonk\CoverageGuard\Config;
6+
use ShipMonk\CoverageGuard\Hierarchy\ClassMethodBlock;
7+
use ShipMonk\CoverageGuard\Hierarchy\CodeBlock;
8+
use ShipMonk\CoverageGuard\Rule\CoverageError;
9+
use ShipMonk\CoverageGuard\Rule\CoverageRule;
10+
use ShipMonk\CoverageGuard\Rule\InspectionContext;
11+
use ShipMonk\PHPStan\DeadCode\Compatibility\BackwardCompatibilityChecker;
12+
use ShipMonk\PHPStan\DeadCode\Provider\MemberUsageProvider;
13+
use ShipMonk\PHPStan\DeadCode\Reflection\ReflectionHelper;
14+
15+
$config = new Config();
16+
$config->addRule(new class implements CoverageRule {
17+
18+
public function inspect(
19+
CodeBlock $codeBlock,
20+
InspectionContext $context
21+
): ?CoverageError
22+
{
23+
if (!$codeBlock instanceof ClassMethodBlock) {
24+
return null;
25+
}
26+
27+
if ($codeBlock->getExecutableLinesCount() < 5) {
28+
return null;
29+
}
30+
31+
$coverage = $codeBlock->getCoveragePercentage();
32+
$classReflection = $codeBlock->getMethodReflection()->getDeclaringClass();
33+
$requiredCoverage = $this->getRequiredCoverage($classReflection);
34+
35+
if ($codeBlock->getCoveragePercentage() < $requiredCoverage) {
36+
return CoverageError::create("Method <white>{$codeBlock->getMethodName()}</white> requires $requiredCoverage% coverage, but has only $coverage%.");
37+
}
38+
39+
return null;
40+
}
41+
42+
/**
43+
* @param ReflectionClass<object> $classReflection
44+
*/
45+
private function getRequiredCoverage(ReflectionClass $classReflection): int
46+
{
47+
$isPoor = in_array($classReflection->getName(), [
48+
BackwardCompatibilityChecker::class,
49+
ReflectionHelper::class,
50+
], true);
51+
52+
$isCore = $classReflection->implementsInterface(MemberUsageProvider::class)
53+
|| $classReflection->implementsInterface(Collector::class)
54+
|| $classReflection->implementsInterface(Rule::class);
55+
56+
return match (true) {
57+
$isCore => 80,
58+
$isPoor => 20,
59+
default => 50,
60+
};
61+
}
62+
63+
});
64+
65+
return $config;

phpunit.xml.dist

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@
1515
<include>
1616
<directory suffix=".php">src</directory>
1717
</include>
18-
<report>
19-
<clover outputFile="cache/coverage/phpstorm/clover.xml"/>
20-
<cobertura outputFile="cache/coverage/ci/cobertura.xml"/>
21-
<html outputDirectory="cache/coverage/html"/>
22-
<text outputFile="cache/coverage/coverage.txt"/>
23-
<php outputFile="cache/coverage/coverage.cov"/>
24-
</report>
2518
</coverage>
2619
<php>
2720
<ini name="error_reporting" value="-1"/>

tests/Rule/data/providers/custom.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class Methods
77
const SOME_CONSTANT = 1; // error: Unused CustomProvider\Methods::SOME_CONSTANT
88

99
public function method(): void {}
10+
11+
public function mixedTestThatExcludersCanExcludeProvidedUsage(): void {} // error: Unused CustomProvider\Methods::mixedTestThatExcludersCanExcludeProvidedUsage (all usages excluded by mixedPrefix excluder)
1012
}
1113

1214
class Constants

tests/Rule/data/providers/phpbench.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public function benchWithParams(array $params): void
2828
{
2929
}
3030

31+
#[ParamProviders([1])]
32+
public function benchWithInvalidAttributeDontBreakIt(array $params): void
33+
{
34+
}
35+
3136
public function provideParams(): array
3237
{
3338
return [];

0 commit comments

Comments
 (0)