tarantool_test/
lib.rs

1#![doc = include_str!("../README.md")]
2use serde::{Deserialize, Serialize};
3use tarantool::test::test_cases;
4use tester::{TestDescAndFn, TestFn, TestOpts};
5
6use anyhow::bail;
7pub use tarantool::proc as test_entrypoint;
8pub use tarantool::test;
9
10pub const DEFAULT_TEST_ENTRYPOINT_NAME: &str = "default_test_entrypoint";
11
12/// Implementors provide custom test suite logic.
13pub trait TestSuite {
14    fn before_all() {}
15    fn after_all() {}
16
17    fn before_each() {}
18    fn after_each() {}
19}
20
21// () is the simplest test suite - does nothing in `before_all`, `after_all`, etc.
22impl TestSuite for () {}
23
24#[derive(Clone, Deserialize, Serialize, Debug, Default)]
25pub struct TestsConfig {
26    #[serde(default)]
27    pub filter: Option<String>,
28}
29
30/// Generates entrypoint function for the test suite.
31///
32/// Generated entrypoints are functions that could be used from tarantool runtime - in other words, stored procedures.
33#[macro_export]
34macro_rules! bind_test_suite {
35    () => {
36        bind_test_suite!(@default_entrypoint, ());
37    };
38    ($suite:ty) => {
39        bind_test_suite!(@default_entrypoint, $suite);
40    };
41    ($entrypoint:ident) => {
42        bind_test_suite($entrypoint, ());
43    };
44    // helper branch that inserts `default_test_entrypoint` as an entrypoint
45    (@default_entrypoint, $suite:ty) => {
46        bind_test_suite!(default_test_entrypoint, $suite);
47    };
48    ($entrypoint:ident, $suite:ty) => {
49        #[$crate::test_entrypoint]
50        fn $entrypoint(input: String) -> Result<(), Box<dyn std::error::Error>> {
51            $crate::handle_entrypoint::<$suite>(input)?;
52            Ok(())
53        }
54    };
55}
56
57/// Collects tests from the tarantool tester with given suite setup.
58pub fn collect_tests<S: TestSuite>(cfg: TestsConfig) -> Vec<TestDescAndFn> {
59    let tests = test_cases();
60
61    tests
62        .iter()
63        .filter(|case| {
64            cfg.filter
65                .as_ref()
66                .map(|must_contains| case.name().contains(must_contains))
67                .unwrap_or(true)
68        })
69        .map(|case| {
70            let test_fn = move || {
71                S::before_each();
72                case.run();
73                S::after_each();
74            };
75            let mut tester = case.to_tester();
76            tester.testfn = TestFn::DynTestFn(Box::new(test_fn));
77            tester
78        })
79        .collect()
80}
81
82/// Executes tests with given config and user-provided suite.
83pub fn execute_tests<S: TestSuite>(mut cfg: TestsConfig) -> anyhow::Result<()> {
84    if cfg.filter == Some("".to_string()) {
85        cfg.filter = None
86    }
87
88    let opts = &TestOpts {
89        list: false,
90        filter: None,
91        filter_exact: false,
92        force_run_in_process: false,
93        exclude_should_panic: false,
94        run_ignored: tester::RunIgnored::No,
95        run_tests: true,
96        bench_benchmarks: false,
97        logfile: None,
98        nocapture: false,
99        color: tester::ColorConfig::AutoColor,
100        format: tester::OutputFormat::Pretty,
101        test_threads: Some(1),
102        skip: vec![],
103        time_options: None,
104        options: tester::Options::new(),
105    };
106
107    S::before_all();
108    let result = tester::run_tests_console(opts, collect_tests::<S>(cfg));
109    S::after_all();
110
111    match result {
112        Ok(true) => Ok(()),
113        failure => {
114            bail!("failed to successfully pass the tests due to failure: {failure:?}");
115        }
116    }
117}
118
119/// Process entrypoint with given input and specific `TestSuite`(given as generic argument).
120///
121/// Normally you wouldn't like to call this function yourself - it exists for `bind_test_suite` macro expansion.
122pub fn handle_entrypoint<S: TestSuite>(input: String) -> anyhow::Result<()> {
123    let input: TestsConfig = serde_json::from_str(&input)?;
124    execute_tests::<S>(input)?;
125    Ok(())
126}