This commit is contained in:
pgherveou
2025-10-08 10:14:22 +02:00
parent 6e658aec49
commit 86f2173e8b
3 changed files with 29 additions and 49 deletions
-1
View File
@@ -26,7 +26,6 @@ use revive_dt_node::{
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use tracing::info; use tracing::info;
// Re-export helper types
pub use helpers::CachedCompiler; pub use helpers::CachedCompiler;
/// A trait that describes the interface for the platforms that are supported by the tool. /// A trait that describes the interface for the platforms that are supported by the tool.
+15 -33
View File
@@ -50,12 +50,14 @@ struct MlTestRunnerArgs {
start_platform: bool, start_platform: bool,
/// Private key to use for wallet initialization (hex string with or without 0x prefix) /// Private key to use for wallet initialization (hex string with or without 0x prefix)
#[arg(long = "private-key", default_value = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d")] #[arg(
long = "private-key",
default_value = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"
)]
private_key: String, private_key: String,
} }
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
// Initialize tracing subscriber
let subscriber = FmtSubscriber::builder() let subscriber = FmtSubscriber::builder()
.with_env_filter(EnvFilter::from_default_env()) .with_env_filter(EnvFilter::from_default_env())
.with_writer(std::io::stderr) .with_writer(std::io::stderr)
@@ -68,7 +70,6 @@ fn main() -> anyhow::Result<()> {
info!("Platform: {:?}", args.platform); info!("Platform: {:?}", args.platform);
info!("Start platform: {}", args.start_platform); info!("Start platform: {}", args.start_platform);
// Run the async body
tokio::runtime::Builder::new_multi_thread() tokio::runtime::Builder::new_multi_thread()
.enable_all() .enable_all()
.build() .build()
@@ -79,12 +80,10 @@ fn main() -> anyhow::Result<()> {
async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> { async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
let start_time = Instant::now(); let start_time = Instant::now();
// Discover test files
info!("Discovering test files from: {}", args.path.display()); info!("Discovering test files from: {}", args.path.display());
let test_files = discover_test_files(&args.path)?; let test_files = discover_test_files(&args.path)?;
info!("Found {} test file(s)", test_files.len()); info!("Found {} test file(s)", test_files.len());
// Load cached passed tests if provided
let cached_passed = if let Some(cache_file) = &args.cached_passed { let cached_passed = if let Some(cache_file) = &args.cached_passed {
let cached = load_cached_passed(cache_file)?; let cached = load_cached_passed(cache_file)?;
info!("Loaded {} cached passed test(s)", cached.len()); info!("Loaded {} cached passed test(s)", cached.len());
@@ -95,7 +94,6 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
let cached_passed = Arc::new(Mutex::new(cached_passed)); let cached_passed = Arc::new(Mutex::new(cached_passed));
// Statistics
let mut passed_files = 0; let mut passed_files = 0;
let mut failed_files = 0; let mut failed_files = 0;
let mut skipped_files = 0; let mut skipped_files = 0;
@@ -108,7 +106,6 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
const BOLD: &str = "\x1B[1m"; const BOLD: &str = "\x1B[1m";
const BOLD_RESET: &str = "\x1B[22m"; const BOLD_RESET: &str = "\x1B[22m";
// Process each file
for test_file in test_files { for test_file in test_files {
let file_display = test_file.display().to_string(); let file_display = test_file.display().to_string();
@@ -122,7 +119,6 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
} }
} }
// Load metadata from file
info!("Loading metadata from: {}", test_file.display()); info!("Loading metadata from: {}", test_file.display());
let metadata_file = match load_metadata_file(&test_file) { let metadata_file = match load_metadata_file(&test_file) {
Ok(mf) => { Ok(mf) => {
@@ -141,7 +137,6 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
}, },
}; };
// Execute test cases for this file
info!("Executing test file: {}", file_display); info!("Executing test file: {}", file_display);
match execute_test_file(&args, &metadata_file).await { match execute_test_file(&args, &metadata_file).await {
Ok(_) => { Ok(_) => {
@@ -149,7 +144,6 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
info!("Test file passed: {}", file_display); info!("Test file passed: {}", file_display);
passed_files += 1; passed_files += 1;
// Add to cache
{ {
let mut cache = cached_passed.lock().await; let mut cache = cached_passed.lock().await;
cache.insert(file_display); cache.insert(file_display);
@@ -157,7 +151,6 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
}, },
Err(e) => { Err(e) => {
println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display); println!("test {} ... {RED}FAILED{COLOUR_RESET}", file_display);
info!("Test file failed: {}", file_display);
failed_files += 1; failed_files += 1;
failures.push((file_display, format!("{:?}", e))); failures.push((file_display, format!("{:?}", e)));
@@ -169,7 +162,6 @@ async fn run(args: MlTestRunnerArgs) -> anyhow::Result<()> {
} }
} }
// Save cached passed tests
if let Some(cache_file) = &args.cached_passed { if let Some(cache_file) = &args.cached_passed {
let cache = cached_passed.lock().await; let cache = cached_passed.lock().await;
info!("Saving {} cached passed test(s)", cache.len()); info!("Saving {} cached passed test(s)", cache.len());
@@ -280,11 +272,9 @@ async fn execute_test_file(
TestingPlatform::Zombienet => &revive_dt_core::ZombienetPolkavmResolcPlatform, TestingPlatform::Zombienet => &revive_dt_core::ZombienetPolkavmResolcPlatform,
}; };
// Create temporary working directory
let temp_dir = TempDir::new()?; let temp_dir = TempDir::new()?;
info!("Created temporary directory: {}", temp_dir.path().display()); info!("Created temporary directory: {}", temp_dir.path().display());
// Create a test execution context (with defaults)
let test_context = TestExecutionContext::default(); let test_context = TestExecutionContext::default();
let context = revive_dt_config::Context::Test(Box::new(test_context)); let context = revive_dt_config::Context::Test(Box::new(test_context));
@@ -300,9 +290,8 @@ async fn execute_test_file(
.context("Failed to start node")?; .context("Failed to start node")?;
info!("Node started with ID: {}, connection: {}", node.id(), node.connection_string()); info!("Node started with ID: {}, connection: {}", node.id(), node.connection_string());
let node = Box::leak(node);
// Run pre-transactions on the node
let node = Box::leak(node); // Leak to get 'static lifetime for simplicity
info!("Running pre-transactions..."); info!("Running pre-transactions...");
node.pre_transactions().await.context("Failed to run pre-transactions")?; node.pre_transactions().await.context("Failed to run pre-transactions")?;
info!("Pre-transactions completed"); info!("Pre-transactions completed");
@@ -311,36 +300,37 @@ async fn execute_test_file(
} else { } else {
info!("Using existing node"); info!("Using existing node");
let existing_node: Box<dyn revive_dt_node_interaction::EthereumNode> = match args.platform { let existing_node: Box<dyn revive_dt_node_interaction::EthereumNode> = match args.platform {
TestingPlatform::Geth => TestingPlatform::Geth => Box::new(
Box::new(revive_dt_node::node_implementations::geth::GethNode::new_existing(&args.private_key).await?), revive_dt_node::node_implementations::geth::GethNode::new_existing(
&args.private_key,
)
.await?,
),
TestingPlatform::Kitchensink | TestingPlatform::Zombienet => Box::new( TestingPlatform::Kitchensink | TestingPlatform::Zombienet => Box::new(
revive_dt_node::node_implementations::substrate::SubstrateNode::new_existing(&args.private_key).await?, revive_dt_node::node_implementations::substrate::SubstrateNode::new_existing(
&args.private_key,
)
.await?,
), ),
}; };
Box::leak(existing_node) Box::leak(existing_node)
}; };
// Create a cached compiler for this file (wrapped in Arc like the main code does)
info!("Initializing cached compiler"); info!("Initializing cached compiler");
let cached_compiler = CachedCompiler::new(temp_dir.path().join("compilation_cache"), false) let cached_compiler = CachedCompiler::new(temp_dir.path().join("compilation_cache"), false)
.await .await
.map(Arc::new) .map(Arc::new)
.context("Failed to create cached compiler")?; .context("Failed to create cached compiler")?;
// Create a private key allocator
let private_key_allocator = let private_key_allocator =
Arc::new(Mutex::new(PrivateKeyAllocator::new(alloy::primitives::U256::from(100)))); Arc::new(Mutex::new(PrivateKeyAllocator::new(alloy::primitives::U256::from(100))));
// Create reporter infrastructure (minimal, just for the Driver API)
// Note: We need to keep the report_task alive, otherwise the reporter channel closes
let (reporter, report_task) = let (reporter, report_task) =
revive_dt_report::ReportAggregator::new(context.clone()).into_task(); revive_dt_report::ReportAggregator::new(context.clone()).into_task();
// Spawn the report task in the background to keep the channel open
tokio::spawn(report_task); tokio::spawn(report_task);
info!("Building test definitions for {} case(s)", metadata_file.cases.len()); info!("Building test definitions for {} case(s)", metadata_file.cases.len());
// Build all test definitions upfront
let mut test_definitions = Vec::new(); let mut test_definitions = Vec::new();
for (case_idx, case) in metadata_file.cases.iter().enumerate() { for (case_idx, case) in metadata_file.cases.iter().enumerate() {
info!("Building test definition for case {}", case_idx); info!("Building test definition for case {}", case_idx);
@@ -361,7 +351,6 @@ async fn execute_test_file(
} }
} }
// Execute each test case
info!("Executing {} test definition(s)", test_definitions.len()); info!("Executing {} test definition(s)", test_definitions.len());
for (idx, test_definition) in test_definitions.iter().enumerate() { for (idx, test_definition) in test_definitions.iter().enumerate() {
info!("─────────────────────────────────────────────────────────────────"); info!("─────────────────────────────────────────────────────────────────");
@@ -413,7 +402,6 @@ async fn build_test_definition<'a>(
context: &revive_dt_config::Context, context: &revive_dt_config::Context,
reporter: &revive_dt_report::Reporter, reporter: &revive_dt_report::Reporter,
) -> anyhow::Result<Option<TestDefinition<'a>>> { ) -> anyhow::Result<Option<TestDefinition<'a>>> {
// Determine mode - use case mode if specified, otherwise use default
let mode = case let mode = case
.modes .modes
.as_ref() .as_ref()
@@ -424,13 +412,11 @@ async fn build_test_definition<'a>(
.or_else(|| revive_dt_compiler::Mode::all().next().map(Cow::Borrowed)) .or_else(|| revive_dt_compiler::Mode::all().next().map(Cow::Borrowed))
.unwrap(); .unwrap();
// Create a compiler for this mode
let compiler = platform let compiler = platform
.new_compiler(context.clone(), mode.version.clone().map(Into::into)) .new_compiler(context.clone(), mode.version.clone().map(Into::into))
.await .await
.context("Failed to create compiler")?; .context("Failed to create compiler")?;
// Create test-specific reporter
let test_reporter = let test_reporter =
reporter.test_specific_reporter(Arc::new(revive_dt_report::TestSpecifier { reporter.test_specific_reporter(Arc::new(revive_dt_report::TestSpecifier {
solc_mode: mode.as_ref().clone(), solc_mode: mode.as_ref().clone(),
@@ -438,18 +424,15 @@ async fn build_test_definition<'a>(
case_idx: CaseIdx::new(case_idx), case_idx: CaseIdx::new(case_idx),
})); }));
// Create execution-specific reporter
let execution_reporter = let execution_reporter =
test_reporter.execution_specific_reporter(node.id(), platform.platform_identifier()); test_reporter.execution_specific_reporter(node.id(), platform.platform_identifier());
// Build platform information
let mut platforms = BTreeMap::new(); let mut platforms = BTreeMap::new();
platforms.insert( platforms.insert(
platform.platform_identifier(), platform.platform_identifier(),
TestPlatformInformation { platform, node, compiler, reporter: execution_reporter }, TestPlatformInformation { platform, node, compiler, reporter: execution_reporter },
); );
// Build test definition
let test_definition = TestDefinition { let test_definition = TestDefinition {
metadata: metadata_file, metadata: metadata_file,
metadata_file_path: &metadata_file.metadata_file_path, metadata_file_path: &metadata_file.metadata_file_path,
@@ -460,7 +443,6 @@ async fn build_test_definition<'a>(
reporter: test_reporter, reporter: test_reporter,
}; };
// Check compatibility
if let Err((reason, _)) = test_definition.check_compatibility() { if let Err((reason, _)) = test_definition.check_compatibility() {
println!(" Skipping case {}: {}", case_idx, reason); println!(" Skipping case {}: {}", case_idx, reason);
return Ok(None); return Ok(None);
+14 -15
View File
@@ -138,7 +138,10 @@ impl GethNode {
.map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to decode private key hex: {}", e))?;
if key_bytes.len() != 32 { if key_bytes.len() != 32 {
anyhow::bail!("Private key must be 32 bytes (64 hex characters), got {}", key_bytes.len()); anyhow::bail!(
"Private key must be 32 bytes (64 hex characters), got {}",
key_bytes.len()
);
} }
let mut bytes = [0u8; 32]; let mut bytes = [0u8; 32];
@@ -177,13 +180,9 @@ impl GethNode {
providers::{Provider, ProviderBuilder}, providers::{Provider, ProviderBuilder},
}; };
// Create a simple HTTP provider without wallet for balance check let provider = ProviderBuilder::new().connect_http(self.connection_string.parse()?);
let simple_provider = let balance = provider.get_balance(address).await?;
ProviderBuilder::new().connect_http(self.connection_string.parse()?); let min_balance = parse_ether("1000")?;
// Check current balance
let balance = simple_provider.get_balance(address).await?;
let min_balance = parse_ether("1000")?; // 1000 ETH
if balance >= min_balance { if balance >= min_balance {
tracing::info!( tracing::info!(
@@ -200,20 +199,21 @@ impl GethNode {
format_ether(balance) format_ether(balance)
); );
// Need to fund the account - get the node's managed account // Get the node's managed account
let accounts = simple_provider.get_accounts().await?; let accounts = provider.get_accounts().await?;
if accounts.is_empty() { if accounts.is_empty() {
anyhow::bail!("No managed accounts available on the node to fund wallet"); anyhow::bail!("No managed accounts available on the node to fund wallet");
} }
let from_account = accounts[0]; let from_account = accounts[0];
// Send funding transaction from managed account (unsigned, node will sign)
let funding_amount = min_balance - balance; let funding_amount = min_balance - balance;
let tx = let tx = TransactionRequest::default()
TransactionRequest::default().from(from_account).to(address).value(funding_amount); .from(from_account)
.to(address)
.value(funding_amount);
simple_provider provider
.send_transaction(tx) .send_transaction(tx)
.await? .await?
.get_receipt() .get_receipt()
@@ -221,7 +221,6 @@ impl GethNode {
.context("Failed to get receipt for funding transaction")?; .context("Failed to get receipt for funding transaction")?;
tracing::info!("Successfully funded wallet {}", address); tracing::info!("Successfully funded wallet {}", address);
Ok(()) Ok(())
} }