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