diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index e275904..329a00e 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -92,9 +92,13 @@ pub struct Arguments { #[arg(long = "compile-only")] pub compile_only: Option, - /// Determines the amount of tests that are executed in parallel. - #[arg(long = "workers", default_value = "12")] - pub workers: usize, + /// Determines the amount of nodes that will be spawned for each chain. + #[arg(long, default_value = "12")] + pub number_of_nodes: usize, + + /// Determines the amount of threads that will will be used. + #[arg(long, default_value = "12")] + pub number_of_threads: usize, /// Extract problems back to the test corpus. #[arg(short, long = "extract-problems")] diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index 721003e..088741c 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -27,7 +27,7 @@ use semver::Version; use revive_dt_common::iterators::FilesWithExtensionIterator; use revive_dt_compiler::{Compiler, SolidityCompiler}; use revive_dt_config::Arguments; -use revive_dt_format::case::CaseIdx; +use revive_dt_format::case::{Case, CaseIdx}; use revive_dt_format::input::{Calldata, EtherValue, Expected, ExpectedOutput, Method}; use revive_dt_format::metadata::{ContractInstance, ContractPathAndIdent}; use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode}; @@ -733,6 +733,8 @@ where pub struct Driver<'a, Leader: Platform, Follower: Platform> { metadata: &'a Metadata, + case: &'a Case, + case_idx: CaseIdx, config: &'a Arguments, leader_node: &'a Leader::Blockchain, follower_node: &'a Follower::Blockchain, @@ -745,12 +747,16 @@ where { pub fn new( metadata: &'a Metadata, + case: &'a Case, + case_idx: impl Into, config: &'a Arguments, leader_node: &'a L::Blockchain, follower_node: &'a F::Blockchain, ) -> Driver<'a, L, F> { Self { metadata, + case, + case_idx: case_idx.into(), config, leader_node, follower_node, @@ -907,125 +913,113 @@ where // For cases if one of the inputs fail then we move on to the next case and we do NOT // bail out of the whole thing. - 'case_loop: for (case_idx, case) in self.metadata.cases.iter().enumerate() { - let tracing_span = tracing::info_span!( - "Handling case", - case_name = case.name, - case_idx = case_idx - ); + let case = self.case; + let case_idx = self.case_idx; + let tracing_span = + tracing::info_span!("Handling case", case_name = case.name, case_idx = *case_idx); + let _guard = tracing_span.enter(); + + let case_idx = CaseIdx::new(case_idx); + + // For inputs if one of the inputs fail we move on to the next case (we do not move + // on to the next input as it doesn't make sense. It depends on the previous one). + for (input_idx, input) in case.inputs_iterator().enumerate() { + let tracing_span = tracing::info_span!("Handling input", input_idx); let _guard = tracing_span.enter(); - let case_idx = CaseIdx::new(case_idx); - - // For inputs if one of the inputs fail we move on to the next case (we do not move - // on to the next input as it doesn't make sense. It depends on the previous one). - for (input_idx, input) in case.inputs_iterator().enumerate() { - let tracing_span = tracing::info_span!("Handling input", input_idx); - let _guard = tracing_span.enter(); - - let execution_result = - tracing::info_span!("Executing input", contract_name = ?input.instance) - .in_scope(|| { - let (leader_receipt, _, leader_diff) = match leader_state - .handle_input( - self.metadata, + let input_execution_result = + tracing::info_span!("Executing input", contract_name = ?input.instance) + .in_scope(|| { + let (leader_receipt, _, leader_diff) = match leader_state.handle_input( + self.metadata, + case_idx, + &input, + self.leader_node, + &mode, + ) { + Ok(result) => result, + Err(error) => { + tracing::error!( + target = ?Target::Leader, + ?error, + "Contract execution failed" + ); + execution_result.add_failed_case( + Target::Leader, + mode.clone(), + case.name.as_deref().unwrap_or("no case name").to_owned(), case_idx, - &input, - self.leader_node, - &mode, - ) { - Ok(result) => result, - Err(error) => { - tracing::error!( - target = ?Target::Leader, - ?error, - "Contract execution failed" - ); - execution_result.add_failed_case( - Target::Leader, - mode.clone(), - case.name - .as_deref() - .unwrap_or("no case name") - .to_owned(), - case_idx, - input_idx, - anyhow::Error::msg(format!("{error}")), - ); - return Err(error); - } - }; + input_idx, + anyhow::Error::msg(format!("{error}")), + ); + return Err(error); + } + }; - let (follower_receipt, _, follower_diff) = match follower_state - .handle_input( - self.metadata, + let (follower_receipt, _, follower_diff) = match follower_state + .handle_input( + self.metadata, + case_idx, + &input, + self.follower_node, + &mode, + ) { + Ok(result) => result, + Err(error) => { + tracing::error!( + target = ?Target::Follower, + ?error, + "Contract execution failed" + ); + execution_result.add_failed_case( + Target::Follower, + mode.clone(), + case.name.as_deref().unwrap_or("no case name").to_owned(), case_idx, - &input, - self.follower_node, - &mode, - ) { - Ok(result) => result, - Err(error) => { - tracing::error!( - target = ?Target::Follower, - ?error, - "Contract execution failed" - ); - execution_result.add_failed_case( - Target::Follower, - mode.clone(), - case.name - .as_deref() - .unwrap_or("no case name") - .to_owned(), - case_idx, - input_idx, - anyhow::Error::msg(format!("{error}")), - ); - return Err(error); - } - }; + input_idx, + anyhow::Error::msg(format!("{error}")), + ); + return Err(error); + } + }; - Ok((leader_receipt, leader_diff, follower_receipt, follower_diff)) - }); - let Ok((leader_receipt, leader_diff, follower_receipt, follower_diff)) = - execution_result - else { - // Noting it again here: if something in the input fails we do not move on - // to the next input, we move to the next case completely. - continue 'case_loop; - }; + Ok((leader_receipt, leader_diff, follower_receipt, follower_diff)) + }); + let Ok((leader_receipt, leader_diff, follower_receipt, follower_diff)) = + input_execution_result + else { + return execution_result; + }; - if leader_diff == follower_diff { - tracing::debug!("State diffs match between leader and follower."); - } else { - tracing::debug!("State diffs mismatch between leader and follower."); - Self::trace_diff_mode("Leader", &leader_diff); - Self::trace_diff_mode("Follower", &follower_diff); - } - - if leader_receipt.logs() != follower_receipt.logs() { - tracing::debug!("Log/event mismatch between leader and follower."); - tracing::trace!("Leader logs: {:?}", leader_receipt.logs()); - tracing::trace!("Follower logs: {:?}", follower_receipt.logs()); - } + if leader_diff == follower_diff { + tracing::debug!("State diffs match between leader and follower."); + } else { + tracing::debug!("State diffs mismatch between leader and follower."); + Self::trace_diff_mode("Leader", &leader_diff); + Self::trace_diff_mode("Follower", &follower_diff); } - // Note: Only consider the case as having been successful after we have processed - // all of the inputs and completed the entire loop over the input. - execution_result.add_successful_case( - Target::Leader, - mode.clone(), - case.name.clone().unwrap_or("no case name".to_owned()), - case_idx, - ); - execution_result.add_successful_case( - Target::Follower, - mode.clone(), - case.name.clone().unwrap_or("no case name".to_owned()), - case_idx, - ); + if leader_receipt.logs() != follower_receipt.logs() { + tracing::debug!("Log/event mismatch between leader and follower."); + tracing::trace!("Leader logs: {:?}", leader_receipt.logs()); + tracing::trace!("Follower logs: {:?}", follower_receipt.logs()); + } } + + // Note: Only consider the case as having been successful after we have processed + // all of the inputs and completed the entire loop over the input. + execution_result.add_successful_case( + Target::Leader, + mode.clone(), + case.name.clone().unwrap_or("no case name".to_owned()), + case_idx, + ); + execution_result.add_successful_case( + Target::Follower, + mode.clone(), + case.name.clone().unwrap_or("no case name".to_owned()), + case_idx, + ); } execution_result diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index c8403af..84a0c90 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -63,7 +63,7 @@ fn init_cli() -> anyhow::Result { tracing::info!("workdir: {}", args.directory().display()); ThreadPoolBuilder::new() - .num_threads(args.workers) + .num_threads(args.number_of_threads) .build_global()?; Ok(args) @@ -93,15 +93,25 @@ where let leader_nodes = NodePool::::new(args)?; let follower_nodes = NodePool::::new(args)?; - tests.par_iter().for_each( - |MetadataFile { - content: metadata, - path: metadata_file_path, - }| { - // Starting a new tracing span for this metadata file. This allows our logs to be clear - // about which metadata file the logs belong to. We can add other information into this - // as well to be able to associate the logs with the correct metadata file and case - // that's being executed. + let test_cases = tests + .iter() + .flat_map( + |MetadataFile { + path, + content: metadata, + }| { + metadata + .cases + .iter() + .enumerate() + .map(move |(case_idx, case)| (path, metadata, case_idx, case)) + }, + ) + .collect::>(); + + test_cases + .into_par_iter() + .for_each(|(metadata_file_path, metadata, case_idx, case)| { let tracing_span = tracing::span!( Level::INFO, "Running driver", @@ -111,6 +121,8 @@ where let mut driver = Driver::::new( metadata, + case, + case_idx, args, leader_nodes.round_robbin(), follower_nodes.round_robbin(), @@ -135,8 +147,7 @@ where } else { tracing::info!("Execution failed"); } - }, - ); + }); Ok(()) } diff --git a/crates/format/src/metadata.rs b/crates/format/src/metadata.rs index b72376b..aad74fc 100644 --- a/crates/format/src/metadata.rs +++ b/crates/format/src/metadata.rs @@ -310,16 +310,22 @@ impl FromStr for ContractPathAndIdent { identifier = Some(next_item.to_owned()) } } - let Some(path) = path else { - anyhow::bail!("Path is not defined"); - }; - let Some(identifier) = identifier else { - anyhow::bail!("Contract identifier is not defined") - }; - Ok(Self { - contract_source_path: PathBuf::from(path), - contract_ident: ContractIdent::new(identifier), - }) + match (path, identifier) { + (Some(path), Some(identifier)) => Ok(Self { + contract_source_path: PathBuf::from(path), + contract_ident: ContractIdent::new(identifier), + }), + (None, Some(path)) | (Some(path), None) => { + let Some(identifier) = path.split(".").next().map(ToOwned::to_owned) else { + anyhow::bail!("Failed to find identifier"); + }; + Ok(Self { + contract_source_path: PathBuf::from(path), + contract_ident: ContractIdent::new(identifier), + }) + } + (None, None) => anyhow::bail!("Failed to find the path and identifier"), + } } } diff --git a/crates/node/src/pool.rs b/crates/node/src/pool.rs index 10d4d59..8cb83cc 100644 --- a/crates/node/src/pool.rs +++ b/crates/node/src/pool.rs @@ -24,7 +24,7 @@ where { /// Create a new Pool. This will start as many nodes as there are workers in `config`. pub fn new(config: &Arguments) -> anyhow::Result { - let nodes = config.workers; + let nodes = config.number_of_nodes; let genesis = read_to_string(&config.genesis_file).context(format!( "can not read genesis file: {}", config.genesis_file.display()