Skip to content

Commit

Permalink
feat(invariant): on failures show original and current sequence len (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
grandizzy authored Feb 3, 2025
1 parent 6e919af commit f6133f9
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 13 deletions.
4 changes: 2 additions & 2 deletions crates/evm/fuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ pub use inspector::Fuzzer;
pub enum CounterExample {
/// Call used as a counter example for fuzz tests.
Single(BaseCounterExample),
/// Sequence of calls used as a counter example for invariant tests.
Sequence(Vec<BaseCounterExample>),
/// Original sequence size and sequence of calls used as a counter example for invariant tests.
Sequence(usize, Vec<BaseCounterExample>),
}

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down
12 changes: 9 additions & 3 deletions crates/forge/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,14 @@ impl fmt::Display for TestResult {
CounterExample::Single(ex) => {
write!(s, "; counterexample: {ex}]").unwrap();
}
CounterExample::Sequence(sequence) => {
s.push_str("]\n\t[Sequence]\n");
CounterExample::Sequence(original, sequence) => {
s.push_str(
format!(
"]\n\t[Sequence] (original: {original}, shrunk: {})\n",
sequence.len()
)
.as_str(),
);
for ex in sequence {
writeln!(s, "\t\t{ex}").unwrap();
}
Expand Down Expand Up @@ -593,7 +599,7 @@ impl TestResult {
} else {
Some(format!("{invariant_name} persisted failure revert"))
};
self.counterexample = Some(CounterExample::Sequence(call_sequence));
self.counterexample = Some(CounterExample::Sequence(call_sequence.len(), call_sequence));
}

/// Returns the fail result for invariant test setup.
Expand Down
13 changes: 11 additions & 2 deletions crates/forge/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use foundry_evm::{
traces::{load_contracts, TraceKind, TraceMode},
};
use proptest::test_runner::{
FailurePersistence, FileFailurePersistence, RngAlgorithm, TestRng, TestRunner,
FailurePersistence, FileFailurePersistence, RngAlgorithm, TestError, TestRng, TestRunner,
};
use rayon::prelude::*;
use std::{borrow::Cow, cmp::min, collections::BTreeMap, sync::Arc, time::Instant};
Expand Down Expand Up @@ -686,7 +686,16 @@ impl<'a> FunctionRunner<'a> {
) {
error!(%err, "Failed to record call sequence");
}
counterexample = Some(CounterExample::Sequence(call_sequence))

let original_seq_len =
if let TestError::Fail(_, calls) = &case_data.test_error {
calls.len()
} else {
call_sequence.len()
};

counterexample =
Some(CounterExample::Sequence(original_seq_len, call_sequence))
}
}
Err(err) => {
Expand Down
48 changes: 42 additions & 6 deletions crates/forge/tests/it/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ async fn test_invariant_shrink() {
match get_counterexample!(runner, &filter) {
CounterExample::Single(_) => panic!("CounterExample should be a sequence."),
// `fuzz_seed` at 119 makes this sequence shrinkable from 4 to 2.
CounterExample::Sequence(sequence) => {
CounterExample::Sequence(_, sequence) => {
assert!(sequence.len() <= 3);

if sequence.len() == 2 {
Expand Down Expand Up @@ -314,7 +314,7 @@ async fn check_shrink_sequence(test_pattern: &str, expected_len: usize) {

match get_counterexample!(runner, &filter) {
CounterExample::Single(_) => panic!("CounterExample should be a sequence."),
CounterExample::Sequence(sequence) => {
CounterExample::Sequence(_, sequence) => {
assert_eq!(sequence.len(), expected_len);
}
};
Expand Down Expand Up @@ -346,7 +346,7 @@ async fn test_shrink_big_sequence() {

let initial_sequence = match initial_counterexample {
CounterExample::Single(_) => panic!("CounterExample should be a sequence."),
CounterExample::Sequence(sequence) => sequence,
CounterExample::Sequence(_, sequence) => sequence,
};
// ensure shrinks to same sequence of 77
assert_eq!(initial_sequence.len(), 77);
Expand Down Expand Up @@ -379,7 +379,7 @@ async fn test_shrink_big_sequence() {
.unwrap()
{
CounterExample::Single(_) => panic!("CounterExample should be a sequence."),
CounterExample::Sequence(sequence) => sequence,
CounterExample::Sequence(_, sequence) => sequence,
};
// ensure shrinks to same sequence of 77
assert_eq!(new_sequence.len(), 77);
Expand Down Expand Up @@ -407,7 +407,7 @@ async fn test_shrink_fail_on_revert() {

match get_counterexample!(runner, &filter) {
CounterExample::Single(_) => panic!("CounterExample should be a sequence."),
CounterExample::Sequence(sequence) => {
CounterExample::Sequence(_, sequence) => {
// ensure shrinks to sequence of 10
assert_eq!(sequence.len(), 10);
}
Expand Down Expand Up @@ -696,7 +696,7 @@ async fn test_no_reverts_in_counterexample() {

match get_counterexample!(runner, &filter) {
CounterExample::Single(_) => panic!("CounterExample should be a sequence."),
CounterExample::Sequence(sequence) => {
CounterExample::Sequence(_, sequence) => {
// ensure original counterexample len is 10 (even without shrinking)
assert_eq!(sequence.len(), 10);
}
Expand Down Expand Up @@ -1064,3 +1064,39 @@ contract InvariantSelectorsWeightTest is Test {

cmd.args(["test", "--fuzz-seed", "119", "--mt", "invariant_selectors_weight"]).assert_success();
});

// Tests original and new counterexample lengths are displayed on failure.
forgetest_init!(invariant_sequence_len, |prj, cmd| {
prj.update_config(|config| {
config.fuzz.seed = Some(U256::from(100u32));
});

prj.add_test(
"InvariantSequenceLenTest.t.sol",
r#"
import {Test} from "forge-std/Test.sol";
import "src/Counter.sol";
contract InvariantSequenceLenTest is Test {
Counter public counter;
function setUp() public {
counter = new Counter();
targetContract(address(counter));
}
function invariant_increment() public {
require(counter.number() / 2 < 100000000000000000000000000000000, "invariant increment failure");
}
}
"#,
)
.unwrap();

cmd.args(["test", "--mt", "invariant_increment"]).assert_failure().stdout_eq(str![[r#"
...
[FAIL: revert: invariant increment failure]
[Sequence] (original: 4, shrunk: 1)
...
"#]]);
});

0 comments on commit f6133f9

Please sign in to comment.