use crate::prelude::*;
use crate::testing::runner::*;
use crate::testing::utils::*;
use send_wrapper::SendWrapper;
use test::TestDesc;
use test::TestDescAndFn;
pub fn tests_bundle_borrowed(tests: &[&TestDescAndFn]) -> impl Bundle {
let tests = tests
.iter()
.map(|test| test_ext::clone_static(test))
.collect();
tests_bundle(tests)
}
pub fn tests_bundle(tests: Vec<TestDescAndFn>) -> impl Bundle {
let test_bundles: Vec<_> = tests.into_iter().map(test_bundle).collect();
(
Children::spawn(SpawnIter(test_bundles.into_iter())),
)
}
fn test_bundle(test: TestDescAndFn) -> impl Bundle {
(test_desc_bundle(test.desc), test_fn_bundle(test.testfn))
}
fn test_fn_bundle(func: test::TestFn) -> impl Bundle {
match func {
test::TestFn::StaticTestFn(func) => {
OnSpawn::insert(TestFunc::new(func))
}
test::TestFn::DynTestFn(fn_once) => {
OnSpawn::insert(NonSendTestFunc::new(fn_once))
}
test::TestFn::StaticBenchFn(_) => todo!(),
test::TestFn::DynBenchFn(_) => todo!(),
test::TestFn::StaticBenchAsTestFn(_) => todo!(),
test::TestFn::DynBenchAsTestFn(_) => todo!(),
}
}
fn test_desc_bundle(desc: test::TestDesc) -> impl Bundle {
(
Name::new(desc.name.to_string()),
FileSpan::new(
desc.source_file,
LineCol::new(desc.start_line as u32, desc.start_col as u32),
LineCol::new(desc.end_line as u32, desc.end_col as u32),
),
Test::new(desc.clone()),
OnSpawn::new(move |entity| try_skip(entity, &desc)),
)
}
fn try_skip(entity: &mut EntityWorldMut, desc: &TestDesc) {
if desc.no_run {
entity.insert(TestOutcome::Skip(TestSkip::NoRun));
}
if desc.compile_fail {
entity.insert(TestOutcome::Skip(TestSkip::CompileFail));
}
if desc.ignore {
entity.insert(TestOutcome::Skip(TestSkip::Ignore(desc.ignore_message)));
}
}
#[derive(Debug, Clone, Component)]
pub struct TestCaseParams {
pub timeout: Option<Duration>,
}
impl Default for TestCaseParams {
fn default() -> Self { Self { timeout: None } }
}
impl TestCaseParams {
pub fn new() -> Self { Self::default() }
pub fn with_timeout_ms(mut self, timeout_ms: u64) -> Self {
self.timeout = Some(Duration::from_millis(timeout_ms));
self
}
}
#[derive(Debug, Component)]
pub struct Test {
desc: TestDesc,
timer: Stopwatch,
}
impl std::ops::Deref for Test {
type Target = TestDesc;
fn deref(&self) -> &Self::Target { &self.desc }
}
impl Test {
pub fn new(desc: TestDesc) -> Self {
Self {
desc,
timer: default(),
}
}
pub fn desc(&self) -> &TestDesc { &self.desc }
pub fn do_not_run(&self) -> bool {
self.desc.ignore || self.desc.no_run || self.desc.compile_fail
}
pub fn tick(&mut self, delta: Duration) { self.timer.tick(delta); }
pub fn elapsed(&self) -> Duration { self.timer.elapsed() }
}
impl TestDescExt for Test {
fn desc(&self) -> &TestDesc { &self.desc }
fn desc_mut(&mut self) -> &mut TestDesc { &mut self.desc }
}
#[derive(Debug, Copy, Clone, Component)]
pub struct TestFunc(fn() -> Result<(), String>);
impl TestFunc {
pub fn new(func: fn() -> Result<(), String>) -> Self { Self(func) }
pub fn run(&self) -> Result<(), String> { self.0() }
}
#[derive(Component)]
pub struct NonSendTestFunc(
SendWrapper<Box<dyn 'static + FnOnce() -> Result<(), String>>>,
);
impl NonSendTestFunc {
pub fn new(func: impl 'static + FnOnce() -> Result<(), String>) -> Self {
Self(SendWrapper::new(Box::new(func)))
}
pub fn run(self) -> Result<(), String> { self.0.take()() }
}
#[cfg(test)]
mod tests {
use super::*;
fn setup() -> Vec<test::TestDescAndFn> {
vec![
test_ext::new("test1", "crates/crate1/file1.rs", || Ok(())),
test_ext::new("test2", "crates/crate1/file1.rs", || Ok(())),
test_ext::new("test1", "crates/crate2/file1.rs", || Ok(())),
test_ext::new("test1", "crates/crate2/dir1/file1.rs", || Ok(())),
test_ext::new("test2", "crates/crate2/dir1/file1.rs", || Ok(())),
]
}
#[test]
fn test_tree() {
let mut world = World::new();
let root = world.spawn(tests_bundle(setup())).id();
world
.component_names_related::<Children>(root)
.iter_to_string_indented()
.xpect_snapshot();
}
}