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
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}