Skip to content

Commit db5319f

Browse files
Even more experimentation with how branch and path coverage is presented
1 parent 9fa169f commit db5319f

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
@@ -638,8 +638,8 @@ private function renderSourceWithBranchCoverage(FileNode $node): string
638638
$testData = $node->testData();
639639
$codeLines = $this->loadFile($node->pathAsString());
640640

641-
$lineData = [];
642-
$branchStartData = [];
641+
$lineData = [];
642+
$decisionPointData = [];
643643

644644
foreach (array_keys($codeLines) as $line) {
645645
$lineData[$line + 1] = [
@@ -652,18 +652,22 @@ private function renderSourceWithBranchCoverage(FileNode $node): string
652652
/** @var ProcessedFunctionCoverageData $method */
653653
foreach ($functionCoverageData as $method) {
654654
/** @var ProcessedBranchCoverageData $branch */
655-
foreach ($method->branches as $branch) {
656-
$startLine = min($branch->line_start, $branch->line_end);
655+
foreach ($method->branches as $branchId => $branch) {
656+
if (count($branch->out) > 1) {
657+
$decisionLine = max($branch->line_start, $branch->line_end);
657658

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

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

665-
if ($branch->hit !== []) {
666-
$branchStartData[$startLine]['hit']++;
668+
if (count($targets) > 1) {
669+
$decisionPointData[$decisionLine] = $targets;
670+
}
667671
}
668672
}
669673

@@ -702,8 +706,16 @@ private function renderSourceWithBranchCoverage(FileNode $node): string
702706
$lineCss = 'warning';
703707
}
704708

705-
if (isset($branchStartData[$i]) && $branchStartData[$i]['total'] > 1) {
706-
$coverageCount = $branchStartData[$i]['hit'] . '/' . $branchStartData[$i]['total'];
709+
if (isset($decisionPointData[$i])) {
710+
$markers = '';
711+
712+
foreach ($decisionPointData[$i] as $isHit) {
713+
$markers .= $isHit
714+
? '<span class="branch-hit">&bull;</span>'
715+
: '<span class="branch-miss">&bull;</span>';
716+
}
717+
718+
$coverageCount = $markers;
707719
}
708720

709721
$popoverContent = '<ul>';
@@ -848,7 +860,23 @@ private function renderBranchStructure(FileNode $node): string
848860
continue;
849861
}
850862

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

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

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

935-
$paths .= '<h5 class="structure-heading"><a name="' . htmlspecialchars($methodName, self::HTML_SPECIAL_CHARS_FLAGS) . '">' . $this->abbreviateMethodName($methodName) . '</a></h5>' . "\n";
936978
$paths .= '<table class="table table-bordered table-sm structure-table">' . "\n";
937979
$paths .= '<thead><tr><th>#</th><th>Branches</th><th>Status</th><th>Tests</th></tr></thead>' . "\n";
938980
$paths .= '<tbody>' . "\n";
@@ -990,6 +1032,10 @@ private function renderPathStructure(FileNode $node): string
9901032
}
9911033

9921034
$paths .= '</tbody></table>' . "\n";
1035+
1036+
if ($pathCount > 100) {
1037+
$paths .= '</details>' . "\n";
1038+
}
9931039
}
9941040

9951041
$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)