use std::{
any::{Any, TypeId},
borrow::Cow,
collections::HashMap,
ops::Deref,
time::Duration,
};
use crate::{Whatever, capture::OutputCapture, test::TestResult};
#[derive(Debug)]
pub struct TestOutcome {
pub status: TestStatus,
pub duration: Duration,
pub output: OutputCapture,
pub attachments: TestOutcomeAttachments,
}
impl Deref for TestOutcome {
type Target = TestStatus;
fn deref(&self) -> &Self::Target {
&self.status
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TestStatus {
Passed,
TimedOut,
Ignored {
reason: Option<Cow<'static, str>>,
},
Failed(TestFailure),
Other(Whatever),
}
impl TestStatus {
pub fn is_good(&self) -> bool {
matches!(
self,
TestStatus::Passed | TestStatus::Ignored { .. } | TestStatus::Other(_)
)
}
pub fn is_bad(&self) -> bool {
matches!(self, TestStatus::Failed(_) | TestStatus::TimedOut)
}
}
impl TestStatus {
pub fn passed(&self) -> bool {
matches!(self, TestStatus::Passed)
}
pub fn timed_out(&self) -> bool {
matches!(self, TestStatus::TimedOut)
}
pub fn ignored(&self) -> bool {
matches!(self, TestStatus::Ignored { .. })
}
pub fn failed(&self) -> bool {
matches!(self, TestStatus::Failed(_))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TestFailure {
Error(Whatever),
Panicked(String),
DidNotPanic {
expected: Option<String>,
},
PanicMismatch {
got: String,
expected: Option<String>,
},
}
impl From<TestResult> for TestStatus {
fn from(value: TestResult) -> Self {
match value.0 {
Ok(None) => TestStatus::Passed,
Ok(Some(details)) => TestStatus::Other(details),
Err(err) => TestStatus::Failed(TestFailure::Error(Whatever::from(err))),
}
}
}
#[derive(Default, Debug)]
pub struct TestOutcomeAttachments(HashMap<TypeId, Box<dyn Any + Send + Sync + 'static>>);
impl TestOutcomeAttachments {
pub fn new() -> Self {
Self::default()
}
pub fn insert<T: Send + Sync + 'static>(&mut self, v: T) {
self.0.insert(TypeId::of::<T>(), Box::new(v));
}
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.0.get(&TypeId::of::<T>())?.downcast_ref()
}
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
self.0.get_mut(&TypeId::of::<T>())?.downcast_mut()
}
pub fn take<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.0
.remove(&TypeId::of::<T>())?
.downcast()
.ok()
.map(|b| *b)
}
}