-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(test): add fuzz tests failure persistence #7336
Changes from 3 commits
e62feca
9691469
85d9b9f
9668719
51fb697
b8d61ea
6145c5f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,17 @@ | ||
//! Configuration for fuzz testing. | ||
|
||
use crate::inline::{ | ||
parse_config_u32, InlineConfigParser, InlineConfigParserError, INLINE_CONFIG_FUZZ_KEY, | ||
use crate::{ | ||
inline::{ | ||
parse_config_u32, InlineConfigParser, InlineConfigParserError, INLINE_CONFIG_FUZZ_KEY, | ||
}, | ||
Config, | ||
}; | ||
use alloy_primitives::U256; | ||
use serde::{Deserialize, Serialize}; | ||
use std::path::PathBuf; | ||
|
||
/// Contains for fuzz testing | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||
pub struct FuzzConfig { | ||
/// The number of test cases that must execute for each property test | ||
pub runs: u32, | ||
|
@@ -22,6 +26,10 @@ pub struct FuzzConfig { | |
/// The fuzz dictionary configuration | ||
#[serde(flatten)] | ||
pub dictionary: FuzzDictionaryConfig, | ||
/// Path where fuzz failures are recorded and replayed, defaults to `~/.foundry/cache/fuzz` | ||
pub failure_persist_dir: PathBuf, | ||
/// Name of the file to record fuzz failures, defaults to `failures` | ||
pub failure_persist_file: String, | ||
} | ||
|
||
impl Default for FuzzConfig { | ||
|
@@ -31,6 +39,8 @@ impl Default for FuzzConfig { | |
max_test_rejects: 65536, | ||
seed: None, | ||
dictionary: FuzzDictionaryConfig::default(), | ||
failure_persist_dir: Config::foundry_fuzz_cache_dir().unwrap(), | ||
failure_persist_file: "failures".to_string(), | ||
} | ||
} | ||
} | ||
|
@@ -47,8 +57,7 @@ impl InlineConfigParser for FuzzConfig { | |
return Ok(None) | ||
} | ||
|
||
// self is Copy. We clone it with dereference. | ||
let mut conf_clone = *self; | ||
let mut conf_clone = self.clone(); | ||
|
||
for pair in overrides { | ||
let key = pair.0; | ||
|
@@ -59,6 +68,7 @@ impl InlineConfigParser for FuzzConfig { | |
"dictionary-weight" => { | ||
conf_clone.dictionary.dictionary_weight = parse_config_u32(key, value)? | ||
} | ||
"failure-persist-file" => conf_clone.failure_persist_file = value, | ||
_ => Err(InlineConfigParserError::InvalidConfigProperty(key))?, | ||
} | ||
} | ||
|
@@ -127,11 +137,13 @@ mod tests { | |
let configs = &[ | ||
"forge-config: default.fuzz.runs = 42424242".to_string(), | ||
"forge-config: default.fuzz.dictionary-weight = 42".to_string(), | ||
"forge-config: default.fuzz.failure-persist-file = fuzz-failure".to_string(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Am I correct that this means that by default, each run overwrites the previous? If so I think that's good, to prevent the cache from silently getting too large. We should also make sure an env var args are supported so it's easy to set a one-off name or one-off input file without modifying the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re file persistence:
Re env var: there's |
||
]; | ||
let base_config = FuzzConfig::default(); | ||
let merged: FuzzConfig = base_config.try_merge(configs).expect("No errors").unwrap(); | ||
assert_eq!(merged.runs, 42424242); | ||
assert_eq!(merged.dictionary.dictionary_weight, 42); | ||
assert_eq!(merged.failure_persist_file, "fuzz-failure".to_string()); | ||
} | ||
|
||
#[test] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity 0.8.18; | ||
|
||
import "ds-test/test.sol"; | ||
import "../cheats/Vm.sol"; | ||
|
||
struct TestTuple { | ||
address user; | ||
uint256 amount; | ||
} | ||
|
||
contract FuzzFailurePersistTest is DSTest { | ||
Vm vm = Vm(HEVM_ADDRESS); | ||
|
||
function test_persist_fuzzed_failure( | ||
uint256 x, | ||
int256 y, | ||
address addr, | ||
bool cond, | ||
string calldata test, | ||
TestTuple calldata tuple, | ||
address[] calldata addresses | ||
) public { | ||
// dummy assume to trigger runs | ||
vm.assume(x > 1 && x < 1111111111111111111111111111); | ||
vm.assume(y > 1 && y < 1111111111111111111111111111); | ||
require(false); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we persist the data in the project's local
./cache
folder instead? I haven't looked at exactly what's in the file, but if the fuzz data is unique to the project we probably should use the local cache. cc @klkvrThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this should definitely be somewhere in project's root. Let's write to
./{cache_path}/fuzz
by default.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, right, I pushed a commit to write in project root dir
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, the fuzz data is unique per fuzzed test