Skip to main content

test/
types.rs

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