nu_test_support/harness/
test.rs1use 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}