Skip to content

Commit 44e0b3b

Browse files
Even more experimentation with how branch and path coverage is presented
1 parent 8448c89 commit 44e0b3b

4 files changed

Lines changed: 82 additions & 26 deletions

File tree

src/Report/Html/Renderer/File.php

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,8 @@ private function renderSourceWithBranchCoverage(FileNode $node): string
636636
$testData = $node->testData();
637637
$codeLines = $this->loadFile($node->pathAsString());
638638

639-
$lineData = [];
640-
$branchStartData = [];
639+
$lineData = [];
640+
$decisionPointData = [];
641641

642642
foreach (array_keys($codeLines) as $line) {
643643
$lineData[$line + 1] = [
@@ -650,18 +650,22 @@ private function renderSourceWithBranchCoverage(FileNode $node): string
650650
/** @var ProcessedFunctionCoverageData $method */
651651
foreach ($functionCoverageData as $method) {
652652
/** @var ProcessedBranchCoverageData $branch */
653-
foreach ($method->branches as $branch) {
654-
$startLine = min($branch->line_start, $branch->line_end);
653+
foreach ($method->branches as $branchId => $branch) {
654+
if (count($branch->out) > 1) {
655+
$decisionLine = max($branch->line_start, $branch->line_end);
655656

656-
if (isset($lineData[$startLine])) {
657-
if (!isset($branchStartData[$startLine])) {
658-
$branchStartData[$startLine] = ['total' => 0, 'hit' => 0];
659-
}
657+
if (isset($lineData[$decisionLine]) && !isset($decisionPointData[$decisionLine])) {
658+
$targets = [];
660659

661-
$branchStartData[$startLine]['total']++;
660+
foreach ($branch->out as $targetBranchId) {
661+
if (isset($method->branches[$targetBranchId])) {
662+
$targets[] = $method->branches[$targetBranchId]->hit !== [];
663+
}
664+
}
662665

663-
if ($branch->hit !== []) {
664-
$branchStartData[$startLine]['hit']++;
666+
if (count($targets) > 1) {
667+
$decisionPointData[$decisionLine] = $targets;
668+
}
665669
}
666670
}
667671

@@ -700,8 +704,16 @@ private function renderSourceWithBranchCoverage(FileNode $node): string
700704
$lineCss = 'warning';
701705
}
702706

703-
if (isset($branchStartData[$i]) && $branchStartData[$i]['total'] > 1) {
704-
$coverageCount = $branchStartData[$i]['hit'] . '/' . $branchStartData[$i]['total'];
707+
if (isset($decisionPointData[$i])) {
708+
$markers = '';
709+
710+
foreach ($decisionPointData[$i] as $isHit) {
711+
$markers .= $isHit
712+
? '<span class="branch-hit">&bull;</span>'
713+
: '<span class="branch-miss">&bull;</span>';
714+
}
715+
716+
$coverageCount = $markers;
705717
}
706718

707719
$popoverContent = '<ul>';
@@ -846,7 +858,23 @@ private function renderBranchStructure(FileNode $node): string
846858
continue;
847859
}
848860

849-
$branches .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
861+
$branchCount = count($methodData->branches);
862+
$hitBranchCount = 0;
863+
864+
foreach ($methodData->branches as $branch) {
865+
if ($branch->hit !== []) {
866+
$hitBranchCount++;
867+
}
868+
}
869+
870+
$badge = sprintf(
871+
' <span class="%s">%d/%d</span>',
872+
$hitBranchCount === $branchCount ? 'success' : ($hitBranchCount === 0 ? 'danger' : 'warning'),
873+
$hitBranchCount,
874+
$branchCount,
875+
);
876+
877+
$branches .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a>' . $badge . '</h5>' . "\n";
850878
$branches .= '<table class="table table-bordered table-sm structure-table">' . "\n";
851879
$branches .= '<thead><tr><th>#</th><th>Lines</th><th>Status</th><th>Tests</th></tr></thead>' . "\n";
852880
$branches .= '<tbody>' . "\n";
@@ -923,14 +951,28 @@ private function renderPathStructure(FileNode $node): string
923951
continue;
924952
}
925953

926-
if (count($methodData->paths) > 100) {
927-
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
928-
$paths .= '<p>' . count($methodData->paths) . ' is too many paths to sensibly render, consider refactoring your code to bring this number down.</p>';
954+
$pathCount = count($methodData->paths);
955+
$hitPathCount = 0;
929956

930-
continue;
957+
foreach ($methodData->paths as $path) {
958+
if ($path->hit !== []) {
959+
$hitPathCount++;
960+
}
961+
}
962+
963+
$badge = sprintf(
964+
' <span class="%s">%d/%d</span>',
965+
$hitPathCount === $pathCount ? 'success' : ($hitPathCount === 0 ? 'danger' : 'warning'),
966+
$hitPathCount,
967+
$pathCount,
968+
);
969+
970+
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a>' . $badge . '</h5>' . "\n";
971+
972+
if ($pathCount > 100) {
973+
$paths .= '<details><summary>' . $pathCount . ' paths &mdash; click to expand</summary>' . "\n";
931974
}
932975

933-
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
934976
$paths .= '<table class="table table-bordered table-sm structure-table">' . "\n";
935977
$paths .= '<thead><tr><th>#</th><th>Branches</th><th>Status</th><th>Tests</th></tr></thead>' . "\n";
936978
$paths .= '<tbody>' . "\n";
@@ -988,6 +1030,10 @@ private function renderPathStructure(FileNode $node): string
9881030
}
9891031

9901032
$paths .= '</tbody></table>' . "\n";
1033+
1034+
if ($pathCount > 100) {
1035+
$paths .= '</details>' . "\n";
1036+
}
9911037
}
9921038

9931039
$pathsTemplate->setVar(['paths' => $paths]);

src/Report/Html/Renderer/Template/css/style.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,16 @@ td.col-0 {
185185
display: none;
186186
}
187187

188+
.branch-hit {
189+
color: var(--phpunit-success-bar);
190+
font-weight: bold;
191+
}
192+
193+
.branch-miss {
194+
color: var(--phpunit-danger-bar);
195+
font-weight: bold;
196+
}
197+
188198
td span.comment {
189199
color: var(--bs-secondary-color);
190200
}

tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_branch.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@
166166
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 12. These are covering 0 out of the 1 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="12" href="#12">12</a></td><td class="coverage-count"></td><td class="col codeLine"><span class="keyword">function</span><span class="default">&nbsp;</span><span class="default">&amp;</span><span class="default">foo</span><span class="keyword">(</span><span class="default">$bar</span><span class="keyword">)</span></td></tr>
167167
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 13. These are covering 0 out of the 1 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="13" href="#13">13</a></td><td class="coverage-count"></td><td class="col codeLine"><span class="keyword">{</span></td></tr>
168168
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 14. These are covering 0 out of the 2 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="14" href="#14">14</a></td><td class="coverage-count"></td><td class="col codeLine"><span class="default">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="default">$baz</span><span class="default">&nbsp;</span><span class="keyword">=</span><span class="default">&nbsp;</span><span class="keyword">function</span><span class="default">&nbsp;</span><span class="keyword">(</span><span class="keyword">)</span><span class="default">&nbsp;</span><span class="keyword">{</span><span class="keyword">}</span><span class="keyword">;</span></td></tr>
169-
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 15. These are covering 0 out of the 4 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="15" href="#15">15</a></td><td class="coverage-count">0/3</td><td class="col codeLine"><span class="default">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="default">$a</span><span class="default">&nbsp;&nbsp;&nbsp;</span><span class="keyword">=</span><span class="default">&nbsp;</span><span class="default">true</span><span class="default">&nbsp;</span><span class="keyword">?</span><span class="default">&nbsp;</span><span class="default">true</span><span class="default">&nbsp;</span><span class="keyword">:</span><span class="default">&nbsp;</span><span class="default">false</span><span class="keyword">;</span></td></tr>
169+
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 15. These are covering 0 out of the 4 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="15" href="#15">15</a></td><td class="coverage-count"><span class="branch-miss">&bull;</span><span class="branch-miss">&bull;</span></td><td class="col codeLine"><span class="default">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="default">$a</span><span class="default">&nbsp;&nbsp;&nbsp;</span><span class="keyword">=</span><span class="default">&nbsp;</span><span class="default">true</span><span class="default">&nbsp;</span><span class="keyword">?</span><span class="default">&nbsp;</span><span class="default">true</span><span class="default">&nbsp;</span><span class="keyword">:</span><span class="default">&nbsp;</span><span class="default">false</span><span class="keyword">;</span></td></tr>
170170
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 16. These are covering 0 out of the 1 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="16" href="#16">16</a></td><td class="coverage-count"></td><td class="col codeLine"><span class="default">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="default">$b</span><span class="default">&nbsp;&nbsp;&nbsp;</span><span class="keyword">=</span><span class="default">&nbsp;</span><span class="string">&quot;</span><span class="string">{</span><span class="string">$a</span><span class="keyword">}</span><span class="string">&quot;</span><span class="keyword">;</span></td></tr>
171171
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 17. These are covering 0 out of the 1 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="17" href="#17">17</a></td><td class="coverage-count"></td><td class="col codeLine"><span class="default">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="default">$c</span><span class="default">&nbsp;&nbsp;&nbsp;</span><span class="keyword">=</span><span class="default">&nbsp;</span><span class="string">&quot;</span><span class="string">${</span><span class="string">b</span><span class="keyword">}</span><span class="string">&quot;</span><span class="keyword">;</span></td></tr>
172172
<tr class="danger popin d-flex"><td data-bs-title="0 tests cover line 18. These are covering 0 out of the 1 code branches." data-bs-content="&lt;ul&gt;&lt;/ul&gt;" data-bs-placement="top" data-bs-html="true" class="col-1 text-end"><a id="18" href="#18">18</a></td><td class="coverage-count"></td><td class="col codeLine"><span class="keyword">}</span></td></tr>
@@ -182,7 +182,7 @@ <h4>Branches</h4>
182182
Please also be aware that some branches may be implicit rather than explicit, e.g. an <code>if</code> statement
183183
<i>always</i> has an <code>else</code> as part of its logical flow even if you didn't write one.
184184
</p>
185-
<h5 class="structure-heading"><a name="foo">foo</a></h5>
185+
<h5 class="structure-heading"><a name="foo">foo</a> <span class="danger">0/4</span></h5>
186186
<table class="table table-bordered table-sm structure-table">
187187
<thead><tr><th>#</th><th>Lines</th><th>Status</th><th>Tests</th></tr></thead>
188188
<tbody>
@@ -191,13 +191,13 @@ <h5 class="structure-heading"><a name="foo">foo</a></h5>
191191
<tr class="danger"><td>3</td><td><a href="#15">L15</a></td><td>Not covered</td><td>&mdash;</td></tr>
192192
<tr class="danger"><td>4</td><td><a href="#15">L15</a>&ndash;<a href="#18">L18</a></td><td>Not covered</td><td>&mdash;</td></tr>
193193
</tbody></table>
194-
<h5 class="structure-heading"><a name="{closure:%se.php:14-14}">{closure:%se.php:14-14}</a></h5>
194+
<h5 class="structure-heading"><a name="{closure:%se.php:14-14}">{closure:%se.php:14-14}</a> <span class="danger">0/1</span></h5>
195195
<table class="table table-bordered table-sm structure-table">
196196
<thead><tr><th>#</th><th>Lines</th><th>Status</th><th>Tests</th></tr></thead>
197197
<tbody>
198198
<tr class="danger"><td>1</td><td><a href="#14">L14</a></td><td>Not covered</td><td>&mdash;</td></tr>
199199
</tbody></table>
200-
<h5 class="structure-heading"><a name="{main}">{main}</a></h5>
200+
<h5 class="structure-heading"><a name="{main}">{main}</a> <span class="success">1/1</span></h5>
201201
<table class="table table-bordered table-sm structure-table">
202202
<thead><tr><th>#</th><th>Lines</th><th>Status</th><th>Tests</th></tr></thead>
203203
<tbody>

tests/_files/Report/HTML/PathCoverageForSourceWithoutNamespace/source_without_namespace.php_path.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,20 @@ <h4>Paths</h4>
182182
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an <code>if</code> statement
183183
<i>always</i> has an <code>else</code> as part of its logical flow even if you didn't write one.
184184
</p>
185-
<h5 class="structure-heading"><a name="foo">foo</a></h5>
185+
<h5 class="structure-heading"><a name="foo">foo</a> <span class="danger">0/2</span></h5>
186186
<table class="table table-bordered table-sm structure-table">
187187
<thead><tr><th>#</th><th>Branches</th><th>Status</th><th>Tests</th></tr></thead>
188188
<tbody>
189189
<tr class="danger"><td>1</td><td><a href="#12">L12</a> &rarr; <a href="#15">L15</a> &rarr; <a href="#15">L15</a></td><td>Not covered</td><td>&mdash;</td></tr>
190190
<tr class="danger"><td>2</td><td><a href="#12">L12</a> &rarr; <a href="#15">L15</a> &rarr; <a href="#15">L15</a></td><td>Not covered</td><td>&mdash;</td></tr>
191191
</tbody></table>
192-
<h5 class="structure-heading"><a name="{closure:%se.php:14-14}">{closure:%se.php:14-14}</a></h5>
192+
<h5 class="structure-heading"><a name="{closure:%se.php:14-14}">{closure:%se.php:14-14}</a> <span class="danger">0/1</span></h5>
193193
<table class="table table-bordered table-sm structure-table">
194194
<thead><tr><th>#</th><th>Branches</th><th>Status</th><th>Tests</th></tr></thead>
195195
<tbody>
196196
<tr class="danger"><td>1</td><td><a href="#14">L14</a></td><td>Not covered</td><td>&mdash;</td></tr>
197197
</tbody></table>
198-
<h5 class="structure-heading"><a name="{main}">{main}</a></h5>
198+
<h5 class="structure-heading"><a name="{main}">{main}</a> <span class="success">1/1</span></h5>
199199
<table class="table table-bordered table-sm structure-table">
200200
<thead><tr><th>#</th><th>Branches</th><th>Status</th><th>Tests</th></tr></thead>
201201
<tbody>

0 commit comments

Comments
 (0)