use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use revive_dt_common::{macros::define_wrapper_type, types::Mode}; use crate::{mode::ParsedMode, steps::*}; #[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq, JsonSchema)] pub struct Case { /// An optional name of the test case. #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, /// An optional comment on the case which has no impact on the execution in any way. #[serde(skip_serializing_if = "Option::is_none")] pub comment: Option, /// This represents a mode that has been parsed from test metadata. /// /// Mode strings can take the following form (in pseudo-regex): /// /// ```text /// [YEILV][+-]? (M[0123sz])? ? /// ``` /// /// If this is provided then it takes higher priority than the modes specified in the metadata /// file. #[serde(skip_serializing_if = "Option::is_none")] pub modes: Option>, /// The set of steps to run as part of this test case. #[serde(rename = "inputs")] pub steps: Vec, /// An optional name of the group of tests that this test belongs to. #[serde(skip_serializing_if = "Option::is_none")] pub group: Option, /// An optional set of expectations and assertions to make about the transaction after it ran. /// /// If this is not specified then the only assertion that will be ran is that the transaction /// was successful. /// /// This expectation that's on the case itself will be attached to the final step of the case. #[serde(skip_serializing_if = "Option::is_none")] pub expected: Option, /// An optional boolean which defines if the case as a whole should be ignored. If null then the /// case will not be ignored. #[serde(skip_serializing_if = "Option::is_none")] pub ignore: Option, } impl Case { pub fn steps_iterator(&self) -> impl Iterator { let steps_len = self.steps.len(); self.steps .clone() .into_iter() .enumerate() .map(move |(idx, mut step)| { let Step::FunctionCall(ref mut input) = step else { return step; }; if idx + 1 == steps_len { if input.expected.is_none() { input.expected = self.expected.clone(); } // TODO: What does it mean for us to have an `expected` field on the case itself // but the final input also has an expected field that doesn't match the one on // the case? What are we supposed to do with that final expected field on the // case? step } else { step } }) } pub fn steps_iterator_for_benchmarks( &self, default_repeat_count: usize, ) -> Box + '_> { let contains_repeat = self .steps_iterator() .any(|step| matches!(&step, Step::Repeat(..))); if contains_repeat { Box::new(self.steps_iterator()) as Box<_> } else { Box::new(std::iter::once(Step::Repeat(Box::new(RepeatStep { comment: None, repeat: default_repeat_count, steps: self.steps_iterator().collect(), })))) as Box<_> } } pub fn solc_modes(&self) -> Vec { match &self.modes { Some(modes) => ParsedMode::many_to_modes(modes.iter()).collect(), None => Mode::all().cloned().collect(), } } } define_wrapper_type!( /// A wrapper type for the index of test cases found in metadata file. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[serde(transparent)] pub struct CaseIdx(usize) impl Display, FromStr; );