From 255d5235794fe2154de5ae940efd69710502329a Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 12:28:12 +0200 Subject: [PATCH 01/30] Skip config resolution for non-matching targets in `--exact` runs --- crates/forge-runner/src/running/target.rs | 103 +++++++++++++++--- crates/forge/src/run_tests/package.rs | 17 ++- .../data/lazy_config_filtering/Scarb.toml | 13 +++ crates/forge/tests/e2e/running.rs | 24 ++++ crates/forge/tests/integration/setup_fork.rs | 14 ++- crates/forge/tests/utils/running_tests.rs | 8 +- 6 files changed, 155 insertions(+), 24 deletions(-) create mode 100644 crates/forge/tests/data/lazy_config_filtering/Scarb.toml diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 09bf0e77ac..761d808c2e 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -4,6 +4,7 @@ use crate::{ TestDetails, raw::TestTargetRaw, with_config::{TestCaseWithConfig, TestTargetWithConfig}, + with_config_resolved::sanitize_test_case_name, }, running::config_run::run_config_pass, }; @@ -12,15 +13,24 @@ use cairo_lang_sierra::{ ids::ConcreteTypeId, program::{GenFunction, StatementIdx, TypeDeclaration}, }; +use rayon::iter::IntoParallelIterator; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; +use universal_sierra_compiler_api::representation::{AssembledCairoProgram, RawCasmProgram}; + +#[derive(Debug, Clone, Copy)] +pub enum TestSelectionMode<'a> { + All, + Exact(&'a str), +} #[tracing::instrument(skip_all, level = "debug")] pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, + test_selection_mode: TestSelectionMode<'_>, ) -> Result { macro_rules! by_id { ($field:ident) => {{ @@ -38,10 +48,6 @@ pub fn prepare_test_target( let funcs = by_id!(funcs); let type_declarations = by_id!(type_declarations); - let casm_program = Arc::new(compile_raw_sierra_at_path( - test_target_raw.sierra_program_path.as_std_path(), - )?); - let default_executables = vec![]; let executables = test_target_raw .sierra_program @@ -50,22 +56,54 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let test_cases = executables - .par_iter() - .map(|case| -> Result { - let func = funcs[&case.id]; + let exact_matches = match test_selection_mode.clone() { + TestSelectionMode::All => None, + TestSelectionMode::Exact(exact_match) => Some( + executables + .iter() + .filter_map(|case| { + let name: String = case.debug_name.clone().unwrap().into(); + (sanitize_test_case_name(&name) == exact_match).then_some((case, name)) + }) + .collect::>(), + ), + }; - let test_details = build_test_details(func, &type_declarations); + if exact_matches.as_ref().is_some_and(Vec::is_empty) { + return Ok(empty_test_target(test_target_raw)); + } - let raw_config = run_config_pass(&test_details, &casm_program, tracked_resource)?; + let casm_program = Arc::new(compile_raw_sierra_at_path( + test_target_raw.sierra_program_path.as_std_path(), + )?); - Ok(TestCaseWithConfig { - config: raw_config.into(), - name: case.debug_name.clone().unwrap().into(), - test_details, + let test_cases = if let Some(exact_matches) = exact_matches { + exact_matches + .into_par_iter() + .map(|(case, name)| { + build_test_case_with_config( + funcs[&case.id], + name, + &type_declarations, + &casm_program, + tracked_resource, + ) + }) + .collect::>()? + } else { + executables + .par_iter() + .map(|case| { + build_test_case_with_config( + funcs[&case.id], + case.debug_name.clone().unwrap().into(), + &type_declarations, + &casm_program, + tracked_resource, + ) }) - }) - .collect::>()?; + .collect::>()? + }; Ok(TestTargetWithConfig { tests_location: test_target_raw.tests_location, @@ -76,6 +114,39 @@ pub fn prepare_test_target( }) } +fn empty_test_target(test_target_raw: TestTargetRaw) -> TestTargetWithConfig { + TestTargetWithConfig { + tests_location: test_target_raw.tests_location, + test_cases: vec![], + sierra_program: test_target_raw.sierra_program, + sierra_program_path: test_target_raw.sierra_program_path.into(), + casm_program: Arc::new(RawCasmProgram { + assembled_cairo_program: AssembledCairoProgram { + bytecode: vec![], + hints: vec![], + }, + debug_info: vec![], + }), + } +} + +fn build_test_case_with_config( + func: &GenFunction, + name: String, + type_declarations: &HashMap, + casm_program: &Arc, + tracked_resource: &ForgeTrackedResource, +) -> Result { + let test_details = build_test_details(func, type_declarations); + let raw_config = run_config_pass(&test_details, casm_program, tracked_resource)?; + + Ok(TestCaseWithConfig { + config: raw_config.into(), + name, + test_details, + }) +} + #[tracing::instrument(skip_all, level = "debug")] fn build_test_details( func: &GenFunction, diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 326e5d5fe5..b8bb30d0fe 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -30,7 +30,7 @@ use forge_runner::{ with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, }, partition::PartitionConfig, - running::target::prepare_test_target, + running::target::{TestSelectionMode, prepare_test_target}, scarb::load_test_artifacts, test_case_summary::AnyTestCaseSummary, test_target_summary::TestTargetSummary, @@ -125,10 +125,14 @@ impl RunForPackageArgs { } let tracked_resource = forge_config.test_runner_config.tracked_resource; + let exact_sanitized_name = match &tests_filter.name_filter { + NameFilter::ExactMatch(name) => Some(name.clone()), + _ => None, + }; let target_handles = raw_test_targets .into_iter() - .map(|t| spawn_prepare_test_target(t, tracked_resource)) + .map(|t| spawn_prepare_test_target(t, tracked_resource, exact_sanitized_name.clone())) .collect(); Ok(RunForPackageArgs { @@ -145,8 +149,15 @@ impl RunForPackageArgs { fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, + exact_sanitized_name: Option, ) -> JoinHandle> { - tokio::task::spawn_blocking(move || prepare_test_target(target, &tracked_resource)) + tokio::task::spawn_blocking(move || { + let selection = match exact_sanitized_name.as_deref() { + Some(exact_match) => TestSelectionMode::Exact(exact_match), + None => TestSelectionMode::All, + }; + prepare_test_target(target, &tracked_resource, selection) + }) } fn sum_test_cases_from_test_target( diff --git a/crates/forge/tests/data/lazy_config_filtering/Scarb.toml b/crates/forge/tests/data/lazy_config_filtering/Scarb.toml new file mode 100644 index 0000000000..9fba9368a4 --- /dev/null +++ b/crates/forge/tests/data/lazy_config_filtering/Scarb.toml @@ -0,0 +1,13 @@ +[package] +name = "lazy_config_filtering" +version = "0.1.0" +edition = "2024_07" + +[dependencies] +starknet = "2.4.0" + +[dev-dependencies] +snforge_std = { path = "../../../../../snforge_std" } + +[[target.starknet-contract]] +sierra = true diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 6cfb706d65..6c9330ab1a 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -299,6 +299,30 @@ fn with_exact_filter() { ); } +#[test] +fn exact_filter_does_not_resolve_config_for_filtered_out_tests() { + let temp = setup_package("lazy_config_filtering"); + + let output = test_runner(&temp) + .args(["--exact", "lazy_config_filtering::tests::selected_exact"]) + .assert() + .success(); + + assert_stdout_contains( + output, + indoc! {r" + [..]Compiling[..] + [..]Finished[..] + + + Collected 1 test(s) from lazy_config_filtering package + Running 1 test(s) from src/ + [PASS] lazy_config_filtering::tests::selected_exact [..] + Tests: 1 passed, 0 failed, 0 ignored, other filtered out + "}, + ); +} + #[test] fn with_skip_filter_matching_module() { let temp = setup_package("simple_package"); diff --git a/crates/forge/tests/integration/setup_fork.rs b/crates/forge/tests/integration/setup_fork.rs index cd135c3b94..98efee273f 100644 --- a/crates/forge/tests/integration/setup_fork.rs +++ b/crates/forge/tests/integration/setup_fork.rs @@ -27,7 +27,7 @@ use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, OutputConfig, TestRunnerConfig, }; -use forge_runner::running::target::prepare_test_target; +use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata_for_dir; @@ -139,7 +139,11 @@ fn fork_aliased_decorator() { .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target(t, &ForgeTrackedResource::CairoSteps) + prepare_test_target( + t, + &ForgeTrackedResource::CairoSteps, + TestSelectionMode::All, + ) }) }) .collect(); @@ -245,7 +249,11 @@ fn fork_aliased_decorator_overrding() { .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target(t, &ForgeTrackedResource::CairoSteps) + prepare_test_target( + t, + &ForgeTrackedResource::CairoSteps, + TestSelectionMode::All, + ) }) }) .collect(); diff --git a/crates/forge/tests/utils/running_tests.rs b/crates/forge/tests/utils/running_tests.rs index 63318bbec6..c8ad9a1ab3 100644 --- a/crates/forge/tests/utils/running_tests.rs +++ b/crates/forge/tests/utils/running_tests.rs @@ -14,7 +14,7 @@ use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, ForgeTrackedResource, OutputConfig, TestRunnerConfig, }; use forge_runner::partition::PartitionConfig; -use forge_runner::running::target::prepare_test_target; +use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use forge_runner::test_target_summary::TestTargetSummary; use foundry_ui::UI; @@ -53,7 +53,11 @@ pub fn run_test_case( rt.block_on(async { let target_handles = raw_test_targets .into_iter() - .map(|t| tokio::task::spawn_blocking(move || prepare_test_target(t, &tracked_resource))) + .map(|t| { + tokio::task::spawn_blocking(move || { + prepare_test_target(t, &tracked_resource, TestSelectionMode::All) + }) + }) .collect(); run_for_package( RunForPackageArgs { From 6ce1dbb1b6a792bf2374e54cbde7196e92b605c0 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 14:30:27 +0200 Subject: [PATCH 02/30] Remove `exact_filter_does_not_resolve_config_for_filtered_out_tests` --- crates/forge/tests/e2e/running.rs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 6c9330ab1a..6cfb706d65 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -299,30 +299,6 @@ fn with_exact_filter() { ); } -#[test] -fn exact_filter_does_not_resolve_config_for_filtered_out_tests() { - let temp = setup_package("lazy_config_filtering"); - - let output = test_runner(&temp) - .args(["--exact", "lazy_config_filtering::tests::selected_exact"]) - .assert() - .success(); - - assert_stdout_contains( - output, - indoc! {r" - [..]Compiling[..] - [..]Finished[..] - - - Collected 1 test(s) from lazy_config_filtering package - Running 1 test(s) from src/ - [PASS] lazy_config_filtering::tests::selected_exact [..] - Tests: 1 passed, 0 failed, 0 ignored, other filtered out - "}, - ); -} - #[test] fn with_skip_filter_matching_module() { let temp = setup_package("simple_package"); From 33f62e122a4d47f60b2993c08b6a2b5401fb342a Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 14:31:07 +0200 Subject: [PATCH 03/30] Remove test package --- .../tests/data/lazy_config_filtering/Scarb.toml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 crates/forge/tests/data/lazy_config_filtering/Scarb.toml diff --git a/crates/forge/tests/data/lazy_config_filtering/Scarb.toml b/crates/forge/tests/data/lazy_config_filtering/Scarb.toml deleted file mode 100644 index 9fba9368a4..0000000000 --- a/crates/forge/tests/data/lazy_config_filtering/Scarb.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "lazy_config_filtering" -version = "0.1.0" -edition = "2024_07" - -[dependencies] -starknet = "2.4.0" - -[dev-dependencies] -snforge_std = { path = "../../../../../snforge_std" } - -[[target.starknet-contract]] -sierra = true From 0d4a06a1f4c52d57c531f17639a45ef0d5069f6a Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 14:45:07 +0200 Subject: [PATCH 04/30] Make self review --- crates/forge-runner/src/running/target.rs | 2 +- crates/forge/src/run_tests/package.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 761d808c2e..2c43c2b1ab 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -56,7 +56,7 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let exact_matches = match test_selection_mode.clone() { + let exact_matches = match test_selection_mode { TestSelectionMode::All => None, TestSelectionMode::Exact(exact_match) => Some( executables diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index b8bb30d0fe..e46eb9add6 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -125,14 +125,14 @@ impl RunForPackageArgs { } let tracked_resource = forge_config.test_runner_config.tracked_resource; - let exact_sanitized_name = match &tests_filter.name_filter { + let maybe_exact_name = match &tests_filter.name_filter { NameFilter::ExactMatch(name) => Some(name.clone()), _ => None, }; let target_handles = raw_test_targets .into_iter() - .map(|t| spawn_prepare_test_target(t, tracked_resource, exact_sanitized_name.clone())) + .map(|t| spawn_prepare_test_target(t, tracked_resource, maybe_exact_name.clone())) .collect(); Ok(RunForPackageArgs { @@ -149,10 +149,10 @@ impl RunForPackageArgs { fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, - exact_sanitized_name: Option, + maybe_exact_name: Option, ) -> JoinHandle> { tokio::task::spawn_blocking(move || { - let selection = match exact_sanitized_name.as_deref() { + let selection = match maybe_exact_name.as_deref() { Some(exact_match) => TestSelectionMode::Exact(exact_match), None => TestSelectionMode::All, }; From fb5392073cf1007040ae8333e7b0b5605a60bfe3 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 14:52:19 +0200 Subject: [PATCH 05/30] Add `exact_filter_does_not_resolve_config_for_filtered_out_tests` test --- .../data/lazy_config_filtering/Scarb.toml | 10 ++++++++ .../data/lazy_config_filtering/src/lib.cairo | 16 +++++++++++++ crates/forge/tests/e2e/running.rs | 24 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 crates/forge/tests/data/lazy_config_filtering/Scarb.toml create mode 100644 crates/forge/tests/data/lazy_config_filtering/src/lib.cairo diff --git a/crates/forge/tests/data/lazy_config_filtering/Scarb.toml b/crates/forge/tests/data/lazy_config_filtering/Scarb.toml new file mode 100644 index 0000000000..3b66cfc21d --- /dev/null +++ b/crates/forge/tests/data/lazy_config_filtering/Scarb.toml @@ -0,0 +1,10 @@ +[package] +name = "lazy_config_filtering" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +snforge_std = { path = "../../../../../snforge_std" } + +[scripts] +test = "snforge test" diff --git a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo new file mode 100644 index 0000000000..9a70f76604 --- /dev/null +++ b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo @@ -0,0 +1,16 @@ +#[cfg(test)] +mod tests { + #[test] + fn selected_exact() { + assert(true, 'ok'); + } + + // This config is intentionally invalid. + // When runninng with `--exact lazy_config_filtering::tests::selected_exact`, this test + // should be filtered out and thus not fail. + #[test] + #[fork("missing_fork")] + fn filtered_out_broken_config() { + assert(true, 'should never run'); + } +} diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 6cfb706d65..6c9330ab1a 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -299,6 +299,30 @@ fn with_exact_filter() { ); } +#[test] +fn exact_filter_does_not_resolve_config_for_filtered_out_tests() { + let temp = setup_package("lazy_config_filtering"); + + let output = test_runner(&temp) + .args(["--exact", "lazy_config_filtering::tests::selected_exact"]) + .assert() + .success(); + + assert_stdout_contains( + output, + indoc! {r" + [..]Compiling[..] + [..]Finished[..] + + + Collected 1 test(s) from lazy_config_filtering package + Running 1 test(s) from src/ + [PASS] lazy_config_filtering::tests::selected_exact [..] + Tests: 1 passed, 0 failed, 0 ignored, other filtered out + "}, + ); +} + #[test] fn with_skip_filter_matching_module() { let temp = setup_package("simple_package"); From 40240e80db6851c168020708ab088cfa397ae3a1 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 14:55:17 +0200 Subject: [PATCH 06/30] Fix lint --- crates/forge-runner/src/running/target.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 2c43c2b1ab..c5f4df3d9d 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -86,7 +86,7 @@ pub fn prepare_test_target( name, &type_declarations, &casm_program, - tracked_resource, + *tracked_resource, ) }) .collect::>()? @@ -99,7 +99,7 @@ pub fn prepare_test_target( case.debug_name.clone().unwrap().into(), &type_declarations, &casm_program, - tracked_resource, + *tracked_resource, ) }) .collect::>()? @@ -135,10 +135,10 @@ fn build_test_case_with_config( name: String, type_declarations: &HashMap, casm_program: &Arc, - tracked_resource: &ForgeTrackedResource, + tracked_resource: ForgeTrackedResource, ) -> Result { let test_details = build_test_details(func, type_declarations); - let raw_config = run_config_pass(&test_details, casm_program, tracked_resource)?; + let raw_config = run_config_pass(&test_details, casm_program, &tracked_resource)?; Ok(TestCaseWithConfig { config: raw_config.into(), From 283a8749e7d4bc46a814b89c39de46e0309958c3 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 15:04:34 +0200 Subject: [PATCH 07/30] Small improvements --- crates/forge-runner/src/running/target.rs | 13 +++++++------ crates/forge/src/run_tests/package.rs | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index c5f4df3d9d..4fb0aad999 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -23,7 +23,7 @@ use universal_sierra_compiler_api::representation::{AssembledCairoProgram, RawCa #[derive(Debug, Clone, Copy)] pub enum TestSelectionMode<'a> { All, - Exact(&'a str), + ExactMatch(&'a str), } #[tracing::instrument(skip_all, level = "debug")] @@ -58,12 +58,12 @@ pub fn prepare_test_target( let exact_matches = match test_selection_mode { TestSelectionMode::All => None, - TestSelectionMode::Exact(exact_match) => Some( + TestSelectionMode::ExactMatch(exact_match) => Some( executables .iter() - .filter_map(|case| { + .filter(|case| { let name: String = case.debug_name.clone().unwrap().into(); - (sanitize_test_case_name(&name) == exact_match).then_some((case, name)) + sanitize_test_case_name(&name) == exact_match }) .collect::>(), ), @@ -80,10 +80,10 @@ pub fn prepare_test_target( let test_cases = if let Some(exact_matches) = exact_matches { exact_matches .into_par_iter() - .map(|(case, name)| { + .map(|case| { build_test_case_with_config( funcs[&case.id], - name, + case.debug_name.clone().unwrap().into(), &type_declarations, &casm_program, *tracked_resource, @@ -115,6 +115,7 @@ pub fn prepare_test_target( } fn empty_test_target(test_target_raw: TestTargetRaw) -> TestTargetWithConfig { + // For non-matching `--exact` targets, return an empty test target. TestTargetWithConfig { tests_location: test_target_raw.tests_location, test_cases: vec![], diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index e46eb9add6..166b39d8b3 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -153,7 +153,7 @@ fn spawn_prepare_test_target( ) -> JoinHandle> { tokio::task::spawn_blocking(move || { let selection = match maybe_exact_name.as_deref() { - Some(exact_match) => TestSelectionMode::Exact(exact_match), + Some(exact_match) => TestSelectionMode::ExactMatch(exact_match), None => TestSelectionMode::All, }; prepare_test_target(target, &tracked_resource, selection) From 148824ec63a1f4ac6590f3b0813e9a3f50b493d1 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 15:17:51 +0200 Subject: [PATCH 08/30] Fix typo --- crates/forge/tests/data/lazy_config_filtering/src/lib.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo index 9a70f76604..465692af0e 100644 --- a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo +++ b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo @@ -6,7 +6,7 @@ mod tests { } // This config is intentionally invalid. - // When runninng with `--exact lazy_config_filtering::tests::selected_exact`, this test + // When running with `--exact lazy_config_filtering::tests::selected_exact`, this test // should be filtered out and thus not fail. #[test] #[fork("missing_fork")] From da14311214cfc7c2a22881a0d09ddeb0e0dae79b Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 21 Apr 2026 16:43:13 +0200 Subject: [PATCH 09/30] Format comment --- crates/forge/tests/data/lazy_config_filtering/src/lib.cairo | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo index 465692af0e..190ab5373e 100644 --- a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo +++ b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo @@ -5,9 +5,9 @@ mod tests { assert(true, 'ok'); } - // This config is intentionally invalid. - // When running with `--exact lazy_config_filtering::tests::selected_exact`, this test - // should be filtered out and thus not fail. + // This config is intentionally invalid. When running with + // `--exact lazy_config_filtering::tests::selected_exact`, this test should be + // filtered out and thus not fail. #[test] #[fork("missing_fork")] fn filtered_out_broken_config() { From fa6719fd751274a9cbb7772d273b3aebf7f4f3a4 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 22 Apr 2026 13:39:59 +0200 Subject: [PATCH 10/30] Extend logic for work with match too --- crates/forge-runner/src/running/target.rs | 36 ++++++++++++++----- crates/forge/src/run_tests/package.rs | 19 +++++----- crates/forge/src/test_filter.rs | 2 +- .../data/lazy_config_filtering/src/lib.cairo | 5 +++ crates/forge/tests/e2e/running.rs | 24 +++++++++++++ crates/forge/tests/integration/setup_fork.rs | 6 ++-- crates/forge/tests/utils/running_tests.rs | 4 +-- 7 files changed, 71 insertions(+), 25 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 4fb0aad999..50aa253cd2 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -21,8 +21,9 @@ use universal_sierra_compiler_api::compile_raw_sierra_at_path; use universal_sierra_compiler_api::representation::{AssembledCairoProgram, RawCasmProgram}; #[derive(Debug, Clone, Copy)] -pub enum TestSelectionMode<'a> { +pub enum TestNameSelection<'a> { All, + Match(&'a str), ExactMatch(&'a str), } @@ -30,7 +31,7 @@ pub enum TestSelectionMode<'a> { pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, - test_selection_mode: TestSelectionMode<'_>, + test_selection_mode: TestNameSelection<'_>, ) -> Result { macro_rules! by_id { ($field:ident) => {{ @@ -56,20 +57,37 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let exact_matches = match test_selection_mode { - TestSelectionMode::All => None, - TestSelectionMode::ExactMatch(exact_match) => Some( + let selected_cases = match test_selection_mode { + TestNameSelection::All => None, + TestNameSelection::Match(filter) => Some( executables .iter() .filter(|case| { - let name: String = case.debug_name.clone().unwrap().into(); + let name: String = case + .debug_name + .clone() + .expect("Failed to get test debug name") + .into(); + sanitize_test_case_name(&name).contains(filter) + }) + .collect::>(), + ), + TestNameSelection::ExactMatch(exact_match) => Some( + executables + .iter() + .filter(|case| { + let name: String = case + .debug_name + .clone() + .expect("Failed to get test debug name") + .into(); sanitize_test_case_name(&name) == exact_match }) .collect::>(), ), }; - if exact_matches.as_ref().is_some_and(Vec::is_empty) { + if selected_cases.as_ref().is_some_and(Vec::is_empty) { return Ok(empty_test_target(test_target_raw)); } @@ -77,8 +95,8 @@ pub fn prepare_test_target( test_target_raw.sierra_program_path.as_std_path(), )?); - let test_cases = if let Some(exact_matches) = exact_matches { - exact_matches + let test_cases = if let Some(selected_cases) = selected_cases { + selected_cases .into_par_iter() .map(|case| { build_test_case_with_config( diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 166b39d8b3..883eca8e13 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -30,7 +30,7 @@ use forge_runner::{ with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, }, partition::PartitionConfig, - running::target::{TestSelectionMode, prepare_test_target}, + running::target::{TestNameSelection, prepare_test_target}, scarb::load_test_artifacts, test_case_summary::AnyTestCaseSummary, test_target_summary::TestTargetSummary, @@ -125,14 +125,12 @@ impl RunForPackageArgs { } let tracked_resource = forge_config.test_runner_config.tracked_resource; - let maybe_exact_name = match &tests_filter.name_filter { - NameFilter::ExactMatch(name) => Some(name.clone()), - _ => None, - }; let target_handles = raw_test_targets .into_iter() - .map(|t| spawn_prepare_test_target(t, tracked_resource, maybe_exact_name.clone())) + .map(|t| { + spawn_prepare_test_target(t, tracked_resource, tests_filter.name_filter.clone()) + }) .collect(); Ok(RunForPackageArgs { @@ -149,12 +147,13 @@ impl RunForPackageArgs { fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, - maybe_exact_name: Option, + name_filter: NameFilter, ) -> JoinHandle> { tokio::task::spawn_blocking(move || { - let selection = match maybe_exact_name.as_deref() { - Some(exact_match) => TestSelectionMode::ExactMatch(exact_match), - None => TestSelectionMode::All, + let selection = match &name_filter { + NameFilter::All => TestNameSelection::All, + NameFilter::Match(filter) => TestNameSelection::Match(filter), + NameFilter::ExactMatch(exact_match) => TestNameSelection::ExactMatch(exact_match), }; prepare_test_target(target, &tracked_resource, selection) }) diff --git a/crates/forge/src/test_filter.rs b/crates/forge/src/test_filter.rs index 599aa3aa59..96cc6941df 100644 --- a/crates/forge/src/test_filter.rs +++ b/crates/forge/src/test_filter.rs @@ -23,7 +23,7 @@ pub struct TestsFilter { pub(crate) partitioning_config: PartitionConfig, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub(crate) enum NameFilter { All, Match(String), diff --git a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo index 190ab5373e..cae4cc1bb1 100644 --- a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo +++ b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo @@ -5,6 +5,11 @@ mod tests { assert(true, 'ok'); } + #[test] + fn selected_by_name_filter() { + assert(true, 'ok'); + } + // This config is intentionally invalid. When running with // `--exact lazy_config_filtering::tests::selected_exact`, this test should be // filtered out and thus not fail. diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 6c9330ab1a..c37bd760fb 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -323,6 +323,30 @@ fn exact_filter_does_not_resolve_config_for_filtered_out_tests() { ); } +#[test] +fn name_filter_does_not_resolve_config_for_filtered_out_tests() { + let temp = setup_package("lazy_config_filtering"); + + let output = test_runner(&temp) + .arg("selected_by_name_") + .assert() + .success(); + + assert_stdout_contains( + output, + indoc! {r" + [..]Compiling[..] + [..]Finished[..] + + + Collected 1 test(s) from lazy_config_filtering package + Running 1 test(s) from src/ + [PASS] lazy_config_filtering::tests::selected_by_name_filter [..] + Tests: 1 passed, 0 failed, 0 ignored, 2 filtered out + "}, + ); +} + #[test] fn with_skip_filter_matching_module() { let temp = setup_package("simple_package"); diff --git a/crates/forge/tests/integration/setup_fork.rs b/crates/forge/tests/integration/setup_fork.rs index 98efee273f..881f695423 100644 --- a/crates/forge/tests/integration/setup_fork.rs +++ b/crates/forge/tests/integration/setup_fork.rs @@ -27,7 +27,7 @@ use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, OutputConfig, TestRunnerConfig, }; -use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; +use forge_runner::running::target::{TestNameSelection, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata_for_dir; @@ -142,7 +142,7 @@ fn fork_aliased_decorator() { prepare_test_target( t, &ForgeTrackedResource::CairoSteps, - TestSelectionMode::All, + TestNameSelection::All, ) }) }) @@ -252,7 +252,7 @@ fn fork_aliased_decorator_overrding() { prepare_test_target( t, &ForgeTrackedResource::CairoSteps, - TestSelectionMode::All, + TestNameSelection::All, ) }) }) diff --git a/crates/forge/tests/utils/running_tests.rs b/crates/forge/tests/utils/running_tests.rs index c8ad9a1ab3..fb58962683 100644 --- a/crates/forge/tests/utils/running_tests.rs +++ b/crates/forge/tests/utils/running_tests.rs @@ -14,7 +14,7 @@ use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, ForgeTrackedResource, OutputConfig, TestRunnerConfig, }; use forge_runner::partition::PartitionConfig; -use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; +use forge_runner::running::target::{TestNameSelection, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use forge_runner::test_target_summary::TestTargetSummary; use foundry_ui::UI; @@ -55,7 +55,7 @@ pub fn run_test_case( .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target(t, &tracked_resource, TestSelectionMode::All) + prepare_test_target(t, &tracked_resource, TestNameSelection::All) }) }) .collect(); From f36729159d88d344454cc474483f1fe39177c7a8 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 22 Apr 2026 16:32:04 +0200 Subject: [PATCH 11/30] Revert "Extend logic for work with match too" This reverts commit fa6719fd751274a9cbb7772d273b3aebf7f4f3a4. --- crates/forge-runner/src/running/target.rs | 36 +++++-------------- crates/forge/src/run_tests/package.rs | 19 +++++----- crates/forge/src/test_filter.rs | 2 +- .../data/lazy_config_filtering/src/lib.cairo | 5 --- crates/forge/tests/e2e/running.rs | 24 ------------- crates/forge/tests/integration/setup_fork.rs | 6 ++-- crates/forge/tests/utils/running_tests.rs | 4 +-- 7 files changed, 25 insertions(+), 71 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 50aa253cd2..4fb0aad999 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -21,9 +21,8 @@ use universal_sierra_compiler_api::compile_raw_sierra_at_path; use universal_sierra_compiler_api::representation::{AssembledCairoProgram, RawCasmProgram}; #[derive(Debug, Clone, Copy)] -pub enum TestNameSelection<'a> { +pub enum TestSelectionMode<'a> { All, - Match(&'a str), ExactMatch(&'a str), } @@ -31,7 +30,7 @@ pub enum TestNameSelection<'a> { pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, - test_selection_mode: TestNameSelection<'_>, + test_selection_mode: TestSelectionMode<'_>, ) -> Result { macro_rules! by_id { ($field:ident) => {{ @@ -57,37 +56,20 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let selected_cases = match test_selection_mode { - TestNameSelection::All => None, - TestNameSelection::Match(filter) => Some( + let exact_matches = match test_selection_mode { + TestSelectionMode::All => None, + TestSelectionMode::ExactMatch(exact_match) => Some( executables .iter() .filter(|case| { - let name: String = case - .debug_name - .clone() - .expect("Failed to get test debug name") - .into(); - sanitize_test_case_name(&name).contains(filter) - }) - .collect::>(), - ), - TestNameSelection::ExactMatch(exact_match) => Some( - executables - .iter() - .filter(|case| { - let name: String = case - .debug_name - .clone() - .expect("Failed to get test debug name") - .into(); + let name: String = case.debug_name.clone().unwrap().into(); sanitize_test_case_name(&name) == exact_match }) .collect::>(), ), }; - if selected_cases.as_ref().is_some_and(Vec::is_empty) { + if exact_matches.as_ref().is_some_and(Vec::is_empty) { return Ok(empty_test_target(test_target_raw)); } @@ -95,8 +77,8 @@ pub fn prepare_test_target( test_target_raw.sierra_program_path.as_std_path(), )?); - let test_cases = if let Some(selected_cases) = selected_cases { - selected_cases + let test_cases = if let Some(exact_matches) = exact_matches { + exact_matches .into_par_iter() .map(|case| { build_test_case_with_config( diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 883eca8e13..166b39d8b3 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -30,7 +30,7 @@ use forge_runner::{ with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, }, partition::PartitionConfig, - running::target::{TestNameSelection, prepare_test_target}, + running::target::{TestSelectionMode, prepare_test_target}, scarb::load_test_artifacts, test_case_summary::AnyTestCaseSummary, test_target_summary::TestTargetSummary, @@ -125,12 +125,14 @@ impl RunForPackageArgs { } let tracked_resource = forge_config.test_runner_config.tracked_resource; + let maybe_exact_name = match &tests_filter.name_filter { + NameFilter::ExactMatch(name) => Some(name.clone()), + _ => None, + }; let target_handles = raw_test_targets .into_iter() - .map(|t| { - spawn_prepare_test_target(t, tracked_resource, tests_filter.name_filter.clone()) - }) + .map(|t| spawn_prepare_test_target(t, tracked_resource, maybe_exact_name.clone())) .collect(); Ok(RunForPackageArgs { @@ -147,13 +149,12 @@ impl RunForPackageArgs { fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, - name_filter: NameFilter, + maybe_exact_name: Option, ) -> JoinHandle> { tokio::task::spawn_blocking(move || { - let selection = match &name_filter { - NameFilter::All => TestNameSelection::All, - NameFilter::Match(filter) => TestNameSelection::Match(filter), - NameFilter::ExactMatch(exact_match) => TestNameSelection::ExactMatch(exact_match), + let selection = match maybe_exact_name.as_deref() { + Some(exact_match) => TestSelectionMode::ExactMatch(exact_match), + None => TestSelectionMode::All, }; prepare_test_target(target, &tracked_resource, selection) }) diff --git a/crates/forge/src/test_filter.rs b/crates/forge/src/test_filter.rs index 96cc6941df..599aa3aa59 100644 --- a/crates/forge/src/test_filter.rs +++ b/crates/forge/src/test_filter.rs @@ -23,7 +23,7 @@ pub struct TestsFilter { pub(crate) partitioning_config: PartitionConfig, } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq)] pub(crate) enum NameFilter { All, Match(String), diff --git a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo index cae4cc1bb1..190ab5373e 100644 --- a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo +++ b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo @@ -5,11 +5,6 @@ mod tests { assert(true, 'ok'); } - #[test] - fn selected_by_name_filter() { - assert(true, 'ok'); - } - // This config is intentionally invalid. When running with // `--exact lazy_config_filtering::tests::selected_exact`, this test should be // filtered out and thus not fail. diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index c37bd760fb..6c9330ab1a 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -323,30 +323,6 @@ fn exact_filter_does_not_resolve_config_for_filtered_out_tests() { ); } -#[test] -fn name_filter_does_not_resolve_config_for_filtered_out_tests() { - let temp = setup_package("lazy_config_filtering"); - - let output = test_runner(&temp) - .arg("selected_by_name_") - .assert() - .success(); - - assert_stdout_contains( - output, - indoc! {r" - [..]Compiling[..] - [..]Finished[..] - - - Collected 1 test(s) from lazy_config_filtering package - Running 1 test(s) from src/ - [PASS] lazy_config_filtering::tests::selected_by_name_filter [..] - Tests: 1 passed, 0 failed, 0 ignored, 2 filtered out - "}, - ); -} - #[test] fn with_skip_filter_matching_module() { let temp = setup_package("simple_package"); diff --git a/crates/forge/tests/integration/setup_fork.rs b/crates/forge/tests/integration/setup_fork.rs index 881f695423..98efee273f 100644 --- a/crates/forge/tests/integration/setup_fork.rs +++ b/crates/forge/tests/integration/setup_fork.rs @@ -27,7 +27,7 @@ use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, OutputConfig, TestRunnerConfig, }; -use forge_runner::running::target::{TestNameSelection, prepare_test_target}; +use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata_for_dir; @@ -142,7 +142,7 @@ fn fork_aliased_decorator() { prepare_test_target( t, &ForgeTrackedResource::CairoSteps, - TestNameSelection::All, + TestSelectionMode::All, ) }) }) @@ -252,7 +252,7 @@ fn fork_aliased_decorator_overrding() { prepare_test_target( t, &ForgeTrackedResource::CairoSteps, - TestNameSelection::All, + TestSelectionMode::All, ) }) }) diff --git a/crates/forge/tests/utils/running_tests.rs b/crates/forge/tests/utils/running_tests.rs index fb58962683..c8ad9a1ab3 100644 --- a/crates/forge/tests/utils/running_tests.rs +++ b/crates/forge/tests/utils/running_tests.rs @@ -14,7 +14,7 @@ use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, ForgeTrackedResource, OutputConfig, TestRunnerConfig, }; use forge_runner::partition::PartitionConfig; -use forge_runner::running::target::{TestNameSelection, prepare_test_target}; +use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use forge_runner::test_target_summary::TestTargetSummary; use foundry_ui::UI; @@ -55,7 +55,7 @@ pub fn run_test_case( .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target(t, &tracked_resource, TestNameSelection::All) + prepare_test_target(t, &tracked_resource, TestSelectionMode::All) }) }) .collect(); From 513ac0175324ce65956b14d9577f1fe74ea383dd Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 22 Apr 2026 16:34:04 +0200 Subject: [PATCH 12/30] Reorder `prepare_test_target` body --- crates/forge-runner/src/running/target.rs | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 4fb0aad999..cbad08473c 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -32,22 +32,6 @@ pub fn prepare_test_target( tracked_resource: &ForgeTrackedResource, test_selection_mode: TestSelectionMode<'_>, ) -> Result { - macro_rules! by_id { - ($field:ident) => {{ - let temp: HashMap<_, _> = test_target_raw - .sierra_program - .program - .$field - .iter() - .map(|f| (f.id.id, f)) - .collect(); - - temp - }}; - } - let funcs = by_id!(funcs); - let type_declarations = by_id!(type_declarations); - let default_executables = vec![]; let executables = test_target_raw .sierra_program @@ -73,6 +57,22 @@ pub fn prepare_test_target( return Ok(empty_test_target(test_target_raw)); } + macro_rules! by_id { + ($field:ident) => {{ + let temp: HashMap<_, _> = test_target_raw + .sierra_program + .program + .$field + .iter() + .map(|f| (f.id.id, f)) + .collect(); + + temp + }}; + } + let funcs = by_id!(funcs); + let type_declarations = by_id!(type_declarations); + let casm_program = Arc::new(compile_raw_sierra_at_path( test_target_raw.sierra_program_path.as_std_path(), )?); From 8086a48ef2df47830082e82416d8039fc2a88d09 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 22 Apr 2026 16:36:14 +0200 Subject: [PATCH 13/30] Rename variables --- crates/forge-runner/src/running/target.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index cbad08473c..094bbda340 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -40,7 +40,7 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let exact_matches = match test_selection_mode { + let selected_test_cases = match test_selection_mode { TestSelectionMode::All => None, TestSelectionMode::ExactMatch(exact_match) => Some( executables @@ -53,7 +53,7 @@ pub fn prepare_test_target( ), }; - if exact_matches.as_ref().is_some_and(Vec::is_empty) { + if selected_test_cases.as_ref().is_some_and(Vec::is_empty) { return Ok(empty_test_target(test_target_raw)); } @@ -77,7 +77,7 @@ pub fn prepare_test_target( test_target_raw.sierra_program_path.as_std_path(), )?); - let test_cases = if let Some(exact_matches) = exact_matches { + let test_cases = if let Some(exact_matches) = selected_test_cases { exact_matches .into_par_iter() .map(|case| { From 72cb08b69768c9b4a339f282828ef2831a270304 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 22 Apr 2026 17:26:16 +0200 Subject: [PATCH 14/30] Update comment --- crates/forge-runner/src/running/target.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 094bbda340..002ddf6413 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -46,7 +46,11 @@ pub fn prepare_test_target( executables .iter() .filter(|case| { - let name: String = case.debug_name.clone().unwrap().into(); + let name: String = case + .debug_name + .clone() + .expect("Failed to get test case name") + .into(); sanitize_test_case_name(&name) == exact_match }) .collect::>(), @@ -83,7 +87,10 @@ pub fn prepare_test_target( .map(|case| { build_test_case_with_config( funcs[&case.id], - case.debug_name.clone().unwrap().into(), + case.debug_name + .clone() + .expect("Failed to get test case name") + .into(), &type_declarations, &casm_program, *tracked_resource, @@ -96,7 +103,10 @@ pub fn prepare_test_target( .map(|case| { build_test_case_with_config( funcs[&case.id], - case.debug_name.clone().unwrap().into(), + case.debug_name + .clone() + .expect("Failed to get test case name") + .into(), &type_declarations, &casm_program, *tracked_resource, @@ -114,8 +124,8 @@ pub fn prepare_test_target( }) } +/// For non-matching name-selected targets, return an empty test target; its CASM is never used. fn empty_test_target(test_target_raw: TestTargetRaw) -> TestTargetWithConfig { - // For non-matching `--exact` targets, return an empty test target. TestTargetWithConfig { tests_location: test_target_raw.tests_location, test_cases: vec![], From 3c50e06dda6faf14bc53e09a3c01b12dec6a90c3 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 22 Apr 2026 17:33:25 +0200 Subject: [PATCH 15/30] Rename `TestSelectionMode` -> `TestNameSelection` --- crates/forge-runner/src/running/target.rs | 10 +++++----- crates/forge/src/run_tests/package.rs | 6 +++--- crates/forge/tests/integration/setup_fork.rs | 6 +++--- crates/forge/tests/utils/running_tests.rs | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 002ddf6413..412bd8e86c 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -21,7 +21,7 @@ use universal_sierra_compiler_api::compile_raw_sierra_at_path; use universal_sierra_compiler_api::representation::{AssembledCairoProgram, RawCasmProgram}; #[derive(Debug, Clone, Copy)] -pub enum TestSelectionMode<'a> { +pub enum TestNameSelection<'a> { All, ExactMatch(&'a str), } @@ -30,7 +30,7 @@ pub enum TestSelectionMode<'a> { pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, - test_selection_mode: TestSelectionMode<'_>, + test_name_selection: TestNameSelection<'_>, ) -> Result { let default_executables = vec![]; let executables = test_target_raw @@ -40,9 +40,9 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let selected_test_cases = match test_selection_mode { - TestSelectionMode::All => None, - TestSelectionMode::ExactMatch(exact_match) => Some( + let selected_test_cases = match test_name_selection { + TestNameSelection::All => None, + TestNameSelection::ExactMatch(exact_match) => Some( executables .iter() .filter(|case| { diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 166b39d8b3..f3b73ecb60 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -30,7 +30,7 @@ use forge_runner::{ with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, }, partition::PartitionConfig, - running::target::{TestSelectionMode, prepare_test_target}, + running::target::{TestNameSelection, prepare_test_target}, scarb::load_test_artifacts, test_case_summary::AnyTestCaseSummary, test_target_summary::TestTargetSummary, @@ -153,8 +153,8 @@ fn spawn_prepare_test_target( ) -> JoinHandle> { tokio::task::spawn_blocking(move || { let selection = match maybe_exact_name.as_deref() { - Some(exact_match) => TestSelectionMode::ExactMatch(exact_match), - None => TestSelectionMode::All, + Some(exact_match) => TestNameSelection::ExactMatch(exact_match), + None => TestNameSelection::All, }; prepare_test_target(target, &tracked_resource, selection) }) diff --git a/crates/forge/tests/integration/setup_fork.rs b/crates/forge/tests/integration/setup_fork.rs index 98efee273f..881f695423 100644 --- a/crates/forge/tests/integration/setup_fork.rs +++ b/crates/forge/tests/integration/setup_fork.rs @@ -27,7 +27,7 @@ use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, OutputConfig, TestRunnerConfig, }; -use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; +use forge_runner::running::target::{TestNameSelection, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata_for_dir; @@ -142,7 +142,7 @@ fn fork_aliased_decorator() { prepare_test_target( t, &ForgeTrackedResource::CairoSteps, - TestSelectionMode::All, + TestNameSelection::All, ) }) }) @@ -252,7 +252,7 @@ fn fork_aliased_decorator_overrding() { prepare_test_target( t, &ForgeTrackedResource::CairoSteps, - TestSelectionMode::All, + TestNameSelection::All, ) }) }) diff --git a/crates/forge/tests/utils/running_tests.rs b/crates/forge/tests/utils/running_tests.rs index c8ad9a1ab3..fb58962683 100644 --- a/crates/forge/tests/utils/running_tests.rs +++ b/crates/forge/tests/utils/running_tests.rs @@ -14,7 +14,7 @@ use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, ForgeTrackedResource, OutputConfig, TestRunnerConfig, }; use forge_runner::partition::PartitionConfig; -use forge_runner::running::target::{TestSelectionMode, prepare_test_target}; +use forge_runner::running::target::{TestNameSelection, prepare_test_target}; use forge_runner::scarb::load_test_artifacts; use forge_runner::test_target_summary::TestTargetSummary; use foundry_ui::UI; @@ -55,7 +55,7 @@ pub fn run_test_case( .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target(t, &tracked_resource, TestSelectionMode::All) + prepare_test_target(t, &tracked_resource, TestNameSelection::All) }) }) .collect(); From 82b81982533e05bdb76c806f399237a22855a6ab Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 29 Apr 2026 16:35:36 +0200 Subject: [PATCH 16/30] Apply review suggestion --- crates/forge-runner/src/running/target.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 412bd8e86c..bb5bb8ef93 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -45,13 +45,10 @@ pub fn prepare_test_target( TestNameSelection::ExactMatch(exact_match) => Some( executables .iter() - .filter(|case| { - let name: String = case - .debug_name - .clone() - .expect("Failed to get test case name") - .into(); - sanitize_test_case_name(&name) == exact_match + .filter_map(|case| { + let raw_name: String = case.debug_name.clone()?.into(); + (sanitize_test_case_name(&raw_name) == exact_match) + .then_some((&case.id, raw_name)) }) .collect::>(), ), @@ -84,13 +81,10 @@ pub fn prepare_test_target( let test_cases = if let Some(exact_matches) = selected_test_cases { exact_matches .into_par_iter() - .map(|case| { + .map(|(id, name)| { build_test_case_with_config( - funcs[&case.id], - case.debug_name - .clone() - .expect("Failed to get test case name") - .into(), + funcs[id], + name, &type_declarations, &casm_program, *tracked_resource, From 0660f4142e3e816c4e45f8715cf0c08fea0cde0d Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Fri, 1 May 2026 07:45:16 +0200 Subject: [PATCH 17/30] Refactor name filter --- crates/forge-runner/src/filtering.rs | 72 ++++++++++++++++++++ crates/forge-runner/src/running/target.rs | 33 ++++----- crates/forge/src/run_tests/package.rs | 20 ++---- crates/forge/src/test_filter.rs | 35 ++-------- crates/forge/tests/integration/setup_fork.rs | 15 ++-- crates/forge/tests/utils/running_tests.rs | 5 +- 6 files changed, 104 insertions(+), 76 deletions(-) diff --git a/crates/forge-runner/src/filtering.rs b/crates/forge-runner/src/filtering.rs index 052cc080a9..1eb8489bb2 100644 --- a/crates/forge-runner/src/filtering.rs +++ b/crates/forge-runner/src/filtering.rs @@ -1,5 +1,45 @@ use crate::package_tests::TestCase; +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NameFilter { + All, + Match(String), + ExactMatch(String), +} + +impl NameFilter { + #[must_use] + pub fn from_flags(test_name_filter: Option, exact_match: bool) -> Self { + if exact_match { + Self::ExactMatch( + test_name_filter + .expect("Argument test_name_filter cannot be None with exact_match"), + ) + } else if let Some(name) = test_name_filter { + Self::Match(name) + } else { + Self::All + } + } + + #[must_use] + pub fn matches(&self, sanitized_name: &str) -> bool { + match self { + Self::All => true, + Self::Match(filter) => sanitized_name.contains(filter), + Self::ExactMatch(name) => sanitized_name == name, + } + } + + #[must_use] + pub fn exact_match(&self) -> Option<&str> { + match self { + Self::ExactMatch(name) => Some(name), + Self::All | Self::Match(_) => None, + } + } +} + /// Result of filtering a test case. #[derive(Debug)] pub enum FilterResult { @@ -27,3 +67,35 @@ pub trait TestCaseFilter { pub trait TestCaseIsIgnored { fn is_ignored(&self) -> bool; } + +#[cfg(test)] +mod tests { + use super::NameFilter; + + #[test] + fn name_filter_all_matches_everything() { + assert!(NameFilter::All.matches("any::test")); + } + + #[test] + fn name_filter_match_uses_substring_matching() { + assert!(NameFilter::Match("selected".to_string()).matches("pkg::selected_test")); + assert!(!NameFilter::Match("other".to_string()).matches("pkg::selected_test")); + } + + #[test] + fn name_filter_exact_match_uses_equality() { + assert!(NameFilter::ExactMatch("pkg::test".to_string()).matches("pkg::test")); + assert!(!NameFilter::ExactMatch("pkg::test".to_string()).matches("pkg::test_case")); + } + + #[test] + fn name_filter_exact_match_helper_returns_only_exact_filter() { + assert_eq!( + NameFilter::ExactMatch("pkg::test".to_string()).exact_match(), + Some("pkg::test") + ); + assert_eq!(NameFilter::All.exact_match(), None); + assert_eq!(NameFilter::Match("pkg".to_string()).exact_match(), None); + } +} diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index bb5bb8ef93..7b9775fcbe 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -1,4 +1,5 @@ use crate::{ + filtering::NameFilter, forge_config::ForgeTrackedResource, package_tests::{ TestDetails, @@ -20,17 +21,11 @@ use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; use universal_sierra_compiler_api::representation::{AssembledCairoProgram, RawCasmProgram}; -#[derive(Debug, Clone, Copy)] -pub enum TestNameSelection<'a> { - All, - ExactMatch(&'a str), -} - #[tracing::instrument(skip_all, level = "debug")] pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, - test_name_selection: TestNameSelection<'_>, + name_filter: &NameFilter, ) -> Result { let default_executables = vec![]; let executables = test_target_raw @@ -40,19 +35,17 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let selected_test_cases = match test_name_selection { - TestNameSelection::All => None, - TestNameSelection::ExactMatch(exact_match) => Some( - executables - .iter() - .filter_map(|case| { - let raw_name: String = case.debug_name.clone()?.into(); - (sanitize_test_case_name(&raw_name) == exact_match) - .then_some((&case.id, raw_name)) - }) - .collect::>(), - ), - }; + let selected_test_cases = name_filter.exact_match().map(|exact_match| { + executables + .iter() + .filter_map(|case| { + let raw_name: String = case.debug_name.clone()?.into(); + (name_filter.matches(&sanitize_test_case_name(&raw_name)) + && sanitize_test_case_name(&raw_name) == exact_match) + .then_some((&case.id, raw_name)) + }) + .collect::>() + }); if selected_test_cases.as_ref().is_some_and(Vec::is_empty) { return Ok(empty_test_target(test_target_raw)); diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index f3b73ecb60..62cfb4b768 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -15,7 +15,7 @@ use crate::{ tests_summary::TestsSummaryMessage, }, shared_cache::FailedTestsCache, - test_filter::{NameFilter, TestsFilter}, + test_filter::TestsFilter, warn::warn_if_incompatible_rpc_version, }; use anyhow::Result; @@ -23,6 +23,7 @@ use camino::{Utf8Path, Utf8PathBuf}; use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData; use console::Style; use forge_runner::{ + filtering::NameFilter, forge_config::{ForgeConfig, ForgeTrackedResource}, package_tests::{ raw::TestTargetRaw, @@ -30,7 +31,7 @@ use forge_runner::{ with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, }, partition::PartitionConfig, - running::target::{TestNameSelection, prepare_test_target}, + running::target::prepare_test_target, scarb::load_test_artifacts, test_case_summary::AnyTestCaseSummary, test_target_summary::TestTargetSummary, @@ -125,14 +126,11 @@ impl RunForPackageArgs { } let tracked_resource = forge_config.test_runner_config.tracked_resource; - let maybe_exact_name = match &tests_filter.name_filter { - NameFilter::ExactMatch(name) => Some(name.clone()), - _ => None, - }; + let name_filter = tests_filter.name_filter.clone(); let target_handles = raw_test_targets .into_iter() - .map(|t| spawn_prepare_test_target(t, tracked_resource, maybe_exact_name.clone())) + .map(|t| spawn_prepare_test_target(t, tracked_resource, name_filter.clone())) .collect(); Ok(RunForPackageArgs { @@ -149,14 +147,10 @@ impl RunForPackageArgs { fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, - maybe_exact_name: Option, + name_filter: NameFilter, ) -> JoinHandle> { tokio::task::spawn_blocking(move || { - let selection = match maybe_exact_name.as_deref() { - Some(exact_match) => TestNameSelection::ExactMatch(exact_match), - None => TestNameSelection::All, - }; - prepare_test_target(target, &tracked_resource, selection) + prepare_test_target(target, &tracked_resource, &name_filter) }) } diff --git a/crates/forge/src/test_filter.rs b/crates/forge/src/test_filter.rs index 599aa3aa59..a829a3f2f9 100644 --- a/crates/forge/src/test_filter.rs +++ b/crates/forge/src/test_filter.rs @@ -1,6 +1,8 @@ use crate::shared_cache::FailedTestsCache; use anyhow::Result; -use forge_runner::filtering::{ExcludeReason, FilterResult, TestCaseFilter, TestCaseIsIgnored}; +use forge_runner::filtering::{ + ExcludeReason, FilterResult, NameFilter, TestCaseFilter, TestCaseIsIgnored, +}; use forge_runner::package_tests::TestCase; use forge_runner::package_tests::with_config_resolved::{ TestCaseWithResolvedConfig, sanitize_test_case_name, @@ -23,13 +25,6 @@ pub struct TestsFilter { pub(crate) partitioning_config: PartitionConfig, } -#[derive(Debug, PartialEq)] -pub(crate) enum NameFilter { - All, - Match(String), - ExactMatch(String), -} - #[derive(Debug, PartialEq)] pub enum IgnoredFilter { IncludeAll, @@ -64,19 +59,8 @@ impl TestsFilter { IgnoredFilter::ExcludeIgnored }; - let name_filter = if exact_match { - NameFilter::ExactMatch( - test_name_filter - .expect("Argument test_name_filter cannot be None with exact_match"), - ) - } else if let Some(name) = test_name_filter { - NameFilter::Match(name) - } else { - NameFilter::All - }; - Self { - name_filter, + name_filter: NameFilter::from_flags(test_name_filter, exact_match), ignored_filter, last_failed_filter: rerun_failed, skip_filter: skip, @@ -104,16 +88,7 @@ impl TestsFilter { &self, test_cases: &mut Vec, ) -> Result<()> { - match &self.name_filter { - NameFilter::All => {} - NameFilter::Match(filter) => { - test_cases.retain(|tc| tc.name.contains(filter)); - } - - NameFilter::ExactMatch(name) => { - test_cases.retain(|tc| tc.name == *name); - } - } + test_cases.retain(|tc| self.name_filter.matches(&tc.name)); if self.last_failed_filter { match self.failed_tests_cache.load()?.as_slice() { diff --git a/crates/forge/tests/integration/setup_fork.rs b/crates/forge/tests/integration/setup_fork.rs index 881f695423..00d0a69a07 100644 --- a/crates/forge/tests/integration/setup_fork.rs +++ b/crates/forge/tests/integration/setup_fork.rs @@ -23,11 +23,12 @@ use forge::run_tests::package::RunForPackageArgs; use forge::shared_cache::FailedTestsCache; use forge_runner::CACHE_DIR; use forge_runner::debugging::TraceArgs; +use forge_runner::filtering::NameFilter; use forge_runner::forge_config::ForgeTrackedResource; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, OutputConfig, TestRunnerConfig, }; -use forge_runner::running::target::{TestNameSelection, prepare_test_target}; +use forge_runner::running::target::prepare_test_target; use forge_runner::scarb::load_test_artifacts; use scarb_api::ScarbCommand; use scarb_api::metadata::metadata_for_dir; @@ -139,11 +140,7 @@ fn fork_aliased_decorator() { .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target( - t, - &ForgeTrackedResource::CairoSteps, - TestNameSelection::All, - ) + prepare_test_target(t, &ForgeTrackedResource::CairoSteps, &NameFilter::All) }) }) .collect(); @@ -249,11 +246,7 @@ fn fork_aliased_decorator_overrding() { .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target( - t, - &ForgeTrackedResource::CairoSteps, - TestNameSelection::All, - ) + prepare_test_target(t, &ForgeTrackedResource::CairoSteps, &NameFilter::All) }) }) .collect(); diff --git a/crates/forge/tests/utils/running_tests.rs b/crates/forge/tests/utils/running_tests.rs index fb58962683..c443e5f976 100644 --- a/crates/forge/tests/utils/running_tests.rs +++ b/crates/forge/tests/utils/running_tests.rs @@ -10,11 +10,12 @@ use forge::{ }; use forge_runner::CACHE_DIR; use forge_runner::debugging::TraceArgs; +use forge_runner::filtering::NameFilter; use forge_runner::forge_config::{ ExecutionDataToSave, ForgeConfig, ForgeTrackedResource, OutputConfig, TestRunnerConfig, }; use forge_runner::partition::PartitionConfig; -use forge_runner::running::target::{TestNameSelection, prepare_test_target}; +use forge_runner::running::target::prepare_test_target; use forge_runner::scarb::load_test_artifacts; use forge_runner::test_target_summary::TestTargetSummary; use foundry_ui::UI; @@ -55,7 +56,7 @@ pub fn run_test_case( .into_iter() .map(|t| { tokio::task::spawn_blocking(move || { - prepare_test_target(t, &tracked_resource, TestNameSelection::All) + prepare_test_target(t, &tracked_resource, &NameFilter::All) }) }) .collect(); From 1bcb710b1bde6761454353c85367427b883ee039 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 01:03:22 +0200 Subject: [PATCH 18/30] Return `Option` from `prepare_test_target` on no matches --- crates/forge-runner/src/running/target.rs | 27 +++++------------------ crates/forge/src/run_tests/package.rs | 8 ++++--- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 7b9775fcbe..7f40f7d5fb 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -19,14 +19,14 @@ use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; -use universal_sierra_compiler_api::representation::{AssembledCairoProgram, RawCasmProgram}; +use universal_sierra_compiler_api::representation::RawCasmProgram; #[tracing::instrument(skip_all, level = "debug")] pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, name_filter: &NameFilter, -) -> Result { +) -> Result> { let default_executables = vec![]; let executables = test_target_raw .sierra_program @@ -48,7 +48,7 @@ pub fn prepare_test_target( }); if selected_test_cases.as_ref().is_some_and(Vec::is_empty) { - return Ok(empty_test_target(test_target_raw)); + return Ok(None); } macro_rules! by_id { @@ -102,30 +102,13 @@ pub fn prepare_test_target( .collect::>()? }; - Ok(TestTargetWithConfig { + Ok(Some(TestTargetWithConfig { tests_location: test_target_raw.tests_location, test_cases, sierra_program: test_target_raw.sierra_program, sierra_program_path: test_target_raw.sierra_program_path.into(), casm_program, - }) -} - -/// For non-matching name-selected targets, return an empty test target; its CASM is never used. -fn empty_test_target(test_target_raw: TestTargetRaw) -> TestTargetWithConfig { - TestTargetWithConfig { - tests_location: test_target_raw.tests_location, - test_cases: vec![], - sierra_program: test_target_raw.sierra_program, - sierra_program_path: test_target_raw.sierra_program_path.into(), - casm_program: Arc::new(RawCasmProgram { - assembled_cairo_program: AssembledCairoProgram { - bytecode: vec![], - hints: vec![], - }, - debug_info: vec![], - }), - } + })) } fn build_test_case_with_config( diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 62cfb4b768..d23181b7c1 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -68,7 +68,7 @@ impl PackageTestResult { } pub struct RunForPackageArgs { - pub target_handles: Vec>>, + pub target_handles: Vec>>>, pub tests_filter: TestsFilter, pub forge_config: Arc, pub fork_targets: Vec, @@ -148,7 +148,7 @@ fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, name_filter: NameFilter, -) -> JoinHandle> { +) -> JoinHandle>> { tokio::task::spawn_blocking(move || { prepare_test_target(target, &tracked_resource, &name_filter) }) @@ -195,7 +195,9 @@ pub async fn run_for_package( let mut not_filtered_total = 0; for handle in target_handles { - let target_with_config = handle.await??; + let Some(target_with_config) = handle.await?? else { + continue; + }; let mut resolved = resolve_config( target_with_config, From 40c77f8f7359b6ff3b086b9c852608ba960bfc21 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 01:07:44 +0200 Subject: [PATCH 19/30] Guard filtered-out test against silent pass --- crates/forge/tests/data/lazy_config_filtering/src/lib.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo index 190ab5373e..aefa3ce104 100644 --- a/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo +++ b/crates/forge/tests/data/lazy_config_filtering/src/lib.cairo @@ -11,6 +11,6 @@ mod tests { #[test] #[fork("missing_fork")] fn filtered_out_broken_config() { - assert(true, 'should never run'); + assert(false, 'should never run'); } } From 1d1b3409f952783fb1643d90e64f32dcca10e84a Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 01:10:19 +0200 Subject: [PATCH 20/30] Simplify test case selection by using `matches` for all filter types --- crates/forge-runner/src/running/target.rs | 65 ++++++++--------------- 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 7f40f7d5fb..7adcaae789 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -15,7 +15,6 @@ use cairo_lang_sierra::{ program::{GenFunction, StatementIdx, TypeDeclaration}, }; use rayon::iter::IntoParallelIterator; -use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; @@ -35,19 +34,17 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let selected_test_cases = name_filter.exact_match().map(|exact_match| { - executables - .iter() - .filter_map(|case| { - let raw_name: String = case.debug_name.clone()?.into(); - (name_filter.matches(&sanitize_test_case_name(&raw_name)) - && sanitize_test_case_name(&raw_name) == exact_match) - .then_some((&case.id, raw_name)) - }) - .collect::>() - }); + let selected_test_cases: Vec<_> = executables + .iter() + .filter_map(|case| { + let raw_name: String = case.debug_name.clone()?.into(); + name_filter + .matches(&sanitize_test_case_name(&raw_name)) + .then_some((&case.id, raw_name)) + }) + .collect(); - if selected_test_cases.as_ref().is_some_and(Vec::is_empty) { + if selected_test_cases.is_empty() { return Ok(None); } @@ -71,36 +68,18 @@ pub fn prepare_test_target( test_target_raw.sierra_program_path.as_std_path(), )?); - let test_cases = if let Some(exact_matches) = selected_test_cases { - exact_matches - .into_par_iter() - .map(|(id, name)| { - build_test_case_with_config( - funcs[id], - name, - &type_declarations, - &casm_program, - *tracked_resource, - ) - }) - .collect::>()? - } else { - executables - .par_iter() - .map(|case| { - build_test_case_with_config( - funcs[&case.id], - case.debug_name - .clone() - .expect("Failed to get test case name") - .into(), - &type_declarations, - &casm_program, - *tracked_resource, - ) - }) - .collect::>()? - }; + let test_cases = selected_test_cases + .into_par_iter() + .map(|(id, name)| { + build_test_case_with_config( + funcs[id], + name, + &type_declarations, + &casm_program, + *tracked_resource, + ) + }) + .collect::>()?; Ok(Some(TestTargetWithConfig { tests_location: test_target_raw.tests_location, From c445e987b54e33633f2ebd59ef48dfa13da2709a Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 09:24:26 +0200 Subject: [PATCH 21/30] Fix filtered out count regression for `Match` filter --- crates/forge-runner/src/running/target.rs | 68 +++++++++++++++-------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 7adcaae789..20bb0bd165 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -15,6 +15,7 @@ use cairo_lang_sierra::{ program::{GenFunction, StatementIdx, TypeDeclaration}, }; use rayon::iter::IntoParallelIterator; +use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; @@ -34,17 +35,22 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let selected_test_cases: Vec<_> = executables - .iter() - .filter_map(|case| { - let raw_name: String = case.debug_name.clone()?.into(); - name_filter - .matches(&sanitize_test_case_name(&raw_name)) - .then_some((&case.id, raw_name)) - }) - .collect(); + // For exact matching, pre-filter here to skip CASM compilation entirely when + // no test in this target matches. For other filters, all test cases must flow + // through resolve_config so that the "filtered out" count stays correct. + let exact_matches = name_filter.exact_match().map(|_| { + executables + .iter() + .filter_map(|case| { + let raw_name: String = case.debug_name.clone()?.into(); + name_filter + .matches(&sanitize_test_case_name(&raw_name)) + .then_some((&case.id, raw_name)) + }) + .collect::>() + }); - if selected_test_cases.is_empty() { + if exact_matches.as_ref().is_some_and(Vec::is_empty) { return Ok(None); } @@ -68,18 +74,36 @@ pub fn prepare_test_target( test_target_raw.sierra_program_path.as_std_path(), )?); - let test_cases = selected_test_cases - .into_par_iter() - .map(|(id, name)| { - build_test_case_with_config( - funcs[id], - name, - &type_declarations, - &casm_program, - *tracked_resource, - ) - }) - .collect::>()?; + let test_cases = if let Some(matches) = exact_matches { + matches + .into_par_iter() + .map(|(id, name)| { + build_test_case_with_config( + funcs[id], + name, + &type_declarations, + &casm_program, + *tracked_resource, + ) + }) + .collect::>()? + } else { + executables + .par_iter() + .map(|case| { + build_test_case_with_config( + funcs[&case.id], + case.debug_name + .clone() + .expect("Failed to get test case name") + .into(), + &type_declarations, + &casm_program, + *tracked_resource, + ) + }) + .collect::>()? + }; Ok(Some(TestTargetWithConfig { tests_location: test_target_raw.tests_location, From ad4d14394c31aad3ed9dcd1e2f849dc74cd90059 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 09:34:24 +0200 Subject: [PATCH 22/30] Skip CASM compilation for targets with no matching tests --- crates/forge-runner/src/running/target.rs | 91 +++++++++-------------- crates/forge/src/run_tests/package.rs | 9 ++- 2 files changed, 42 insertions(+), 58 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 20bb0bd165..7456182d44 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -15,7 +15,6 @@ use cairo_lang_sierra::{ program::{GenFunction, StatementIdx, TypeDeclaration}, }; use rayon::iter::IntoParallelIterator; -use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; @@ -26,7 +25,7 @@ pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, name_filter: &NameFilter, -) -> Result> { +) -> Result<(Option, usize)> { let default_executables = vec![]; let executables = test_target_raw .sierra_program @@ -35,23 +34,20 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - // For exact matching, pre-filter here to skip CASM compilation entirely when - // no test in this target matches. For other filters, all test cases must flow - // through resolve_config so that the "filtered out" count stays correct. - let exact_matches = name_filter.exact_match().map(|_| { - executables - .iter() - .filter_map(|case| { - let raw_name: String = case.debug_name.clone()?.into(); - name_filter - .matches(&sanitize_test_case_name(&raw_name)) - .then_some((&case.id, raw_name)) - }) - .collect::>() - }); + let matching: Vec<_> = executables + .iter() + .filter_map(|case| { + let raw_name: String = case.debug_name.clone()?.into(); + name_filter + .matches(&sanitize_test_case_name(&raw_name)) + .then_some((&case.id, raw_name)) + }) + .collect(); + + let pre_filtered_count = executables.len() - matching.len(); - if exact_matches.as_ref().is_some_and(Vec::is_empty) { - return Ok(None); + if matching.is_empty() { + return Ok((None, pre_filtered_count)); } macro_rules! by_id { @@ -74,44 +70,29 @@ pub fn prepare_test_target( test_target_raw.sierra_program_path.as_std_path(), )?); - let test_cases = if let Some(matches) = exact_matches { - matches - .into_par_iter() - .map(|(id, name)| { - build_test_case_with_config( - funcs[id], - name, - &type_declarations, - &casm_program, - *tracked_resource, - ) - }) - .collect::>()? - } else { - executables - .par_iter() - .map(|case| { - build_test_case_with_config( - funcs[&case.id], - case.debug_name - .clone() - .expect("Failed to get test case name") - .into(), - &type_declarations, - &casm_program, - *tracked_resource, - ) - }) - .collect::>()? - }; + let test_cases = matching + .into_par_iter() + .map(|(id, name)| { + build_test_case_with_config( + funcs[id], + name, + &type_declarations, + &casm_program, + *tracked_resource, + ) + }) + .collect::>()?; - Ok(Some(TestTargetWithConfig { - tests_location: test_target_raw.tests_location, - test_cases, - sierra_program: test_target_raw.sierra_program, - sierra_program_path: test_target_raw.sierra_program_path.into(), - casm_program, - })) + Ok(( + Some(TestTargetWithConfig { + tests_location: test_target_raw.tests_location, + test_cases, + sierra_program: test_target_raw.sierra_program, + sierra_program_path: test_target_raw.sierra_program_path.into(), + casm_program, + }), + pre_filtered_count, + )) } fn build_test_case_with_config( diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index d23181b7c1..5f2416ba10 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -68,7 +68,7 @@ impl PackageTestResult { } pub struct RunForPackageArgs { - pub target_handles: Vec>>>, + pub target_handles: Vec, usize)>>>, pub tests_filter: TestsFilter, pub forge_config: Arc, pub fork_targets: Vec, @@ -148,7 +148,7 @@ fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, name_filter: NameFilter, -) -> JoinHandle>> { +) -> JoinHandle, usize)>> { tokio::task::spawn_blocking(move || { prepare_test_target(target, &tracked_resource, &name_filter) }) @@ -195,7 +195,10 @@ pub async fn run_for_package( let mut not_filtered_total = 0; for handle in target_handles { - let Some(target_with_config) = handle.await?? else { + let (maybe_target, pre_filtered) = handle.await??; + all_tests += pre_filtered; + + let Some(target_with_config) = maybe_target else { continue; }; From dc93e706a548502b4e0da1df0c9feef36554016d Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 15:07:03 +0200 Subject: [PATCH 23/30] Revert non-exact match filtering optimization --- crates/forge-runner/src/running/target.rs | 88 +++++++++++++---------- crates/forge/src/run_tests/package.rs | 11 ++- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 7456182d44..3ba84967ef 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -15,6 +15,7 @@ use cairo_lang_sierra::{ program::{GenFunction, StatementIdx, TypeDeclaration}, }; use rayon::iter::IntoParallelIterator; +use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use std::{collections::HashMap, sync::Arc}; use universal_sierra_compiler_api::compile_raw_sierra_at_path; @@ -25,7 +26,7 @@ pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, name_filter: &NameFilter, -) -> Result<(Option, usize)> { +) -> Result> { let default_executables = vec![]; let executables = test_target_raw .sierra_program @@ -34,20 +35,20 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let matching: Vec<_> = executables - .iter() - .filter_map(|case| { - let raw_name: String = case.debug_name.clone()?.into(); - name_filter - .matches(&sanitize_test_case_name(&raw_name)) - .then_some((&case.id, raw_name)) - }) - .collect(); - - let pre_filtered_count = executables.len() - matching.len(); + let exact_matches = name_filter.exact_match().map(|_| { + executables + .iter() + .filter_map(|case| { + let raw_name: String = case.debug_name.clone()?.into(); + name_filter + .matches(&sanitize_test_case_name(&raw_name)) + .then_some((&case.id, raw_name)) + }) + .collect::>() + }); - if matching.is_empty() { - return Ok((None, pre_filtered_count)); + if exact_matches.as_ref().is_some_and(Vec::is_empty) { + return Ok(None); } macro_rules! by_id { @@ -70,29 +71,44 @@ pub fn prepare_test_target( test_target_raw.sierra_program_path.as_std_path(), )?); - let test_cases = matching - .into_par_iter() - .map(|(id, name)| { - build_test_case_with_config( - funcs[id], - name, - &type_declarations, - &casm_program, - *tracked_resource, - ) - }) - .collect::>()?; + let test_cases = if let Some(matches) = exact_matches { + matches + .into_par_iter() + .map(|(id, name)| { + build_test_case_with_config( + funcs[id], + name, + &type_declarations, + &casm_program, + *tracked_resource, + ) + }) + .collect::>()? + } else { + executables + .par_iter() + .map(|case| { + build_test_case_with_config( + funcs[&case.id], + case.debug_name + .clone() + .expect("Failed to get test case name") + .into(), + &type_declarations, + &casm_program, + *tracked_resource, + ) + }) + .collect::>()? + }; - Ok(( - Some(TestTargetWithConfig { - tests_location: test_target_raw.tests_location, - test_cases, - sierra_program: test_target_raw.sierra_program, - sierra_program_path: test_target_raw.sierra_program_path.into(), - casm_program, - }), - pre_filtered_count, - )) + Ok(Some(TestTargetWithConfig { + tests_location: test_target_raw.tests_location, + test_cases, + sierra_program: test_target_raw.sierra_program, + sierra_program_path: test_target_raw.sierra_program_path.into(), + casm_program, + })) } fn build_test_case_with_config( diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 5f2416ba10..538d2e051b 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -42,6 +42,8 @@ use scarb_metadata::{Metadata, PackageMetadata}; use std::sync::Arc; use tokio::task::JoinHandle; +type PrepareTargetHandle = JoinHandle>>; + pub struct PackageTestResult { summaries: Vec, filtered: Option, @@ -68,7 +70,7 @@ impl PackageTestResult { } pub struct RunForPackageArgs { - pub target_handles: Vec, usize)>>>, + pub target_handles: Vec, pub tests_filter: TestsFilter, pub forge_config: Arc, pub fork_targets: Vec, @@ -148,7 +150,7 @@ fn spawn_prepare_test_target( target: TestTargetRaw, tracked_resource: ForgeTrackedResource, name_filter: NameFilter, -) -> JoinHandle, usize)>> { +) -> PrepareTargetHandle { tokio::task::spawn_blocking(move || { prepare_test_target(target, &tracked_resource, &name_filter) }) @@ -195,10 +197,7 @@ pub async fn run_for_package( let mut not_filtered_total = 0; for handle in target_handles { - let (maybe_target, pre_filtered) = handle.await??; - all_tests += pre_filtered; - - let Some(target_with_config) = maybe_target else { + let Some(target_with_config) = handle.await?? else { continue; }; From 8cbc7ff7b20559367e78ce72d050e7135e462322 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 16:06:08 +0200 Subject: [PATCH 24/30] Fix missing "Running 0 test(s)" message for exact-match filtered-out targets --- crates/forge-runner/src/running/target.rs | 24 ++++++++++++--------- crates/forge/src/run_tests/package.rs | 26 ++++++++++++++++++++--- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 3ba84967ef..4750dbf7c2 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -2,7 +2,7 @@ use crate::{ filtering::NameFilter, forge_config::ForgeTrackedResource, package_tests::{ - TestDetails, + TestDetails, TestTargetLocation, raw::TestTargetRaw, with_config::{TestCaseWithConfig, TestTargetWithConfig}, with_config_resolved::sanitize_test_case_name, @@ -26,7 +26,8 @@ pub fn prepare_test_target( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, name_filter: &NameFilter, -) -> Result> { +) -> Result<(Option, TestTargetLocation)> { + let tests_location = test_target_raw.tests_location; let default_executables = vec![]; let executables = test_target_raw .sierra_program @@ -48,7 +49,7 @@ pub fn prepare_test_target( }); if exact_matches.as_ref().is_some_and(Vec::is_empty) { - return Ok(None); + return Ok((None, tests_location)); } macro_rules! by_id { @@ -102,13 +103,16 @@ pub fn prepare_test_target( .collect::>()? }; - Ok(Some(TestTargetWithConfig { - tests_location: test_target_raw.tests_location, - test_cases, - sierra_program: test_target_raw.sierra_program, - sierra_program_path: test_target_raw.sierra_program_path.into(), - casm_program, - })) + Ok(( + Some(TestTargetWithConfig { + tests_location, + test_cases, + sierra_program: test_target_raw.sierra_program, + sierra_program_path: test_target_raw.sierra_program_path.into(), + casm_program, + }), + tests_location, + )) } fn build_test_case_with_config( diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 538d2e051b..c15ff3500d 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -26,6 +26,7 @@ use forge_runner::{ filtering::NameFilter, forge_config::{ForgeConfig, ForgeTrackedResource}, package_tests::{ + TestTargetLocation, raw::TestTargetRaw, with_config::TestTargetWithConfig, with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, @@ -42,7 +43,7 @@ use scarb_metadata::{Metadata, PackageMetadata}; use std::sync::Arc; use tokio::task::JoinHandle; -type PrepareTargetHandle = JoinHandle>>; +type PrepareTargetHandle = JoinHandle, TestTargetLocation)>>; pub struct PackageTestResult { summaries: Vec, @@ -192,12 +193,22 @@ pub async fn run_for_package( exit_first_channel: &mut ExitFirstChannel, ) -> Result { // Resolve all targets first so the collected count includes #[ignore] filtering. + // target_order preserves the original handle order for "Running X" messages. + enum TargetOrder { + Skipped(TestTargetLocation), + ResolvedIdx(usize), + } + let mut resolved_targets = vec![]; + let mut target_order: Vec = vec![]; let mut all_tests = 0; let mut not_filtered_total = 0; for handle in target_handles { - let Some(target_with_config) = handle.await?? else { + let (maybe_target, tests_location) = handle.await??; + + let Some(target_with_config) = maybe_target else { + target_order.push(TargetOrder::Skipped(tests_location)); continue; }; @@ -221,6 +232,7 @@ pub async fn run_for_package( all_tests += all; not_filtered_total += not_filtered; + target_order.push(TargetOrder::ResolvedIdx(resolved_targets.len())); resolved_targets.push(resolved); } @@ -233,7 +245,15 @@ pub async fn run_for_package( let mut summaries = vec![]; - for resolved in resolved_targets { + for order in target_order { + let resolved = match order { + TargetOrder::Skipped(location) => { + ui.println(&TestsRunMessage::new(location, 0)); + continue; + } + TargetOrder::ResolvedIdx(idx) => resolved_targets.remove(idx - summaries.len()), + }; + ui.println(&TestsRunMessage::new( resolved.tests_location, sum_test_cases_from_test_target( From 7dab4d49ecfc9a9be7f696fd76f7eafab13fa7bf Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 16:46:48 +0200 Subject: [PATCH 25/30] Simplify target ordering --- crates/forge/src/run_tests/package.rs | 44 +++++++++++++++------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index c15ff3500d..ca2ccbfb08 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -29,7 +29,9 @@ use forge_runner::{ TestTargetLocation, raw::TestTargetRaw, with_config::TestTargetWithConfig, - with_config_resolved::{TestCaseWithResolvedConfig, sanitize_test_case_name}, + with_config_resolved::{ + TestCaseWithResolvedConfig, TestTargetWithResolvedConfig, sanitize_test_case_name, + }, }, partition::PartitionConfig, running::target::prepare_test_target, @@ -45,6 +47,11 @@ use tokio::task::JoinHandle; type PrepareTargetHandle = JoinHandle, TestTargetLocation)>>; +struct PreparedTarget { + location: TestTargetLocation, + resolved: Option, +} + pub struct PackageTestResult { summaries: Vec, filtered: Option, @@ -193,14 +200,7 @@ pub async fn run_for_package( exit_first_channel: &mut ExitFirstChannel, ) -> Result { // Resolve all targets first so the collected count includes #[ignore] filtering. - // target_order preserves the original handle order for "Running X" messages. - enum TargetOrder { - Skipped(TestTargetLocation), - ResolvedIdx(usize), - } - - let mut resolved_targets = vec![]; - let mut target_order: Vec = vec![]; + let mut prepared_targets = vec![]; let mut all_tests = 0; let mut not_filtered_total = 0; @@ -208,7 +208,10 @@ pub async fn run_for_package( let (maybe_target, tests_location) = handle.await??; let Some(target_with_config) = maybe_target else { - target_order.push(TargetOrder::Skipped(tests_location)); + prepared_targets.push(PreparedTarget { + location: tests_location, + resolved: None, + }); continue; }; @@ -232,10 +235,16 @@ pub async fn run_for_package( all_tests += all; not_filtered_total += not_filtered; - target_order.push(TargetOrder::ResolvedIdx(resolved_targets.len())); - resolved_targets.push(resolved); + prepared_targets.push(PreparedTarget { + location: tests_location, + resolved: Some(resolved), + }); } + let resolved_targets: Vec<_> = prepared_targets + .iter() + .filter_map(|target| target.resolved.as_ref().cloned()) + .collect(); warn_if_incompatible_rpc_version(&resolved_targets, ui.clone()).await?; ui.println(&CollectedTestsCountMessage { @@ -245,13 +254,10 @@ pub async fn run_for_package( let mut summaries = vec![]; - for order in target_order { - let resolved = match order { - TargetOrder::Skipped(location) => { - ui.println(&TestsRunMessage::new(location, 0)); - continue; - } - TargetOrder::ResolvedIdx(idx) => resolved_targets.remove(idx - summaries.len()), + for prepared_target in prepared_targets { + let Some(resolved) = prepared_target.resolved else { + ui.println(&TestsRunMessage::new(prepared_target.location, 0)); + continue; }; ui.println(&TestsRunMessage::new( From 4c4335af677889efe3b47cba7d742f3a293650f4 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 17:04:31 +0200 Subject: [PATCH 26/30] Fix lint --- crates/forge/src/run_tests/package.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index ca2ccbfb08..cf5a7b043a 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -243,7 +243,7 @@ pub async fn run_for_package( let resolved_targets: Vec<_> = prepared_targets .iter() - .filter_map(|target| target.resolved.as_ref().cloned()) + .filter_map(|target| target.resolved.clone()) .collect(); warn_if_incompatible_rpc_version(&resolved_targets, ui.clone()).await?; From e1a7c8c5f41390059a72fc55ea1a91ff3f5b2418 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 17:14:04 +0200 Subject: [PATCH 27/30] Use ordered target tuples instead of `PreparedTarget` --- crates/forge/src/run_tests/package.rs | 30 +++++++++------------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index cf5a7b043a..0062fbdb65 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -47,11 +47,6 @@ use tokio::task::JoinHandle; type PrepareTargetHandle = JoinHandle, TestTargetLocation)>>; -struct PreparedTarget { - location: TestTargetLocation, - resolved: Option, -} - pub struct PackageTestResult { summaries: Vec, filtered: Option, @@ -200,7 +195,8 @@ pub async fn run_for_package( exit_first_channel: &mut ExitFirstChannel, ) -> Result { // Resolve all targets first so the collected count includes #[ignore] filtering. - let mut prepared_targets = vec![]; + let mut resolved_targets: Vec<(TestTargetLocation, Option)> = + vec![]; let mut all_tests = 0; let mut not_filtered_total = 0; @@ -208,10 +204,7 @@ pub async fn run_for_package( let (maybe_target, tests_location) = handle.await??; let Some(target_with_config) = maybe_target else { - prepared_targets.push(PreparedTarget { - location: tests_location, - resolved: None, - }); + resolved_targets.push((tests_location, None)); continue; }; @@ -235,17 +228,14 @@ pub async fn run_for_package( all_tests += all; not_filtered_total += not_filtered; - prepared_targets.push(PreparedTarget { - location: tests_location, - resolved: Some(resolved), - }); + resolved_targets.push((tests_location, Some(resolved))); } - let resolved_targets: Vec<_> = prepared_targets + let resolved_targets_for_warning: Vec<_> = resolved_targets .iter() - .filter_map(|target| target.resolved.clone()) + .filter_map(|(_, resolved)| resolved.clone()) .collect(); - warn_if_incompatible_rpc_version(&resolved_targets, ui.clone()).await?; + warn_if_incompatible_rpc_version(&resolved_targets_for_warning, ui.clone()).await?; ui.println(&CollectedTestsCountMessage { tests_num: not_filtered_total, @@ -254,9 +244,9 @@ pub async fn run_for_package( let mut summaries = vec![]; - for prepared_target in prepared_targets { - let Some(resolved) = prepared_target.resolved else { - ui.println(&TestsRunMessage::new(prepared_target.location, 0)); + for (location, resolved) in resolved_targets { + let Some(resolved) = resolved else { + ui.println(&TestsRunMessage::new(location, 0)); continue; }; From 42dabacc0d88d760652418efcc1d13943cc443be Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 17:28:40 +0200 Subject: [PATCH 28/30] Drop `NameFilter::exact_match` helper --- crates/forge-runner/src/filtering.rs | 18 ------------- crates/forge-runner/src/running/target.rs | 32 +++++++++++++---------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/crates/forge-runner/src/filtering.rs b/crates/forge-runner/src/filtering.rs index 1eb8489bb2..2fac374d05 100644 --- a/crates/forge-runner/src/filtering.rs +++ b/crates/forge-runner/src/filtering.rs @@ -30,14 +30,6 @@ impl NameFilter { Self::ExactMatch(name) => sanitized_name == name, } } - - #[must_use] - pub fn exact_match(&self) -> Option<&str> { - match self { - Self::ExactMatch(name) => Some(name), - Self::All | Self::Match(_) => None, - } - } } /// Result of filtering a test case. @@ -88,14 +80,4 @@ mod tests { assert!(NameFilter::ExactMatch("pkg::test".to_string()).matches("pkg::test")); assert!(!NameFilter::ExactMatch("pkg::test".to_string()).matches("pkg::test_case")); } - - #[test] - fn name_filter_exact_match_helper_returns_only_exact_filter() { - assert_eq!( - NameFilter::ExactMatch("pkg::test".to_string()).exact_match(), - Some("pkg::test") - ); - assert_eq!(NameFilter::All.exact_match(), None); - assert_eq!(NameFilter::Match("pkg".to_string()).exact_match(), None); - } } diff --git a/crates/forge-runner/src/running/target.rs b/crates/forge-runner/src/running/target.rs index 4750dbf7c2..677c8db0e2 100644 --- a/crates/forge-runner/src/running/target.rs +++ b/crates/forge-runner/src/running/target.rs @@ -36,21 +36,25 @@ pub fn prepare_test_target( .and_then(|info| info.executables.get("snforge_internal_test_executable")) .unwrap_or(&default_executables); - let exact_matches = name_filter.exact_match().map(|_| { - executables - .iter() - .filter_map(|case| { - let raw_name: String = case.debug_name.clone()?.into(); - name_filter - .matches(&sanitize_test_case_name(&raw_name)) - .then_some((&case.id, raw_name)) - }) - .collect::>() - }); + let exact_matches = match name_filter { + NameFilter::ExactMatch(exact_match) => { + let matches = executables + .iter() + .filter_map(|case| { + let raw_name: String = case.debug_name.clone()?.into(); + let sanitized_name = sanitize_test_case_name(&raw_name); + (sanitized_name == *exact_match).then_some((&case.id, raw_name)) + }) + .collect::>(); - if exact_matches.as_ref().is_some_and(Vec::is_empty) { - return Ok((None, tests_location)); - } + if matches.is_empty() { + return Ok((None, tests_location)); + } + + Some(matches) + } + NameFilter::All | NameFilter::Match(_) => None, + }; macro_rules! by_id { ($field:ident) => {{ From 6152fc8b9f8f5d9fedd139998e05f5d10802c918 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Tue, 5 May 2026 17:49:48 +0200 Subject: [PATCH 29/30] Avoid cloning targets for RPC warning checks --- crates/forge/src/run_tests/package.rs | 12 +++++++----- crates/forge/src/warn.rs | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 0062fbdb65..2dc1434ca4 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -231,11 +231,13 @@ pub async fn run_for_package( resolved_targets.push((tests_location, Some(resolved))); } - let resolved_targets_for_warning: Vec<_> = resolved_targets - .iter() - .filter_map(|(_, resolved)| resolved.clone()) - .collect(); - warn_if_incompatible_rpc_version(&resolved_targets_for_warning, ui.clone()).await?; + warn_if_incompatible_rpc_version( + resolved_targets + .iter() + .filter_map(|(_, resolved)| resolved.as_ref()), + ui.clone(), + ) + .await?; ui.println(&CollectedTestsCountMessage { tests_num: not_filtered_total, diff --git a/crates/forge/src/warn.rs b/crates/forge/src/warn.rs index 2983d7d7d1..09e4415ee7 100644 --- a/crates/forge/src/warn.rs +++ b/crates/forge/src/warn.rs @@ -13,8 +13,8 @@ use std::env; use std::sync::Arc; use url::Url; -pub(crate) async fn warn_if_incompatible_rpc_version( - test_targets: &[TestTargetWithResolvedConfig], +pub(crate) async fn warn_if_incompatible_rpc_version<'a>( + test_targets: impl IntoIterator, ui: Arc, ) -> Result<()> { let mut urls = HashSet::::new(); From 61f8526ae9b7e9e4deddb359e682af3ca978b272 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 6 May 2026 14:00:14 +0200 Subject: [PATCH 30/30] Trigger CI