mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-19 16:51:03 +00:00
Replace infra with the dyn infra
This commit is contained in:
Generated
+4
@@ -4471,6 +4471,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"moka",
|
"moka",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"schemars 1.0.4",
|
||||||
"semver 1.0.26",
|
"semver 1.0.26",
|
||||||
"serde",
|
"serde",
|
||||||
"strum",
|
"strum",
|
||||||
@@ -4505,6 +4506,7 @@ dependencies = [
|
|||||||
"alloy",
|
"alloy",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"revive-dt-common",
|
||||||
"semver 1.0.26",
|
"semver 1.0.26",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -4586,6 +4588,8 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"revive-common",
|
||||||
|
"revive-dt-common",
|
||||||
"revive-dt-format",
|
"revive-dt-format",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ moka = { workspace = true, features = ["sync"] }
|
|||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
semver = { workspace = true }
|
semver = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
schemars = { workspace = true }
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
tokio = { workspace = true, default-features = false, features = ["time"] }
|
tokio = { workspace = true, default-features = false, features = ["time"] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{AsRefStr, Display, EnumString, IntoStaticStr};
|
use strum::{AsRefStr, Display, EnumString, IntoStaticStr};
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ use strum::{AsRefStr, Display, EnumString, IntoStaticStr};
|
|||||||
Display,
|
Display,
|
||||||
AsRefStr,
|
AsRefStr,
|
||||||
IntoStaticStr,
|
IntoStaticStr,
|
||||||
|
JsonSchema,
|
||||||
)]
|
)]
|
||||||
#[serde(rename = "kebab-case")]
|
#[serde(rename = "kebab-case")]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
@@ -52,9 +54,8 @@ pub enum PlatformIdentifier {
|
|||||||
Display,
|
Display,
|
||||||
AsRefStr,
|
AsRefStr,
|
||||||
IntoStaticStr,
|
IntoStaticStr,
|
||||||
|
JsonSchema,
|
||||||
)]
|
)]
|
||||||
#[serde(rename = "kebab-case")]
|
|
||||||
#[strum(serialize_all = "kebab-case")]
|
|
||||||
pub enum CompilerIdentifier {
|
pub enum CompilerIdentifier {
|
||||||
/// The solc compiler.
|
/// The solc compiler.
|
||||||
Solc,
|
Solc,
|
||||||
@@ -79,9 +80,8 @@ pub enum CompilerIdentifier {
|
|||||||
Display,
|
Display,
|
||||||
AsRefStr,
|
AsRefStr,
|
||||||
IntoStaticStr,
|
IntoStaticStr,
|
||||||
|
JsonSchema,
|
||||||
)]
|
)]
|
||||||
#[serde(rename = "kebab-case")]
|
|
||||||
#[strum(serialize_all = "kebab-case")]
|
|
||||||
pub enum NodeIdentifier {
|
pub enum NodeIdentifier {
|
||||||
/// The go-ethereum node implementation.
|
/// The go-ethereum node implementation.
|
||||||
Geth,
|
Geth,
|
||||||
@@ -108,12 +108,15 @@ pub enum NodeIdentifier {
|
|||||||
Display,
|
Display,
|
||||||
AsRefStr,
|
AsRefStr,
|
||||||
IntoStaticStr,
|
IntoStaticStr,
|
||||||
|
JsonSchema,
|
||||||
)]
|
)]
|
||||||
#[serde(rename = "kebab-case")]
|
#[serde(rename = "lowercase")]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "lowercase")]
|
||||||
pub enum VmIdentifier {
|
pub enum VmIdentifier {
|
||||||
/// The ethereum virtual machine.
|
/// The ethereum virtual machine.
|
||||||
Evm,
|
Evm,
|
||||||
|
/// The EraVM virtual machine.
|
||||||
|
EraVM,
|
||||||
/// Polkadot's PolaVM Risc-v based virtual machine.
|
/// Polkadot's PolaVM Risc-v based virtual machine.
|
||||||
Polkavm,
|
PolkaVM,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ repository.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
revive-dt-common = { workspace = true }
|
||||||
|
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use alloy::{
|
|||||||
signers::local::PrivateKeySigner,
|
signers::local::PrivateKeySigner,
|
||||||
};
|
};
|
||||||
use clap::{Parser, ValueEnum, ValueHint};
|
use clap::{Parser, ValueEnum, ValueHint};
|
||||||
|
use revive_dt_common::types::PlatformIdentifier;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use strum::{AsRefStr, Display, EnumString, IntoStaticStr};
|
use strum::{AsRefStr, Display, EnumString, IntoStaticStr};
|
||||||
@@ -165,13 +166,9 @@ pub struct ExecutionContext {
|
|||||||
)]
|
)]
|
||||||
pub working_directory: WorkingDirectoryConfiguration,
|
pub working_directory: WorkingDirectoryConfiguration,
|
||||||
|
|
||||||
/// The differential testing leader node implementation.
|
/// The set of platforms that the differential tests should run on.
|
||||||
#[arg(short, long = "leader", default_value_t = TestingPlatform::Geth)]
|
#[arg(short = 'p', long = "platform")]
|
||||||
pub leader: TestingPlatform,
|
pub platforms: Vec<PlatformIdentifier>,
|
||||||
|
|
||||||
/// The differential testing follower node implementation.
|
|
||||||
#[arg(short, long = "follower", default_value_t = TestingPlatform::Kitchensink)]
|
|
||||||
pub follower: TestingPlatform,
|
|
||||||
|
|
||||||
/// A list of test corpus JSON files to be tested.
|
/// A list of test corpus JSON files to be tested.
|
||||||
#[arg(long = "corpus", short)]
|
#[arg(long = "corpus", short)]
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use revive_dt_common::iterators::FilesWithExtensionIterator;
|
use revive_dt_common::{iterators::FilesWithExtensionIterator, types::CompilerIdentifier};
|
||||||
use revive_dt_compiler::{Compiler, CompilerOutput, Mode, SolidityCompiler};
|
use revive_dt_compiler::{Compiler, CompilerOutput, DynSolidityCompiler, Mode};
|
||||||
use revive_dt_config::TestingPlatform;
|
use revive_dt_core::DynPlatform;
|
||||||
use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata};
|
use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata};
|
||||||
|
|
||||||
use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address};
|
use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address};
|
||||||
@@ -22,8 +22,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use tokio::sync::{Mutex, RwLock};
|
use tokio::sync::{Mutex, RwLock};
|
||||||
use tracing::{Instrument, debug, debug_span, instrument};
|
use tracing::{Instrument, debug, debug_span, instrument};
|
||||||
|
|
||||||
use crate::Platform;
|
|
||||||
|
|
||||||
pub struct CachedCompiler<'a> {
|
pub struct CachedCompiler<'a> {
|
||||||
/// The cache that stores the compiled contracts.
|
/// The cache that stores the compiled contracts.
|
||||||
artifacts_cache: ArtifactsCache,
|
artifacts_cache: ArtifactsCache,
|
||||||
@@ -57,21 +55,22 @@ impl<'a> CachedCompiler<'a> {
|
|||||||
fields(
|
fields(
|
||||||
metadata_file_path = %metadata_file_path.display(),
|
metadata_file_path = %metadata_file_path.display(),
|
||||||
%mode,
|
%mode,
|
||||||
platform = P::config_id().to_string()
|
platform = %platform.platform_identifier()
|
||||||
),
|
),
|
||||||
err
|
err
|
||||||
)]
|
)]
|
||||||
pub async fn compile_contracts<P: Platform>(
|
pub async fn compile_contracts(
|
||||||
&self,
|
&self,
|
||||||
metadata: &'a Metadata,
|
metadata: &'a Metadata,
|
||||||
metadata_file_path: &'a Path,
|
metadata_file_path: &'a Path,
|
||||||
mode: Cow<'a, Mode>,
|
mode: Cow<'a, Mode>,
|
||||||
deployed_libraries: Option<&HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
deployed_libraries: Option<&HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||||
compiler: &P::Compiler,
|
compiler: &dyn DynSolidityCompiler,
|
||||||
|
platform: &dyn DynPlatform,
|
||||||
reporter: &ExecutionSpecificReporter,
|
reporter: &ExecutionSpecificReporter,
|
||||||
) -> Result<CompilerOutput> {
|
) -> Result<CompilerOutput> {
|
||||||
let cache_key = CacheKey {
|
let cache_key = CacheKey {
|
||||||
platform_key: P::config_id(),
|
compiler_identifier: platform.compiler_identifier(),
|
||||||
compiler_version: compiler.version().clone(),
|
compiler_version: compiler.version().clone(),
|
||||||
metadata_file_path,
|
metadata_file_path,
|
||||||
solc_mode: mode.clone(),
|
solc_mode: mode.clone(),
|
||||||
@@ -79,7 +78,7 @@ impl<'a> CachedCompiler<'a> {
|
|||||||
|
|
||||||
let compilation_callback = || {
|
let compilation_callback = || {
|
||||||
async move {
|
async move {
|
||||||
compile_contracts::<P>(
|
compile_contracts(
|
||||||
metadata
|
metadata
|
||||||
.directory()
|
.directory()
|
||||||
.context("Failed to get metadata directory while preparing compilation")?,
|
.context("Failed to get metadata directory while preparing compilation")?,
|
||||||
@@ -96,7 +95,7 @@ impl<'a> CachedCompiler<'a> {
|
|||||||
}
|
}
|
||||||
.instrument(debug_span!(
|
.instrument(debug_span!(
|
||||||
"Running compilation for the cache key",
|
"Running compilation for the cache key",
|
||||||
cache_key.platform_key = %cache_key.platform_key,
|
cache_key.compiler_identifier = %cache_key.compiler_identifier,
|
||||||
cache_key.compiler_version = %cache_key.compiler_version,
|
cache_key.compiler_version = %cache_key.compiler_version,
|
||||||
cache_key.metadata_file_path = %cache_key.metadata_file_path.display(),
|
cache_key.metadata_file_path = %cache_key.metadata_file_path.display(),
|
||||||
cache_key.solc_mode = %cache_key.solc_mode,
|
cache_key.solc_mode = %cache_key.solc_mode,
|
||||||
@@ -179,12 +178,12 @@ impl<'a> CachedCompiler<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn compile_contracts<P: Platform>(
|
async fn compile_contracts(
|
||||||
metadata_directory: impl AsRef<Path>,
|
metadata_directory: impl AsRef<Path>,
|
||||||
mut files_to_compile: impl Iterator<Item = PathBuf>,
|
mut files_to_compile: impl Iterator<Item = PathBuf>,
|
||||||
mode: &Mode,
|
mode: &Mode,
|
||||||
deployed_libraries: Option<&HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
deployed_libraries: Option<&HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||||
compiler: &P::Compiler,
|
compiler: &dyn DynSolidityCompiler,
|
||||||
reporter: &ExecutionSpecificReporter,
|
reporter: &ExecutionSpecificReporter,
|
||||||
) -> Result<CompilerOutput> {
|
) -> Result<CompilerOutput> {
|
||||||
let all_sources_in_dir = FilesWithExtensionIterator::new(metadata_directory.as_ref())
|
let all_sources_in_dir = FilesWithExtensionIterator::new(metadata_directory.as_ref())
|
||||||
@@ -218,7 +217,7 @@ async fn compile_contracts<P: Platform>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
let input = compilation.input().clone();
|
let input = compilation.input().clone();
|
||||||
let output = compilation.try_build(compiler).await;
|
let output = compilation.dyn_try_build(compiler).await;
|
||||||
|
|
||||||
match (output.as_ref(), deployed_libraries.is_some()) {
|
match (output.as_ref(), deployed_libraries.is_some()) {
|
||||||
(Ok(output), true) => {
|
(Ok(output), true) => {
|
||||||
@@ -332,9 +331,8 @@ impl ArtifactsCache {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
|
||||||
struct CacheKey<'a> {
|
struct CacheKey<'a> {
|
||||||
/// The platform name that this artifact was compiled for. For example, this could be EVM or
|
/// The identifier of the used compiler.
|
||||||
/// PVM.
|
compiler_identifier: CompilerIdentifier,
|
||||||
platform_key: &'a TestingPlatform,
|
|
||||||
|
|
||||||
/// The version of the compiler that was used to compile the artifacts.
|
/// The version of the compiler that was used to compile the artifacts.
|
||||||
compiler_version: Version,
|
compiler_version: Version,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
//! The test driver handles the compilation and execution of the test cases.
|
//! The test driver handles the compilation and execution of the test cases.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use alloy::consensus::EMPTY_ROOT_HASH;
|
use alloy::consensus::EMPTY_ROOT_HASH;
|
||||||
@@ -19,8 +18,9 @@ use alloy::{
|
|||||||
rpc::types::{TransactionRequest, trace::geth::DiffMode},
|
rpc::types::{TransactionRequest, trace::geth::DiffMode},
|
||||||
};
|
};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use futures::TryStreamExt;
|
use futures::{TryStreamExt, future::try_join_all};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use revive_dt_common::types::PlatformIdentifier;
|
||||||
use revive_dt_format::traits::{ResolutionContext, ResolverApi};
|
use revive_dt_format::traits::{ResolutionContext, ResolverApi};
|
||||||
use revive_dt_report::ExecutionSpecificReporter;
|
use revive_dt_report::ExecutionSpecificReporter;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
@@ -36,9 +36,7 @@ use revive_dt_node_interaction::EthereumNode;
|
|||||||
use tokio::try_join;
|
use tokio::try_join;
|
||||||
use tracing::{Instrument, info, info_span, instrument};
|
use tracing::{Instrument, info, info_span, instrument};
|
||||||
|
|
||||||
use crate::Platform;
|
pub struct CaseState {
|
||||||
|
|
||||||
pub struct CaseState<T: Platform> {
|
|
||||||
/// A map of all of the compiled contracts for the given metadata file.
|
/// A map of all of the compiled contracts for the given metadata file.
|
||||||
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
||||||
|
|
||||||
@@ -54,14 +52,9 @@ pub struct CaseState<T: Platform> {
|
|||||||
|
|
||||||
/// The execution reporter.
|
/// The execution reporter.
|
||||||
execution_reporter: ExecutionSpecificReporter,
|
execution_reporter: ExecutionSpecificReporter,
|
||||||
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> CaseState<T>
|
impl CaseState {
|
||||||
where
|
|
||||||
T: Platform,
|
|
||||||
{
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
compiler_version: Version,
|
compiler_version: Version,
|
||||||
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
||||||
@@ -74,7 +67,6 @@ where
|
|||||||
variables: Default::default(),
|
variables: Default::default(),
|
||||||
compiler_version,
|
compiler_version,
|
||||||
execution_reporter,
|
execution_reporter,
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +74,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
step: &Step,
|
step: &Step,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<StepOutput> {
|
) -> anyhow::Result<StepOutput> {
|
||||||
match step {
|
match step {
|
||||||
Step::FunctionCall(input) => {
|
Step::FunctionCall(input) => {
|
||||||
@@ -113,8 +105,10 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
||||||
|
let resolver = node.resolver().await?;
|
||||||
|
|
||||||
let deployment_receipts = self
|
let deployment_receipts = self
|
||||||
.handle_input_contract_deployment(metadata, input, node)
|
.handle_input_contract_deployment(metadata, input, node)
|
||||||
.await
|
.await
|
||||||
@@ -130,7 +124,12 @@ where
|
|||||||
self.handle_input_variable_assignment(input, &tracing_result)
|
self.handle_input_variable_assignment(input, &tracing_result)
|
||||||
.context("Failed to assign variables from callframe output")?;
|
.context("Failed to assign variables from callframe output")?;
|
||||||
let (_, (geth_trace, diff_mode)) = try_join!(
|
let (_, (geth_trace, diff_mode)) = try_join!(
|
||||||
self.handle_input_expectations(input, &execution_receipt, node, &tracing_result),
|
self.handle_input_expectations(
|
||||||
|
input,
|
||||||
|
&execution_receipt,
|
||||||
|
resolver.as_ref(),
|
||||||
|
&tracing_result
|
||||||
|
),
|
||||||
self.handle_input_diff(execution_receipt.transaction_hash, node)
|
self.handle_input_diff(execution_receipt.transaction_hash, node)
|
||||||
)
|
)
|
||||||
.context("Failed while evaluating expectations and diffs in parallel")?;
|
.context("Failed while evaluating expectations and diffs in parallel")?;
|
||||||
@@ -142,7 +141,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
balance_assertion: &BalanceAssertion,
|
balance_assertion: &BalanceAssertion,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.handle_balance_assertion_contract_deployment(metadata, balance_assertion, node)
|
self.handle_balance_assertion_contract_deployment(metadata, balance_assertion, node)
|
||||||
.await
|
.await
|
||||||
@@ -158,7 +157,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
storage_empty: &StorageEmptyAssertion,
|
storage_empty: &StorageEmptyAssertion,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.handle_storage_empty_assertion_contract_deployment(metadata, storage_empty, node)
|
self.handle_storage_empty_assertion_contract_deployment(metadata, storage_empty, node)
|
||||||
.await
|
.await
|
||||||
@@ -175,7 +174,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<HashMap<ContractInstance, TransactionReceipt>> {
|
) -> anyhow::Result<HashMap<ContractInstance, TransactionReceipt>> {
|
||||||
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
|
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
|
||||||
for instance in input.find_all_contract_instances().into_iter() {
|
for instance in input.find_all_contract_instances().into_iter() {
|
||||||
@@ -220,7 +219,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
mut deployment_receipts: HashMap<ContractInstance, TransactionReceipt>,
|
mut deployment_receipts: HashMap<ContractInstance, TransactionReceipt>,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<TransactionReceipt> {
|
) -> anyhow::Result<TransactionReceipt> {
|
||||||
match input.method {
|
match input.method {
|
||||||
// This input was already executed when `handle_input` was called. We just need to
|
// This input was already executed when `handle_input` was called. We just need to
|
||||||
@@ -229,8 +228,9 @@ where
|
|||||||
.remove(&input.instance)
|
.remove(&input.instance)
|
||||||
.context("Failed to find deployment receipt for constructor call"),
|
.context("Failed to find deployment receipt for constructor call"),
|
||||||
Method::Fallback | Method::FunctionName(_) => {
|
Method::Fallback | Method::FunctionName(_) => {
|
||||||
|
let resolver = node.resolver().await?;
|
||||||
let tx = match input
|
let tx = match input
|
||||||
.legacy_transaction(node, self.default_resolution_context())
|
.legacy_transaction(resolver.as_ref(), self.default_resolution_context())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(tx) => tx,
|
Ok(tx) => tx,
|
||||||
@@ -251,7 +251,7 @@ where
|
|||||||
async fn handle_input_call_frame_tracing(
|
async fn handle_input_call_frame_tracing(
|
||||||
&self,
|
&self,
|
||||||
tx_hash: TxHash,
|
tx_hash: TxHash,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<CallFrame> {
|
) -> anyhow::Result<CallFrame> {
|
||||||
node.trace_transaction(
|
node.trace_transaction(
|
||||||
tx_hash,
|
tx_hash,
|
||||||
@@ -314,7 +314,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
execution_receipt: &TransactionReceipt,
|
execution_receipt: &TransactionReceipt,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
tracing_result: &CallFrame,
|
tracing_result: &CallFrame,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
// Resolving the `input.expected` into a series of expectations that we can then assert on.
|
// Resolving the `input.expected` into a series of expectations that we can then assert on.
|
||||||
@@ -362,7 +362,7 @@ where
|
|||||||
async fn handle_input_expectation_item(
|
async fn handle_input_expectation_item(
|
||||||
&self,
|
&self,
|
||||||
execution_receipt: &TransactionReceipt,
|
execution_receipt: &TransactionReceipt,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
expectation: ExpectedOutput,
|
expectation: ExpectedOutput,
|
||||||
tracing_result: &CallFrame,
|
tracing_result: &CallFrame,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
@@ -508,7 +508,7 @@ where
|
|||||||
async fn handle_input_diff(
|
async fn handle_input_diff(
|
||||||
&self,
|
&self,
|
||||||
tx_hash: TxHash,
|
tx_hash: TxHash,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<(GethTrace, DiffMode)> {
|
) -> anyhow::Result<(GethTrace, DiffMode)> {
|
||||||
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
|
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
|
||||||
diff_mode: Some(true),
|
diff_mode: Some(true),
|
||||||
@@ -533,7 +533,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
balance_assertion: &BalanceAssertion,
|
balance_assertion: &BalanceAssertion,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let Some(instance) = balance_assertion
|
let Some(instance) = balance_assertion
|
||||||
.address
|
.address
|
||||||
@@ -562,11 +562,12 @@ where
|
|||||||
expected_balance: amount,
|
expected_balance: amount,
|
||||||
..
|
..
|
||||||
}: &BalanceAssertion,
|
}: &BalanceAssertion,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
let resolver = node.resolver().await?;
|
||||||
let address = Address::from_slice(
|
let address = Address::from_slice(
|
||||||
Calldata::new_compound([address_string])
|
Calldata::new_compound([address_string])
|
||||||
.calldata(node, self.default_resolution_context())
|
.calldata(resolver.as_ref(), self.default_resolution_context())
|
||||||
.await?
|
.await?
|
||||||
.get(12..32)
|
.get(12..32)
|
||||||
.expect("Can't fail"),
|
.expect("Can't fail"),
|
||||||
@@ -595,7 +596,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
storage_empty_assertion: &StorageEmptyAssertion,
|
storage_empty_assertion: &StorageEmptyAssertion,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let Some(instance) = storage_empty_assertion
|
let Some(instance) = storage_empty_assertion
|
||||||
.address
|
.address
|
||||||
@@ -624,11 +625,12 @@ where
|
|||||||
is_storage_empty,
|
is_storage_empty,
|
||||||
..
|
..
|
||||||
}: &StorageEmptyAssertion,
|
}: &StorageEmptyAssertion,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
let resolver = node.resolver().await?;
|
||||||
let address = Address::from_slice(
|
let address = Address::from_slice(
|
||||||
Calldata::new_compound([address_string])
|
Calldata::new_compound([address_string])
|
||||||
.calldata(node, self.default_resolution_context())
|
.calldata(resolver.as_ref(), self.default_resolution_context())
|
||||||
.await?
|
.await?
|
||||||
.get(12..32)
|
.get(12..32)
|
||||||
.expect("Can't fail"),
|
.expect("Can't fail"),
|
||||||
@@ -667,7 +669,7 @@ where
|
|||||||
deployer: Address,
|
deployer: Address,
|
||||||
calldata: Option<&Calldata>,
|
calldata: Option<&Calldata>,
|
||||||
value: Option<EtherValue>,
|
value: Option<EtherValue>,
|
||||||
node: &T::Blockchain,
|
node: &dyn EthereumNode,
|
||||||
) -> anyhow::Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
|
) -> anyhow::Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
|
||||||
if let Some((_, address, abi)) = self.deployed_contracts.get(contract_instance) {
|
if let Some((_, address, abi)) = self.deployed_contracts.get(contract_instance) {
|
||||||
return Ok((*address, abi.clone(), None));
|
return Ok((*address, abi.clone(), None));
|
||||||
@@ -710,8 +712,9 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(calldata) = calldata {
|
if let Some(calldata) = calldata {
|
||||||
|
let resolver = node.resolver().await?;
|
||||||
let calldata = calldata
|
let calldata = calldata
|
||||||
.calldata(node, self.default_resolution_context())
|
.calldata(resolver.as_ref(), self.default_resolution_context())
|
||||||
.await?;
|
.await?;
|
||||||
code.extend(calldata);
|
code.extend(calldata);
|
||||||
}
|
}
|
||||||
@@ -728,11 +731,7 @@ where
|
|||||||
let receipt = match node.execute_transaction(tx).await {
|
let receipt = match node.execute_transaction(tx).await {
|
||||||
Ok(receipt) => receipt,
|
Ok(receipt) => receipt,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
tracing::error!(
|
tracing::error!(?error, "Contract deployment transaction failed.");
|
||||||
node = std::any::type_name::<T>(),
|
|
||||||
?error,
|
|
||||||
"Contract deployment transaction failed."
|
|
||||||
);
|
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -763,36 +762,23 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CaseDriver<'a, Leader: Platform, Follower: Platform> {
|
pub struct CaseDriver<'a> {
|
||||||
metadata: &'a Metadata,
|
metadata: &'a Metadata,
|
||||||
case: &'a Case,
|
case: &'a Case,
|
||||||
leader_node: &'a Leader::Blockchain,
|
platform_state: Vec<(&'a dyn EthereumNode, PlatformIdentifier, CaseState)>,
|
||||||
follower_node: &'a Follower::Blockchain,
|
|
||||||
leader_state: CaseState<Leader>,
|
|
||||||
follower_state: CaseState<Follower>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L, F> CaseDriver<'a, L, F>
|
impl<'a> CaseDriver<'a> {
|
||||||
where
|
|
||||||
L: Platform,
|
|
||||||
F: Platform,
|
|
||||||
{
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
metadata: &'a Metadata,
|
metadata: &'a Metadata,
|
||||||
case: &'a Case,
|
case: &'a Case,
|
||||||
leader_node: &'a L::Blockchain,
|
platform_state: Vec<(&'a dyn EthereumNode, PlatformIdentifier, CaseState)>,
|
||||||
follower_node: &'a F::Blockchain,
|
) -> CaseDriver<'a> {
|
||||||
leader_state: CaseState<L>,
|
|
||||||
follower_state: CaseState<F>,
|
|
||||||
) -> CaseDriver<'a, L, F> {
|
|
||||||
Self {
|
Self {
|
||||||
metadata,
|
metadata,
|
||||||
case,
|
case,
|
||||||
leader_node,
|
platform_state,
|
||||||
follower_node,
|
|
||||||
leader_state,
|
|
||||||
follower_state,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,42 +791,44 @@ where
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, v)| (StepIdx::new(idx), v))
|
.map(|(idx, v)| (StepIdx::new(idx), v))
|
||||||
{
|
{
|
||||||
let (leader_step_output, follower_step_output) = try_join!(
|
// Run this step concurrently across all platforms; short-circuit on first failure
|
||||||
self.leader_state
|
let metadata = self.metadata;
|
||||||
.handle_step(self.metadata, &step, self.leader_node)
|
let step_futs =
|
||||||
.instrument(info_span!(
|
self.platform_state
|
||||||
"Handling Step",
|
.iter_mut()
|
||||||
%step_idx,
|
.map(|(node, platform_id, case_state)| {
|
||||||
target = "Leader",
|
let platform_id = *platform_id;
|
||||||
)),
|
let node_ref = *node;
|
||||||
self.follower_state
|
let step_clone = step.clone();
|
||||||
.handle_step(self.metadata, &step, self.follower_node)
|
let span = info_span!(
|
||||||
.instrument(info_span!(
|
"Handling Step",
|
||||||
"Handling Step",
|
%step_idx,
|
||||||
%step_idx,
|
platform = %platform_id,
|
||||||
target = "Follower",
|
);
|
||||||
))
|
async move {
|
||||||
)?;
|
case_state
|
||||||
|
.handle_step(metadata, &step_clone, node_ref)
|
||||||
|
.await
|
||||||
|
.map_err(|e| (platform_id, e))
|
||||||
|
}
|
||||||
|
.instrument(span)
|
||||||
|
});
|
||||||
|
|
||||||
match (leader_step_output, follower_step_output) {
|
match try_join_all(step_futs).await {
|
||||||
(StepOutput::FunctionCall(..), StepOutput::FunctionCall(..)) => {
|
Ok(_outputs) => {
|
||||||
// TODO: We need to actually work out how/if we will compare the diff between
|
// All platforms succeeded for this step
|
||||||
// the leader and the follower. The diffs are almost guaranteed to be different
|
steps_executed += 1;
|
||||||
// from leader and follower and therefore without an actual strategy for this
|
}
|
||||||
// we have something that's guaranteed to fail. Even a simple call to some
|
Err((platform_id, error)) => {
|
||||||
// contract will produce two non-equal diffs because on the leader the contract
|
tracing::error!(
|
||||||
// has address X and on the follower it has address Y. On the leader contract X
|
%step_idx,
|
||||||
// contains address A in the state and on the follower it contains address B. So
|
platform = %platform_id,
|
||||||
// this isn't exactly a straightforward thing to do and I'm not even sure that
|
?error,
|
||||||
// it's possible to do. Once we have an actual strategy for doing the diffs we
|
"Step failed on platform",
|
||||||
// will implement it here. Until then, this remains empty.
|
);
|
||||||
|
return Err(error);
|
||||||
}
|
}
|
||||||
(StepOutput::BalanceAssertion, StepOutput::BalanceAssertion) => {}
|
|
||||||
(StepOutput::StorageEmptyAssertion, StepOutput::StorageEmptyAssertion) => {}
|
|
||||||
_ => unreachable!("The two step outputs can not be of a different kind"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
steps_executed += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(steps_executed)
|
Ok(steps_executed)
|
||||||
|
|||||||
+27
-2
@@ -103,6 +103,7 @@ pub trait DynPlatform {
|
|||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn DynSolidityCompiler>>>>>;
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn DynSolidityCompiler>>>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||||
pub struct GethEvmSolcPlatform;
|
pub struct GethEvmSolcPlatform;
|
||||||
|
|
||||||
impl DynPlatform for GethEvmSolcPlatform {
|
impl DynPlatform for GethEvmSolcPlatform {
|
||||||
@@ -147,6 +148,7 @@ impl DynPlatform for GethEvmSolcPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||||
pub struct KitchensinkPolkavmResolcPlatform;
|
pub struct KitchensinkPolkavmResolcPlatform;
|
||||||
|
|
||||||
impl DynPlatform for KitchensinkPolkavmResolcPlatform {
|
impl DynPlatform for KitchensinkPolkavmResolcPlatform {
|
||||||
@@ -159,7 +161,7 @@ impl DynPlatform for KitchensinkPolkavmResolcPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vm_identifier(&self) -> VmIdentifier {
|
fn vm_identifier(&self) -> VmIdentifier {
|
||||||
VmIdentifier::Polkavm
|
VmIdentifier::PolkaVM
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compiler_identifier(&self) -> CompilerIdentifier {
|
fn compiler_identifier(&self) -> CompilerIdentifier {
|
||||||
@@ -198,6 +200,7 @@ impl DynPlatform for KitchensinkPolkavmResolcPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||||
pub struct KitchensinkRevmSolcPlatform;
|
pub struct KitchensinkRevmSolcPlatform;
|
||||||
|
|
||||||
impl DynPlatform for KitchensinkRevmSolcPlatform {
|
impl DynPlatform for KitchensinkRevmSolcPlatform {
|
||||||
@@ -249,6 +252,7 @@ impl DynPlatform for KitchensinkRevmSolcPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||||
pub struct ReviveDevNodePolkavmResolcPlatform;
|
pub struct ReviveDevNodePolkavmResolcPlatform;
|
||||||
|
|
||||||
impl DynPlatform for ReviveDevNodePolkavmResolcPlatform {
|
impl DynPlatform for ReviveDevNodePolkavmResolcPlatform {
|
||||||
@@ -261,7 +265,7 @@ impl DynPlatform for ReviveDevNodePolkavmResolcPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vm_identifier(&self) -> VmIdentifier {
|
fn vm_identifier(&self) -> VmIdentifier {
|
||||||
VmIdentifier::Polkavm
|
VmIdentifier::PolkaVM
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compiler_identifier(&self) -> CompilerIdentifier {
|
fn compiler_identifier(&self) -> CompilerIdentifier {
|
||||||
@@ -300,6 +304,7 @@ impl DynPlatform for ReviveDevNodePolkavmResolcPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
|
||||||
pub struct ReviveDevNodeRevmSolcPlatform;
|
pub struct ReviveDevNodeRevmSolcPlatform;
|
||||||
|
|
||||||
impl DynPlatform for ReviveDevNodeRevmSolcPlatform {
|
impl DynPlatform for ReviveDevNodeRevmSolcPlatform {
|
||||||
@@ -371,6 +376,26 @@ impl From<PlatformIdentifier> for Box<dyn DynPlatform> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PlatformIdentifier> for &dyn DynPlatform {
|
||||||
|
fn from(value: PlatformIdentifier) -> Self {
|
||||||
|
match value {
|
||||||
|
PlatformIdentifier::GethEvmSolc => &GethEvmSolcPlatform as &dyn DynPlatform,
|
||||||
|
PlatformIdentifier::KitchensinkPolkavmResolc => {
|
||||||
|
&KitchensinkPolkavmResolcPlatform as &dyn DynPlatform
|
||||||
|
}
|
||||||
|
PlatformIdentifier::KitchensinkRevmSolc => {
|
||||||
|
&KitchensinkRevmSolcPlatform as &dyn DynPlatform
|
||||||
|
}
|
||||||
|
PlatformIdentifier::ReviveDevNodePolkavmResolc => {
|
||||||
|
&ReviveDevNodePolkavmResolcPlatform as &dyn DynPlatform
|
||||||
|
}
|
||||||
|
PlatformIdentifier::ReviveDevNodeRevmSolc => {
|
||||||
|
&ReviveDevNodeRevmSolcPlatform as &dyn DynPlatform
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn spawn_node<T: Node + EthereumNode + Send + Sync>(
|
fn spawn_node<T: Node + EthereumNode + Send + Sync>(
|
||||||
mut node: T,
|
mut node: T,
|
||||||
genesis: Genesis,
|
genesis: Genesis,
|
||||||
|
|||||||
+285
-342
@@ -1,8 +1,9 @@
|
|||||||
mod cached_compiler;
|
mod cached_compiler;
|
||||||
|
mod pool;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeSet, HashMap},
|
||||||
io::{BufWriter, Write, stderr},
|
io::{BufWriter, Write, stderr},
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@@ -20,20 +21,19 @@ use futures::{Stream, StreamExt};
|
|||||||
use indexmap::{IndexMap, indexmap};
|
use indexmap::{IndexMap, indexmap};
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
use revive_dt_report::{
|
use revive_dt_report::{
|
||||||
NodeDesignation, ReportAggregator, Reporter, ReporterEvent, TestCaseStatus,
|
ExecutionSpecificReporter, ReportAggregator, Reporter, ReporterEvent, TestCaseStatus,
|
||||||
TestSpecificReporter, TestSpecifier,
|
TestSpecificReporter, TestSpecifier,
|
||||||
};
|
};
|
||||||
use schemars::schema_for;
|
use schemars::schema_for;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use tokio::try_join;
|
|
||||||
use tracing::{debug, error, info, info_span, instrument};
|
use tracing::{debug, error, info, info_span, instrument};
|
||||||
use tracing_subscriber::{EnvFilter, FmtSubscriber};
|
use tracing_subscriber::{EnvFilter, FmtSubscriber};
|
||||||
|
|
||||||
use revive_dt_common::{iterators::EitherIter, types::Mode};
|
use revive_dt_common::{iterators::EitherIter, types::Mode};
|
||||||
use revive_dt_compiler::{CompilerOutput, SolidityCompiler};
|
use revive_dt_compiler::DynSolidityCompiler;
|
||||||
use revive_dt_config::{Context, *};
|
use revive_dt_config::{Context, *};
|
||||||
use revive_dt_core::{
|
use revive_dt_core::{
|
||||||
Geth, Kitchensink, Platform,
|
DynPlatform,
|
||||||
driver::{CaseDriver, CaseState},
|
driver::{CaseDriver, CaseState},
|
||||||
};
|
};
|
||||||
use revive_dt_format::{
|
use revive_dt_format::{
|
||||||
@@ -43,9 +43,9 @@ use revive_dt_format::{
|
|||||||
metadata::{ContractPathAndIdent, Metadata, MetadataFile},
|
metadata::{ContractPathAndIdent, Metadata, MetadataFile},
|
||||||
mode::ParsedMode,
|
mode::ParsedMode,
|
||||||
};
|
};
|
||||||
use revive_dt_node::{Node, pool::NodePool};
|
|
||||||
|
|
||||||
use crate::cached_compiler::CachedCompiler;
|
use crate::cached_compiler::CachedCompiler;
|
||||||
|
use crate::pool::NodePool;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let (writer, _guard) = tracing_appender::non_blocking::NonBlockingBuilder::default()
|
let (writer, _guard) = tracing_appender::non_blocking::NonBlockingBuilder::default()
|
||||||
@@ -133,32 +133,28 @@ fn collect_corpora(
|
|||||||
Ok(corpora)
|
Ok(corpora)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_driver<L, F>(
|
async fn run_driver(
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
metadata_files: &[MetadataFile],
|
metadata_files: &[MetadataFile],
|
||||||
reporter: Reporter,
|
reporter: Reporter,
|
||||||
report_aggregator_task: impl Future<Output = anyhow::Result<()>>,
|
report_aggregator_task: impl Future<Output = anyhow::Result<()>>,
|
||||||
) -> anyhow::Result<()>
|
platforms: Vec<&dyn DynPlatform>,
|
||||||
where
|
) -> anyhow::Result<()> {
|
||||||
L: Platform,
|
let mut nodes = Vec::<(&dyn DynPlatform, NodePool)>::new();
|
||||||
F: Platform,
|
for platform in platforms.into_iter() {
|
||||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
let pool = NodePool::new(Context::ExecuteTests(Box::new(context.clone())), platform)
|
||||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
.context("Failed to initialize follower node pool")?;
|
||||||
{
|
nodes.push((platform, pool));
|
||||||
let leader_nodes = NodePool::<L::Blockchain>::new(context.clone())
|
}
|
||||||
.context("Failed to initialize leader node pool")?;
|
|
||||||
let follower_nodes = NodePool::<F::Blockchain>::new(context.clone())
|
|
||||||
.context("Failed to initialize follower node pool")?;
|
|
||||||
|
|
||||||
let tests_stream = tests_stream(
|
let tests_stream = tests_stream(
|
||||||
&context,
|
&context,
|
||||||
metadata_files.iter(),
|
metadata_files.iter(),
|
||||||
&leader_nodes,
|
nodes.as_slice(),
|
||||||
&follower_nodes,
|
|
||||||
reporter.clone(),
|
reporter.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let driver_task = start_driver_task::<L, F>(&context, tests_stream)
|
let driver_task = start_driver_task(&context, tests_stream)
|
||||||
.await
|
.await
|
||||||
.context("Failed to start driver task")?;
|
.context("Failed to start driver task")?;
|
||||||
let cli_reporting_task = start_cli_reporting_task(reporter);
|
let cli_reporting_task = start_cli_reporting_task(reporter);
|
||||||
@@ -169,19 +165,12 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tests_stream<'a, L, F>(
|
async fn tests_stream<'a>(
|
||||||
args: &ExecutionContext,
|
args: &ExecutionContext,
|
||||||
metadata_files: impl IntoIterator<Item = &'a MetadataFile> + Clone,
|
metadata_files: impl IntoIterator<Item = &'a MetadataFile> + Clone,
|
||||||
leader_node_pool: &'a NodePool<L::Blockchain>,
|
nodes: &'a [(&dyn DynPlatform, NodePool)],
|
||||||
follower_node_pool: &'a NodePool<F::Blockchain>,
|
|
||||||
reporter: Reporter,
|
reporter: Reporter,
|
||||||
) -> impl Stream<Item = Test<'a, L, F>>
|
) -> impl Stream<Item = Test<'a>> {
|
||||||
where
|
|
||||||
L: Platform,
|
|
||||||
F: Platform,
|
|
||||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
|
||||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let tests = metadata_files
|
let tests = metadata_files
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|metadata_file| {
|
.flat_map(|metadata_file| {
|
||||||
@@ -231,35 +220,36 @@ where
|
|||||||
stream::iter(tests.into_iter())
|
stream::iter(tests.into_iter())
|
||||||
.filter_map(
|
.filter_map(
|
||||||
move |(metadata_file, case_idx, case, mode, reporter)| async move {
|
move |(metadata_file, case_idx, case, mode, reporter)| async move {
|
||||||
let leader_compiler = <L::Compiler as SolidityCompiler>::new(
|
let mut platforms = Vec::new();
|
||||||
args,
|
for (platform, node_pool) in nodes.iter() {
|
||||||
mode.version.clone().map(Into::into),
|
let node = node_pool.round_robbin();
|
||||||
)
|
let compiler = platform
|
||||||
.await
|
.new_compiler(
|
||||||
.inspect_err(|err| error!(?err, "Failed to instantiate the leader compiler"))
|
Context::ExecuteTests(Box::new(args.clone())),
|
||||||
.ok()?;
|
mode.version.clone().map(Into::into),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
?err,
|
||||||
|
platform_identifier = %platform.platform_identifier(),
|
||||||
|
"Failed to instantiate the compiler"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
let follower_compiler = <F::Compiler as SolidityCompiler>::new(
|
let reporter = reporter
|
||||||
args,
|
.execution_specific_reporter(node.id(), platform.platform_identifier());
|
||||||
mode.version.clone().map(Into::into),
|
platforms.push((*platform, node, compiler, reporter));
|
||||||
)
|
}
|
||||||
.await
|
|
||||||
.inspect_err(|err| error!(?err, "Failed to instantiate the follower compiler"))
|
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
let leader_node = leader_node_pool.round_robbin();
|
Some(Test {
|
||||||
let follower_node = follower_node_pool.round_robbin();
|
|
||||||
|
|
||||||
Some(Test::<L, F> {
|
|
||||||
metadata: metadata_file,
|
metadata: metadata_file,
|
||||||
metadata_file_path: metadata_file.metadata_file_path.as_path(),
|
metadata_file_path: metadata_file.metadata_file_path.as_path(),
|
||||||
mode: mode.clone(),
|
mode: mode.clone(),
|
||||||
case_idx: CaseIdx::new(case_idx),
|
case_idx: CaseIdx::new(case_idx),
|
||||||
case,
|
case,
|
||||||
leader_node,
|
platforms,
|
||||||
follower_node,
|
|
||||||
leader_compiler,
|
|
||||||
follower_compiler,
|
|
||||||
reporter,
|
reporter,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -293,18 +283,10 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_driver_task<'a, L, F>(
|
async fn start_driver_task<'a>(
|
||||||
context: &ExecutionContext,
|
context: &ExecutionContext,
|
||||||
tests: impl Stream<Item = Test<'a, L, F>>,
|
tests: impl Stream<Item = Test<'a>>,
|
||||||
) -> anyhow::Result<impl Future<Output = ()>>
|
) -> anyhow::Result<impl Future<Output = ()>> {
|
||||||
where
|
|
||||||
L: Platform,
|
|
||||||
F: Platform,
|
|
||||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
|
||||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
|
||||||
L::Compiler: 'a,
|
|
||||||
F::Compiler: 'a,
|
|
||||||
{
|
|
||||||
info!("Starting driver task");
|
info!("Starting driver task");
|
||||||
|
|
||||||
let cached_compiler = Arc::new(
|
let cached_compiler = Arc::new(
|
||||||
@@ -327,23 +309,18 @@ where
|
|||||||
let cached_compiler = cached_compiler.clone();
|
let cached_compiler = cached_compiler.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
test.reporter
|
for (platform, node, _, _) in test.platforms.iter() {
|
||||||
.report_leader_node_assigned_event(
|
test.reporter
|
||||||
test.leader_node.id(),
|
.report_node_assigned_event(
|
||||||
*L::config_id(),
|
node.id(),
|
||||||
test.leader_node.connection_string(),
|
platform.platform_identifier(),
|
||||||
)
|
node.connection_string(),
|
||||||
.expect("Can't fail");
|
)
|
||||||
test.reporter
|
.expect("Can't fail");
|
||||||
.report_follower_node_assigned_event(
|
}
|
||||||
test.follower_node.id(),
|
|
||||||
*F::config_id(),
|
|
||||||
test.follower_node.connection_string(),
|
|
||||||
)
|
|
||||||
.expect("Can't fail");
|
|
||||||
|
|
||||||
let reporter = test.reporter.clone();
|
let reporter = test.reporter.clone();
|
||||||
let result = handle_case_driver::<L, F>(test, cached_compiler).await;
|
let result = handle_case_driver(&test, cached_compiler).await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(steps_executed) => reporter
|
Ok(steps_executed) => reporter
|
||||||
@@ -449,230 +426,174 @@ async fn start_cli_reporting_task(reporter: Reporter) {
|
|||||||
mode = %test.mode,
|
mode = %test.mode,
|
||||||
case_idx = %test.case_idx,
|
case_idx = %test.case_idx,
|
||||||
case_name = test.case.name.as_deref().unwrap_or("Unnamed Case"),
|
case_name = test.case.name.as_deref().unwrap_or("Unnamed Case"),
|
||||||
leader_node = test.leader_node.id(),
|
|
||||||
follower_node = test.follower_node.id(),
|
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
async fn handle_case_driver<'a, L, F>(
|
async fn handle_case_driver<'a>(
|
||||||
test: Test<'a, L, F>,
|
test: &Test<'a>,
|
||||||
cached_compiler: Arc<CachedCompiler<'a>>,
|
cached_compiler: Arc<CachedCompiler<'a>>,
|
||||||
) -> anyhow::Result<usize>
|
) -> anyhow::Result<usize> {
|
||||||
where
|
let platform_state = stream::iter(test.platforms.iter())
|
||||||
L: Platform,
|
// Compiling the pre-link contracts.
|
||||||
F: Platform,
|
.filter_map(|(platform, node, compiler, reporter)| {
|
||||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
let cached_compiler = cached_compiler.clone();
|
||||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
|
||||||
L::Compiler: 'a,
|
|
||||||
F::Compiler: 'a,
|
|
||||||
{
|
|
||||||
let leader_reporter = test
|
|
||||||
.reporter
|
|
||||||
.execution_specific_reporter(test.leader_node.id(), NodeDesignation::Leader);
|
|
||||||
let follower_reporter = test
|
|
||||||
.reporter
|
|
||||||
.execution_specific_reporter(test.follower_node.id(), NodeDesignation::Follower);
|
|
||||||
|
|
||||||
let (
|
async move {
|
||||||
CompilerOutput {
|
let compiler_output = cached_compiler
|
||||||
contracts: leader_pre_link_contracts,
|
.compile_contracts(
|
||||||
},
|
test.metadata,
|
||||||
CompilerOutput {
|
test.metadata_file_path,
|
||||||
contracts: follower_pre_link_contracts,
|
test.mode.clone(),
|
||||||
},
|
None,
|
||||||
) = try_join!(
|
compiler.as_ref(),
|
||||||
cached_compiler.compile_contracts::<L>(
|
*platform,
|
||||||
test.metadata,
|
reporter,
|
||||||
test.metadata_file_path,
|
)
|
||||||
test.mode.clone(),
|
.await
|
||||||
None,
|
.inspect_err(|err| {
|
||||||
&test.leader_compiler,
|
error!(
|
||||||
&leader_reporter,
|
%err,
|
||||||
),
|
platform_identifier = %platform.platform_identifier(),
|
||||||
cached_compiler.compile_contracts::<F>(
|
"Pre-linking compilation failed"
|
||||||
test.metadata,
|
)
|
||||||
test.metadata_file_path,
|
})
|
||||||
test.mode.clone(),
|
.ok()?;
|
||||||
None,
|
Some((test, platform, node, compiler, reporter, compiler_output))
|
||||||
&test.follower_compiler,
|
|
||||||
&follower_reporter
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.context("Failed to compile pre-link contracts for leader/follower in parallel")?;
|
|
||||||
|
|
||||||
let mut leader_deployed_libraries = None::<HashMap<_, _>>;
|
|
||||||
let mut follower_deployed_libraries = None::<HashMap<_, _>>;
|
|
||||||
let mut contract_sources = test
|
|
||||||
.metadata
|
|
||||||
.contract_sources()
|
|
||||||
.context("Failed to retrieve contract sources from metadata")?;
|
|
||||||
for library_instance in test
|
|
||||||
.metadata
|
|
||||||
.libraries
|
|
||||||
.iter()
|
|
||||||
.flatten()
|
|
||||||
.flat_map(|(_, map)| map.values())
|
|
||||||
{
|
|
||||||
debug!(%library_instance, "Deploying Library Instance");
|
|
||||||
|
|
||||||
let ContractPathAndIdent {
|
|
||||||
contract_source_path: library_source_path,
|
|
||||||
contract_ident: library_ident,
|
|
||||||
} = contract_sources
|
|
||||||
.remove(library_instance)
|
|
||||||
.context("Failed to find the contract source")?;
|
|
||||||
|
|
||||||
let (leader_code, leader_abi) = leader_pre_link_contracts
|
|
||||||
.get(&library_source_path)
|
|
||||||
.and_then(|contracts| contracts.get(library_ident.as_str()))
|
|
||||||
.context("Declared library was not compiled")?;
|
|
||||||
let (follower_code, follower_abi) = follower_pre_link_contracts
|
|
||||||
.get(&library_source_path)
|
|
||||||
.and_then(|contracts| contracts.get(library_ident.as_str()))
|
|
||||||
.context("Declared library was not compiled")?;
|
|
||||||
|
|
||||||
let leader_code = match alloy::hex::decode(leader_code) {
|
|
||||||
Ok(code) => code,
|
|
||||||
Err(error) => {
|
|
||||||
anyhow::bail!("Failed to hex-decode the byte code {}", error)
|
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
let follower_code = match alloy::hex::decode(follower_code) {
|
// Deploying the libraries for the platform.
|
||||||
Ok(code) => code,
|
.filter_map(
|
||||||
Err(error) => {
|
|(test, platform, node, compiler, reporter, compiler_output)| async move {
|
||||||
anyhow::bail!("Failed to hex-decode the byte code {}", error)
|
let mut deployed_libraries = None::<HashMap<_, _>>;
|
||||||
}
|
let mut contract_sources = test
|
||||||
};
|
.metadata
|
||||||
|
.contract_sources()
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
%err,
|
||||||
|
platform_identifier = %platform.platform_identifier(),
|
||||||
|
"Failed to retrieve contract sources from metadata"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
for library_instance in test
|
||||||
|
.metadata
|
||||||
|
.libraries
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.flat_map(|(_, map)| map.values())
|
||||||
|
{
|
||||||
|
debug!(%library_instance, "Deploying Library Instance");
|
||||||
|
|
||||||
// Getting the deployer address from the cases themselves. This is to ensure that we're
|
let ContractPathAndIdent {
|
||||||
// doing the deployments from different accounts and therefore we're not slowed down by
|
contract_source_path: library_source_path,
|
||||||
// the nonce.
|
contract_ident: library_ident,
|
||||||
let deployer_address = test
|
} = contract_sources.remove(library_instance)?;
|
||||||
.case
|
|
||||||
.steps
|
|
||||||
.iter()
|
|
||||||
.filter_map(|step| match step {
|
|
||||||
Step::FunctionCall(input) => Some(input.caller),
|
|
||||||
Step::BalanceAssertion(..) => None,
|
|
||||||
Step::StorageEmptyAssertion(..) => None,
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
.unwrap_or(Input::default_caller());
|
|
||||||
let leader_tx = TransactionBuilder::<Ethereum>::with_deploy_code(
|
|
||||||
TransactionRequest::default().from(deployer_address),
|
|
||||||
leader_code,
|
|
||||||
);
|
|
||||||
let follower_tx = TransactionBuilder::<Ethereum>::with_deploy_code(
|
|
||||||
TransactionRequest::default().from(deployer_address),
|
|
||||||
follower_code,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (leader_receipt, follower_receipt) = try_join!(
|
let (code, leader_abi) = compiler_output
|
||||||
test.leader_node.execute_transaction(leader_tx),
|
.contracts
|
||||||
test.follower_node.execute_transaction(follower_tx)
|
.get(&library_source_path)
|
||||||
)?;
|
.and_then(|contracts| contracts.get(library_ident.as_str()))?;
|
||||||
|
|
||||||
debug!(
|
let code = alloy::hex::decode(code).ok()?;
|
||||||
?library_instance,
|
|
||||||
library_address = ?leader_receipt.contract_address,
|
|
||||||
"Deployed library to leader"
|
|
||||||
);
|
|
||||||
debug!(
|
|
||||||
?library_instance,
|
|
||||||
library_address = ?follower_receipt.contract_address,
|
|
||||||
"Deployed library to follower"
|
|
||||||
);
|
|
||||||
|
|
||||||
let leader_library_address = leader_receipt
|
// Getting the deployer address from the cases themselves. This is to ensure
|
||||||
.contract_address
|
// that we're doing the deployments from different accounts and therefore we're
|
||||||
.context("Contract deployment didn't return an address")?;
|
// not slowed down by the nonce.
|
||||||
let follower_library_address = follower_receipt
|
let deployer_address = test
|
||||||
.contract_address
|
.case
|
||||||
.context("Contract deployment didn't return an address")?;
|
.steps
|
||||||
|
.iter()
|
||||||
|
.filter_map(|step| match step {
|
||||||
|
Step::FunctionCall(input) => Some(input.caller),
|
||||||
|
Step::BalanceAssertion(..) => None,
|
||||||
|
Step::StorageEmptyAssertion(..) => None,
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.unwrap_or(Input::default_caller());
|
||||||
|
let tx = TransactionBuilder::<Ethereum>::with_deploy_code(
|
||||||
|
TransactionRequest::default().from(deployer_address),
|
||||||
|
code,
|
||||||
|
);
|
||||||
|
let receipt = node
|
||||||
|
.execute_transaction(tx)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
%err,
|
||||||
|
%library_instance,
|
||||||
|
platform_identifier = %platform.platform_identifier(),
|
||||||
|
"Failed to deploy the library"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
leader_deployed_libraries.get_or_insert_default().insert(
|
debug!(
|
||||||
library_instance.clone(),
|
?library_instance,
|
||||||
(
|
platform_identifier = %platform.platform_identifier(),
|
||||||
library_ident.clone(),
|
"Deployed library"
|
||||||
leader_library_address,
|
);
|
||||||
leader_abi.clone(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
follower_deployed_libraries.get_or_insert_default().insert(
|
|
||||||
library_instance.clone(),
|
|
||||||
(
|
|
||||||
library_ident,
|
|
||||||
follower_library_address,
|
|
||||||
follower_abi.clone(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
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::<BTreeMap<_, _>>(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
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::<BTreeMap<_, _>>(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (
|
let library_address = receipt.contract_address?;
|
||||||
CompilerOutput {
|
|
||||||
contracts: leader_post_link_contracts,
|
deployed_libraries.get_or_insert_default().insert(
|
||||||
},
|
library_instance.clone(),
|
||||||
CompilerOutput {
|
(library_ident.clone(), library_address, leader_abi.clone()),
|
||||||
contracts: follower_post_link_contracts,
|
);
|
||||||
},
|
}
|
||||||
) = try_join!(
|
|
||||||
cached_compiler.compile_contracts::<L>(
|
Some((
|
||||||
test.metadata,
|
test,
|
||||||
test.metadata_file_path,
|
platform,
|
||||||
test.mode.clone(),
|
node,
|
||||||
leader_deployed_libraries.as_ref(),
|
compiler,
|
||||||
&test.leader_compiler,
|
reporter,
|
||||||
&leader_reporter,
|
compiler_output,
|
||||||
),
|
deployed_libraries,
|
||||||
cached_compiler.compile_contracts::<F>(
|
))
|
||||||
test.metadata,
|
},
|
||||||
test.metadata_file_path,
|
|
||||||
test.mode.clone(),
|
|
||||||
follower_deployed_libraries.as_ref(),
|
|
||||||
&test.follower_compiler,
|
|
||||||
&follower_reporter
|
|
||||||
)
|
)
|
||||||
)
|
// Compiling the post-link contracts.
|
||||||
.context("Failed to compile post-link contracts for leader/follower in parallel")?;
|
.filter_map(
|
||||||
|
|(test, platform, node, compiler, reporter, _, deployed_libraries)| {
|
||||||
|
let cached_compiler = cached_compiler.clone();
|
||||||
|
|
||||||
let leader_state = CaseState::<L>::new(
|
async move {
|
||||||
test.leader_compiler.version().clone(),
|
let compiler_output = cached_compiler
|
||||||
leader_post_link_contracts,
|
.compile_contracts(
|
||||||
leader_deployed_libraries.unwrap_or_default(),
|
test.metadata,
|
||||||
leader_reporter,
|
test.metadata_file_path,
|
||||||
);
|
test.mode.clone(),
|
||||||
let follower_state = CaseState::<F>::new(
|
deployed_libraries.as_ref(),
|
||||||
test.follower_compiler.version().clone(),
|
compiler.as_ref(),
|
||||||
follower_post_link_contracts,
|
*platform,
|
||||||
follower_deployed_libraries.unwrap_or_default(),
|
reporter,
|
||||||
follower_reporter,
|
)
|
||||||
);
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
%err,
|
||||||
|
platform_identifier = %platform.platform_identifier(),
|
||||||
|
"Pre-linking compilation failed"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
let mut driver = CaseDriver::<L, F>::new(
|
let case_state = CaseState::new(
|
||||||
test.metadata,
|
compiler.version().clone(),
|
||||||
test.case,
|
compiler_output.contracts,
|
||||||
test.leader_node,
|
deployed_libraries.unwrap_or_default(),
|
||||||
test.follower_node,
|
reporter.clone(),
|
||||||
leader_state,
|
);
|
||||||
follower_state,
|
|
||||||
);
|
Some((*node, platform.platform_identifier(), case_state))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
// Collect
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut driver = CaseDriver::new(test.metadata, test.case, platform_state);
|
||||||
driver
|
driver
|
||||||
.execute()
|
.execute()
|
||||||
.await
|
.await
|
||||||
@@ -685,36 +606,38 @@ async fn execute_corpus(
|
|||||||
reporter: Reporter,
|
reporter: Reporter,
|
||||||
report_aggregator_task: impl Future<Output = anyhow::Result<()>>,
|
report_aggregator_task: impl Future<Output = anyhow::Result<()>>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
match (&context.leader, &context.follower) {
|
let platforms = context
|
||||||
(TestingPlatform::Geth, TestingPlatform::Kitchensink) => {
|
.platforms
|
||||||
run_driver::<Geth, Kitchensink>(context, tests, reporter, report_aggregator_task)
|
.iter()
|
||||||
.await?
|
.copied()
|
||||||
}
|
.collect::<BTreeSet<_>>()
|
||||||
(TestingPlatform::Geth, TestingPlatform::Geth) => {
|
.into_iter()
|
||||||
run_driver::<Geth, Geth>(context, tests, reporter, report_aggregator_task).await?
|
.map(Into::<&dyn DynPlatform>::into)
|
||||||
}
|
.collect::<Vec<_>>();
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
run_driver(context, tests, reporter, report_aggregator_task, platforms).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// this represents a single "test"; a mode, path and collection of cases.
|
/// this represents a single "test"; a mode, path and collection of cases.
|
||||||
#[derive(Clone)]
|
#[allow(clippy::type_complexity)]
|
||||||
struct Test<'a, L: Platform, F: Platform> {
|
struct Test<'a> {
|
||||||
metadata: &'a MetadataFile,
|
metadata: &'a MetadataFile,
|
||||||
metadata_file_path: &'a Path,
|
metadata_file_path: &'a Path,
|
||||||
mode: Cow<'a, Mode>,
|
mode: Cow<'a, Mode>,
|
||||||
case_idx: CaseIdx,
|
case_idx: CaseIdx,
|
||||||
case: &'a Case,
|
case: &'a Case,
|
||||||
leader_node: &'a <L as Platform>::Blockchain,
|
platforms: Vec<(
|
||||||
follower_node: &'a <F as Platform>::Blockchain,
|
&'a dyn DynPlatform,
|
||||||
leader_compiler: L::Compiler,
|
&'a dyn EthereumNode,
|
||||||
follower_compiler: F::Compiler,
|
Box<dyn DynSolidityCompiler>,
|
||||||
|
ExecutionSpecificReporter,
|
||||||
|
)>,
|
||||||
reporter: TestSpecificReporter,
|
reporter: TestSpecificReporter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Platform, F: Platform> Test<'a, L, F> {
|
impl<'a> Test<'a> {
|
||||||
/// Checks if this test can be ran with the current configuration.
|
/// Checks if this test can be ran with the current configuration.
|
||||||
pub fn check_compatibility(&self) -> TestCheckFunctionResult {
|
pub fn check_compatibility(&self) -> TestCheckFunctionResult {
|
||||||
self.check_metadata_file_ignored()?;
|
self.check_metadata_file_ignored()?;
|
||||||
@@ -743,24 +666,39 @@ impl<'a, L: Platform, F: Platform> Test<'a, L, F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the leader and the follower both support the desired targets in the metadata file.
|
/// Checks if the platforms all support the desired targets in the metadata file.
|
||||||
fn check_target_compatibility(&self) -> TestCheckFunctionResult {
|
fn check_target_compatibility(&self) -> TestCheckFunctionResult {
|
||||||
let leader_support =
|
let mut error_map = indexmap! {
|
||||||
<L::Blockchain as Node>::matches_target(self.metadata.targets.as_deref());
|
"test_desired_targets" => json!(self.metadata.targets.as_ref()),
|
||||||
let follower_support =
|
};
|
||||||
<F::Blockchain as Node>::matches_target(self.metadata.targets.as_deref());
|
let mut is_allowed = true;
|
||||||
let is_allowed = leader_support && follower_support;
|
for (platform, ..) in self.platforms.iter() {
|
||||||
|
let is_allowed_for_platform = match self.metadata.targets.as_ref() {
|
||||||
|
None => true,
|
||||||
|
Some(targets) => {
|
||||||
|
let mut target_matches = false;
|
||||||
|
for target in targets.iter() {
|
||||||
|
if &platform.vm_identifier() == target {
|
||||||
|
target_matches = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target_matches
|
||||||
|
}
|
||||||
|
};
|
||||||
|
is_allowed &= is_allowed_for_platform;
|
||||||
|
error_map.insert(
|
||||||
|
platform.platform_identifier().into(),
|
||||||
|
json!(is_allowed_for_platform),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if is_allowed {
|
if is_allowed {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err((
|
Err((
|
||||||
"Either the leader or the follower do not support the target desired by the test.",
|
"One of the platforms do do not support the targets allowed by the test.",
|
||||||
indexmap! {
|
error_map,
|
||||||
"test_desired_targets" => json!(self.metadata.targets.as_ref()),
|
|
||||||
"leader_support" => json!(leader_support),
|
|
||||||
"follower_support" => json!(follower_support),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -771,46 +709,51 @@ impl<'a, L: Platform, F: Platform> Test<'a, L, F> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let leader_support = evm_version_requirement
|
let mut error_map = indexmap! {
|
||||||
.matches(&<L::Blockchain as revive_dt_node::Node>::evm_version());
|
"test_desired_evm_version" => json!(self.metadata.required_evm_version),
|
||||||
let follower_support = evm_version_requirement
|
};
|
||||||
.matches(&<F::Blockchain as revive_dt_node::Node>::evm_version());
|
let mut is_allowed = true;
|
||||||
let is_allowed = leader_support && follower_support;
|
for (platform, node, ..) in self.platforms.iter() {
|
||||||
|
let is_allowed_for_platform = evm_version_requirement.matches(&node.evm_version());
|
||||||
|
is_allowed &= is_allowed_for_platform;
|
||||||
|
error_map.insert(
|
||||||
|
platform.platform_identifier().into(),
|
||||||
|
json!(is_allowed_for_platform),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if is_allowed {
|
if is_allowed {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err((
|
Err((
|
||||||
"EVM version is incompatible with either the leader or the follower.",
|
"EVM version is incompatible for the platforms specified",
|
||||||
indexmap! {
|
error_map,
|
||||||
"test_desired_evm_version" => json!(self.metadata.required_evm_version),
|
|
||||||
"leader_support" => json!(leader_support),
|
|
||||||
"follower_support" => json!(follower_support),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the leader and follower compilers support the mode that the test is for.
|
/// Checks if the leader and follower compilers support the mode that the test is for.
|
||||||
fn check_compiler_compatibility(&self) -> TestCheckFunctionResult {
|
fn check_compiler_compatibility(&self) -> TestCheckFunctionResult {
|
||||||
let leader_support = self
|
let mut error_map = indexmap! {
|
||||||
.leader_compiler
|
"test_desired_evm_version" => json!(self.metadata.required_evm_version),
|
||||||
.supports_mode(self.mode.optimize_setting, self.mode.pipeline);
|
};
|
||||||
let follower_support = self
|
let mut is_allowed = true;
|
||||||
.follower_compiler
|
for (platform, _, compiler, ..) in self.platforms.iter() {
|
||||||
.supports_mode(self.mode.optimize_setting, self.mode.pipeline);
|
let is_allowed_for_platform =
|
||||||
let is_allowed = leader_support && follower_support;
|
compiler.supports_mode(self.mode.optimize_setting, self.mode.pipeline);
|
||||||
|
is_allowed &= is_allowed_for_platform;
|
||||||
|
error_map.insert(
|
||||||
|
platform.platform_identifier().into(),
|
||||||
|
json!(is_allowed_for_platform),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if is_allowed {
|
if is_allowed {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err((
|
Err((
|
||||||
"Compilers do not support this mode either for the leader or for the follower.",
|
"Compilers do not support this mode either for the provided platforms.",
|
||||||
indexmap! {
|
error_map,
|
||||||
"mode" => json!(self.mode),
|
|
||||||
"leader_support" => json!(leader_support),
|
|
||||||
"follower_support" => json!(follower_support),
|
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
//! This crate implements concurrent handling of testing node.
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
|
use revive_dt_config::*;
|
||||||
|
use revive_dt_core::DynPlatform;
|
||||||
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
|
|
||||||
|
/// The node pool starts one or more [Node] which then can be accessed
|
||||||
|
/// in a round robbin fashion.
|
||||||
|
pub struct NodePool {
|
||||||
|
next: AtomicUsize,
|
||||||
|
nodes: Vec<Box<dyn EthereumNode + Send + Sync>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodePool {
|
||||||
|
/// Create a new Pool. This will start as many nodes as there are workers in `config`.
|
||||||
|
pub fn new(context: Context, platform: &dyn DynPlatform) -> anyhow::Result<Self> {
|
||||||
|
let concurrency_configuration = AsRef::<ConcurrencyConfiguration>::as_ref(&context);
|
||||||
|
let nodes = concurrency_configuration.number_of_nodes;
|
||||||
|
|
||||||
|
let mut handles = Vec::with_capacity(nodes);
|
||||||
|
for _ in 0..nodes {
|
||||||
|
let context = context.clone();
|
||||||
|
handles.push(platform.new_node(context)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nodes = Vec::with_capacity(nodes);
|
||||||
|
for handle in handles {
|
||||||
|
nodes.push(
|
||||||
|
handle
|
||||||
|
.join()
|
||||||
|
.map_err(|error| anyhow::anyhow!("failed to spawn node: {:?}", error))
|
||||||
|
.context("Failed to join node spawn thread")?
|
||||||
|
.map_err(|error| anyhow::anyhow!("node failed to spawn: {error}"))
|
||||||
|
.context("Node failed to spawn")?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
nodes,
|
||||||
|
next: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a handle to the next node.
|
||||||
|
pub fn round_robbin(&self) -> &dyn EthereumNode {
|
||||||
|
let current = self.next.fetch_add(1, Ordering::SeqCst) % self.nodes.len();
|
||||||
|
self.nodes.get(current).unwrap().as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -308,7 +308,7 @@ impl Input {
|
|||||||
|
|
||||||
pub async fn encoded_input(
|
pub async fn encoded_input(
|
||||||
&self,
|
&self,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
) -> anyhow::Result<Bytes> {
|
) -> anyhow::Result<Bytes> {
|
||||||
match self.method {
|
match self.method {
|
||||||
@@ -377,7 +377,7 @@ impl Input {
|
|||||||
/// Parse this input into a legacy transaction.
|
/// Parse this input into a legacy transaction.
|
||||||
pub async fn legacy_transaction(
|
pub async fn legacy_transaction(
|
||||||
&self,
|
&self,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
) -> anyhow::Result<TransactionRequest> {
|
) -> anyhow::Result<TransactionRequest> {
|
||||||
let input_data = self
|
let input_data = self
|
||||||
@@ -466,7 +466,7 @@ impl Calldata {
|
|||||||
|
|
||||||
pub async fn calldata(
|
pub async fn calldata(
|
||||||
&self,
|
&self,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
) -> anyhow::Result<Vec<u8>> {
|
) -> anyhow::Result<Vec<u8>> {
|
||||||
let mut buffer = Vec::<u8>::with_capacity(self.size_requirement());
|
let mut buffer = Vec::<u8>::with_capacity(self.size_requirement());
|
||||||
@@ -478,7 +478,7 @@ impl Calldata {
|
|||||||
pub async fn calldata_into_slice(
|
pub async fn calldata_into_slice(
|
||||||
&self,
|
&self,
|
||||||
buffer: &mut Vec<u8>,
|
buffer: &mut Vec<u8>,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
@@ -515,7 +515,7 @@ impl Calldata {
|
|||||||
pub async fn is_equivalent(
|
pub async fn is_equivalent(
|
||||||
&self,
|
&self,
|
||||||
other: &[u8],
|
other: &[u8],
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
) -> anyhow::Result<bool> {
|
) -> anyhow::Result<bool> {
|
||||||
match self {
|
match self {
|
||||||
@@ -557,7 +557,7 @@ impl CalldataItem {
|
|||||||
#[instrument(level = "info", skip_all, err)]
|
#[instrument(level = "info", skip_all, err)]
|
||||||
async fn resolve(
|
async fn resolve(
|
||||||
&self,
|
&self,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
) -> anyhow::Result<U256> {
|
) -> anyhow::Result<U256> {
|
||||||
let mut stack = Vec::<CalldataToken<U256>>::new();
|
let mut stack = Vec::<CalldataToken<U256>>::new();
|
||||||
@@ -662,7 +662,7 @@ impl<T: AsRef<str>> CalldataToken<T> {
|
|||||||
/// https://github.com/matter-labs/era-compiler-tester/blob/0ed598a27f6eceee7008deab3ff2311075a2ec69/compiler_tester/src/test/case/input/value.rs#L43-L146
|
/// https://github.com/matter-labs/era-compiler-tester/blob/0ed598a27f6eceee7008deab3ff2311075a2ec69/compiler_tester/src/test/case/input/value.rs#L43-L146
|
||||||
async fn resolve(
|
async fn resolve(
|
||||||
self,
|
self,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
) -> anyhow::Result<CalldataToken<U256>> {
|
) -> anyhow::Result<CalldataToken<U256>> {
|
||||||
match self {
|
match self {
|
||||||
@@ -1010,7 +1010,7 @@ mod tests {
|
|||||||
async fn resolve_calldata_item(
|
async fn resolve_calldata_item(
|
||||||
input: &str,
|
input: &str,
|
||||||
deployed_contracts: &HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
deployed_contracts: &HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
||||||
resolver: &impl ResolverApi,
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
) -> anyhow::Result<U256> {
|
) -> anyhow::Result<U256> {
|
||||||
let context = ResolutionContext::default().with_deployed_contracts(deployed_contracts);
|
let context = ResolutionContext::default().with_deployed_contracts(deployed_contracts);
|
||||||
CalldataItem::new(input).resolve(resolver, context).await
|
CalldataItem::new(input).resolve(resolver, context).await
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use revive_common::EVMVersion;
|
use revive_common::EVMVersion;
|
||||||
use revive_dt_common::{
|
use revive_dt_common::{
|
||||||
cached_fs::read_to_string, iterators::FilesWithExtensionIterator, macros::define_wrapper_type,
|
cached_fs::read_to_string,
|
||||||
types::Mode,
|
iterators::FilesWithExtensionIterator,
|
||||||
|
macros::define_wrapper_type,
|
||||||
|
types::{Mode, VmIdentifier},
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
@@ -81,7 +83,7 @@ pub struct Metadata {
|
|||||||
/// example, if we wish for the metadata file's cases to only be run on PolkaVM then we'd
|
/// example, if we wish for the metadata file's cases to only be run on PolkaVM then we'd
|
||||||
/// specify a target of "PolkaVM" in here.
|
/// specify a target of "PolkaVM" in here.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub targets: Option<Vec<String>>,
|
pub targets: Option<Vec<VmIdentifier>>,
|
||||||
|
|
||||||
/// A vector of the test cases and workloads contained within the metadata file. This is their
|
/// A vector of the test cases and workloads contained within the metadata file. This is their
|
||||||
/// primary description.
|
/// primary description.
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ repository.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
revive-common = { workspace = true }
|
||||||
|
|
||||||
|
revive-dt-common = { workspace = true }
|
||||||
revive-dt-format = { workspace = true }
|
revive-dt-format = { workspace = true }
|
||||||
|
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
//! This crate implements all node interactions.
|
//! This crate implements all node interactions.
|
||||||
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use alloy::primitives::{Address, StorageKey, TxHash, U256};
|
use alloy::primitives::{Address, StorageKey, TxHash, U256};
|
||||||
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace};
|
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace};
|
||||||
use alloy::rpc::types::{EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest};
|
use alloy::rpc::types::{EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use revive_common::EVMVersion;
|
||||||
use revive_dt_format::traits::ResolverApi;
|
use revive_dt_format::traits::ResolverApi;
|
||||||
|
|
||||||
/// An interface for all interactions with Ethereum compatible nodes.
|
/// An interface for all interactions with Ethereum compatible nodes.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub trait EthereumNode {
|
pub trait EthereumNode {
|
||||||
|
fn id(&self) -> usize;
|
||||||
|
|
||||||
|
/// Returns the nodes connection string.
|
||||||
|
fn connection_string(&self) -> &str;
|
||||||
|
|
||||||
/// Execute the [TransactionRequest] and return a [TransactionReceipt].
|
/// Execute the [TransactionRequest] and return a [TransactionReceipt].
|
||||||
fn execute_transaction(
|
fn execute_transaction(
|
||||||
&self,
|
&self,
|
||||||
@@ -38,5 +46,8 @@ pub trait EthereumNode {
|
|||||||
) -> Pin<Box<dyn Future<Output = Result<EIP1186AccountProofResponse>> + '_>>;
|
) -> Pin<Box<dyn Future<Output = Result<EIP1186AccountProofResponse>> + '_>>;
|
||||||
|
|
||||||
/// Returns the resolver that is to use with this ethereum node.
|
/// Returns the resolver that is to use with this ethereum node.
|
||||||
fn resolver(&self) -> Pin<Box<dyn Future<Output = Result<Box<dyn ResolverApi + '_>>> + '_>>;
|
fn resolver(&self) -> Pin<Box<dyn Future<Output = Result<Arc<dyn ResolverApi + '_>>> + '_>>;
|
||||||
|
|
||||||
|
/// Returns the EVM version of the node.
|
||||||
|
fn evm_version(&self) -> EVMVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-23
@@ -327,6 +327,14 @@ impl GethNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EthereumNode for GethNode {
|
impl EthereumNode for GethNode {
|
||||||
|
fn id(&self) -> usize {
|
||||||
|
self.id as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connection_string(&self) -> &str {
|
||||||
|
&self.connection_string
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(
|
#[instrument(
|
||||||
level = "info",
|
level = "info",
|
||||||
skip_all,
|
skip_all,
|
||||||
@@ -498,13 +506,17 @@ impl EthereumNode for GethNode {
|
|||||||
// #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
|
// #[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
|
||||||
fn resolver(
|
fn resolver(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn ResolverApi + '_>>> + '_>> {
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<Arc<dyn ResolverApi + '_>>> + '_>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
let provider = self.provider().await?;
|
let provider = self.provider().await?;
|
||||||
Ok(Box::new(GethNodeResolver { id, provider }) as Box<dyn ResolverApi>)
|
Ok(Arc::new(GethNodeResolver { id, provider }) as Arc<dyn ResolverApi>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn evm_version(&self) -> EVMVersion {
|
||||||
|
EVMVersion::Cancun
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
|
pub struct GethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
|
||||||
@@ -788,16 +800,6 @@ impl ResolverApi for GethNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Node for GethNode {
|
impl Node for GethNode {
|
||||||
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
|
|
||||||
fn id(&self) -> usize {
|
|
||||||
self.id as _
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
|
|
||||||
fn connection_string(&self) -> String {
|
|
||||||
self.connection_string.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
|
#[instrument(level = "info", skip_all, fields(geth_node_id = self.id))]
|
||||||
fn shutdown(&mut self) -> anyhow::Result<()> {
|
fn shutdown(&mut self) -> anyhow::Result<()> {
|
||||||
// Terminate the processes in a graceful manner to allow for the output to be flushed.
|
// Terminate the processes in a graceful manner to allow for the output to be flushed.
|
||||||
@@ -840,17 +842,6 @@ impl Node for GethNode {
|
|||||||
.stdout;
|
.stdout;
|
||||||
Ok(String::from_utf8_lossy(&output).into())
|
Ok(String::from_utf8_lossy(&output).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_target(targets: Option<&[String]>) -> bool {
|
|
||||||
match targets {
|
|
||||||
None => true,
|
|
||||||
Some(targets) => targets.iter().any(|str| str.as_str() == "evm"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evm_version() -> EVMVersion {
|
|
||||||
EVMVersion::Cancun
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for GethNode {
|
impl Drop for GethNode {
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
//! This crate implements the testing nodes.
|
//! This crate implements the testing nodes.
|
||||||
|
|
||||||
use alloy::genesis::Genesis;
|
use alloy::genesis::Genesis;
|
||||||
use revive_common::EVMVersion;
|
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
pub mod geth;
|
pub mod geth;
|
||||||
pub mod pool;
|
|
||||||
pub mod substrate;
|
pub mod substrate;
|
||||||
|
|
||||||
/// An abstract interface for testing nodes.
|
/// An abstract interface for testing nodes.
|
||||||
pub trait Node: EthereumNode {
|
pub trait Node: EthereumNode {
|
||||||
/// Returns the identifier of the node.
|
|
||||||
fn id(&self) -> usize;
|
|
||||||
|
|
||||||
/// Spawns a node configured according to the genesis json.
|
/// Spawns a node configured according to the genesis json.
|
||||||
///
|
///
|
||||||
/// Blocking until it's ready to accept transactions.
|
/// Blocking until it's ready to accept transactions.
|
||||||
@@ -25,16 +20,6 @@ pub trait Node: EthereumNode {
|
|||||||
/// Blocking until it's completely stopped.
|
/// Blocking until it's completely stopped.
|
||||||
fn shutdown(&mut self) -> anyhow::Result<()>;
|
fn shutdown(&mut self) -> anyhow::Result<()>;
|
||||||
|
|
||||||
/// Returns the nodes connection string.
|
|
||||||
fn connection_string(&self) -> String;
|
|
||||||
|
|
||||||
/// Returns the node version.
|
/// Returns the node version.
|
||||||
fn version(&self) -> anyhow::Result<String>;
|
fn version(&self) -> anyhow::Result<String>;
|
||||||
|
|
||||||
/// Given a list of targets from the metadata file, this function determines if the metadata
|
|
||||||
/// file can be ran on this node or not.
|
|
||||||
fn matches_target(targets: Option<&[String]>) -> bool;
|
|
||||||
|
|
||||||
/// Returns the EVM version of the node.
|
|
||||||
fn evm_version() -> EVMVersion;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
//! This crate implements concurrent handling of testing node.
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
|
|
||||||
use alloy::genesis::Genesis;
|
|
||||||
use anyhow::Context as _;
|
|
||||||
use revive_dt_config::{
|
|
||||||
ConcurrencyConfiguration, EthRpcConfiguration, GenesisConfiguration, GethConfiguration,
|
|
||||||
KitchensinkConfiguration, ReviveDevNodeConfiguration, WalletConfiguration,
|
|
||||||
WorkingDirectoryConfiguration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::Node;
|
|
||||||
|
|
||||||
/// The node pool starts one or more [Node] which then can be accessed
|
|
||||||
/// in a round robbin fashion.
|
|
||||||
pub struct NodePool<T> {
|
|
||||||
next: AtomicUsize,
|
|
||||||
nodes: Vec<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> NodePool<T>
|
|
||||||
where
|
|
||||||
T: Node + Send + 'static,
|
|
||||||
{
|
|
||||||
/// Create a new Pool. This will start as many nodes as there are workers in `config`.
|
|
||||||
pub fn new(
|
|
||||||
context: impl AsRef<WorkingDirectoryConfiguration>
|
|
||||||
+ AsRef<ConcurrencyConfiguration>
|
|
||||||
+ AsRef<GenesisConfiguration>
|
|
||||||
+ AsRef<WalletConfiguration>
|
|
||||||
+ AsRef<GethConfiguration>
|
|
||||||
+ AsRef<KitchensinkConfiguration>
|
|
||||||
+ AsRef<ReviveDevNodeConfiguration>
|
|
||||||
+ AsRef<EthRpcConfiguration>
|
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ Clone
|
|
||||||
+ 'static,
|
|
||||||
) -> anyhow::Result<Self> {
|
|
||||||
let concurrency_configuration = AsRef::<ConcurrencyConfiguration>::as_ref(&context);
|
|
||||||
let genesis_configuration = AsRef::<GenesisConfiguration>::as_ref(&context);
|
|
||||||
|
|
||||||
let nodes = concurrency_configuration.number_of_nodes;
|
|
||||||
let genesis = genesis_configuration.genesis()?;
|
|
||||||
|
|
||||||
let mut handles = Vec::with_capacity(nodes);
|
|
||||||
for _ in 0..nodes {
|
|
||||||
let context = context.clone();
|
|
||||||
let genesis = genesis.clone();
|
|
||||||
handles.push(thread::spawn(move || spawn_node::<T>(context, genesis)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut nodes = Vec::with_capacity(nodes);
|
|
||||||
for handle in handles {
|
|
||||||
nodes.push(
|
|
||||||
handle
|
|
||||||
.join()
|
|
||||||
.map_err(|error| anyhow::anyhow!("failed to spawn node: {:?}", error))
|
|
||||||
.context("Failed to join node spawn thread")?
|
|
||||||
.map_err(|error| anyhow::anyhow!("node failed to spawn: {error}"))
|
|
||||||
.context("Node failed to spawn")?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
nodes,
|
|
||||||
next: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a handle to the next node.
|
|
||||||
pub fn round_robbin(&self) -> &T {
|
|
||||||
let current = self.next.fetch_add(1, Ordering::SeqCst) % self.nodes.len();
|
|
||||||
self.nodes.get(current).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_node<T: Node + Send>(
|
|
||||||
_: impl AsRef<WorkingDirectoryConfiguration>
|
|
||||||
+ AsRef<ConcurrencyConfiguration>
|
|
||||||
+ AsRef<GenesisConfiguration>
|
|
||||||
+ AsRef<WalletConfiguration>
|
|
||||||
+ AsRef<GethConfiguration>
|
|
||||||
+ AsRef<KitchensinkConfiguration>
|
|
||||||
+ AsRef<ReviveDevNodeConfiguration>
|
|
||||||
+ AsRef<EthRpcConfiguration>
|
|
||||||
+ Clone
|
|
||||||
+ 'static,
|
|
||||||
_: Genesis,
|
|
||||||
) -> anyhow::Result<T> {
|
|
||||||
todo!("Remove");
|
|
||||||
}
|
|
||||||
@@ -432,6 +432,14 @@ impl SubstrateNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EthereumNode for SubstrateNode {
|
impl EthereumNode for SubstrateNode {
|
||||||
|
fn id(&self) -> usize {
|
||||||
|
self.id as _
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connection_string(&self) -> &str {
|
||||||
|
&self.rpc_url
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_transaction(
|
fn execute_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: alloy::rpc::types::TransactionRequest,
|
transaction: alloy::rpc::types::TransactionRequest,
|
||||||
@@ -520,13 +528,17 @@ impl EthereumNode for SubstrateNode {
|
|||||||
|
|
||||||
fn resolver(
|
fn resolver(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<Box<dyn ResolverApi + '_>>> + '_>> {
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<Arc<dyn ResolverApi + '_>>> + '_>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let id = self.id;
|
let id = self.id;
|
||||||
let provider = self.provider().await?;
|
let provider = self.provider().await?;
|
||||||
Ok(Box::new(SubstrateNodeResolver { id, provider }) as Box<dyn ResolverApi>)
|
Ok(Arc::new(SubstrateNodeResolver { id, provider }) as Arc<dyn ResolverApi>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn evm_version(&self) -> EVMVersion {
|
||||||
|
EVMVersion::Cancun
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubstrateNodeResolver<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> {
|
pub struct SubstrateNodeResolver<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> {
|
||||||
@@ -803,14 +815,6 @@ impl ResolverApi for SubstrateNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Node for SubstrateNode {
|
impl Node for SubstrateNode {
|
||||||
fn id(&self) -> usize {
|
|
||||||
self.id as _
|
|
||||||
}
|
|
||||||
|
|
||||||
fn connection_string(&self) -> String {
|
|
||||||
self.rpc_url.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shutdown(&mut self) -> anyhow::Result<()> {
|
fn shutdown(&mut self) -> anyhow::Result<()> {
|
||||||
// Terminate the processes in a graceful manner to allow for the output to be flushed.
|
// Terminate the processes in a graceful manner to allow for the output to be flushed.
|
||||||
if let Some(mut child) = self.process_proxy.take() {
|
if let Some(mut child) = self.process_proxy.take() {
|
||||||
@@ -854,17 +858,6 @@ impl Node for SubstrateNode {
|
|||||||
.stdout;
|
.stdout;
|
||||||
Ok(String::from_utf8_lossy(&output).into())
|
Ok(String::from_utf8_lossy(&output).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_target(targets: Option<&[String]>) -> bool {
|
|
||||||
match targets {
|
|
||||||
None => true,
|
|
||||||
Some(targets) => targets.iter().any(|str| str.as_str() == "pvm"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evm_version() -> EVMVersion {
|
|
||||||
EVMVersion::Cancun
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for SubstrateNode {
|
impl Drop for SubstrateNode {
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ use std::{
|
|||||||
use alloy_primitives::Address;
|
use alloy_primitives::Address;
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use revive_dt_common::types::PlatformIdentifier;
|
||||||
use revive_dt_compiler::{CompilerInput, CompilerOutput, Mode};
|
use revive_dt_compiler::{CompilerInput, CompilerOutput, Mode};
|
||||||
use revive_dt_config::{Context, TestingPlatform};
|
use revive_dt_config::Context;
|
||||||
use revive_dt_format::{case::CaseIdx, corpus::Corpus, metadata::ContractInstance};
|
use revive_dt_format::{case::CaseIdx, corpus::Corpus, metadata::ContractInstance};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -84,11 +85,8 @@ impl ReportAggregator {
|
|||||||
RunnerEvent::TestIgnored(event) => {
|
RunnerEvent::TestIgnored(event) => {
|
||||||
self.handle_test_ignored_event(*event);
|
self.handle_test_ignored_event(*event);
|
||||||
}
|
}
|
||||||
RunnerEvent::LeaderNodeAssigned(event) => {
|
RunnerEvent::NodeAssigned(event) => {
|
||||||
self.handle_leader_node_assigned_event(*event);
|
self.handle_node_assigned_event(*event);
|
||||||
}
|
|
||||||
RunnerEvent::FollowerNodeAssigned(event) => {
|
|
||||||
self.handle_follower_node_assigned_event(*event);
|
|
||||||
}
|
}
|
||||||
RunnerEvent::PreLinkContractsCompilationSucceeded(event) => {
|
RunnerEvent::PreLinkContractsCompilationSucceeded(event) => {
|
||||||
self.handle_pre_link_contracts_compilation_succeeded_event(*event)
|
self.handle_pre_link_contracts_compilation_succeeded_event(*event)
|
||||||
@@ -257,28 +255,15 @@ impl ReportAggregator {
|
|||||||
let _ = self.listener_tx.send(event);
|
let _ = self.listener_tx.send(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_leader_node_assigned_event(&mut self, event: LeaderNodeAssignedEvent) {
|
fn handle_node_assigned_event(&mut self, event: NodeAssignedEvent) {
|
||||||
let execution_information = self.execution_information(&ExecutionSpecifier {
|
let execution_information = self.execution_information(&ExecutionSpecifier {
|
||||||
test_specifier: event.test_specifier,
|
test_specifier: event.test_specifier,
|
||||||
node_id: event.id,
|
node_id: event.id,
|
||||||
node_designation: NodeDesignation::Leader,
|
platform_identifier: event.platform_identifier,
|
||||||
});
|
});
|
||||||
execution_information.node = Some(TestCaseNodeInformation {
|
execution_information.node = Some(TestCaseNodeInformation {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
platform: event.platform,
|
platform_identifier: event.platform_identifier,
|
||||||
connection_string: event.connection_string,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_follower_node_assigned_event(&mut self, event: FollowerNodeAssignedEvent) {
|
|
||||||
let execution_information = self.execution_information(&ExecutionSpecifier {
|
|
||||||
test_specifier: event.test_specifier,
|
|
||||||
node_id: event.id,
|
|
||||||
node_designation: NodeDesignation::Follower,
|
|
||||||
});
|
|
||||||
execution_information.node = Some(TestCaseNodeInformation {
|
|
||||||
id: event.id,
|
|
||||||
platform: event.platform,
|
|
||||||
connection_string: event.connection_string,
|
connection_string: event.connection_string,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -413,14 +398,11 @@ impl ReportAggregator {
|
|||||||
specifier: &ExecutionSpecifier,
|
specifier: &ExecutionSpecifier,
|
||||||
) -> &mut ExecutionInformation {
|
) -> &mut ExecutionInformation {
|
||||||
let test_case_report = self.test_case_report(&specifier.test_specifier);
|
let test_case_report = self.test_case_report(&specifier.test_specifier);
|
||||||
match specifier.node_designation {
|
test_case_report
|
||||||
NodeDesignation::Leader => test_case_report
|
.platform_execution
|
||||||
.leader_execution_information
|
.entry(specifier.platform_identifier)
|
||||||
.get_or_insert_default(),
|
.or_default()
|
||||||
NodeDesignation::Follower => test_case_report
|
.get_or_insert_default()
|
||||||
.follower_execution_information
|
|
||||||
.get_or_insert_default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,12 +437,8 @@ pub struct TestCaseReport {
|
|||||||
/// Information on the status of the test case and whether it succeeded, failed, or was ignored.
|
/// Information on the status of the test case and whether it succeeded, failed, or was ignored.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub status: Option<TestCaseStatus>,
|
pub status: Option<TestCaseStatus>,
|
||||||
/// Information related to the execution on the leader.
|
/// Information related to the execution on one of the platforms.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub platform_execution: BTreeMap<PlatformIdentifier, Option<ExecutionInformation>>,
|
||||||
pub leader_execution_information: Option<ExecutionInformation>,
|
|
||||||
/// Information related to the execution on the follower.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub follower_execution_information: Option<ExecutionInformation>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information related to the status of the test. Could be that the test succeeded, failed, or that
|
/// Information related to the status of the test. Could be that the test succeeded, failed, or that
|
||||||
@@ -494,7 +472,7 @@ pub struct TestCaseNodeInformation {
|
|||||||
/// The ID of the node that this case is being executed on.
|
/// The ID of the node that this case is being executed on.
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
/// The platform of the node.
|
/// The platform of the node.
|
||||||
pub platform: TestingPlatform,
|
pub platform_identifier: PlatformIdentifier,
|
||||||
/// The connection string of the node.
|
/// The connection string of the node.
|
||||||
pub connection_string: String,
|
pub connection_string: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use revive_dt_common::define_wrapper_type;
|
use revive_dt_common::{define_wrapper_type, types::PlatformIdentifier};
|
||||||
use revive_dt_compiler::Mode;
|
use revive_dt_compiler::Mode;
|
||||||
use revive_dt_format::{case::CaseIdx, input::StepIdx};
|
use revive_dt_format::{case::CaseIdx, input::StepIdx};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -27,13 +27,7 @@ pub struct TestSpecifier {
|
|||||||
pub struct ExecutionSpecifier {
|
pub struct ExecutionSpecifier {
|
||||||
pub test_specifier: Arc<TestSpecifier>,
|
pub test_specifier: Arc<TestSpecifier>,
|
||||||
pub node_id: usize,
|
pub node_id: usize,
|
||||||
pub node_designation: NodeDesignation,
|
pub platform_identifier: PlatformIdentifier,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub enum NodeDesignation {
|
|
||||||
Leader,
|
|
||||||
Follower,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use std::{collections::BTreeMap, path::PathBuf, sync::Arc};
|
|||||||
use alloy_primitives::Address;
|
use alloy_primitives::Address;
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use revive_dt_common::types::PlatformIdentifier;
|
||||||
use revive_dt_compiler::{CompilerInput, CompilerOutput};
|
use revive_dt_compiler::{CompilerInput, CompilerOutput};
|
||||||
use revive_dt_config::TestingPlatform;
|
|
||||||
use revive_dt_format::metadata::Metadata;
|
use revive_dt_format::metadata::Metadata;
|
||||||
use revive_dt_format::{corpus::Corpus, metadata::ContractInstance};
|
use revive_dt_format::{corpus::Corpus, metadata::ContractInstance};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
@@ -412,14 +412,14 @@ macro_rules! define_event {
|
|||||||
pub fn execution_specific_reporter(
|
pub fn execution_specific_reporter(
|
||||||
&self,
|
&self,
|
||||||
node_id: impl Into<usize>,
|
node_id: impl Into<usize>,
|
||||||
node_designation: impl Into<$crate::common::NodeDesignation>
|
platform_identifier: impl Into<PlatformIdentifier>
|
||||||
) -> [< $ident ExecutionSpecificReporter >] {
|
) -> [< $ident ExecutionSpecificReporter >] {
|
||||||
[< $ident ExecutionSpecificReporter >] {
|
[< $ident ExecutionSpecificReporter >] {
|
||||||
reporter: self.reporter.clone(),
|
reporter: self.reporter.clone(),
|
||||||
execution_specifier: Arc::new($crate::common::ExecutionSpecifier {
|
execution_specifier: Arc::new($crate::common::ExecutionSpecifier {
|
||||||
test_specifier: self.test_specifier.clone(),
|
test_specifier: self.test_specifier.clone(),
|
||||||
node_id: node_id.into(),
|
node_id: node_id.into(),
|
||||||
node_designation: node_designation.into(),
|
platform_identifier: platform_identifier.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -521,24 +521,13 @@ define_event! {
|
|||||||
reason: String,
|
reason: String,
|
||||||
},
|
},
|
||||||
/// An event emitted when the test case is assigned a leader node.
|
/// An event emitted when the test case is assigned a leader node.
|
||||||
LeaderNodeAssigned {
|
NodeAssigned {
|
||||||
/// A specifier for the test that the assignment is for.
|
/// A specifier for the test that the assignment is for.
|
||||||
test_specifier: Arc<TestSpecifier>,
|
test_specifier: Arc<TestSpecifier>,
|
||||||
/// The ID of the node that this case is being executed on.
|
/// The ID of the node that this case is being executed on.
|
||||||
id: usize,
|
id: usize,
|
||||||
/// The platform of the node.
|
/// The identifier of the platform used.
|
||||||
platform: TestingPlatform,
|
platform_identifier: PlatformIdentifier,
|
||||||
/// The connection string of the node.
|
|
||||||
connection_string: String,
|
|
||||||
},
|
|
||||||
/// An event emitted when the test case is assigned a follower node.
|
|
||||||
FollowerNodeAssigned {
|
|
||||||
/// A specifier for the test that the assignment is for.
|
|
||||||
test_specifier: Arc<TestSpecifier>,
|
|
||||||
/// The ID of the node that this case is being executed on.
|
|
||||||
id: usize,
|
|
||||||
/// The platform of the node.
|
|
||||||
platform: TestingPlatform,
|
|
||||||
/// The connection string of the node.
|
/// The connection string of the node.
|
||||||
connection_string: String,
|
connection_string: String,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user