diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index ec521fd..d6939c3 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -30,7 +30,7 @@ use revive_dt_format::case::Case; use revive_dt_format::metadata::{ContractIdent, ContractInstance, ContractPathAndIdent}; use revive_dt_format::steps::{ BalanceAssertionStep, Calldata, EtherValue, Expected, ExpectedOutput, FunctionCallStep, Method, - StepIdx, StorageEmptyAssertionStep, + StepIdx, StepPath, StorageEmptyAssertionStep, }; use revive_dt_format::{metadata::Metadata, steps::Step}; use revive_dt_node_interaction::EthereumNode; @@ -83,6 +83,7 @@ impl CaseState { &mut self, metadata: &Metadata, step: &Step, + step_path: &StepPath, node: &dyn EthereumNode, ) -> anyhow::Result { match step { @@ -110,6 +111,7 @@ impl CaseState { metadata, repetition_step.repeat, &repetition_step.steps, + step_path, node, ) .await @@ -200,13 +202,15 @@ impl CaseState { metadata: &Metadata, repetitions: usize, steps: &[Step], + step_path: &StepPath, node: &dyn EthereumNode, ) -> anyhow::Result<()> { let tasks = (0..repetitions).map(|_| { let mut state = self.clone(); async move { - for step in steps { - state.handle_step(metadata, step, node).await?; + for (step_idx, step) in steps.iter().enumerate() { + let step_path = step_path.append(step_idx); + state.handle_step(metadata, step, &step_path, node).await?; } Ok::<(), anyhow::Error>(()) } @@ -842,32 +846,31 @@ impl<'a> CaseDriver<'a> { .enumerate() .map(|(idx, v)| (StepIdx::new(idx), v)) { - // Run this step concurrently across all platforms; short-circuit on first failure let metadata = self.metadata; - let step_futs = + let step_futures = self.platform_state .iter_mut() .map(|(node, platform_id, case_state)| { let platform_id = *platform_id; let node_ref = *node; - let step_clone = step.clone(); + let step = step.clone(); let span = info_span!( "Handling Step", %step_idx, platform = %platform_id, ); async move { + let step_path = StepPath::from_iterator([step_idx]); case_state - .handle_step(metadata, &step_clone, node_ref) + .handle_step(metadata, &step, &step_path, node_ref) .await .map_err(|e| (platform_id, e)) } .instrument(span) }); - match try_join_all(step_futs).await { + match try_join_all(step_futures).await { Ok(_outputs) => { - // All platforms succeeded for this step steps_executed += 1; } Err((platform_id, error)) => { diff --git a/crates/format/src/steps.rs b/crates/format/src/steps.rs index 7e859f5..36dc7d6 100644 --- a/crates/format/src/steps.rs +++ b/crates/format/src/steps.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt::Display}; +use std::{collections::HashMap, fmt::Display, str::FromStr}; use alloy::{ eips::BlockNumberOrTag, @@ -46,9 +46,71 @@ pub enum Step { define_wrapper_type!( #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] - pub struct StepIdx(usize) impl Display; + pub struct StepIdx(usize) impl Display, FromStr; ); +define_wrapper_type!( + #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] + #[serde(try_from = "String", into = "String")] + pub struct StepPath(Vec); +); + +impl StepPath { + pub fn from_iterator(path: impl IntoIterator>) -> Self { + Self(path.into_iter().map(|value| value.into()).collect()) + } + + pub fn increment(&self) -> Self { + let mut this = self.clone(); + if let Some(last) = this.last_mut() { + last.0 += 1 + } + this + } + + pub fn append(&self, step_idx: impl Into) -> Self { + let mut this = self.clone(); + this.0.push(step_idx.into()); + this + } +} + +impl Display for StepPath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0 + .iter() + .map(|idx| idx.to_string()) + .collect::>() + .join(".") + .fmt(f) + } +} + +impl FromStr for StepPath { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + s.split(".") + .map(StepIdx::from_str) + .collect::>>() + .map(Self) + } +} + +impl From for String { + fn from(value: StepPath) -> Self { + value.to_string() + } +} + +impl TryFrom for StepPath { + type Error = anyhow::Error; + + fn try_from(value: String) -> Result { + value.parse() + } +} + /// This is an input step which is a transaction description that the framework translates into a /// transaction and executes on the nodes. #[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq, JsonSchema)] diff --git a/crates/report/src/common.rs b/crates/report/src/common.rs index 205d761..2c28bf1 100644 --- a/crates/report/src/common.rs +++ b/crates/report/src/common.rs @@ -4,7 +4,7 @@ use std::{path::PathBuf, sync::Arc}; use revive_dt_common::{define_wrapper_type, types::PlatformIdentifier}; use revive_dt_compiler::Mode; -use revive_dt_format::{case::CaseIdx, steps::StepIdx}; +use revive_dt_format::{case::CaseIdx, steps::StepPath}; use serde::{Deserialize, Serialize}; define_wrapper_type!( @@ -33,5 +33,5 @@ pub struct ExecutionSpecifier { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct StepExecutionSpecifier { pub execution_specifier: Arc, - pub step_idx: StepIdx, + pub step_idx: StepPath, } diff --git a/schema.json b/schema.json index 16a492e..bffedee 100644 --- a/schema.json +++ b/schema.json @@ -580,4 +580,4 @@ ] } } -} \ No newline at end of file +}