1use derive_getters::Getters;
9use time::{Duration, OffsetDateTime};
10
11#[derive(Debug, Clone, Getters)]
13pub struct TestSuite {
14 pub name: String,
15 pub package: String,
16 pub timestamp: OffsetDateTime,
17 pub hostname: String,
18 pub testcases: Vec<TestCase>,
19 pub system_out: Option<String>,
20 pub system_err: Option<String>,
21}
22
23impl TestSuite {
24 pub fn new(name: &str) -> Self {
26 TestSuite {
27 hostname: "localhost".into(),
28 package: format!("testsuite/{}", &name),
29 name: name.into(),
30 timestamp: OffsetDateTime::now_utc(),
31 testcases: Vec::new(),
32 system_out: None,
33 system_err: None,
34 }
35 }
36
37 pub fn add_testcase(&mut self, testcase: TestCase) {
39 self.testcases.push(testcase);
40 }
41
42 pub fn add_testcases(&mut self, testcases: impl IntoIterator<Item = TestCase>) {
44 self.testcases.extend(testcases);
45 }
46
47 pub fn set_timestamp(&mut self, timestamp: OffsetDateTime) {
51 self.timestamp = timestamp;
52 }
53
54 pub fn set_system_out(&mut self, system_out: &str) {
55 self.system_out = Some(system_out.to_owned());
56 }
57
58 pub fn set_system_err(&mut self, system_err: &str) {
59 self.system_err = Some(system_err.to_owned());
60 }
61
62 pub fn tests(&self) -> usize {
63 self.testcases.len()
64 }
65
66 pub fn errors(&self) -> usize {
67 self.testcases.iter().filter(|x| x.is_error()).count()
68 }
69
70 pub fn failures(&self) -> usize {
71 self.testcases.iter().filter(|x| x.is_failure()).count()
72 }
73
74 pub fn skipped(&self) -> usize {
75 self.testcases.iter().filter(|x| x.is_skipped()).count()
76 }
77
78 pub fn time(&self) -> Duration {
79 self.testcases
80 .iter()
81 .fold(Duration::ZERO, |sum, d| sum + d.time)
82 }
83}
84
85#[derive(Debug, Clone, Getters)]
87pub struct TestSuiteBuilder {
88 pub testsuite: TestSuite,
89}
90
91impl TestSuiteBuilder {
92 pub fn new(name: &str) -> Self {
94 TestSuiteBuilder {
95 testsuite: TestSuite::new(name),
96 }
97 }
98
99 pub fn add_testcase(&mut self, testcase: TestCase) -> &mut Self {
101 self.testsuite.testcases.push(testcase);
102 self
103 }
104
105 pub fn add_testcases(&mut self, testcases: impl IntoIterator<Item = TestCase>) -> &mut Self {
107 self.testsuite.testcases.extend(testcases);
108 self
109 }
110
111 pub fn set_timestamp(&mut self, timestamp: OffsetDateTime) -> &mut Self {
115 self.testsuite.timestamp = timestamp;
116 self
117 }
118
119 pub fn set_system_out(&mut self, system_out: &str) -> &mut Self {
120 self.testsuite.system_out = Some(system_out.to_owned());
121 self
122 }
123
124 pub fn set_system_err(&mut self, system_err: &str) -> &mut Self {
125 self.testsuite.system_err = Some(system_err.to_owned());
126 self
127 }
128
129 pub fn build(&self) -> TestSuite {
131 self.testsuite.clone()
132 }
133}
134
135#[derive(Debug, Clone, Getters)]
137pub struct TestCase {
138 pub name: String,
139 pub time: Duration,
140 pub result: TestResult,
141 pub classname: Option<String>,
142 pub filepath: Option<String>,
143 pub system_out: Option<String>,
144 pub system_err: Option<String>,
145}
146
147#[derive(Debug, Clone)]
149pub enum TestResult {
150 Success,
151 Skipped,
152 Error { type_: String, message: String },
153 Failure { type_: String, message: String },
154}
155
156impl TestCase {
157 pub fn success(name: &str, time: Duration) -> Self {
159 TestCase {
160 name: name.into(),
161 time,
162 result: TestResult::Success,
163 classname: None,
164 filepath: None,
165 system_out: None,
166 system_err: None,
167 }
168 }
169
170 pub fn set_classname(&mut self, classname: &str) {
172 self.classname = Some(classname.to_owned());
173 }
174
175 pub fn set_filepath(&mut self, filepath: &str) {
177 self.filepath = Some(filepath.to_owned());
178 }
179
180 pub fn set_system_out(&mut self, system_out: &str) {
182 self.system_out = Some(system_out.to_owned());
183 }
184
185 pub fn set_system_err(&mut self, system_err: &str) {
187 self.system_err = Some(system_err.to_owned());
188 }
189
190 pub fn is_success(&self) -> bool {
192 matches!(self.result, TestResult::Success)
193 }
194
195 pub fn error(name: &str, time: Duration, type_: &str, message: &str) -> Self {
199 TestCase {
200 name: name.into(),
201 time,
202 result: TestResult::Error {
203 type_: type_.into(),
204 message: message.into(),
205 },
206 classname: None,
207 filepath: None,
208 system_out: None,
209 system_err: None,
210 }
211 }
212
213 pub fn is_error(&self) -> bool {
215 matches!(self.result, TestResult::Error { .. })
216 }
217
218 pub fn failure(name: &str, time: Duration, type_: &str, message: &str) -> Self {
222 TestCase {
223 name: name.into(),
224 time,
225 result: TestResult::Failure {
226 type_: type_.into(),
227 message: message.into(),
228 },
229 classname: None,
230 filepath: None,
231 system_out: None,
232 system_err: None,
233 }
234 }
235
236 pub fn is_failure(&self) -> bool {
238 matches!(self.result, TestResult::Failure { .. })
239 }
240
241 pub fn skipped(name: &str) -> Self {
245 TestCase {
246 name: name.into(),
247 time: Duration::ZERO,
248 result: TestResult::Skipped,
249 classname: None,
250 filepath: None,
251 system_out: None,
252 system_err: None,
253 }
254 }
255
256 pub fn is_skipped(&self) -> bool {
258 matches!(self.result, TestResult::Skipped)
259 }
260}
261
262#[derive(Debug, Clone, Getters)]
264pub struct TestCaseBuilder {
265 pub testcase: TestCase,
266}
267
268impl TestCaseBuilder {
269 pub fn success(name: &str, time: Duration) -> Self {
271 TestCaseBuilder {
272 testcase: TestCase::success(name, time),
273 }
274 }
275
276 pub fn set_classname(&mut self, classname: &str) -> &mut Self {
278 self.testcase.classname = Some(classname.to_owned());
279 self
280 }
281
282 pub fn set_filepath(&mut self, filepath: &str) -> &mut Self {
284 self.testcase.filepath = Some(filepath.to_owned());
285 self
286 }
287
288 pub fn set_system_out(&mut self, system_out: &str) -> &mut Self {
290 self.testcase.system_out = Some(system_out.to_owned());
291 self
292 }
293
294 pub fn set_system_err(&mut self, system_err: &str) -> &mut Self {
296 self.testcase.system_err = Some(system_err.to_owned());
297 self
298 }
299
300 pub fn error(name: &str, time: Duration, type_: &str, message: &str) -> Self {
304 TestCaseBuilder {
305 testcase: TestCase::error(name, time, type_, message),
306 }
307 }
308
309 pub fn failure(name: &str, time: Duration, type_: &str, message: &str) -> Self {
313 TestCaseBuilder {
314 testcase: TestCase::failure(name, time, type_, message),
315 }
316 }
317
318 pub fn skipped(name: &str) -> Self {
322 TestCaseBuilder {
323 testcase: TestCase::skipped(name),
324 }
325 }
326
327 pub fn build(&self) -> TestCase {
329 self.testcase.clone()
330 }
331}
332
333#[cfg(doctest)]
335doc_comment::doctest!("../README.md");