Skip to content

Commit 72af151

Browse files
Implement Facade::summary() to get a high-level summary
1 parent c8ab0fa commit 72af151

File tree

7 files changed

+279
-45
lines changed

7 files changed

+279
-45
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
name: CI
88

99
env:
10-
COMPOSER_ROOT_VERSION: 14.0.x-dev
10+
COMPOSER_ROOT_VERSION: 14.1.x-dev
1111
PHP_VERSION: 8.4
1212

1313
jobs:

ChangeLog-14.0.md

Lines changed: 0 additions & 42 deletions
This file was deleted.

ChangeLog-14.1.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# ChangeLog
2+
3+
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
4+
5+
## [14.1.0] - 2026-MM-DD
6+
7+
### Added
8+
9+
* `SebastianBergmann\CodeCoverage\Report\Facade::summary()` method that returns a value object that provides the number of executable lines, the number of executed lines, and line coverage in percent (as well as the respective numbers for branches and paths when available)
10+
11+
### Changed
12+
13+
* The XML document of the code coverage report in Cobertura XML format no longer has the `<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">` line at the beginning. No document exists at this URL any more, referencing remote DTD URLs is problematic, and no common consumer of Cobertura XML relies on this line.
14+
15+
### Fixed
16+
17+
* [#1147](https://github.com/sebastianbergmann/php-code-coverage/pull/1147): `CoversClass` does not transitively target traits used by enumerations
18+
19+
[14.1.0]: https://github.com/sebastianbergmann/php-code-coverage/compare/14.0.0...main

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"nikic/php-parser": "^5.7.0",
3737
"phpunit/php-text-template": "^6.0",
3838
"sebastian/complexity": "^6.0",
39-
"sebastian/environment": "^9.1",
39+
"sebastian/environment": "^9.2",
4040
"sebastian/git-state": "^1.0",
4141
"sebastian/lines-of-code": "^5.0",
4242
"sebastian/version": "^7.0",
@@ -61,7 +61,7 @@
6161
},
6262
"extra": {
6363
"branch-alias": {
64-
"dev-main": "14.0.x-dev"
64+
"dev-main": "14.1.x-dev"
6565
}
6666
}
6767
}

src/Report/Facade.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ public function renderCrap4j(string $target, int $threshold = 30, ?string $name
141141
);
142142
}
143143

144+
public function summary(): Summary
145+
{
146+
return new Summary(
147+
$this->report->numberOfExecutableLines(),
148+
$this->report->numberOfExecutedLines(),
149+
$this->report->numberOfExecutableBranches(),
150+
$this->report->numberOfExecutedBranches(),
151+
$this->report->numberOfExecutablePaths(),
152+
$this->report->numberOfExecutedPaths(),
153+
);
154+
}
155+
144156
/**
145157
* @param ?non-empty-string $target
146158
*/

src/Report/Summary.php

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of phpunit/php-code-coverage.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace SebastianBergmann\CodeCoverage\Report;
11+
12+
use function sprintf;
13+
14+
/**
15+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for phpunit/php-code-coverage
16+
*/
17+
final readonly class Summary
18+
{
19+
/**
20+
* @var non-negative-int
21+
*/
22+
private int $numberOfExecutableLines;
23+
24+
/**
25+
* @var non-negative-int
26+
*/
27+
private int $numberOfExecutedLines;
28+
29+
/**
30+
* @var non-negative-int
31+
*/
32+
private int $numberOfExecutableBranches;
33+
34+
/**
35+
* @var non-negative-int
36+
*/
37+
private int $numberOfExecutedBranches;
38+
39+
/**
40+
* @var non-negative-int
41+
*/
42+
private int $numberOfExecutablePaths;
43+
44+
/**
45+
* @var non-negative-int
46+
*/
47+
private int $numberOfExecutedPaths;
48+
49+
/**
50+
* @param non-negative-int $numberOfExecutableLines
51+
* @param non-negative-int $numberOfExecutedLines
52+
* @param non-negative-int $numberOfExecutableBranches
53+
* @param non-negative-int $numberOfExecutedBranches
54+
* @param non-negative-int $numberOfExecutablePaths
55+
* @param non-negative-int $numberOfExecutedPaths
56+
*/
57+
public function __construct(int $numberOfExecutableLines, int $numberOfExecutedLines, int $numberOfExecutableBranches, int $numberOfExecutedBranches, int $numberOfExecutablePaths, int $numberOfExecutedPaths)
58+
{
59+
$this->numberOfExecutableLines = $numberOfExecutableLines;
60+
$this->numberOfExecutedLines = $numberOfExecutedLines;
61+
$this->numberOfExecutableBranches = $numberOfExecutableBranches;
62+
$this->numberOfExecutedBranches = $numberOfExecutedBranches;
63+
$this->numberOfExecutablePaths = $numberOfExecutablePaths;
64+
$this->numberOfExecutedPaths = $numberOfExecutedPaths;
65+
}
66+
67+
/**
68+
* @return non-negative-int
69+
*/
70+
public function numberOfExecutableLines(): int
71+
{
72+
return $this->numberOfExecutableLines;
73+
}
74+
75+
/**
76+
* @return non-negative-int
77+
*/
78+
public function numberOfExecutedLines(): int
79+
{
80+
return $this->numberOfExecutedLines;
81+
}
82+
83+
public function lineCoverageAsPercentage(): float
84+
{
85+
return $this->percentage($this->numberOfExecutedLines, $this->numberOfExecutableLines);
86+
}
87+
88+
public function hasBranchAndPathCoverage(): bool
89+
{
90+
return $this->numberOfExecutableBranches > 0;
91+
}
92+
93+
/**
94+
* @return non-negative-int
95+
*/
96+
public function numberOfExecutableBranches(): int
97+
{
98+
return $this->numberOfExecutableBranches;
99+
}
100+
101+
/**
102+
* @return non-negative-int
103+
*/
104+
public function numberOfExecutedBranches(): int
105+
{
106+
return $this->numberOfExecutedBranches;
107+
}
108+
109+
public function branchCoverageAsPercentage(): float
110+
{
111+
return $this->percentage($this->numberOfExecutedBranches, $this->numberOfExecutableBranches);
112+
}
113+
114+
/**
115+
* @return non-negative-int
116+
*/
117+
public function numberOfExecutablePaths(): int
118+
{
119+
return $this->numberOfExecutablePaths;
120+
}
121+
122+
/**
123+
* @return non-negative-int
124+
*/
125+
public function numberOfExecutedPaths(): int
126+
{
127+
return $this->numberOfExecutedPaths;
128+
}
129+
130+
public function pathCoverageAsPercentage(): float
131+
{
132+
return $this->percentage($this->numberOfExecutedPaths, $this->numberOfExecutablePaths);
133+
}
134+
135+
/**
136+
* @return non-empty-string
137+
*/
138+
public function asString(): string
139+
{
140+
$buffer = sprintf(
141+
'Lines: %01.2F%% (%d/%d)',
142+
$this->lineCoverageAsPercentage(),
143+
$this->numberOfExecutedLines,
144+
$this->numberOfExecutableLines,
145+
);
146+
147+
if ($this->hasBranchAndPathCoverage()) {
148+
$buffer .= sprintf(
149+
', Branches: %01.2F%% (%d/%d)',
150+
$this->branchCoverageAsPercentage(),
151+
$this->numberOfExecutedBranches,
152+
$this->numberOfExecutableBranches,
153+
);
154+
155+
$buffer .= sprintf(
156+
', Paths: %01.2F%% (%d/%d)',
157+
$this->pathCoverageAsPercentage(),
158+
$this->numberOfExecutedPaths,
159+
$this->numberOfExecutablePaths,
160+
);
161+
}
162+
163+
return $buffer;
164+
}
165+
166+
/**
167+
* @param non-negative-int $fraction
168+
* @param non-negative-int $total
169+
*/
170+
private function percentage(int $fraction, int $total): float
171+
{
172+
if ($total > 0) {
173+
return ($fraction / $total) * 100;
174+
}
175+
176+
return 100.0;
177+
}
178+
}

tests/tests/Report/SummaryTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of phpunit/php-code-coverage.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace SebastianBergmann\CodeCoverage\Report;
11+
12+
use PHPUnit\Framework\Attributes\CoversClass;
13+
use SebastianBergmann\CodeCoverage\TestCase;
14+
15+
#[CoversClass(Summary::class)]
16+
final class SummaryTest extends TestCase
17+
{
18+
public function testLineCoverageForBankAccountTest(): void
19+
{
20+
$report = $this->getLineCoverageForBankAccount()->getReport();
21+
$summary = new Summary(
22+
$report->numberOfExecutableLines(),
23+
$report->numberOfExecutedLines(),
24+
$report->numberOfExecutableBranches(),
25+
$report->numberOfExecutedBranches(),
26+
$report->numberOfExecutablePaths(),
27+
$report->numberOfExecutedPaths(),
28+
);
29+
30+
$this->assertSame(8, $summary->numberOfExecutableLines());
31+
$this->assertSame(5, $summary->numberOfExecutedLines());
32+
$this->assertEqualsWithDelta(62.50, $summary->lineCoverageAsPercentage(), 0.01);
33+
$this->assertFalse($summary->hasBranchAndPathCoverage());
34+
$this->assertSame(
35+
'Lines: 62.50% (5/8)',
36+
$summary->asString(),
37+
);
38+
}
39+
40+
public function testPathCoverageForBankAccountTest(): void
41+
{
42+
$report = $this->getPathCoverageForBankAccount()->getReport();
43+
$summary = new Summary(
44+
$report->numberOfExecutableLines(),
45+
$report->numberOfExecutedLines(),
46+
$report->numberOfExecutableBranches(),
47+
$report->numberOfExecutedBranches(),
48+
$report->numberOfExecutablePaths(),
49+
$report->numberOfExecutedPaths(),
50+
);
51+
52+
$this->assertSame(8, $summary->numberOfExecutableLines());
53+
$this->assertSame(5, $summary->numberOfExecutedLines());
54+
$this->assertEqualsWithDelta(62.50, $summary->lineCoverageAsPercentage(), 0.01);
55+
$this->assertTrue($summary->hasBranchAndPathCoverage());
56+
$this->assertSame(7, $summary->numberOfExecutableBranches());
57+
$this->assertSame(3, $summary->numberOfExecutedBranches());
58+
$this->assertEqualsWithDelta(42.86, $summary->branchCoverageAsPercentage(), 0.01);
59+
$this->assertSame(5, $summary->numberOfExecutablePaths());
60+
$this->assertSame(3, $summary->numberOfExecutedPaths());
61+
$this->assertEqualsWithDelta(60.00, $summary->pathCoverageAsPercentage(), 0.01);
62+
$this->assertSame(
63+
'Lines: 62.50% (5/8), Branches: 42.86% (3/7), Paths: 60.00% (3/5)',
64+
$summary->asString(),
65+
);
66+
}
67+
}

0 commit comments

Comments
 (0)