Skip to main content

nu_test_support/harness/
test.rs

1use std::{any::Any, fmt::Debug, num::NonZeroUsize, sync::atomic::Ordering, thread::Scope};
2
3use kitest::{
4    Whatever,
5    capture::DefaultPanicHookProvider,
6    outcome::TestOutcome,
7    runner::{DefaultRunner, SimpleRunner, scope::NoScopeFactory},
8    test::{TestMeta, TestResult},
9};
10use nu_experimental::ExperimentalOption;
11use nu_utils::downcast;
12
13use crate::{harness::group::RUN_TEST_GROUP_IN_SERIAL, tester::TestError};
14
15pub struct Extra {
16    pub run_in_serial: bool,
17    pub experimental_options: &'static [(&'static ExperimentalOption, bool)],
18    pub environment_variables: &'static [(&'static str, &'static str)],
19}
20
21#[derive(Debug, Default)]
22pub struct TestRunner {
23    parallel: DefaultRunner<DefaultPanicHookProvider, NoScopeFactory>,
24    serial: SimpleRunner<DefaultPanicHookProvider, NoScopeFactory>,
25    exact: bool,
26}
27
28impl TestRunner {
29    pub fn with_thread_count(self, thread_count: NonZeroUsize) -> Self {
30        Self {
31            parallel: self.parallel.with_thread_count(thread_count),
32            ..self
33        }
34    }
35
36    pub fn with_exact(self, exact: bool) -> Self {
37        Self { exact, ..self }
38    }
39}
40
41enum NuTestRunnerIterator<IP, IS> {
42    Parallel(IP),
43    Serial(IS),
44}
45
46impl<'t, IP, IS> Iterator for NuTestRunnerIterator<IP, IS>
47where
48    IP: Iterator<Item = (&'t TestMeta<Extra>, TestOutcome)>,
49    IS: Iterator<Item = (&'t TestMeta<Extra>, TestOutcome)>,
50    Extra: 't,
51{
52    type Item = (&'t TestMeta<Extra>, TestOutcome);
53
54    fn next(&mut self) -> Option<Self::Item> {
55        match self {
56            Self::Parallel(iter) => iter.next(),
57            Self::Serial(iter) => iter.next(),
58        }
59    }
60}
61
62impl<'t> kitest::runner::TestRunner<'t, Extra> for TestRunner {
63    fn run<'s, I, F>(
64        &self,
65        tests: I,
66        scope: &'s Scope<'s, 't>,
67    ) -> impl Iterator<Item = (&'t TestMeta<Extra>, kitest::outcome::TestOutcome)>
68    where
69        I: ExactSizeIterator<Item = (F, &'t TestMeta<Extra>)>,
70        F: (Fn() -> kitest::outcome::TestStatus) + Send + 's,
71        Extra: 't,
72    {
73        match self.exact || RUN_TEST_GROUP_IN_SERIAL.load(Ordering::Relaxed) {
74            false => NuTestRunnerIterator::Parallel(
75                <DefaultRunner<_, _> as kitest::runner::TestRunner<Extra>>::run(
76                    &self.parallel,
77                    tests,
78                    scope,
79                ),
80            ),
81            true => {
82                NuTestRunnerIterator::Serial(<SimpleRunner<_, _> as kitest::runner::TestRunner<
83                    Extra,
84                >>::run(&self.serial, tests, scope))
85            }
86        }
87    }
88
89    fn worker_count(&self, tests_count: usize) -> NonZeroUsize {
90        match RUN_TEST_GROUP_IN_SERIAL.load(Ordering::Relaxed) {
91            true => const { NonZeroUsize::new(1).unwrap() },
92            false => <DefaultRunner<_, _> as kitest::runner::TestRunner<Extra>>::worker_count(
93                &self.parallel,
94                tests_count,
95            ),
96        }
97    }
98}
99
100pub trait IntoTestResult {
101    fn into_test_result(self) -> TestResult;
102}
103
104impl IntoTestResult for () {
105    fn into_test_result(self) -> TestResult {
106        self.into()
107    }
108}
109
110impl<E: Debug + Any> IntoTestResult for Result<(), E> {
111    fn into_test_result(self) -> TestResult {
112        let Err(err) = self else {
113            return TestResult(Ok(None));
114        };
115
116        match downcast::<E, TestError>(err) {
117            Ok(test_error) => TestResult(Err(Whatever::from(test_error))),
118            Err(err) => Err(err).into(),
119        }
120    }
121}