-
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 (proposal): UX for granularity over fuzz, invariant, and solidity sections #3062
Comments
To select the profile |
|
i've got mixed feelings about the config complexity this introduces. @mds1 wdtyt about keeping the sections packed & simply adding the [fuzz]
runs = 250
overrides = { "testFuzzLow.*" = { runs = 50 }, "testFuzzHigh.*" = { runs = 500 }}
[profile.ci.fuzz]
runs = 1000
overrides = { "testFuzzLow.*" = { runs = 500 }, "testFuzzHigh.*" = { runs = 5000, max-global-rejects = 100_000 } } |
Yea I feel the same way about the complexity, but couldn't think of a good alternative 😕 In general I like your
Which was part of my motivation for the original format |
yeah, makes sense. not a fan of long lines either hence mixed feelings. one more idea is to have a cheatcode for a contract or test to set the config values, however that'd be both cumbersome to implement and for the end user to track across the codebase |
Yea I do think cheatcodes could be nice UX, see approach 4 in #744. However even that can get cumbersome since there's multiple parameters to set—we'd probably want a struct that defines all fuzz values and use some magic values to mean "I'm not setting this one, fallback to the default". But like you said it's probably significantly more work to implement and harder for users to track. I think the original proposal feels complex and hard to read because everything is visually "flat", whereas if this were formatted as JSON it would be more readable due to the indentation changes. Therefore I think using some comments to visually separate profiles can make it more readable (inline tables would also help, though technically they're supposed to be limited to one line I believe): # =================================
# ======== Default Profile ========
# =================================
# -------- General --------
[profile.default]
verbosity = 3
# -------- Solidity --------
[profile.lite.solidity]
optimizer = false
[profile.default.solidity]
optimizer = true
optimizer-runs = 10_000_000
[profile.default.solidity.size]
optimizer = true
optimizer-runs = 1
match-contract = "MyBigContract"
# -------- Fuzz --------
[profile.default.fuzz]
runs = 250 # 250 runs by default for the default profile
[profile.default.fuzz.low]
match-test = "testFuzzLow.*"
runs = 50
[profile.default.fuzz.high]
match-test = "testFuzzHigh.*"
runs = 500
# ====================
# ======== CI ========
# ====================
# -------- Fuzz --------
[profile.ci.fuzz]
runs = 1000
[profile.ci.fuzz.low]
match-test = "testFuzzLow.*"
runs = 500
[profile.ci.fuzz.high]
match-test = "testFuzzHigh.*"
runs = 5000
max-global-rejects = 100_000 |
An alternative is to format the TOML with Taplo, which can be enabled with the Even Better TOML extension in VSCode: "[toml]": {
"editor.defaultFormatter": "tamasfe.even-better-toml"
} |
Agreed, using taplo to indent the keys would also help. I personally do that for all toml files and find it much more readable |
I'm starting to think comment directives might the way to go here. Originally we didn't go this route because we thought it required compilation + contract MyTest is Test {
// forge-config: default.fuzz.runs = 100
// forge-config: ci.fuzz.runs = 500
function test_SimpleFuzzTest(uint256 x) public {
// --- snip ---
}
// forge-config: default.fuzz.runs = 500
// forge-config: ci.fuzz.runs = 10000
function test_ImportantFuzzTest(uint256 x) public {
// --- snip ---
}
} In this example:
Benefits over the config file approach:
Similar to the original proposal here, those comment directives could be supported above tests (to apply to a single test), above contracts (to apply to all tests in that contract), or at the root of a file (to apply to all tests in that file), with the most specific ones taking precendence. The main downside is the added noise to tests, but I think it's worth it. cc @brockelmore @transmissions11 @PaulRBerg @PatrickAlphaC for UX thoughts. I think this would be a huge unlock for the fuzzer |
@mds1 100% in favor of your feature proposal; it would be super useful. Regarding |
Why not use modifiers instead of comments? I really dislike the idea of using comments as a sort of function decorator. What are the thoughts on:
[profile.ci.fuzz]
runs = 1000
[profile.ci.fuzz.low]
match-test = "testFuzzLow.*"
runs = 500
[profile.ci.fuzz.high]
match-test = "testFuzzHigh.*"
runs = 5000
max-global-rejects = 100_000
contract MyTest is Test {
// it takes a list of strings as profile, and a list of uint256 as the number of fuzz runs repective with the profile
function test_SimpleFuzzTest(uint256 x) public fuzzRuns(["ci.fuzz.low"], [500]){
// --- snip ---
}
function test_ImportantFuzzTest(uint256 x) public fuzzRuns(["ci.fuzz.low"], [500]) {
// --- snip ---
}
} Additionally, we could overload modifiers to accept one parameter to override everything in the config. function test_ImportantFuzzTest(uint256 x) public fuzzRuns(500) {
// --- snip ---
} That way, we could rely on the compiler that our modifiers are setup more correctly. This could be part of the foundry standard test library. |
Compared to comments, using modifiers for test granularity has two downsides:
|
It should be noted that dapple (dapptools progenitor) uses this comment style as well (or use to at least). |
Hmm... I see your points. They make sense. But maybe there is some advantage to having the compiler take it on. For example, if you format your custom runs incorrectly, the compiler will catch it vs you'd test your code incorrectly. I sort of like the idea that having a modifier would make sure that I'm formatting my fuzz runs right. I don't love the idea of comments as the driving force. I think it isn't very clear. for newer devs especially. "Comments don't affect how your code runs, oh, except in this case in foundry." <- This feels weird to teach. Comments becoming an important factor in how your test suite runs are... meh. I like python's decorators, but I suppose we can't do something like that in solidity/foundry. This isn't a hill I'll die on as I see your points. But TL;DR, my thoughts are that using comments you'd:
But you get:
|
@PatrickAlphaC nice write-up; my take is that the benefits of using comments outweigh the cons.
Surely Foundry can catch misconfigured inputs. And if you don't use the correct comment syntax, you can quite quickly notice this when you run your tests and see that the fuzzing config didn't apply (e.g. you want to fuzz 50 times but in fact you fuzz 1,000 times, this is easy to see).
IMO, that's a bit of a stretch;
|
Thanks for the response. Re: "In any case, comments already affect the bytecode of smart contracts (because of metadata), so the logic of comments in Solidity has to be taught anyway." They affect the code size, but not the running code. I just know that someone is going to post something on stack exchange asking us to debug their tests, and the answer is that their comments are breaking it. In any case, I can't think of a better solution, so I think comments might be the best bet. Excited for this feature. |
Just curious how would the build know not to run optimizer 10,000,000x on MyBigContract? |
Given that #4744 was merged, should we close this issue? |
Closing, thanks! |
Component
Forge
Describe the feature you would like
Supersedes #744
For the
[fuzz]
and[invariant]
sections, you must define configuration per-profile and it applies to all tests. Some tests need more/less fuzzing than others, so it would be great to configure this on a per-test basis. Below is a proposal for how to structure thefoundry.toml
to allow this.Similarly, it would be great to allow different solc settings per contract (e.g. 1 optimizer run for
MyBigContract
but 10M optimizer runs for all others). This proposal would enable that as well.Additional context
No response
The text was updated successfully, but these errors were encountered: