ctrf_rs/
summary.rs

1// crate import(s)
2use crate::impl_extra;
3
4// std import(s)
5use std::{
6    collections::HashMap,
7    time::{SystemTime, UNIX_EPOCH},
8};
9
10// other import(s)
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13
14/// Result summary element for a CTRF report.
15/// Corresponds to the spec's ["Summary"](https://ctrf.io/docs/specification/summary) object.
16#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
17#[serde(rename_all = "camelCase")]
18pub struct Summary {
19    tests: usize,
20    passed: usize,
21    failed: usize,
22    pending: usize,
23    skipped: usize,
24    other: usize,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    flaky: Option<usize>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    suites: Option<usize>,
29    start: u64,
30    stop: u64,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    duration: Option<u64>,
33    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
34    extra: HashMap<String, Value>,
35}
36
37impl Summary {
38    /// Creates a report `Summary` instance
39    pub fn new(start: SystemTime, stop: SystemTime) -> Self {
40        Self {
41            tests: 0,
42            passed: 0,
43            failed: 0,
44            pending: 0,
45            skipped: 0,
46            other: 0,
47            flaky: None,
48            suites: None,
49            start: start.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64,
50            stop: stop.duration_since(UNIX_EPOCH).unwrap().as_millis() as u64,
51            duration: Some(stop.duration_since(start).unwrap().as_millis() as u64),
52            extra: HashMap::new(),
53        }
54    }
55
56    /// Sets the count of passed tests and updates the overall total
57    pub fn passed(&mut self, count: usize) {
58        self.passed = count;
59
60        self.update_tests();
61    }
62
63    /// Sets the count of failed tests and updates the overall total
64    pub fn failed(&mut self, count: usize) {
65        self.failed = count;
66
67        self.update_tests();
68    }
69
70    /// Sets the count of pending tests and updates the overall total
71    pub fn pending(&mut self, count: usize) {
72        self.pending = count;
73
74        self.update_tests();
75    }
76
77    /// Sets the count of skipped tests and updates the overall total
78    pub fn skipped(&mut self, count: usize) {
79        self.skipped = count;
80
81        self.update_tests();
82    }
83
84    /// Sets the count of other tests and updates the overall total
85    pub fn other(&mut self, count: usize) {
86        self.other = count;
87
88        self.update_tests();
89    }
90
91    /// Sets the count of flaky tests
92    pub fn flaky(&mut self, count: Option<usize>) {
93        self.flaky = count;
94    }
95
96    /// Sets the number of suites
97    pub fn suites(&mut self, suites: Option<usize>) {
98        self.suites = suites;
99    }
100
101    /// Updates the total test count
102    fn update_tests(&mut self) {
103        self.tests = self.passed + self.failed + self.pending + self.skipped + self.other;
104    }
105}
106
107impl_extra!(Summary);
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    use std::time::SystemTime;
114
115    #[test]
116    fn add_passed() {
117        const PASSED_COUNT: usize = 5;
118        let time = SystemTime::now();
119        let mut summary = Summary::new(time, time);
120
121        summary.passed(PASSED_COUNT);
122
123        assert_eq!(summary.passed, PASSED_COUNT);
124        assert_eq!(summary.tests, PASSED_COUNT);
125    }
126
127    #[test]
128    fn add_failed() {
129        const FAILED_COUNT: usize = 20;
130        let time = SystemTime::now();
131        let mut summary = Summary::new(time, time);
132
133        summary.passed(FAILED_COUNT);
134
135        assert_eq!(summary.passed, FAILED_COUNT);
136        assert_eq!(summary.tests, FAILED_COUNT);
137    }
138
139    #[test]
140    fn add_pending() {
141        const PENDING_COUNT: usize = 10;
142        let time = SystemTime::now();
143        let mut summary = Summary::new(time, time);
144
145        summary.passed(PENDING_COUNT);
146
147        assert_eq!(summary.passed, PENDING_COUNT);
148        assert_eq!(summary.tests, PENDING_COUNT);
149    }
150
151    #[test]
152    fn add_skipped() {
153        const SKIPPED_COUNT: usize = 2;
154        let time = SystemTime::now();
155        let mut summary = Summary::new(time, time);
156
157        summary.passed(SKIPPED_COUNT);
158
159        assert_eq!(summary.passed, SKIPPED_COUNT);
160        assert_eq!(summary.tests, SKIPPED_COUNT);
161    }
162
163    #[test]
164    fn add_other() {
165        const OTHER_COUNT: usize = 50;
166        let time = SystemTime::now();
167        let mut summary = Summary::new(time, time);
168
169        summary.passed(OTHER_COUNT);
170
171        assert_eq!(summary.passed, OTHER_COUNT);
172        assert_eq!(summary.tests, OTHER_COUNT);
173    }
174
175    #[test]
176    fn add_all_types() {
177        const PASSED_COUNT: usize = 5;
178        const FAILED_COUNT: usize = 40;
179        const PENDING_COUNT: usize = 300;
180        const SKIPPED_COUNT: usize = 2000;
181        const OTHER_COUNT: usize = 10000;
182        let time = SystemTime::now();
183        let mut summary = Summary::new(time, time);
184
185        summary.passed(PASSED_COUNT);
186        summary.failed(FAILED_COUNT);
187        summary.skipped(SKIPPED_COUNT);
188        summary.pending(PENDING_COUNT);
189        summary.other(OTHER_COUNT);
190
191        assert_eq!(summary.passed, PASSED_COUNT);
192        assert_eq!(summary.failed, FAILED_COUNT);
193        assert_eq!(summary.skipped, SKIPPED_COUNT);
194        assert_eq!(summary.pending, PENDING_COUNT);
195        assert_eq!(summary.other, OTHER_COUNT);
196        assert_eq!(
197            summary.tests,
198            PASSED_COUNT + FAILED_COUNT + PENDING_COUNT + SKIPPED_COUNT + OTHER_COUNT,
199        );
200    }
201
202    #[test]
203    fn revise_value() {
204        const PASSED_COUNT: usize = 5;
205        const FAILED_COUNT: usize = 40;
206        const PENDING_COUNT: usize = 300;
207        const SKIPPED_COUNT: usize = 2000;
208        const OTHER_COUNT: usize = 10000;
209        let time = SystemTime::now();
210        let mut summary = Summary::new(time, time);
211
212        summary.passed(PASSED_COUNT);
213        summary.failed(FAILED_COUNT);
214        summary.skipped(SKIPPED_COUNT);
215        summary.pending(PENDING_COUNT);
216        summary.other(OTHER_COUNT);
217
218        assert_eq!(summary.passed, PASSED_COUNT);
219        assert_eq!(summary.failed, FAILED_COUNT);
220        assert_eq!(summary.skipped, SKIPPED_COUNT);
221        assert_eq!(summary.pending, PENDING_COUNT);
222        assert_eq!(summary.other, OTHER_COUNT);
223        assert_eq!(
224            summary.tests,
225            PASSED_COUNT + FAILED_COUNT + PENDING_COUNT + SKIPPED_COUNT + OTHER_COUNT,
226        );
227
228        const NEW_PASSED: usize = 24681;
229        summary.passed(NEW_PASSED);
230
231        assert_eq!(summary.passed, NEW_PASSED);
232        assert_eq!(summary.failed, FAILED_COUNT);
233        assert_eq!(summary.skipped, SKIPPED_COUNT);
234        assert_eq!(summary.pending, PENDING_COUNT);
235        assert_eq!(summary.other, OTHER_COUNT);
236        assert_eq!(
237            summary.tests,
238            NEW_PASSED + FAILED_COUNT + PENDING_COUNT + SKIPPED_COUNT + OTHER_COUNT,
239        );
240    }
241
242    #[test]
243    fn add_flaky() {
244        const FLAKY_COUNT: Option<usize> = Some(80);
245        let time = SystemTime::now();
246        let mut summary = Summary::new(time, time);
247
248        summary.flaky(FLAKY_COUNT);
249
250        assert_eq!(summary.flaky, FLAKY_COUNT);
251    }
252
253    #[test]
254    fn add_suites() {
255        const SUITES: Option<usize> = Some(16);
256        let time = SystemTime::now();
257        let mut summary = Summary::new(time, time);
258
259        summary.suites(SUITES);
260
261        assert_eq!(summary.suites, SUITES)
262    }
263}