Skip to content

Commit b28c2b2

Browse files
authored
fix(forge): show lcov hits for do while statements (#10423)
1 parent 96c40b6 commit b28c2b2

File tree

2 files changed

+74
-6
lines changed

2 files changed

+74
-6
lines changed

crates/forge/src/coverage.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Coverage reports.
22
3-
use alloy_primitives::map::HashMap;
3+
use alloy_primitives::map::{HashMap, HashSet};
44
use comfy_table::{modifiers::UTF8_ROUND_CORNERS, Attribute, Cell, Color, Row, Table};
55
use evm_disassembler::disassemble_bytes;
66
use foundry_common::fs;
@@ -118,6 +118,8 @@ impl CoverageReporter for LcovReporter {
118118
writeln!(out, "TN:")?;
119119
writeln!(out, "SF:{}", path.display())?;
120120

121+
let mut recorded_lines = HashSet::new();
122+
121123
for item in items {
122124
let line = item.loc.lines.start;
123125
// `lines` is half-open, so we need to subtract 1 to get the last included line.
@@ -140,8 +142,11 @@ impl CoverageReporter for LcovReporter {
140142
writeln!(out, "FNDA:{hits},{name}")?;
141143
}
142144
}
143-
CoverageItemKind::Line => {
144-
writeln!(out, "DA:{line},{hits}")?;
145+
// Add lines / statement hits only once.
146+
CoverageItemKind::Line | CoverageItemKind::Statement => {
147+
if recorded_lines.insert(line) {
148+
writeln!(out, "DA:{line},{hits}")?;
149+
}
145150
}
146151
CoverageItemKind::Branch { branch_id, path_id, .. } => {
147152
writeln!(
@@ -150,9 +155,6 @@ impl CoverageReporter for LcovReporter {
150155
if hits == 0 { "-".to_string() } else { hits.to_string() }
151156
)?;
152157
}
153-
// Statements are not in the LCOV format.
154-
// We don't add them in order to avoid doubling line hits.
155-
CoverageItemKind::Statement => {}
156158
}
157159
}
158160

crates/forge/tests/cli/coverage.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,72 @@ contract ArrayConditionTest is DSTest {
18451845
"#]]);
18461846
});
18471847

1848+
// <https://github.com/foundry-rs/foundry/issues/10422>
1849+
// Test that line hits are properly recorded in lcov report.
1850+
forgetest!(do_while_lcov, |prj, cmd| {
1851+
prj.insert_ds_test();
1852+
prj.add_source(
1853+
"Counter.sol",
1854+
r#"
1855+
contract Counter {
1856+
uint256 public number = 21;
1857+
1858+
function increment() public {
1859+
uint256 i = 0;
1860+
do {
1861+
number++;
1862+
if (number > 20) {
1863+
number -= 2;
1864+
}
1865+
} while (++i < 10);
1866+
}
1867+
}
1868+
"#,
1869+
)
1870+
.unwrap();
1871+
1872+
prj.add_source(
1873+
"Counter.t.sol",
1874+
r#"
1875+
import "./test.sol";
1876+
import "./Counter.sol";
1877+
1878+
contract CounterTest is DSTest {
1879+
function test_do_while() public {
1880+
Counter counter = new Counter();
1881+
counter.increment();
1882+
}
1883+
}
1884+
"#,
1885+
)
1886+
.unwrap();
1887+
1888+
assert_lcov(
1889+
cmd.arg("coverage"),
1890+
str![[r#"
1891+
TN:
1892+
SF:src/Counter.sol
1893+
DA:7,1
1894+
FN:7,Counter.increment
1895+
FNDA:1,Counter.increment
1896+
DA:8,1
1897+
DA:14,10
1898+
DA:10,10
1899+
DA:11,10
1900+
BRDA:11,0,0,6
1901+
DA:12,6
1902+
FNF:1
1903+
FNH:1
1904+
LF:3
1905+
LH:3
1906+
BRF:1
1907+
BRH:1
1908+
end_of_record
1909+
1910+
"#]],
1911+
);
1912+
});
1913+
18481914
#[track_caller]
18491915
fn assert_lcov(cmd: &mut TestCommand, data: impl IntoData) {
18501916
cmd.args(["--report=lcov", "--report-file"]).assert_file(data.into_data());

0 commit comments

Comments
 (0)