From cb03120969b800a2a0ecc2198ecf241c64278f82 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 20:55:16 +0100 Subject: [PATCH 1/7] Add logic for tests precollection --- crates/forge-runner/src/package_tests.rs | 79 ++++++++++++++++++- .../src/package_tests/with_config.rs | 4 +- .../src/package_tests/with_config_resolved.rs | 4 +- crates/forge/src/run_tests/package.rs | 5 +- crates/forge/src/run_tests/workspace.rs | 36 +++++++++ 5 files changed, 118 insertions(+), 10 deletions(-) diff --git a/crates/forge-runner/src/package_tests.rs b/crates/forge-runner/src/package_tests.rs index 735fb685f6..73bb4cbc39 100644 --- a/crates/forge-runner/src/package_tests.rs +++ b/crates/forge-runner/src/package_tests.rs @@ -128,8 +128,9 @@ impl TestDetails { } } +// TODO: Remove in next PRs #[derive(Debug, Clone)] -pub struct TestTarget { +pub struct TestTargetDeprecated { pub tests_location: TestTargetLocation, pub sierra_program: ProgramArtifact, pub sierra_program_path: Arc, @@ -137,9 +138,10 @@ pub struct TestTarget { pub test_cases: Vec>, } -impl TestTarget { +// TODO: Remove in next PRs +impl TestTargetDeprecated { #[tracing::instrument(skip_all, level = "debug")] - pub fn from_raw( + pub fn from_raw_deprecated( test_target_raw: TestTargetRaw, tracked_resource: &ForgeTrackedResource, ) -> Result { @@ -200,7 +202,7 @@ impl TestTarget { test_cases, sierra_program: test_target_raw.sierra_program, sierra_program_path: test_target_raw.sierra_program_path.into(), - casm_program, + casm_program: casm_program, }) } } @@ -211,3 +213,72 @@ pub struct TestCase { pub name: String, pub config: C, } + +#[derive(Debug, Clone)] +pub struct TestTarget { + pub tests_location: TestTargetLocation, + pub sierra_program: ProgramArtifact, + pub sierra_program_path: Arc, + pub casm_program: Option>, + pub test_cases: Vec, +} + +impl TestTarget { + #[tracing::instrument(skip_all, level = "debug")] + pub fn from_raw(test_target_raw: TestTargetRaw) -> 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 sierra_program_registry = + ProgramRegistry::::new(&test_target_raw.sierra_program.program)?; + let type_size_map = get_type_size_map( + &test_target_raw.sierra_program.program, + &sierra_program_registry, + ) + .ok_or_else(|| anyhow!("can not get type size map"))?; + + let default_executables = vec![]; + let debug_info = test_target_raw.sierra_program.debug_info.clone(); + let executables = debug_info + .as_ref() + .and_then(|info| info.executables.get("snforge_internal_test_executable")) + .unwrap_or(&default_executables); + + let test_cases = executables + .par_iter() + .map(|case| { + let func = funcs[&case.id]; + let name = case.debug_name.clone().unwrap().into(); + let test_details = TestDetails::build(func, &type_declarations, &type_size_map); + + Ok(TestCandidate { name, test_details }) + }) + .collect::>()?; + + Ok(TestTarget { + 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: None, + }) + } +} + +pub struct TestCandidate { + pub name: String, + pub test_details: TestDetails, +} diff --git a/crates/forge-runner/src/package_tests/with_config.rs b/crates/forge-runner/src/package_tests/with_config.rs index 73481e43ea..c74de5cafe 100644 --- a/crates/forge-runner/src/package_tests/with_config.rs +++ b/crates/forge-runner/src/package_tests/with_config.rs @@ -1,4 +1,4 @@ -use super::{TestCase, TestTarget}; +use super::{TestCase, TestTargetDeprecated}; use crate::{ TestCaseIsIgnored, expected_result::{ExpectedPanicValue, ExpectedTestResult}, @@ -9,7 +9,7 @@ use cheatnet::runtime_extensions::forge_config_extension::config::{ }; use conversions::serde::serialize::SerializeToFeltVec; -pub type TestTargetWithConfig = TestTarget; +pub type TestTargetWithConfig = TestTargetDeprecated; pub type TestCaseWithConfig = TestCase; diff --git a/crates/forge-runner/src/package_tests/with_config_resolved.rs b/crates/forge-runner/src/package_tests/with_config_resolved.rs index 0423a75559..b3a744a508 100644 --- a/crates/forge-runner/src/package_tests/with_config_resolved.rs +++ b/crates/forge-runner/src/package_tests/with_config_resolved.rs @@ -1,4 +1,4 @@ -use super::{TestCase, TestTarget}; +use super::{TestCase, TestTargetDeprecated}; use crate::{TestCaseIsIgnored, expected_result::ExpectedTestResult, package_tests::TestDetails}; use anyhow::Result; use cairo_vm::types::program::Program; @@ -9,7 +9,7 @@ use starknet_api::block::BlockNumber; use universal_sierra_compiler_api::representation::RawCasmProgram; use url::Url; -pub type TestTargetWithResolvedConfig = TestTarget; +pub type TestTargetWithResolvedConfig = TestTargetDeprecated; pub type TestCaseWithResolvedConfig = TestCase; diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 660dc29cf4..ffd668f10d 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -27,7 +27,8 @@ use console::Style; use forge_runner::{ forge_config::ForgeConfig, package_tests::{ - TestTarget, raw::TestTargetRaw, with_config_resolved::TestTargetWithResolvedConfig, + TestTargetDeprecated, raw::TestTargetRaw, + with_config_resolved::TestTargetWithResolvedConfig, }, test_case_summary::AnyTestCaseSummary, test_target_summary::TestTargetSummary, @@ -144,7 +145,7 @@ async fn test_package_with_config_resolved( let mut test_targets_with_resolved_config = Vec::with_capacity(test_targets.len()); for test_target in test_targets { - let test_target = TestTarget::from_raw( + let test_target = TestTargetDeprecated::from_raw_deprecated( test_target, &forge_config.test_runner_config.tracked_resource, )?; diff --git a/crates/forge/src/run_tests/workspace.rs b/crates/forge/src/run_tests/workspace.rs index a5d9513846..3bb82ddb4c 100644 --- a/crates/forge/src/run_tests/workspace.rs +++ b/crates/forge/src/run_tests/workspace.rs @@ -2,12 +2,14 @@ use super::package::RunForPackageArgs; use crate::run_tests::messages::latest_blocks_numbers::LatestBlocksNumbersMessage; use crate::run_tests::messages::tests_failure_summary::TestsFailureSummaryMessage; use crate::run_tests::messages::workspace_summary::WorkspaceSummaryMessage; +use crate::scarb::load_test_artifacts; use crate::{ ExitStatus, TestArgs, block_number_map::BlockNumberMap, run_tests::package::run_for_package, scarb::build_artifacts_with_scarb, shared_cache::FailedTestsCache, }; use anyhow::{Context, Result}; use camino::Utf8PathBuf; +use forge_runner::package_tests::{TestCandidate, TestTarget}; use forge_runner::test_case_summary::AnyTestCaseSummary; use forge_runner::{CACHE_DIR, test_target_summary::TestTargetSummary}; use foundry_ui::UI; @@ -15,8 +17,10 @@ use scarb_api::{ metadata::{Metadata, PackageMetadata}, target_dir_for_workspace, }; +use scarb_metadata::PackageId; use scarb_ui::args::PackagesFilter; use shared::consts::SNFORGE_TEST_FILTER; +use std::collections::HashMap; use std::env; use std::sync::Arc; @@ -42,6 +46,38 @@ impl WorkspaceDirs { } } +#[expect(dead_code)] +struct PackageData { + metadata: PackageMetadata, + test_targets: Vec>, +} + +pub struct Workspace(HashMap); + +impl Workspace { + pub fn new(workspace_dirs: &WorkspaceDirs, packages: &[PackageMetadata]) -> Result { + let mut result = Workspace(HashMap::new()); + + for package in packages { + let test_targets_raw = load_test_artifacts(&workspace_dirs.artifacts_dir, &package)?; + let test_targets = test_targets_raw + .into_iter() + .map(|raw| TestTarget::from_raw(raw)) + .collect::>>()?; + + result.0.insert( + package.id.clone(), + PackageData { + metadata: package.clone(), + test_targets: test_targets, + }, + ); + } + + Ok(result) + } +} + #[tracing::instrument(skip_all, level = "debug")] pub async fn run_for_workspace( scarb_metadata: &Metadata, From 5dd210c998beb7eb7fb3a337e7b9da3b570f26de Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 21:08:13 +0100 Subject: [PATCH 2/7] Fix linting --- crates/forge-runner/src/package_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge-runner/src/package_tests.rs b/crates/forge-runner/src/package_tests.rs index 73bb4cbc39..34c98270b1 100644 --- a/crates/forge-runner/src/package_tests.rs +++ b/crates/forge-runner/src/package_tests.rs @@ -202,7 +202,7 @@ impl TestTargetDeprecated { test_cases, sierra_program: test_target_raw.sierra_program, sierra_program_path: test_target_raw.sierra_program_path.into(), - casm_program: casm_program, + casm_program, }) } } From c34c52260679cad9da0488a06603eb3e746da23b Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Thu, 6 Nov 2025 23:16:01 +0100 Subject: [PATCH 3/7] Fix linting --- crates/forge/src/run_tests/workspace.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/forge/src/run_tests/workspace.rs b/crates/forge/src/run_tests/workspace.rs index 3bb82ddb4c..bc15606986 100644 --- a/crates/forge/src/run_tests/workspace.rs +++ b/crates/forge/src/run_tests/workspace.rs @@ -59,17 +59,17 @@ impl Workspace { let mut result = Workspace(HashMap::new()); for package in packages { - let test_targets_raw = load_test_artifacts(&workspace_dirs.artifacts_dir, &package)?; + let test_targets_raw = load_test_artifacts(&workspace_dirs.artifacts_dir, package)?; let test_targets = test_targets_raw .into_iter() - .map(|raw| TestTarget::from_raw(raw)) + .map(TestTarget::from_raw) .collect::>>()?; result.0.insert( package.id.clone(), PackageData { metadata: package.clone(), - test_targets: test_targets, + test_targets, }, ); } From 9eb7b17623983359b4408cf9c15c3086a523ef71 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Fri, 7 Nov 2025 13:46:52 +0100 Subject: [PATCH 4/7] Remove `casm_program` field from `TestTarget` --- crates/forge-runner/src/package_tests.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/forge-runner/src/package_tests.rs b/crates/forge-runner/src/package_tests.rs index 34c98270b1..943c248de2 100644 --- a/crates/forge-runner/src/package_tests.rs +++ b/crates/forge-runner/src/package_tests.rs @@ -219,7 +219,6 @@ pub struct TestTarget { pub tests_location: TestTargetLocation, pub sierra_program: ProgramArtifact, pub sierra_program_path: Arc, - pub casm_program: Option>, pub test_cases: Vec, } @@ -273,7 +272,6 @@ impl TestTarget { test_cases, sierra_program: test_target_raw.sierra_program, sierra_program_path: test_target_raw.sierra_program_path.into(), - casm_program: None, }) } } From 56e3f8b7f22853f7ab9a58019f5eb743364b4190 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Fri, 7 Nov 2025 13:51:46 +0100 Subject: [PATCH 5/7] Revert "Remove `casm_program` field from `TestTarget`" This reverts commit 9eb7b17623983359b4408cf9c15c3086a523ef71. --- crates/forge-runner/src/package_tests.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/forge-runner/src/package_tests.rs b/crates/forge-runner/src/package_tests.rs index 943c248de2..34c98270b1 100644 --- a/crates/forge-runner/src/package_tests.rs +++ b/crates/forge-runner/src/package_tests.rs @@ -219,6 +219,7 @@ pub struct TestTarget { pub tests_location: TestTargetLocation, pub sierra_program: ProgramArtifact, pub sierra_program_path: Arc, + pub casm_program: Option>, pub test_cases: Vec, } @@ -272,6 +273,7 @@ impl TestTarget { test_cases, sierra_program: test_target_raw.sierra_program, sierra_program_path: test_target_raw.sierra_program_path.into(), + casm_program: None, }) } } From a8af7e6142f72e728fe218b278652efb4611c544 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Fri, 7 Nov 2025 15:06:04 +0100 Subject: [PATCH 6/7] Simply collection --- crates/forge/src/run_tests/workspace.rs | 44 +++++++++---------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/crates/forge/src/run_tests/workspace.rs b/crates/forge/src/run_tests/workspace.rs index bc15606986..f98e1f468e 100644 --- a/crates/forge/src/run_tests/workspace.rs +++ b/crates/forge/src/run_tests/workspace.rs @@ -17,10 +17,8 @@ use scarb_api::{ metadata::{Metadata, PackageMetadata}, target_dir_for_workspace, }; -use scarb_metadata::PackageId; use scarb_ui::args::PackagesFilter; use shared::consts::SNFORGE_TEST_FILTER; -use std::collections::HashMap; use std::env; use std::sync::Arc; @@ -46,36 +44,26 @@ impl WorkspaceDirs { } } +type TestTargets = Vec>; + #[expect(dead_code)] -struct PackageData { - metadata: PackageMetadata, - test_targets: Vec>, -} +fn collect_packages_with_tests( + workspace_dirs: &WorkspaceDirs, + packages: &[PackageMetadata], +) -> Result> { + let mut result = Vec::new(); -pub struct Workspace(HashMap); - -impl Workspace { - pub fn new(workspace_dirs: &WorkspaceDirs, packages: &[PackageMetadata]) -> Result { - let mut result = Workspace(HashMap::new()); - - for package in packages { - let test_targets_raw = load_test_artifacts(&workspace_dirs.artifacts_dir, package)?; - let test_targets = test_targets_raw - .into_iter() - .map(TestTarget::from_raw) - .collect::>>()?; - - result.0.insert( - package.id.clone(), - PackageData { - metadata: package.clone(), - test_targets, - }, - ); - } + for package in packages { + let test_targets_raw = load_test_artifacts(&workspace_dirs.artifacts_dir, package)?; + let test_targets = test_targets_raw + .into_iter() + .map(TestTarget::from_raw) + .collect::>>()?; - Ok(result) + result.push((package.clone(), test_targets)); } + + Ok(result) } #[tracing::instrument(skip_all, level = "debug")] From 35721571072274bd05299117266737b226c9d7c1 Mon Sep 17 00:00:00 2001 From: Fiiranek Date: Wed, 12 Nov 2025 09:18:29 +0100 Subject: [PATCH 7/7] Parralelize `collect_packages_with_tests` --- Cargo.lock | 1 + crates/forge/Cargo.toml | 1 + crates/forge/src/run_tests/workspace.rs | 25 ++++++++++++------------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69116f9832..223f60586d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3198,6 +3198,7 @@ dependencies = [ "packages_validation", "project-root", "rand 0.8.5", + "rayon", "regex", "scarb-api", "scarb-metadata", diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 42a85edd9a..0c1a413d37 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -56,6 +56,7 @@ chrono.workspace = true tracing-subscriber.workspace = true tracing.workspace = true tracing-chrome.workspace = true +rayon.workspace = true [[bin]] name = "snforge" diff --git a/crates/forge/src/run_tests/workspace.rs b/crates/forge/src/run_tests/workspace.rs index f98e1f468e..732d428036 100644 --- a/crates/forge/src/run_tests/workspace.rs +++ b/crates/forge/src/run_tests/workspace.rs @@ -13,6 +13,7 @@ use forge_runner::package_tests::{TestCandidate, TestTarget}; use forge_runner::test_case_summary::AnyTestCaseSummary; use forge_runner::{CACHE_DIR, test_target_summary::TestTargetSummary}; use foundry_ui::UI; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use scarb_api::{ metadata::{Metadata, PackageMetadata}, target_dir_for_workspace, @@ -51,19 +52,17 @@ fn collect_packages_with_tests( workspace_dirs: &WorkspaceDirs, packages: &[PackageMetadata], ) -> Result> { - let mut result = Vec::new(); - - for package in packages { - let test_targets_raw = load_test_artifacts(&workspace_dirs.artifacts_dir, package)?; - let test_targets = test_targets_raw - .into_iter() - .map(TestTarget::from_raw) - .collect::>>()?; - - result.push((package.clone(), test_targets)); - } - - Ok(result) + packages + .par_iter() + .map(|package| { + let test_targets_raw = load_test_artifacts(&workspace_dirs.artifacts_dir, package)?; + let test_targets = test_targets_raw + .into_iter() + .map(TestTarget::from_raw) + .collect::>>()?; + Ok((package.clone(), test_targets)) + }) + .collect() } #[tracing::instrument(skip_all, level = "debug")]