1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
6pub struct TestReport {
7 pub suite_name: Option<String>,
8 pub total: usize,
9 pub passed: usize,
10 pub failed: usize,
11 pub skipped: usize,
12 pub errored: usize,
13}
14
15impl TestReport {
16 pub const fn new() -> Self {
17 Self {
18 suite_name: None,
19 total: 0,
20 passed: 0,
21 failed: 0,
22 skipped: 0,
23 errored: 0,
24 }
25 }
26
27 #[allow(clippy::cast_precision_loss)]
28 pub fn success_rate(&self) -> Option<f64> {
29 if self.total == 0 {
30 None
31 } else {
32 Some(self.passed as f64 / self.total as f64)
33 }
34 }
35
36 pub const fn is_success(&self) -> bool {
37 self.failed == 0 && self.errored == 0
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::TestReport;
44
45 #[test]
46 fn new_report_is_empty() {
47 let report = TestReport::new();
48
49 assert_eq!(report.suite_name, None);
50 assert_eq!(report.total, 0);
51 assert_eq!(report.success_rate(), None);
52 assert!(report.is_success());
53 assert_eq!(TestReport::default(), report);
54 }
55
56 #[test]
57 fn computes_success_rate() {
58 let report = TestReport {
59 suite_name: Some("core".to_string()),
60 total: 4,
61 passed: 3,
62 failed: 1,
63 skipped: 0,
64 errored: 0,
65 };
66
67 assert_eq!(report.success_rate(), Some(0.75));
68 assert!(!report.is_success());
69 }
70
71 #[test]
72 fn errored_report_is_not_successful() {
73 let report = TestReport {
74 suite_name: None,
75 total: 1,
76 passed: 0,
77 failed: 0,
78 skipped: 0,
79 errored: 1,
80 };
81
82 assert!(!report.is_success());
83 }
84}