@@ -11,7 +11,7 @@ use std::{path::PathBuf, sync::Arc, time::Instant};
1111use alloy_primitives:: keccak256;
1212use eyre:: { Result , WrapErr } ;
1313use foundry_cli:: utils:: FoundryPathExt ;
14- use foundry_common:: sh_println;
14+ use foundry_common:: { compile :: ProjectCompiler , sh_println} ;
1515use foundry_compilers:: {
1616 Language , ProjectCompileOutput ,
1717 compilers:: multi:: { MultiCompiler , MultiCompilerLanguage } ,
@@ -39,6 +39,7 @@ struct ExecutionCacheFingerprint<'a> {
3939 schema : & ' static str ,
4040 config : & ' a Config ,
4141 evm_opts : & ' a EvmOpts ,
42+ num_workers : usize ,
4243 artifacts : & ' a [ ArtifactCacheFingerprint ] ,
4344}
4445
@@ -98,7 +99,12 @@ pub async fn run_mutation_testing(
9899
99100 // Determine which paths to mutate
100101 let mutate_paths = resolve_mutate_paths ( & config, output, & mutation_config) ?;
101- let execution_cache_key = mutation_execution_cache_key ( & config, output, & evm_opts) ?;
102+ let execution_cache_output = ProjectCompiler :: new ( )
103+ . dynamic_test_linking ( config. dynamic_test_linking )
104+ . quiet ( json_output)
105+ . compile ( & config. project ( ) ?) ?;
106+ let execution_cache_key =
107+ mutation_execution_cache_key ( & config, & execution_cache_output, & evm_opts, num_workers) ?;
102108
103109 if !mutation_config. show_progress && !json_output {
104110 sh_println ! ( "Running mutation tests with {} parallel workers..." , num_workers) ?;
@@ -265,12 +271,15 @@ pub async fn run_mutation_testing(
265271/// settings. Hashing the full serialized config intentionally includes fuzz /
266272/// invariant settings, test filters, fs permissions, sender/balance/env values,
267273/// and future config fields unless explicitly skipped by `Config` itself. The
268- /// artifact fingerprint covers source and test build IDs, so changing a test
269- /// file invalidates result caches even when the mutated source did not change.
274+ /// artifact fingerprint covers the unfiltered source and test build IDs, so
275+ /// changing a test file invalidates result caches even when the outer test run
276+ /// was filtered. Worker count is included because adaptive span skipping is
277+ /// concurrency-sensitive.
270278fn mutation_execution_cache_key (
271279 config : & Config ,
272280 output : & ProjectCompileOutput < MultiCompiler > ,
273281 evm_opts : & EvmOpts ,
282+ num_workers : usize ,
274283) -> Result < String > {
275284 let artifacts = output
276285 . artifact_ids ( )
@@ -282,19 +291,21 @@ fn mutation_execution_cache_key(
282291 profile : id. profile ,
283292 } )
284293 . collect :: < Vec < _ > > ( ) ;
285- mutation_execution_cache_key_from_parts ( config, evm_opts, artifacts)
294+ mutation_execution_cache_key_from_parts ( config, evm_opts, num_workers , artifacts)
286295}
287296
288297fn mutation_execution_cache_key_from_parts (
289298 config : & Config ,
290299 evm_opts : & EvmOpts ,
300+ num_workers : usize ,
291301 mut artifacts : Vec < ArtifactCacheFingerprint > ,
292302) -> Result < String > {
293303 artifacts. sort ( ) ;
294304 let fingerprint = ExecutionCacheFingerprint {
295305 schema : "mutation-results-v1" ,
296306 config,
297307 evm_opts,
308+ num_workers,
298309 artifacts : & artifacts,
299310 } ;
300311 let encoded = serde_json:: to_vec ( & fingerprint)
@@ -391,9 +402,10 @@ mod tests {
391402 let artifacts = vec ! [ artifact( "build-a" ) ] ;
392403
393404 let first_key =
394- mutation_execution_cache_key_from_parts ( & first, & evm_opts, artifacts. clone ( ) ) . unwrap ( ) ;
405+ mutation_execution_cache_key_from_parts ( & first, & evm_opts, 1 , artifacts. clone ( ) )
406+ . unwrap ( ) ;
395407 let second_key =
396- mutation_execution_cache_key_from_parts ( & second, & evm_opts, artifacts) . unwrap ( ) ;
408+ mutation_execution_cache_key_from_parts ( & second, & evm_opts, 1 , artifacts) . unwrap ( ) ;
397409
398410 assert_ne ! ( first_key, second_key) ;
399411 }
@@ -408,9 +420,9 @@ mod tests {
408420 let artifacts = vec ! [ artifact( "build-a" ) ] ;
409421
410422 let first_key =
411- mutation_execution_cache_key_from_parts ( & config, & first, artifacts. clone ( ) ) . unwrap ( ) ;
423+ mutation_execution_cache_key_from_parts ( & config, & first, 1 , artifacts. clone ( ) ) . unwrap ( ) ;
412424 let second_key =
413- mutation_execution_cache_key_from_parts ( & config, & second, artifacts) . unwrap ( ) ;
425+ mutation_execution_cache_key_from_parts ( & config, & second, 1 , artifacts) . unwrap ( ) ;
414426
415427 assert_ne ! ( first_key, second_key) ;
416428 }
@@ -420,12 +432,20 @@ mod tests {
420432 let config = Config :: default ( ) ;
421433 let evm_opts = EvmOpts :: default ( ) ;
422434
423- let first_key =
424- mutation_execution_cache_key_from_parts ( & config, & evm_opts, vec ! [ artifact( "build-a" ) ] )
425- . unwrap ( ) ;
426- let second_key =
427- mutation_execution_cache_key_from_parts ( & config, & evm_opts, vec ! [ artifact( "build-b" ) ] )
428- . unwrap ( ) ;
435+ let first_key = mutation_execution_cache_key_from_parts (
436+ & config,
437+ & evm_opts,
438+ 1 ,
439+ vec ! [ artifact( "build-a" ) ] ,
440+ )
441+ . unwrap ( ) ;
442+ let second_key = mutation_execution_cache_key_from_parts (
443+ & config,
444+ & evm_opts,
445+ 1 ,
446+ vec ! [ artifact( "build-b" ) ] ,
447+ )
448+ . unwrap ( ) ;
429449
430450 assert_ne ! ( first_key, second_key) ;
431451 }
@@ -438,10 +458,26 @@ mod tests {
438458 let first = vec ! [ artifact( "build-a" ) , artifact( "build-b" ) ] ;
439459 let second = vec ! [ artifact( "build-b" ) , artifact( "build-a" ) ] ;
440460
441- let first_key = mutation_execution_cache_key_from_parts ( & config, & evm_opts, first) . unwrap ( ) ;
461+ let first_key =
462+ mutation_execution_cache_key_from_parts ( & config, & evm_opts, 1 , first) . unwrap ( ) ;
442463 let second_key =
443- mutation_execution_cache_key_from_parts ( & config, & evm_opts, second) . unwrap ( ) ;
464+ mutation_execution_cache_key_from_parts ( & config, & evm_opts, 1 , second) . unwrap ( ) ;
444465
445466 assert_eq ! ( first_key, second_key) ;
446467 }
468+
469+ #[ test]
470+ fn execution_cache_key_changes_when_worker_count_changes ( ) {
471+ let config = Config :: default ( ) ;
472+ let evm_opts = EvmOpts :: default ( ) ;
473+ let artifacts = vec ! [ artifact( "build-a" ) ] ;
474+
475+ let first_key =
476+ mutation_execution_cache_key_from_parts ( & config, & evm_opts, 1 , artifacts. clone ( ) )
477+ . unwrap ( ) ;
478+ let second_key =
479+ mutation_execution_cache_key_from_parts ( & config, & evm_opts, 4 , artifacts) . unwrap ( ) ;
480+
481+ assert_ne ! ( first_key, second_key) ;
482+ }
447483}
0 commit comments