#![allow(unexpected_cfgs)]
#[cfg(all(not(feature = "std"), feature = "testing-csp"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::{sync::Arc, vec::Vec};
#[cfg(feature = "std")]
use std::{sync::Arc, vec::Vec};
use crate::error::TightBeamError;
use crate::testing::macros::{BuiltAssertSpec, TraceCollector};
use crate::testing::specs::SpecViolation;
use crate::trace::ConsumedTrace;
#[cfg(feature = "testing-fdr")]
use crate::testing::fdr::{FdrConfig, FdrVerdict};
#[cfg(feature = "testing-csp")]
use crate::testing::specs::csp::ProcessSpec;
#[cfg(feature = "testing-timing")]
use crate::testing::timing::TimingConstraints;
#[derive(Clone)]
pub struct ScenarioConf<E = ()> {
specs: Arc<Vec<&'static BuiltAssertSpec>>,
trace: Arc<TraceCollector>,
hooks: Option<Arc<TestHooks>>,
env_config: Option<Arc<E>>,
#[cfg(feature = "testing-csp")]
csp: Option<Arc<dyn ProcessSpec + Send + Sync>>,
#[cfg(feature = "testing-fdr")]
fdr: Option<Arc<FdrConfig>>,
}
impl<E> ScenarioConf<E> {
pub fn builder() -> ScenarioConfBuilder<E> {
ScenarioConfBuilder::default()
}
pub fn specs(&self) -> &[&'static BuiltAssertSpec] {
&self.specs
}
#[cfg(feature = "testing-csp")]
pub fn csp(&self) -> Option<&Arc<dyn ProcessSpec + Send + Sync>> {
self.csp.as_ref()
}
#[cfg(feature = "testing-fdr")]
pub fn fdr(&self) -> Option<&Arc<FdrConfig>> {
self.fdr.as_ref()
}
pub fn trace(&self) -> Arc<TraceCollector> {
Arc::clone(&self.trace)
}
pub fn hooks(&self) -> Option<&Arc<TestHooks>> {
self.hooks.as_ref()
}
pub fn env_config(&self) -> &Arc<E> {
self.env_config
.as_ref()
.expect("env_config not set - call with_env_config() before build()")
}
}
impl Default for ScenarioConf<()> {
fn default() -> Self {
Self {
specs: Arc::new(Vec::new()),
trace: Arc::new(TraceCollector::default()),
hooks: None,
env_config: Some(Arc::new(())),
#[cfg(feature = "testing-csp")]
csp: None,
#[cfg(feature = "testing-fdr")]
fdr: None,
}
}
}
pub struct ScenarioConfBuilder<E = ()> {
specs: Vec<&'static BuiltAssertSpec>,
trace: TraceCollector,
hooks: Option<TestHooks>,
env_config: Option<E>,
#[cfg(feature = "testing-csp")]
csp: Option<Box<dyn ProcessSpec + Send + Sync>>,
#[cfg(feature = "testing-fdr")]
fdr: Option<FdrConfig>,
}
impl<E> Default for ScenarioConfBuilder<E> {
fn default() -> Self {
Self {
specs: Vec::new(),
trace: TraceCollector::default(),
hooks: None,
env_config: None,
#[cfg(feature = "testing-csp")]
csp: None,
#[cfg(feature = "testing-fdr")]
fdr: None,
}
}
}
impl<E> ScenarioConfBuilder<E> {
pub fn with_spec(mut self, spec: &'static BuiltAssertSpec) -> Self {
self.specs.push(spec);
self
}
pub fn with_specs(mut self, specs: impl IntoIterator<Item = &'static BuiltAssertSpec>) -> Self {
self.specs = specs.into_iter().collect();
self
}
#[cfg(feature = "testing-csp")]
pub fn with_csp<P: ProcessSpec + Send + Sync + 'static>(mut self, csp: P) -> Self {
self.csp = Some(Box::new(csp));
self
}
#[cfg(feature = "testing-fdr")]
pub fn with_fdr(mut self, fdr: FdrConfig) -> Self {
self.fdr = Some(fdr);
self
}
pub fn with_trace(mut self, trace: TraceCollector) -> Self {
self.trace = trace;
self
}
pub fn with_hooks(mut self, hooks: TestHooks) -> Self {
self.hooks = Some(hooks);
self
}
pub fn with_env_config(mut self, env_config: E) -> Self {
self.env_config = Some(env_config);
self
}
}
impl<E> ScenarioConfBuilder<E> {
pub fn build(self) -> ScenarioConf<E> {
ScenarioConf {
specs: Arc::new(self.specs),
trace: Arc::new(self.trace),
hooks: self.hooks.map(Arc::new),
env_config: self.env_config.map(Arc::new),
#[cfg(feature = "testing-csp")]
csp: self.csp.map(|csp| Arc::from(csp) as Arc<dyn ProcessSpec + Send + Sync>),
#[cfg(feature = "testing-fdr")]
fdr: self.fdr.map(Arc::new),
}
}
}
pub struct HookContext {
pub assert_spec: Option<&'static BuiltAssertSpec>,
pub trace: ConsumedTrace,
#[cfg(feature = "testing-fdr")]
pub fdr_verdict: Option<FdrVerdict>,
#[cfg(feature = "testing-fdr")]
pub fdr_config: Option<Arc<FdrConfig>>,
#[cfg(feature = "testing-csp")]
pub process: Option<Arc<dyn ProcessSpec + Send + Sync>>,
#[cfg(feature = "testing-timing")]
pub timing_constraints: Option<Arc<TimingConstraints>>,
}
impl HookContext {
pub fn new(trace: ConsumedTrace) -> Self {
Self {
assert_spec: None,
trace,
#[cfg(feature = "testing-fdr")]
fdr_verdict: None,
#[cfg(feature = "testing-fdr")]
fdr_config: None,
#[cfg(feature = "testing-csp")]
process: None,
#[cfg(feature = "testing-timing")]
timing_constraints: None,
}
}
}
pub struct TestHooks {
#[allow(clippy::type_complexity)]
pub on_pass: Option<Arc<dyn Fn(&HookContext) -> Result<(), TightBeamError> + Send + Sync>>,
#[allow(clippy::type_complexity)]
pub on_fail: Option<Arc<dyn Fn(&HookContext, &SpecViolation) -> Result<(), TightBeamError> + Send + Sync>>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_env_config_sets_value() {
#[derive(Debug, Clone, PartialEq)]
struct TestEnvConfig {
value: u32,
}
impl Default for TestEnvConfig {
fn default() -> Self {
Self { value: 42 }
}
}
let custom_config = TestEnvConfig { value: 123 };
let env_config = custom_config.clone();
let config = ScenarioConf::builder().with_env_config(env_config).build();
assert_eq!(config.env_config().value, 123);
}
}