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 contract_pre_test_result_reporting!();
33 self.tests.len()
34 }
35}
36
37#[derive(Debug, Clone)]
39pub struct TestCase {
40 pub name: String,
42 pub timeout_ms: u64,
44}
45
46impl TestCase {
47 #[must_use]
49 pub fn new(name: impl Into<String>) -> Self {
50 Self {
51 name: name.into(),
52 timeout_ms: 30000, }
54 }
55
56 #[must_use]
58 pub const fn with_timeout(mut self, ms: u64) -> Self {
59 self.timeout_ms = ms;
60 self
61 }
62}
63
64#[derive(Debug, Clone)]
66pub struct TestResult {
67 pub name: String,
69 pub passed: bool,
71 pub error: Option<String>,
73 pub duration: Duration,
75}
76
77impl TestResult {
78 #[must_use]
80 pub fn pass(name: impl Into<String>) -> Self {
81 contract_pre_test_result_reporting!();
82 Self {
83 name: name.into(),
84 passed: true,
85 error: None,
86 duration: Duration::ZERO,
87 }
88 }
89
90 #[must_use]
92 pub fn fail(name: impl Into<String>, error: impl Into<String>) -> Self {
93 contract_pre_test_result_reporting!();
94 Self {
95 name: name.into(),
96 passed: false,
97 error: Some(error.into()),
98 duration: Duration::ZERO,
99 }
100 }
101
102 #[must_use]
104 pub const fn with_duration(mut self, duration: Duration) -> Self {
105 self.duration = duration;
106 self
107 }
108}
109
110#[derive(Debug, Clone)]
112pub struct SuiteResults {
113 pub suite_name: String,
115 pub results: Vec<TestResult>,
117 pub duration: Duration,
119}
120
121impl SuiteResults {
122 #[must_use]
124 pub fn all_passed(&self) -> bool {
125 self.results.iter().all(|r| r.passed)
126 }
127
128 #[must_use]
130 pub fn passed_count(&self) -> usize {
131 self.results.iter().filter(|r| r.passed).count()
132 }
133
134 #[must_use]
136 pub fn failed_count(&self) -> usize {
137 self.results.iter().filter(|r| !r.passed).count()
138 }
139
140 #[must_use]
142 pub fn total(&self) -> usize {
143 self.results.len()
144 }
145
146 #[must_use]
148 pub fn failures(&self) -> Vec<&TestResult> {
149 self.results.iter().filter(|r| !r.passed).collect()
150 }
151}
152
153#[derive(Debug, Default)]
155pub struct TestHarness {
156 pub fail_fast: bool,
158 pub parallel: bool,
160}
161
162impl TestHarness {
163 #[must_use]
165 pub fn new() -> Self {
166 Self::default()
167 }
168
169 #[must_use]
171 pub const fn with_fail_fast(mut self) -> Self {
172 self.fail_fast = true;
173 self
174 }
175
176 #[must_use]
178 pub const fn with_parallel(mut self) -> Self {
179 self.parallel = true;
180 self
181 }
182
183 #[must_use]
185 pub fn run(&self, suite: &TestSuite) -> SuiteResults {
186 let start = Instant::now();
187 let results = Vec::new();
188
189 SuiteResults {
193 suite_name: suite.name.clone(),
194 results,
195 duration: start.elapsed(),
196 }
197 }
198}