clnrm_core/formatting/
test_result.rs

1//! Test Result Structures
2//!
3//! Core data structures for representing test execution results.
4//! Used by all formatters to generate output.
5
6use std::time::Duration;
7
8/// Status of a test execution
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum TestStatus {
11    /// Test passed successfully
12    Passed,
13    /// Test failed with error
14    Failed,
15    /// Test was skipped
16    Skipped,
17    /// Test status is unknown or pending
18    Unknown,
19}
20
21/// Individual test result
22#[derive(Debug, Clone)]
23pub struct TestResult {
24    /// Test name
25    pub name: String,
26    /// Test status
27    pub status: TestStatus,
28    /// Test duration
29    pub duration: Option<Duration>,
30    /// Error message (if failed)
31    pub error: Option<String>,
32    /// Standard output captured during test
33    pub stdout: Option<String>,
34    /// Standard error captured during test
35    pub stderr: Option<String>,
36    /// Test metadata
37    pub metadata: std::collections::HashMap<String, String>,
38}
39
40impl TestResult {
41    /// Create a new passed test result
42    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    /// Create a new failed test result
55    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    /// Create a new skipped test result
68    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    /// Set test duration
81    pub fn with_duration(mut self, duration: Duration) -> Self {
82        self.duration = Some(duration);
83        self
84    }
85
86    /// Set stdout
87    pub fn with_stdout(mut self, stdout: impl Into<String>) -> Self {
88        self.stdout = Some(stdout.into());
89        self
90    }
91
92    /// Set stderr
93    pub fn with_stderr(mut self, stderr: impl Into<String>) -> Self {
94        self.stderr = Some(stderr.into());
95        self
96    }
97
98    /// Add metadata
99    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    /// Check if test passed
105    pub fn is_passed(&self) -> bool {
106        self.status == TestStatus::Passed
107    }
108
109    /// Check if test failed
110    pub fn is_failed(&self) -> bool {
111        self.status == TestStatus::Failed
112    }
113
114    /// Check if test was skipped
115    pub fn is_skipped(&self) -> bool {
116        self.status == TestStatus::Skipped
117    }
118}
119
120/// Test suite containing multiple test results
121#[derive(Debug, Clone)]
122pub struct TestSuite {
123    /// Suite name
124    pub name: String,
125    /// Test results
126    pub results: Vec<TestResult>,
127    /// Suite duration
128    pub duration: Option<Duration>,
129    /// Suite metadata
130    pub metadata: std::collections::HashMap<String, String>,
131}
132
133impl TestSuite {
134    /// Create a new empty test suite
135    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    /// Add a test result
145    pub fn add_result(mut self, result: TestResult) -> Self {
146        self.results.push(result);
147        self
148    }
149
150    /// Set suite duration
151    pub fn with_duration(mut self, duration: Duration) -> Self {
152        self.duration = Some(duration);
153        self
154    }
155
156    /// Add metadata
157    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    /// Get count of passed tests
163    pub fn passed_count(&self) -> usize {
164        self.results.iter().filter(|r| r.is_passed()).count()
165    }
166
167    /// Get count of failed tests
168    pub fn failed_count(&self) -> usize {
169        self.results.iter().filter(|r| r.is_failed()).count()
170    }
171
172    /// Get count of skipped tests
173    pub fn skipped_count(&self) -> usize {
174        self.results.iter().filter(|r| r.is_skipped()).count()
175    }
176
177    /// Get total test count
178    pub fn total_count(&self) -> usize {
179        self.results.len()
180    }
181
182    /// Check if all tests passed
183    pub fn is_success(&self) -> bool {
184        self.failed_count() == 0 && self.total_count() > 0
185    }
186}