Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
166 commits
Select commit Hold shift + click to select a range
c0ffee9
feat: init
simon-something Feb 27, 2025
c0ffeea
chore: lexing/parsing
simon-something Feb 27, 2025
c0ffeef
chore: visiting contracts
simon-something Feb 28, 2025
c0ffeea
chore: wip
simon-something Mar 1, 2025
c0ffee4
chore: wip
simon-something Mar 1, 2025
c0ffee0
chore: wip
simon-something Mar 1, 2025
c0ffee0
chore: visitor not visiting anything
simon-something Mar 1, 2025
c0ffee1
chore: visitor visiting
simon-something Mar 1, 2025
c0ffee0
chore: quick refactor before its too late
simon-something Mar 3, 2025
c0ffee1
chore: quick refactor
simon-something Mar 3, 2025
c0ffee1
chore: mutation gen part1
simon-something Mar 3, 2025
c0ffeed
feat: mutation collection
simon-something Mar 4, 2025
c0ffeee
feat: visitor refactor
simon-something Mar 4, 2025
c0ffeeb
feat: visitor refactor
simon-something Mar 4, 2025
c0ffeef
feat: temp folder mgmt
simon-something Mar 5, 2025
c0ffeec
feat: temp file creation logic
simon-something Mar 6, 2025
c0ffee6
feat: compiling mutants
simon-something Mar 6, 2025
c0ffee2
chore: wip future multithread
simon-something Mar 9, 2025
c0ffeeb
feat: mutation set building
simon-something Mar 10, 2025
c0ffee1
feat: multithread compile
simon-something Mar 11, 2025
c0ffee8
chore: fmt
simon-something Mar 11, 2025
c0ffeeb
chore: fmt
simon-something Mar 11, 2025
c0ffeef
chore: fmt
simon-something Mar 11, 2025
c0ffeee
chore: fmt
simon-something Mar 11, 2025
c0ffeec
feat: refactor for test runner
simon-something Mar 12, 2025
c0ffee4
chore: test runner wip
simon-something Mar 12, 2025
c0ffee1
feat: working poc
simon-something Mar 12, 2025
c0ffee2
chore: doc
simon-something Mar 12, 2025
c0ffee5
chore: fmt
simon-something Mar 12, 2025
c0ffeec
chore: fmt
simon-something Mar 12, 2025
c0ffeed
chore: fmt
simon-something Mar 12, 2025
c0ffee2
feat: assign mut gen
simon-something Mar 12, 2025
c0ffeef
feat: unary mut
simon-something Mar 13, 2025
c0ffeea
feat: binary mut
simon-something Mar 13, 2025
c0ffee4
feat: members unary mut
simon-something Mar 13, 2025
c0ffee4
feat: members unary mut
simon-something Mar 13, 2025
c0ffee1
feat: rm delegatecall mut
simon-something Mar 13, 2025
c0ffeea
chore: refactor modular mut wip
simon-something Mar 13, 2025
c0ffee0
chore: refactor modular mut wip
simon-something Mar 13, 2025
c0ffee5
chore: refactor modular mut wip
simon-something Mar 14, 2025
c0ffeee
feat: mutator assign trait
simon-something Mar 17, 2025
c0ffee3
chore: fmt
simon-something Mar 17, 2025
c0ffee2
chore: fmt
simon-something Mar 17, 2025
c0ffee6
feat: bin mutator
simon-something Mar 18, 2025
c0ffee1
feat: other mutator mod
simon-something Mar 19, 2025
c0ffeea
feat: visitor refactor registry
simon-something Mar 20, 2025
0060f79
Merge branch 'master' into feat/mutation-tests-wonder
simon-something Mar 20, 2025
c0ffee8
chore: merge fix
simon-something Mar 20, 2025
39e5f62
Merge pull request #2 from defi-wonderland/feat/mutation-tests-wonder
simon-something Mar 20, 2025
1a90f9c
Merge branch 'master' into master
simon-something Mar 20, 2025
18fb058
chore: comment
simon-something Mar 20, 2025
c0ffee1
fix: solar visitor use
simon-something Mar 20, 2025
c0ffee8
chore: clippy and min refactors
simon-something Mar 21, 2025
c0ffeef
feat: assign mutator tests
simon-something Mar 21, 2025
c0ffeea
feat: test gen mut ident
simon-something Mar 21, 2025
5b67d3f
feat: test binop and delete expr
simon-something Mar 25, 2025
7690e44
feat: test delegate unaryop mut
simon-something Mar 25, 2025
8137d21
feat: test unaryop mut
simon-something Mar 25, 2025
15b8cec
feat: wip generic test
simon-something Mar 25, 2025
d12d1ca
refactor: add mutate-path and mutate-contract optional args, keep mut…
Mar 26, 2025
565ce30
feat: enable contract match filter in mutation tests
Mar 26, 2025
47aa3ca
Merge remote-tracking branch 'upstream/master'
simon-something Mar 28, 2025
b782603
feat: optional mutators
simon-something Mar 28, 2025
246d21e
feat: generic mutator test(length)
simon-something Mar 28, 2025
2e8dc62
Merge pull request #1 from simon-something/feat/mutation-tests-filters
simon-something Mar 28, 2025
bf7ae62
feat: generic mutator test(content)
simon-something Mar 28, 2025
775600f
fix: unary mut for bool
simon-something Mar 28, 2025
7e3a9c0
test: unary mut
simon-something Mar 28, 2025
be42620
test: all mutators
simon-something Mar 28, 2025
8d9dab2
test: add neg case
simon-something Mar 28, 2025
c0ffee0
chore: refactor visitor
simon-something Mar 30, 2025
c0ffee1
chore: clippy
simon-something Mar 30, 2025
c0ffeec
chore: typos
simon-something Mar 30, 2025
c0ffeec
feat: integ test (hacky)
simon-something Mar 31, 2025
c0ffee5
feat: basic reporting
simon-something Apr 9, 2025
5821d58
fix: copy only src
simon-something May 28, 2025
c0ffeec
fix: copy only src
simon-something May 28, 2025
b7ea26e
fix: rel path
simon-something May 28, 2025
c5387f6
fix: add walker in visitor
simon-something May 28, 2025
7b7bcfc
fix: assign mutator
simon-something May 28, 2025
bfeb71c
chore: dbg cleanup
simon-something May 28, 2025
c0ffee1
chore: merge upstream
simon-something Jul 21, 2025
c0ffeec
fix: dyn test linking
simon-something Jul 25, 2025
c9e9d06
fix: missing mutant fmt
simon-something Jul 25, 2025
c0ffeea
fix: path in mut ctxt
simon-something Jul 28, 2025
c0ffee9
feat: save and resume mutation tests
simon-something Aug 29, 2025
c0ffeea
chore: typo and doc
simon-something Aug 29, 2025
b4271c3
chore: remove cache file
emo-eth Oct 5, 2025
2dea042
Merge branch 'master' into simon-something/master
emo-eth Oct 5, 2025
9607840
merge master
emo-eth Oct 5, 2025
5e731f1
clippy
emo-eth Oct 5, 2025
752700a
update
emo-eth Oct 6, 2025
ab63df5
use serde instead of manual dto for json caching
emo-eth Oct 6, 2025
26fd5e5
fix cache filenames
emo-eth Oct 6, 2025
84479d2
initial pass at adaptive testing - skip surviving sibling or child spans
emo-eth Oct 6, 2025
17a232e
Merge branch 'master' into mutation-testing
emo-eth Oct 6, 2025
2ee3570
clippy && fmt
emo-eth Oct 6, 2025
5daeda3
Merge branch 'master' into mutation-testing
grandizzy Oct 7, 2025
6bc4145
Merge branch 'master' into mutation-re
grandizzy Oct 24, 2025
8a7470e
Merge remote-tracking branch 'upstream/master' into mutation-re
grandizzy Nov 19, 2025
a06858e
feat(forge): parallel mutation testing with isolated workspaces
gakonst Jan 15, 2026
2ca0f4b
fix: resolve merge conflicts with master
gakonst Jan 15, 2026
5ee120a
fix: clippy, rustfmt, and config test for mutation_dir
gakonst Jan 15, 2026
72ae4eb
Merge branch 'master' into mutation-testing-fast
grandizzy Jan 21, 2026
dbee51d
feat(mutation): add progress display with Ctrl+C support
grandizzy Jan 22, 2026
781f57e
Merge upstream/master into mutation-testing-fast
grandizzy Jan 22, 2026
71e44ed
chore: rename 'runs' to 'mutation runs' in progress bar
grandizzy Jan 22, 2026
8002982
fix: mark doc code block as text to fix doctest
grandizzy Jan 22, 2026
c5490a2
refactor: address grandizzy review comments
grandizzy Jan 23, 2026
91093bc
Add time and report legend
grandizzy Jan 23, 2026
43b94c0
feat(mutation): add RequireMutator and source hash cache validation
grandizzy Jan 23, 2026
825a1f7
Merge branch 'master' into mutation-testing-fast
grandizzy Jan 23, 2026
f2ee1a9
Add unary_op_mutator_test module to tests
zerosnacks Jan 23, 2026
8d2d51c
fmt imports
zerosnacks Jan 23, 2026
1be3317
Update crates/forge/src/mutation/mutators/mod.rs
zerosnacks Jan 23, 2026
4bdd63b
nits
zerosnacks Jan 23, 2026
bd37c20
Update crates/forge/src/mutation/mutators/assignment_mutator.rs
zerosnacks Jan 23, 2026
80c638e
nits
zerosnacks Jan 23, 2026
a27ff7e
refactor: replace ctrlc with tokio signal, update rstest to 0.26
gakonst Jan 23, 2026
1ab47ff
feat(mutation): add --json support for mutation testing
grandizzy Jan 26, 2026
df01d65
Merge branch 'master' into mutation-testing-fast
zerosnacks Jan 26, 2026
34def3a
fix(mutation): symlink node_modules and soldeer dependencies in temp …
gakonst Jan 26, 2026
8566373
fix(mutation): recursively symlink nested lib directories in temp wor…
gakonst Jan 26, 2026
57d2288
Merge branch 'master' into mutation-testing-fast
zerosnacks Feb 5, 2026
91e600b
feat(mutation): add Yul opcode mutator (#13341)
zerosnacks Feb 6, 2026
56fd05c
Merge branch 'master' into mutation-testing-fast
zerosnacks Feb 10, 2026
659a9c2
feat: make mutation test operators configurable (#13410)
zerosnacks Feb 10, 2026
f5f466c
refactor: rename UnaryOperatorMutator to UnaryOpMutator (#13411)
zerosnacks Feb 10, 2026
d51bbeb
fix: address mutation testing issues (#13412)
zerosnacks Feb 10, 2026
b306b31
chore: remove dead code from mutation module (#13413)
zerosnacks Feb 10, 2026
401400c
refactor(mutation): extract workspace utilities into shared module (#…
zerosnacks Feb 11, 2026
ed2dd15
merge: resolve conflicts with master
decofe Apr 13, 2026
f0ac55b
fix: resolve clippy warnings in mutation module
decofe Apr 13, 2026
7116b9c
fix: use then_some instead of then with closure
decofe Apr 13, 2026
e9f09df
fix: make default_excluded const fn
decofe Apr 13, 2026
c4f943a
fix: add const to eligible mutation module functions
decofe Apr 13, 2026
017f341
fix: revert with_path to non-const (PathBuf has destructor)
decofe Apr 13, 2026
e7caa0c
fix: make builder() const fn
decofe Apr 13, 2026
373e98f
fix: address review feedback
decofe Apr 13, 2026
ae21231
fix: rustfmt and add depth arg to test calls
decofe Apr 13, 2026
3f80c09
Merge remote-tracking branch 'origin/master' into mutation-testing-fast
zerosnacks May 7, 2026
fd652f9
test(mutation): replace rstest with mutator_tests! macro
zerosnacks May 7, 2026
c9f1595
Merge branch 'master' into mutation-testing-fast
zerosnacks May 11, 2026
10669ad
Merge branch 'master' into mutation-testing-fast
zerosnacks May 19, 2026
4c74d45
fix(mutation): address PR review feedback
zerosnacks May 19, 2026
a36b44e
feat(mutation): add --mutation-timeout + richer progress UX (WIP)
zerosnacks May 19, 2026
c668b91
fix(mutation): enforce per-mutant timeout in fuzz/invariant harness; …
zerosnacks May 19, 2026
5a0579a
fix(mutation): rustfmt, no-default-features, config snapshot
zerosnacks May 19, 2026
3c62a1b
Merge branch 'master' into mutation-testing-fast
zerosnacks May 20, 2026
9b2bf3e
fix(mutation): address oracle review blockers and highs
zerosnacks May 20, 2026
101c29e
Merge branch 'master' into mutation-testing-fast
mablr May 29, 2026
6c0d06e
fix(mutation): key cached results by execution inputs (#14971)
mablr May 29, 2026
3841222
Merge branch 'master' into mutation-testing-fast
mablr May 30, 2026
bda7953
fix(mutation): honor filters and isolation (#14848)
zerosnacks Jun 1, 2026
dac38d9
Merge branch 'master' into mutation-testing-fast
mablr Jun 1, 2026
3ce9b66
fix(mutation): reject mutation under machine mode (#15009)
mablr Jun 1, 2026
fc77e99
fix(mutation): reject empty mutation selections (#15010)
mablr Jun 1, 2026
eaaa755
fix(forge): preserve mutation workspace config (#15011)
mablr Jun 1, 2026
273331f
fix(mutation): use source spans for unary targets (#15014)
mablr Jun 1, 2026
7dba35b
fix(mutation): surface mutator generation errors (#15015)
mablr Jun 1, 2026
65180ab
fix(mutation): bound timed-out mutation cleanup (#15016)
mablr Jun 1, 2026
b340423
fix(mutation): filter no-op mutation outputs (#15013)
mablr Jun 2, 2026
0ae6090
fix(mutation): copy mutation compiler paths (#15012)
mablr Jun 2, 2026
44c32b4
fix(mutation): handle cancellation without progress UI (#15030)
mablr Jun 2, 2026
29dc2b6
Merge branch 'master' into mutation-testing-fast
mablr Jun 2, 2026
f2594d3
Merge branch 'master' into mutation-testing-fast
mablr Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ tracing.workspace = true
walkdir.workspace = true
yansi.workspace = true
clap = { version = "4", features = ["derive"] }
strum = { workspace = true, features = ["derive"] }

[target.'cfg(target_os = "windows")'.dependencies]
path-slash = "0.2"
Expand Down
27 changes: 24 additions & 3 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ pub use fuzz::{FuzzConfig, FuzzCorpusConfig, FuzzDictionaryConfig};
mod invariant;
pub use invariant::InvariantConfig;

pub mod mutation;
pub use mutation::{MutationConfig, MutatorType};

mod inline;
pub use inline::{InlineConfig, InlineConfigError, NatSpec};

Expand Down Expand Up @@ -351,6 +354,8 @@ pub struct Config {
pub coverage_pattern_inverse: Option<RegexWrapper>,
/// Path where last test run failures are recorded.
pub test_failures_file: PathBuf,
/// Path where mutation tests are cached, to resume running them
pub mutation_dir: PathBuf,
/// Max concurrent threads to use.
pub threads: Option<usize>,
/// Whether to show test execution progress.
Expand All @@ -359,6 +364,8 @@ pub struct Config {
pub fuzz: FuzzConfig,
/// Configuration for invariant testing
pub invariant: InvariantConfig,
/// Configuration for mutation testing
pub mutation: MutationConfig,
/// Whether to allow ffi cheatcodes in test
pub ffi: bool,
/// Whether to show `console.log` outputs in realtime during script/test execution
Expand Down Expand Up @@ -710,6 +717,7 @@ impl Config {
"doc",
"fuzz",
"invariant",
"mutation",
"labels",
"dependencies",
"soldeer",
Expand Down Expand Up @@ -1329,6 +1337,9 @@ impl Config {
));
}

// Remove mutation test cache directory
let _ = fs::remove_dir_all(project.root().join(&self.mutation_dir));

// Remove fuzz and invariant cache directories.
let mut remove_test_dir = |test_dir: &Option<PathBuf>| {
if let Some(test_dir) = test_dir {
Expand Down Expand Up @@ -2684,10 +2695,12 @@ impl Default for Config {
path_pattern_inverse: None,
coverage_pattern_inverse: None,
test_failures_file: "cache/test-failures".into(),
mutation_dir: "cache/mutation".into(),
threads: None,
show_progress: false,
fuzz: FuzzConfig::new("cache/fuzz".into()),
invariant: InvariantConfig::new("cache/invariant".into()),
mutation: MutationConfig::default(),
always_use_create_2_factory: false,
ffi: false,
live_logs: false,
Expand Down Expand Up @@ -6953,6 +6966,9 @@ mod tests {
runs = 256
unknown_invariant_key = "should_warn"

[mutation]
unknown_mutation_key = "should_warn"

[vyper]
unknown_vyper_key = "should_warn"

Expand Down Expand Up @@ -6981,6 +6997,9 @@ mod tests {
runs = 512
unknown_nested_invariant_key = "should_warn"

[profile.default.mutation]
unknown_nested_mutation_key = "should_warn"

[profile.default.vyper]
unknown_nested_vyper_key = "should_warn"

Expand Down Expand Up @@ -7019,6 +7038,7 @@ mod tests {
("unknown_doc_key", "doc"),
("unknown_fuzz_key", "fuzz"),
("unknown_invariant_key", "invariant"),
("unknown_mutation_key", "mutation"),
("unknown_vyper_key", "vyper"),
("unknown_bind_json_key", "bind_json"),
];
Expand All @@ -7044,6 +7064,7 @@ mod tests {
("unknown_nested_doc_key", "doc"),
("unknown_nested_fuzz_key", "fuzz"),
("unknown_nested_invariant_key", "invariant"),
("unknown_nested_mutation_key", "mutation"),
("unknown_nested_vyper_key", "vyper"),
("unknown_nested_bind_json_key", "bind_json"),
];
Expand Down Expand Up @@ -7092,11 +7113,11 @@ mod tests {
})
.collect();

// 1 profile key + 7 standalone + 7 nested + 2 array = 17 total
// 1 profile key + 8 standalone + 8 nested + 2 array = 19 total
assert_eq!(
unknown_key_warnings.len(),
17,
"Expected 17 unknown key warnings (1 profile + 7 standalone + 7 nested + 2 array), got {}: {:?}",
19,
"Expected 19 unknown key warnings (1 profile + 8 standalone + 8 nested + 2 array), got {}: {:?}",
unknown_key_warnings.len(),
unknown_key_warnings
);
Expand Down
80 changes: 80 additions & 0 deletions crates/config/src/mutation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//! Configuration for mutation testing.

use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;

/// Represents each available mutation operator.
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
strum::Display,
strum::EnumString,
strum::EnumIter,
)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum MutatorType {
Assembly,
Assignment,
BinaryOp,
DeleteExpression,
ElimDelegate,
Require,
UnaryOp,
}

impl MutatorType {
/// Returns a list of all available mutator types.
pub fn all() -> Vec<Self> {
Self::iter().collect()
}

/// Returns the operators that are excluded by default.
pub const fn default_excluded() -> Vec<Self> {
Vec::new()
}
}

/// Configuration for mutation testing.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct MutationConfig {
/// Re-enable operators that are excluded by default.
pub include_operators: Vec<MutatorType>,
/// Exclude additional operators beyond the defaults.
pub exclude_operators: Vec<MutatorType>,
/// Per-mutant wall-clock timeout, in seconds.
///
/// When set, each mutant's compile-and-test work is bounded by this
/// duration; mutants that exceed it are recorded as `TimedOut`. This is
/// the analog of `invariant.timeout` for mutation campaigns.
///
/// Note: enforcement is best-effort. Background work for a timed-out
/// mutant may continue briefly until the underlying compile / test loop
/// reaches a checkpoint, but the worker slot is freed immediately so
/// other mutants can proceed. Cleanup backlog is bounded by the configured
/// mutation worker count.
pub timeout: Option<u32>,
}

impl MutationConfig {
/// Returns the list of operators that are currently enabled.
///
/// Effective set: `all() - default_excluded - exclude_operators + include_operators`
pub fn enabled_operators(&self) -> Vec<MutatorType> {
let default_excluded = MutatorType::default_excluded();
MutatorType::all()
.into_iter()
.filter(|op| {
let excluded = default_excluded.contains(op) || self.exclude_operators.contains(op);
let included = self.include_operators.contains(op);
!excluded || included
})
.collect()
}
}
6 changes: 3 additions & 3 deletions crates/forge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ alloy-transport.workspace = true

tempo-alloy.workspace = true

revm.workspace = true

clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] }
clap_complete.workspace = true
dunce.workspace = true
Expand All @@ -75,13 +73,14 @@ inferno = { version = "0.12", default-features = false }
itertools.workspace = true
parking_lot.workspace = true
regex = { workspace = true, default-features = false }
revm.workspace = true
semver.workspace = true
serde_json.workspace = true
similar = { version = "2", features = ["inline"] }
solar.workspace = true
strum = { workspace = true, features = ["derive"] }
thiserror.workspace = true
tokio = { workspace = true, features = ["time"] }
tokio = { workspace = true, features = ["time", "signal"] }
toml_edit.workspace = true
watchexec = "8.0"
watchexec-events = "6.0"
Expand All @@ -101,6 +100,7 @@ opener = "0.8"
soldeer-commands.workspace = true
soldeer-core.workspace = true
quick-junit = "0.5.2"
tempfile.workspace = true

[dev-dependencies]
alloy-hardforks.workspace = true
Expand Down
Loading
Loading