Skip to content

Commit

Permalink
Add test from issue 5868
Browse files Browse the repository at this point in the history
  • Loading branch information
grandizzy committed Feb 27, 2024
1 parent d208a4d commit abe6c8b
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/config/src/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub struct FuzzDictionaryConfig {
pub max_fuzz_dictionary_values: usize,
/// How many random addresses to use and to recycle when fuzzing calldata.
/// If not specified then `max_fuzz_dictionary_addresses` value applies.
#[serde(deserialize_with = "crate::deserialize_usize_or_max")]
pub max_calldata_fuzz_dictionary_addresses: usize,
}

Expand Down
52 changes: 52 additions & 0 deletions crates/forge/tests/it/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ async fn test_invariant() {
None,
)],
),
(
"fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary",
vec![("invariant_owner_never_changes()", true, None, None, None)],
),
]),
);
}
Expand Down Expand Up @@ -244,3 +248,51 @@ async fn test_invariant_shrink() {
}
};
}

#[tokio::test(flavor = "multi_thread")]
async fn test_invariant_calldata_fuzz_dictionary_addresses() {
let mut runner = runner().await;

// should not fail with default options (address dict not finite)
let opts = test_opts();
runner.test_options = opts.clone();
let results = runner
.test_collect(
&Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantCalldataDictionary.t.sol"),
opts,
)
.await;
assert_multiple(
&results,
BTreeMap::from([(
"fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary",
vec![("invariant_owner_never_changes()", true, None, None, None)],
)]),
);

// same test should fail when calldata address dict is bounded
let mut opts = test_opts();
// set address dictionary to single entry to fail fast
opts.invariant.dictionary.max_calldata_fuzz_dictionary_addresses = 1;
runner.test_options = opts.clone();

let results = runner
.test_collect(
&Filter::new(".*", ".*", ".*fuzz/invariant/common/InvariantCalldataDictionary.t.sol"),
opts,
)
.await;
assert_multiple(
&results,
BTreeMap::from([(
"fuzz/invariant/common/InvariantCalldataDictionary.t.sol:InvariantCalldataDictionary",
vec![(
"invariant_owner_never_changes()",
false,
Some("<empty revert data>".into()),
None,
None,
)],
)]),
);
}
84 changes: 84 additions & 0 deletions testdata/fuzz/invariant/common/InvariantCalldataDictionary.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.18;

import "ds-test/test.sol";
import "../../../cheats/Vm.sol";

struct FuzzSelector {
address addr;
bytes4[] selectors;
}

// https://github.com/foundry-rs/foundry/issues/5868
contract Owned {
address public owner;
address private ownerCandidate;

constructor() {
owner = msg.sender;
}

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

modifier onlyOwnerCandidate() {
require(msg.sender == ownerCandidate);
_;
}

function transferOwnership(address candidate) external onlyOwner {
ownerCandidate = candidate;
}

function acceptOwnership() external onlyOwnerCandidate {
owner = ownerCandidate;
}
}

contract Handler is DSTest {
Vm constant vm = Vm(HEVM_ADDRESS);
Owned owned;

constructor(Owned _owned) {
owned = _owned;
}

function transferOwnership(address sender, address candidate) external {
vm.assume(sender != address(0));
vm.prank(sender);
owned.transferOwnership(candidate);
}

function acceptOwnership(address sender) external {
vm.assume(sender != address(0));
vm.prank(sender);
owned.acceptOwnership();
}
}

contract InvariantCalldataDictionary is DSTest {
address owner;
Owned owned;
Handler handler;

function setUp() public {
owner = address(this);
owned = new Owned();
handler = new Handler(owned);
}

function targetSelectors() public returns (FuzzSelector[] memory) {
FuzzSelector[] memory targets = new FuzzSelector[](1);
bytes4[] memory selectors = new bytes4[](2);
selectors[0] = handler.transferOwnership.selector;
selectors[1] = handler.acceptOwnership.selector;
targets[0] = FuzzSelector(address(handler), selectors);
return targets;
}

function invariant_owner_never_changes() public {
assertEq(owned.owner(), owner);
}
}

0 comments on commit abe6c8b

Please sign in to comment.