Skip to content

Commit b53fd1e

Browse files
committed
fix(coverage): write coverage file even when tests fail
1 parent df9abcc commit b53fd1e

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

crates/forge/src/cmd/coverage.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ impl CoverageArgs {
250250
let filter = self.test.filter(&config)?;
251251
let outcome =
252252
self.test.run_tests(project_root, config, evm_opts, output, &filter, true).await?;
253-
outcome.ensure_ok(false)?;
254253

255254
let known_contracts = outcome.runner.as_ref().unwrap().known_contracts.clone();
256255

@@ -300,6 +299,10 @@ impl CoverageArgs {
300299
// Output final reports.
301300
self.report(&report)?;
302301

302+
// Check for test failures after generating coverage report.
303+
// This ensures coverage data is written even when tests fail.
304+
outcome.ensure_ok(false)?;
305+
303306
Ok(())
304307
}
305308

crates/forge/tests/cli/coverage.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,3 +2203,66 @@ contract CounterTest is DSTest {
22032203
...
22042204
"#]]);
22052205
});
2206+
2207+
// Test that coverage files are written even when tests fail.
2208+
forgetest!(coverage_with_failing_tests, |prj, cmd| {
2209+
prj.insert_ds_test();
2210+
prj.add_source(
2211+
"Counter.sol",
2212+
r#"
2213+
contract Counter {
2214+
uint256 public number;
2215+
2216+
function setNumber(uint256 newNumber) public {
2217+
number = newNumber;
2218+
}
2219+
2220+
function increment() public {
2221+
number++;
2222+
}
2223+
}
2224+
"#,
2225+
);
2226+
2227+
prj.add_source(
2228+
"CounterTest.sol",
2229+
r#"
2230+
import "./test.sol";
2231+
import {Counter} from "./Counter.sol";
2232+
2233+
contract CounterTest is DSTest {
2234+
Counter public counter;
2235+
2236+
function setUp() public {
2237+
counter = new Counter();
2238+
counter.setNumber(0);
2239+
}
2240+
2241+
function test_Increment() public {
2242+
counter.increment();
2243+
assertEq(counter.number(), 1);
2244+
}
2245+
2246+
function test_FailingTest() public {
2247+
counter.increment();
2248+
// This assertion will fail
2249+
assertEq(counter.number(), 999);
2250+
}
2251+
}
2252+
"#,
2253+
);
2254+
2255+
// Run coverage - this should exit with error code 1 due to failing test,
2256+
// but the lcov file should still be written.
2257+
cmd.arg("coverage").args(["--report=lcov"]).assert_failure();
2258+
2259+
// Verify that the lcov.info file was created despite test failure
2260+
let lcov = prj.root().join("lcov.info");
2261+
assert!(lcov.exists(), "lcov.info should be created even when tests fail");
2262+
2263+
// Verify the coverage data is valid and includes the counter contract
2264+
let lcov_content = std::fs::read_to_string(&lcov).unwrap();
2265+
assert!(lcov_content.contains("SF:src/Counter.sol"), "Coverage should include Counter.sol");
2266+
assert!(lcov_content.contains("FN:"), "Coverage should include function data");
2267+
assert!(lcov_content.contains("DA:"), "Coverage should include line hit data");
2268+
});

0 commit comments

Comments
 (0)