diff --git a/Cargo.lock b/Cargo.lock index ef552af..7e0f75d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4588,6 +4588,7 @@ dependencies = [ name = "revive-dt-report" version = "0.1.0" dependencies = [ + "alloy-primitives", "anyhow", "indexmap 2.10.0", "paste", @@ -4600,6 +4601,7 @@ dependencies = [ "serde_json", "serde_with", "tokio", + "tracing", ] [[package]] diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 86ba19e..1f62dd1 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -124,6 +124,14 @@ pub struct Arguments { /// Controls if the compilation cache should be invalidated or not. #[arg(short, long)] pub invalidate_compilation_cache: bool, + + /// Controls if the compiler input is included in the final report. + #[clap(long = "report.include-compiler-input")] + pub report_include_compiler_input: bool, + + /// Controls if the compiler output is included in the final report. + #[clap(long = "report.include-compiler-output")] + pub report_include_compiler_output: bool, } impl Arguments { diff --git a/crates/core/src/cached_compiler.rs b/crates/core/src/cached_compiler.rs index b94b1f3..188befb 100644 --- a/crates/core/src/cached_compiler.rs +++ b/crates/core/src/cached_compiler.rs @@ -9,7 +9,7 @@ use std::{ use futures::FutureExt; use revive_dt_common::iterators::FilesWithExtensionIterator; -use revive_dt_compiler::{Compiler, CompilerOutput, Mode, SolidityCompiler}; +use revive_dt_compiler::{Compiler, CompilerInput, CompilerOutput, Mode, SolidityCompiler}; use revive_dt_config::Arguments; use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata}; @@ -35,6 +35,7 @@ impl CachedCompiler { } /// Compiles or gets the compilation artifacts from the cache. + #[allow(clippy::too_many_arguments)] #[instrument( level = "debug", skip_all, @@ -52,6 +53,19 @@ impl CachedCompiler { mode: &Mode, config: &Arguments, deployed_libraries: Option<&HashMap>, + compilation_success_report_callback: impl Fn( + Version, + PathBuf, + bool, + Option, + CompilerOutput, + ) + Clone, + compilation_failure_report_callback: impl Fn( + Option, + Option, + Option, + String, + ), ) -> Result<(CompilerOutput, Version)> { static CACHE_KEY_LOCK: Lazy>>>> = Lazy::new(Default::default); @@ -61,10 +75,21 @@ impl CachedCompiler { config, compiler_version_or_requirement, ) - .await?; + .await + .inspect_err(|err| { + compilation_failure_report_callback(None, None, None, err.to_string()) + })?; let compiler_version = ::new(compiler_path.clone()) .version() - .await?; + .await + .inspect_err(|err| { + compilation_failure_report_callback( + None, + Some(compiler_path.clone()), + None, + err.to_string(), + ) + })?; let cache_key = CacheKey { platform_key: P::config_id().to_string(), @@ -74,13 +99,19 @@ impl CachedCompiler { }; let compilation_callback = || { + let compiler_path = compiler_path.clone(); + let compiler_version = compiler_version.clone(); + let compilation_success_report_callback = compilation_success_report_callback.clone(); async move { compile_contracts::

( metadata.directory()?, compiler_path, + compiler_version, metadata.files_to_compile()?, mode, deployed_libraries, + compilation_success_report_callback, + compilation_failure_report_callback, ) .map(|compilation_result| compilation_result.map(CacheValue::new)) .await @@ -125,10 +156,19 @@ impl CachedCompiler { }; let _guard = mutex.lock().await; - self.0 - .get_or_insert_with(&cache_key, compilation_callback) - .await - .map(|value| value.compiler_output)? + match self.0.get(&cache_key).await { + Some(cache_value) => { + compilation_success_report_callback( + compiler_version.clone(), + compiler_path, + true, + None, + cache_value.compiler_output.clone(), + ); + cache_value.compiler_output + } + None => compilation_callback().await?.compiler_output, + } } }; @@ -136,19 +176,34 @@ impl CachedCompiler { } } +#[allow(clippy::too_many_arguments)] async fn compile_contracts( metadata_directory: impl AsRef, compiler_path: impl AsRef, + compiler_version: Version, mut files_to_compile: impl Iterator, mode: &Mode, deployed_libraries: Option<&HashMap>, + compilation_success_report_callback: impl Fn( + Version, + PathBuf, + bool, + Option, + CompilerOutput, + ), + compilation_failure_report_callback: impl Fn( + Option, + Option, + Option, + String, + ), ) -> Result { let all_sources_in_dir = FilesWithExtensionIterator::new(metadata_directory.as_ref()) .with_allowed_extension("sol") .with_use_cached_fs(true) .collect::>(); - Compiler::::new() + let compiler = Compiler::::new() .with_allow_path(metadata_directory) // Handling the modes .with_optimization(mode.optimize_setting) @@ -156,6 +211,14 @@ async fn compile_contracts( // Adding the contract sources to the compiler. .try_then(|compiler| { files_to_compile.try_fold(compiler, |compiler, path| compiler.with_source(path)) + }) + .inspect_err(|err| { + compilation_failure_report_callback( + Some(compiler_version.clone()), + Some(compiler_path.as_ref().to_path_buf()), + None, + err.to_string(), + ) })? // Adding the deployed libraries to the compiler. .then(|compiler| { @@ -171,9 +234,28 @@ async fn compile_contracts( .fold(compiler, |compiler, (ident, address, path)| { compiler.with_library(path, ident.as_str(), *address) }) - }) - .try_build(compiler_path) + }); + + let compiler_input = compiler.input(); + let compiler_output = compiler + .try_build(compiler_path.as_ref()) .await + .inspect_err(|err| { + compilation_failure_report_callback( + Some(compiler_version.clone()), + Some(compiler_path.as_ref().to_path_buf()), + Some(compiler_input.clone()), + err.to_string(), + ) + })?; + compilation_success_report_callback( + compiler_version, + compiler_path.as_ref().to_path_buf(), + false, + Some(compiler_input), + compiler_output.clone(), + ); + Ok(compiler_output) } struct ArtifactsCache { diff --git a/crates/core/src/main.rs b/crates/core/src/main.rs index ae853d8..f24e7dc 100644 --- a/crates/core/src/main.rs +++ b/crates/core/src/main.rs @@ -1,7 +1,7 @@ mod cached_compiler; use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, io::{BufWriter, Write, stderr}, path::Path, sync::{Arc, LazyLock}, @@ -19,10 +19,11 @@ use futures::{Stream, StreamExt}; use indexmap::IndexMap; use revive_dt_node_interaction::EthereumNode; use revive_dt_report::{ - ReportAggregator, Reporter, ReporterEvent, TestCaseStatus, TestSpecificReporter, TestSpecifier, + NodeDesignation, ReportAggregator, Reporter, ReporterEvent, TestCaseStatus, + TestSpecificReporter, TestSpecifier, }; use temp_dir::TempDir; -use tokio::{join, sync::broadcast::Receiver, try_join}; +use tokio::{join, try_join}; use tracing::{debug, info, info_span, instrument}; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{EnvFilter, FmtSubscriber}; @@ -181,13 +182,11 @@ where L::Blockchain: revive_dt_node::Node + Send + Sync + 'static, F::Blockchain: revive_dt_node::Node + Send + Sync + 'static, { - let reporter_events_rx = reporter.subscribe().await?; - - let tests = prepare_tests::(args, metadata_files, reporter); + let tests = prepare_tests::(args, metadata_files, reporter.clone()); let driver_task = start_driver_task::(args, tests).await?; - let cli_reporting_task = start_cli_reporting_task(reporter_events_rx); + let cli_reporting_task = start_cli_reporting_task(reporter); - let (_, _, rtn) = tokio::join!(cli_reporting_task, driver_task, report_aggregator_task,); + let (_, _, rtn) = tokio::join!(cli_reporting_task, driver_task, report_aggregator_task); rtn?; Ok(()) @@ -441,6 +440,8 @@ where L::Blockchain: revive_dt_node::Node + Send + Sync + 'static, F::Blockchain: revive_dt_node::Node + Send + Sync + 'static, { + info!("Starting driver task"); + let leader_nodes = Arc::new(NodePool::::new(args)?); let follower_nodes = Arc::new(NodePool::::new(args)?); let number_concurrent_tasks = args.number_of_concurrent_tasks(); @@ -510,7 +511,10 @@ where #[allow(clippy::uninlined_format_args)] #[allow(irrefutable_let_patterns)] -async fn start_cli_reporting_task(mut aggregator_events_rx: Receiver) { +async fn start_cli_reporting_task(reporter: Reporter) { + let mut aggregator_events_rx = reporter.subscribe().await.expect("Can't fail"); + drop(reporter); + let start = Instant::now(); const GREEN: &str = "\x1B[32m"; @@ -606,6 +610,13 @@ where L::Blockchain: revive_dt_node::Node + Send + Sync + 'static, F::Blockchain: revive_dt_node::Node + Send + Sync + 'static, { + let leader_reporter = test + .reporter + .execution_specific_reporter(leader_node.id(), NodeDesignation::Leader); + let follower_reporter = test + .reporter + .execution_specific_reporter(follower_node.id(), NodeDesignation::Follower); + let ( ( CompilerOutput { @@ -625,14 +636,56 @@ where test.metadata_file_path, &test.mode, config, - None + None, + |compiler_version, compiler_path, is_cached, compiler_input, compiler_output| { + leader_reporter + .report_pre_link_contracts_compilation_succeeded_event( + compiler_version, + compiler_path, + is_cached, + compiler_input, + compiler_output, + ) + .expect("Can't fail") + }, + |compiler_version, compiler_path, compiler_input, failure_reason| { + leader_reporter + .report_pre_link_contracts_compilation_failed_event( + compiler_version, + compiler_path, + compiler_input, + failure_reason, + ) + .expect("Can't fail") + } ), cached_compiler.compile_contracts::( test.metadata, test.metadata_file_path, &test.mode, config, - None + None, + |compiler_version, compiler_path, is_cached, compiler_input, compiler_output| { + follower_reporter + .report_pre_link_contracts_compilation_succeeded_event( + compiler_version, + compiler_path, + is_cached, + compiler_input, + compiler_output, + ) + .expect("Can't fail") + }, + |compiler_version, compiler_path, compiler_input, failure_reason| { + follower_reporter + .report_pre_link_contracts_compilation_failed_event( + compiler_version, + compiler_path, + compiler_input, + failure_reason, + ) + .expect("Can't fail") + } ) )?; @@ -740,6 +793,24 @@ where ), ); } + if let Some(ref leader_deployed_libraries) = leader_deployed_libraries { + leader_reporter.report_libraries_deployed_event( + leader_deployed_libraries + .clone() + .into_iter() + .map(|(key, (_, address, _))| (key, address)) + .collect::>(), + )?; + } + if let Some(ref follower_deployed_libraries) = follower_deployed_libraries { + follower_reporter.report_libraries_deployed_event( + follower_deployed_libraries + .clone() + .into_iter() + .map(|(key, (_, address, _))| (key, address)) + .collect::>(), + )?; + } let ( ( @@ -760,14 +831,56 @@ where test.metadata_file_path, &test.mode, config, - leader_deployed_libraries.as_ref() + leader_deployed_libraries.as_ref(), + |compiler_version, compiler_path, is_cached, compiler_input, compiler_output| { + leader_reporter + .report_post_link_contracts_compilation_succeeded_event( + compiler_version, + compiler_path, + is_cached, + compiler_input, + compiler_output, + ) + .expect("Can't fail") + }, + |compiler_version, compiler_path, compiler_input, failure_reason| { + leader_reporter + .report_post_link_contracts_compilation_failed_event( + compiler_version, + compiler_path, + compiler_input, + failure_reason, + ) + .expect("Can't fail") + } ), cached_compiler.compile_contracts::( test.metadata, test.metadata_file_path, &test.mode, config, - follower_deployed_libraries.as_ref() + follower_deployed_libraries.as_ref(), + |compiler_version, compiler_path, is_cached, compiler_input, compiler_output| { + follower_reporter + .report_post_link_contracts_compilation_succeeded_event( + compiler_version, + compiler_path, + is_cached, + compiler_input, + compiler_output, + ) + .expect("Can't fail") + }, + |compiler_version, compiler_path, compiler_input, failure_reason| { + follower_reporter + .report_post_link_contracts_compilation_failed_event( + compiler_version, + compiler_path, + compiler_input, + failure_reason, + ) + .expect("Can't fail") + } ) )?; @@ -849,6 +962,8 @@ async fn compile_corpus( &mode, config, None, + |_, _, _, _, _| {}, + |_, _, _, _| {}, ) .await; } @@ -860,6 +975,8 @@ async fn compile_corpus( &mode, config, None, + |_, _, _, _, _| {}, + |_, _, _, _| {}, ) .await; } diff --git a/crates/node/src/pool.rs b/crates/node/src/pool.rs index 015c004..d195988 100644 --- a/crates/node/src/pool.rs +++ b/crates/node/src/pool.rs @@ -9,6 +9,7 @@ use revive_dt_common::cached_fs::read_to_string; use anyhow::Context; use revive_dt_config::Arguments; +use tracing::info; use crate::Node; @@ -63,6 +64,16 @@ where fn spawn_node(args: &Arguments, genesis: String) -> anyhow::Result { let mut node = T::new(args); + info!( + id = node.id(), + connection_string = node.connection_string(), + "Spawning node" + ); node.spawn(genesis)?; + info!( + id = node.id(), + connection_string = node.connection_string(), + "Spawned node" + ); Ok(node) } diff --git a/crates/report/Cargo.toml b/crates/report/Cargo.toml index 33982fb..eae7fa7 100644 --- a/crates/report/Cargo.toml +++ b/crates/report/Cargo.toml @@ -13,6 +13,7 @@ revive-dt-config = { workspace = true } revive-dt-format = { workspace = true } revive-dt-compiler = { workspace = true } +alloy-primitives = { workspace = true } anyhow = { workspace = true } paste = { workspace = true } indexmap = { workspace = true, features = ["serde"] } @@ -21,6 +22,7 @@ serde = { workspace = true } serde_json = { workspace = true } serde_with = { workspace = true } tokio = { workspace = true } +tracing = { workspace = true } [lints] workspace = true diff --git a/crates/report/src/aggregator.rs b/crates/report/src/aggregator.rs index 46f75f8..457acd1 100644 --- a/crates/report/src/aggregator.rs +++ b/crates/report/src/aggregator.rs @@ -4,20 +4,24 @@ use std::{ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, fs::OpenOptions, + path::PathBuf, time::{SystemTime, UNIX_EPOCH}, }; +use alloy_primitives::Address; use anyhow::Result; use indexmap::IndexMap; -use revive_dt_compiler::Mode; +use revive_dt_compiler::{CompilerInput, CompilerOutput, Mode}; use revive_dt_config::{Arguments, TestingPlatform}; -use revive_dt_format::{case::CaseIdx, corpus::Corpus}; +use revive_dt_format::{case::CaseIdx, corpus::Corpus, metadata::ContractInstance}; +use semver::Version; use serde::Serialize; use serde_with::{DisplayFromStr, serde_as}; use tokio::sync::{ broadcast::{Sender, channel}, mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel}, }; +use tracing::debug; use crate::*; @@ -54,7 +58,10 @@ impl ReportAggregator { } async fn aggregate(mut self) -> Result<()> { + debug!("Starting to aggregate report"); + while let Some(event) = self.runner_rx.recv().await { + debug!(?event, "Received Event"); match event { RunnerEvent::SubscribeToEvents(event) => { self.handle_subscribe_to_events_event(*event); @@ -83,8 +90,24 @@ impl ReportAggregator { RunnerEvent::FollowerNodeAssigned(event) => { self.handle_follower_node_assigned_event(*event); } + RunnerEvent::PreLinkContractsCompilationSucceeded(event) => { + self.handle_pre_link_contracts_compilation_succeeded_event(*event) + } + RunnerEvent::PostLinkContractsCompilationSucceeded(event) => { + self.handle_post_link_contracts_compilation_succeeded_event(*event) + } + RunnerEvent::PreLinkContractsCompilationFailed(event) => { + self.handle_pre_link_contracts_compilation_failed_event(*event) + } + RunnerEvent::PostLinkContractsCompilationFailed(event) => { + self.handle_post_link_contracts_compilation_failed_event(*event) + } + RunnerEvent::LibrariesDeployed(event) => { + self.handle_libraries_deployed_event(*event); + } } } + debug!("Report aggregation completed"); let file_name = { let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); @@ -232,6 +255,113 @@ impl ReportAggregator { }); } + fn handle_pre_link_contracts_compilation_succeeded_event( + &mut self, + event: PreLinkContractsCompilationSucceededEvent, + ) { + let include_input = self.report.config.report_include_compiler_input; + let include_output = self.report.config.report_include_compiler_output; + + let execution_information = self.execution_information(&event.execution_specifier); + + let compiler_input = if include_input { + event.compiler_input + } else { + None + }; + let compiler_output = if include_output { + Some(event.compiler_output) + } else { + None + }; + + execution_information.pre_link_compilation_status = Some(CompilationStatus::Success { + is_cached: event.is_cached, + compiler_version: event.compiler_version, + compiler_path: event.compiler_path, + compiler_input, + compiler_output, + }); + } + + fn handle_post_link_contracts_compilation_succeeded_event( + &mut self, + event: PostLinkContractsCompilationSucceededEvent, + ) { + let include_input = self.report.config.report_include_compiler_input; + let include_output = self.report.config.report_include_compiler_output; + + let execution_information = self.execution_information(&event.execution_specifier); + + let compiler_input = if include_input { + event.compiler_input + } else { + None + }; + let compiler_output = if include_output { + Some(event.compiler_output) + } else { + None + }; + + execution_information.post_link_compilation_status = Some(CompilationStatus::Success { + is_cached: event.is_cached, + compiler_version: event.compiler_version, + compiler_path: event.compiler_path, + compiler_input, + compiler_output, + }); + } + + fn handle_pre_link_contracts_compilation_failed_event( + &mut self, + event: PreLinkContractsCompilationFailedEvent, + ) { + let include_input = self.report.config.report_include_compiler_input; + + let execution_information = self.execution_information(&event.execution_specifier); + + let compiler_input = if include_input { + event.compiler_input + } else { + None + }; + + execution_information.pre_link_compilation_status = Some(CompilationStatus::Failure { + reason: event.reason, + compiler_version: event.compiler_version, + compiler_path: event.compiler_path, + compiler_input, + }); + } + + fn handle_post_link_contracts_compilation_failed_event( + &mut self, + event: PostLinkContractsCompilationFailedEvent, + ) { + let include_input = self.report.config.report_include_compiler_input; + + let execution_information = self.execution_information(&event.execution_specifier); + + let compiler_input = if include_input { + event.compiler_input + } else { + None + }; + + execution_information.post_link_compilation_status = Some(CompilationStatus::Failure { + reason: event.reason, + compiler_version: event.compiler_version, + compiler_path: event.compiler_path, + compiler_input, + }); + } + + fn handle_libraries_deployed_event(&mut self, event: LibrariesDeployedEvent) { + self.execution_information(&event.execution_specifier) + .deployed_libraries = Some(event.libraries); + } + fn test_case_report(&mut self, specifier: &TestSpecifier) -> &mut TestCaseReport { self.report .test_case_information @@ -242,6 +372,21 @@ impl ReportAggregator { .entry(specifier.case_idx) .or_default() } + + fn execution_information( + &mut self, + specifier: &ExecutionSpecifier, + ) -> &mut ExecutionInformation { + let test_case_report = self.test_case_report(&specifier.test_specifier); + match specifier.node_designation { + NodeDesignation::Leader => test_case_report + .leader_execution_information + .get_or_insert_default(), + NodeDesignation::Follower => test_case_report + .follower_execution_information + .get_or_insert_default(), + } + } } #[serde_as] @@ -287,6 +432,12 @@ pub struct TestCaseReport { /// Information related to the follower node assigned to this test case. #[serde(skip_serializing_if = "Option::is_none")] pub follower_node: Option, + /// Information related to the execution on the leader. + #[serde(skip_serializing_if = "Option::is_none")] + pub leader_execution_information: Option, + /// Information related to the execution on the follower. + #[serde(skip_serializing_if = "Option::is_none")] + pub follower_execution_information: Option, } /// Information related to the status of the test. Could be that the test succeeded, failed, or that @@ -324,3 +475,57 @@ pub struct TestCaseNodeInformation { /// The connection string of the node. pub connection_string: String, } + +/// Execution information tied to the leader or the follower. +#[derive(Clone, Debug, Default, Serialize)] +pub struct ExecutionInformation { + /// Information on the pre-link compiled contracts. + #[serde(skip_serializing_if = "Option::is_none")] + pub pre_link_compilation_status: Option, + /// Information on the post-link compiled contracts. + #[serde(skip_serializing_if = "Option::is_none")] + pub post_link_compilation_status: Option, + /// Information on the deployed libraries. + #[serde(skip_serializing_if = "Option::is_none")] + pub deployed_libraries: Option>, +} + +/// Information related to compilation +#[derive(Clone, Debug, Serialize)] +#[serde(tag = "status")] +pub enum CompilationStatus { + /// The compilation was successful. + Success { + /// A flag with information on whether the compilation artifacts were cached or not. + is_cached: bool, + /// The version of the compiler used to compile the contracts. + compiler_version: Version, + /// The path of the compiler used to compile the contracts. + compiler_path: PathBuf, + /// The input provided to the compiler to compile the contracts. This is only included if + /// the appropriate flag is set in the CLI configuration and if the contracts were not + /// cached and the compiler was invoked. + #[serde(skip_serializing_if = "Option::is_none")] + compiler_input: Option, + /// The output of the compiler. This is only included if the appropriate flag is set in the + /// CLI configurations. + #[serde(skip_serializing_if = "Option::is_none")] + compiler_output: Option, + }, + /// The compilation failed. + Failure { + /// The failure reason. + reason: String, + /// The version of the compiler used to compile the contracts. + #[serde(skip_serializing_if = "Option::is_none")] + compiler_version: Option, + /// The path of the compiler used to compile the contracts. + #[serde(skip_serializing_if = "Option::is_none")] + compiler_path: Option, + /// The input provided to the compiler to compile the contracts. This is only included if + /// the appropriate flag is set in the CLI configuration and if the contracts were not + /// cached and the compiler was invoked. + #[serde(skip_serializing_if = "Option::is_none")] + compiler_input: Option, + }, +} diff --git a/crates/report/src/common.rs b/crates/report/src/common.rs index 4f27568..550a5a9 100644 --- a/crates/report/src/common.rs +++ b/crates/report/src/common.rs @@ -1,12 +1,11 @@ //! Common types and functions used throughout the crate. -use std::path::PathBuf; +use std::{path::PathBuf, sync::Arc}; use revive_dt_common::define_wrapper_type; use revive_dt_compiler::Mode; use revive_dt_format::case::CaseIdx; use serde::{Deserialize, Serialize}; -use serde_with::{DisplayFromStr, serde_as}; define_wrapper_type!( #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] @@ -15,11 +14,24 @@ define_wrapper_type!( ); /// An absolute specifier for a test. -#[serde_as] -#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct TestSpecifier { - #[serde_as(as = "DisplayFromStr")] pub solc_mode: Mode, pub metadata_file_path: PathBuf, pub case_idx: CaseIdx, } + +/// An absolute path for a test that also includes information about the node that it's assigned to +/// and whether it's the leader or follower. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ExecutionSpecifier { + pub test_specifier: Arc, + pub node_id: usize, + pub node_designation: NodeDesignation, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum NodeDesignation { + Leader, + Follower, +} diff --git a/crates/report/src/runner_event.rs b/crates/report/src/runner_event.rs index d95f174..7a915b1 100644 --- a/crates/report/src/runner_event.rs +++ b/crates/report/src/runner_event.rs @@ -1,17 +1,20 @@ //! The types associated with the events sent by the runner to the reporter. #![allow(dead_code)] -use std::sync::Arc; +use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; +use alloy_primitives::Address; use indexmap::IndexMap; +use revive_dt_compiler::{CompilerInput, CompilerOutput}; use revive_dt_config::TestingPlatform; -use revive_dt_format::corpus::Corpus; use revive_dt_format::metadata::Metadata; +use revive_dt_format::{corpus::Corpus, metadata::ContractInstance}; +use semver::Version; use tokio::sync::{broadcast, oneshot}; -use crate::{ReporterEvent, TestSpecifier, common::MetadataFilePath}; +use crate::{ExecutionSpecifier, ReporterEvent, TestSpecifier, common::MetadataFilePath}; -macro_rules! __report_gen__emit_test_specific { +macro_rules! __report_gen_emit_test_specific { ( $ident:ident, $variant_ident:ident, @@ -36,21 +39,21 @@ macro_rules! __report_gen__emit_test_specific { }; } -macro_rules! __report_gen__emit_test_specific_by_parse { +macro_rules! __report_gen_emit_test_specific_by_parse { ( $ident:ident, $variant_ident:ident, $skip_field:ident; $( $bname:ident : $bty:ty, )* ; $( $aname:ident : $aty:ty, )* ) => { - __report_gen__emit_test_specific!( + __report_gen_emit_test_specific!( $ident, $variant_ident, $skip_field; $( $bname : $bty, )* ; $( $aname : $aty, )* ); }; } -macro_rules! __report_gen__scan_before { +macro_rules! __report_gen_scan_before { ( $ident:ident, $variant_ident:ident; $( $before:ident : $bty:ty, )* @@ -59,7 +62,7 @@ macro_rules! __report_gen__scan_before { $( $after:ident : $aty:ty, )* ; ) => { - __report_gen__emit_test_specific_by_parse!( + __report_gen_emit_test_specific_by_parse!( $ident, $variant_ident, test_specifier; $( $before : $bty, )* ; $( $after : $aty, )* ); @@ -71,7 +74,7 @@ macro_rules! __report_gen__scan_before { $name:ident : $ty:ty, $( $after:ident : $aty:ty, )* ; ) => { - __report_gen__scan_before!( + __report_gen_scan_before!( $ident, $variant_ident; $( $before : $bty, )* $name : $ty, ; @@ -97,7 +100,7 @@ macro_rules! __report_gen_for_variant { $variant_ident:ident; $( $field_ident:ident : $field_ty:ty ),+ $(,)? ) => { - __report_gen__scan_before!( + __report_gen_scan_before!( $ident, $variant_ident; ; $( $field_ident : $field_ty, )* @@ -106,11 +109,99 @@ macro_rules! __report_gen_for_variant { }; } -macro_rules! keep_if_doc { - (#[doc = $doc:expr]) => { - #[doc = $doc] +macro_rules! __report_gen_emit_execution_specific { + ( + $ident:ident, + $variant_ident:ident, + $skip_field:ident; + $( $bname:ident : $bty:ty, )* + ; + $( $aname:ident : $aty:ty, )* + ) => { + paste::paste! { + pub fn [< report_ $variant_ident:snake _event >]( + &self + $(, $bname: impl Into<$bty> )* + $(, $aname: impl Into<$aty> )* + ) -> anyhow::Result<()> { + self.report([< $variant_ident Event >] { + $skip_field: self.execution_specifier.clone() + $(, $bname: $bname.into() )* + $(, $aname: $aname.into() )* + }) + } + } + }; +} + +macro_rules! __report_gen_emit_execution_specific_by_parse { + ( + $ident:ident, + $variant_ident:ident, + $skip_field:ident; + $( $bname:ident : $bty:ty, )* ; $( $aname:ident : $aty:ty, )* + ) => { + __report_gen_emit_execution_specific!( + $ident, $variant_ident, $skip_field; + $( $bname : $bty, )* ; $( $aname : $aty, )* + ); + }; +} + +macro_rules! __report_gen_scan_before_exec { + ( + $ident:ident, $variant_ident:ident; + $( $before:ident : $bty:ty, )* + ; + execution_specifier : $skip_ty:ty, + $( $after:ident : $aty:ty, )* + ; + ) => { + __report_gen_emit_execution_specific_by_parse!( + $ident, $variant_ident, execution_specifier; + $( $before : $bty, )* ; $( $after : $aty, )* + ); + }; + ( + $ident:ident, $variant_ident:ident; + $( $before:ident : $bty:ty, )* + ; + $name:ident : $ty:ty, $( $after:ident : $aty:ty, )* + ; + ) => { + __report_gen_scan_before_exec!( + $ident, $variant_ident; + $( $before : $bty, )* $name : $ty, + ; + $( $after : $aty, )* + ; + ); + }; + ( + $ident:ident, $variant_ident:ident; + $( $before:ident : $bty:ty, )* + ; + ; + ) => {}; +} + +macro_rules! __report_gen_for_variant_exec { + ( + $ident:ident, + $variant_ident:ident; + ) => {}; + ( + $ident:ident, + $variant_ident:ident; + $( $field_ident:ident : $field_ty:ty ),+ $(,)? + ) => { + __report_gen_scan_before_exec!( + $ident, $variant_ident; + ; + $( $field_ident : $field_ty, )* + ; + ); }; - ( $($_:tt)* ) => {}; } /// Defines the runner-event which is sent from the test runners to the report aggregator. @@ -149,6 +240,7 @@ macro_rules! define_event { ) => { paste::paste! { $(#[$enum_meta])* + #[derive(Debug)] $vis enum $ident { $( $(#[$variant_meta])* @@ -157,6 +249,7 @@ macro_rules! define_event { } $( + #[derive(Debug)] $(#[$variant_meta])* $vis struct [<$variant_ident Event>] { $( @@ -204,7 +297,6 @@ macro_rules! define_event { } $( - keep_if_doc!($(#[$variant_meta])*); pub fn [< report_ $variant_ident:snake _event >](&self, $($field_ident: impl Into<$field_ty>),*) -> anyhow::Result<()> { self.report([< $variant_ident Event >] { $($field_ident: $field_ident.into()),* @@ -221,6 +313,21 @@ macro_rules! define_event { } impl [< $ident TestSpecificReporter >] { + pub fn execution_specific_reporter( + &self, + node_id: impl Into, + node_designation: impl Into<$crate::common::NodeDesignation> + ) -> [< $ident ExecutionSpecificReporter >] { + [< $ident ExecutionSpecificReporter >] { + reporter: self.reporter.clone(), + execution_specifier: Arc::new($crate::common::ExecutionSpecifier { + test_specifier: self.test_specifier.clone(), + node_id: node_id.into(), + node_designation: node_designation.into(), + }) + } + } + fn report(&self, event: impl Into<$ident>) -> anyhow::Result<()> { self.reporter.report(event) } @@ -229,6 +336,23 @@ macro_rules! define_event { __report_gen_for_variant! { $ident, $variant_ident; $( $field_ident : $field_ty ),* } )* } + + /// A reporter that's tied to a specific execution of the test case such as execution on + /// a specific node like the leader or follower. + #[derive(Clone, Debug)] + pub struct [< $ident ExecutionSpecificReporter >] { + $vis reporter: [< $ident Reporter >], + $vis execution_specifier: std::sync::Arc<$crate::common::ExecutionSpecifier>, + } + + impl [< $ident ExecutionSpecificReporter >] { + fn report(&self, event: impl Into<$ident>) -> anyhow::Result<()> { + self.reporter.report(event) + } + $( + __report_gen_for_variant_exec! { $ident, $variant_ident; $( $field_ident : $field_ty ),* } + )* + } } }; } @@ -303,7 +427,79 @@ define_event! { platform: TestingPlatform, /// The connection string of the node. connection_string: String, - } + }, + /// An event emitted by the runners when the compilation of the contracts has succeeded + /// on the pre-link contracts. + PreLinkContractsCompilationSucceeded { + /// A specifier for the execution that's taking place. + execution_specifier: Arc, + /// The version of the compiler used to compile the contracts. + compiler_version: Version, + /// The path of the compiler used to compile the contracts. + compiler_path: PathBuf, + /// A flag of whether the contract bytecode and ABI were cached or if they were compiled + /// anew. + is_cached: bool, + /// The input provided to the compiler - this is optional and not provided if the + /// contracts were obtained from the cache. + compiler_input: Option, + /// The output of the compiler. + compiler_output: CompilerOutput + }, + /// An event emitted by the runners when the compilation of the contracts has succeeded + /// on the post-link contracts. + PostLinkContractsCompilationSucceeded { + /// A specifier for the execution that's taking place. + execution_specifier: Arc, + /// The version of the compiler used to compile the contracts. + compiler_version: Version, + /// The path of the compiler used to compile the contracts. + compiler_path: PathBuf, + /// A flag of whether the contract bytecode and ABI were cached or if they were compiled + /// anew. + is_cached: bool, + /// The input provided to the compiler - this is optional and not provided if the + /// contracts were obtained from the cache. + compiler_input: Option, + /// The output of the compiler. + compiler_output: CompilerOutput + }, + /// An event emitted by the runners when the compilation of the pre-link contract has + /// failed. + PreLinkContractsCompilationFailed { + /// A specifier for the execution that's taking place. + execution_specifier: Arc, + /// The version of the compiler used to compile the contracts. + compiler_version: Option, + /// The path of the compiler used to compile the contracts. + compiler_path: Option, + /// The input provided to the compiler - this is optional and not provided if the + /// contracts were obtained from the cache. + compiler_input: Option, + /// The failure reason. + reason: String, + }, + /// An event emitted by the runners when the compilation of the post-link contract has + /// failed. + PostLinkContractsCompilationFailed { + /// A specifier for the execution that's taking place. + execution_specifier: Arc, + /// The version of the compiler used to compile the contracts. + compiler_version: Option, + /// The path of the compiler used to compile the contracts. + compiler_path: Option, + /// The input provided to the compiler - this is optional and not provided if the + /// contracts were obtained from the cache. + compiler_input: Option, + /// The failure reason. + reason: String, + }, + LibrariesDeployed { + /// A specifier for the execution that's taking place. + execution_specifier: Arc, + /// The addresses of the libraries that were deployed. + libraries: BTreeMap + }, } } @@ -318,3 +514,4 @@ impl RunnerEventReporter { pub type Reporter = RunnerEventReporter; pub type TestSpecificReporter = RunnerEventTestSpecificReporter; +pub type ExecutionSpecificReporter = RunnerEventExecutionSpecificReporter;