test/
types.rs

1//! Common types used by `libtest`.
2
3use std::borrow::Cow;
4use std::fmt;
5use std::sync::mpsc::Sender;
6
7pub use NamePadding::*;
8pub use TestFn::*;
9pub use TestName::*;
10
11use super::bench::Bencher;
12use super::event::CompletedTest;
13use super::{__rust_begin_short_backtrace, options};
14
15/// Type of the test according to the [Rust book](https://doc.rust-lang.org/cargo/guide/tests.html)
16/// conventions.
17#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
18pub enum TestType {
19    /// Unit-tests are expected to be in the `src` folder of the crate.
20    UnitTest,
21    /// Integration-style tests are expected to be in the `tests` folder of the crate.
22    IntegrationTest,
23    /// Doctests are created by the `librustdoc` manually, so it's a different type of test.
24    DocTest,
25    /// Tests for the sources that don't follow the project layout convention
26    /// (e.g. tests in raw `main.rs` compiled by calling `rustc --test` directly).
27    Unknown,
28}
29
30#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
31pub enum NamePadding {
32    PadNone,
33    PadOnRight,
34}
35
36// The name of a test. By convention this follows the rules for rust
37// paths; i.e., it should be a series of identifiers separated by double
38// colons. This way if some test runner wants to arrange the tests
39// hierarchically it may.
40#[derive(Clone, PartialEq, Eq, Hash, Debug)]
41pub enum TestName {
42    StaticTestName(&'static str),
43    DynTestName(String),
44    AlignedTestName(Cow<'static, str>, NamePadding),
45}
46
47impl TestName {
48    pub fn as_slice(&self) -> &str {
49        match *self {
50            StaticTestName(s) => s,
51            DynTestName(ref s) => s,
52            AlignedTestName(ref s, _) => s,
53        }
54    }
55
56    pub fn padding(&self) -> NamePadding {
57        match self {
58            &AlignedTestName(_, p) => p,
59            _ => PadNone,
60        }
61    }
62
63    pub fn with_padding(&self, padding: NamePadding) -> TestName {
64        let name = match *self {
65            TestName::StaticTestName(name) => Cow::Borrowed(name),
66            TestName::DynTestName(ref name) => Cow::Owned(name.clone()),
67            TestName::AlignedTestName(ref name, _) => name.clone(),
68        };
69
70        TestName::AlignedTestName(name, padding)
71    }
72}
73impl fmt::Display for TestName {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        fmt::Display::fmt(self.as_slice(), f)
76    }
77}
78
79// A function that runs a test. If the function returns successfully,
80// the test succeeds; if the function panics or returns Result::Err
81// then the test fails. We may need to come up with a more clever
82// definition of test in order to support isolation of tests into
83// threads.
84pub enum TestFn {
85    StaticTestFn(fn() -> Result<(), String>),
86    StaticBenchFn(fn(&mut Bencher) -> Result<(), String>),
87    StaticBenchAsTestFn(fn(&mut Bencher) -> Result<(), String>),
88    DynTestFn(Box<dyn FnOnce() -> Result<(), String> + Send>),
89    DynBenchFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
90    DynBenchAsTestFn(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
91}
92
93impl TestFn {
94    pub fn padding(&self) -> NamePadding {
95        match *self {
96            StaticTestFn(..) => PadNone,
97            StaticBenchFn(..) => PadOnRight,
98            StaticBenchAsTestFn(..) => PadNone,
99            DynTestFn(..) => PadNone,
100            DynBenchFn(..) => PadOnRight,
101            DynBenchAsTestFn(..) => PadNone,
102        }
103    }
104
105    pub(crate) fn into_runnable(self) -> Runnable {
106        match self {
107            StaticTestFn(f) => Runnable::Test(RunnableTest::Static(f)),
108            StaticBenchFn(f) => Runnable::Bench(RunnableBench::Static(f)),
109            StaticBenchAsTestFn(f) => Runnable::Test(RunnableTest::StaticBenchAsTest(f)),
110            DynTestFn(f) => Runnable::Test(RunnableTest::Dynamic(f)),
111            DynBenchFn(f) => Runnable::Bench(RunnableBench::Dynamic(f)),
112            DynBenchAsTestFn(f) => Runnable::Test(RunnableTest::DynamicBenchAsTest(f)),
113        }
114    }
115}
116
117impl fmt::Debug for TestFn {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.write_str(match *self {
120            StaticTestFn(..) => "StaticTestFn(..)",
121            StaticBenchFn(..) => "StaticBenchFn(..)",
122            StaticBenchAsTestFn(..) => "StaticBenchAsTestFn(..)",
123            DynTestFn(..) => "DynTestFn(..)",
124            DynBenchFn(..) => "DynBenchFn(..)",
125            DynBenchAsTestFn(..) => "DynBenchAsTestFn(..)",
126        })
127    }
128}
129
130pub(crate) enum Runnable {
131    Test(RunnableTest),
132    Bench(RunnableBench),
133}
134
135pub(crate) enum RunnableTest {
136    Static(fn() -> Result<(), String>),
137    Dynamic(Box<dyn FnOnce() -> Result<(), String> + Send>),
138    StaticBenchAsTest(fn(&mut Bencher) -> Result<(), String>),
139    DynamicBenchAsTest(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
140}
141
142impl RunnableTest {
143    pub(crate) fn run(self) -> Result<(), String> {
144        match self {
145            RunnableTest::Static(f) => __rust_begin_short_backtrace(f),
146            RunnableTest::Dynamic(f) => __rust_begin_short_backtrace(f),
147            RunnableTest::StaticBenchAsTest(f) => {
148                crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b)))
149            }
150            RunnableTest::DynamicBenchAsTest(f) => {
151                crate::bench::run_once(|b| __rust_begin_short_backtrace(|| f(b)))
152            }
153        }
154    }
155
156    pub(crate) fn is_dynamic(&self) -> bool {
157        match self {
158            RunnableTest::Static(_) => false,
159            RunnableTest::StaticBenchAsTest(_) => false,
160            RunnableTest::Dynamic(_) => true,
161            RunnableTest::DynamicBenchAsTest(_) => true,
162        }
163    }
164}
165
166pub(crate) enum RunnableBench {
167    Static(fn(&mut Bencher) -> Result<(), String>),
168    Dynamic(Box<dyn Fn(&mut Bencher) -> Result<(), String> + Send>),
169}
170
171impl RunnableBench {
172    pub(crate) fn run(
173        self,
174        id: TestId,
175        desc: &TestDesc,
176        monitor_ch: &Sender<CompletedTest>,
177        nocapture: bool,
178    ) {
179        match self {
180            RunnableBench::Static(f) => {
181                crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f)
182            }
183            RunnableBench::Dynamic(f) => {
184                crate::bench::benchmark(id, desc.clone(), monitor_ch.clone(), nocapture, f)
185            }
186        }
187    }
188}
189
190// A unique integer associated with each test.
191#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
192pub struct TestId(pub usize);
193
194// The definition of a single test. A test runner will run a list of
195// these.
196#[derive(Clone, Debug)]
197pub struct TestDesc {
198    pub name: TestName,
199    pub ignore: bool,
200    pub ignore_message: Option<&'static str>,
201    pub source_file: &'static str,
202    pub start_line: usize,
203    pub start_col: usize,
204    pub end_line: usize,
205    pub end_col: usize,
206    pub should_panic: options::ShouldPanic,
207    pub compile_fail: bool,
208    pub no_run: bool,
209    pub test_type: TestType,
210}
211
212impl TestDesc {
213    pub fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
214        let mut name = String::from(self.name.as_slice());
215        let fill = column_count.saturating_sub(name.len());
216        let pad = " ".repeat(fill);
217        match align {
218            PadNone => name,
219            PadOnRight => {
220                name.push_str(&pad);
221                name
222            }
223        }
224    }
225
226    /// Returns None for ignored test or tests that are just run, otherwise returns a description of the type of test.
227    /// Descriptions include "should panic", "compile fail" and "compile".
228    pub fn test_mode(&self) -> Option<&'static str> {
229        if self.ignore {
230            return None;
231        }
232        match self.should_panic {
233            options::ShouldPanic::Yes | options::ShouldPanic::YesWithMessage(_) => {
234                return Some("should panic");
235            }
236            options::ShouldPanic::No => {}
237        }
238        if self.compile_fail {
239            return Some("compile fail");
240        }
241        if self.no_run {
242            return Some("compile");
243        }
244        None
245    }
246}
247
248#[derive(Debug)]
249pub struct TestDescAndFn {
250    pub desc: TestDesc,
251    pub testfn: TestFn,
252}
253
254impl TestDescAndFn {
255    pub const fn new_doctest(
256        test_name: &'static str,
257        ignore: bool,
258        source_file: &'static str,
259        start_line: usize,
260        no_run: bool,
261        should_panic: bool,
262        testfn: TestFn,
263    ) -> Self {
264        Self {
265            desc: TestDesc {
266                name: StaticTestName(test_name),
267                ignore,
268                ignore_message: None,
269                source_file,
270                start_line,
271                start_col: 0,
272                end_line: 0,
273                end_col: 0,
274                compile_fail: false,
275                no_run,
276                should_panic: if should_panic {
277                    options::ShouldPanic::Yes
278                } else {
279                    options::ShouldPanic::No
280                },
281                test_type: TestType::DocTest,
282            },
283            testfn,
284        }
285    }
286}