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: &State) -> bool;
14
15    fn run(&self, state: &State) -> 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::RunStatus,
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::RunStatus::Failed,
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    pub(crate) fn ignore() -> Self {
73        Self {
74            status: notify::RunStatus::Ignored,
75            cause: None,
76        }
77    }
78
79    pub(crate) fn ignore_for(reason: String) -> Self {
80        Self {
81            status: notify::RunStatus::Ignored,
82            cause: Some(Box::new(Message(reason))),
83        }
84    }
85
86    pub(crate) fn status(&self) -> notify::RunStatus {
87        self.status
88    }
89
90    pub(crate) fn cause(&self) -> Option<&(dyn std::error::Error + Send + Sync)> {
91        self.cause.as_ref().map(|b| b.as_ref())
92    }
93}
94
95impl<E> From<E> for RunError
96where
97    E: std::error::Error + Send + Sync + 'static,
98{
99    fn from(error: E) -> Self {
100        Self::with_cause(error)
101    }
102}
103
104#[derive(Debug)]
105struct Message(String);
106
107impl std::fmt::Display for Message {
108    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        self.0.fmt(formatter)
110    }
111}
112
113impl std::error::Error for Message {}