Skip to content

Commit eb51a9d

Browse files
refactor(invariant): rename to InvariantSecondaryFailure / invariant_secondary_failures and serialize sparsely
Renames TestResult.other_failures -> invariant_secondary_failures and the underlying InvariantOtherFailure struct -> InvariantSecondaryFailure. The previous names were generic ('other relative to what?'); the new names align with the existing 'primary/secondary' terminology used throughout the assert_all rollout and follow the Rust Vec<Foo>/foos plural-of-singular convention. Also marks the field with #[serde(default, skip_serializing_if = 'Vec::is_empty')] so it is omitted from JSON output for any test that has no secondary failure data — plain unit tests, fuzz tests, passing tests. Pre-PR JSON consumers continue to see the same shape on those results. invariant_failure_dir and assert_all_invariant_count already had Option::is_none guards. Updates the SimpleContractTest{NonVerbose,Verbose}.json fixtures to drop the now-skipped empty field. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de
1 parent c09d7c1 commit eb51a9d

4 files changed

Lines changed: 15 additions & 15 deletions

File tree

crates/forge/src/result.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ impl TestStatus {
413413
/// Carries everything needed to render the failure on its own (matching how the primary is
414414
/// displayed) and to point users at the persisted counterexample for re-running.
415415
#[derive(Clone, Debug, Serialize, Deserialize)]
416-
pub struct InvariantOtherFailure {
416+
pub struct InvariantSecondaryFailure {
417417
/// Invariant function name (e.g. `invariant_cond3`).
418418
pub name: String,
419419
/// Revert reason or assertion failure message.
@@ -443,7 +443,8 @@ pub struct TestResult {
443443
///
444444
/// Each entry carries the invariant's name, the failure reason, an optional counterexample,
445445
/// and the path where the counterexample has been persisted for shrinking on a subsequent run.
446-
pub other_failures: Vec<InvariantOtherFailure>,
446+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
447+
pub invariant_secondary_failures: Vec<InvariantSecondaryFailure>,
447448

448449
/// Directory where invariant failure counterexamples have been persisted (set when one or more
449450
/// secondary invariant failures were written, so users can locate persisted counterexamples).
@@ -541,7 +542,7 @@ impl fmt::Display for TestResult {
541542
let primary_broke = self.reason.is_some() || self.counterexample.is_some();
542543
let render_primary_header = primary_broke
543544
|| self.assert_all_invariant_count.is_none()
544-
|| self.other_failures.is_empty();
545+
|| self.invariant_secondary_failures.is_empty();
545546
let mut s = String::new();
546547
if render_primary_header {
547548
s.push_str("[FAIL");
@@ -576,16 +577,17 @@ impl fmt::Display for TestResult {
576577
if let Some(total) = self.assert_all_invariant_count
577578
&& total > 1
578579
{
579-
let broken = usize::from(primary_broke) + self.other_failures.len();
580+
let broken =
581+
usize::from(primary_broke) + self.invariant_secondary_failures.len();
580582
let prefix = if render_primary_header { "\n" } else { "" };
581583
writeln!(s, "{prefix}Suite assert_all: {broken}/{total} invariants broken")
582584
.unwrap();
583585
}
584-
if !self.other_failures.is_empty() {
586+
if !self.invariant_secondary_failures.is_empty() {
585587
if self.assert_all_invariant_count.is_none() {
586588
writeln!(s).unwrap();
587589
}
588-
for failure in &self.other_failures {
590+
for failure in &self.invariant_secondary_failures {
589591
// If we have a (shrunk) counterexample, render the secondary the same
590592
// way the primary is rendered: `[FAIL: reason]\n\t[Sequence] ...`.
591593
// Otherwise fall back to the terse `name: reason` one-liner so the
@@ -612,7 +614,7 @@ impl fmt::Display for TestResult {
612614
writeln!(
613615
s,
614616
"{} invariant failures persisted to {} — rerun to shrink",
615-
self.other_failures.len(),
617+
self.invariant_secondary_failures.len(),
616618
dir.display()
617619
)
618620
.unwrap();
@@ -821,7 +823,7 @@ impl TestResult {
821823
gas_report_traces: Vec<Vec<CallTraceArena>>,
822824
success: bool,
823825
reason: Option<String>,
824-
other_failures: Vec<InvariantOtherFailure>,
826+
invariant_secondary_failures: Vec<InvariantSecondaryFailure>,
825827
invariant_failure_dir: Option<std::path::PathBuf>,
826828
assert_all_invariant_count: Option<usize>,
827829
counterexample: Option<CounterExample>,
@@ -846,7 +848,7 @@ impl TestResult {
846848
TestStatus::Failure
847849
};
848850
self.reason = reason;
849-
self.other_failures = other_failures;
851+
self.invariant_secondary_failures = invariant_secondary_failures;
850852
self.invariant_failure_dir = invariant_failure_dir;
851853
self.assert_all_invariant_count = assert_all_invariant_count;
852854
self.counterexample = counterexample;

crates/forge/src/runner.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
fuzz::{BaseCounterExample, FuzzTestResult},
77
multi_runner::{TestContract, TestRunnerConfig},
88
progress::{TestsProgress, start_fuzz_progress},
9-
result::{InvariantOtherFailure, SuiteResult, TestResult, TestSetup},
9+
result::{InvariantSecondaryFailure, SuiteResult, TestResult, TestSetup},
1010
};
1111
use alloy_dyn_abi::{DynSolValue, JsonAbiExt};
1212
use alloy_json_abi::Function;
@@ -988,7 +988,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> {
988988
.errors
989989
.get(&invariant_contract.primary_invariant_fn.name)
990990
.and_then(|err| err.revert_reason());
991-
let mut other_failures = vec![];
991+
let mut invariant_secondary_failures = vec![];
992992
let mut any_secondary_persisted = false;
993993

994994
if success {
@@ -1192,7 +1192,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> {
11921192
}
11931193
}
11941194
};
1195-
other_failures.push(InvariantOtherFailure {
1195+
invariant_secondary_failures.push(InvariantSecondaryFailure {
11961196
name: invariant.name.clone(),
11971197
reason: error.revert_reason().unwrap_or_default(),
11981198
counterexample: secondary_counterexample,
@@ -1213,7 +1213,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> {
12131213
invariant_result.gas_report_traces,
12141214
success,
12151215
reason,
1216-
other_failures,
1216+
invariant_secondary_failures,
12171217
invariant_failure_dir,
12181218
assert_all_invariant_count,
12191219
counterexample,

crates/forge/tests/fixtures/SimpleContractTestNonVerbose.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"test()": {
66
"status": "Success",
77
"reason": null,
8-
"other_failures": [],
98
"counterexample": null,
109
"logs": [],
1110
"decoded_logs": [],

crates/forge/tests/fixtures/SimpleContractTestVerbose.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"test()": {
66
"status": "Success",
77
"reason": null,
8-
"other_failures": [],
98
"counterexample": null,
109
"logs": [
1110
{

0 commit comments

Comments
 (0)