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