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