litcheck_lit/test/
result.rs1use std::{
2 fmt,
3 time::{Duration, Instant},
4};
5
6use super::TestStatus;
7
8#[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}