libtest2_harness/
case.rs

1pub(crate) use crate::*;
2
3pub trait Case: Send + Sync + 'static {
4    /// The name of a test
5    ///
6    /// By convention this follows the rules for rust paths; i.e., it should be a series of
7    /// identifiers separated by double colons. This way if some test runner wants to arrange the
8    /// tests hierarchically it may.
9    fn name(&self) -> &str;
10    fn kind(&self) -> TestKind;
11    fn source(&self) -> Option<&Source>;
12    /// This case cannot run in parallel to other cases within this binary
13    fn exclusive(&self, state: &TestContext) -> bool;
14
15    fn run(&self, state: &TestContext) -> Result<(), RunError>;
16}
17
18/// Type of the test according to the [rust book](https://doc.rust-lang.org/cargo/guide/tests.html)
19/// conventions.
20#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
21pub enum TestKind {
22    /// Unit-tests are expected to be in the `src` folder of the crate.
23    UnitTest,
24    /// Integration-style tests are expected to be in the `tests` folder of the crate.
25    IntegrationTest,
26    /// Doctests are created by the `librustdoc` manually, so it's a different type of test.
27    DocTest,
28    /// Tests for the sources that don't follow the project layout convention
29    /// (e.g. tests in raw `main.rs` compiled by calling `rustc --test` directly).
30    Unknown,
31}
32
33impl Default for TestKind {
34    fn default() -> Self {
35        Self::Unknown
36    }
37}
38
39#[derive(Debug)]
40#[non_exhaustive]
41pub enum Source {
42    Rust {
43        source_file: std::path::PathBuf,
44        start_line: usize,
45        start_col: usize,
46        end_line: usize,
47        end_col: usize,
48    },
49    Path(std::path::PathBuf),
50}
51
52pub type RunResult = Result<(), RunError>;
53
54#[derive(Debug)]
55pub struct RunError {
56    status: notify::MessageKind,
57    cause: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
58}
59
60impl RunError {
61    pub fn with_cause(cause: impl std::error::Error + Send + Sync + 'static) -> Self {
62        Self {
63            status: notify::MessageKind::Error,
64            cause: Some(Box::new(cause)),
65        }
66    }
67
68    pub fn fail(cause: impl std::fmt::Display) -> Self {
69        Self::with_cause(Message(cause.to_string()))
70    }
71
72    /// Should not be called with `libtest_lexarg::RunIgnored::Yes`
73    pub fn ignore() -> Self {
74        Self {
75            status: notify::MessageKind::Ignored,
76            cause: None,
77        }
78    }
79
80    /// Should not be called with `libtest_lexarg::RunIgnored::Yes`
81    pub fn ignore_for(reason: String) -> Self {
82        Self {
83            status: notify::MessageKind::Ignored,
84            cause: Some(Box::new(Message(reason))),
85        }
86    }
87
88    pub(crate) fn status(&self) -> notify::MessageKind {
89        self.status
90    }
91
92    pub(crate) fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync)> {
93        self.cause.as_ref().map(|b| b.as_ref())
94    }
95}
96
97impl<E> From<E> for RunError
98where
99    E: std::error::Error + Send + Sync + 'static,
100{
101    fn from(error: E) -> Self {
102        Self::with_cause(error)
103    }
104}
105
106#[derive(Debug)]
107struct Message(String);
108
109impl std::fmt::Display for Message {
110    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        self.0.fmt(formatter)
112    }
113}
114
115impl std::error::Error for Message {}