#![doc = include_str!("../README.md")]
use serde::{Deserialize, Serialize};
use tarantool::test::test_cases;
use tester::{TestDescAndFn, TestFn, TestOpts};
use anyhow::bail;
pub use tarantool::proc as test_entrypoint;
pub use tarantool::test;
pub const DEFAULT_TEST_ENTRYPOINT_NAME: &str = "default_test_entrypoint";
pub trait TestSuite {
fn before_all() {}
fn after_all() {}
fn before_each() {}
fn after_each() {}
}
impl TestSuite for () {}
#[derive(Clone, Deserialize, Serialize, Debug, Default)]
pub struct TestsConfig {
#[serde(default)]
pub filter: Option<String>,
}
#[macro_export]
macro_rules! bind_test_suite {
() => {
bind_test_suite!(@default_entrypoint, ());
};
($suite:ty) => {
bind_test_suite!(@default_entrypoint, $suite);
};
($entrypoint:ident) => {
bind_test_suite($entrypoint, ());
};
(@default_entrypoint, $suite:ty) => {
bind_test_suite!(default_test_entrypoint, $suite);
};
($entrypoint:ident, $suite:ty) => {
#[$crate::test_entrypoint]
fn $entrypoint(input: String) -> Result<(), Box<dyn std::error::Error>> {
$crate::handle_entrypoint::<$suite>(input)?;
Ok(())
}
};
}
pub fn collect_tests<S: TestSuite>(cfg: TestsConfig) -> Vec<TestDescAndFn> {
let tests = test_cases();
tests
.iter()
.filter(|case| {
cfg.filter
.as_ref()
.map(|must_contains| case.name().contains(must_contains))
.unwrap_or(true)
})
.map(|case| {
let test_fn = move || {
S::before_each();
case.run();
S::after_each();
};
let mut tester = case.to_tester();
tester.testfn = TestFn::DynTestFn(Box::new(test_fn));
tester
})
.collect()
}
pub fn execute_tests<S: TestSuite>(mut cfg: TestsConfig) -> anyhow::Result<()> {
if cfg.filter == Some("".to_string()) {
cfg.filter = None
}
let opts = &TestOpts {
list: false,
filter: None,
filter_exact: false,
force_run_in_process: false,
exclude_should_panic: false,
run_ignored: tester::RunIgnored::No,
run_tests: true,
bench_benchmarks: false,
logfile: None,
nocapture: false,
color: tester::ColorConfig::AutoColor,
format: tester::OutputFormat::Pretty,
test_threads: Some(1),
skip: vec![],
time_options: None,
options: tester::Options::new(),
};
S::before_all();
let result = tester::run_tests_console(opts, collect_tests::<S>(cfg));
S::after_all();
match result {
Ok(true) => Ok(()),
failure => {
bail!("failed to successfully pass the tests due to failure: {failure:?}");
}
}
}
pub fn handle_entrypoint<S: TestSuite>(input: String) -> anyhow::Result<()> {
let input: TestsConfig = serde_json::from_str(&input)?;
execute_tests::<S>(input)?;
Ok(())
}