1use std::time::{Duration, Instant};
4
5#[derive(Debug, Clone)]
7pub struct TestSuite {
8 pub name: String,
10 pub tests: Vec<TestCase>,
12}
13
14impl TestSuite {
15 #[must_use]
17 pub fn new(name: impl Into<String>) -> Self {
18 Self {
19 name: name.into(),
20 tests: Vec::new(),
21 }
22 }
23
24 pub fn add_test(&mut self, test: TestCase) {
26 self.tests.push(test);
27 }
28
29 #[must_use]
31 pub fn test_count(&self) -> usize {
32 self.tests.len()
33 }
34}
35
36#[derive(Debug, Clone)]
38pub struct TestCase {
39 pub name: String,
41 pub timeout_ms: u64,
43}
44
45impl TestCase {
46 #[must_use]
48 pub fn new(name: impl Into<String>) -> Self {
49 Self {
50 name: name.into(),
51 timeout_ms: 30000, }
53 }
54
55 #[must_use]
57 pub const fn with_timeout(mut self, ms: u64) -> Self {
58 self.timeout_ms = ms;
59 self
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct TestResult {
66 pub name: String,
68 pub passed: bool,
70 pub error: Option<String>,
72 pub duration: Duration,
74}
75
76impl TestResult {
77 #[must_use]
79 pub fn pass(name: impl Into<String>) -> Self {
80 Self {
81 name: name.into(),
82 passed: true,
83 error: None,
84 duration: Duration::ZERO,
85 }
86 }
87
88 #[must_use]
90 pub fn fail(name: impl Into<String>, error: impl Into<String>) -> Self {
91 Self {
92 name: name.into(),
93 passed: false,
94 error: Some(error.into()),
95 duration: Duration::ZERO,
96 }
97 }
98
99 #[must_use]
101 pub const fn with_duration(mut self, duration: Duration) -> Self {
102 self.duration = duration;
103 self
104 }
105}
106
107#[derive(Debug, Clone)]
109pub struct SuiteResults {
110 pub suite_name: String,
112 pub results: Vec<TestResult>,
114 pub duration: Duration,
116}
117
118impl SuiteResults {
119 #[must_use]
121 pub fn all_passed(&self) -> bool {
122 self.results.iter().all(|r| r.passed)
123 }
124
125 #[must_use]
127 pub fn passed_count(&self) -> usize {
128 self.results.iter().filter(|r| r.passed).count()
129 }
130
131 #[must_use]
133 pub fn failed_count(&self) -> usize {
134 self.results.iter().filter(|r| !r.passed).count()
135 }
136
137 #[must_use]
139 pub fn total(&self) -> usize {
140 self.results.len()
141 }
142
143 #[must_use]
145 pub fn failures(&self) -> Vec<&TestResult> {
146 self.results.iter().filter(|r| !r.passed).collect()
147 }
148}
149
150#[derive(Debug, Default)]
152pub struct TestHarness {
153 pub fail_fast: bool,
155 pub parallel: bool,
157}
158
159impl TestHarness {
160 #[must_use]
162 pub fn new() -> Self {
163 Self::default()
164 }
165
166 #[must_use]
168 pub const fn with_fail_fast(mut self) -> Self {
169 self.fail_fast = true;
170 self
171 }
172
173 #[must_use]
175 pub const fn with_parallel(mut self) -> Self {
176 self.parallel = true;
177 self
178 }
179
180 #[must_use]
182 pub fn run(&self, suite: &TestSuite) -> SuiteResults {
183 let start = Instant::now();
184 let results = Vec::new();
185
186 SuiteResults {
190 suite_name: suite.name.clone(),
191 results,
192 duration: start.elapsed(),
193 }
194 }
195}