litcheck_lit/test/
result.rs

1use std::{
2    fmt,
3    time::{Duration, Instant},
4};
5
6use super::TestStatus;
7
8/// Wrapper for the results of executing an individual test
9#[derive(Default)]
10pub struct TestResult {
11    pub status: TestStatus,
12    exit_status: Option<std::process::ExitStatus>,
13    stdout: Vec<u8>,
14    stderr: Vec<u8>,
15    elapsed: Option<Duration>,
16    start: Option<Instant>,
17}
18unsafe impl Send for TestResult {}
19impl From<std::process::Output> for TestResult {
20    fn from(output: std::process::Output) -> Self {
21        let mut result = Self::new(output.status.into());
22        result.exit_status = Some(output.status);
23        result.stdout = output.stdout;
24        result.stderr = output.stderr;
25        result
26    }
27}
28impl TestResult {
29    pub fn new(status: TestStatus) -> Self {
30        Self {
31            status,
32            ..Default::default()
33        }
34    }
35
36    #[inline(always)]
37    pub fn with_stdout(mut self, stdout: Vec<u8>) -> Self {
38        self.stdout = stdout;
39        self
40    }
41
42    #[inline(always)]
43    pub fn with_stderr(mut self, stderr: Vec<u8>) -> Self {
44        self.stderr = stderr;
45        self
46    }
47
48    #[inline(always)]
49    pub fn with_elapsed(mut self, elapsed: Duration) -> Self {
50        self.elapsed = Some(elapsed);
51        self
52    }
53
54    #[inline(always)]
55    pub fn with_start(mut self, start: Instant) -> Self {
56        self.start = Some(start);
57        self
58    }
59
60    #[inline(always)]
61    pub fn with_exit_status(mut self, exit_status: std::process::ExitStatus) -> Self {
62        self.exit_status = Some(exit_status);
63        self
64    }
65
66    #[inline(always)]
67    pub fn status(&self) -> TestStatus {
68        self.status
69    }
70
71    pub fn stdout(&self) -> std::borrow::Cow<'_, str> {
72        String::from_utf8_lossy(&self.stdout)
73    }
74
75    pub fn stderr(&self) -> std::borrow::Cow<'_, str> {
76        String::from_utf8_lossy(&self.stderr)
77    }
78
79    #[inline(always)]
80    pub fn is_failure(&self) -> bool {
81        self.status.is_failure()
82    }
83
84    #[inline(always)]
85    pub fn is_timeout(&self) -> bool {
86        matches!(self.status, TestStatus::Timeout)
87    }
88
89    #[inline]
90    pub fn exit_code(&self) -> Option<i32> {
91        self.exit_status.as_ref().and_then(|status| status.code())
92    }
93}
94impl fmt::Display for TestResult {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        if let Some(exit_code) = self.exit_code() {
97            if exit_code != 0 {
98                write!(f, "{}", console::style("Exit Code: ").dim().red())?;
99                writeln!(f, "{}", console::style(exit_code).dim().red())?;
100            }
101        } else {
102            write!(f, "{}", console::style("Exit Code: ").dim().yellow())?;
103            writeln!(f, "{}", console::style("N/A").dim().yellow())?;
104        }
105
106        if self.is_timeout() {
107            let elapsed = self
108                .elapsed
109                .expect("expected elapsed duration to be set for timeout results");
110            writeln!(
111                f,
112                "{}: Reached timeout after {}s",
113                console::style("Timeout").dim().yellow(),
114                elapsed.as_secs()
115            )?;
116        } else if let Some(elapsed) = self.elapsed {
117            let secs = elapsed.as_secs();
118            write!(f, "{}: ", console::style("Elapsed").dim())?;
119            if secs == 0 {
120                writeln!(f, "{}ms", elapsed.subsec_millis())?;
121            } else {
122                writeln!(
123                    f,
124                    "{}",
125                    console::style(format!("{:.2}s", elapsed.as_secs_f64()))
126                        .dim()
127                        .yellow()
128                )?;
129            }
130        } else {
131            writeln!(f)?;
132        }
133
134        if !self.stdout.is_empty() {
135            writeln!(
136                f,
137                "{}",
138                console::style("Command Output (stdout) ----").dim()
139            )?;
140            writeln!(f, "{}", console::style(&self.stdout()).dim().cyan())?;
141            writeln!(
142                f,
143                "{}",
144                console::style("----------------------------").dim()
145            )?;
146        }
147
148        if !self.stderr.is_empty() {
149            writeln!(
150                f,
151                "{}",
152                console::style("Command Output (stderr) ----").dim()
153            )?;
154            writeln!(f, "{}", console::style(&self.stderr()).dim().cyan())?;
155            writeln!(
156                f,
157                "{}",
158                console::style("----------------------------").dim()
159            )?;
160        }
161
162        Ok(())
163    }
164}