clnrm_core/formatting/
test_result.rs1use std::time::Duration;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum TestStatus {
11 Passed,
13 Failed,
15 Skipped,
17 Unknown,
19}
20
21#[derive(Debug, Clone)]
23pub struct TestResult {
24 pub name: String,
26 pub status: TestStatus,
28 pub duration: Option<Duration>,
30 pub error: Option<String>,
32 pub stdout: Option<String>,
34 pub stderr: Option<String>,
36 pub metadata: std::collections::HashMap<String, String>,
38}
39
40impl TestResult {
41 pub fn passed(name: impl Into<String>) -> Self {
43 Self {
44 name: name.into(),
45 status: TestStatus::Passed,
46 duration: None,
47 error: None,
48 stdout: None,
49 stderr: None,
50 metadata: std::collections::HashMap::new(),
51 }
52 }
53
54 pub fn failed(name: impl Into<String>, error: impl Into<String>) -> Self {
56 Self {
57 name: name.into(),
58 status: TestStatus::Failed,
59 duration: None,
60 error: Some(error.into()),
61 stdout: None,
62 stderr: None,
63 metadata: std::collections::HashMap::new(),
64 }
65 }
66
67 pub fn skipped(name: impl Into<String>) -> Self {
69 Self {
70 name: name.into(),
71 status: TestStatus::Skipped,
72 duration: None,
73 error: None,
74 stdout: None,
75 stderr: None,
76 metadata: std::collections::HashMap::new(),
77 }
78 }
79
80 pub fn with_duration(mut self, duration: Duration) -> Self {
82 self.duration = Some(duration);
83 self
84 }
85
86 pub fn with_stdout(mut self, stdout: impl Into<String>) -> Self {
88 self.stdout = Some(stdout.into());
89 self
90 }
91
92 pub fn with_stderr(mut self, stderr: impl Into<String>) -> Self {
94 self.stderr = Some(stderr.into());
95 self
96 }
97
98 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
100 self.metadata.insert(key.into(), value.into());
101 self
102 }
103
104 pub fn is_passed(&self) -> bool {
106 self.status == TestStatus::Passed
107 }
108
109 pub fn is_failed(&self) -> bool {
111 self.status == TestStatus::Failed
112 }
113
114 pub fn is_skipped(&self) -> bool {
116 self.status == TestStatus::Skipped
117 }
118}
119
120#[derive(Debug, Clone)]
122pub struct TestSuite {
123 pub name: String,
125 pub results: Vec<TestResult>,
127 pub duration: Option<Duration>,
129 pub metadata: std::collections::HashMap<String, String>,
131}
132
133impl TestSuite {
134 pub fn new(name: impl Into<String>) -> Self {
136 Self {
137 name: name.into(),
138 results: Vec::new(),
139 duration: None,
140 metadata: std::collections::HashMap::new(),
141 }
142 }
143
144 pub fn add_result(mut self, result: TestResult) -> Self {
146 self.results.push(result);
147 self
148 }
149
150 pub fn with_duration(mut self, duration: Duration) -> Self {
152 self.duration = Some(duration);
153 self
154 }
155
156 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
158 self.metadata.insert(key.into(), value.into());
159 self
160 }
161
162 pub fn passed_count(&self) -> usize {
164 self.results.iter().filter(|r| r.is_passed()).count()
165 }
166
167 pub fn failed_count(&self) -> usize {
169 self.results.iter().filter(|r| r.is_failed()).count()
170 }
171
172 pub fn skipped_count(&self) -> usize {
174 self.results.iter().filter(|r| r.is_skipped()).count()
175 }
176
177 pub fn total_count(&self) -> usize {
179 self.results.len()
180 }
181
182 pub fn is_success(&self) -> bool {
184 self.failed_count() == 0 && self.total_count() > 0
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn test_test_result_passed() {
194 let result = TestResult::passed("test_name");
196
197 assert_eq!(result.name, "test_name");
199 assert_eq!(result.status, TestStatus::Passed);
200 assert!(result.is_passed());
201 assert!(!result.is_failed());
202 assert!(!result.is_skipped());
203 }
204
205 #[test]
206 fn test_test_result_failed() {
207 let result = TestResult::failed("test_name", "error message");
209
210 assert_eq!(result.name, "test_name");
212 assert_eq!(result.status, TestStatus::Failed);
213 assert_eq!(result.error, Some("error message".to_string()));
214 assert!(!result.is_passed());
215 assert!(result.is_failed());
216 assert!(!result.is_skipped());
217 }
218
219 #[test]
220 fn test_test_result_skipped() {
221 let result = TestResult::skipped("test_name");
223
224 assert_eq!(result.name, "test_name");
226 assert_eq!(result.status, TestStatus::Skipped);
227 assert!(!result.is_passed());
228 assert!(!result.is_failed());
229 assert!(result.is_skipped());
230 }
231
232 #[test]
233 fn test_test_result_with_duration() {
234 let result = TestResult::passed("test_name").with_duration(Duration::from_millis(100));
236
237 assert_eq!(result.duration, Some(Duration::from_millis(100)));
239 }
240
241 #[test]
242 fn test_test_result_with_stdout() {
243 let result = TestResult::passed("test_name").with_stdout("output");
245
246 assert_eq!(result.stdout, Some("output".to_string()));
248 }
249
250 #[test]
251 fn test_test_result_with_metadata() {
252 let result = TestResult::passed("test_name").with_metadata("key", "value");
254
255 assert_eq!(result.metadata.get("key"), Some(&"value".to_string()));
257 }
258
259 #[test]
260 fn test_test_suite_new() {
261 let suite = TestSuite::new("suite_name");
263
264 assert_eq!(suite.name, "suite_name");
266 assert_eq!(suite.total_count(), 0);
267 }
268
269 #[test]
270 fn test_test_suite_add_result() {
271 let suite = TestSuite::new("suite_name");
273
274 let suite = suite.add_result(TestResult::passed("test1"));
276
277 assert_eq!(suite.total_count(), 1);
279 assert_eq!(suite.passed_count(), 1);
280 }
281
282 #[test]
283 fn test_test_suite_counts() {
284 let suite = TestSuite::new("suite_name")
286 .add_result(TestResult::passed("test1"))
287 .add_result(TestResult::passed("test2"))
288 .add_result(TestResult::failed("test3", "error"))
289 .add_result(TestResult::skipped("test4"));
290
291 assert_eq!(suite.total_count(), 4);
293 assert_eq!(suite.passed_count(), 2);
294 assert_eq!(suite.failed_count(), 1);
295 assert_eq!(suite.skipped_count(), 1);
296 }
297
298 #[test]
299 fn test_test_suite_is_success_with_all_passed() {
300 let suite = TestSuite::new("suite_name")
302 .add_result(TestResult::passed("test1"))
303 .add_result(TestResult::passed("test2"));
304
305 assert!(suite.is_success());
307 }
308
309 #[test]
310 fn test_test_suite_is_success_with_failures() {
311 let suite = TestSuite::new("suite_name")
313 .add_result(TestResult::passed("test1"))
314 .add_result(TestResult::failed("test2", "error"));
315
316 assert!(!suite.is_success());
318 }
319
320 #[test]
321 fn test_test_suite_is_success_empty() {
322 let suite = TestSuite::new("suite_name");
324
325 assert!(!suite.is_success());
327 }
328}