Skip to content

feat: Group invariant campaigns by test contract#14844

Merged
grandizzy merged 21 commits into
foundry-rs:masterfrom
0xKarl98:OSS-272
May 22, 2026
Merged

feat: Group invariant campaigns by test contract#14844
grandizzy merged 21 commits into
foundry-rs:masterfrom
0xKarl98:OSS-272

Conversation

@0xKarl98

@0xKarl98 0xKarl98 commented May 20, 2026

Copy link
Copy Markdown
Contributor

On oss-272

Make the invariant test contract the campaign and corpus boundary for compatible boolean invariants. Per-predicate and per-handler failures are still reported individually, optimization invariants keep separate corpus state, and legacy failure caches remain readable with a warning. This also preserves counterexamples in JUnit output.

@0xKarl98 0xKarl98 marked this pull request as ready for review May 20, 2026 10:57
Comment on lines +1949 to +1951
Suite assert_all: 2/2 invariants broken
...
invariant_with_[..]() ([..])
Suite predicates:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just say "Assertion Tests" and "Invariant/Property Tests"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add the invariant contract name as well?

@grandizzy grandizzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, that goes in the right direction! I think the target should be to get rid of assert_all altogether as now we assume campaign by test contract, hence redundant. Would also be nice to preserve the behavior of --mt to give the possibility of running single test & clean output for dev cycle (nice to have, not 100% sure if still possible?)

Comment thread crates/forge/src/runner.rs Outdated
Comment thread crates/forge/src/runner.rs Outdated
@0xKarl98 0xKarl98 requested review from grandizzy May 22, 2026 00:54
@0xKarl98

Copy link
Copy Markdown
Contributor Author

@ptal again , have addressed your concerns

@grandizzy grandizzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, only some nits, pls check

Comment thread crates/evm/evm/src/executors/corpus.rs Outdated
Comment thread crates/forge/src/result.rs Outdated
Comment thread crates/forge/src/result.rs Outdated
Comment thread crates/forge/src/result.rs Outdated
@@ -797,6 +864,7 @@ impl<'a, FEN: FoundryEvmNetwork> FunctionRunner<'a, FEN> {
let runner = self.invariant_runner();
let invariant_config = self.config.invariant.clone();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skip preflight just above this only checks the campaign anchor (func). With contract-level invariant campaigns, secondary predicates can also call vm.skip; those are now swallowed because we return before building per-predicate results. This shows up in test_cmd::skip_output: invariant_skipInvariantReason() disappears and the suite drops from 6 skipped to 5.

Suggested fix: preflight every predicate in invariants, collect skipped predicates as InvariantPredicateResult { status: TestStatus::Skipped, reason }, render skip reasons in write_invariant_predicate_results ([SKIP: reason] name), and exclude skipped predicates from the fuzz campaign. If all predicates skip, return a skipped campaign result carrying those predicate results.

Invariant/Property Tests: 2/2 invariants broken
[FAIL: first broken] invariant_first
[FAIL: second broken] invariant_second
2 invariant failure(s) persisted to /private/var/folders/p4/rl3tc43j51s311jt6pq7zq180000gn/T/[..]/cache/invariant/failures/StaleSecondaryTest — rerun to shrink

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hard-codes a macOS temp root, so the snapshot fails on Linux CI where the same logical path is under /tmp/<test-dir>/cache/invariant/failures/StaleSecondaryTest. Please redact the temp project root here, e.g. expect [..]/cache/invariant/failures/StaleSecondaryTest — rerun to shrink (and same below), so the assertion checks the invariant failure subpath without depending on host temp-dir layout.

@0xKarl98

Copy link
Copy Markdown
Contributor Author

Have addressed your nits , ptal again @decofe

mattsse
mattsse previously approved these changes May 22, 2026

@mattsse mattsse left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed locally. The invariant campaign grouping looks acceptable to me, and cargo check -p forge --tests passed.

grandizzy
grandizzy previously approved these changes May 22, 2026
@decofe decofe dismissed stale reviews from mattsse and grandizzy via fe617aa May 22, 2026 14:09
@grandizzy grandizzy enabled auto-merge (squash) May 22, 2026 14:25
@grandizzy grandizzy requested review from grandizzy and mattsse May 22, 2026 15:07
@grandizzy grandizzy merged commit ed0c58f into foundry-rs:master May 22, 2026
19 checks passed
@github-project-automation github-project-automation Bot moved this to Done in Foundry May 22, 2026
// anchor without searching by name.
let invariant_fns: Vec<(&Function, bool)> = live_invariants
.into_iter()
.filter(|(invariant_fn, _)| {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This drops selected secondary predicates with compatible persisted failures from the campaign without adding them to the result. I reproduced this: first broad --mt invariant_ persists a secondary failure, then the same broad run exits 0 and reports only the passing anchor. Persisted selected failures should still fail/report, not just warn.

let Some((anchor, rest)) = funcs.split_first() else {
return true;
};
let anchor_config = match self.inline_config(Some(anchor)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invariant_suite_configs_match only compares config.invariant, but merged campaigns run under the anchor’s executor config. I reproduced a secondary with forge-config: default.evm_version = "shanghai": it passes alone, but fails when merged under the anchor’s Cancun config. Please include executor-affecting inline config in compatibility, or don’t merge when function-level config differs.

@0xKarl98 0xKarl98 deleted the OSS-272 branch May 25, 2026 10:12
pcaversaccio added a commit to pcaversaccio/snekmate that referenced this pull request Jun 1, 2026
### 🕓 Changelog

This PR updates the version `pragma`s in all 🐍Vyper source files (`git
ls-files '*.vy' '*.vyi' | wc -l`) to target 🐍Vyper's latest master
version `0.5.0a3` (`git grep -l '=0.5.0a2' | xargs sed -i
's/=0.5.0a2/=0.5.0a3/g'`). In addition, it updates the MetaMask
[`eth_signTypedData_v4`](https://docs.metamask.io/metamask-connect/evm/reference/json-rpc-api/eth_signTypedData_v4)
reference URL to the current documentation path, removes the obsolete
`assert_all = false` `foundry.toml` configuration, and updates the
invariant comments following
[foundry-rs/foundry#14844](foundry-rs/foundry#14844),
which made all invariants in a contract share a single campaign by
default.

---------

Signed-off-by: pcaversaccio <pascal.caversaccio@hotmail.ch>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants